source: trunk/src/sh_suidchk.c@ 568

Last change on this file since 568 was 543, checked in by katerina, 6 years ago

Fix for ticket #434 (option to init for alternative root fs).

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