source: trunk/src/sh_suidchk.c@ 399

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

Fix for ticket #281 (warnings from clang static analyzer).

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