source: trunk/src/sh_suidchk.c@ 590

Last change on this file since 590 was 590, checked in by katerina, 17 hours ago

Fix for ticket #478 (cppcheck warnings).

File size: 63.6 KB
Line 
1/* SAMHAIN file system integrity testing */
2/* Copyright (C) 2001 Rainer Wichmann */
3/* */
4/* This program is free software; you can redistribute it */
5/* and/or modify */
6/* it under the terms of the GNU General Public License as */
7/* published by */
8/* the Free Software Foundation; either version 2 of the License, or */
9/* (at your option) any later version. */
10/* */
11/* This program is distributed in the hope that it will be useful, */
12/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14/* GNU General Public License for more details. */
15/* */
16/* You should have received a copy of the GNU General Public License */
17/* along with this program; if not, write to the Free Software */
18/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20#include "config_xor.h"
21
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/types.h>
27#include <sys/stat.h>
28#include <fcntl.h>
29#include <unistd.h>
30#include <errno.h>
31#include <limits.h>
32
33#ifdef HAVE_SCHED_H
34#include <sched.h>
35#endif
36
37#ifdef SH_USE_SUIDCHK
38
39#undef FIL__
40#define FIL__ _("sh_suidchk.c")
41
42#if defined (SH_WITH_CLIENT) || defined (SH_STANDALONE)
43
44#if HAVE_SYS_TIME_H
45#include <sys/time.h>
46#endif
47#include <time.h>
48
49#ifdef HAVE_DIRENT_H
50#include <dirent.h>
51#define NAMLEN(dirent) sl_strlen((dirent)->d_name)
52#else
53#define dirent direct
54#define NAMLEN(dirent) (dirent)->d_namlen
55#ifdef HAVE_SYS_NDIR_H
56#include <sys/ndir.h>
57#endif
58#ifdef HAVE_SYS_DIR_H
59#include <sys/dir.h>
60#endif
61#ifdef HAVE_NDIR_H
62#include <ndir.h>
63#endif
64#endif
65#define NEED_ADD_DIRENT
66
67#include "samhain.h"
68#include "sh_pthread.h"
69#include "sh_utils.h"
70#include "sh_error.h"
71#include "sh_modules.h"
72#include "sh_suidchk.h"
73#include "sh_hash.h"
74#include "sh_dbIO.h"
75#include "sh_unix.h"
76#include "sh_files.h"
77#include "sh_schedule.h"
78#include "sh_calls.h"
79#include "zAVLTree.h"
80
81
82sh_rconf sh_suidchk_table[] = {
83 {
84 N_("severitysuidcheck"),
85 sh_suidchk_set_severity
86 },
87 {
88 N_("suidcheckactive"),
89 sh_suidchk_set_activate
90 },
91 {
92 N_("suidcheckinterval"),
93 sh_suidchk_set_timer
94 },
95 {
96 N_("suidcheckschedule"),
97 sh_suidchk_set_schedule
98 },
99 {
100 N_("suidcheckexclude"),
101 sh_suidchk_set_exclude
102 },
103 {
104 N_("suidcheckfps"),
105 sh_suidchk_set_fps
106 },
107 {
108 N_("suidcheckyield"),
109 sh_suidchk_set_yield
110 },
111 {
112 N_("suidchecknosuid"),
113 sh_suidchk_set_nosuid
114 },
115 {
116 N_("suidcheckquarantinefiles"),
117 sh_suidchk_set_quarantine
118 },
119 {
120 N_("suidcheckquarantinemethod"),
121 sh_suidchk_set_qmethod
122 },
123 {
124 N_("suidcheckquarantinedelete"),
125 sh_suidchk_set_qdelete
126 },
127 {
128 NULL,
129 NULL
130 },
131};
132
133
134static time_t lastcheck = (time_t) 0;
135static int ShSuidchkActive = S_TRUE;
136static time_t ShSuidchkInterval = 7200;
137static unsigned long ShSuidchkFps = 0;
138static int ShSuidchkNosuid = S_FALSE;
139static int ShSuidchkYield = S_FALSE;
140static int ShSuidchkQEnable = S_FALSE;
141static int ShSuidchkQMethod = SH_Q_CHANGEPERM;
142static int ShSuidchkQDelete = S_FALSE;
143static int ShSuidchkSeverity = SH_ERR_SEVERE;
144
145static time_t FileLimNow = 0;
146static time_t FileLimStart = 0;
147static unsigned long FileLimNum = 0;
148static unsigned long FileLimTotal = 0;
149
150static sh_schedule_t * ShSuidchkSched = NULL;
151
152
153static zAVLTree * ShSuidchkExclude = NULL;
154static void sh_suid_exclude_free()
155{
156 zAVL_string_reset(ShSuidchkExclude);
157 ShSuidchkExclude = NULL;
158 return;
159}
160static int sh_suid_exclude_add(const char * str)
161{
162 size_t len;
163 int ret;
164 char * key = sh_util_strdup(str);
165
166 len = sl_strlen (key);
167 if (len && key[len-1] == '/')
168 {
169 key[len-1] = '\0';
170 }
171 ret = zAVL_string_set(&ShSuidchkExclude, key);
172 SH_FREE(key);
173 return ret;
174}
175
176
177static char *
178filesystem_type (char * path, char * relpath, struct stat * statp);
179
180#ifndef PATH_MAX
181#define PATH_MAX 1024
182#endif
183
184SH_MUTEX_STATIC(mutex_suid_check, PTHREAD_MUTEX_INITIALIZER);
185
186extern unsigned long sh_files_maskof (int class);
187
188static void set_defaults (void)
189{
190 ShSuidchkActive = S_TRUE;
191 ShSuidchkInterval = 7200;
192 ShSuidchkFps = 0;
193 ShSuidchkNosuid = S_FALSE;
194 ShSuidchkYield = S_FALSE;
195 ShSuidchkQEnable = S_FALSE;
196 ShSuidchkQMethod = SH_Q_CHANGEPERM;
197 ShSuidchkQDelete = S_FALSE;
198 ShSuidchkSeverity = SH_ERR_SEVERE;
199 if (ShSuidchkExclude != NULL)
200 sh_suid_exclude_free();
201
202 FileLimNow = 0;
203 FileLimStart = 0;
204 FileLimNum = 0;
205 FileLimTotal = 0;
206
207 return;
208}
209
210/* Recursively descend into the directory to make sure that
211 * there is no symlink in the path.
212 *
213 * Use retry_lstat_ns() here because we cannot chdir the subprocess
214 * that does the lstat().
215 */
216static int do_truncate_int (char * path, int depth)
217{
218 char * q;
219 struct stat one;
220 struct stat two;
221 int fd;
222 char errbuf[SH_ERRBUF_SIZE];
223
224 if (depth > 99)
225 {
226 SH_MUTEX_LOCK(mutex_thread_nolog);
227 sh_error_handle ((-1), FIL__, __LINE__, EINVAL,
228 MSG_SUID_ERROR,
229 _("do_truncate: max depth 99 exceeded"));
230 SH_MUTEX_UNLOCK(mutex_thread_nolog);
231 return -1;
232 }
233 ++depth;
234 if (path[0] != '/')
235 {
236 SH_MUTEX_LOCK(mutex_thread_nolog);
237 sh_error_handle ((-1), FIL__, __LINE__, EINVAL,
238 MSG_SUID_ERROR,
239 _("do_truncate: not an absolute path"));
240 SH_MUTEX_UNLOCK(mutex_thread_nolog);
241 return -1;
242 }
243 ++path;
244 q = strchr(path, '/');
245 if (q)
246 {
247 *q = '\0';
248 if (0 != retry_lstat_ns(FIL__, __LINE__, path, &one))
249 {
250 SH_MUTEX_LOCK(mutex_thread_nolog);
251 sh_error_handle ((-1), FIL__, __LINE__, errno,
252 MSG_SUID_ERROR,
253 sh_error_message(errno, errbuf, sizeof(errbuf)));
254 SH_MUTEX_UNLOCK(mutex_thread_nolog);
255 *q = '/';
256 return -1;
257 }
258 if (/*@-usedef@*/!S_ISDIR(one.st_mode)/*@+usedef@*/)
259
260 {
261 SH_MUTEX_LOCK(mutex_thread_nolog);
262 sh_error_handle ((-1), FIL__, __LINE__, EINVAL,
263 MSG_SUID_ERROR,
264 _("Possible race: not a directory"));
265 SH_MUTEX_UNLOCK(mutex_thread_nolog);
266 *q = '/';
267 return -1;
268 }
269
270
271 if (0 != chdir(path))
272 {
273 SH_MUTEX_LOCK(mutex_thread_nolog);
274 sh_error_handle ((-1), FIL__, __LINE__, errno,
275 MSG_SUID_ERROR,
276 sh_error_message(errno, errbuf, sizeof(errbuf)));
277 SH_MUTEX_UNLOCK(mutex_thread_nolog);
278 *q = '/';
279 return -1;
280 }
281 *q = '/';
282 if (0 != retry_lstat_ns(FIL__, __LINE__, ".", &two))
283 {
284 SH_MUTEX_LOCK(mutex_thread_nolog);
285 sh_error_handle ((-1), FIL__, __LINE__, errno,
286 MSG_SUID_ERROR,
287 sh_error_message(errno, errbuf, sizeof(errbuf)));
288 SH_MUTEX_UNLOCK(mutex_thread_nolog);
289 return -1;
290 }
291 if (/*@-usedef@*/(one.st_dev != two.st_dev) ||
292 (one.st_ino != two.st_ino) ||
293 (!S_ISDIR(two.st_mode))/*@+usedef@*/)
294 {
295 SH_MUTEX_LOCK(mutex_thread_nolog);
296 sh_error_handle ((-1), FIL__, __LINE__, EINVAL,
297 MSG_SUID_ERROR,
298 _("Possible race: lstat(dir) != lstat(.)"));
299 SH_MUTEX_UNLOCK(mutex_thread_nolog);
300 return -1;
301 }
302
303
304 return (do_truncate_int(q, depth));
305 }
306 else
307 {
308 /* no more '/', so this is the file
309 */
310 if (*path == '\0')
311 return -1;
312 if (0 != retry_lstat_ns(FIL__, __LINE__, path, &one))
313 {
314 SH_MUTEX_LOCK(mutex_thread_nolog);
315 sh_error_handle ((-1), FIL__, __LINE__, errno,
316 MSG_SUID_ERROR,
317 sh_error_message(errno, errbuf, sizeof(errbuf)));
318 SH_MUTEX_UNLOCK(mutex_thread_nolog);
319 return -1;
320 }
321 fd = open(path, O_RDWR);
322 if (-1 == fd)
323 {
324 SH_MUTEX_LOCK(mutex_thread_nolog);
325 sh_error_handle ((-1), FIL__, __LINE__, errno,
326 MSG_SUID_ERROR,
327 sh_error_message(errno, errbuf, sizeof(errbuf)));
328 SH_MUTEX_UNLOCK(mutex_thread_nolog);
329 return -1;
330 }
331 if (0 != retry_fstat(FIL__, __LINE__, fd, &two))
332 {
333 SH_MUTEX_LOCK(mutex_thread_nolog);
334 sh_error_handle ((-1), FIL__, __LINE__, errno,
335 MSG_SUID_ERROR,
336 sh_error_message(errno, errbuf, sizeof(errbuf)));
337 SH_MUTEX_UNLOCK(mutex_thread_nolog);
338 (void) sl_close_fd(FIL__, __LINE__, fd);
339 return -1;
340 }
341 if (/*@-usedef@*/(one.st_dev != two.st_dev) ||
342 (one.st_ino != two.st_ino)/*@+usedef@*/)
343 {
344 SH_MUTEX_LOCK(mutex_thread_nolog);
345 sh_error_handle ((-1), FIL__, __LINE__, EINVAL,
346 MSG_SUID_ERROR,
347 _("Possible race: lstat != fstat"));
348 SH_MUTEX_UNLOCK(mutex_thread_nolog);
349 (void) sl_close_fd(FIL__, __LINE__, fd);
350 return -1;
351 }
352 if (!S_ISREG(two.st_mode))
353 {
354 SH_MUTEX_LOCK(mutex_thread_nolog);
355 sh_error_handle ((-1), FIL__, __LINE__, EINVAL,
356 MSG_SUID_ERROR,
357 _("Possible race: not a regular file"));
358 SH_MUTEX_UNLOCK(mutex_thread_nolog);
359 (void) sl_close_fd(FIL__, __LINE__, fd);
360 return -1;
361 }
362 if ((0 == (two.st_mode & S_ISUID)) && (0 == (two.st_mode & S_ISGID)))
363 {
364 SH_MUTEX_LOCK(mutex_thread_nolog);
365 sh_error_handle ((-1), FIL__, __LINE__, EINVAL,
366 MSG_SUID_ERROR,
367 _("Possible race: not a suid/sgid file"));
368 SH_MUTEX_UNLOCK(mutex_thread_nolog);
369 (void) sl_close_fd(FIL__, __LINE__, fd);
370 return -1;
371 }
372 if (ShSuidchkQDelete == S_FALSE)
373 {
374 if ((two.st_mode & S_ISUID) > 0)
375 two.st_mode -= S_ISUID;
376 if ((two.st_mode & S_ISGID) > 0)
377 two.st_mode -= S_ISGID;
378#ifdef HAVE_FCHMOD
379 if (-1 == /*@-unrecog@*/fchmod(fd, two.st_mode)/*@+unrecog@*/)
380 {
381 SH_MUTEX_LOCK(mutex_thread_nolog);
382 sh_error_handle ((-1), FIL__, __LINE__, errno,
383 MSG_SUID_ERROR,
384 sh_error_message(errno, errbuf, sizeof(errbuf)));
385 SH_MUTEX_UNLOCK(mutex_thread_nolog);
386 (void) sl_close_fd(FIL__, __LINE__, fd);
387 return -1;
388 }
389#else
390 SH_MUTEX_LOCK(mutex_thread_nolog);
391 sh_error_handle ((-1), FIL__, __LINE__, errno,
392 MSG_SUID_ERROR,
393 _("The fchmod() function is not available"));
394 SH_MUTEX_UNLOCK(mutex_thread_nolog);
395 (void) sl_close_fd(FIL__, __LINE__, fd);
396 return -1;
397#endif
398 if (two.st_nlink > 1)
399 {
400 SH_MUTEX_LOCK(mutex_thread_nolog);
401 sh_error_handle ((-1), FIL__, __LINE__, 0,
402 MSG_SUID_ERROR,
403 _("Not truncated because hardlink count gt 1"));
404 SH_MUTEX_UNLOCK(mutex_thread_nolog);
405 (void) sl_close_fd(FIL__, __LINE__, fd);
406 return -1;
407 }
408 /* The man page says: 'POSIX has ftruncate'
409 */
410 if (-1 == /*@-unrecog@*/ftruncate(fd, 0)/*@+unrecog@*/)
411 {
412 SH_MUTEX_LOCK(mutex_thread_nolog);
413 sh_error_handle ((-1), FIL__, __LINE__, errno,
414 MSG_SUID_ERROR,
415 sh_error_message(errno, errbuf, sizeof(errbuf)));
416 SH_MUTEX_UNLOCK(mutex_thread_nolog);
417 (void) sl_close_fd(FIL__, __LINE__, fd);
418 return -1;
419 }
420 }
421 else
422 {
423 if (-1 == retry_aud_unlink(FIL__, __LINE__, path))
424 {
425 SH_MUTEX_LOCK(mutex_thread_nolog);
426 sh_error_handle ((-1), FIL__, __LINE__, errno,
427 MSG_SUID_ERROR,
428 sh_error_message(errno, errbuf, sizeof(errbuf)));
429 SH_MUTEX_UNLOCK(mutex_thread_nolog);
430 (void) sl_close_fd(FIL__, __LINE__, fd);
431 return -1;
432 }
433 }
434 (void) sl_close_fd (FIL__, __LINE__, fd);
435 return (0);
436 }
437}
438
439static int do_truncate (const char * path_in)
440{
441 volatile int caperr;
442 int result;
443 char * path;
444 char errbuf[SH_ERRBUF_SIZE];
445
446 if (0 != chdir("/"))
447 {
448 SH_MUTEX_LOCK(mutex_thread_nolog);
449 sh_error_handle ((-1), FIL__, __LINE__, errno,
450 MSG_SUID_ERROR,
451 sh_error_message(errno, errbuf, sizeof(errbuf)));
452 SH_MUTEX_UNLOCK(mutex_thread_nolog);
453 }
454
455 if (0 != (caperr = sl_get_cap_qdel()))
456 {
457 SH_MUTEX_LOCK(mutex_thread_nolog);
458 sh_error_handle((-1), FIL__, __LINE__, caperr, MSG_E_SUBGEN,
459 sh_error_message (caperr, errbuf, sizeof(errbuf)),
460 _("sl_get_cap_qdel"));
461 SH_MUTEX_UNLOCK(mutex_thread_nolog);
462 }
463
464 path = sh_util_strdup (path_in);
465 result = do_truncate_int (path, 0);
466 SH_FREE(path);
467
468 if (0 != (caperr = sl_drop_cap_qdel()))
469 {
470 SH_MUTEX_LOCK(mutex_thread_nolog);
471 sh_error_handle((-1), FIL__, __LINE__, caperr, MSG_E_SUBGEN,
472 sh_error_message (caperr, errbuf, sizeof(errbuf)),
473 _("sl_drop_cap_qdel"));
474 SH_MUTEX_UNLOCK(mutex_thread_nolog);
475 }
476
477 if (0 != chdir("/"))
478 {
479 SH_MUTEX_LOCK(mutex_thread_nolog);
480 sh_error_handle ((-1), FIL__, __LINE__, errno,
481 MSG_SUID_ERROR,
482 sh_error_message(errno, errbuf, sizeof(errbuf)));
483 SH_MUTEX_UNLOCK(mutex_thread_nolog);
484 }
485 return result;
486}
487
488/* This variable is not used anywhere. It only exists
489 * to assign &dirlist to it, which keeps gcc from
490 * putting it into a register, and avoids the 'clobbered
491 * by longjmp' warning. And no, 'volatile' proved insufficient.
492 */
493static void * sh_dummy_tmp = NULL;
494
495static void sh_q_delete(const char * fullpath)
496{
497 int status;
498 char * msg;
499 char * tmp;
500
501 /* Take the address to keep gcc from putting it into a register.
502 * Avoids the 'clobbered by longjmp' warning.
503 */
504 sh_dummy_tmp = (void*) &tmp;
505
506 if (do_truncate (fullpath) == -1)
507 {
508 status = errno;
509 msg = SH_ALLOC(SH_BUFSIZE);
510 tmp = sh_util_safe_name(fullpath);
511
512 (void) sl_snprintf(msg, SH_BUFSIZE,
513 _("Problem quarantining file. File NOT quarantined. errno = %ld"),
514 status);
515 SH_MUTEX_LOCK(mutex_thread_nolog);
516 sh_error_handle (ShSuidchkSeverity,
517 FIL__, __LINE__,
518 status,
519 MSG_SUID_QREPORT, msg,
520 tmp );
521 SH_MUTEX_UNLOCK(mutex_thread_nolog);
522 SH_FREE(tmp);
523 SH_FREE(msg);
524 }
525 else
526 {
527 tmp = sh_util_safe_name(fullpath);
528 SH_MUTEX_LOCK(mutex_thread_nolog);
529 sh_error_handle (ShSuidchkSeverity,
530 FIL__, __LINE__, 0,
531 MSG_SUID_QREPORT,
532 _("Quarantine method applied"),
533 tmp );
534 SH_MUTEX_UNLOCK(mutex_thread_nolog);
535 SH_FREE(tmp);
536 }
537 return;
538}
539
540/* This variable is not used anywhere. It only exists
541 * to assign &dirlist to it, which keeps gcc from
542 * putting it into a register, and avoids the 'clobbered
543 * by longjmp' warning. And no, 'volatile' proved insufficient.
544 */
545static void * sh_dummy_mtmp = NULL;
546static void * sh_dummy_mmsg = NULL;
547
548static void sh_q_move(const char * fullpath, file_type * theFile,
549 const char * timestrc, const char * timestra,
550 const char * timestrm)
551{
552 volatile int status;
553 int readFile = -1;
554 volatile int writeFile = -1;
555 struct stat fileInfo;
556 ssize_t count;
557 char * msg;
558 char * tmp;
559 char * basetmp;
560 char * filetmp;
561 char buffer[1024];
562 char * dir = SH_ALLOC(PATH_MAX+1);
563 mode_t umask_old;
564 FILE * filePtr = NULL;
565
566 /* Take the address to keep gcc from putting it into a register.
567 * Avoids the 'clobbered by longjmp' warning.
568 */
569 sh_dummy_mtmp = (void*) &tmp;
570 sh_dummy_mmsg = (void*) &msg;
571
572 (void) sl_strlcpy (dir, DEFAULT_QDIR, PATH_MAX+1);
573
574 if (retry_stat (FIL__, __LINE__, dir, &fileInfo) != 0)
575 {
576 /* Quarantine directory does not exist,
577 */
578 status = errno;
579 msg = SH_ALLOC(SH_BUFSIZE);
580 tmp = sh_util_safe_name(fullpath);
581
582 (void) sl_snprintf(msg, SH_BUFSIZE,
583 _("Problem quarantining file. File NOT quarantined. errno = %ld (stat)"),
584 status);
585 SH_MUTEX_LOCK(mutex_thread_nolog);
586 sh_error_handle (ShSuidchkSeverity,
587 FIL__, __LINE__,
588 status,
589 MSG_SUID_QREPORT, msg,
590 tmp );
591 SH_MUTEX_UNLOCK(mutex_thread_nolog);
592 SH_FREE(tmp);
593 SH_FREE(msg);
594 }
595 else
596 {
597 if (retry_lstat (FIL__, __LINE__,
598 fullpath, &fileInfo) == -1)
599 {
600 status = errno;
601 msg = SH_ALLOC(SH_BUFSIZE);
602 tmp = sh_util_safe_name(fullpath);
603
604 (void) sl_snprintf(msg, SH_BUFSIZE, _("I/O error. errno = %ld(stat)"), status);
605 SH_MUTEX_LOCK(mutex_thread_nolog);
606 sh_error_handle (ShSuidchkSeverity,
607 FIL__, __LINE__,
608 status,
609 MSG_SUID_QREPORT,
610 msg, tmp );
611 SH_MUTEX_UNLOCK(mutex_thread_nolog);
612 SH_FREE(tmp);
613 SH_FREE(msg);
614 }
615 else
616 {
617 basetmp = sh_util_strdup(fullpath);
618 filetmp = SH_ALLOC(PATH_MAX+1);
619 tmp = sh_util_basename(basetmp);
620
621 (void) sl_snprintf(filetmp, PATH_MAX+1, "%s/%s",
622 DEFAULT_QDIR, tmp);
623 SH_FREE(tmp);
624 SH_FREE(basetmp);
625
626 readFile = open (fullpath, O_RDONLY);
627 if (readFile != -1)
628 writeFile = open (filetmp, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IXUSR);
629
630 if ((readFile == -1) || (writeFile == -1))
631 {
632 status = errno;
633 msg = SH_ALLOC(SH_BUFSIZE);
634 tmp = sh_util_safe_name(fullpath);
635
636 (void) sl_snprintf(msg, SH_BUFSIZE, _("Problem quarantining file. File NOT quarantined. errno = %ld (open)"), status);
637 SH_MUTEX_LOCK(mutex_thread_nolog);
638 sh_error_handle (ShSuidchkSeverity,
639 FIL__, __LINE__, status,
640 MSG_SUID_QREPORT,
641 msg, tmp );
642 SH_MUTEX_UNLOCK(mutex_thread_nolog);
643 SH_FREE(tmp);
644 SH_FREE(msg);
645 }
646 else
647 {
648 /* sizeof(buffer) is 1024
649 */
650 while ((count = (int) read (readFile, buffer, sizeof (buffer))) > 0)
651 {
652 if ((int) write (writeFile, buffer, (size_t) count) != count)
653 {
654 status = errno;
655 msg = SH_ALLOC(SH_BUFSIZE);
656 tmp = sh_util_safe_name(fullpath);
657
658 (void) sl_snprintf(msg, SH_BUFSIZE,
659 _("I/O error. errno = %ld (write)"), status);
660 SH_MUTEX_LOCK(mutex_thread_nolog);
661 sh_error_handle (ShSuidchkSeverity,
662 FIL__,
663 __LINE__,
664 status,
665 MSG_SUID_QREPORT,
666 msg, tmp );
667 SH_MUTEX_UNLOCK(mutex_thread_nolog);
668 SH_FREE(tmp);
669 SH_FREE(msg);
670 }
671 }
672 }
673
674 (void) sl_close_fd (FIL__, __LINE__, readFile);
675 (void) fchmod(writeFile, S_IRUSR | S_IWUSR | S_IXUSR);
676 (void) sl_close_fd (FIL__, __LINE__, writeFile);
677
678 if (do_truncate (fullpath) == -1)
679 {
680 status = errno;
681 msg = SH_ALLOC(SH_BUFSIZE);
682 tmp = sh_util_safe_name(fullpath);
683
684 (void) sl_snprintf(msg, SH_BUFSIZE,
685 _("Problem quarantining file. File NOT quarantined. errno = %ld"),
686 status);
687 SH_MUTEX_LOCK(mutex_thread_nolog);
688 sh_error_handle (ShSuidchkSeverity,
689 FIL__, __LINE__, status,
690 MSG_SUID_QREPORT,
691 msg, tmp );
692 SH_MUTEX_UNLOCK(mutex_thread_nolog);
693 SH_FREE(tmp);
694 SH_FREE(msg);
695 }
696 else
697 {
698 tmp = sh_util_basename(fullpath);
699
700 (void) sl_snprintf(filetmp, PATH_MAX+1, "%s/%s.info",
701 DEFAULT_QDIR,
702 tmp);
703
704 SH_FREE(tmp);
705 /*
706 * avoid chmod by setting umask
707 */
708 umask_old = umask (0077);
709 filePtr = fopen (filetmp, "w+");
710
711 /*@-usedef@*/
712 if (filePtr)
713 {
714 fprintf(filePtr,
715 _("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"),
716 fullpath,
717 (unsigned long) theFile->size,
718 theFile->c_owner, (int) theFile->owner,
719 theFile->c_group, (int) theFile->group,
720 timestrc, timestra, timestrm);
721 (void) sl_fclose (FIL__, __LINE__, filePtr);
722 }
723 /*@+usedef@*/
724 umask (umask_old);
725
726 tmp = sh_util_safe_name(fullpath);
727 SH_MUTEX_LOCK(mutex_thread_nolog);
728 sh_error_handle (ShSuidchkSeverity,
729 FIL__,__LINE__,
730 0, MSG_SUID_QREPORT,
731 _("Quarantine method applied"),
732 tmp );
733 SH_MUTEX_UNLOCK(mutex_thread_nolog);
734 SH_FREE(tmp);
735 }
736 SH_FREE(filetmp);
737 }
738 }
739 SH_FREE(dir);
740 return;
741}
742
743/* This variable is not used anywhere. It only exists
744 * to assign &dirlist to it, which keeps gcc from
745 * putting it into a register, and avoids the 'clobbered
746 * by longjmp' warning. And no, 'volatile' proved insufficient.
747 */
748static void * sh_dummy_ctmp = NULL;
749static void * sh_dummy_cmsg = NULL;
750
751static void sh_q_changeperm(const char * fullpath)
752{
753 volatile int caperr;
754 volatile int status;
755 char * msg;
756 char * tmp;
757 struct stat fileInfo;
758 struct stat fileInfo_F;
759 int cperm_status = 0;
760 volatile int file_d = -1;
761 char errbuf[SH_ERRBUF_SIZE];
762
763 /* Take the address to keep gcc from putting it into a register.
764 * Avoids the 'clobbered by longjmp' warning.
765 */
766 sh_dummy_ctmp = (void*) &tmp;
767 sh_dummy_cmsg = (void*) &msg;
768
769 if (retry_lstat(FIL__, __LINE__, fullpath, &fileInfo) == -1)
770 {
771 status = errno;
772 msg = SH_ALLOC(SH_BUFSIZE);
773 tmp = sh_util_safe_name(fullpath);
774
775 (void) sl_snprintf(msg, SH_BUFSIZE, _("I/O error. errno = %ld"), status);
776 SH_MUTEX_LOCK(mutex_thread_nolog);
777 sh_error_handle (ShSuidchkSeverity,
778 FIL__, __LINE__,
779 status,
780 MSG_SUID_QREPORT, msg,
781 tmp );
782 SH_MUTEX_UNLOCK(mutex_thread_nolog);
783 SH_FREE(tmp);
784 SH_FREE(msg);
785 cperm_status = -1;
786 }
787
788 if (cperm_status == 0)
789 {
790 if (0 != (caperr = sl_get_cap_qdel()))
791 {
792 SH_MUTEX_LOCK(mutex_thread_nolog);
793 sh_error_handle((-1), FIL__, __LINE__,
794 caperr, MSG_E_SUBGEN,
795 sh_error_message (caperr, errbuf, sizeof(errbuf)),
796 _("sl_get_cap_qdel"));
797 SH_MUTEX_UNLOCK(mutex_thread_nolog);
798 cperm_status = -1;
799 }
800 }
801
802 if (cperm_status == 0)
803 {
804 file_d = aud_open (FIL__, __LINE__, SL_YESPRIV,
805 fullpath, O_RDONLY, 0);
806 if (-1 == file_d)
807 {
808 status = errno;
809 msg = SH_ALLOC(SH_BUFSIZE);
810 tmp = sh_util_safe_name(fullpath);
811
812 (void) sl_snprintf(msg, SH_BUFSIZE, _("I/O error. errno = %ld"), status);
813 SH_MUTEX_LOCK(mutex_thread_nolog);
814 sh_error_handle (ShSuidchkSeverity,
815 FIL__, __LINE__,
816 status,
817 MSG_SUID_QREPORT, msg,
818 tmp );
819 SH_MUTEX_UNLOCK(mutex_thread_nolog);
820 SH_FREE(tmp);
821 SH_FREE(msg);
822 cperm_status = -1;
823 }
824 }
825
826 if (cperm_status == 0)
827 {
828 if (retry_fstat(FIL__, __LINE__, file_d, &fileInfo_F) == -1)
829 {
830 status = errno;
831 msg = SH_ALLOC(SH_BUFSIZE);
832 tmp = sh_util_safe_name(fullpath);
833
834 (void) sl_snprintf(msg, SH_BUFSIZE,
835 _("I/O error. errno = %ld"), status);
836 SH_MUTEX_LOCK(mutex_thread_nolog);
837 sh_error_handle (ShSuidchkSeverity,
838 FIL__, __LINE__,
839 status,
840 MSG_SUID_QREPORT, msg,
841 tmp );
842 SH_MUTEX_UNLOCK(mutex_thread_nolog);
843 SH_FREE(tmp);
844 SH_FREE(msg);
845 cperm_status = -1;
846 }
847 }
848
849 if (cperm_status == 0)
850 {
851 if (fileInfo_F.st_ino != fileInfo.st_ino ||
852 fileInfo_F.st_dev != fileInfo.st_dev ||
853 fileInfo_F.st_mode != fileInfo.st_mode)
854 {
855 status = errno;
856 msg = SH_ALLOC(SH_BUFSIZE);
857 tmp = sh_util_safe_name(fullpath);
858
859 (void) sl_snprintf(msg, SH_BUFSIZE,
860 _("Race detected. errno = %ld"), status);
861 SH_MUTEX_LOCK(mutex_thread_nolog);
862 sh_error_handle (ShSuidchkSeverity,
863 FIL__, __LINE__,
864 status,
865 MSG_SUID_QREPORT, msg,
866 tmp );
867 SH_MUTEX_UNLOCK(mutex_thread_nolog);
868 SH_FREE(tmp);
869 SH_FREE(msg);
870 cperm_status = -1;
871 }
872 }
873
874 if ((fileInfo.st_mode & S_ISUID) > 0)
875 fileInfo.st_mode -= S_ISUID;
876 if ((fileInfo.st_mode & S_ISGID) > 0)
877 fileInfo.st_mode -= S_ISGID;
878
879 if (cperm_status == 0)
880 {
881 if (fchmod(file_d, fileInfo.st_mode) == -1)
882 {
883 status = errno;
884 msg = SH_ALLOC(SH_BUFSIZE);
885 tmp = sh_util_safe_name(fullpath);
886
887 (void) sl_snprintf(msg, SH_BUFSIZE,
888 _("Problem quarantining file. File NOT quarantined. errno = %ld"),
889 status);
890 SH_MUTEX_LOCK(mutex_thread_nolog);
891 sh_error_handle (ShSuidchkSeverity,
892 FIL__, __LINE__,
893 status,
894 MSG_SUID_QREPORT,
895 msg, tmp );
896 SH_MUTEX_UNLOCK(mutex_thread_nolog);
897 SH_FREE(tmp);
898 SH_FREE(msg);
899 }
900 else
901 {
902 tmp = sh_util_safe_name(fullpath);
903 SH_MUTEX_LOCK(mutex_thread_nolog);
904 sh_error_handle (ShSuidchkSeverity,
905 FIL__, __LINE__,
906 0,
907 MSG_SUID_QREPORT,
908 _("Quarantine method applied"),
909 tmp );
910 SH_MUTEX_UNLOCK(mutex_thread_nolog);
911 SH_FREE(tmp);
912 }
913 }
914
915 if (0 != (caperr = sl_drop_cap_qdel()))
916 {
917 SH_MUTEX_LOCK(mutex_thread_nolog);
918 sh_error_handle((-1), FIL__, __LINE__,
919 caperr, MSG_E_SUBGEN,
920 sh_error_message (caperr, errbuf, sizeof(errbuf)),
921 _("sl_drop_cap_qdel"));
922 SH_MUTEX_UNLOCK(mutex_thread_nolog);
923 }
924
925 if (file_d != -1)
926 {
927 do {
928 status = sl_close_fd (FIL__, __LINE__, file_d);
929 } while (status == -1 && errno == EINTR);
930
931 if (-1 == status)
932 {
933 status = errno;
934 msg = SH_ALLOC(SH_BUFSIZE);
935 tmp = sh_util_safe_name(fullpath);
936
937 (void) sl_snprintf(msg, SH_BUFSIZE,
938 _("I/O error. errno = %ld"), status);
939 SH_MUTEX_LOCK(mutex_thread_nolog);
940 sh_error_handle (ShSuidchkSeverity,
941 FIL__, __LINE__,
942 status,
943 MSG_SUID_QREPORT, msg,
944 tmp );
945 SH_MUTEX_UNLOCK(mutex_thread_nolog);
946 SH_FREE(tmp);
947 SH_FREE(msg);
948 }
949 }
950 return;
951}
952
953static void report_file (const char * tmpcat, file_type * theFile,
954 char * timestrc, char * timestra, char * timestrm)
955{
956 char * msg = SH_ALLOC(SH_BUFSIZE);
957 char * tmp = sh_util_safe_name(tmpcat);
958
959 msg[0] = '\0';
960 /*@-usedef@*/
961
962#ifdef SH_USE_XML
963 (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\""),
964 theFile->c_owner, theFile->owner,
965 theFile->c_group, theFile->group,
966 (unsigned long) theFile->size,
967 timestrc, timestra, timestrm);
968#else
969 (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>"),
970 theFile->c_owner, theFile->owner,
971 theFile->c_group, theFile->group,
972 (unsigned long) theFile->size,
973 timestrc, timestra, timestrm);
974#endif
975 /*@+usedef@*/
976
977 SH_MUTEX_LOCK(mutex_thread_nolog);
978 sh_error_handle (ShSuidchkSeverity, FIL__, __LINE__,
979 0, MSG_SUID_POLICY,
980 _("suid/sgid file not in database"),
981 tmp, msg );
982 SH_MUTEX_UNLOCK(mutex_thread_nolog);
983 SH_FREE(tmp);
984 SH_FREE(msg);
985 return;
986}
987
988/* This variable is not used anywhere. It only exists
989 * to assign &dirlist to it, which keeps gcc from
990 * putting it into a register, and avoids the 'clobbered
991 * by longjmp' warning. And no, 'volatile' proved insufficient.
992 */
993void * sh_dummy_idirlist = NULL;
994void * sh_dummy_itmp = NULL;
995
996
997static
998int sh_suidchk_check_internal (char * iname)
999{
1000 DIR * thisDir = NULL;
1001 struct dirent * thisEntry;
1002 char * tmpcat;
1003 char * tmp;
1004 char timestrc[32];
1005 char timestra[32];
1006 char timestrm[32];
1007 struct stat buf;
1008 volatile int status;
1009 int fflags;
1010 char * fs;
1011 volatile long sl_status;
1012 file_type * theFile = NULL;
1013 char fileHash[2*(KEY_LEN + 1)];
1014
1015 struct sh_dirent * dirlist;
1016 struct sh_dirent * dirlist_orig;
1017 char errbuf[SH_ERRBUF_SIZE];
1018
1019 SL_ENTER(_("sh_suidchk_check_internal"));
1020
1021 if (iname == NULL)
1022 {
1023 TPT((0, FIL__, __LINE__ , _("msg=<directory name is NULL>\n")));
1024 SL_RETURN( (-1), _("sh_suidchk_check_internal"));
1025 }
1026
1027 if (sig_urgent > 0) {
1028 SL_RETURN( (0), _("sh_suidchk_check_internal"));
1029 }
1030
1031 /* Take the address to keep gcc from putting it into a register.
1032 * Avoids the 'clobbered by longjmp' warning.
1033 */
1034 sh_dummy_idirlist = (void*) &dirlist;
1035 sh_dummy_itmp = (void*) &tmp;
1036
1037 thisDir = opendir (iname);
1038
1039 if (thisDir == NULL)
1040 {
1041 status = errno;
1042 tmp = sh_util_safe_name(iname);
1043 SH_MUTEX_LOCK(mutex_thread_nolog);
1044 sh_error_handle (ShDFLevel[SH_ERR_T_DIR], FIL__, __LINE__, status,
1045 MSG_E_OPENDIR,
1046 sh_error_message (status, errbuf, sizeof(errbuf)), tmp);
1047 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1048 SH_FREE(tmp);
1049 sh_dummy_idirlist = NULL;
1050 sh_dummy_itmp = NULL;
1051 SL_RETURN( (-1), _("sh_suidchk_check_internal"));
1052 }
1053
1054 /* Loop over directory entries
1055 */
1056 SH_MUTEX_LOCK(mutex_readdir);
1057
1058 dirlist = NULL;
1059 dirlist_orig = NULL;
1060
1061 do {
1062
1063 thisEntry = readdir (thisDir);
1064
1065 if (thisEntry != NULL) {
1066
1067 if (sl_strcmp (thisEntry->d_name, ".") == 0)
1068 continue;
1069
1070 if (sl_strcmp (thisEntry->d_name, "..") == 0)
1071 continue;
1072
1073 dirlist = addto_sh_dirlist (thisEntry, dirlist);
1074 }
1075
1076 } while (thisEntry != NULL);
1077
1078 SH_MUTEX_UNLOCK(mutex_readdir);
1079
1080 closedir(thisDir);
1081
1082 dirlist_orig = dirlist;
1083
1084 sl_status = SL_ENONE;
1085
1086 do {
1087
1088 /* If the directory is empty, dirlist = NULL
1089 */
1090 if (!dirlist)
1091 break;
1092
1093 if (sig_urgent > 0) {
1094 SL_RETURN( (0), _("sh_suidchk_check_internal"));
1095 }
1096
1097 tmpcat = SH_ALLOC(PATH_MAX);
1098 (void) sl_strlcpy(tmpcat, iname, PATH_MAX);
1099
1100 if ((sl_strlen(tmpcat) != sl_strlen(iname)) || (tmpcat[0] == '\0'))
1101 {
1102 sl_status = SL_ETRUNC;
1103 }
1104 else
1105 {
1106 if (tmpcat[1] != '\0')
1107 sl_status = sl_strlcat(tmpcat, "/", PATH_MAX);
1108 }
1109
1110 if (! SL_ISERROR(sl_status))
1111 sl_status = sl_strlcat(tmpcat, dirlist->sh_d_name, PATH_MAX);
1112
1113 if (SL_ISERROR(sl_status))
1114 {
1115 tmp = sh_util_safe_name(tmpcat);
1116 SH_MUTEX_LOCK(mutex_thread_nolog);
1117 sh_error_handle ((-1), FIL__, __LINE__, (int) sl_status,
1118 MSG_E_SUBGPATH,
1119 _("path too long"),
1120 _("sh_suidchk_check_internal"), tmp );
1121 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1122 SH_FREE(tmp);
1123 SH_FREE(tmpcat);
1124 dirlist = dirlist->next;
1125 continue;
1126 }
1127
1128 ++FileLimNum;
1129 ++FileLimTotal;
1130
1131 /* Rate limit (Fps == Files per second)
1132 */
1133 if ((ShSuidchkFps > 0 && FileLimNum > ShSuidchkFps) &&
1134 (ShSuidchkYield == S_FALSE))
1135 {
1136 FileLimNum = 0;
1137 FileLimNow = time(NULL);
1138
1139 if ( (FileLimNow - FileLimStart) > 0 &&
1140 FileLimTotal/(FileLimNow - FileLimStart) > ShSuidchkFps )
1141 (void) retry_msleep((int)((FileLimTotal/(FileLimNow-FileLimStart))/
1142 ShSuidchkFps) , 0);
1143 }
1144
1145 status = (int) retry_lstat(FIL__, __LINE__, tmpcat, &buf);
1146
1147 if (status != 0)
1148 {
1149 volatile int elevel = SH_ERR_ERR;
1150 size_t tlen;
1151
1152 status = errno;
1153 tmp = sh_util_safe_name(tmpcat);
1154 tlen = strlen(tmp);
1155 if (tlen >= 6 && 0 == strcmp(&tmp[tlen-6], _("/.gvfs")))
1156 elevel = SH_ERR_NOTICE;
1157 else if (tlen >= 5 && 0 == strcmp(&tmp[tlen-5], _("/gvfs")))
1158 elevel = SH_ERR_NOTICE;
1159
1160 /* If we are scanning a temporary directory where dirs and files
1161 * can be created/deleted, an lstat() error is something which
1162 * may occur frequently. As a missing dir/file is not an important
1163 * problem for the suidcheck, the error level is only SH_ERR_NOTICE.
1164 */
1165 if (status == ENOENT)
1166 elevel = SH_ERR_NOTICE;
1167
1168 SH_MUTEX_LOCK(mutex_thread_nolog);
1169 sh_error_handle (elevel, FIL__, __LINE__, status, MSG_ERR_LSTAT,
1170 sh_error_message(status, errbuf, sizeof(errbuf)),
1171 tmp );
1172 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1173 SH_FREE(tmp);
1174 }
1175 else
1176 {
1177 if (/*@-usedef@*/S_ISDIR(buf.st_mode)/*@+usedef@*/ &&
1178 (ShSuidchkExclude == NULL ||
1179 NULL == zAVL_string_get(ShSuidchkExclude, tmpcat)))
1180 {
1181 /* fs is a STATIC string or NULL
1182 */
1183 fs = filesystem_type (tmpcat, tmpcat, &buf);
1184
1185 if (fs != NULL
1186#ifndef SH_SUIDTESTDIR
1187 &&
1188 0 != strncmp (_("afs"), fs, 3) &&
1189 0 != strncmp (_("devfs"), fs, 5) &&
1190 0 != strncmp (_("fdesc"), fs, 5) &&
1191 0 != strncmp (_("iso9660"), fs, 7) &&
1192 0 != strncmp (_("cd9660"), fs, 6) &&
1193 0 != strncmp (_("lustre"), fs, 6) &&
1194 0 != strncmp (_("mmfs"), fs, 4) &&
1195 0 != strncmp (_("msdos"), fs, 5) &&
1196 0 != strncmp (_("nfs"), fs, 3) &&
1197 0 != strncmp (_("proc"), fs, 4) &&
1198 0 != strncmp (_("sysfs"), fs, 5) &&
1199 0 != strncmp (_("vfat"), fs, 4)
1200#endif
1201 )
1202 {
1203 if ((ShSuidchkNosuid == S_TRUE) ||
1204 (0 != strncmp (_("nosuid"), fs, 6)))
1205 /* fprintf(stderr, "%s: %s\n", fs, tmpcat); */
1206 (void) sh_suidchk_check_internal(tmpcat);
1207 }
1208 }
1209 else if (S_ISREG(buf.st_mode) &&
1210 (0 !=(S_ISUID & buf.st_mode) ||
1211#if defined(HOST_IS_LINUX)
1212 (0 !=(S_ISGID & buf.st_mode) &&
1213 0 !=(S_IXGRP & buf.st_mode))
1214#else
1215 0 !=(S_ISGID & buf.st_mode)
1216#endif
1217 )
1218 )
1219 {
1220 int dummy;
1221 int class;
1222 unsigned long check_flags = 0;
1223
1224 theFile = SH_ALLOC(sizeof(file_type));
1225
1226 (void) sl_strlcpy (theFile->fullpath, tmpcat, PATH_MAX);
1227 theFile->check_flags = sh_files_maskof(SH_LEVEL_READONLY);
1228 CLEAR_SH_FFLAG_REPORTED(theFile->file_reported);
1229 theFile->attr_string = NULL;
1230 theFile->link_path = NULL;
1231
1232 sh_files_search_file(tmpcat, &class, &check_flags, &dummy);
1233 if ((check_flags & MODI_PREL) != 0)
1234 MODI_SET(theFile->check_flags, MODI_PREL);
1235
1236 status = sh_unix_getinfo (ShDFLevel[SH_ERR_T_RO],
1237 dirlist->sh_d_name,
1238 theFile, fileHash, 0);
1239
1240 tmp = sh_util_safe_name(tmpcat);
1241
1242 if (status != 0)
1243 {
1244 SH_MUTEX_LOCK(mutex_thread_nolog);
1245 sh_error_handle (ShSuidchkSeverity, FIL__, __LINE__,
1246 0, MSG_E_SUBGPATH,
1247 _("Could not check suid/sgid file"),
1248 _("sh_suidchk_check_internal"),
1249 tmp);
1250 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1251 }
1252 else
1253 {
1254
1255 if ( sh.flag.update == S_TRUE &&
1256 (sh.flag.checkSum == SH_CHECK_INIT ||
1257 sh.flag.checkSum == SH_CHECK_CHECK))
1258 {
1259 int compret;
1260
1261 /* Updating database. Report new files that
1262 * are not in database already. Then compare
1263 * to database and report changes.
1264 */
1265 if (-1 == sh_hash_have_it (tmpcat))
1266 {
1267 SH_MUTEX_LOCK(mutex_thread_nolog);
1268 sh_error_handle ((-1), FIL__, __LINE__,
1269 0, MSG_SUID_FOUND, tmp );
1270 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1271 }
1272 else
1273 {
1274 SH_MUTEX_LOCK(mutex_thread_nolog);
1275 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
1276 0, MSG_SUID_FOUND, tmp );
1277 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1278 }
1279
1280 SH_MUTEX_LOCK(mutex_thread_nolog);
1281 compret = sh_hash_compdata (SH_LEVEL_READONLY,
1282 theFile, fileHash,
1283 _("[SuidCheck]"),
1284 ShSuidchkSeverity);
1285 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1286
1287 if (compret == 0)
1288 {
1289 sh_hash_pushdata_memory (theFile, fileHash); /* no call to sh_error_handle */
1290 }
1291
1292 sh_hash_set_flag(tmpcat, SH_FFLAG_SUIDCHK); /* no call to sh_error_handle */
1293
1294 }
1295
1296 else if (sh.flag.checkSum == SH_CHECK_INIT &&
1297 sh.flag.update == S_FALSE )
1298 {
1299 /* Running init. Report on files detected.
1300 */
1301 sh_dbIO_rootfs_strip(theFile->fullpath);
1302 sh_dbIO_data_write (theFile, fileHash); /* no call to sh_error_handle */
1303 SH_MUTEX_LOCK(mutex_thread_nolog);
1304 sh_error_handle ((-1), FIL__, __LINE__,
1305 0, MSG_SUID_FOUND, tmp );
1306 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1307 }
1308
1309 else if (sh.flag.checkSum == SH_CHECK_CHECK )
1310 {
1311 /* Running file check. Report on new files
1312 * detected, and quarantine them.
1313 */
1314 SH_MUTEX_LOCK(mutex_thread_nolog);
1315 sh_error_handle (SH_ERR_ALL, FIL__, __LINE__,
1316 0, MSG_SUID_FOUND, tmp );
1317 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1318
1319 fflags = sh_hash_getflags(tmpcat); /* no call to sh_error_handle */
1320
1321 if ( (-1 == fflags) || (!SH_FFLAG_SUIDCHK_SET(fflags)))
1322 {
1323 if (-1 == fflags)
1324 {
1325 (void) sh_unix_gmttime (theFile->ctime, timestrc, sizeof(timestrc));
1326 (void) sh_unix_gmttime (theFile->atime, timestra, sizeof(timestra));
1327 (void) sh_unix_gmttime (theFile->mtime, timestrm, sizeof(timestrm));
1328
1329 report_file(tmpcat, theFile, timestrc, timestra, timestrm);
1330 }
1331 /* Quarantine file according to configured method
1332 */
1333 if (ShSuidchkQEnable == S_TRUE)
1334 {
1335 switch (ShSuidchkQMethod)
1336 {
1337 case SH_Q_DELETE:
1338 sh_q_delete(theFile->fullpath);
1339 break;
1340 case SH_Q_CHANGEPERM:
1341 sh_q_changeperm(theFile->fullpath);
1342 break;
1343 case SH_Q_MOVE:
1344 sh_q_move(theFile->fullpath, theFile, timestrc, timestra, timestrm);
1345 break;
1346 default:
1347 SH_MUTEX_LOCK(mutex_thread_nolog);
1348 sh_error_handle (ShSuidchkSeverity, FIL__,
1349 __LINE__, 0, MSG_SUID_QREPORT,
1350 _("Bad quarantine method"), tmp);
1351 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1352 break;
1353 }
1354 }
1355 else
1356 {
1357 /* 1.8.1 push file to in-memory database
1358 */
1359 SH_MUTEX_LOCK(mutex_thread_nolog);
1360 (void) sh_hash_compdata (SH_LEVEL_READONLY,
1361 theFile, fileHash,
1362 _("[SuidCheck]"),
1363 ShSuidchkSeverity);
1364 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1365
1366 sh_hash_set_flag(tmpcat, SH_FFLAG_SUIDCHK); /* no call to sh_error_handle */
1367
1368 }
1369 }
1370 else
1371 {
1372 /* File exists. Check for modifications.
1373 */
1374 SH_MUTEX_LOCK(mutex_thread_nolog);
1375 (void) sh_hash_compdata (SH_LEVEL_READONLY,
1376 theFile, fileHash,
1377 _("[SuidCheck]"),
1378 ShSuidchkSeverity);
1379 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1380 sh_hash_set_flag(tmpcat, SH_FFLAG_SUIDCHK); /* no call to sh_error_handle */
1381
1382 }
1383 }
1384 }
1385 SH_FREE(tmp);
1386 if (theFile->attr_string) SH_FREE(theFile->attr_string);
1387 if (theFile->link_path) SH_FREE(theFile->link_path);
1388 SH_FREE(theFile);
1389 }
1390 }
1391 SH_FREE(tmpcat);
1392
1393
1394#ifdef HAVE_SCHED_YIELD
1395 if (ShSuidchkYield == S_TRUE)
1396 {
1397 if (sched_yield() == -1)
1398 {
1399 status = errno;
1400 SH_MUTEX_LOCK(mutex_thread_nolog);
1401 sh_error_handle ((-1), FIL__, __LINE__, status, MSG_E_SUBGEN,
1402 _("Failed to release time slice"),
1403 _("sh_suidchk_check_internal") );
1404 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1405 }
1406 }
1407#endif
1408
1409 dirlist = dirlist->next;
1410
1411 } while (dirlist != NULL);
1412
1413
1414 kill_sh_dirlist (dirlist_orig);
1415
1416 sh_dummy_idirlist = NULL;
1417 sh_dummy_itmp = NULL;
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 * filesystem_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
2214 return current_fstype;
2215}
2216
2217/* Return a newly allocated string naming the type of filesystem that the
2218 file PATH, described by STATP, is on.
2219 RELPATH is the file name relative to the current directory.
2220 Return "unknown" if its filesystem type is unknown. */
2221
2222void * sh_dummy_2229_type;
2223
2224static char * filesystem_type_uncached (char * path, char * relpath, struct stat * statp)
2225{
2226 char * type = NULL;
2227#ifdef MFSNAMELEN /* NetBSD. */
2228 static char my_tmp_type[64];
2229#endif
2230
2231#ifdef FSTYPE_MNTENT /* 4.3BSD, SunOS, HP-UX, Dynix, Irix,Linux */
2232 char *table = MOUNTED;
2233 FILE *mfp;
2234 struct mntent *mnt;
2235
2236 if (path == NULL || relpath == NULL)
2237 return NULL;
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 mfp = setmntent (table, "r");
2245 if (mfp == NULL)
2246 {
2247 SH_MUTEX_LOCK(mutex_thread_nolog);
2248 sh_error_handle ((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
2249 _("setmntent() failed"),
2250 _("filesystem_type_uncached") );
2251 SH_MUTEX_UNLOCK(mutex_thread_nolog);
2252 sh_dummy_2229_type = NULL;
2253 return NULL;
2254 }
2255
2256 /* Find the entry with the same device number as STATP, and return
2257 that entry's fstype. */
2258 while (type == NULL && (mnt = getmntent (mfp)) != NULL)
2259 {
2260 const char *devopt;
2261 dev_t dev;
2262 struct stat disk_stats;
2263
2264#ifdef MNTTYPE_IGNORE
2265 if (0 == strcmp (mnt->mnt_type, MNTTYPE_IGNORE))
2266 continue;
2267#endif
2268
2269 /* Newer systems like SunOS 4.1 keep the dev number in the mtab,
2270 in the options string. For older systems, we need to stat the
2271 directory that the filesystem is mounted on to get it.
2272
2273 Unfortunately, the HPUX 9.x mnttab entries created by automountq
2274 contain a dev= option but the option value does not match the
2275 st_dev value of the file (maybe the lower 16 bits match?). */
2276
2277#if !defined(hpux) && !defined(__hpux__)
2278 devopt = sl_strstr (mnt->mnt_opts, "dev=");
2279 if (devopt)
2280 {
2281 if (devopt[4] == '0' && (devopt[5] == 'x' || devopt[5] == 'X'))
2282 dev = (dev_t) xatoi (devopt + 6);
2283 else
2284 dev = (dev_t) xatoi (devopt + 4);
2285 }
2286 else
2287#endif /* not hpux */
2288 {
2289 if (stat (mnt->mnt_dir, &disk_stats) == -1)
2290 {
2291 char errmsg[256];
2292 volatile int elevel = SH_ERR_ERR;
2293 size_t tlen = strlen(mnt->mnt_dir);
2294
2295 if (tlen >= 6 && 0 == strcmp(&((mnt->mnt_dir)[tlen-6]), _("/.gvfs")))
2296 elevel = SH_ERR_NOTICE;
2297 else if (tlen >= 5 && 0 == strcmp(&((mnt->mnt_dir)[tlen-5]), _("/gvfs")))
2298 elevel = SH_ERR_NOTICE;
2299 else if (0 == strcmp (mnt->mnt_type, _("tracefs")))
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 continue;
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_2229_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_2229_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 sh_dummy_2229_type = NULL;
2382 return NULL;
2383 }
2384 }
2385 else if (!sysfs (GETFSTYP, fss.f_fstyp, typebuf))
2386 type = typebuf;
2387#endif
2388
2389#ifdef FSTYPE_STATVFS /* SVR4. */
2390 struct statvfs fss;
2391
2392 if (path == NULL || relpath == NULL)
2393 return NULL;
2394
2395 /* Take the address to keep gcc from putting it into a register.
2396 * Avoids the 'clobbered by longjmp' warning.
2397 */
2398 sh_dummy_2229_type = (void*) &type;
2399
2400 if (statvfs (relpath, &fss) == -1)
2401 {
2402 /* Don't die if a file was just removed. */
2403 if (errno != ENOENT)
2404 {
2405 SH_MUTEX_LOCK(mutex_thread_nolog);
2406 sh_error_handle ((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
2407 _("statvfs() failed"),
2408 _("filesystem_type_uncached") );
2409 SH_MUTEX_UNLOCK(mutex_thread_nolog);
2410 sh_dummy_2229_type = NULL;
2411 return NULL;
2412 }
2413 }
2414 else
2415 {
2416 type = fss.f_basetype;
2417
2418 /* patch by Konstantin Khrooschev <nathoo@co.ru>
2419 */
2420 if( (fss.f_flag & ST_NOSUID) && (ShSuidchkNosuid == S_FALSE))
2421 type = _("nosuid");
2422 }
2423 (void) statp; /* fix compiler warning */
2424#endif
2425
2426#ifdef FSTYPE_STATFS /* 4.4BSD. */
2427 struct statfs fss;
2428 char *p;
2429#if defined(MNT_VISFLAGMASK) && defined(HAVE_STRUCT_STATFS_F_FLAGS)
2430 int flags;
2431#endif
2432 /* char * sh_dirname(const char *path); */
2433
2434 if (path == NULL || relpath == NULL)
2435 return NULL;
2436
2437 /* Take the address to keep gcc from putting it into a register.
2438 * Avoids the 'clobbered by longjmp' warning.
2439 */
2440 sh_dummy_2229_type = (void*) &type;
2441
2442 if (S_ISLNK (statp->st_mode))
2443 p = sh_dirname (relpath);
2444 else
2445 p = relpath;
2446
2447 if (statfs (p, &fss) == -1)
2448 {
2449 /* Don't die if symlink to nonexisting file, or a file that was
2450 just removed. */
2451 if (errno != ENOENT)
2452 {
2453 SH_MUTEX_LOCK(mutex_thread_nolog);
2454 sh_error_handle ((-1), FIL__, __LINE__, errno, MSG_E_SUBGEN,
2455 _("statfs() failed"),
2456 _("filesystem_type_uncached") );
2457 SH_MUTEX_UNLOCK(mutex_thread_nolog);
2458 sh_dummy_2229_type = NULL;
2459 return NULL;
2460 }
2461 }
2462 else
2463 {
2464
2465#ifdef MFSNAMELEN /* NetBSD. */
2466 /* MEMORY LEAK !!!
2467 * type = sh_util_strdup (fss.f_fstypename);
2468 */
2469 sl_strlcpy (my_tmp_type, fss.f_fstypename, 64);
2470 type = my_tmp_type;
2471#else
2472 type = fstype_to_string (fss.f_type);
2473#endif
2474
2475#ifdef HAVE_STRUCT_STATFS_F_FLAGS
2476#ifdef MNT_VISFLAGMASK
2477 flags = fss.f_flags & MNT_VISFLAGMASK;
2478 if ((flags & MNT_NOSUID) && (ShSuidchkNosuid == S_FALSE))
2479#else
2480 if ((fss.f_flags & MNT_NOSUID) && (ShSuidchkNosuid == S_FALSE))
2481#endif
2482 type = _("nosuid");
2483#endif
2484 }
2485 if (p != relpath)
2486 SH_FREE (p);
2487#endif
2488
2489#ifdef AFS
2490 if ((!type || !strcmp (type, "xx")) && in_afs (relpath))
2491 type = "afs";
2492#endif
2493
2494 /* An unknown value can be caused by an ENOENT error condition.
2495 Don't cache those values. */
2496 fstype_known = (int)(type != NULL);
2497
2498 sh_dummy_2229_type = NULL;
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 xatoi (const char * cp)
2508{
2509 int val;
2510
2511 val = 0;
2512 while (*cp != '\0')
2513 {
2514 /*@+charint@*/
2515 if (*cp >= 'a' && *cp <= 'f')
2516 val = val * 16 + *cp - 'a' + 10;
2517 else if (*cp >= 'A' && *cp <= 'F')
2518 val = val * 16 + *cp - 'A' + 10;
2519 else if (*cp >= '0' && *cp <= '9')
2520 val = val * 16 + *cp - '0';
2521 else
2522 break;
2523 /*@-charint@*/
2524 cp++;
2525 }
2526 return val;
2527}
2528#endif
2529
2530
2531
2532#endif
2533
2534
2535/* #ifdef SH_USE_UTMP */
2536#endif
2537
2538
2539
Note: See TracBrowser for help on using the repository browser.