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