source: trunk/src/sh_suidchk.c@ 321

Last change on this file since 321 was 317, checked in by katerina, 14 years ago

Further refinements for lstat in subprocess (ticket #236).

File size: 62.5 KB
Line 
1/* SAMHAIN file system integrity testing */
2/* Copyright (C) 2001 Rainer Wichmann */
3/* */
4/* This program is free software; you can redistribute it */
5/* and/or modify */
6/* it under the terms of the GNU General Public License as */
7/* published by */
8/* the Free Software Foundation; either version 2 of the License, or */
9/* (at your option) any later version. */
10/* */
11/* This program is distributed in the hope that it will be useful, */
12/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14/* GNU General Public License for more details. */
15/* */
16/* You should have received a copy of the GNU General Public License */
17/* along with this program; if not, write to the Free Software */
18/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20#include "config_xor.h"
21
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/types.h>
27#include <sys/stat.h>
28#include <fcntl.h>
29#include <unistd.h>
30#include <errno.h>
31#include <limits.h>
32
33#ifdef HAVE_SCHED_H
34#include <sched.h>
35#endif
36
37#ifdef SH_USE_SUIDCHK
38
39#undef FIL__
40#define FIL__ _("sh_suidchk.c")
41
42#if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE)
43
44#if TIME_WITH_SYS_TIME
45#include <sys/time.h>
46#include <time.h>
47#else
48#if HAVE_SYS_TIME_H
49#include <sys/time.h>
50#else
51#include <time.h>
52#endif
53#endif
54
55#ifdef HAVE_DIRENT_H
56#include <dirent.h>
57#define NAMLEN(dirent) sl_strlen((dirent)->d_name)
58#else
59#define dirent direct
60#define NAMLEN(dirent) (dirent)->d_namlen
61#ifdef HAVE_SYS_NDIR_H
62#include <sys/ndir.h>
63#endif
64#ifdef HAVE_SYS_DIR_H
65#include <sys/dir.h>
66#endif
67#ifdef HAVE_NDIR_H
68#include <ndir.h>
69#endif
70#endif
71#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 theFile = SH_ALLOC(sizeof(file_type));
1189
1190 (void) sl_strlcpy (theFile->fullpath, tmpcat, PATH_MAX);
1191 theFile->check_mask = sh_files_maskof(SH_LEVEL_READONLY);
1192 CLEAR_SH_FFLAG_REPORTED(theFile->file_reported);
1193 theFile->attr_string = NULL;
1194 theFile->link_path = NULL;
1195
1196 status = sh_unix_getinfo (ShDFLevel[SH_ERR_T_RO],
1197 dirlist->sh_d_name,
1198 theFile, fileHash, 0);
1199
1200 tmp = sh_util_safe_name(tmpcat);
1201
1202 if (status != 0)
1203 {
1204 SH_MUTEX_LOCK(mutex_thread_nolog);
1205 sh_error_handle (ShSuidchkSeverity, FIL__, __LINE__,
1206 0, MSG_E_SUBGPATH,
1207 _("Could not check suid/sgid file"),
1208 _("sh_suidchk_check_internal"),
1209 tmp);
1210 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1211 }
1212 else
1213 {
1214
1215 if ( sh.flag.update == S_TRUE &&
1216 (sh.flag.checkSum == SH_CHECK_INIT ||
1217 sh.flag.checkSum == SH_CHECK_CHECK))
1218 {
1219 int compret;
1220
1221 /* Updating database. Report new files that
1222 * are not in database already. Then compare
1223 * to database and report changes.
1224 */
1225 if (-1 == sh_hash_have_it (tmpcat))
1226 {
1227 SH_MUTEX_LOCK(mutex_thread_nolog);
1228 sh_error_handle ((-1), FIL__, __LINE__,
1229 0, MSG_SUID_FOUND, tmp );
1230 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1231 }
1232 else
1233 {
1234 SH_MUTEX_LOCK(mutex_thread_nolog);
1235 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
1236 0, MSG_SUID_FOUND, tmp );
1237 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1238 }
1239
1240 SH_MUTEX_LOCK(mutex_thread_nolog);
1241 compret = sh_hash_compdata (SH_LEVEL_READONLY,
1242 theFile, fileHash,
1243 _("[SuidCheck]"),
1244 ShSuidchkSeverity);
1245 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1246
1247 if (compret == 0)
1248 {
1249 sh_hash_pushdata_memory (theFile, fileHash); /* no call to sh_error_handle */
1250 }
1251
1252 sh_hash_addflag(tmpcat, SH_FFLAG_SUIDCHK); /* no call to sh_error_handle */
1253
1254 }
1255
1256 else if (sh.flag.checkSum == SH_CHECK_INIT &&
1257 sh.flag.update == S_FALSE )
1258 {
1259 /* Running init. Report on files detected.
1260 */
1261 sh_hash_pushdata (theFile, fileHash); /* no call to sh_error_handle */
1262 SH_MUTEX_LOCK(mutex_thread_nolog);
1263 sh_error_handle ((-1), FIL__, __LINE__,
1264 0, MSG_SUID_FOUND, tmp );
1265 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1266 }
1267
1268 else if (sh.flag.checkSum == SH_CHECK_CHECK )
1269 {
1270 /* Running file check. Report on new files
1271 * detected, and quarantine them.
1272 */
1273 SH_MUTEX_LOCK(mutex_thread_nolog);
1274 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
1275 0, MSG_SUID_FOUND, tmp );
1276 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1277
1278 fflags = sh_hash_getflags(tmpcat); /* no call to sh_error_handle */
1279
1280 if ( (-1 == fflags) || (!SH_FFLAG_SUIDCHK_SET(fflags)))
1281 {
1282 if (-1 == fflags)
1283 {
1284 (void) sh_unix_gmttime (theFile->ctime, timestrc, sizeof(timestrc));
1285 (void) sh_unix_gmttime (theFile->atime, timestra, sizeof(timestra));
1286 (void) sh_unix_gmttime (theFile->mtime, timestrm, sizeof(timestrm));
1287
1288 report_file(tmpcat, theFile, timestrc, timestra, timestrm);
1289 }
1290 /* Quarantine file according to configured method
1291 */
1292 if (ShSuidchkQEnable == S_TRUE)
1293 {
1294 switch (ShSuidchkQMethod)
1295 {
1296 case SH_Q_DELETE:
1297 sh_q_delete(theFile->fullpath);
1298 break;
1299 case SH_Q_CHANGEPERM:
1300 sh_q_changeperm(theFile->fullpath);
1301 break;
1302 case SH_Q_MOVE:
1303 sh_q_move(theFile->fullpath, theFile, timestrc, timestra, timestrm);
1304 break;
1305 default:
1306 SH_MUTEX_LOCK(mutex_thread_nolog);
1307 sh_error_handle (ShSuidchkSeverity, FIL__,
1308 __LINE__, 0, MSG_SUID_QREPORT,
1309 _("Bad quarantine method"), tmp);
1310 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1311 break;
1312 }
1313 }
1314 else
1315 {
1316 /* 1.8.1 push file to in-memory database
1317 */
1318 SH_MUTEX_LOCK(mutex_thread_nolog);
1319 (void) sh_hash_compdata (SH_LEVEL_READONLY,
1320 theFile, fileHash,
1321 _("[SuidCheck]"),
1322 ShSuidchkSeverity);
1323 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1324
1325 sh_hash_addflag(tmpcat, SH_FFLAG_SUIDCHK); /* no call to sh_error_handle */
1326
1327 }
1328 }
1329 else
1330 {
1331 /* File exists. Check for modifications.
1332 */
1333 SH_MUTEX_LOCK(mutex_thread_nolog);
1334 (void) sh_hash_compdata (SH_LEVEL_READONLY,
1335 theFile, fileHash,
1336 _("[SuidCheck]"),
1337 ShSuidchkSeverity);
1338 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1339 sh_hash_addflag(tmpcat, SH_FFLAG_SUIDCHK); /* no call to sh_error_handle */
1340
1341 }
1342 }
1343 }
1344 SH_FREE(tmp);
1345 if (theFile->attr_string) SH_FREE(theFile->attr_string);
1346 if (theFile->link_path) SH_FREE(theFile->link_path);
1347 SH_FREE(theFile);
1348 }
1349 }
1350 SH_FREE(tmpcat);
1351
1352
1353#ifdef HAVE_SCHED_YIELD
1354 if (ShSuidchkYield == S_TRUE)
1355 {
1356 if (sched_yield() == -1)
1357 {
1358 status = errno;
1359 SH_MUTEX_LOCK(mutex_thread_nolog);
1360 sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
1361 _("Failed to release time slice"),
1362 _("sh_suidchk_check_internal") );
1363 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1364 }
1365 }
1366#endif
1367
1368 dirlist = dirlist->next;
1369
1370 } while (dirlist != NULL);
1371
1372
1373 kill_sh_dirlist (dirlist_orig);
1374
1375 SL_RETURN( (0), _("sh_suidchk_check_internal"));
1376}
1377
1378/*************
1379 *
1380 * module init
1381 *
1382 *************/
1383int sh_suidchk_init (struct mod_type * arg)
1384{
1385#ifndef HAVE_PTHREAD
1386 (void) arg;
1387#endif
1388
1389 if (ShSuidchkActive == S_FALSE)
1390 return SH_MOD_FAILED;
1391
1392#ifdef HAVE_PTHREAD
1393 if (arg != NULL && arg->initval < 0 &&
1394 (sh.flag.isdaemon == S_TRUE || sh.flag.loop == S_TRUE))
1395 {
1396 if (0 == sh_pthread_create(sh_threaded_module_run, (void *)arg))
1397 return SH_MOD_THREAD;
1398 else
1399 return SH_MOD_FAILED;
1400 }
1401#endif
1402
1403 return (0);
1404}
1405
1406
1407/*************
1408 *
1409 * module cleanup
1410 *
1411 *************/
1412int sh_suidchk_end ()
1413{
1414 return (0);
1415}
1416
1417
1418/*************
1419 *
1420 * module timer
1421 *
1422 *************/
1423int sh_suidchk_timer (time_t tcurrent)
1424{
1425 if (sh.flag.checkSum == SH_CHECK_INIT)
1426 return -1;
1427
1428 /* One-shot (not daemon and not loop forever)
1429 */
1430 if (sh.flag.isdaemon != S_TRUE && sh.flag.loop == S_FALSE)
1431 return -1;
1432
1433 if (ShSuidchkSched != NULL)
1434 {
1435 return test_sched(ShSuidchkSched);
1436 }
1437 if ((time_t) (tcurrent - lastcheck) >= ShSuidchkInterval)
1438 {
1439 lastcheck = tcurrent;
1440 return (-1);
1441 }
1442 return 0;
1443}
1444
1445/*************
1446 *
1447 * module check
1448 *
1449 *************/
1450
1451int sh_suidchk_check ()
1452{
1453 volatile int status;
1454
1455 SL_ENTER(_("sh_suidchk_check"));
1456
1457 if (ShSuidchkActive == S_FALSE)
1458 SL_RETURN(-1, _("sh_suidchk_check"));
1459
1460 SH_MUTEX_LOCK(mutex_thread_nolog);
1461 sh_error_handle (SH_ERR_INFO, FIL__, __LINE__, EINVAL, MSG_E_SUBGEN,
1462 _("Checking for SUID programs"),
1463 _("sh_suidchk_check") );
1464 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1465
1466 FileLimNow = time(NULL);
1467 FileLimStart = FileLimNow;
1468 FileLimNum = 0;
1469 FileLimTotal = 0;
1470
1471#ifdef SH_SUIDTESTDIR
1472 status = sh_suidchk_check_internal (SH_SUIDTESTDIR);
1473#else
1474 status = sh_suidchk_check_internal ("/");
1475#endif
1476
1477 SH_MUTEX_LOCK(mutex_thread_nolog);
1478 sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_SUID_SUMMARY,
1479 FileLimTotal,
1480 (long) (time(NULL) - FileLimStart) );
1481 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1482
1483 SL_RETURN(status, _("sh_suidchk_check"));
1484}
1485
1486/*************
1487 *
1488 * module setup
1489 *
1490 *************/
1491
1492int sh_suidchk_set_severity (const char * c)
1493{
1494 int retval;
1495 char tmp[32];
1496
1497 SL_ENTER(_("sh_suidchk_set_severity"));
1498 tmp[0] = '='; tmp[1] = '\0';
1499 (void) sl_strlcat (tmp, c, 32);
1500 retval = sh_error_set_level (tmp, &ShSuidchkSeverity);
1501 SL_RETURN(retval, _("sh_suidchk_set_severity"));
1502}
1503
1504int sh_suidchk_set_exclude (const char * c)
1505{
1506 SL_ENTER(_("sh_suidchk_set_exclude"));
1507
1508 if (c == NULL || c[0] == '\0')
1509 {
1510 SL_RETURN(-1, _("sh_suidchk_set_exclude"));
1511 }
1512
1513 if (0 == sl_strncmp(c, _("NULL"), 4))
1514 {
1515 if (ShSuidchkExclude != NULL)
1516 SH_FREE(ShSuidchkExclude);
1517 ShSuidchkExclude = NULL;
1518 SL_RETURN(0, _("sh_suidchk_set_exclude"));
1519 }
1520
1521 if (ShSuidchkExclude != NULL)
1522 SH_FREE(ShSuidchkExclude);
1523
1524 ShSuidchkExclude = sh_util_strdup (c);
1525 ExcludeLen = sl_strlen (ShSuidchkExclude);
1526 if (ShSuidchkExclude[ExcludeLen-1] == '/')
1527 {
1528 ShSuidchkExclude[ExcludeLen-1] = '\0';
1529 ExcludeLen--;
1530 }
1531 SL_RETURN(0, _("sh_suidchk_set_exclude"));
1532}
1533
1534int sh_suidchk_set_timer (const char * c)
1535{
1536 volatile long val;
1537
1538 SL_ENTER(_("sh_suidchk_set_timer"));
1539
1540 val = strtol (c, (char **)NULL, 10);
1541 if (val <= 0)
1542 {
1543 SH_MUTEX_LOCK(mutex_thread_nolog);
1544 sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
1545 _("suidchk timer"), c);
1546 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1547 }
1548 val = (val <= 0 ? 7200 : val);
1549
1550 ShSuidchkInterval = (time_t) val;
1551 SL_RETURN( 0, _("sh_suidchk_set_timer"));
1552}
1553
1554
1555static void sh_suidchk_free_schedule (void)
1556{
1557 sh_schedule_t * current = ShSuidchkSched;
1558 sh_schedule_t * next = NULL;
1559
1560 while (current != NULL)
1561 {
1562 next = current->next;
1563 SH_FREE(current);
1564 current = next;
1565 }
1566 ShSuidchkSched = NULL;
1567 return;
1568}
1569
1570int sh_suidchk_reconf ()
1571{
1572 SH_MUTEX_LOCK(mutex_suid_check);
1573 sh_suidchk_free_schedule();
1574 set_defaults();
1575 SH_MUTEX_UNLOCK(mutex_suid_check);
1576 return 0;
1577}
1578
1579int sh_suidchk_set_schedule (const char * str)
1580{
1581 int status;
1582 sh_schedule_t * newSched = NULL;
1583
1584 SL_ENTER(_("sh_suidchk_set_schedule"));
1585
1586 /*
1587 if (ShSuidchkSched != NULL)
1588 {
1589 SH_FREE(ShSuidchkSched);
1590 ShSuidchkSched = NULL;
1591 }
1592 */
1593
1594 if (0 == sl_strncmp(str, _("NULL"), 4))
1595 {
1596 (void) sh_suidchk_free_schedule ();
1597 return 0;
1598 }
1599
1600 newSched = SH_ALLOC(sizeof(sh_schedule_t));
1601 status = create_sched(str, newSched);
1602 if (status != 0)
1603 {
1604 SH_FREE(newSched);
1605 newSched = NULL;
1606 }
1607 else
1608 {
1609 newSched->next = ShSuidchkSched;
1610 ShSuidchkSched = newSched;
1611 }
1612 SL_RETURN( status, _("sh_suidchk_set_schedule"));
1613}
1614
1615
1616
1617int sh_suidchk_set_fps (const char * c)
1618{
1619 volatile long val;
1620
1621 SL_ENTER(_("sh_suidchk_set_fps"));
1622
1623 val = strtol (c, (char **)NULL, 10);
1624 if (val < 0)
1625 {
1626 SH_MUTEX_LOCK(mutex_thread_nolog);
1627 sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
1628 _("suidchk fps"), c);
1629 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1630 }
1631 val = (val < 0 ? 0 : val);
1632
1633 ShSuidchkFps = val;
1634 SL_RETURN( 0, _("sh_suidchk_set_fps"));
1635}
1636
1637int sh_suidchk_set_yield (const char * c)
1638{
1639 int i;
1640 SL_ENTER(_("sh_suidchk_set_yield"));
1641#ifdef HAVE_SCHED_YIELD
1642 i = sh_util_flagval(c, &ShSuidchkYield);
1643#else
1644 (void) c; /* cast to void to avoid compiler warning */
1645 i = -1;
1646#endif
1647 SL_RETURN(i, _("sh_suidchk_set_yield"));
1648}
1649
1650int sh_suidchk_set_activate (const char * c)
1651{
1652 int i;
1653 SL_ENTER(_("sh_suidchk_set_activate"));
1654 i = sh_util_flagval(c, &ShSuidchkActive);
1655 SL_RETURN(i, _("sh_suidchk_set_activate"));
1656}
1657
1658int sh_suidchk_set_nosuid (const char * c)
1659{
1660 int i;
1661 SL_ENTER(_("sh_suidchk_set_nosuid"));
1662 i = sh_util_flagval(c, &ShSuidchkNosuid);
1663 SL_RETURN(i, _("sh_suidchk_set_nosuid"));
1664}
1665
1666int sh_suidchk_set_quarantine (const char * c)
1667{
1668 int i;
1669 SL_ENTER(_("sh_suidchk_set_quarantine"));
1670 i = sh_util_flagval(c, &ShSuidchkQEnable);
1671 SL_RETURN(i, _("sh_suidchk_set_quarantine"));
1672}
1673
1674int sh_suidchk_set_qdelete (const char * c)
1675{
1676 int i;
1677 SL_ENTER(_("sh_suidchk_set_qdelete"));
1678 i = sh_util_flagval(c, &ShSuidchkQDelete);
1679 SL_RETURN(i, _("sh_suidchk_set_qdelete"));
1680}
1681
1682int sh_suidchk_set_qmethod (const char * c)
1683{
1684 volatile long val;
1685 volatile int ret = 0;
1686 struct stat buf;
1687
1688 SL_ENTER(_("sh_suidchk_set_qmethod"));
1689
1690 val = strtol (c, (char **)NULL, 10);
1691 if (val < 0)
1692 {
1693 SH_MUTEX_LOCK(mutex_thread_nolog);
1694 sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
1695 _("suidchk qmethod"), c);
1696 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1697 ret = -1;
1698 }
1699 else
1700 {
1701 switch (val)
1702 {
1703 case SH_Q_DELETE:
1704 ShSuidchkQMethod = SH_Q_DELETE;
1705 break;
1706 case SH_Q_CHANGEPERM:
1707 ShSuidchkQMethod = SH_Q_CHANGEPERM;
1708 break;
1709 case SH_Q_MOVE:
1710 if (retry_stat (FIL__, __LINE__, DEFAULT_QDIR, &buf) != 0)
1711 {
1712 if (mkdir (DEFAULT_QDIR, 0750) == -1)
1713 {
1714 SH_MUTEX_LOCK(mutex_thread_nolog);
1715 sh_error_handle ((-1), FIL__, __LINE__, EINVAL,
1716 MSG_SUID_ERROR,
1717 _("Unable to create quarantine directory"));
1718 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1719 }
1720 }
1721 ShSuidchkQMethod = SH_Q_MOVE;
1722 break;
1723 default:
1724 SH_MUTEX_LOCK(mutex_thread_nolog);
1725 sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
1726 _("suidchk qmethod"), c);
1727 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1728 ShSuidchkQMethod = -1;
1729 ret = -1;
1730 break;
1731 }
1732 }
1733
1734 SL_RETURN( ret, _("sh_suidchk_set_qmethod"));
1735}
1736
1737#if defined(FSTYPE_STATFS) || defined(FSTYPE_AIX_STATFS)
1738/* dirname.c -- return all but the last element in a path
1739 Copyright (C) 1990 Free Software Foundation, Inc.
1740
1741 This program is free software; you can redistribute it and/or modify
1742 it under the terms of the GNU General Public License as published by
1743 the Free Software Foundation; either version 2, or (at your option)
1744 any later version.
1745
1746 This program is distributed in the hope that it will be useful,
1747 but WITHOUT ANY WARRANTY; without even the implied warranty of
1748 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1749 GNU General Public License for more details.
1750
1751 You should have received a copy of the GNU General Public License
1752 along with this program; if not, write to the Free Software Foundation,
1753 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
1754
1755/* Return the leading directories part of PATH,
1756 allocated with malloc. If out of memory, return 0.
1757 Assumes that trailing slashes have already been
1758 removed. */
1759
1760char * sh_dirname (const char * path)
1761{
1762 char *newpath;
1763 char *slash;
1764 int length; /* Length of result, not including NUL. */
1765
1766 slash = strrchr (path, '/');
1767 if (slash == NULL)
1768 {
1769 /* File is in the current directory. */
1770 path = ".";
1771 length = 1;
1772 }
1773 else
1774 {
1775 /* Remove any trailing slashes from the result. */
1776 while (slash > path && *slash == '/')
1777 --slash;
1778
1779 length = slash - path + 1;
1780 }
1781 newpath = (char *) SH_ALLOC (length + 1);
1782 if (newpath == NULL)
1783 return NULL;
1784 strncpy (newpath, path, length);
1785 newpath[length] = '\0';
1786 return newpath;
1787}
1788/* #ifdef FSTYPE_STATFS */
1789#endif
1790
1791/* fstype.c -- determine type of filesystems that files are on
1792 Copyright (C) 1990, 91, 92, 93, 94 Free Software Foundation, Inc.
1793
1794 This program is free software; you can redistribute it and/or modify
1795 it under the terms of the GNU General Public License as published by
1796 the Free Software Foundation; either version 2, or (at your option)
1797 any later version.
1798
1799 This program is distributed in the hope that it will be useful,
1800 but WITHOUT ANY WARRANTY; without even the implied warranty of
1801 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1802 GNU General Public License for more details.
1803
1804 You should have received a copy of the GNU General Public License
1805 along with this program; if not, write to the Free Software
1806 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
1807
1808/* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */
1809
1810/* Modified by R. Wichmann:
1811 - replaced error() by sh_error_handle()
1812 - replaced xstrdup() by sl_strdup()
1813 - replaced strstr() by sl_strstr()
1814 - some additions to recognize nosuid fs
1815*/
1816
1817/* modetype.h -- file type bits definitions for POSIX systems
1818 Requires sys/types.h sys/stat.h.
1819 Copyright (C) 1990 Free Software Foundation, Inc.
1820
1821 This program is free software; you can redistribute it and/or modify
1822 it under the terms of the GNU General Public License as published by
1823 the Free Software Foundation; either version 2, or (at your option)
1824 any later version.
1825
1826 This program is distributed in the hope that it will be useful,
1827 but WITHOUT ANY WARRANTY; without even the implied warranty of
1828 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1829 GNU General Public License for more details.
1830
1831 You should have received a copy of the GNU General Public License
1832 along with this program; if not, write to the Free Software
1833 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
1834
1835/* POSIX.1 doesn't mention the S_IFMT bits; instead, it uses S_IStype
1836 test macros. To make storing file types more convenient, define
1837 them; the values don't need to correspond to what the kernel uses,
1838 because of the way we use them. */
1839#ifndef S_IFMT /* Doesn't have traditional Unix macros. */
1840#define S_IFBLK 1
1841#define S_IFCHR 2
1842#define S_IFDIR 4
1843#define S_IFREG 8
1844#ifdef S_ISLNK
1845#define S_IFLNK 16
1846#endif
1847#ifdef S_ISFIFO
1848#define S_IFIFO 32
1849#endif
1850#ifdef S_ISSOCK
1851#define S_IFSOCK 64
1852#endif
1853#endif /* !S_IFMT */
1854
1855#ifdef STAT_MACROS_BROKEN
1856#undef S_ISBLK
1857#undef S_ISCHR
1858#undef S_ISDIR
1859#undef S_ISREG
1860#undef S_ISFIFO
1861#undef S_ISLNK
1862#undef S_ISSOCK
1863#undef S_ISMPB
1864#undef S_ISMPC
1865#undef S_ISNWK
1866#endif
1867
1868/* Do the reverse: define the POSIX.1 macros for traditional Unix systems
1869 that don't have them. */
1870#if !defined(S_ISBLK) && defined(S_IFBLK)
1871#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
1872#endif
1873#if !defined(S_ISCHR) && defined(S_IFCHR)
1874#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
1875#endif
1876#if !defined(S_ISDIR) && defined(S_IFDIR)
1877#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
1878#endif
1879#if !defined(S_ISREG) && defined(S_IFREG)
1880#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
1881#endif
1882#if !defined(S_ISFIFO) && defined(S_IFIFO)
1883#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
1884#endif
1885#if !defined(S_ISLNK) && defined(S_IFLNK)
1886#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
1887#endif
1888#if !defined(S_ISSOCK) && defined(S_IFSOCK)
1889#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
1890#endif
1891#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */
1892#define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB)
1893#define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC)
1894#endif
1895#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */
1896#define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK)
1897#endif
1898
1899
1900static char *filesystem_type_uncached (char *path, char *relpath,
1901 struct stat *statp);
1902
1903#ifdef FSTYPE_MNTENT /* 4.3BSD etc. */
1904static int xatoi (const char *cp);
1905#endif
1906
1907#ifdef FSTYPE_MNTENT /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
1908#include <mntent.h>
1909#if !defined(MOUNTED)
1910# if defined(MNT_MNTTAB) /* HP-UX. */
1911# define MOUNTED MNT_MNTTAB
1912# endif
1913# if defined(MNTTABNAME) /* Dynix. */
1914# define MOUNTED MNTTABNAME
1915# endif
1916#endif
1917#endif
1918
1919#ifdef FSTYPE_GETMNT /* Ultrix. */
1920#include <sys/param.h>
1921#include <sys/mount.h>
1922#include <sys/fs_types.h>
1923#endif
1924
1925#ifdef FSTYPE_USG_STATFS /* SVR3. */
1926#include <sys/statfs.h>
1927#include <sys/fstyp.h>
1928#endif
1929
1930#ifdef FSTYPE_STATVFS /* SVR4. */
1931#include <sys/statvfs.h>
1932#include <sys/fstyp.h>
1933#endif
1934
1935#ifdef FSTYPE_STATFS /* 4.4BSD. */
1936#include <sys/param.h> /* NetBSD needs this. */
1937#include <sys/mount.h>
1938
1939#ifndef MFSNAMELEN /* NetBSD defines this. */
1940static char *
1941fstype_to_string (t)
1942 short t;
1943{
1944#ifdef INITMOUNTNAMES /* Defined in 4.4BSD, not in NET/2. */
1945 static char *mn[] = INITMOUNTNAMES;
1946 if (t >= 0 && t <= MOUNT_MAXTYPE)
1947 return mn[t];
1948 else
1949 return "?";
1950#else /* !INITMOUNTNAMES */
1951 switch (t)
1952 {
1953#ifdef MOUNT_UFS
1954 case MOUNT_UFS:
1955 return _("ufs");
1956#endif
1957#ifdef MOUNT_ISO9660
1958 case MOUNT_ISO9660:
1959 return _("iso9660fs");
1960#endif
1961#ifdef MOUNT_CD9660
1962 case MOUNT_CD9660:
1963 return _("cd9660");
1964#endif
1965#ifdef MOUNT_NFS
1966 case MOUNT_NFS:
1967 return _("nfs");
1968#endif
1969#ifdef MOUNT_PC
1970 case MOUNT_PC:
1971 return _("pc");
1972#endif
1973#ifdef MOUNT_MFS
1974 case MOUNT_MFS:
1975 return _("mfs");
1976#endif
1977#ifdef MOUNT_LO
1978 case MOUNT_LO:
1979 return _("lofs");
1980#endif
1981#ifdef MOUNT_TFS
1982 case MOUNT_TFS:
1983 return _("tfs");
1984#endif
1985#ifdef MOUNT_TMP
1986 case MOUNT_TMP:
1987 return _("tmp");
1988#endif
1989#ifdef MOUNT_MSDOS
1990 case MOUNT_MSDOS:
1991 return _("msdos");
1992#endif
1993#ifdef MOUNT_LFS
1994 case MOUNT_LFS:
1995 return _("lfs");
1996#endif
1997#ifdef MOUNT_LOFS
1998 case MOUNT_LOFS:
1999 return _("lofs");
2000#endif
2001#ifdef MOUNT_FDESC
2002 case MOUNT_FDESC:
2003 return _("fdesc");
2004#endif
2005#ifdef MOUNT_PORTAL
2006 case MOUNT_PORTAL:
2007 return _("portal");
2008#endif
2009#ifdef MOUNT_NULL
2010 case MOUNT_NULL:
2011 return _("null");
2012#endif
2013#ifdef MOUNT_UMAP
2014 case MOUNT_UMAP:
2015 return _("umap");
2016#endif
2017#ifdef MOUNT_KERNFS
2018 case MOUNT_KERNFS:
2019 return _("kernfs");
2020#endif
2021#ifdef MOUNT_PROCFS
2022 case MOUNT_PROCFS:
2023 return _("procfs");
2024#endif
2025#ifdef MOUNT_DEVFS
2026 case MOUNT_DEVFS:
2027 return _("devfs");
2028#endif
2029#ifdef MOUNT_EXT2FS
2030 case MOUNT_EXT2FS:
2031 return _("ext2fs");
2032#endif
2033#ifdef MOUNT_UNION
2034 case MOUNT_UNION:
2035 return _("union");
2036#endif
2037 default:
2038 return "?";
2039 }
2040#endif /* !INITMOUNTNAMES */
2041}
2042#endif /* !MFSNAMELEN */
2043#endif /* FSTYPE_STATFS */
2044
2045#ifdef FSTYPE_AIX_STATFS /* AIX. */
2046#include <sys/vmount.h>
2047#include <sys/statfs.h>
2048
2049#define FSTYPE_STATFS /* Otherwise like 4.4BSD. */
2050#define f_type f_vfstype
2051
2052static char *
2053fstype_to_string (t)
2054 short t;
2055{
2056 switch (t)
2057 {
2058 case MNT_AIX:
2059 return _("aix"); /* AIX 4.3: NFS filesystems are actually MNT_AIX. */
2060#ifdef MNT_NAMEFS
2061 case MNT_NAMEFS:
2062 return _("namefs");
2063#endif
2064 case MNT_NFS:
2065 return _("nfs");
2066 case MNT_JFS:
2067 return _("jfs");
2068 case MNT_CDROM:
2069 return _("cdrom");
2070#ifdef MNT_PROCFS
2071 case MNT_PROCFS:
2072 return _("procfs");
2073#endif
2074#ifdef MNT_SFS
2075 case MNT_SFS:
2076 return _("sfs");
2077#endif
2078#ifdef MNT_CACHEFS
2079 case MNT_CACHEFS:
2080 return _("cachefs");
2081#endif
2082#ifdef MNT_NFS3
2083 case MNT_NFS3:
2084 return _("nfs3");
2085#endif
2086#ifdef MNT_AUTOFS
2087 case MNT_AUTOFS:
2088 return _("autofs");
2089#endif
2090#ifdef MNT_VXFS
2091 case MNT_VXFS:
2092 return _("vxfs");
2093#endif
2094#ifdef MNT_VXODM
2095 case MNT_VXODM:
2096 return _("veritasfs");
2097#endif
2098#ifdef MNT_UDF
2099 case MNT_UDF:
2100 return _("udfs");
2101#endif
2102#ifdef MNT_NFS4
2103 case MNT_NFS4:
2104 return _("nfs4");
2105#endif
2106#ifdef MNT_RFS4
2107 case MNT_RFS4:
2108 return _("nfs4");
2109#endif
2110#ifdef MNT_CIFS
2111 case MNT_CIFS:
2112 return _("cifs");
2113#endif
2114 default:
2115 return "?";
2116 }
2117}
2118#endif /* FSTYPE_AIX_STATFS */
2119
2120#ifdef AFS
2121#include <netinet/in.h>
2122#include <afs/venus.h>
2123#if __STDC__
2124/* On SunOS 4, afs/vice.h defines this to rely on a pre-ANSI cpp. */
2125#undef _VICEIOCTL
2126#define _VICEIOCTL(id) ((unsigned int ) _IOW('V', id, struct ViceIoctl))
2127#endif
2128#ifndef _IOW
2129/* AFS on Solaris 2.3 doesn't get this definition. */
2130#include <sys/ioccom.h>
2131#endif
2132
2133static int
2134in_afs (path)
2135 char *path;
2136{
2137 static char space[2048];
2138 struct ViceIoctl vi;
2139
2140 vi.in_size = 0;
2141 vi.out_size = sizeof (space);
2142 vi.out = space;
2143
2144 if (pioctl (path, VIOC_FILE_CELL_NAME, &vi, 1)
2145 && (errno == EINVAL || errno == ENOENT))
2146 return 0;
2147 return 1;
2148}
2149#endif /* AFS */
2150
2151/* Nonzero if the current filesystem's type is known. */
2152static int fstype_known = 0;
2153
2154/* Return a static string naming the type of filesystem that the file PATH,
2155 described by STATP, is on.
2156 RELPATH is the file name relative to the current directory.
2157 Return "unknown" if its filesystem type is unknown. */
2158
2159static char *
2160filesystem_type (char * path, char * relpath, struct stat * statp)
2161{
2162 static char *current_fstype = NULL;
2163 static dev_t current_dev;
2164
2165 if (current_fstype != NULL)
2166 {
2167 if ((0 != fstype_known) && statp->st_dev == current_dev)
2168 return current_fstype; /* Cached value. */
2169 SH_FREE (current_fstype);
2170 }
2171 current_dev = statp->st_dev;
2172 current_fstype = filesystem_type_uncached (path, relpath, statp);
2173 return current_fstype;
2174}
2175
2176/* This variable is not used anywhere. It only exists
2177 * to assign &dirlist to it, which keeps gcc from
2178 * putting it into a register, and avoids the 'clobbered
2179 * by longjmp' warning. And no, 'volatile' proved insufficient.
2180 */
2181static void * sh_dummy_type = NULL;
2182
2183
2184/* Return a newly allocated string naming the type of filesystem that the
2185 file PATH, described by STATP, is on.
2186 RELPATH is the file name relative to the current directory.
2187 Return "unknown" if its filesystem type is unknown. */
2188
2189static char *
2190filesystem_type_uncached (path, relpath, statp)
2191 char *path;
2192 char *relpath;
2193 struct stat *statp;
2194{
2195 char * type = NULL;
2196#ifdef MFSNAMELEN /* NetBSD. */
2197 static char my_tmp_type[64];
2198#endif
2199
2200#ifdef FSTYPE_MNTENT /* 4.3BSD, SunOS, HP-UX, Dynix, Irix. */
2201 char *table = MOUNTED;
2202 FILE *mfp;
2203 struct mntent *mnt;
2204
2205 if (path == NULL || relpath == NULL)
2206 return NULL;
2207
2208 mfp = setmntent (table, "r");
2209 if (mfp == NULL)
2210 {
2211 SH_MUTEX_LOCK(mutex_thread_nolog);
2212 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
2213 _("setmntent() failed"),
2214 _("filesystem_type_uncached") );
2215 SH_MUTEX_UNLOCK(mutex_thread_nolog);
2216 return NULL;
2217 }
2218
2219 /* Take the address to keep gcc from putting it into a register.
2220 * Avoids the 'clobbered by longjmp' warning.
2221 */
2222 sh_dummy_type = (void*) &type;
2223
2224 /* Find the entry with the same device number as STATP, and return
2225 that entry's fstype. */
2226 while (type == NULL && (mnt = getmntent (mfp)) != NULL)
2227 {
2228 const char *devopt;
2229 dev_t dev;
2230 struct stat disk_stats;
2231
2232#ifdef MNTTYPE_IGNORE
2233 if (0 == strcmp (mnt->mnt_type, MNTTYPE_IGNORE))
2234 continue;
2235#endif
2236
2237 /* Newer systems like SunOS 4.1 keep the dev number in the mtab,
2238 in the options string. For older systems, we need to stat the
2239 directory that the filesystem is mounted on to get it.
2240
2241 Unfortunately, the HPUX 9.x mnttab entries created by automountq
2242 contain a dev= option but the option value does not match the
2243 st_dev value of the file (maybe the lower 16 bits match?). */
2244
2245#if !defined(hpux) && !defined(__hpux__)
2246 devopt = sl_strstr (mnt->mnt_opts, "dev=");
2247 if (devopt)
2248 {
2249 if (devopt[4] == '0' && (devopt[5] == 'x' || devopt[5] == 'X'))
2250 dev = (dev_t) xatoi (devopt + 6);
2251 else
2252 dev = (dev_t) xatoi (devopt + 4);
2253 }
2254 else
2255#endif /* not hpux */
2256 {
2257 if (stat (mnt->mnt_dir, &disk_stats) == -1)
2258 {
2259 char errmsg[256];
2260 volatile int elevel = SH_ERR_ERR;
2261 size_t tlen = strlen(mnt->mnt_dir);
2262 if (tlen >= 6 && 0 == strcmp(&((mnt->mnt_dir)[tlen-6]), _("/.gvfs")))
2263 elevel = SH_ERR_NOTICE;
2264 sl_snprintf(errmsg, sizeof(errmsg), _("stat(%s) failed"),
2265 mnt->mnt_dir);
2266 SH_MUTEX_LOCK(mutex_thread_nolog);
2267 sh_error_handle (elevel, FIL__, __LINE__, 0, MSG_E_SUBGEN,
2268 errmsg,
2269 _("filesystem_type_uncached") );
2270 SH_MUTEX_UNLOCK(mutex_thread_nolog);
2271 return NULL;
2272 }
2273 dev = disk_stats.st_dev;
2274 }
2275
2276 if (dev == statp->st_dev)
2277 {
2278 /* check for the "nosuid" option
2279 */
2280#ifdef HAVE_HASMNTOPT
2281 if (NULL == hasmntopt(mnt, "nosuid") || (ShSuidchkNosuid == S_TRUE))
2282 type = mnt->mnt_type;
2283 else
2284 type = _("nosuid"); /* hasmntopt (nosuid) */
2285#else
2286 type = mnt->mnt_type;
2287#endif
2288 }
2289 }
2290
2291 if (endmntent (mfp) == 0)
2292 {
2293 SH_MUTEX_LOCK(mutex_thread_nolog);
2294 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
2295 _("endmntent() failed"),
2296 _("filesystem_type_uncached") );
2297 SH_MUTEX_UNLOCK(mutex_thread_nolog);
2298 }
2299#endif
2300
2301#ifdef FSTYPE_GETMNT /* Ultrix. */
2302 int offset = 0;
2303 struct fs_data fsd;
2304
2305 if (path == NULL || relpath == NULL)
2306 return NULL;
2307
2308 /* Take the address to keep gcc from putting it into a register.
2309 * Avoids the 'clobbered by longjmp' warning.
2310 */
2311 sh_dummy_type = (void*) &type;
2312
2313 while (type == NULL
2314 && getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY, 0) > 0)
2315 {
2316 if (fsd.fd_req.dev == statp->st_dev)
2317 type = gt_names[fsd.fd_req.fstype];
2318 }
2319#endif
2320
2321#ifdef FSTYPE_USG_STATFS /* SVR3. */
2322 struct statfs fss;
2323 char typebuf[FSTYPSZ];
2324
2325 if (path == NULL || relpath == NULL)
2326 return NULL;
2327
2328 /* Take the address to keep gcc from putting it into a register.
2329 * Avoids the 'clobbered by longjmp' warning.
2330 */
2331 sh_dummy_type = (void*) &type;
2332
2333 if (statfs (relpath, &fss, sizeof (struct statfs), 0) == -1)
2334 {
2335 /* Don't die if a file was just removed. */
2336 if (errno != ENOENT)
2337 {
2338 SH_MUTEX_LOCK(mutex_thread_nolog);
2339 sh_error_handle ((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
2340 _("statfs() failed"),
2341 _("filesystem_type_uncached") );
2342 SH_MUTEX_UNLOCK(mutex_thread_nolog);
2343 return NULL;
2344 }
2345 }
2346 else if (!sysfs (GETFSTYP, fss.f_fstyp, typebuf))
2347 type = typebuf;
2348#endif
2349
2350#ifdef FSTYPE_STATVFS /* SVR4. */
2351 struct statvfs fss;
2352
2353 if (path == NULL || relpath == NULL)
2354 return NULL;
2355
2356 /* Take the address to keep gcc from putting it into a register.
2357 * Avoids the 'clobbered by longjmp' warning.
2358 */
2359 sh_dummy_type = (void*) &type;
2360
2361 if (statvfs (relpath, &fss) == -1)
2362 {
2363 /* Don't die if a file was just removed. */
2364 if (errno != ENOENT)
2365 {
2366 SH_MUTEX_LOCK(mutex_thread_nolog);
2367 sh_error_handle ((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
2368 _("statvfs() failed"),
2369 _("filesystem_type_uncached") );
2370 SH_MUTEX_UNLOCK(mutex_thread_nolog);
2371 return NULL;
2372 }
2373 }
2374 else
2375 {
2376 type = fss.f_basetype;
2377
2378 /* patch by Konstantin Khrooschev <nathoo@co.ru>
2379 */
2380 if( (fss.f_flag & ST_NOSUID) && (ShSuidchkNosuid == S_FALSE))
2381 type = _("nosuid");
2382 }
2383 (void) statp; /* fix compiler warning */
2384#endif
2385
2386#ifdef FSTYPE_STATFS /* 4.4BSD. */
2387 struct statfs fss;
2388 char *p;
2389#if defined(MNT_VISFLAGMASK) && defined(HAVE_STRUCT_STATFS_F_FLAGS)
2390 int flags;
2391#endif
2392 /* char * sh_dirname(const char *path); */
2393
2394 if (path == NULL || relpath == NULL)
2395 return NULL;
2396
2397 /* Take the address to keep gcc from putting it into a register.
2398 * Avoids the 'clobbered by longjmp' warning.
2399 */
2400 sh_dummy_type = (void*) &type;
2401
2402 if (S_ISLNK (statp->st_mode))
2403 p = sh_dirname (relpath);
2404 else
2405 p = relpath;
2406
2407 if (statfs (p, &fss) == -1)
2408 {
2409 /* Don't die if symlink to nonexisting file, or a file that was
2410 just removed. */
2411 if (errno != ENOENT)
2412 {
2413 SH_MUTEX_LOCK(mutex_thread_nolog);
2414 sh_error_handle ((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
2415 _("statfs() failed"),
2416 _("filesystem_type_uncached") );
2417 SH_MUTEX_UNLOCK(mutex_thread_nolog);
2418 return NULL;
2419 }
2420 }
2421 else
2422 {
2423
2424#ifdef MFSNAMELEN /* NetBSD. */
2425 /* MEMORY LEAK !!!
2426 * type = sh_util_strdup (fss.f_fstypename);
2427 */
2428 sl_strlcpy (my_tmp_type, fss.f_fstypename, 64);
2429 type = my_tmp_type;
2430#else
2431 type = fstype_to_string (fss.f_type);
2432#endif
2433
2434#ifdef HAVE_STRUCT_STATFS_F_FLAGS
2435#ifdef MNT_VISFLAGMASK
2436 flags = fss.f_flags & MNT_VISFLAGMASK;
2437 if ((flags & MNT_NOSUID) && (ShSuidchkNosuid == S_FALSE))
2438#else
2439 if ((fss.f_flags & MNT_NOSUID) && (ShSuidchkNosuid == S_FALSE))
2440#endif
2441 type = _("nosuid");
2442#endif
2443 }
2444 if (p != relpath)
2445 SH_FREE (p);
2446#endif
2447
2448#ifdef AFS
2449 if ((!type || !strcmp (type, "xx")) && in_afs (relpath))
2450 type = "afs";
2451#endif
2452
2453 /* An unknown value can be caused by an ENOENT error condition.
2454 Don't cache those values. */
2455 fstype_known = (int)(type != NULL);
2456
2457 return sh_util_strdup (type ? type : "unknown");
2458}
2459
2460#ifdef FSTYPE_MNTENT /* 4.3BSD etc. */
2461/* Return the value of the hexadecimal number represented by CP.
2462 No prefix (like '0x') or suffix (like 'h') is expected to be
2463 part of CP. */
2464
2465static int
2466xatoi (cp)
2467 const char *cp;
2468{
2469 int val;
2470
2471 val = 0;
2472 while (*cp != '\0')
2473 {
2474 /*@+charint@*/
2475 if (*cp >= 'a' && *cp <= 'f')
2476 val = val * 16 + *cp - 'a' + 10;
2477 else if (*cp >= 'A' && *cp <= 'F')
2478 val = val * 16 + *cp - 'A' + 10;
2479 else if (*cp >= '0' && *cp <= '9')
2480 val = val * 16 + *cp - '0';
2481 else
2482 break;
2483 /*@-charint@*/
2484 cp++;
2485 }
2486 return val;
2487}
2488#endif
2489
2490
2491
2492#endif
2493
2494
2495/* #ifdef SH_USE_UTMP */
2496#endif
2497
2498
2499
Note: See TracBrowser for help on using the repository browser.