source: trunk/src/trustfile.c

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

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

File size: 29.6 KB
Line 
1/* debug problems        */
2/* #define TRUST_DEBUG   */
3
4/* switch off full check */
5/* #define TEST_ONLY     */
6
7/* standalone            */
8/* #define TRUST_MAIN    */
9/* $(CC) -DTRUST_MAIN -DSL_ALWAYS_TRUSTED=...  */
10
11/* LINTLIBRARY */
12/*
13 * This is the file with all the library routines in it
14 *
15 * Author information:
16 * Matt Bishop
17 * Department of Computer Science
18 * University of California at Davis
19 * Davis, CA  95616-8562
20 * phone (916) 752-8060
21 * email bishop@cs.ucdavis.edu
22 *
23 * This code is placed in the public domain.  I do ask that
24 * you keep my name associated with it, that you not represent
25 * it as written by you, and that you preserve these comments.
26 * This software is provided "as is" and without any guarantees
27 * of any sort.
28 *
29 * Compilation notes:
30 * * this does NOT use malloc(3), but fixed storage.  this means we
31 *   do lots of bounds checking, but it still is faster, and smaller,
32 *   than forcing inclusion of malloc.  All buffers etc. are of size
33 *   MAXFILENAME (defined in trustfile.h); to get more room, recompile
34 *   with this set larger.
35 * * if you support the following directory semantics, define STICKY;
36 *   otherwise, undefine it
37 *      "if a directory is both world-writeable AND has the sticky bit
38 *       set, then ONLY the owner of an existing file may delete it"
39 *   On some systems (eg, IRIX), you can delete the file under these
40 *   conditions if the file is world writeable.  Foor our purposes,
41 *   this is irrelevant since if the file is world-writeable it is
42 *   untrustworthy; either it can be replaced with another file (the
43 *   IRIX version) or it can be altered (all versions).
44 *   if this is true and STICKY is not set, the sticky bit is ignored
45 *   and the directory will be flagged as untrustworthy, even when only
46 *   a trusted user could delete the file
47 * * this uses a library call to get the name of the current working
48 *   directory.  Define the following to get the various versions:
49 *   GETCWD     for Solaris 2.x, SunOS 4.1.x, IRIX 5.x
50 *                      char *getcwd(char *buf, int bufsz);
51 *              where buf is a buffer for the path name, and bufsz is
52 *              the size of the buffer; if the size if too small, you
53 *              get an error return (NULL)
54 *   GETWD      for Ultrix 4.4
55 *                      char *getwd(char *buf)
56 *              where buf is a buffer for the path name, and it is
57 *              assumed to be at lease as big as MAXPATHLEN.
58 *              *** IMPORTANT NOTE ***
59 *              Ultrix supports getcwd as well, but it uses popen to
60 *              run the command "pwd" (according to the manual).  This
61 *              means it's vulnerable to a number of attacks if used
62 *              in a privileged program.  YOU DON'T WANT THIS.
63 * * the debugging flag DEBUG prints out each step of the file name
64 *   checking, as well as info on symbolic links (if S_IFLNK defined),
65 *   file name canonicalization, and user, group, and permission for
66 *   each file or directory; this is useful if you want to be sure
67 *   you're checking the right file
68 *
69 * Version information:
70 * 1.0          December 28, 1995       Matt Bishop
71 *
72 * 2.0          March    26, 2000       Rainer Wichmann -- adapted for slib.
73 */
74
75/* --- Why strcpy is safe here: ----                                  */
76
77/* The input path is checked once, and then either shortened [in dirz()],
78 * or safely expanded (symlinks) with bound checking.
79 * I.e., the path length can never exceed (MAXFILENAME-1), while the path
80 * is always copied between buffers of length MAXFILENAME.
81 */
82
83#ifndef  TRUST_MAIN
84#include "config_xor.h"
85#include "sh_calls.h"
86#else
87#define UID_CAST long
88#define HAVE_GETPWENT
89#define SH_MUTEX_LOCK(a)   ((void)0)
90#define SH_MUTEX_UNLOCK(a) ((void)0)
91#endif
92
93#include <stdio.h>
94#include <stdlib.h>
95#include <sys/types.h>
96#include <sys/stat.h>
97#include <grp.h>
98#include <pwd.h>
99#include <string.h>
100#include <unistd.h>
101#include <errno.h>
102
103
104#if !defined(TRUST_MAIN)
105
106#include "slib.h"
107#define SH_NEED_PWD_GRP 1
108#include "sh_static.h"
109#include "sh_pthread.h"
110
111#else
112
113#define sh_getgrgid   getgrgid
114#define sh_getgrgid_r getgrgid_r
115#define sh_getpwnam   getpwnam
116#define sh_getpwnam_r getpwnam_r
117#define sh_getpwuid   getpwuid
118#define sh_getpwuid_r getpwuid_r
119#define sh_getpwent   getpwent
120#define sh_endpwent   endpwent
121
122#define TRUST_DEBUG
123#define S_FALSE 0
124#define S_TRUE  1
125#define SL_ENTER(string)
126#define SL_IRETURN(a, b)  return a
127#define retry_lstat(a,b,c,d) lstat(c,d)
128#define _(string)  string
129#define N_(string) string
130#define MAXFILENAME     4096
131static int sl_errno = 0;
132#define SL_ENONE         0
133#define SL_ENULL     -1024     /* Invalid use of NULL pointer.         */
134#define SL_ERANGE    -1025     /* Argument out of range.               */
135#define SL_ETRUNC    -1026     /* Result truncated.                    */
136#define SL_EINTERNAL -1028     /* Internal error.                      */
137#define SL_EBADFILE  -1030     /* File access error. Check errno.      */
138#define SL_EMEM      -1032     /* Out of memory.                       */
139#define SL_EBADNAME  -1040     /* Invalid name.                        */
140#define SL_ESTAT     -1041     /* stat of file failed. Check errno.    */
141#define SL_EBADUID   -1050      /* Owner not trustworthy.              */
142#define SL_EBADGID   -1051      /* Group writeable and not trustworthy.*/
143#define SL_EBADOTH   -1052      /* World writeable.                    */
144
145#endif
146
147
148#if defined(__linux__) || defined(__FreeBSD__)
149#define STICKY
150#endif
151
152#undef  FIL__
153#define FIL__  _("trustfile.c")
154
155/*
156 * the get current working directory function
157 * every version of UNIX seems to have its own
158 * idea of how to do this, so we group them by
159 * arguments ...
160 * all must return a pointer to the right name
161 */
162
163
164#ifndef TRUST_MAIN
165
166#if defined(HAVE_GETCWD) && !defined(HAVE_BROKEN_GETCWD)
167#define CURDIR(buf,nbuf)        getcwd((buf), (nbuf))
168#elif defined(HAVE_GETWD)
169#define CURDIR(buf,nbuf)        getwd((buf))
170#endif
171
172#else
173
174#define CURDIR(buf,nbuf)        getcwd((buf), (nbuf))
175
176#endif
177
178
179
180/*
181 * this checks to see if there are symbolic links
182 * assumes the link bit in the protection mask is called S_IFLNK
183 * (seems to be true on all UNIXes with them)
184 */
185#ifndef S_IFLNK
186#define lstat   stat
187#endif
188
189
190/*
191 * these are useful global variables
192 *
193 * first set: who you gonna trust, by default?
194 *      if the user does not specify a trusted or untrusted set of users,
195 *      all users are considered untrusted EXCEPT:
196 *      UID 0 -- root   as root can do anything on most UNIX systems, this
197 *                      seems reasonable
198 *      tf_euid -- programmer-selectable UID
199 *                      if the caller specifies a specific UID by putting
200 *                      it in this variable, it will be trusted; this is
201 *                      typically used to trust the effective UID of the
202 *                      process (note: NOT the real UID, which will cause all
203 *                      sorts of problems!)  By default, this is set to -1,
204 *                      so if it's not set, root is the only trusted user
205 */
206
207/* modified Tue Feb 22 10:36:44 NFT 2000 Rainer Wichmann                */
208
209
210#ifndef SL_ALWAYS_TRUSTED
211#define SL_ALWAYS_TRUSTED  0
212#endif
213uid_t test_rootonly[] = { SL_ALWAYS_TRUSTED };
214
215#define tf_uid_neg ((uid_t)-1)
216
217uid_t rootonly[] = { SL_ALWAYS_TRUSTED, 
218                    tf_uid_neg, tf_uid_neg, tf_uid_neg, tf_uid_neg, 
219                    tf_uid_neg, tf_uid_neg, tf_uid_neg, tf_uid_neg, 
220                    tf_uid_neg, tf_uid_neg, tf_uid_neg, tf_uid_neg, 
221                    tf_uid_neg, tf_uid_neg, tf_uid_neg, tf_uid_neg };
222
223uid_t tf_euid = tf_uid_neg;
224unsigned int EUIDSLOT = sizeof(test_rootonly)/sizeof(uid_t);
225unsigned int ORIG_EUIDSLOT = sizeof(test_rootonly)/sizeof(uid_t);
226
227char  tf_path[MAXFILENAME];             /* error path for trust function */
228uid_t tf_baduid;
229gid_t tf_badgid;
230
231static 
232int dirz(char *path)
233{
234  register char *p = path;/* points to rest of path to clean up */
235  register char *q;     /* temp pointer for skipping over stuff */
236
237  static   char swp[MAXFILENAME];
238
239  SL_ENTER(_("dirz"));
240  /*
241   * standard error checking
242   */
243  if (path == NULL)
244    SL_IRETURN(SL_ENULL, _("dirz"));
245  if (path[0] == '.')
246    SL_IRETURN(SL_EINTERNAL, _("dirz"));
247 
248 
249  /*
250   * loop over the path name until everything is checked
251   */
252  while(*p)
253    {
254      /* skip
255       */
256      if (*p != '/')
257        {
258          p++;
259          continue;
260        }
261
262      /* "/./" or "/."
263       */
264      if (p[1] == '.' && (p[2] == '/' || p[2] == '\0'))
265        {
266          /* yes -- delete "/."
267           */
268          (void) strcpy(swp, &p[2]);                     /* known to fit  */
269          (void) strcpy(p, swp);                         /* known to fit  */
270
271          /* special case "/." as full path name
272           */
273          if (p == path && *p == '\0')
274            {
275            *p++ = '/';
276            *p = '\0';
277          }
278      }
279
280      /* "//"
281       */
282      else if (p[1] == '/')
283        {
284          /* yes -- skip
285           */
286          for(q = &p[2]; *q == '/'; q++)
287            ;
288          (void) strcpy(swp, q);                         /* known to fit  */
289          (void) strcpy(&p[1], swp);                     /* known to fit  */
290        }
291
292      /* "/../" or "/.."
293       */
294      else if (p[1] == '.' && p[2] == '.' && (p[3] == '/' || p[3] == '\0'))
295        {
296          /* yes -- if it's root, delete .. only
297           */
298          if (p == path)
299            {
300              (void) strcpy(swp, &p[3]);                 /* known to fit  */
301              (void) strcpy(p, swp);                     /* known to fit  */
302            }
303          else
304            {
305              /* back up over previous component
306               */
307              q = p - 1;
308              while(q != path && *q != '/')
309                q--;
310              /* now wipe it out
311               */
312              (void) strcpy(swp, &p[3]);                 /* known to fit  */
313              (void) strcpy(q, swp);                     /* known to fit  */
314              p = q;
315            }
316        }
317      else
318        p++;
319    }
320  SL_IRETURN(SL_ENONE, _("dirz"));
321}
322                       
323
324
325/* not static to circumvent stupid gcc 4 bug */ 
326int getfname(const char *fname, char *rbuf, int rsz)
327{
328#ifndef TRUST_MAIN
329  register int status;
330#endif
331
332  SL_ENTER(_("getfname"));
333  /*
334   * do the initial checking
335   * NULL pointer
336   */
337  if (fname == NULL || rbuf == NULL)
338    SL_IRETURN(SL_ENULL, _("getfname"));
339  if (rsz <= 0)
340    SL_IRETURN(SL_ERANGE, _("getfname"));
341 
342 
343  /* already a full path name */
344  if (*fname == '/')
345    rbuf[0] = '\0';
346  else
347    {
348      if (CURDIR(rbuf, rsz)  == NULL)
349        {
350#ifdef TRUST_DEBUG
351          fprintf(stderr, "trustfile: getcwd failed\n");
352#endif
353          SL_IRETURN(SL_EBADNAME, _("getfname"));
354        }
355    }
356 
357  /*
358   * append the file name and reduce
359   */
360  if (fname != NULL && *fname != '\0')
361    {
362#ifndef TRUST_MAIN
363      status = sl_strlcat(rbuf, "/", rsz);
364      if (status == SL_ENONE)
365        status = sl_strlcat(rbuf, fname, rsz);
366      if (status != SL_ENONE)
367        SL_IRETURN(status, _("getfname"));
368#else
369      strncat(rbuf, "/",   rsz-strlen(rbuf)-1);
370      rbuf[rsz-1] = '\0';
371      strncat(rbuf, fname, rsz-strlen(rbuf)-1);
372      rbuf[rsz-1] = '\0';
373#endif
374    }
375  SL_IRETURN(dirz(rbuf), _("getfname"));
376}
377
378static 
379int isin(uid_t n, uid_t *list)
380{
381  SL_ENTER(_("isin"));
382  if (list == NULL)
383    SL_IRETURN(S_FALSE, _("isin"));
384
385  while(*list != tf_uid_neg && *list != n)
386    {
387#ifdef TRUST_DEBUG
388      fprintf (stderr, 
389               "trustfile: owner_uid=%ld, trusted uid=%ld, no match\n", 
390               (UID_CAST) n, (UID_CAST) *list);
391#endif
392      list++;
393    }
394
395  if (*list == tf_uid_neg)
396    {
397#ifdef TRUST_DEBUG
398      fprintf (stderr, 
399               "trustfile: owner_uid=%ld, no match with any trusted user --> ERROR\n", 
400               (UID_CAST) n);
401#endif
402      SL_IRETURN(S_FALSE, _("isin"));
403    }
404
405#ifdef TRUST_DEBUG
406  fprintf (stderr, 
407           "trustfile: owner_uid=%ld, trusted_uid=%ld, match found --> OK\n", 
408           (UID_CAST)n, (UID_CAST)*list);
409#endif
410  SL_IRETURN(S_TRUE, _("isin"));
411}
412
413/* comment added by R. Wichmann
414 *  RETURN TRUE if ANYONE in ulist is group member
415 */
416/* not static to circumvent stupid gcc 4 bug */ 
417int isingrp(gid_t grp, uid_t *ulist, int * errval)
418{
419  struct passwd *w;             /* info about group member */
420  register uid_t *u;            /* points to current ulist member */
421  register char **p;            /* points to current group member */
422  struct group *g;              /* pointer to group information */
423
424  int status;
425 
426#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETGRGID_R)
427  struct group    gr;
428  char          * buffer = NULL;
429  struct passwd   pwd;
430  char          * pbuffer = NULL;
431#endif
432
433  SL_ENTER(_("isingrp"));
434
435  *errval = 0;
436
437#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETGRGID_R)
438  buffer = calloc(1,SH_GRBUF_SIZE);
439  status = sh_getgrgid_r(grp, &gr, buffer, SH_GRBUF_SIZE, &g);
440#else
441  errno = 0;
442  g = sh_getgrgid(grp);
443  status = errno;
444#endif
445
446  if (g == NULL)
447    {
448      if (status == ERANGE)
449        *errval = status;
450
451      goto end_false;
452    }
453
454  /* this will return at the first match
455   */
456#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETGRGID_R)
457  pbuffer = calloc(1,SH_PWBUF_SIZE);
458#endif
459
460  for(p = g->gr_mem; *p != NULL; p++)
461    {
462      for(u = ulist; *u != tf_uid_neg; u++)
463        {
464          /* map user name to UID and compare */
465#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETPWNAM_R)
466          sh_getpwnam_r(*p, &pwd, pbuffer, SH_PWBUF_SIZE, &w);
467#else
468          w = sh_getpwnam(*p);
469#endif
470
471#ifdef TRUST_MAIN
472          if (w != NULL && *u == (uid_t)(w->pw_uid) )
473            goto end_true;
474#else
475          if (w != NULL && *u == (uid_t)(w->pw_uid) )
476            {
477              goto end_true;
478            }
479#endif
480        }
481    }
482  /* added by R. Wichmann Fri Mar 30 08:16:14 CEST 2001:
483   * a user can have a GID but no entry in /etc/group
484   */
485  for(u = ulist; *u != tf_uid_neg; u++)
486    {
487#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETPWUID_R)
488      sh_getpwuid_r(*u, &pwd, pbuffer, SH_PWBUF_SIZE, &w);
489#else
490      w = sh_getpwuid(*u);
491#endif
492#ifdef TRUST_MAIN
493      if (w != NULL && grp == (gid_t)(w->pw_gid) )
494        goto end_true;
495#else
496      if (w != NULL && grp == (gid_t)(w->pw_gid) )
497        {
498          goto end_true;
499        }
500#endif
501    }
502
503 end_false:
504#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETGRGID_R)
505  if (buffer)  free(buffer);
506  if (pbuffer) free(pbuffer);
507#endif
508  SL_IRETURN(S_FALSE, _("isingrp"));
509
510 end_true:
511#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETGRGID_R)
512  if (buffer)  free(buffer);
513  if (pbuffer) free(pbuffer);
514#endif
515  SL_IRETURN(S_TRUE, _("isingrp"));
516}
517
518/* added by R. Wichmann Fri Mar 30 08:16:14 CEST 2001
519 *  RETURN TRUE only if ALL group members are trusted
520 */
521/* not static to circumvent stupid gcc 4 bug */ 
522int onlytrustedingrp(gid_t grp, uid_t *ulist, int * errval)
523{
524  struct passwd *w;             /* info about group member */
525  register uid_t *u;            /* points to current ulist member */
526  register char **p;            /* points to current group member */
527  struct group *g;              /* pointer to group information */
528  register int flag = -1;       /* group member found */
529
530  int status;
531
532#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETGRGID_R)
533  struct group    gr;
534  char          * buffer  = NULL;
535  struct passwd   pw;
536  char          * pbuffer = NULL;
537#endif
538
539  int retval = S_FALSE;
540
541  SL_ENTER(_("onlytrustedingrp"));
542
543  *errval = 0;
544
545#ifdef TRUST_DEBUG
546  fprintf(stderr, "trustfile: group writeable, group_gid: %ld\n", 
547          (UID_CAST)grp); 
548#endif
549
550#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETGRGID_R)
551  buffer = calloc(1,SH_GRBUF_SIZE);
552  status = sh_getgrgid_r(grp, &gr, buffer, SH_GRBUF_SIZE, &g);
553#else
554  errno = 0;
555  g = sh_getgrgid(grp);
556  status = errno;
557#endif
558
559  if (g == NULL)
560    {
561      if (status == ERANGE)
562        *errval = status;
563
564#ifdef TRUST_DEBUG
565      fprintf(stderr, 
566              "trustfile: group_gid: %ld, no such group --> ERROR\n", 
567              (UID_CAST)grp); 
568#endif
569      retval = S_FALSE;
570      goto end_retval;
571    }
572
573  /* empty group -> no problem
574   
575  if(g->gr_mem == NULL || g->gr_mem[0] == NULL )
576    SL_IRETURN(S_TRUE, _("onlytrustedingrp") );
577  */
578
579  /* check for untrusted members of the group
580   */
581#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETPWNAM_R)
582  pbuffer = calloc(1,SH_PWBUF_SIZE);
583#endif
584
585  for(p = g->gr_mem; *p != NULL; p++)
586    {
587      flag = -1;
588#ifdef TRUST_DEBUG
589      fprintf(stderr, "trustfile: group_member: %s\n", *p); 
590#endif
591      /* map user name to UID and compare
592       */
593#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETPWNAM_R)
594      sh_getpwnam_r(*p, &pw, pbuffer, SH_PWBUF_SIZE, &w);
595#else
596      w = sh_getpwnam(*p);
597#endif
598
599      if (w == NULL)    /* not a valid user, ignore    */
600        {
601          flag = 0; 
602        }
603      else              /* check list of trusted users */
604        {
605#ifdef TRUST_DEBUG
606          fprintf (stderr, 
607                   "trustfile: uid=%ld, checking whether it is trusted\n",
608                   (UID_CAST)(w->pw_uid));
609#endif
610          for(u = ulist; *u != tf_uid_neg; u++)
611            {
612              if (*u == (w->pw_uid) )
613                {
614#ifdef TRUST_DEBUG
615                  fprintf (stderr, 
616                           "trustfile: uid=%ld, trusted_uid=%ld, match found --> OK\n", 
617                           (UID_CAST)(w->pw_uid), (UID_CAST)*u);
618#endif
619                  flag = 0;
620                  break;
621                }
622              else
623                {
624#ifdef TRUST_DEBUG
625                  fprintf (stderr, 
626                           "trustfile: uid=%ld, trusted_uid=%ld, no match\n", 
627                           (UID_CAST)(w->pw_uid), (UID_CAST)*u);
628#endif
629                  ;
630                }
631            }
632        }
633      /* not found
634       */
635      if (flag == -1)
636        {
637#ifdef TRUST_DEBUG
638          fprintf (stderr, 
639                   "trustfile: user=%s (gid %ld), not a trusted user --> ERROR\n", *p, (UID_CAST)grp);
640#endif
641          tf_baduid = w->pw_uid;
642          retval = S_FALSE;
643#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETGRGID_R)
644          if (pbuffer) free(pbuffer);
645#endif
646          goto end_retval;
647        }
648    }
649
650#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETGRGID_R)
651  if (pbuffer) free(pbuffer);
652#endif
653
654#ifndef TEST_ONLY       
655#ifdef HAVE_GETPWENT
656  /* now check ALL users for their GID !!!
657   */
658  SH_MUTEX_LOCK(mutex_pwent);
659
660  while (NULL != (w = sh_getpwent())) 
661    {
662      if (grp == (gid_t)(w->pw_gid))
663        {
664#ifdef TRUST_DEBUG
665          fprintf(stderr, "trustfile: checking group member %s, uid %ld\n", 
666                  w->pw_name, (UID_CAST)w->pw_uid); 
667#endif
668          /* is it a trusted user ?
669           */
670          flag = -1;
671          for(u = ulist; *u != tf_uid_neg; u++)
672            {
673              if (*u == (uid_t)(w->pw_uid))
674                {
675#ifdef TRUST_DEBUG
676                  fprintf (stderr, 
677                           "trustfile: uid=%ld, trusted_uid=%ld, match found --> OK\n", 
678                           (UID_CAST)(w->pw_uid), (UID_CAST)(*u));
679#endif
680                  flag = 0;
681                  break;
682                }
683              else
684                {
685#ifdef TRUST_DEBUG
686                  fprintf (stderr, 
687                           "trustfile: uid=%ld, trusted_uid=%ld, no match\n", 
688                           (UID_CAST)(w->pw_uid), (UID_CAST)*u);
689#endif
690                  ;
691                }
692            }
693          /* not found */
694          if (flag == -1)
695            {
696#ifdef TRUST_DEBUG
697              fprintf(stderr,"trustfile: group member %s not found in trusted users --> ERROR\n", w->pw_name); 
698#endif
699              tf_baduid = w->pw_uid;
700              retval = S_FALSE;
701              goto out;
702              /* SL_IRETURN(S_FALSE, _("onlytrustedingrp")); */
703            }
704        }
705    }
706  retval = S_TRUE;
707
708 out:
709
710#ifdef HAVE_ENDPWENT
711  sh_endpwent();
712#endif
713
714  SH_MUTEX_UNLOCK(mutex_pwent);
715
716  /* TEST_ONLY */
717#endif
718  /* #ifdef HAVE_GETPWENT */
719#endif
720
721#ifdef TRUST_DEBUG
722  if (retval == S_TRUE)
723    fprintf(stderr,
724            "trustfile: group %ld:  all members are trusted users --> OK\n", 
725            (UID_CAST)grp);
726#endif
727  /* all found
728   */
729 end_retval:
730#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETGRGID_R)
731  if (buffer)  free(buffer);
732#endif
733  SL_IRETURN(retval, _("onlytrustedingrp"));
734}
735
736int sl_trustfile(const char *fname, uid_t *okusers, uid_t *badusers)
737{
738  char * fexp = NULL;           /* file name fully expanded        */
739  register char *p;             /* used to hold name to be checked */
740  struct stat stbuf;            /* used to check file permissions  */
741  char c;                       /* used to hold temp char          */
742 
743  int errgrp = 0;
744
745  SL_ENTER(_("sl_trustfile"));
746  if (fname == NULL)
747    SL_IRETURN(SL_EBADFILE, _("sl_trustfile"));
748
749  fexp = calloc(1, MAXFILENAME );
750  if (!fexp)
751    SL_IRETURN(SL_EMEM, _("sl_trustfile"));
752
753  p = fexp;
754
755  /*
756   * next expand to the full file name
757   * getfname sets sl_errno as appropriate
758   */
759#ifdef TRUST_MAIN
760  sl_errno = getfname(fname, fexp, MAXFILENAME);
761  if (sl_errno != 0)
762    {
763      free(fexp);
764      return sl_errno;
765    }
766#else
767  if (SL_ISERROR(getfname(fname, fexp, MAXFILENAME)))
768    {
769      free(fexp);
770      SL_IRETURN(sl_errno, _("sl_trustfile"));
771    }
772#endif
773
774  if (okusers == NULL && badusers == NULL)
775    {
776      okusers = rootonly;
777      rootonly[EUIDSLOT] = tf_euid;
778    }
779
780  /*
781   * now loop through the path a component at a time
782   * note we have to special-case root
783   */
784  while(*p)
785    {
786      /*
787       * get next component
788       */
789      while(*p && *p != '/')
790        p++;
791
792      /* save where you are
793       */
794      if (p == fexp)
795        {
796          /* keep the / if it's the root dir
797           */
798          c    = p[1];
799          p[1] = '\0';
800        }
801      else
802        {
803          /* clobber the / if it isn't the root dir
804           */
805          c  = *p;
806          *p = '\0';
807        }
808
809      /*
810       * now get the information
811       */
812      if (retry_lstat(FIL__, __LINE__, fexp, &stbuf) < 0)
813        {
814          (void) strncpy(tf_path, fexp, sizeof(tf_path));
815          tf_path[sizeof(tf_path)-1] = '\0';
816#ifdef TRUST_MAIN
817          fprintf(stderr, "---------------------------------------------\n");
818          fprintf(stderr, "trustfile: ESTAT: stat(%s) failed,\n", fexp);
819          fprintf(stderr, "maybe the file does not exist\n");
820          fprintf(stderr, "---------------------------------------------\n");
821#endif
822          free(fexp);
823          SL_IRETURN(SL_ESTAT, _("sl_trustfile"));
824        }
825
826#ifdef S_IFLNK
827      /*
828       * if it's a symbolic link, recurse
829       */
830      if ((stbuf.st_mode & S_IFLNK) == S_IFLNK)
831        {
832          /*
833           * this is tricky
834           * if the symlink is to an absolute path
835           * name, just call trustfile on it; but
836           * if it's a relative path name, it's
837           * interpreted WRT the current working
838           * directory AND NOT THE FILE NAME!!!!!
839           * so, we simply put /../ at the end of
840           * the file name, then append the symlink
841           * contents; trustfile will canonicalize
842           * this, and the /../ we added "undoes"
843           * the name of the symlink to put us in
844           * the current working directory, at
845           * which point the symlink contents (appended
846           * to the CWD) are interpreted correctly.
847           * got it?
848           */
849          char * csym;                  /* contents of symlink file  */
850          char * full;                  /* "full" name of symlink    */
851          char *b;                      /* used to copy stuff around */
852          const char *t;                /* used to copy stuff around */
853          int lsym;                     /* num chars in symlink ref  */
854          int i;                        /* trustworthy or not?       */
855          const char * t_const;
856          char *end;
857
858          /*
859           * get what the symbolic link points to
860           *
861           * The original code does not check the return code of readlink(),
862           * and has an off-by-one error
863           * (MAXFILENAME instead of MAXFILENAME-1)
864           * R.W. Tue May 29 22:05:16 CEST 2001
865           */
866          csym = calloc(1, MAXFILENAME );
867          if (!csym)
868            {
869              free(fexp);
870              SL_IRETURN(SL_EMEM, _("sl_trustfile"));
871            }
872
873          lsym = readlink(fexp, csym, MAXFILENAME-1);
874          if (lsym >= 0) 
875            csym[lsym] = '\0';
876          else
877            {
878#ifdef TRUST_MAIN
879              fprintf(stderr, "---------------------------------------------\n");
880              fprintf(stderr, "trustfile: EBADNAME: readlink(%s) failed\n",
881                      fexp);
882              fprintf(stderr, "---------------------------------------------\n");
883#endif
884              free(csym);
885              free(fexp);
886              SL_IRETURN(SL_EBADNAME, _("sl_trustfile"));
887            }
888
889          full = calloc(1, MAXFILENAME );
890          if (!full)
891            {
892              free(csym);
893              free(fexp);
894              SL_IRETURN(SL_EMEM, _("sl_trustfile"));
895            }
896
897          /*
898           * relative or absolute referent?
899           */
900          if (csym[0] != '/')
901            {
902              /* pointer to one above last element
903               */
904              end = &full[MAXFILENAME-1]; ++end;
905
906              /* initialize pointers
907               */
908              b = full;
909
910              /* copy in base path
911               */
912              t = fexp;
913              while(*t && b < end)
914                *b++ = *t++;
915
916              /* smack on the /../
917               */
918              t_const = "/../"; t = (const char *)t_const;
919              while(*t && b < end)
920                *b++ = *t++;
921
922              /* append the symlink referent
923               */
924              t = csym;
925              while(*t && b < end)
926                *b++ = *t++;
927
928              /* see if we're too big
929               */
930              if (*t || b == end)
931                {
932                  /* yes -- error
933                   */
934                  (void) strncpy(tf_path, fexp, sizeof(tf_path));
935                  tf_path[sizeof(tf_path)-1] = '\0';
936#ifdef TRUST_MAIN
937                  fprintf(stderr, "---------------------------------------------\n");
938                  fprintf(stderr, 
939                          "trustfile: ETRUNC: normalized path too long (%s)\n",
940                          fexp);
941                  fprintf(stderr, "---------------------------------------------\n");
942#endif
943                  free(full);
944                  free(csym);
945                  free(fexp);
946                  SL_IRETURN(SL_ETRUNC, _("sl_trustfile"));
947                }
948              *b = '\0';
949            }
950          else
951            {
952              /* absolute -- just copy                */
953              /* overflow can't occur as the arrays   */
954              /* are the same size                    */
955              (void) strcpy(full, csym);                 /* known to fit  */
956            }
957          /*
958           * now check out this file and its ancestors
959           */
960          if ((i = sl_trustfile(full, okusers, badusers)) != SL_ENONE)
961            {
962              free(full);
963              free(csym);
964              free(fexp);
965              SL_IRETURN(i, _("sl_trustfile"));
966            }
967
968          /*
969           * okay, this part is valid ... let's check the rest
970           * put the / back
971           */
972          if (p == fexp)
973            {
974              /* special case for root */
975              p[1] = c;
976              p++;
977            }
978          else
979            {
980              /* ordinary case for everything else */
981              *p = c;
982              if (*p)
983                p++;
984            }
985          free(full);
986          free(csym);
987          continue;
988        }
989#endif
990
991                       
992#ifdef TRUST_DEBUG
993      fprintf(stderr, "\ntrustfile: checking path=%s\n", fexp); 
994#endif
995      /*
996       * if the owner is not trusted then -- as the owner can
997       * change protection modes -- he/she can write to the
998       * file regardless of permissions, so bomb
999       */
1000      if (((okusers != NULL && S_FALSE == isin((uid_t)stbuf.st_uid,okusers))||
1001           (badusers != NULL && S_TRUE == isin((uid_t)stbuf.st_uid,badusers))))
1002        {
1003#ifdef TRUST_DEBUG
1004          fprintf(stderr, "---------------------------------------------\n");
1005          fprintf(stderr, "trustfile: EBADUID %s (owner not trusted)\n", 
1006                  fexp); 
1007          fprintf(stderr, "The owner of this file/directory is not in samhains\n"); 
1008          fprintf(stderr, "list of trusted users.\n");
1009          fprintf(stderr, "Please run ./configure again with the option\n");
1010          fprintf(stderr, " ./configure [more options] --with-trusted=0,...,UID\n"); 
1011          fprintf(stderr, "where UID is the UID of the (yet) untrusted user.\n"); 
1012          fprintf(stderr, "---------------------------------------------\n");
1013#endif
1014          (void) strncpy(tf_path, fexp, sizeof(tf_path));
1015          tf_path[sizeof(tf_path)-1] = '\0';
1016
1017          tf_baduid = (uid_t) stbuf.st_uid;
1018          free(fexp);
1019          SL_IRETURN(SL_EBADUID, _("sl_trustfile"));
1020        }
1021
1022      /*
1023       * if a group member can write but the
1024       * member is not trusted, bomb; but if
1025       * sticky bit semantics are honored, it's
1026       * okay
1027       */
1028      /* Thu Mar 29 21:10:28 CEST 2001 Rainer Wichmann
1029       * replace !isingrp() with onlytrustedingrp(), as isingrp()
1030       * will return at the first trusted user, even if there are additional
1031       * (untrusted) users in the group
1032       */
1033      if (((stbuf.st_mode & S_IWGRP) == S_IWGRP) &&
1034          ((okusers != NULL && !onlytrustedingrp((gid_t)stbuf.st_gid,okusers,&errgrp))||
1035           (badusers != NULL && isingrp((gid_t)stbuf.st_gid, badusers,&errgrp)))
1036#ifdef STICKY
1037          && ((stbuf.st_mode&S_IFDIR) != S_IFDIR ||
1038              (stbuf.st_mode&S_ISVTX) != S_ISVTX)
1039#endif
1040          )
1041        {
1042#ifdef TRUST_DEBUG
1043          fprintf(stderr, "---------------------------------------------\n");
1044          fprintf(stderr, 
1045                  "trustfile: EBADGID %ld %s (group member not trusted)\n", 
1046                  (UID_CAST)stbuf.st_gid, fexp);
1047          fprintf(stderr, "This file/directory is group writeable, and one of the group members\n");
1048          fprintf(stderr, "is not in samhains list of trusted users.\n"); 
1049          fprintf(stderr, "Please run ./configure again with the option\n");
1050          fprintf(stderr, " ./configure [more options] --with-trusted=0,...,UID\n"); 
1051          fprintf(stderr, "where UID is the UID of the (yet) untrusted user.\n"); 
1052          fprintf(stderr, "---------------------------------------------\n");
1053#endif
1054          (void) strncpy(tf_path, fexp, sizeof(tf_path));
1055          tf_path[sizeof(tf_path)-1] = '\0';
1056
1057          tf_badgid = (gid_t) stbuf.st_gid;
1058          free(fexp);
1059          SL_IRETURN((errgrp == ERANGE) ? SL_ERANGE : SL_EBADGID, _("sl_trustfile"));
1060        }
1061      /*
1062       * if other can write, bomb; but if the sticky
1063       * bit semantics are honored, it's okay
1064       */
1065      if (((stbuf.st_mode & S_IWOTH) == S_IWOTH)
1066#ifdef STICKY
1067          && ((stbuf.st_mode&S_IFDIR) != S_IFDIR ||
1068              (stbuf.st_mode&S_ISVTX) != S_ISVTX)
1069#endif
1070          )
1071        {
1072#ifdef TRUST_DEBUG
1073          fprintf(stderr, "---------------------------------------------\n");
1074          fprintf(stderr, "trustfile: EBADOTH (world writeable): %s\n", 
1075                  fexp);
1076          fprintf(stderr, "This file/directory is world writeable.\n");
1077          fprintf(stderr, "---------------------------------------------\n");
1078#endif
1079          (void) strncpy(tf_path, fexp, sizeof(tf_path));
1080          tf_path[sizeof(tf_path)-1] = '\0';
1081
1082          free(fexp);
1083          SL_IRETURN(SL_EBADOTH, _("sl_trustfile"));
1084        }
1085      /*
1086       * put the / back
1087       */
1088      if (p == fexp)
1089        {
1090          /* special case for root */
1091          p[1] = c;
1092          p++;
1093        }
1094      else
1095        {
1096          /* ordinary case for everything else */
1097          *p = c;
1098          if (*p)
1099            p++;
1100        }
1101    }
1102  /*
1103   * yes, it can be trusted
1104   */
1105  (void) strncpy(tf_path, fexp, sizeof(tf_path));
1106  tf_path[sizeof(tf_path)-1] = '\0';
1107
1108  free(fexp);
1109  SL_IRETURN(SL_ENONE, _("sl_trustfile"));
1110}
1111
1112#ifdef TRUST_MAIN
1113
1114#if defined(HOST_IS_CYGWIN) || defined(__cygwin__) || defined(__CYGWIN32__) || defined(__CYGWIN__)
1115int main()
1116{
1117  return 0;
1118}
1119#else
1120int main (int argc, char * argv[])
1121{
1122  int status;
1123#if defined(SH_WITH_SERVER)
1124  struct passwd * pass;
1125#endif
1126
1127  if (argc < 2) {
1128    fprintf(stderr, "%s: Usage: %s <fullpath>\n", argv[0], argv[0]);
1129    return 1;
1130  }
1131
1132  tf_path[0] = '\0';
1133#if defined(SH_WITH_SERVER)
1134  pass = sh_getpwnam(SH_IDENT);  /* TESTONLY */
1135  if (pass != NULL)
1136    tf_euid = pass->pw_uid;
1137  else
1138    {
1139      fprintf(stderr, "trustfile: ERROR: getpwnam(%s) failed\n",
1140              SH_IDENT);
1141      return 1;
1142    }
1143#else
1144  tf_euid = geteuid();
1145#endif
1146
1147  status = sl_trustfile(argv[1], NULL, NULL);
1148  if (status != SL_ENONE)
1149    {
1150      fprintf(stderr, "trustfile: ERROR: not a trusted path: %s\n",
1151              argv[1]);
1152      return 1;
1153    }
1154  return 0;
1155}
1156#endif
1157#endif
1158
1159
1160
Note: See TracBrowser for help on using the repository browser.