source: trunk/src/sh_suidchk.c@ 375

Last change on this file since 375 was 370, checked in by katerina, 13 years ago

Fix for ticket #271 (conflict between prelink and suid check).

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