source: trunk/src/sh_suidchk.c@ 230

Last change on this file since 230 was 227, checked in by katerina, 16 years ago

Fix warnings with -fstack-check

File size: 51.8 KB
Line 
1/* SAMHAIN file system integrity testing */
2/* Copyright (C) 2001 Rainer Wichmann */
3/* */
4/* This program is free software; you can redistribute it */
5/* and/or modify */
6/* it under the terms of the GNU General Public License as */
7/* published by */
8/* the Free Software Foundation; either version 2 of the License, or */
9/* (at your option) any later version. */
10/* */
11/* This program is distributed in the hope that it will be useful, */
12/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14/* GNU General Public License for more details. */
15/* */
16/* You should have received a copy of the GNU General Public License */
17/* along with this program; if not, write to the Free Software */
18/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20#include "config_xor.h"
21
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/types.h>
27#include <sys/stat.h>
28#include <fcntl.h>
29#include <unistd.h>
30#include <errno.h>
31#include <limits.h>
32
33#ifdef HAVE_SCHED_H
34#include <sched.h>
35#endif
36
37#ifdef SH_USE_SUIDCHK
38
39#undef FIL__
40#define FIL__ _("sh_suidchk.c")
41
42#if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE)
43
44#if TIME_WITH_SYS_TIME
45#include <sys/time.h>
46#include <time.h>
47#else
48#if HAVE_SYS_TIME_H
49#include <sys/time.h>
50#else
51#include <time.h>
52#endif
53#endif
54
55#ifdef HAVE_DIRENT_H
56#include <dirent.h>
57#define NAMLEN(dirent) sl_strlen((dirent)->d_name)
58#else
59#define dirent direct
60#define NAMLEN(dirent) (dirent)->d_namlen
61#ifdef HAVE_SYS_NDIR_H
62#include <sys/ndir.h>
63#endif
64#ifdef HAVE_SYS_DIR_H
65#include <sys/dir.h>
66#endif
67#ifdef HAVE_NDIR_H
68#include <ndir.h>
69#endif
70#endif
71#define NEED_ADD_DIRENT
72
73#include "samhain.h"
74#include "sh_pthread.h"
75#include "sh_utils.h"
76#include "sh_error.h"
77#include "sh_modules.h"
78#include "sh_suidchk.h"
79#include "sh_hash.h"
80#include "sh_unix.h"
81#include "sh_files.h"
82#include "sh_schedule.h"
83#include "sh_calls.h"
84
85
86sh_rconf sh_suidchk_table[] = {
87 {
88 N_("severitysuidcheck"),
89 sh_suidchk_set_severity
90 },
91 {
92 N_("suidcheckactive"),
93 sh_suidchk_set_activate
94 },
95 {
96 N_("suidcheckinterval"),
97 sh_suidchk_set_timer
98 },
99 {
100 N_("suidcheckschedule"),
101 sh_suidchk_set_schedule
102 },
103 {
104 N_("suidcheckexclude"),
105 sh_suidchk_set_exclude
106 },
107 {
108 N_("suidcheckfps"),
109 sh_suidchk_set_fps
110 },
111 {
112 N_("suidcheckyield"),
113 sh_suidchk_set_yield
114 },
115 {
116 N_("suidchecknosuid"),
117 sh_suidchk_set_nosuid
118 },
119 {
120 N_("suidcheckquarantinefiles"),
121 sh_suidchk_set_quarantine
122 },
123 {
124 N_("suidcheckquarantinemethod"),
125 sh_suidchk_set_qmethod
126 },
127 {
128 N_("suidcheckquarantinedelete"),
129 sh_suidchk_set_qdelete
130 },
131 {
132 NULL,
133 NULL
134 },
135};
136
137
138static time_t lastcheck = (time_t) 0;
139static int ShSuidchkActive = S_TRUE;
140static time_t ShSuidchkInterval = 7200;
141static long ShSuidchkFps = 0;
142static int ShSuidchkNosuid = S_FALSE;
143static int ShSuidchkYield = S_FALSE;
144static int ShSuidchkQEnable = S_FALSE;
145static int ShSuidchkQMethod = SH_Q_CHANGEPERM;
146static int ShSuidchkQDelete = S_FALSE;
147static int ShSuidchkSeverity = SH_ERR_SEVERE;
148static char * ShSuidchkExclude = NULL;
149static size_t ExcludeLen = 0;
150
151static time_t FileLimNow = 0;
152static time_t FileLimStart = 0;
153static long FileLimNum = 0;
154static long FileLimTotal = 0;
155
156static sh_schedule_t * ShSuidchkSched = NULL;
157
158static char *
159filesystem_type (char * path, char * relpath, struct stat * statp);
160
161#ifndef PATH_MAX
162#define PATH_MAX 1024
163#endif
164
165extern unsigned long sh_files_maskof (int class);
166
167static void set_defaults (void)
168{
169 ShSuidchkActive = S_TRUE;
170 ShSuidchkInterval = 7200;
171 ShSuidchkFps = 0;
172 ShSuidchkNosuid = S_FALSE;
173 ShSuidchkYield = S_FALSE;
174 ShSuidchkQEnable = S_FALSE;
175 ShSuidchkQMethod = SH_Q_CHANGEPERM;
176 ShSuidchkQDelete = S_FALSE;
177 ShSuidchkSeverity = SH_ERR_SEVERE;
178 if (ShSuidchkExclude != NULL)
179 SH_FREE(ShSuidchkExclude);
180 ShSuidchkExclude = NULL;
181 ExcludeLen = 0;
182
183 FileLimNow = 0;
184 FileLimStart = 0;
185 FileLimNum = 0;
186 FileLimTotal = 0;
187
188 return;
189}
190
191/* Recursively descend into the directory to make sure that
192 * there is no symlink in the path.
193 */
194static int do_truncate_int (char * path, int depth)
195{
196 char * q;
197 struct stat one;
198 struct stat two;
199 int fd;
200 char errbuf[SH_ERRBUF_SIZE];
201
202 if (depth > 99)
203 {
204 sh_error_handle ((-1), FIL__, __LINE__, EINVAL,
205 MSG_SUID_ERROR,
206 _("do_truncate: max depth 99 exceeded"));
207 return -1;
208 }
209 ++depth;
210 if (path[0] != '/')
211 {
212 sh_error_handle ((-1), FIL__, __LINE__, EINVAL,
213 MSG_SUID_ERROR,
214 _("do_truncate: not an absolute path"));
215 return -1;
216 }
217 ++path;
218 q = strchr(path, '/');
219 if (q)
220 {
221 *q = '\0';
222 if (0 != retry_lstat(FIL__, __LINE__, path, &one))
223 {
224 sh_error_handle ((-1), FIL__, __LINE__, errno,
225 MSG_SUID_ERROR,
226 sh_error_message(errno, errbuf, sizeof(errbuf)));
227 *q = '/';
228 return -1;
229 }
230 if (/*@-usedef@*/!S_ISDIR(one.st_mode)/*@+usedef@*/)
231
232 {
233 sh_error_handle ((-1), FIL__, __LINE__, EINVAL,
234 MSG_SUID_ERROR,
235 _("Possible race: not a directory"));
236 *q = '/';
237 return -1;
238 }
239
240
241 if (0 != chdir(path))
242 {
243 sh_error_handle ((-1), FIL__, __LINE__, errno,
244 MSG_SUID_ERROR,
245 sh_error_message(errno, errbuf, sizeof(errbuf)));
246 *q = '/';
247 return -1;
248 }
249 *q = '/';
250 if (0 != retry_lstat(FIL__, __LINE__, ".", &two))
251 {
252 sh_error_handle ((-1), FIL__, __LINE__, errno,
253 MSG_SUID_ERROR,
254 sh_error_message(errno, errbuf, sizeof(errbuf)));
255 return -1;
256 }
257 if (/*@-usedef@*/(one.st_dev != two.st_dev) ||
258 (one.st_ino != two.st_ino) ||
259 (!S_ISDIR(two.st_mode))/*@+usedef@*/)
260 {
261 sh_error_handle ((-1), FIL__, __LINE__, EINVAL,
262 MSG_SUID_ERROR,
263 _("Possible race: lstat(dir) != lstat(.)"));
264 return -1;
265 }
266
267
268 return (do_truncate_int(q, depth));
269 }
270 else
271 {
272 /* no more '/', so this is the file
273 */
274 if (*path == '\0')
275 return -1;
276 if (0 != retry_lstat(FIL__, __LINE__, path, &one))
277 {
278 sh_error_handle ((-1), FIL__, __LINE__, errno,
279 MSG_SUID_ERROR,
280 sh_error_message(errno, errbuf, sizeof(errbuf)));
281 return -1;
282 }
283 fd = open(path, O_RDWR);
284 if (-1 == fd)
285 {
286 sh_error_handle ((-1), FIL__, __LINE__, errno,
287 MSG_SUID_ERROR,
288 sh_error_message(errno, errbuf, sizeof(errbuf)));
289 return -1;
290 }
291 if (0 != retry_fstat(FIL__, __LINE__, fd, &two))
292 {
293 sh_error_handle ((-1), FIL__, __LINE__, errno,
294 MSG_SUID_ERROR,
295 sh_error_message(errno, errbuf, sizeof(errbuf)));
296 (void) close(fd);
297 return -1;
298 }
299 if (/*@-usedef@*/(one.st_dev != two.st_dev) ||
300 (one.st_ino != two.st_ino)/*@+usedef@*/)
301 {
302 sh_error_handle ((-1), FIL__, __LINE__, EINVAL,
303 MSG_SUID_ERROR,
304 _("Possible race: lstat != fstat"));
305 (void) close(fd);
306 return -1;
307 }
308 if (!S_ISREG(two.st_mode))
309 {
310 sh_error_handle ((-1), FIL__, __LINE__, EINVAL,
311 MSG_SUID_ERROR,
312 _("Possible race: not a regular file"));
313 (void) close(fd);
314 return -1;
315 }
316 if ((0 == (two.st_mode & S_ISUID)) && (0 == (two.st_mode & S_ISGID)))
317 {
318 sh_error_handle ((-1), FIL__, __LINE__, EINVAL,
319 MSG_SUID_ERROR,
320 _("Possible race: not a suid/sgid file"));
321 (void) close(fd);
322 return -1;
323 }
324 if (ShSuidchkQDelete == S_FALSE)
325 {
326 if ((two.st_mode & S_ISUID) > 0)
327 two.st_mode -= S_ISUID;
328 if ((two.st_mode & S_ISGID) > 0)
329 two.st_mode -= S_ISGID;
330#ifdef HAVE_FCHMOD
331 if (-1 == /*@-unrecog@*/fchmod(fd, two.st_mode)/*@+unrecog@*/)
332 {
333 sh_error_handle ((-1), FIL__, __LINE__, errno,
334 MSG_SUID_ERROR,
335 sh_error_message(errno, errbuf, sizeof(errbuf)));
336 (void) close(fd);
337 return -1;
338 }
339#else
340 sh_error_handle ((-1), FIL__, __LINE__, errno,
341 MSG_SUID_ERROR,
342 _("The fchmod() function is not available"));
343 (void) close(fd);
344 return -1;
345#endif
346 if (two.st_nlink > 1)
347 {
348 sh_error_handle ((-1), FIL__, __LINE__, 0,
349 MSG_SUID_ERROR,
350 _("Not truncated because hardlink count gt 1"));
351 (void) close(fd);
352 return -1;
353 }
354 /* The man page says: 'POSIX has ftruncate'
355 */
356 if (-1 == /*@-unrecog@*/ftruncate(fd, 0)/*@+unrecog@*/)
357 {
358 sh_error_handle ((-1), FIL__, __LINE__, errno,
359 MSG_SUID_ERROR,
360 sh_error_message(errno, errbuf, sizeof(errbuf)));
361 (void) close(fd);
362 return -1;
363 }
364 }
365 else
366 {
367 if (-1 == retry_aud_unlink(FIL__, __LINE__, path))
368 {
369 sh_error_handle ((-1), FIL__, __LINE__, errno,
370 MSG_SUID_ERROR,
371 sh_error_message(errno, errbuf, sizeof(errbuf)));
372 (void) close(fd);
373 return -1;
374 }
375 }
376 (void) close (fd);
377 return (0);
378 }
379}
380
381static int do_truncate (const char * path_in)
382{
383 int caperr;
384 int result;
385 char * path;
386 char errbuf[SH_ERRBUF_SIZE];
387
388 if (0 != chdir("/"))
389 {
390 sh_error_handle ((-1), FIL__, __LINE__, errno,
391 MSG_SUID_ERROR,
392 sh_error_message(errno, errbuf, sizeof(errbuf)));
393 }
394
395 if (0 != (caperr = sl_get_cap_qdel()))
396 {
397 sh_error_handle((-1), FIL__, __LINE__, caperr, MSG_E_SUBGEN,
398 sh_error_message (caperr, errbuf, sizeof(errbuf)),
399 _("sl_get_cap_qdel"));
400 }
401
402 path = sh_util_strdup (path_in);
403 result = do_truncate_int (path, 0);
404 SH_FREE(path);
405
406 if (0 != (caperr = sl_drop_cap_qdel()))
407 {
408 sh_error_handle((-1), FIL__, __LINE__, caperr, MSG_E_SUBGEN,
409 sh_error_message (caperr, errbuf, sizeof(errbuf)),
410 _("sl_drop_cap_qdel"));
411 }
412
413 if (0 != chdir("/"))
414 {
415 sh_error_handle ((-1), FIL__, __LINE__, errno,
416 MSG_SUID_ERROR,
417 sh_error_message(errno, errbuf, sizeof(errbuf)));
418 }
419 return result;
420}
421
422static void sh_q_delete(const char * fullpath)
423{
424 int status;
425 char * msg;
426 char * tmp;
427
428 if (do_truncate (fullpath) == -1)
429 {
430 status = errno;
431 msg = SH_ALLOC(SH_BUFSIZE);
432 tmp = sh_util_safe_name(fullpath);
433
434 (void) sl_snprintf(msg, SH_BUFSIZE,
435 _("Problem quarantining file. File NOT quarantined. errno = %ld"),
436 status);
437 sh_error_handle (ShSuidchkSeverity,
438 FIL__, __LINE__,
439 status,
440 MSG_SUID_QREPORT, msg,
441 tmp );
442 SH_FREE(tmp);
443 SH_FREE(msg);
444 }
445 else
446 {
447 tmp = sh_util_safe_name(fullpath);
448 sh_error_handle (ShSuidchkSeverity,
449 FIL__, __LINE__, 0,
450 MSG_SUID_QREPORT,
451 _("Quarantine method applied"),
452 tmp );
453 SH_FREE(tmp);
454 }
455 return;
456}
457
458static void sh_q_move(const char * fullpath, file_type * theFile,
459 const char * timestrc, const char * timestra,
460 const char * timestrm)
461{
462 int status;
463 int readFile = -1;
464 int writeFile = -1;;
465 struct stat fileInfo;
466 ssize_t count;
467 char * msg;
468 char * tmp;
469 char * basetmp;
470 char * filetmp;
471 char buffer[1024];
472 char * dir = SH_ALLOC(PATH_MAX+1);
473 mode_t umask_old;
474 FILE * filePtr = NULL;
475
476 (void) sl_strlcpy (dir, DEFAULT_QDIR, PATH_MAX+1);
477
478 if (retry_stat (FIL__, __LINE__, dir, &fileInfo) != 0)
479 {
480 /* Quarantine directory does not exist,
481 */
482 status = errno;
483 msg = SH_ALLOC(SH_BUFSIZE);
484 tmp = sh_util_safe_name(fullpath);
485
486 (void) sl_snprintf(msg, SH_BUFSIZE,
487 _("Problem quarantining file. File NOT quarantined. errno = %ld (stat)"),
488 status);
489 sh_error_handle (ShSuidchkSeverity,
490 FIL__, __LINE__,
491 status,
492 MSG_SUID_QREPORT, msg,
493 tmp );
494 SH_FREE(tmp);
495 SH_FREE(msg);
496 }
497 else
498 {
499 if (retry_lstat (FIL__, __LINE__,
500 fullpath, &fileInfo) == -1)
501 {
502 status = errno;
503 msg = SH_ALLOC(SH_BUFSIZE);
504 tmp = sh_util_safe_name(fullpath);
505
506 (void) sl_snprintf(msg, SH_BUFSIZE, _("I/O error. errno = %ld(stat)"), status);
507 sh_error_handle (ShSuidchkSeverity,
508 FIL__, __LINE__,
509 status,
510 MSG_SUID_QREPORT,
511 msg, tmp );
512 SH_FREE(tmp);
513 SH_FREE(msg);
514 }
515 else
516 {
517 basetmp = sh_util_strdup(fullpath);
518 filetmp = SH_ALLOC(PATH_MAX+1);
519 tmp = sh_util_basename(basetmp);
520
521 (void) sl_snprintf(filetmp, PATH_MAX+1, "%s/%s",
522 DEFAULT_QDIR, tmp);
523 SH_FREE(tmp);
524 SH_FREE(basetmp);
525
526 readFile = open (fullpath, O_RDONLY);
527 if (readFile != -1)
528 writeFile = open (filetmp, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IXUSR);
529
530 if ((readFile == -1) || (writeFile == -1))
531 {
532 status = errno;
533 msg = SH_ALLOC(SH_BUFSIZE);
534 tmp = sh_util_safe_name(fullpath);
535
536 (void) sl_snprintf(msg, SH_BUFSIZE, _("Problem quarantining file. File NOT quarantined. errno = %ld (open)"), status);
537 sh_error_handle (ShSuidchkSeverity,
538 FIL__, __LINE__, status,
539 MSG_SUID_QREPORT,
540 msg, tmp );
541 SH_FREE(tmp);
542 SH_FREE(msg);
543 }
544 else
545 {
546 /* sizeof(buffer) is 1024
547 */
548 while ((count = (int) read (readFile, buffer, sizeof (buffer))) > 0)
549 {
550 if ((int) write (writeFile, buffer, (size_t) count) != count)
551 {
552 status = errno;
553 msg = SH_ALLOC(SH_BUFSIZE);
554 tmp = sh_util_safe_name(fullpath);
555
556 (void) sl_snprintf(msg, SH_BUFSIZE,
557 _("I/O error. errno = %ld (write)"), status);
558 sh_error_handle (ShSuidchkSeverity,
559 FIL__,
560 __LINE__,
561 status,
562 MSG_SUID_QREPORT,
563 msg, tmp );
564 SH_FREE(tmp);
565 SH_FREE(msg);
566 }
567 }
568 }
569
570 (void) close (readFile);
571 (void) fchmod(writeFile, S_IRUSR | S_IWUSR | S_IXUSR);
572 (void) close (writeFile);
573
574 if (do_truncate (fullpath) == -1)
575 {
576 status = errno;
577 msg = SH_ALLOC(SH_BUFSIZE);
578 tmp = sh_util_safe_name(fullpath);
579
580 (void) sl_snprintf(msg, SH_BUFSIZE,
581 _("Problem quarantining file. File NOT quarantined. errno = %ld"),
582 status);
583 sh_error_handle (ShSuidchkSeverity,
584 FIL__, __LINE__, status,
585 MSG_SUID_QREPORT,
586 msg, tmp );
587 SH_FREE(tmp);
588 SH_FREE(msg);
589 }
590 else
591 {
592 tmp = sh_util_basename(fullpath);
593
594 (void) sl_snprintf(filetmp, PATH_MAX+1, "%s/%s.info",
595 DEFAULT_QDIR,
596 tmp);
597
598 SH_FREE(tmp);
599 /*
600 * avoid chmod by setting umask
601 */
602 umask_old = umask (0077);
603 filePtr = fopen (filetmp, "w+");
604
605 /*@-usedef@*/
606 if (filePtr)
607 {
608 fprintf(filePtr,
609 _("File Info:\n filename=%s\n size=%lu\n owner=%s(%d)\n group=%s(%d)\n ctime=%s\n atime=%s\n mtime=%s\n"),
610 fullpath,
611 (unsigned long) theFile->size,
612 theFile->c_owner, (int) theFile->owner,
613 theFile->c_group, (int) theFile->group,
614 timestrc, timestra, timestrm);
615 (void) fclose (filePtr);
616 }
617 /*@+usedef@*/
618 umask (umask_old);
619
620 tmp = sh_util_safe_name(fullpath);
621 sh_error_handle (ShSuidchkSeverity,
622 FIL__,__LINE__,
623 0, MSG_SUID_QREPORT,
624 _("Quarantine method applied"),
625 tmp );
626 SH_FREE(tmp);
627 }
628 SH_FREE(filetmp);
629 }
630 }
631 SH_FREE(dir);
632 return;
633}
634
635static void sh_q_changeperm(const char * fullpath)
636{
637 int caperr;
638 int status;
639 char * msg;
640 char * tmp;
641 struct stat fileInfo;
642 struct stat fileInfo_F;
643 int cperm_status = 0;
644 int file_d = -1;
645 char errbuf[SH_ERRBUF_SIZE];
646
647 if (retry_lstat(FIL__, __LINE__, fullpath, &fileInfo) == -1)
648 {
649 status = errno;
650 msg = SH_ALLOC(SH_BUFSIZE);
651 tmp = sh_util_safe_name(fullpath);
652
653 (void) sl_snprintf(msg, SH_BUFSIZE, _("I/O error. errno = %ld"), status);
654 sh_error_handle (ShSuidchkSeverity,
655 FIL__, __LINE__,
656 status,
657 MSG_SUID_QREPORT, msg,
658 tmp );
659 SH_FREE(tmp);
660 SH_FREE(msg);
661 cperm_status = -1;
662 }
663
664 if (cperm_status == 0)
665 {
666 if (0 != (caperr = sl_get_cap_qdel()))
667 {
668 sh_error_handle((-1), FIL__, __LINE__,
669 caperr, MSG_E_SUBGEN,
670 sh_error_message (caperr, errbuf, sizeof(errbuf)),
671 _("sl_get_cap_qdel"));
672 cperm_status = -1;
673 }
674 }
675
676 if (cperm_status == 0)
677 {
678 file_d = aud_open (FIL__, __LINE__, SL_YESPRIV,
679 fullpath, O_RDONLY, 0);
680 if (-1 == file_d)
681 {
682 status = errno;
683 msg = SH_ALLOC(SH_BUFSIZE);
684 tmp = sh_util_safe_name(fullpath);
685
686 (void) sl_snprintf(msg, SH_BUFSIZE, _("I/O error. errno = %ld"), status);
687 sh_error_handle (ShSuidchkSeverity,
688 FIL__, __LINE__,
689 status,
690 MSG_SUID_QREPORT, msg,
691 tmp );
692 SH_FREE(tmp);
693 SH_FREE(msg);
694 cperm_status = -1;
695 }
696 }
697
698 if (cperm_status == 0)
699 {
700 if (retry_fstat(FIL__, __LINE__, file_d, &fileInfo_F) == -1)
701 {
702 status = errno;
703 msg = SH_ALLOC(SH_BUFSIZE);
704 tmp = sh_util_safe_name(fullpath);
705
706 (void) sl_snprintf(msg, SH_BUFSIZE,
707 _("I/O error. errno = %ld"), status);
708 sh_error_handle (ShSuidchkSeverity,
709 FIL__, __LINE__,
710 status,
711 MSG_SUID_QREPORT, msg,
712 tmp );
713 SH_FREE(tmp);
714 SH_FREE(msg);
715 cperm_status = -1;
716 }
717 }
718
719 if (cperm_status == 0)
720 {
721 if (fileInfo_F.st_ino != fileInfo.st_ino ||
722 fileInfo_F.st_dev != fileInfo.st_dev ||
723 fileInfo_F.st_mode != fileInfo.st_mode)
724 {
725 status = errno;
726 msg = SH_ALLOC(SH_BUFSIZE);
727 tmp = sh_util_safe_name(fullpath);
728
729 (void) sl_snprintf(msg, SH_BUFSIZE,
730 _("Race detected. errno = %ld"), status);
731 sh_error_handle (ShSuidchkSeverity,
732 FIL__, __LINE__,
733 status,
734 MSG_SUID_QREPORT, msg,
735 tmp );
736 SH_FREE(tmp);
737 SH_FREE(msg);
738 cperm_status = -1;
739 }
740 }
741
742 if ((fileInfo.st_mode & S_ISUID) > 0)
743 fileInfo.st_mode -= S_ISUID;
744 if ((fileInfo.st_mode & S_ISGID) > 0)
745 fileInfo.st_mode -= S_ISGID;
746
747 if (cperm_status == 0)
748 {
749 if (fchmod(file_d, fileInfo.st_mode) == -1)
750 {
751 status = errno;
752 msg = SH_ALLOC(SH_BUFSIZE);
753 tmp = sh_util_safe_name(fullpath);
754
755 (void) sl_snprintf(msg, SH_BUFSIZE,
756 _("Problem quarantining file. File NOT quarantined. errno = %ld"),
757 status);
758 sh_error_handle (ShSuidchkSeverity,
759 FIL__, __LINE__,
760 status,
761 MSG_SUID_QREPORT,
762 msg, tmp );
763 SH_FREE(tmp);
764 SH_FREE(msg);
765 }
766 else
767 {
768 tmp = sh_util_safe_name(fullpath);
769 sh_error_handle (ShSuidchkSeverity,
770 FIL__, __LINE__,
771 0,
772 MSG_SUID_QREPORT,
773 _("Quarantine method applied"),
774 tmp );
775 SH_FREE(tmp);
776 }
777 }
778
779 if (0 != (caperr = sl_drop_cap_qdel()))
780 {
781 sh_error_handle((-1), FIL__, __LINE__,
782 caperr, MSG_E_SUBGEN,
783 sh_error_message (caperr, errbuf, sizeof(errbuf)),
784 _("sl_drop_cap_qdel"));
785 }
786
787 if (file_d != -1)
788 {
789 do {
790 status = close (file_d);
791 } while (status == -1 && errno == EINTR);
792
793 if (-1 == status)
794 {
795 status = errno;
796 msg = SH_ALLOC(SH_BUFSIZE);
797 tmp = sh_util_safe_name(fullpath);
798
799 (void) sl_snprintf(msg, SH_BUFSIZE,
800 _("I/O error. errno = %ld"), status);
801 sh_error_handle (ShSuidchkSeverity,
802 FIL__, __LINE__,
803 status,
804 MSG_SUID_QREPORT, msg,
805 tmp );
806 SH_FREE(tmp);
807 SH_FREE(msg);
808 cperm_status = -1;
809 }
810 }
811 return;
812}
813
814static void report_file (const char * tmpcat, file_type * theFile,
815 char * timestrc, char * timestra, char * timestrm)
816{
817 char * msg = SH_ALLOC(SH_BUFSIZE);
818 char * tmp = sh_util_safe_name(tmpcat);
819
820 msg[0] = '\0';
821 /*@-usedef@*/
822
823#ifdef SH_USE_XML
824 (void) sl_snprintf(msg, SH_BUFSIZE, _("owner_new=\"%s\" iowner_new=\"%ld\" group_new=\"%s\" igroup_new=\"%ld\" size_new=\"%lu\" ctime_new=\"%s\" atime_new=\"%s\" mtime_new=\"%s\""),
825 theFile->c_owner, theFile->owner,
826 theFile->c_group, theFile->group,
827 (unsigned long) theFile->size,
828 timestrc, timestra, timestrm);
829#else
830 (void) sl_snprintf(msg, SH_BUFSIZE, _("owner_new=<%s>, iowner_new=<%ld>, group_new=<%s>, igroup_new=<%ld>, filesize=<%lu>, ctime=<%s>, atime=<%s>, mtime=<%s>"),
831 theFile->c_owner, theFile->owner,
832 theFile->c_group, theFile->group,
833 (unsigned long) theFile->size,
834 timestrc, timestra, timestrm);
835#endif
836 /*@+usedef@*/
837
838 sh_error_handle (ShSuidchkSeverity, FIL__, __LINE__,
839 0, MSG_SUID_POLICY,
840 _("suid/sgid file not in database"),
841 tmp, msg );
842 SH_FREE(tmp);
843 SH_FREE(msg);
844 return;
845}
846
847static
848int sh_suidchk_check_internal (char * iname)
849{
850 DIR * thisDir = NULL;
851 struct dirent * thisEntry;
852 char * tmpcat;
853 char * tmp;
854 char timestrc[32];
855 char timestra[32];
856 char timestrm[32];
857 struct stat buf;
858 int status;
859 int fflags;
860 char * fs;
861 long sl_status;
862 file_type * theFile = NULL;
863 char fileHash[2*(KEY_LEN + 1)];
864
865 struct sh_dirent * dirlist;
866 struct sh_dirent * dirlist_orig;
867 char errbuf[SH_ERRBUF_SIZE];
868
869 SL_ENTER(_("sh_suidchk_check_internal"));
870
871 if (iname == NULL)
872 {
873 TPT((0, FIL__, __LINE__ , _("msg=<directory name is NULL>\n")));
874 SL_RETURN( (-1), _("sh_suidchk_check_internal"));
875 }
876
877 if (sig_urgent > 0) {
878 SL_RETURN( (0), _("sh_suidchk_check_internal"));
879 }
880
881 thisDir = opendir (iname);
882
883 if (thisDir == NULL)
884 {
885 status = errno;
886 tmp = sh_util_safe_name(iname);
887 sh_error_handle (ShDFLevel[SH_ERR_T_DIR], FIL__, __LINE__, status,
888 MSG_E_OPENDIR,
889 sh_error_message (status, errbuf, sizeof(errbuf)), tmp);
890 SH_FREE(tmp);
891 SL_RETURN( (-1), _("sh_suidchk_check_internal"));
892 }
893
894 /* Loop over directory entries
895 */
896 SH_MUTEX_LOCK(mutex_readdir);
897
898 dirlist = NULL;
899 dirlist_orig = NULL;
900
901 do {
902
903 thisEntry = readdir (thisDir);
904
905 if (thisEntry != NULL) {
906
907 if (sl_strcmp (thisEntry->d_name, ".") == 0)
908 continue;
909
910 if (sl_strcmp (thisEntry->d_name, "..") == 0)
911 continue;
912
913 dirlist = addto_sh_dirlist (thisEntry, dirlist);
914 }
915
916 } while (thisEntry != NULL);
917
918 SH_MUTEX_UNLOCK(mutex_readdir);
919
920 closedir(thisDir);
921
922 dirlist_orig = dirlist;
923
924 sl_status = SL_ENONE;
925
926 do {
927
928 /* If the directory is empty, dirlist = NULL
929 */
930 if (!dirlist)
931 break;
932
933 if (sig_urgent > 0) {
934 SL_RETURN( (0), _("sh_suidchk_check_internal"));
935 }
936
937 tmpcat = SH_ALLOC(PATH_MAX);
938 (void) sl_strlcpy(tmpcat, iname, PATH_MAX);
939
940 if ((sl_strlen(tmpcat) != sl_strlen(iname)) || (tmpcat[0] == '\0'))
941 {
942 sl_status = SL_ETRUNC;
943 }
944 else
945 {
946 if (tmpcat[1] != '\0')
947 sl_status = sl_strlcat(tmpcat, "/", PATH_MAX);
948 }
949
950 if (! SL_ISERROR(sl_status))
951 sl_status = sl_strlcat(tmpcat, dirlist->sh_d_name, PATH_MAX);
952
953 if (SL_ISERROR(sl_status))
954 {
955 tmp = sh_util_safe_name(tmpcat);
956 sh_error_handle ((-1), FIL__, __LINE__, (int) sl_status,
957 MSG_E_SUBGPATH,
958 _("path too long"),
959 _("sh_suidchk_check_internal"), tmp );
960 SH_FREE(tmp);
961 SH_FREE(tmpcat);
962 dirlist = dirlist->next;
963 continue;
964 }
965
966 ++FileLimNum;
967 ++FileLimTotal;
968
969 /* Rate limit (Fps == Files per second)
970 */
971 if ((ShSuidchkFps > 0 && FileLimNum > ShSuidchkFps && FileLimTotal > 0)&&
972 (ShSuidchkYield == S_FALSE))
973 {
974 FileLimNum = 0;
975 FileLimNow = time(NULL);
976
977 if ( (FileLimNow - FileLimStart) > 0 &&
978 FileLimTotal/(FileLimNow - FileLimStart) > ShSuidchkFps )
979 (void) retry_msleep((int)((FileLimTotal/(FileLimNow-FileLimStart))/
980 ShSuidchkFps) , 0);
981 }
982
983 status = (int) retry_lstat(FIL__, __LINE__, tmpcat, &buf);
984
985 if (status != 0)
986 {
987 status = errno;
988 tmp = sh_util_safe_name(tmpcat);
989 sh_error_handle (SH_ERR_ERR, FIL__, __LINE__, status, MSG_ERR_LSTAT,
990 sh_error_message(status, errbuf, sizeof(errbuf)),
991 tmpcat );
992 SH_FREE(tmp);
993 }
994 else
995 {
996 if (/*@-usedef@*/S_ISDIR(buf.st_mode)/*@+usedef@*/ &&
997 (ShSuidchkExclude == NULL ||
998 0 != strcmp(tmpcat, ShSuidchkExclude)))
999 {
1000 /* fs is a STATIC string or NULL
1001 */
1002 fs = filesystem_type (tmpcat, tmpcat, &buf);
1003 if (fs != NULL
1004#ifndef SH_SUIDTESTDIR
1005 &&
1006 0 != strncmp (_("afs"), fs, 3) &&
1007 0 != strncmp (_("devfs"), fs, 5) &&
1008 0 != strncmp (_("iso9660"), fs, 7) &&
1009 0 != strncmp (_("lustre"), fs, 6) &&
1010 0 != strncmp (_("mmfs"), fs, 4) &&
1011 0 != strncmp (_("msdos"), fs, 5) &&
1012 0 != strncmp (_("nfs"), fs, 3) &&
1013 0 != strncmp (_("proc"), fs, 4) &&
1014 0 != strncmp (_("vfat"), fs, 4)
1015#endif
1016 )
1017 {
1018 if ((ShSuidchkNosuid == S_TRUE) ||
1019 (0 != strncmp (_("nosuid"), fs, 6)))
1020 /* fprintf(stderr, "%s: %s\n", fs, tmpcat); */
1021 (void) sh_suidchk_check_internal(tmpcat);
1022 }
1023 }
1024 else if (S_ISREG(buf.st_mode) &&
1025 (0 !=(S_ISUID & buf.st_mode) ||
1026#if defined(HOST_IS_LINUX)
1027 (0 !=(S_ISGID & buf.st_mode) &&
1028 0 !=(S_IXGRP & buf.st_mode))
1029#else
1030 0 !=(S_ISGID & buf.st_mode)
1031#endif
1032 )
1033 )
1034 {
1035 theFile = SH_ALLOC(sizeof(file_type));
1036
1037 (void) sl_strlcpy (theFile->fullpath, tmpcat, PATH_MAX);
1038 theFile->check_mask = sh_files_maskof(SH_LEVEL_READONLY);
1039 CLEAR_SH_FFLAG_REPORTED(theFile->file_reported);
1040 theFile->attr_string = NULL;
1041 theFile->link_path = NULL;
1042
1043 status = sh_unix_getinfo (ShDFLevel[SH_ERR_T_RO],
1044 dirlist->sh_d_name,
1045 theFile, fileHash, 0);
1046
1047 tmp = sh_util_safe_name(tmpcat);
1048
1049 if (status != 0)
1050 {
1051 sh_error_handle (ShSuidchkSeverity, FIL__, __LINE__,
1052 0, MSG_E_SUBGPATH,
1053 _("Could not check suid/sgid file"),
1054 _("sh_suidchk_check_internal"),
1055 tmp);
1056 }
1057 else
1058 {
1059
1060 if ( sh.flag.update == S_TRUE &&
1061 (sh.flag.checkSum == SH_CHECK_INIT ||
1062 sh.flag.checkSum == SH_CHECK_CHECK))
1063 {
1064 /* Updating database. Report new files that
1065 * are not in database already. Then compare
1066 * to database and report changes.
1067 */
1068 if (-1 == sh_hash_have_it (tmpcat))
1069 {
1070 sh_error_handle ((-1), FIL__, __LINE__,
1071 0, MSG_SUID_FOUND, tmp );
1072 }
1073 else
1074 {
1075 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
1076 0, MSG_SUID_FOUND, tmp );
1077 }
1078
1079 if (0 == sh_hash_compdata (SH_LEVEL_READONLY,
1080 theFile, fileHash,
1081 _("[SuidCheck]"),
1082 ShSuidchkSeverity))
1083 {
1084 sh_hash_pushdata_memory (theFile, fileHash);
1085 }
1086
1087 sh_hash_addflag(tmpcat, SH_FFLAG_SUIDCHK);
1088
1089 }
1090
1091 else if (sh.flag.checkSum == SH_CHECK_INIT &&
1092 sh.flag.update == S_FALSE )
1093 {
1094 /* Running init. Report on files detected.
1095 */
1096 sh_hash_pushdata (theFile, fileHash);
1097 sh_error_handle ((-1), FIL__, __LINE__,
1098 0, MSG_SUID_FOUND, tmp );
1099 }
1100
1101 else if (sh.flag.checkSum == SH_CHECK_CHECK )
1102 {
1103 /* Running file check. Report on new files
1104 * detected, and quarantine them.
1105 */
1106 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
1107 0, MSG_SUID_FOUND, tmp );
1108
1109 fflags = sh_hash_getflags(tmpcat);
1110
1111 if ( (-1 == fflags) || (!SH_FFLAG_SUIDCHK_SET(fflags)))
1112 {
1113 if (-1 == fflags)
1114 {
1115 (void) sh_unix_gmttime (theFile->ctime, timestrc, sizeof(timestrc));
1116 (void) sh_unix_gmttime (theFile->atime, timestra, sizeof(timestra));
1117 (void) sh_unix_gmttime (theFile->mtime, timestrm, sizeof(timestrm));
1118
1119 report_file(tmpcat, theFile, timestrc, timestra, timestrm);
1120 }
1121 /* Quarantine file according to configured method
1122 */
1123 if (ShSuidchkQEnable == S_TRUE)
1124 {
1125 switch (ShSuidchkQMethod)
1126 {
1127 case SH_Q_DELETE:
1128 sh_q_delete(theFile->fullpath);
1129 break;
1130 case SH_Q_CHANGEPERM:
1131 sh_q_changeperm(theFile->fullpath);
1132 break;
1133 case SH_Q_MOVE:
1134 sh_q_move(theFile->fullpath, theFile, timestrc, timestra, timestrm);
1135 break;
1136 default:
1137 sh_error_handle (ShSuidchkSeverity, FIL__,
1138 __LINE__, 0, MSG_SUID_QREPORT,
1139 _("Bad quarantine method"), tmp);
1140 break;
1141 }
1142 }
1143 else
1144 {
1145 /* 1.8.1 push file to in-memory database
1146 */
1147 (void) sh_hash_compdata (SH_LEVEL_READONLY,
1148 theFile, fileHash,
1149 _("[SuidCheck]"),
1150 ShSuidchkSeverity);
1151
1152 sh_hash_addflag(tmpcat, SH_FFLAG_SUIDCHK);
1153
1154 }
1155 }
1156 else
1157 {
1158 /* File exists. Check for modifications.
1159 */
1160 (void) sh_hash_compdata (SH_LEVEL_READONLY,
1161 theFile, fileHash,
1162 _("[SuidCheck]"),
1163 ShSuidchkSeverity);
1164
1165 sh_hash_addflag(tmpcat, SH_FFLAG_SUIDCHK);
1166
1167 }
1168 }
1169 }
1170 SH_FREE(tmp);
1171 if (theFile->attr_string) SH_FREE(theFile->attr_string);
1172 if (theFile->link_path) SH_FREE(theFile->link_path);
1173 SH_FREE(theFile);
1174 }
1175 }
1176 SH_FREE(tmpcat);
1177
1178
1179#ifdef HAVE_SCHED_YIELD
1180 if (ShSuidchkYield == S_TRUE)
1181 {
1182 if (sched_yield() == -1)
1183 {
1184 status = errno;
1185 sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
1186 _("Failed to release time slice"),
1187 _("sh_suidchk_check_internal") );
1188 }
1189 }
1190#endif
1191
1192 dirlist = dirlist->next;
1193
1194 } while (dirlist != NULL);
1195
1196
1197 kill_sh_dirlist (dirlist_orig);
1198
1199 SL_RETURN( (0), _("sh_suidchk_check_internal"));
1200}
1201
1202/*************
1203 *
1204 * module init
1205 *
1206 *************/
1207int sh_suidchk_init (struct mod_type * arg)
1208{
1209 (void) arg;
1210 if (ShSuidchkActive == S_FALSE)
1211 return (-1);
1212
1213 return (0);
1214}
1215
1216
1217/*************
1218 *
1219 * module cleanup
1220 *
1221 *************/
1222int sh_suidchk_end ()
1223{
1224 return (0);
1225}
1226
1227
1228/*************
1229 *
1230 * module timer
1231 *
1232 *************/
1233int sh_suidchk_timer (time_t tcurrent)
1234{
1235 if (sh.flag.checkSum == SH_CHECK_INIT)
1236 return -1;
1237
1238 /* One-shot (not daemon and not loop forever)
1239 */
1240 if (sh.flag.isdaemon != S_TRUE && sh.flag.loop == S_FALSE)
1241 return -1;
1242
1243 if (ShSuidchkSched != NULL)
1244 {
1245 return test_sched(ShSuidchkSched);
1246 }
1247 if ((time_t) (tcurrent - lastcheck) >= ShSuidchkInterval)
1248 {
1249 lastcheck = tcurrent;
1250 return (-1);
1251 }
1252 return 0;
1253}
1254
1255/*************
1256 *
1257 * module check
1258 *
1259 *************/
1260
1261int sh_suidchk_check ()
1262{
1263 int status;
1264
1265 SL_ENTER(_("sh_suidchk_check"));
1266
1267 sh_error_handle (SH_ERR_NOTICE, FIL__, __LINE__, EINVAL, MSG_E_SUBGEN,
1268 _("Checking for SUID programs"),
1269 _("suidchk_check") );
1270
1271 FileLimNow = time(NULL);
1272 FileLimStart = FileLimNow;
1273 FileLimNum = 0;
1274 FileLimTotal = 0;
1275
1276#ifdef SH_SUIDTESTDIR
1277 status = sh_suidchk_check_internal (SH_SUIDTESTDIR);
1278#else
1279 status = sh_suidchk_check_internal ("/");
1280#endif
1281
1282 sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_SUID_SUMMARY,
1283 FileLimTotal,
1284 (long) (time(NULL) - FileLimStart) );
1285
1286 SL_RETURN(status, _("sh_suidchk_check"));
1287}
1288
1289/*************
1290 *
1291 * module setup
1292 *
1293 *************/
1294
1295int sh_suidchk_set_severity (const char * c)
1296{
1297 int retval;
1298 char tmp[32];
1299
1300 SL_ENTER(_("sh_suidchk_set_severity"));
1301 tmp[0] = '='; tmp[1] = '\0';
1302 (void) sl_strlcat (tmp, c, 32);
1303 retval = sh_error_set_level (tmp, &ShSuidchkSeverity);
1304 SL_RETURN(retval, _("sh_suidchk_set_severity"));
1305}
1306
1307int sh_suidchk_set_exclude (const char * c)
1308{
1309 SL_ENTER(_("sh_suidchk_set_exclude"));
1310
1311 if (c == NULL || c[0] == '\0')
1312 {
1313 SL_RETURN(-1, _("sh_suidchk_set_exclude"));
1314 }
1315
1316 if (0 == sl_strncmp(c, _("NULL"), 4))
1317 {
1318 if (ShSuidchkExclude != NULL)
1319 SH_FREE(ShSuidchkExclude);
1320 ShSuidchkExclude = NULL;
1321 SL_RETURN(0, _("sh_suidchk_set_exclude"));
1322 }
1323
1324 if (ShSuidchkExclude != NULL)
1325 SH_FREE(ShSuidchkExclude);
1326
1327 ShSuidchkExclude = sh_util_strdup (c);
1328 ExcludeLen = sl_strlen (ShSuidchkExclude);
1329 if (ShSuidchkExclude[ExcludeLen-1] == '/')
1330 {
1331 ShSuidchkExclude[ExcludeLen-1] = '\0';
1332 ExcludeLen--;
1333 }
1334 SL_RETURN(0, _("sh_suidchk_set_exclude"));
1335}
1336
1337int sh_suidchk_set_timer (const char * c)
1338{
1339 long val;
1340
1341 SL_ENTER(_("sh_suidchk_set_timer"));
1342
1343 val = strtol (c, (char **)NULL, 10);
1344 if (val <= 0)
1345 sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
1346 _("suidchk timer"), c);
1347
1348 val = (val <= 0 ? 7200 : val);
1349
1350 ShSuidchkInterval = (time_t) val;
1351 SL_RETURN( 0, _("sh_suidchk_set_timer"));
1352}
1353
1354
1355static void sh_suidchk_free_schedule (void)
1356{
1357 sh_schedule_t * current = ShSuidchkSched;
1358 sh_schedule_t * next = NULL;
1359
1360 while (current != NULL)
1361 {
1362 next = current->next;
1363 SH_FREE(current);
1364 current = next;
1365 }
1366 ShSuidchkSched = NULL;
1367 return;
1368}
1369
1370int sh_suidchk_reconf ()
1371{
1372 sh_suidchk_free_schedule();
1373 set_defaults();
1374 return 0;
1375}
1376
1377int sh_suidchk_set_schedule (const char * str)
1378{
1379 int status;
1380 sh_schedule_t * newSched = NULL;
1381
1382 SL_ENTER(_("sh_suidchk_set_schedule"));
1383
1384 /*
1385 if (ShSuidchkSched != NULL)
1386 {
1387 SH_FREE(ShSuidchkSched);
1388 ShSuidchkSched = NULL;
1389 }
1390 */
1391
1392 if (0 == sl_strncmp(str, _("NULL"), 4))
1393 {
1394 (void) sh_suidchk_free_schedule ();
1395 return 0;
1396 }
1397
1398 newSched = SH_ALLOC(sizeof(sh_schedule_t));
1399 status = create_sched(str, newSched);
1400 if (status != 0)
1401 {
1402 SH_FREE(newSched);
1403 newSched = NULL;
1404 }
1405 else
1406 {
1407 newSched->next = ShSuidchkSched;
1408 ShSuidchkSched = newSched;
1409 }
1410 SL_RETURN( status, _("sh_suidchk_set_schedule"));
1411}
1412
1413
1414
1415int sh_suidchk_set_fps (const char * c)
1416{
1417 long val;
1418
1419 SL_ENTER(_("sh_suidchk_set_fps"));
1420
1421 val = strtol (c, (char **)NULL, 10);
1422 if (val < 0)
1423 sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
1424 _("suidchk fps"), c);
1425
1426 val = (val < 0 ? 0 : val);
1427
1428 ShSuidchkFps = val;
1429 SL_RETURN( 0, _("sh_suidchk_set_fps"));
1430}
1431
1432int sh_suidchk_set_yield (const char * c)
1433{
1434 int i;
1435 SL_ENTER(_("sh_suidchk_set_yield"));
1436#ifdef HAVE_SCHED_YIELD
1437 i = sh_util_flagval(c, &ShSuidchkYield);
1438#else
1439 (void) c; /* cast to void to avoid compiler warning */
1440 i = -1;
1441#endif
1442 SL_RETURN(i, _("sh_suidchk_set_yield"));
1443}
1444
1445int sh_suidchk_set_activate (const char * c)
1446{
1447 int i;
1448 SL_ENTER(_("sh_suidchk_set_activate"));
1449 i = sh_util_flagval(c, &ShSuidchkActive);
1450 SL_RETURN(i, _("sh_suidchk_set_activate"));
1451}
1452
1453int sh_suidchk_set_nosuid (const char * c)
1454{
1455 int i;
1456 SL_ENTER(_("sh_suidchk_set_nosuid"));
1457 i = sh_util_flagval(c, &ShSuidchkNosuid);
1458 SL_RETURN(i, _("sh_suidchk_set_nosuid"));
1459}
1460
1461int sh_suidchk_set_quarantine (const char * c)
1462{
1463 int i;
1464 SL_ENTER(_("sh_suidchk_set_quarantine"));
1465 i = sh_util_flagval(c, &ShSuidchkQEnable);
1466 SL_RETURN(i, _("sh_suidchk_set_quarantine"));
1467}
1468
1469int sh_suidchk_set_qdelete (const char * c)
1470{
1471 int i;
1472 SL_ENTER(_("sh_suidchk_set_qdelete"));
1473 i = sh_util_flagval(c, &ShSuidchkQDelete);
1474 SL_RETURN(i, _("sh_suidchk_set_qdelete"));
1475}
1476
1477int sh_suidchk_set_qmethod (const char * c)
1478{
1479 long val;
1480 int ret = 0;
1481 struct stat buf;
1482
1483 SL_ENTER(_("sh_suidchk_set_qmethod"));
1484
1485 val = strtol (c, (char **)NULL, 10);
1486 if (val < 0)
1487 {
1488 sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
1489 _("suidchk qmethod"), c);
1490 ret = -1;
1491 }
1492 else
1493 {
1494 switch (val)
1495 {
1496 case SH_Q_DELETE:
1497 ShSuidchkQMethod = SH_Q_DELETE;
1498 break;
1499 case SH_Q_CHANGEPERM:
1500 ShSuidchkQMethod = SH_Q_CHANGEPERM;
1501 break;
1502 case SH_Q_MOVE:
1503 if (retry_stat (FIL__, __LINE__, DEFAULT_QDIR, &buf) != 0)
1504 {
1505 if (mkdir (DEFAULT_QDIR, 0750) == -1)
1506 {
1507 sh_error_handle ((-1), FIL__, __LINE__, EINVAL,
1508 MSG_SUID_ERROR,
1509 _("Unable to create quarantine directory"));
1510 }
1511 }
1512 ShSuidchkQMethod = SH_Q_MOVE;
1513 break;
1514 default:
1515 sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
1516 _("suidchk qmethod"), c);
1517 ShSuidchkQMethod = -1;
1518 ret = -1;
1519 break;
1520 }
1521 }
1522
1523 SL_RETURN( ret, _("sh_suidchk_set_qmethod"));
1524}
1525
1526#if defined(FSTYPE_STATFS) || defined(FSTYPE_AIX_STATFS)
1527/* dirname.c -- return all but the last element in a path
1528 Copyright (C) 1990 Free Software Foundation, Inc.
1529
1530 This program is free software; you can redistribute it and/or modify
1531 it under the terms of the GNU General Public License as published by
1532 the Free Software Foundation; either version 2, or (at your option)
1533 any later version.
1534
1535 This program is distributed in the hope that it will be useful,
1536 but WITHOUT ANY WARRANTY; without even the implied warranty of
1537 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1538 GNU General Public License for more details.
1539
1540 You should have received a copy of the GNU General Public License
1541 along with this program; if not, write to the Free Software Foundation,
1542 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
1543
1544/* Return the leading directories part of PATH,
1545 allocated with malloc. If out of memory, return 0.
1546 Assumes that trailing slashes have already been
1547 removed. */
1548
1549char * sh_dirname (const char * path)
1550{
1551 char *newpath;
1552 char *slash;
1553 int length; /* Length of result, not including NUL. */
1554
1555 slash = strrchr (path, '/');
1556 if (slash == NULL)
1557 {
1558 /* File is in the current directory. */
1559 path = ".";
1560 length = 1;
1561 }
1562 else
1563 {
1564 /* Remove any trailing slashes from the result. */
1565 while (slash > path && *slash == '/')
1566 --slash;
1567
1568 length = slash - path + 1;
1569 }
1570 newpath = (char *) SH_ALLOC (length + 1);
1571 if (newpath == NULL)
1572 return NULL;
1573 strncpy (newpath, path, length);
1574 newpath[length] = '\0';
1575 return newpath;
1576}
1577/* #ifdef FSTYPE_STATFS */
1578#endif
1579
1580/* fstype.c -- determine type of filesystems that files are on
1581 Copyright (C) 1990, 91, 92, 93, 94 Free Software Foundation, Inc.
1582
1583 This program is free software; you can redistribute it and/or modify
1584 it under the terms of the GNU General Public License as published by
1585 the Free Software Foundation; either version 2, or (at your option)
1586 any later version.
1587
1588 This program is distributed in the hope that it will be useful,
1589 but WITHOUT ANY WARRANTY; without even the implied warranty of
1590 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1591 GNU General Public License for more details.
1592
1593 You should have received a copy of the GNU General Public License
1594 along with this program; if not, write to the Free Software
1595 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
1596
1597/* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
1598
1599/* Modified by R. Wichmann:
1600 - replaced error() by sh_error_handle()
1601 - replaced xstrdup() by sl_strdup()
1602 - replaced strstr() by sl_strstr()
1603 - some additions to recognize nosuid fs
1604*/
1605
1606/* modetype.h -- file type bits definitions for POSIX systems
1607 Requires sys/types.h sys/stat.h.
1608 Copyright (C) 1990 Free Software Foundation, Inc.
1609
1610 This program is free software; you can redistribute it and/or modify
1611 it under the terms of the GNU General Public License as published by
1612 the Free Software Foundation; either version 2, or (at your option)
1613 any later version.
1614
1615 This program is distributed in the hope that it will be useful,
1616 but WITHOUT ANY WARRANTY; without even the implied warranty of
1617 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1618 GNU General Public License for more details.
1619
1620 You should have received a copy of the GNU General Public License
1621 along with this program; if not, write to the Free Software
1622 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
1623
1624/* POSIX.1 doesn't mention the S_IFMT bits; instead, it uses S_IStype
1625 test macros. To make storing file types more convenient, define
1626 them; the values don't need to correspond to what the kernel uses,
1627 because of the way we use them. */
1628#ifndef S_IFMT /* Doesn't have traditional Unix macros. */
1629#define S_IFBLK 1
1630#define S_IFCHR 2
1631#define S_IFDIR 4
1632#define S_IFREG 8
1633#ifdef S_ISLNK
1634#define S_IFLNK 16
1635#endif
1636#ifdef S_ISFIFO
1637#define S_IFIFO 32
1638#endif
1639#ifdef S_ISSOCK
1640#define S_IFSOCK 64
1641#endif
1642#endif /* !S_IFMT */
1643
1644#ifdef STAT_MACROS_BROKEN
1645#undef S_ISBLK
1646#undef S_ISCHR
1647#undef S_ISDIR
1648#undef S_ISREG
1649#undef S_ISFIFO
1650#undef S_ISLNK
1651#undef S_ISSOCK
1652#undef S_ISMPB
1653#undef S_ISMPC
1654#undef S_ISNWK
1655#endif
1656
1657/* Do the reverse: define the POSIX.1 macros for traditional Unix systems
1658 that don't have them. */
1659#if !defined(S_ISBLK) && defined(S_IFBLK)
1660#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
1661#endif
1662#if !defined(S_ISCHR) && defined(S_IFCHR)
1663#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
1664#endif
1665#if !defined(S_ISDIR) && defined(S_IFDIR)
1666#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
1667#endif
1668#if !defined(S_ISREG) && defined(S_IFREG)
1669#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
1670#endif
1671#if !defined(S_ISFIFO) && defined(S_IFIFO)
1672#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
1673#endif
1674#if !defined(S_ISLNK) && defined(S_IFLNK)
1675#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
1676#endif
1677#if !defined(S_ISSOCK) && defined(S_IFSOCK)
1678#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
1679#endif
1680#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */
1681#define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB)
1682#define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC)
1683#endif
1684#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */
1685#define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK)
1686#endif
1687
1688
1689static char *filesystem_type_uncached (char *path, char *relpath,
1690 struct stat *statp);
1691
1692#ifdef FSTYPE_MNTENT /* 4.3BSD etc. */
1693static int xatoi (const char *cp);
1694#endif
1695
1696#ifdef FSTYPE_MNTENT /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
1697#include <mntent.h>
1698#if !defined(MOUNTED)
1699# if defined(MNT_MNTTAB) /* HP-UX. */
1700# define MOUNTED MNT_MNTTAB
1701# endif
1702# if defined(MNTTABNAME) /* Dynix. */
1703# define MOUNTED MNTTABNAME
1704# endif
1705#endif
1706#endif
1707
1708#ifdef FSTYPE_GETMNT /* Ultrix. */
1709#include <sys/param.h>
1710#include <sys/mount.h>
1711#include <sys/fs_types.h>
1712#endif
1713
1714#ifdef FSTYPE_USG_STATFS /* SVR3. */
1715#include <sys/statfs.h>
1716#include <sys/fstyp.h>
1717#endif
1718
1719#ifdef FSTYPE_STATVFS /* SVR4. */
1720#include <sys/statvfs.h>
1721#include <sys/fstyp.h>
1722#endif
1723
1724#ifdef FSTYPE_STATFS /* 4.4BSD. */
1725#include <sys/param.h> /* NetBSD needs this. */
1726#include <sys/mount.h>
1727
1728#ifndef MFSNAMELEN /* NetBSD defines this. */
1729static char *
1730fstype_to_string (t)
1731 short t;
1732{
1733#ifdef INITMOUNTNAMES /* Defined in 4.4BSD, not in NET/2. */
1734 static char *mn[] = INITMOUNTNAMES;
1735 if (t >= 0 && t <= MOUNT_MAXTYPE)
1736 return mn[t];
1737 else
1738 return "?";
1739#else /* !INITMOUNTNAMES */
1740 switch (t)
1741 {
1742 case MOUNT_UFS:
1743 return _("ufs");
1744 case MOUNT_NFS:
1745 return _("nfs");
1746#ifdef MOUNT_PC
1747 case MOUNT_PC:
1748 return _("pc");
1749#endif
1750#ifdef MOUNT_MFS
1751 case MOUNT_MFS:
1752 return _("mfs");
1753#endif
1754#ifdef MOUNT_LO
1755 case MOUNT_LO:
1756 return _("lofs");
1757#endif
1758#ifdef MOUNT_TFS
1759 case MOUNT_TFS:
1760 return _("tfs");
1761#endif
1762#ifdef MOUNT_TMP
1763 case MOUNT_TMP:
1764 return _("tmp");
1765#endif
1766#ifdef MOUNT_MSDOS
1767 case MOUNT_MSDOS:
1768 return _("msdos");
1769#endif
1770#ifdef MOUNT_ISO9660
1771 case MOUNT_ISO9660:
1772 return _("iso9660fs");
1773#endif
1774 default:
1775 return "?";
1776 }
1777#endif /* !INITMOUNTNAMES */
1778}
1779#endif /* !MFSNAMELEN */
1780#endif /* FSTYPE_STATFS */
1781
1782#ifdef FSTYPE_AIX_STATFS /* AIX. */
1783#include <sys/vmount.h>
1784#include <sys/statfs.h>
1785
1786#define FSTYPE_STATFS /* Otherwise like 4.4BSD. */
1787#define f_type f_vfstype
1788
1789static char *
1790fstype_to_string (t)
1791 short t;
1792{
1793 switch (t)
1794 {
1795 case MNT_AIX:
1796 return _("aix"); /* AIX 4.3: NFS filesystems are actually MNT_AIX. */
1797#ifdef MNT_NAMEFS
1798 case MNT_NAMEFS:
1799 return _("namefs");
1800#endif
1801 case MNT_NFS:
1802 return _("nfs");
1803 case MNT_JFS:
1804 return _("jfs");
1805 case MNT_CDROM:
1806 return _("cdrom");
1807#ifdef MNT_PROCFS
1808 case MNT_PROCFS:
1809 return _("procfs");
1810#endif
1811#ifdef MNT_SFS
1812 case MNT_SFS:
1813 return _("sfs");
1814#endif
1815#ifdef MNT_CACHEFS
1816 case MNT_CACHEFS:
1817 return _("cachefs");
1818#endif
1819#ifdef MNT_NFS3
1820 case MNT_NFS3:
1821 return _("nfs3");
1822#endif
1823#ifdef MNT_AUTOFS
1824 case MNT_AUTOFS:
1825 return _("autofs");
1826#endif
1827#ifdef MNT_VXFS
1828 case MNT_VXFS:
1829 return _("vxfs");
1830#endif
1831#ifdef MNT_VXODM
1832 case MNT_VXODM:
1833 return _("veritasfs");
1834#endif
1835#ifdef MNT_UDF
1836 case MNT_UDF:
1837 return _("udfs");
1838#endif
1839#ifdef MNT_NFS4
1840 case MNT_NFS4:
1841 return _("nfs4");
1842#endif
1843#ifdef MNT_RFS4
1844 case MNT_RFS4:
1845 return _("nfs4");
1846#endif
1847#ifdef MNT_CIFS
1848 case MNT_CIFS:
1849 return _("cifs");
1850#endif
1851 default:
1852 return "?";
1853 }
1854}
1855#endif /* FSTYPE_AIX_STATFS */
1856
1857#ifdef AFS
1858#include <netinet/in.h>
1859#include <afs/venus.h>
1860#if __STDC__
1861/* On SunOS 4, afs/vice.h defines this to rely on a pre-ANSI cpp. */
1862#undef _VICEIOCTL
1863#define _VICEIOCTL(id) ((unsigned int ) _IOW('V', id, struct ViceIoctl))
1864#endif
1865#ifndef _IOW
1866/* AFS on Solaris 2.3 doesn't get this definition. */
1867#include <sys/ioccom.h>
1868#endif
1869
1870static int
1871in_afs (path)
1872 char *path;
1873{
1874 static char space[2048];
1875 struct ViceIoctl vi;
1876
1877 vi.in_size = 0;
1878 vi.out_size = sizeof (space);
1879 vi.out = space;
1880
1881 if (pioctl (path, VIOC_FILE_CELL_NAME, &vi, 1)
1882 && (errno == EINVAL || errno == ENOENT))
1883 return 0;
1884 return 1;
1885}
1886#endif /* AFS */
1887
1888/* Nonzero if the current filesystem's type is known. */
1889static int fstype_known = 0;
1890
1891/* Return a static string naming the type of filesystem that the file PATH,
1892 described by STATP, is on.
1893 RELPATH is the file name relative to the current directory.
1894 Return "unknown" if its filesystem type is unknown. */
1895
1896static char *
1897filesystem_type (char * path, char * relpath, struct stat * statp)
1898{
1899 static char *current_fstype = NULL;
1900 static dev_t current_dev;
1901
1902 if (current_fstype != NULL)
1903 {
1904 if ((0 != fstype_known) && statp->st_dev == current_dev)
1905 return current_fstype; /* Cached value. */
1906 SH_FREE (current_fstype);
1907 }
1908 current_dev = statp->st_dev;
1909 current_fstype = filesystem_type_uncached (path, relpath, statp);
1910 return current_fstype;
1911}
1912
1913/* Return a newly allocated string naming the type of filesystem that the
1914 file PATH, described by STATP, is on.
1915 RELPATH is the file name relative to the current directory.
1916 Return "unknown" if its filesystem type is unknown. */
1917
1918static char *
1919filesystem_type_uncached (path, relpath, statp)
1920 char *path;
1921 char *relpath;
1922 struct stat *statp;
1923{
1924 char * type = NULL;
1925#ifdef MFSNAMELEN /* NetBSD. */
1926 static char my_tmp_type[64];
1927#endif
1928
1929#ifdef FSTYPE_MNTENT /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
1930 char *table = MOUNTED;
1931 FILE *mfp;
1932 struct mntent *mnt;
1933
1934 if (path == NULL || relpath == NULL)
1935 return NULL;
1936
1937 mfp = setmntent (table, "r");
1938 if (mfp == NULL)
1939 {
1940 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
1941 _("setmntent() failed"),
1942 _("filesystem_type_uncached") );
1943 return NULL;
1944 }
1945
1946 /* Find the entry with the same device number as STATP, and return
1947 that entry's fstype. */
1948 while (type == NULL && (mnt = getmntent (mfp)) != NULL)
1949 {
1950 const char *devopt;
1951 dev_t dev;
1952 struct stat disk_stats;
1953
1954#ifdef MNTTYPE_IGNORE
1955 if (0 == strcmp (mnt->mnt_type, MNTTYPE_IGNORE))
1956 continue;
1957#endif
1958
1959 /* Newer systems like SunOS 4.1 keep the dev number in the mtab,
1960 in the options string. For older systems, we need to stat the
1961 directory that the filesystem is mounted on to get it.
1962
1963 Unfortunately, the HPUX 9.x mnttab entries created by automountq
1964 contain a dev= option but the option value does not match the
1965 st_dev value of the file (maybe the lower 16 bits match?). */
1966
1967#if !defined(hpux) && !defined(__hpux__)
1968 devopt = sl_strstr (mnt->mnt_opts, "dev=");
1969 if (devopt)
1970 {
1971 if (devopt[4] == '0' && (devopt[5] == 'x' || devopt[5] == 'X'))
1972 dev = (dev_t) xatoi (devopt + 6);
1973 else
1974 dev = (dev_t) xatoi (devopt + 4);
1975 }
1976 else
1977#endif /* not hpux */
1978 {
1979 if (stat (mnt->mnt_dir, &disk_stats) == -1)
1980 {
1981 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
1982 _("stat() failed"),
1983 _("filesystem_type_uncached") );
1984 return NULL;
1985 }
1986 dev = disk_stats.st_dev;
1987 }
1988
1989 if (dev == statp->st_dev)
1990 {
1991 /* check for the "nosuid" option
1992 */
1993#ifdef HAVE_HASMNTOPT
1994 if (NULL == hasmntopt(mnt, "nosuid") || (ShSuidchkNosuid == S_TRUE))
1995 type = mnt->mnt_type;
1996 else
1997 type = _("nosuid"); /* hasmntopt (nosuid) */
1998#else
1999 type = mnt->mnt_type;
2000#endif
2001 }
2002 }
2003
2004 if (endmntent (mfp) == 0)
2005 {
2006 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
2007 _("endmntent() failed"),
2008 _("filesystem_type_uncached") );
2009 }
2010#endif
2011
2012#ifdef FSTYPE_GETMNT /* Ultrix. */
2013 int offset = 0;
2014 struct fs_data fsd;
2015
2016 if (path == NULL || relpath == NULL)
2017 return NULL;
2018
2019 while (type == NULL
2020 && getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY, 0) > 0)
2021 {
2022 if (fsd.fd_req.dev == statp->st_dev)
2023 type = gt_names[fsd.fd_req.fstype];
2024 }
2025#endif
2026
2027#ifdef FSTYPE_USG_STATFS /* SVR3. */
2028 struct statfs fss;
2029 char typebuf[FSTYPSZ];
2030
2031 if (path == NULL || relpath == NULL)
2032 return NULL;
2033
2034 if (statfs (relpath, &fss, sizeof (struct statfs), 0) == -1)
2035 {
2036 /* Don't die if a file was just removed. */
2037 if (errno != ENOENT)
2038 {
2039 sh_error_handle ((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
2040 _("statfs() failed"),
2041 _("filesystem_type_uncached") );
2042 return NULL;
2043 }
2044 }
2045 else if (!sysfs (GETFSTYP, fss.f_fstyp, typebuf))
2046 type = typebuf;
2047#endif
2048
2049#ifdef FSTYPE_STATVFS /* SVR4. */
2050 struct statvfs fss;
2051
2052 if (path == NULL || relpath == NULL)
2053 return NULL;
2054
2055 if (statvfs (relpath, &fss) == -1)
2056 {
2057 /* Don't die if a file was just removed. */
2058 if (errno != ENOENT)
2059 {
2060 sh_error_handle ((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
2061 _("statvfs() failed"),
2062 _("filesystem_type_uncached") );
2063 return NULL;
2064 }
2065 }
2066 else
2067 {
2068 type = fss.f_basetype;
2069
2070 /* patch by Konstantin Khrooschev <nathoo@co.ru>
2071 */
2072 if( (fss.f_flag & ST_NOSUID) && (ShSuidchkNosuid == S_FALSE))
2073 type = _("nosuid");
2074 }
2075 (void) statp; /* fix compiler warning */
2076#endif
2077
2078#ifdef FSTYPE_STATFS /* 4.4BSD. */
2079 struct statfs fss;
2080 char *p;
2081#if defined(MNT_VISFLAGMASK) && defined(HAVE_STRUCT_STATFS_F_FLAGS)
2082 int flags;
2083#endif
2084 /* char * sh_dirname(const char *path); */
2085
2086 if (path == NULL || relpath == NULL)
2087 return NULL;
2088
2089 if (S_ISLNK (statp->st_mode))
2090 p = sh_dirname (relpath);
2091 else
2092 p = relpath;
2093
2094 if (statfs (p, &fss) == -1)
2095 {
2096 /* Don't die if symlink to nonexisting file, or a file that was
2097 just removed. */
2098 if (errno != ENOENT)
2099 {
2100 sh_error_handle ((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
2101 _("statfs() failed"),
2102 _("filesystem_type_uncached") );
2103 return NULL;
2104 }
2105 }
2106 else
2107 {
2108
2109#ifdef MFSNAMELEN /* NetBSD. */
2110 /* MEMORY LEAK !!!
2111 * type = sh_util_strdup (fss.f_fstypename);
2112 */
2113 sl_strlcpy (my_tmp_type, fss.f_fstypename, 64);
2114 type = my_tmp_type;
2115#else
2116 type = fstype_to_string (fss.f_type);
2117#endif
2118
2119#ifdef HAVE_STRUCT_STATFS_F_FLAGS
2120#ifdef MNT_VISFLAGMASK
2121 flags = fss.f_flags & MNT_VISFLAGMASK;
2122 if ((flags & MNT_NOSUID) && (ShSuidchkNosuid == S_FALSE))
2123#else
2124 if ((fss.f_flags & MNT_NOSUID) && (ShSuidchkNosuid == S_FALSE))
2125#endif
2126 type = _("nosuid");
2127#endif
2128 }
2129 if (p != relpath)
2130 SH_FREE (p);
2131#endif
2132
2133#ifdef AFS
2134 if ((!type || !strcmp (type, "xx")) && in_afs (relpath))
2135 type = "afs";
2136#endif
2137
2138 /* An unknown value can be caused by an ENOENT error condition.
2139 Don't cache those values. */
2140 fstype_known = (int)(type != NULL);
2141
2142 return sh_util_strdup (type ? type : "unknown");
2143}
2144
2145#ifdef FSTYPE_MNTENT /* 4.3BSD etc. */
2146/* Return the value of the hexadecimal number represented by CP.
2147 No prefix (like '0x') or suffix (like 'h') is expected to be
2148 part of CP. */
2149
2150static int
2151xatoi (cp)
2152 const char *cp;
2153{
2154 int val;
2155
2156 val = 0;
2157 while (*cp != '\0')
2158 {
2159 /*@+charint@*/
2160 if (*cp >= 'a' && *cp <= 'f')
2161 val = val * 16 + *cp - 'a' + 10;
2162 else if (*cp >= 'A' && *cp <= 'F')
2163 val = val * 16 + *cp - 'A' + 10;
2164 else if (*cp >= '0' && *cp <= '9')
2165 val = val * 16 + *cp - '0';
2166 else
2167 break;
2168 /*@-charint@*/
2169 cp++;
2170 }
2171 return val;
2172}
2173#endif
2174
2175
2176
2177#endif
2178
2179
2180/* #ifdef SH_USE_UTMP */
2181#endif
2182
2183
2184
Note: See TracBrowser for help on using the repository browser.