source: trunk/src/sh_suidchk.c@ 118

Last change on this file since 118 was 115, checked in by rainer, 17 years ago

Fix the problem that new suid/sgid file found by the file system check already will not be quarantined in the suid check (ticket #71).

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