source: trunk/src/sh_suidchk.c @ 474

Last change on this file since 474 was 474, checked in by katerina, 7 years ago

Fix for ticket #372 (Replace obsolete smatch by clang in test suite).

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