source: trunk/src/sh_suidchk.c@ 539

Last change on this file since 539 was 517, checked in by katerina, 8 years ago

Fix for ticket #411 (leftover debug statement in changeset 516).

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