source: trunk/src/sh_suidchk.c@ 405

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

Fix for tickets #303, #304, #305. #306, and #307. Update version number.

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