source: trunk/src/sh_suidchk.c@ 481

Last change on this file since 481 was 481, checked in by katerina, 9 years ago

Enhancements and fixes for tickets #374, #375, #376, #377, #378, and #379.

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