source: trunk/src/sh_suidchk.c@ 298

Last change on this file since 298 was 257, checked in by katerina, 15 years ago

Fix for issues with debug code and testsuite (tickets #174, #175).

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