source: trunk/src/sh_suidchk.c@ 351

Last change on this file since 351 was 335, checked in by katerina, 14 years ago

Module cleanup/thread restart at configuration reload.

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