source: trunk/src/sh_suidchk.c@ 128

Last change on this file since 128 was 119, checked in by rainer, 17 years ago

Fix for ticket #75 (option to run suid check on nosuid file systems).

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