source: trunk/src/trustfile.c@ 428

Last change on this file since 428 was 428, checked in by katerina, 12 years ago

Fix for ticket #335 (warn if buffer for group reading is too small).

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