source: trunk/src/trustfile.c@ 228

Last change on this file since 228 was 227, checked in by katerina, 16 years ago

Fix warnings with -fstack-check

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