source: trunk/src/sh_suidchk.c @ 481

Last change on this file since 481 was 481, checked in by katerina, 6 years ago

Enhancements and fixes for tickets #374, #375, #376, #377, #378, and #379.

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