source: trunk/src/sh_suidchk.c

Last change on this file was 543, checked in by katerina, 3 years ago

Fix for ticket #434 (option to init for alternative root fs).

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