source: trunk/src/sh_suidchk.c@ 446

Last change on this file since 446 was 440, checked in by katerina, 11 years ago

Fix for ticket #344 (Problems with Ubuntu 13.04)

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