source: trunk/src/trustfile.c@ 134

Last change on this file since 134 was 134, checked in by rainer, 17 years ago

More thread-safety changes.

File size: 27.2 KB
RevLine 
[1]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#endif
89
90#include <stdio.h>
91#include <stdlib.h>
92#include <sys/types.h>
93#include <sys/stat.h>
94#include <grp.h>
95#include <pwd.h>
96#include <string.h>
97#include <unistd.h>
98
99
100#ifndef TRUST_MAIN
[131]101
[1]102#include "slib.h"
103#define SH_NEED_PWD_GRP 1
104#include "sh_static.h"
[131]105#define SH_GRBUF_SIZE 4096
106#define SH_PWBUF_SIZE 4096
[1]107
108#else
109
110#define sh_getgrgid getgrgid
[131]111#define sh_getgrgid_r getgrgid_r
[1]112#define sh_getpwnam getpwnam
[131]113#define sh_getpwnam_r getpwnam_r
[1]114#define sh_getpwuid getpwuid
[131]115#define sh_getpwuid_r getpwuid_r
116#define sh_getpwent getpwent
[1]117#define sh_endpwent endpwent
118
119#define TRUST_DEBUG
120#define SL_FALSE 0
121#define SL_TRUE 1
122#define SL_ENTER(string)
123#define SL_IRETURN(a, b) return a
124#define retry_lstat(a,b,c,d) lstat(c,d)
125#define _(string) string
126#define N_(string) string
127#define MAXFILENAME 4096
128static int sl_errno = 0;
129#define SL_ENONE 0
130#define SL_ENULL -1024 /* Invalid use of NULL pointer. */
131#define SL_ERANGE -1025 /* Argument out of range. */
132#define SL_ETRUNC -1026 /* Result truncated. */
133#define SL_EINTERNAL -1028 /* Internal error. */
134#define SL_EBADFILE -1030 /* File access error. Check errno. */
135#define SL_EBADNAME -1040 /* Invalid name. */
136#define SL_ESTAT -1041 /* stat of file failed. Check errno. */
137#define SL_EBADUID -1050 /* Owner not trustworthy. */
138#define SL_EBADGID -1051 /* Group writeable and not trustworthy.*/
139#define SL_EBADOTH -1052 /* World writeable. */
140
141#endif
142
143
144#if defined(__linux__) || defined(__FreeBSD__)
145#define STICKY
146#endif
147
148#undef FIL__
149#define FIL__ _("trustfile.c")
150
151/*
152 * the get current working directory function
153 * every version of UNIX seems to have its own
154 * idea of how to do this, so we group them by
155 * arguments ...
156 * all must return a pointer to the right name
157 */
158
159
160#ifndef TRUST_MAIN
161
162#if defined(HAVE_GETCWD) && !defined(HAVE_BROKEN_GETCWD)
163#define CURDIR(buf,nbuf) getcwd((buf), (nbuf))
164#elif defined(HAVE_GETWD)
165#define CURDIR(buf,nbuf) getwd((buf))
166#endif
167
168#else
169
170#define CURDIR(buf,nbuf) getcwd((buf), (nbuf))
171
172#endif
173
174
175
176/*
177 * this checks to see if there are symbolic links
178 * assumes the link bit in the protection mask is called S_IFLNK
179 * (seems to be true on all UNIXes with them)
180 */
181#ifndef S_IFLNK
182#define lstat stat
183#endif
184
185
186/*
187 * these are useful global variables
188 *
189 * first set: who you gonna trust, by default?
190 * if the user does not specify a trusted or untrusted set of users,
191 * all users are considered untrusted EXCEPT:
192 * UID 0 -- root as root can do anything on most UNIX systems, this
193 * seems reasonable
194 * tf_euid -- programmer-selectable UID
195 * if the caller specifies a specific UID by putting
196 * it in this variable, it will be trusted; this is
197 * typically used to trust the effective UID of the
198 * process (note: NOT the real UID, which will cause all
199 * sorts of problems!) By default, this is set to -1,
200 * so if it's not set, root is the only trusted user
201 */
202
203/* modified Tue Feb 22 10:36:44 NFT 2000 Rainer Wichmann */
204
205
206#ifndef SL_ALWAYS_TRUSTED
207#define SL_ALWAYS_TRUSTED 0
208#endif
209static uid_t test_rootonly[] = { SL_ALWAYS_TRUSTED };
210
211#define tf_uid_neg ((uid_t)-1)
212
213uid_t rootonly[] = { SL_ALWAYS_TRUSTED,
214 tf_uid_neg, tf_uid_neg, tf_uid_neg, tf_uid_neg,
215 tf_uid_neg, tf_uid_neg, tf_uid_neg, tf_uid_neg,
216 tf_uid_neg, tf_uid_neg, tf_uid_neg, tf_uid_neg,
217 tf_uid_neg, tf_uid_neg, tf_uid_neg, tf_uid_neg };
218
219uid_t tf_euid = tf_uid_neg;
220int EUIDSLOT = sizeof(test_rootonly)/sizeof(uid_t);
221int ORIG_EUIDSLOT = sizeof(test_rootonly)/sizeof(uid_t);
222
223char tf_path[MAXFILENAME]; /* error path for trust function */
224uid_t tf_baduid;
225gid_t tf_badgid;
226
227static
228int dirz(char *path)
229{
230 register char *p = path;/* points to rest of path to clean up */
231 register char *q; /* temp pointer for skipping over stuff */
232
233 static char swp[MAXFILENAME];
234
235 SL_ENTER(_("dirz"));
236 /*
237 * standard error checking
238 */
239 if (path == NULL)
240 SL_IRETURN(SL_ENULL, _("dirz"));
241 if (path[0] == '.')
242 SL_IRETURN(SL_EINTERNAL, _("dirz"));
243
244
245 /*
246 * loop over the path name until everything is checked
247 */
248 while(*p)
249 {
250 /* skip
251 */
252 if (*p != '/')
253 {
254 p++;
255 continue;
256 }
257
258 /* "/./" or "/."
259 */
260 if (p[1] == '.' && (p[2] == '/' || p[2] == '\0'))
261 {
262 /* yes -- delete "/."
263 */
264 (void) strcpy(swp, &p[2]); /* known to fit */
265 (void) strcpy(p, swp); /* known to fit */
266
267 /* special case "/." as full path name
268 */
269 if (p == path && *p == '\0')
270 {
271 *p++ = '/';
272 *p = '\0';
273 }
274 }
275
276 /* "//"
277 */
278 else if (p[1] == '/')
279 {
280 /* yes -- skip
281 */
282 for(q = &p[2]; *q == '/'; q++)
283 ;
284 (void) strcpy(swp, q); /* known to fit */
285 (void) strcpy(&p[1], swp); /* known to fit */
286 }
287
288 /* "/../" or "/.."
289 */
290 else if (p[1] == '.' && p[2] == '.' && (p[3] == '/' || p[3] == '\0'))
291 {
292 /* yes -- if it's root, delete .. only
293 */
294 if (p == path)
295 {
296 (void) strcpy(swp, &p[3]); /* known to fit */
297 (void) strcpy(p, swp); /* known to fit */
298 }
299 else
300 {
301 /* back up over previous component
302 */
303 q = p - 1;
304 while(q != path && *q != '/')
305 q--;
306 /* now wipe it out
307 */
308 (void) strcpy(swp, &p[3]); /* known to fit */
309 (void) strcpy(q, swp); /* known to fit */
310 p = q;
311 }
312 }
313 else
314 p++;
315 }
316 SL_IRETURN(SL_ENONE, _("dirz"));
317}
318
319
320
321/* not static to circumvent stupid gcc 4 bug */
322int getfname(char *fname, char *rbuf, int rsz)
323{
324#ifndef TRUST_MAIN
325 register int status;
326#endif
327
328 SL_ENTER(_("getfname"));
329 /*
330 * do the initial checking
331 * NULL pointer
332 */
333 if (fname == NULL || rbuf == NULL)
334 SL_IRETURN(SL_ENULL, _("getfname"));
335 if (rsz <= 0)
336 SL_IRETURN(SL_ERANGE, _("getfname"));
337
338
339 /* already a full path name */
340 if (*fname == '/')
341 rbuf[0] = '\0';
342 else
343 {
344 if (CURDIR(rbuf, rsz) == NULL)
345 {
346#ifdef TRUST_DEBUG
347 fprintf(stderr, "trustfile: getcwd failed\n");
348#endif
349 SL_IRETURN(SL_EBADNAME, _("getfname"));
350 }
351 }
352
353 /*
354 * append the file name and reduce
355 */
356 if (fname != NULL && *fname != '\0')
357 {
358#ifndef TRUST_MAIN
359 status = sl_strlcat(rbuf, "/", rsz);
360 if (status == SL_ENONE)
361 status = sl_strlcat(rbuf, fname, rsz);
362 if (status != SL_ENONE)
363 SL_IRETURN(status, _("getfname"));
364#else
365 strncat(rbuf, "/", rsz-strlen(rbuf));
366 strncat(rbuf, fname, rsz-strlen(rbuf));
367#endif
368 }
369 SL_IRETURN(dirz(rbuf), _("getfname"));
370}
371
372static
373int isin(uid_t n, uid_t *list)
374{
375 SL_ENTER(_("isin"));
376 if (list == NULL)
377 SL_IRETURN(SL_FALSE, _("isin"));
378
379 while(*list != tf_uid_neg && *list != n)
380 {
381#ifdef TRUST_DEBUG
382 fprintf (stderr,
383 "trustfile: owner_uid=%ld, trusted uid=%ld, no match\n",
384 (UID_CAST) n, (UID_CAST) *list);
385#endif
386 list++;
387 }
388
389 if (*list == tf_uid_neg)
390 {
391#ifdef TRUST_DEBUG
392 fprintf (stderr,
393 "trustfile: owner_uid=%ld, no match with any trusted user --> ERROR\n",
394 (UID_CAST) n);
395#endif
396 SL_IRETURN(SL_FALSE, _("isin"));
397 }
398
399#ifdef TRUST_DEBUG
400 fprintf (stderr,
401 "trustfile: owner_uid=%ld, trusted_uid=%ld, match found --> OK\n",
402 (UID_CAST)n, (UID_CAST)*list);
403#endif
404 SL_IRETURN(SL_TRUE, _("isin"));
405}
406
407/* comment added by R. Wichmann
408 * RETURN TRUE if ANYONE in ulist is group member
409 */
410/* not static to circumvent stupid gcc 4 bug */
411int isingrp(gid_t grp, uid_t *ulist)
412{
413 register struct passwd *w; /* info about group member */
414 register uid_t *u; /* points to current ulist member */
415 register char **p; /* points to current group member */
416 register struct group *g; /* pointer to group information */
417
[131]418#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETGRGID_R)
419 struct group gr;
420 char buffer[SH_GRBUF_SIZE];
421 struct passwd pwd;
422 char pbuffer[SH_PWBUF_SIZE];
423#endif
424
[1]425 SL_ENTER(_("isingrp"));
426
[131]427#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETGRGID_R)
428 sh_getgrgid_r(grp, &gr, buffer, sizeof(buffer), &g);
429#else
430 g = sh_getgrgid(grp);
431#endif
432
433 if (g == NULL)
[1]434 {
435 SL_IRETURN(SL_FALSE, _("isingrp") );
436 }
437 /*
438 if(g->gr_mem == NULL || g->gr_mem[0] == NULL )
439 SL_IRETURN(SL_FALSE, _("isingrp") );
440 */
441
442 /* this will return at the first match
443 */
444 for(p = g->gr_mem; *p != NULL; p++)
445 {
446 for(u = ulist; *u != tf_uid_neg; u++)
447 {
448 /* map user name to UID and compare */
[131]449#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETPWNAM_R)
450 sh_getpwnam_r(*p, &pwd, pbuffer, sizeof(pbuffer), &w);
451#else
452 w = sh_getpwnam(*p);
453#endif
454
[1]455#ifdef TRUST_MAIN
[131]456 if (w != NULL && *u == (uid_t)(w->pw_uid) )
[1]457 SL_IRETURN(SL_TRUE, _("isingrp"));
458#else
[131]459 if (w != NULL && *u == (uid_t)(w->pw_uid) )
[1]460 {
461 SL_IRETURN(SL_TRUE, _("isingrp"));
462 }
463#endif
464 }
465 }
466 /* added by R. Wichmann Fri Mar 30 08:16:14 CEST 2001:
467 * a user can have a GID but no entry in /etc/group
468 */
469 for(u = ulist; *u != tf_uid_neg; u++)
470 {
[131]471#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETPWUID_R)
472 sh_getpwuid_r(*u, &pwd, pbuffer, sizeof(pbuffer), &w);
473#else
474 w = sh_getpwuid(*u);
475#endif
[1]476#ifdef TRUST_MAIN
[131]477 if (w != NULL && grp == (gid_t)(w->pw_gid) )
[1]478 SL_IRETURN(SL_TRUE, _("isingrp"));
479#else
[131]480 if (w != NULL && grp == (gid_t)(w->pw_gid) )
[1]481 {
482 SL_IRETURN(SL_TRUE, _("isingrp"));
483 }
484#endif
485 }
486
487 SL_IRETURN(SL_FALSE, _("isingrp"));
488}
489
490/* added by R. Wichmann Fri Mar 30 08:16:14 CEST 2001
491 * RETURN TRUE only if ALL group members are trusted
492 */
493/* not static to circumvent stupid gcc 4 bug */
494int onlytrustedingrp(gid_t grp, uid_t *ulist)
495{
496 register struct passwd *w; /* info about group member */
497 register uid_t *u; /* points to current ulist member */
498 register char **p; /* points to current group member */
499 register struct group *g; /* pointer to group information */
500 register int flag = -1; /* group member found */
501
[131]502#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETGRGID_R)
503 struct group gr;
504 char buffer[SH_GRBUF_SIZE];
505 struct group pw;
506 char pbuffer[SH_PWBUF_SIZE];
507#endif
508
[134]509 int retval = SL_FALSE;
510
[1]511 SL_ENTER(_("onlytrustedingrp"));
512
513#ifdef TRUST_DEBUG
514 fprintf(stderr, "trustfile: group writeable, group_gid: %ld\n",
515 (UID_CAST)grp);
516#endif
517
[131]518#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETGRGID_R)
519 sh_getgrgid_r(grp, &gr, buffer, sizeof(buffer), &g);
520#else
521 g = sh_getgrgid(grp);
522#endif
523
524 if (g == NULL)
[1]525 {
526#ifdef TRUST_DEBUG
527 fprintf(stderr,
528 "trustfile: group_gid: %ld, no such group --> ERROR\n",
529 (UID_CAST)grp);
530#endif
531 SL_IRETURN(SL_FALSE, _("onlytrustedingrp") );
532 }
533
534 /* empty group -> no problem
535
536 if(g->gr_mem == NULL || g->gr_mem[0] == NULL )
537 SL_IRETURN(SL_TRUE, _("onlytrustedingrp") );
538 */
539
540 /* check for untrusted members of the group
541 */
542 for(p = g->gr_mem; *p != NULL; p++)
543 {
544 flag = -1;
545#ifdef TRUST_DEBUG
546 fprintf(stderr, "trustfile: group_member: %s\n", *p);
547#endif
548 /* map user name to UID and compare
549 */
[131]550#if defined(HAVE_PTHREAD) && defined (_POSIX_THREAD_SAFE_FUNCTIONS) && defined(HAVE_GETPWNAM_R)
551 sh_getpwnam_r(*p, &pw, pbuffer, sizeof(pbuffer), &w);
552#else
[1]553 w = sh_getpwnam(*p);
554#endif
[131]555
[1]556 if (w == NULL) /* not a valid user, ignore */
557 {
558 flag = 0;
559 }
560 else /* check list of trusted users */
561 {
562#ifdef TRUST_DEBUG
563 fprintf (stderr,
564 "trustfile: uid=%ld, checking whether it is trusted\n",
565 (UID_CAST)(w->pw_uid));
566#endif
567 for(u = ulist; *u != tf_uid_neg; u++)
568 {
569 if (*u == (w->pw_uid) )
570 {
571#ifdef TRUST_DEBUG
572 fprintf (stderr,
573 "trustfile: uid=%ld, trusted_uid=%ld, match found --> OK\n",
574 (UID_CAST)(w->pw_uid), (UID_CAST)*u);
575#endif
576 flag = 0;
577 break;
578 }
579 else
580 {
581#ifdef TRUST_DEBUG
582 fprintf (stderr,
583 "trustfile: uid=%ld, trusted_uid=%ld, no match\n",
584 (UID_CAST)(w->pw_uid), (UID_CAST)*u);
585#endif
586 ;
587 }
588 }
589 }
590 /* not found
591 */
592 if (flag == -1)
593 {
594#ifdef TRUST_DEBUG
595 fprintf (stderr,
596 "trustfile: user=%s (gid %ld), not a trusted user --> ERROR\n", *p, (UID_CAST)grp);
597#endif
598 tf_baduid = w->pw_uid;
599 SL_IRETURN(SL_FALSE, _("onlytrustedingrp"));
600 }
601 }
602
603#ifndef TEST_ONLY
604#ifdef HAVE_GETPWENT
605 /* now check ALL users for their GID !!!
606 */
[134]607 SH_MUTEX_LOCK(mutex_pwent);
608
[1]609 while (NULL != (w = sh_getpwent()))
610 {
611 if (grp == (gid_t)(w->pw_gid))
612 {
613#ifdef TRUST_DEBUG
614 fprintf(stderr, "trustfile: checking group member %s, uid %ld\n",
615 w->pw_name, (UID_CAST)w->pw_uid);
616#endif
617 /* is it a trusted user ?
618 */
619 flag = -1;
620 for(u = ulist; *u != tf_uid_neg; u++)
621 {
622 if (*u == (uid_t)(w->pw_uid))
623 {
624#ifdef TRUST_DEBUG
625 fprintf (stderr,
626 "trustfile: uid=%ld, trusted_uid=%ld, match found --> OK\n",
627 (UID_CAST)(w->pw_uid), *u);
628#endif
629 flag = 0;
630 break;
631 }
632 else
633 {
634#ifdef TRUST_DEBUG
635 fprintf (stderr,
636 "trustfile: uid=%ld, trusted_uid=%ld, no match\n",
637 (UID_CAST)(w->pw_uid), (UID_CAST)*u);
638#endif
639 ;
640 }
641 }
642 /* not found */
643 if (flag == -1)
644 {
645#ifdef TRUST_DEBUG
646 fprintf(stderr,"trustfile: group member %s not found in trusted users --> ERROR\n", w->pw_name);
647#endif
648 tf_baduid = w->pw_uid;
[134]649 retval = SL_FALSE;
650 goto out;
651 /* SL_IRETURN(SL_FALSE, _("onlytrustedingrp")); */
[1]652 }
653 }
654 }
[134]655 retval = SL_TRUE;
[1]656
[134]657 out:
658
[1]659#ifdef HAVE_ENDPWENT
660 sh_endpwent();
661#endif
[134]662
663 SH_MUTEX_UNLOCK(mutex_pwent);
664
[1]665 /* TEST_ONLY */
666#endif
667 /* #ifdef HAVE_GETPWENT */
668#endif
669
670#ifdef TRUST_DEBUG
[134]671 if (retval == SL_TRUE)
672 fprintf(stderr,
673 "trustfile: group %ld: all members are trusted users --> OK\n",
674 (UID_CAST)grp);
[1]675#endif
676 /* all found
677 */
[134]678 SL_IRETURN(retval, _("onlytrustedingrp"));
[1]679}
680
681int sl_trustfile(char *fname, uid_t *okusers, uid_t *badusers)
682{
683 char fexp[MAXFILENAME]; /* file name fully expanded */
684 register char *p = fexp; /* used to hold name to be checked */
685 struct stat stbuf; /* used to check file permissions */
686 char c; /* used to hold temp char */
687
688 SL_ENTER(_("sl_trustfile"));
689 if (fname == NULL)
690 SL_IRETURN(SL_EBADFILE, _("sl_trustfile"));
691
692 /*
693 * next expand to the full file name
694 * getfname sets sl_errno as appropriate
695 */
696#ifdef TRUST_MAIN
697 sl_errno = getfname(fname, fexp, MAXFILENAME);
698 if (sl_errno != 0)
699 return sl_errno;
700#else
701 if (SL_ISERROR(getfname(fname, fexp, MAXFILENAME)))
702 SL_IRETURN(sl_errno, _("sl_trustfile"));
703#endif
704
705 if (okusers == NULL && badusers == NULL)
706 {
707 okusers = rootonly;
708 rootonly[EUIDSLOT] = tf_euid;
709 }
710
711 /*
712 * now loop through the path a component at a time
713 * note we have to special-case root
714 */
715 while(*p)
716 {
717 /*
718 * get next component
719 */
720 while(*p && *p != '/')
721 p++;
722
723 /* save where you are
724 */
725 if (p == fexp)
726 {
727 /* keep the / if it's the root dir
728 */
729 c = p[1];
730 p[1] = '\0';
731 }
732 else
733 {
734 /* clobber the / if it isn't the root dir
735 */
736 c = *p;
737 *p = '\0';
738 }
739
740 /*
741 * now get the information
742 */
743 if (retry_lstat(FIL__, __LINE__, fexp, &stbuf) < 0)
744 {
[76]745 (void) strncpy(tf_path, fexp, sizeof(tf_path));
746 tf_path[sizeof(tf_path)-1] = '\0';
[1]747#ifdef TRUST_MAIN
748 fprintf(stderr, "---------------------------------------------\n");
[76]749 fprintf(stderr, "trustfile: ESTAT: stat(%s) failed,\n", fexp);
750 fprintf(stderr, "maybe the file does not exist\n");
[1]751 fprintf(stderr, "---------------------------------------------\n");
752#endif
753 SL_IRETURN(SL_ESTAT, _("sl_trustfile"));
754 }
755
756#ifdef S_IFLNK
757 /*
758 * if it's a symbolic link, recurse
759 */
760 if ((stbuf.st_mode & S_IFLNK) == S_IFLNK)
761 {
762 /*
763 * this is tricky
764 * if the symlink is to an absolute path
765 * name, just call trustfile on it; but
766 * if it's a relative path name, it's
767 * interpreted WRT the current working
768 * directory AND NOT THE FILE NAME!!!!!
769 * so, we simply put /../ at the end of
770 * the file name, then append the symlink
771 * contents; trustfile will canonicalize
772 * this, and the /../ we added "undoes"
773 * the name of the symlink to put us in
774 * the current working directory, at
775 * which point the symlink contents (appended
776 * to the CWD) are interpreted correctly.
777 * got it?
778 */
779 char csym[MAXFILENAME]; /* contents of symlink file */
780 char full[MAXFILENAME]; /* "full" name of symlink */
781 register char *b, *t; /* used to copy stuff around */
782 register int lsym; /* num chars in symlink ref */
783 register int i; /* trustworthy or not? */
784
785 /*
786 * get what the symbolic link points to
787 *
788 * The original code does not check the return code of readlink(),
789 * and has an off-by-one error
790 * (MAXFILENAME instead of MAXFILENAME-1)
791 * R.W. Tue May 29 22:05:16 CEST 2001
792 */
793 lsym = readlink(fexp, csym, MAXFILENAME-1);
794 if (lsym >= 0)
795 csym[lsym] = '\0';
796 else
797 {
798#ifdef TRUST_MAIN
799 fprintf(stderr, "---------------------------------------------\n");
800 fprintf(stderr, "trustfile: EBADNAME: readlink(%s) failed\n",
801 fexp);
802 fprintf(stderr, "---------------------------------------------\n");
803#endif
804 SL_IRETURN(SL_EBADNAME, _("sl_trustfile"));
805 }
806
807 /*
808 * relative or absolute referent?
809 */
810 if (csym[0] != '/')
811 {
812 /* initialize pointers
813 */
814 b = full;
815
816 /* copy in base path
817 */
818 t = fexp;
819 while(*t && b < &full[MAXFILENAME])
820 *b++ = *t++;
821
822 /* smack on the /../
823 */
824 t = "/../";
825 while(*t && b < &full[MAXFILENAME])
826 *b++ = *t++;
827
828 /* append the symlink referent
829 */
830 t = csym;
831 while(*t && b < &full[MAXFILENAME])
832 *b++ = *t++;
833
834 /* see if we're too big
835 */
836 if (*t || b == &full[MAXFILENAME])
837 {
838 /* yes -- error
839 */
[76]840 (void) strncpy(tf_path, fexp, sizeof(tf_path));
841 tf_path[sizeof(tf_path)-1] = '\0';
[1]842#ifdef TRUST_MAIN
843 fprintf(stderr, "---------------------------------------------\n");
844 fprintf(stderr,
845 "trustfile: ETRUNC: normalized path too long (%s)\n",
846 fexp);
847 fprintf(stderr, "---------------------------------------------\n");
848#endif
849 SL_IRETURN(SL_ETRUNC, _("sl_trustfile"));
850 }
851 *b = '\0';
852 }
853 else
854 {
855 /* absolute -- just copy */
856 /* overflow can't occur as the arrays */
857 /* are the same size */
858 (void) strcpy(full, csym); /* known to fit */
859 }
860 /*
861 * now check out this file and its ancestors
862 */
863 if ((i = sl_trustfile(full, okusers, badusers)) != SL_ENONE)
864 SL_IRETURN(i, _("sl_trustfile"));
865
866 /*
867 * okay, this part is valid ... let's check the rest
868 * put the / back
869 */
870 if (p == fexp)
871 {
872 /* special case for root */
873 p[1] = c;
874 p++;
875 }
876 else
877 {
878 /* ordinary case for everything else */
879 *p = c;
880 if (*p)
881 p++;
882 }
883 continue;
884 }
885#endif
886
887
888#ifdef TRUST_DEBUG
889 fprintf(stderr, "\ntrustfile: checking path=%s\n", fexp);
890#endif
891 /*
892 * if the owner is not trusted then -- as the owner can
893 * change protection modes -- he/she can write to the
894 * file regardless of permissions, so bomb
895 */
896 if (((okusers != NULL && SL_FALSE == isin((uid_t)stbuf.st_uid,okusers))||
897 (badusers != NULL && SL_TRUE == isin((uid_t)stbuf.st_uid,badusers))))
898 {
899#ifdef TRUST_DEBUG
900 fprintf(stderr, "---------------------------------------------\n");
901 fprintf(stderr, "trustfile: EBADUID %s (owner not trusted)\n",
902 fexp);
903 fprintf(stderr, "The owner of this file/directory is not in samhains\n");
904 fprintf(stderr, "list of trusted users.\n");
905 fprintf(stderr, "Please run ./configure again with the option\n");
906 fprintf(stderr, " ./configure [more options] --with-trusted=0,...,UID\n");
907 fprintf(stderr, "where UID is the UID of the (yet) untrusted user.\n");
908 fprintf(stderr, "---------------------------------------------\n");
909#endif
[76]910 (void) strncpy(tf_path, fexp, sizeof(tf_path));
911 tf_path[sizeof(tf_path)-1] = '\0';
912
[1]913 tf_baduid = (uid_t) stbuf.st_uid;
914 SL_IRETURN(SL_EBADUID, _("sl_trustfile"));
915 }
916
917 /*
918 * if a group member can write but the
919 * member is not trusted, bomb; but if
920 * sticky bit semantics are honored, it's
921 * okay
922 */
923 /* Thu Mar 29 21:10:28 CEST 2001 Rainer Wichmann
924 * replace !isingrp() with onlytrustedingrp(), as isingrp()
925 * will return at the first trusted user, even if there are additional
926 * (untrusted) users in the group
927 */
928 if (((stbuf.st_mode & S_IWGRP) == S_IWGRP) &&
929 ((okusers != NULL && !onlytrustedingrp((gid_t)stbuf.st_gid,okusers))||
930 (badusers != NULL && isingrp((gid_t)stbuf.st_gid, badusers)))
931#ifdef STICKY
932 && ((stbuf.st_mode&S_IFDIR) != S_IFDIR ||
933 (stbuf.st_mode&S_ISVTX) != S_ISVTX)
934#endif
935 )
936 {
937#ifdef TRUST_DEBUG
938 fprintf(stderr, "---------------------------------------------\n");
939 fprintf(stderr,
940 "trustfile: EBADGID %ld %s (group member not trusted)\n",
941 (UID_CAST)stbuf.st_gid, fexp);
942 fprintf(stderr, "This file/directory is group writeable, and one of the group members\n");
943 fprintf(stderr, "is not in samhains list of trusted users.\n");
944 fprintf(stderr, "Please run ./configure again with the option\n");
945 fprintf(stderr, " ./configure [more options] --with-trusted=0,...,UID\n");
946 fprintf(stderr, "where UID is the UID of the (yet) untrusted user.\n");
947 fprintf(stderr, "---------------------------------------------\n");
948#endif
[76]949 (void) strncpy(tf_path, fexp, sizeof(tf_path));
950 tf_path[sizeof(tf_path)-1] = '\0';
951
[1]952 tf_badgid = (gid_t) stbuf.st_gid;
953 SL_IRETURN(SL_EBADGID, _("sl_trustfile"));
954 }
955 /*
956 * if other can write, bomb; but if the sticky
957 * bit semantics are honored, it's okay
958 */
959 if (((stbuf.st_mode & S_IWOTH) == S_IWOTH)
960#ifdef STICKY
961 && ((stbuf.st_mode&S_IFDIR) != S_IFDIR ||
962 (stbuf.st_mode&S_ISVTX) != S_ISVTX)
963#endif
964 )
965 {
966#ifdef TRUST_DEBUG
967 fprintf(stderr, "---------------------------------------------\n");
968 fprintf(stderr, "trustfile: EBADOTH (world writeable): %s\n",
969 fexp);
970 fprintf(stderr, "This file/directory is world writeable.\n");
971 fprintf(stderr, "---------------------------------------------\n");
972#endif
[76]973 (void) strncpy(tf_path, fexp, sizeof(tf_path));
974 tf_path[sizeof(tf_path)-1] = '\0';
975
[1]976 SL_IRETURN(SL_EBADOTH, _("sl_trustfile"));
977 }
978 /*
979 * put the / back
980 */
981 if (p == fexp)
982 {
983 /* special case for root */
984 p[1] = c;
985 p++;
986 }
987 else
988 {
989 /* ordinary case for everything else */
990 *p = c;
991 if (*p)
992 p++;
993 }
994 }
995 /*
996 * yes, it can be trusted
997 */
[76]998 (void) strncpy(tf_path, fexp, sizeof(tf_path));
999 tf_path[sizeof(tf_path)-1] = '\0';
1000
[1]1001 SL_IRETURN(SL_ENONE, _("sl_trustfile"));
1002}
1003
1004#ifdef TRUST_MAIN
1005int main (int argc, char * argv[])
1006{
1007 int status;
1008#if defined(SH_WITH_SERVER)
1009 struct passwd * pass;
1010#endif
1011
1012 if (argc < 2) {
1013 fprintf(stderr, "%s: Usage: %s <fullpath>\n", argv[0], argv[0]);
1014 return 1;
1015 }
1016
1017 tf_path[0] = '\0';
1018#if defined(SH_WITH_SERVER)
[131]1019 pass = sh_getpwnam(SH_IDENT); /* TESTONLY */
[1]1020 if (pass != NULL)
1021 tf_euid = pass->pw_uid;
1022 else
1023 {
1024 fprintf(stderr, "trustfile: ERROR: getpwnam(%s) failed\n",
1025 SH_IDENT);
1026 return 1;
1027 }
1028#else
1029 tf_euid = geteuid();
1030#endif
1031
1032 status = sl_trustfile(argv[1], NULL, NULL);
1033 if (status != SL_ENONE)
1034 {
1035 fprintf(stderr, "trustfile: ERROR: not a trusted path: %s\n",
1036 argv[1]);
1037 return 1;
1038 }
1039 return 0;
1040}
1041#endif
1042
1043
1044
Note: See TracBrowser for help on using the repository browser.