source: trunk/third/sendmail/src/safefile.c @ 12554

Revision 12554, 15.4 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12553, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
4 * Copyright (c) 1988, 1993
5 *      The Regents of the University of California.  All rights reserved.
6 *
7 * By using this file, you agree to the terms and conditions set
8 * forth in the LICENSE file which can be found at the top level of
9 * the sendmail distribution.
10 *
11 */
12
13#ifndef lint
14static char sccsid[] = "@(#)safefile.c  8.43 (Berkeley) 10/13/1998";
15#endif /* not lint */
16
17# include "sendmail.h"
18/*
19**  SAFEFILE -- return true if a file exists and is safe for a user.
20**
21**      Parameters:
22**              fn -- filename to check.
23**              uid -- user id to compare against.
24**              gid -- group id to compare against.
25**              uname -- user name to compare against (used for group
26**                      sets).
27**              flags -- modifiers:
28**                      SFF_MUSTOWN -- "uid" must own this file.
29**                      SFF_NOSLINK -- file cannot be a symbolic link.
30**              mode -- mode bits that must match.
31**              st -- if set, points to a stat structure that will
32**                      get the stat info for the file.
33**
34**      Returns:
35**              0 if fn exists, is owned by uid, and matches mode.
36**              An errno otherwise.  The actual errno is cleared.
37**
38**      Side Effects:
39**              none.
40*/
41
42#include <grp.h>
43
44int
45safefile(fn, uid, gid, uname, flags, mode, st)
46        char *fn;
47        UID_T uid;
48        GID_T gid;
49        char *uname;
50        int flags;
51        int mode;
52        struct stat *st;
53{
54        register char *p;
55        register struct group *gr = NULL;
56        int file_errno = 0;
57        bool checkpath;
58        struct stat stbuf;
59        struct stat fstbuf;
60        char fbuf[MAXPATHLEN + 1];
61
62        if (tTd(44, 4))
63                printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n",
64                        fn, (int) uid, (int) gid, flags, mode);
65        errno = 0;
66        if (st == NULL)
67                st = &fstbuf;
68        if (strlen(fn) > sizeof fbuf - 1)
69        {
70                if (tTd(44, 4))
71                        printf("\tpathname too long\n");
72                return ENAMETOOLONG;
73        }
74        strcpy(fbuf, fn);
75        fn = fbuf;
76
77        /* ignore SFF_SAFEDIRPATH if we are debugging */
78        if (RealUid != 0 && RunAsUid == RealUid)
79                flags &= ~SFF_SAFEDIRPATH;
80
81        /* first check to see if the file exists at all */
82#ifdef HASLSTAT
83        if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st)
84                                        : stat(fn, st)) < 0)
85#else
86        if (stat(fn, st) < 0)
87#endif
88        {
89                file_errno = errno;
90        }
91        else if (bitset(SFF_SETUIDOK, flags) &&
92                 !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) &&
93                 S_ISREG(st->st_mode))
94        {
95                /*
96                **  If final file is setuid, run as the owner of that
97                **  file.  Gotta be careful not to reveal anything too
98                **  soon here!
99                */
100
101#ifdef SUID_ROOT_FILES_OK
102                if (bitset(S_ISUID, st->st_mode))
103#else
104                if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 &&
105                    st->st_uid != TrustedUid)
106#endif
107                {
108                        uid = st->st_uid;
109                        uname = NULL;
110                }
111#ifdef SUID_ROOT_FILES_OK
112                if (bitset(S_ISGID, st->st_mode))
113#else
114                if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0)
115#endif
116                        gid = st->st_gid;
117        }
118
119        checkpath = !bitset(SFF_NOPATHCHECK, flags) ||
120                    (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags));
121        if (bitset(SFF_NOWLINK, flags) && !bitset(SFF_SAFEDIRPATH, flags))
122        {
123                int ret;
124
125                /* check the directory */
126                p = strrchr(fn, '/');
127                if (p == NULL)
128                {
129                        ret = safedirpath(".", uid, gid, uname, flags|SFF_SAFEDIRPATH);
130                }
131                else
132                {
133                        *p = '\0';
134                        ret = safedirpath(fn, uid, gid, uname, flags|SFF_SAFEDIRPATH);
135                        *p = '/';
136                }
137                if (ret == 0)
138                {
139                        /* directory is safe */
140                        checkpath = FALSE;
141                }
142                else
143                {
144#ifdef HASLSTAT
145                        /* Need lstat() information if called stat() before */
146                        if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0)
147                        {
148                                ret = errno;
149                                if (tTd(44, 4))
150                                        printf("\t%s\n", errstring(ret));
151                                return ret;
152                        }
153#endif
154                        /* directory is writable: disallow links */
155                        flags |= SFF_NOLINK;
156                }
157        }
158
159        if (checkpath)
160        {
161                int ret;
162
163                p = strrchr(fn, '/');
164                if (p == NULL)
165                {
166                        ret = safedirpath(".", uid, gid, uname, flags);
167                }
168                else
169                {
170                        *p = '\0';
171                        ret = safedirpath(fn, uid, gid, uname, flags);
172                        *p = '/';
173                }
174                if (ret != 0)
175                        return ret;
176        }
177
178        /*
179        **  If the target file doesn't exist, check the directory to
180        **  ensure that it is writable by this user.
181        */
182
183        if (file_errno != 0)
184        {
185                int ret = file_errno;
186                char *dir = fn;
187
188                if (tTd(44, 4))
189                        printf("\t%s\n", errstring(ret));
190
191                errno = 0;
192                if (!bitset(SFF_CREAT, flags) || file_errno != ENOENT)
193                        return ret;
194
195                /* check to see if legal to create the file */
196                p = strrchr(dir, '/');
197                if (p == NULL)
198                        dir = ".";
199                else if (p == dir)
200                        dir = "/";
201                else
202                        *p = '\0';
203                if (stat(dir, &stbuf) >= 0)
204                {
205                        int md = S_IWRITE|S_IEXEC;
206
207                        if (stbuf.st_uid == uid)
208                                ;
209                        else if (uid == 0 && stbuf.st_uid == TrustedUid)
210                                ;
211                        else
212                        {
213                                md >>= 3;
214                                if (stbuf.st_gid == gid)
215                                        ;
216#ifndef NO_GROUP_SET
217                                else if (uname != NULL && !DontInitGroups &&
218                                         ((gr != NULL &&
219                                           gr->gr_gid == stbuf.st_gid) ||
220                                          (gr = getgrgid(stbuf.st_gid)) != NULL))
221                                {
222                                        register char **gp;
223               
224                                        for (gp = gr->gr_mem; *gp != NULL; gp++)
225                                                if (strcmp(*gp, uname) == 0)
226                                                        break;
227                                        if (*gp == NULL)
228                                                md >>= 3;
229                                }
230#endif
231                                else
232                                        md >>= 3;
233                        }
234                        if ((stbuf.st_mode & md) != md)
235                                errno = EACCES;
236                }
237                ret = errno;
238                if (tTd(44, 4))
239                        printf("\t[final dir %s uid %d mode %lo] %s\n",
240                                dir, (int) stbuf.st_uid, (u_long) stbuf.st_mode,
241                                errstring(ret));
242                if (p != NULL)
243                        *p = '/';
244                st->st_mode = ST_MODE_NOFILE;
245                return ret;
246        }
247
248#ifdef S_ISLNK
249        if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode))
250        {
251                if (tTd(44, 4))
252                        printf("\t[slink mode %lo]\tE_SM_NOSLINK\n",
253                                (u_long) st->st_mode);
254                return E_SM_NOSLINK;
255        }
256#endif
257        if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode))
258        {
259                if (tTd(44, 4))
260                        printf("\t[non-reg mode %lo]\tE_SM_REGONLY\n",
261                                (u_long) st->st_mode);
262                return E_SM_REGONLY;
263        }
264        if (bitset(SFF_NOGWFILES, flags) &&
265            bitset(S_IWGRP, st->st_mode))
266        {
267                if (tTd(44, 4))
268                        printf("\t[write bits %lo]\tE_SM_GWFILE\n",
269                               (u_long) st->st_mode);
270                return E_SM_GWFILE;
271        }
272        if (bitset(SFF_NOWWFILES, flags) &&
273            bitset(S_IWOTH, st->st_mode))
274        {
275                if (tTd(44, 4))
276                        printf("\t[write bits %lo]\tE_SM_WWFILE\n",
277                               (u_long) st->st_mode);
278                return E_SM_WWFILE;
279        }
280        if (bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) &&
281            bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode))
282        {
283                if (tTd(44, 4))
284                        printf("\t[exec bits %lo]\tE_SM_ISEXEC]\n",
285                                (u_long) st->st_mode);
286                return E_SM_ISEXEC;
287        }
288        if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1)
289        {
290                if (tTd(44, 4))
291                        printf("\t[link count %d]\tE_SM_NOHLINK\n",
292                                (int) st->st_nlink);
293                return E_SM_NOHLINK;
294        }
295
296        if (uid == 0 && bitset(SFF_OPENASROOT, flags))
297                ;
298        else if (uid == 0 && !bitset(SFF_ROOTOK, flags))
299                mode >>= 6;
300        else if (st->st_uid == uid)
301                ;
302        else if (uid == 0 && st->st_uid == TrustedUid)
303                ;
304        else
305        {
306                mode >>= 3;
307                if (st->st_gid == gid)
308                        ;
309#ifndef NO_GROUP_SET
310                else if (uname != NULL && !DontInitGroups &&
311                         ((gr != NULL && gr->gr_gid == st->st_gid) ||
312                          (gr = getgrgid(st->st_gid)) != NULL))
313                {
314                        register char **gp;
315
316                        for (gp = gr->gr_mem; *gp != NULL; gp++)
317                                if (strcmp(*gp, uname) == 0)
318                                        break;
319                        if (*gp == NULL)
320                                mode >>= 3;
321                }
322#endif
323                else
324                        mode >>= 3;
325        }
326        if (tTd(44, 4))
327                printf("\t[uid %d, nlink %d, stat %lo, mode %lo] ",
328                        (int) st->st_uid, (int) st->st_nlink,
329                        (u_long) st->st_mode, (u_long) mode);
330        if ((st->st_uid == uid || st->st_uid == 0 ||
331             st->st_uid == TrustedUid ||
332             !bitset(SFF_MUSTOWN, flags)) &&
333            (st->st_mode & mode) == mode)
334        {
335                if (tTd(44, 4))
336                        printf("\tOK\n");
337                return 0;
338        }
339        if (tTd(44, 4))
340                printf("\tEACCES\n");
341        return EACCES;
342}
343/*
344**  SAFEDIRPATH -- check to make sure a path to a directory is safe
345**
346**      Safe means not writable and owned by the right folks.
347**
348**      Parameters:
349**              fn -- filename to check.
350**              uid -- user id to compare against.
351**              gid -- group id to compare against.
352**              uname -- user name to compare against (used for group
353**                      sets).
354**              flags -- modifiers:
355**                      SFF_ROOTOK -- ok to use root permissions to open.
356**                      SFF_SAFEDIRPATH -- writable directories are considered
357**                              to be fatal errors.
358**
359**      Returns:
360**              0 -- if the directory path is "safe".
361**              else -- an error number associated with the path.
362*/
363
364int
365safedirpath(fn, uid, gid, uname, flags)
366        char *fn;
367        UID_T uid;
368        GID_T gid;
369        char *uname;
370        int flags;
371{
372        char *p;
373        register struct group *gr = NULL;
374        int ret = 0;
375        int mode = S_IWOTH;
376        struct stat stbuf;
377
378        /* special case root directory */
379        if (*fn == '\0')
380                fn = "/";
381
382        if (tTd(44, 4))
383                printf("safedirpath(%s, uid=%ld, gid=%ld, flags=%x):\n",
384                        fn, (long) uid, (long) gid, flags);
385
386        if (!bitset(DBS_GROUPWRITABLEDIRPATHSAFE, DontBlameSendmail))
387                mode |= S_IWGRP;
388
389        p = fn;
390        do
391        {
392                if (*p == '\0')
393                        *p = '/';
394                p = strchr(++p, '/');
395                if (p != NULL)
396                        *p = '\0';
397                if (stat(fn, &stbuf) < 0)
398                {
399                        ret = errno;
400                        break;
401                }
402                if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) &&
403                    bitset(mode, stbuf.st_mode))
404                {
405                        if (tTd(44, 4))
406                                printf("\t[dir %s] mode %lo\n",
407                                        fn, (u_long) stbuf.st_mode);
408                        if (bitset(SFF_SAFEDIRPATH, flags))
409                        {
410                                if (bitset(S_IWOTH, stbuf.st_mode))
411                                        ret = E_SM_WWDIR;
412                                else
413                                        ret = E_SM_GWDIR;
414                                break;
415                        }
416                        if (Verbose > 1)
417                                message("051 WARNING: %s writable directory %s",
418                                        bitset(S_IWOTH, stbuf.st_mode)
419                                           ? "World"
420                                           : "Group",
421                                        fn);
422                }
423                if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags))
424                {
425                        if (bitset(S_IXOTH, stbuf.st_mode))
426                                continue;
427                        ret = EACCES;
428                        break;
429                }
430
431                /*
432                **  Let OS determine access to file if we are not
433                **  running as a privileged user.  This allows ACLs
434                **  to work.
435                */
436                if (geteuid() != 0)
437                        continue;
438
439                if (stbuf.st_uid == uid &&
440                    bitset(S_IXUSR, stbuf.st_mode))
441                        continue;
442                if (stbuf.st_gid == gid &&
443                    bitset(S_IXGRP, stbuf.st_mode))
444                        continue;
445#ifndef NO_GROUP_SET
446                if (uname != NULL && !DontInitGroups &&
447                    ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
448                     (gr = getgrgid(stbuf.st_gid)) != NULL))
449                {
450                        register char **gp;
451
452                        for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
453                                if (strcmp(*gp, uname) == 0)
454                                        break;
455                        if (gp != NULL && *gp != NULL &&
456                            bitset(S_IXGRP, stbuf.st_mode))
457                                continue;
458                }
459#endif
460                if (!bitset(S_IXOTH, stbuf.st_mode))
461                {
462                        ret = EACCES;
463                        break;
464                }
465        } while (p != NULL);
466        if (ret != 0 && tTd(44, 4))
467                printf("\t[dir %s] %s\n", fn, errstring(ret));
468        if (p != NULL)
469                *p = '/';
470        return ret;
471}
472/*
473**  SAFEOPEN -- do a file open with extra checking
474**
475**      Parameters:
476**              fn -- the file name to open.
477**              omode -- the open-style mode flags.
478**              cmode -- the create-style mode flags.
479**              sff -- safefile flags.
480**
481**      Returns:
482**              Same as open.
483*/
484
485#ifndef O_ACCMODE
486# define O_ACCMODE      (O_RDONLY|O_WRONLY|O_RDWR)
487#endif
488
489int
490safeopen(fn, omode, cmode, sff)
491        char *fn;
492        int omode;
493        int cmode;
494        int sff;
495{
496        int rval;
497        int fd;
498        int smode;
499        struct stat stb;
500
501        if (bitset(O_CREAT, omode))
502                sff |= SFF_CREAT;
503        omode &= ~O_CREAT;
504        smode = 0;
505        switch (omode & O_ACCMODE)
506        {
507          case O_RDONLY:
508                smode = S_IREAD;
509                break;
510
511          case O_WRONLY:
512                smode = S_IWRITE;
513                break;
514
515          case O_RDWR:
516                smode = S_IREAD|S_IWRITE;
517                break;
518
519          default:
520                smode = 0;
521                break;
522        }
523        if (bitset(SFF_OPENASROOT, sff))
524                rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName,
525                                sff, smode, &stb);
526        else
527                rval = safefile(fn, RealUid, RealGid, RealUserName,
528                                sff, smode, &stb);
529        if (rval != 0)
530        {
531                errno = rval;
532                return -1;
533        }
534        if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff))
535                omode |= O_EXCL|O_CREAT;
536
537        fd = dfopen(fn, omode, cmode, sff);
538        if (fd < 0)
539                return fd;
540        if (filechanged(fn, fd, &stb))
541        {
542                syserr("554 cannot open: file %s changed after open", fn);
543                close(fd);
544                errno = E_SM_FILECHANGE;
545                return -1;
546        }
547        return fd;
548}
549/*
550**  SAFEFOPEN -- do a file open with extra checking
551**
552**      Parameters:
553**              fn -- the file name to open.
554**              omode -- the open-style mode flags.
555**              cmode -- the create-style mode flags.
556**              sff -- safefile flags.
557**
558**      Returns:
559**              Same as fopen.
560*/
561
562FILE *
563safefopen(fn, omode, cmode, sff)
564        char *fn;
565        int omode;
566        int cmode;
567        int sff;
568{
569        int fd;
570        FILE *fp;
571        char *fmode;
572
573        switch (omode & O_ACCMODE)
574        {
575          case O_RDONLY:
576                fmode = "r";
577                break;
578
579          case O_WRONLY:
580                if (bitset(O_APPEND, omode))
581                        fmode = "a";
582                else
583                        fmode = "w";
584                break;
585
586          case O_RDWR:
587                if (bitset(O_TRUNC, omode))
588                        fmode = "w+";
589                else if (bitset(O_APPEND, omode))
590                        fmode = "a+";
591                else
592                        fmode = "r+";
593                break;
594
595          default:
596                syserr("safefopen: unknown omode %o", omode);
597                fmode = "x";
598        }
599        fd = safeopen(fn, omode, cmode, sff);
600        if (fd < 0)
601        {
602                if (tTd(44, 10))
603                        printf("safefopen: safeopen failed: %s\n",
604                                errstring(errno));
605                return NULL;
606        }
607        fp = fdopen(fd, fmode);
608        if (fp != NULL)
609                return fp;
610
611        if (tTd(44, 10))
612        {
613                printf("safefopen: fdopen(%s, %s) failed: omode=%x, sff=%x, err=%s\n",
614                        fn, fmode, omode, sff, errstring(errno));
615#ifndef NOT_SENDMAIL
616                dumpfd(fd, TRUE, FALSE);
617#endif
618        }
619        (void) close(fd);
620        return NULL;
621}
622/*
623**  FILECHANGED -- check to see if file changed after being opened
624**
625**      Parameters:
626**              fn -- pathname of file to check.
627**              fd -- file descriptor to check.
628**              stb -- stat structure from before open.
629**
630**      Returns:
631**              TRUE -- if a problem was detected.
632**              FALSE -- if this file is still the same.
633*/
634
635bool
636filechanged(fn, fd, stb)
637        char *fn;
638        int fd;
639        struct stat *stb;
640{
641        struct stat sta;
642
643        if (stb->st_mode == ST_MODE_NOFILE)
644        {
645#if HASLSTAT && BOGUS_O_EXCL
646                /* only necessary if exclusive open follows symbolic links */
647                if (lstat(fn, stb) < 0 || stb->st_nlink != 1)
648                        return TRUE;
649#else
650                return FALSE;
651#endif
652        }
653        if (fstat(fd, &sta) < 0)
654                return TRUE;
655
656        if (sta.st_nlink != stb->st_nlink ||
657            sta.st_dev != stb->st_dev ||
658            sta.st_ino != stb->st_ino ||
659#if HAS_ST_GEN && 0             /* AFS returns garbage in st_gen */
660            sta.st_gen != stb->st_gen ||
661#endif
662            sta.st_uid != stb->st_uid ||
663            sta.st_gid != stb->st_gid)
664        {
665                if (tTd(44, 8))
666                {
667                        printf("File changed after opening:\n");
668                        printf(" nlink  = %ld/%ld\n",
669                                (long) stb->st_nlink, (long) sta.st_nlink);
670                        printf(" dev    = %ld/%ld\n",
671                                (long) stb->st_dev, (long) sta.st_dev);
672                        if (sizeof sta.st_ino > sizeof (long))
673                        {
674                                printf(" ino    = %s/",
675                                        quad_to_string(stb->st_ino));
676                                printf("%s\n",
677                                        quad_to_string(sta.st_ino));
678                        }
679                        else
680                                printf(" ino    = %lu/%lu\n",
681                                        (unsigned long) stb->st_ino,
682                                        (unsigned long) sta.st_ino);
683#if HAS_ST_GEN
684                        printf(" gen    = %ld/%ld\n",
685                                (long) stb->st_gen, (long) sta.st_gen);
686#endif
687                        printf(" uid    = %ld/%ld\n",
688                                (long) stb->st_uid, (long) sta.st_uid);
689                        printf(" gid    = %ld/%ld\n",
690                                (long) stb->st_gid, (long) sta.st_gid);
691                }
692                return TRUE;
693        }
694
695        return FALSE;
696}
697/*
698**  DFOPEN -- determined file open
699**
700**      This routine has the semantics of open, except that it will
701**      keep trying a few times to make this happen.  The idea is that
702**      on very loaded systems, we may run out of resources (inodes,
703**      whatever), so this tries to get around it.
704*/
705
706int
707dfopen(filename, omode, cmode, sff)
708        char *filename;
709        int omode;
710        int cmode;
711        int sff;
712{
713        register int tries;
714        int fd;
715        struct stat st;
716
717        for (tries = 0; tries < 10; tries++)
718        {
719                sleep((unsigned) (10 * tries));
720                errno = 0;
721                fd = open(filename, omode, cmode);
722                if (fd >= 0)
723                        break;
724                switch (errno)
725                {
726                  case ENFILE:          /* system file table full */
727                  case EINTR:           /* interrupted syscall */
728#ifdef ETXTBSY
729                  case ETXTBSY:         /* Apollo: net file locked */
730#endif
731                        continue;
732                }
733                break;
734        }
735        if (!bitset(SFF_NOLOCK, sff) &&
736            fd >= 0 &&
737            fstat(fd, &st) >= 0 &&
738            S_ISREG(st.st_mode))
739        {
740                int locktype;
741
742                /* lock the file to avoid accidental conflicts */
743                if ((omode & O_ACCMODE) != O_RDONLY)
744                        locktype = LOCK_EX;
745                else
746                        locktype = LOCK_SH;
747                (void) lockfile(fd, filename, NULL, locktype);
748                errno = 0;
749        }
750        return fd;
751}
Note: See TracBrowser for help on using the repository browser.