source: trunk/src/sh_suidchk.c@ 468

Last change on this file since 468 was 458, checked in by katerina, 10 years ago

Fix for ticket #358 (repetitive lstat warning) and #359 (reporting of added/deleted top level directories).

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