source: trunk/src/sh_suidchk.c@ 134

Last change on this file since 134 was 132, checked in by rainer, 17 years ago

Make utility functions thread-safe.

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