security patches to seal up symbolic link attacks
authorEric Allman <eric@ucbvax.Berkeley.EDU>
Fri, 26 Nov 1993 03:33:01 +0000 (19:33 -0800)
committerEric Allman <eric@ucbvax.Berkeley.EDU>
Fri, 26 Nov 1993 03:33:01 +0000 (19:33 -0800)
SCCS-vsn: usr.sbin/sendmail/src/sendmail.h 8.28
SCCS-vsn: usr.sbin/sendmail/src/conf.h 8.54
SCCS-vsn: usr.sbin/sendmail/src/util.c 8.17
SCCS-vsn: usr.sbin/sendmail/src/recipient.c 8.24

usr/src/usr.sbin/sendmail/src/conf.h
usr/src/usr.sbin/sendmail/src/recipient.c
usr/src/usr.sbin/sendmail/src/sendmail.h
usr/src/usr.sbin/sendmail/src/util.c

index bf95c49..63478e9 100644 (file)
@@ -5,7 +5,7 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)conf.h      8.53 (Berkeley) %G%
+ *     @(#)conf.h      8.54 (Berkeley) %G%
  */
 
 /*
  */
 
 /*
 # define IDENTPROTO    1       /* use IDENT proto (RFC 1413) */
 #endif
 
 # define IDENTPROTO    1       /* use IDENT proto (RFC 1413) */
 #endif
 
+/*
+**  Most systems have symbolic links today, so default them on.  You
+**  can turn them off by #undef'ing this below.
+*/
+
+# define HASLSTAT      1       /* has lstat(2) call */
+
 /**********************************************************************
 **  Operating system configuration.
 **
 /**********************************************************************
 **  Operating system configuration.
 **
@@ -508,7 +515,7 @@ extern struct group *getgrent(), *getgrnam(), *getgrgid();
 **
 **     Must be compiled in "cc -43" mode.
 **
 **
 **     Must be compiled in "cc -43" mode.
 **
-**     From Kate HedStrom <kate@ahab.rutgers.edu>.
+**     From Kate Hedstrom <kate@ahab.rutgers.edu>.
 **
 **     Note the tweaking below after the BSD defines are set.
 */
 **
 **     Note the tweaking below after the BSD defines are set.
 */
@@ -591,7 +598,10 @@ typedef int                pid_t;
 
 /* System 5 compatibility */
 #ifndef S_ISREG
 
 /* System 5 compatibility */
 #ifndef S_ISREG
-#define S_ISREG(foo)   ((foo & S_IFREG) == S_IFREG)
+# define S_ISREG(foo)  ((foo & S_IFMT) == S_IFREG)
+#endif
+#if !defined(S_ISLNK) && defined(S_IFLNK)
+# define S_ISLNK(foo)  ((foo & S_IFMT) == S_IFLNK)
 #endif
 #ifndef S_IWGRP
 #define S_IWGRP                020
 #endif
 #ifndef S_IWGRP
 #define S_IWGRP                020
index 7d48a99..f71cd23 100644 (file)
@@ -7,7 +7,7 @@
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)recipient.c        8.23 (Berkeley) %G%";
+static char sccsid[] = "@(#)recipient.c        8.24 (Berkeley) %G%";
 #endif /* not lint */
 
 # include "sendmail.h"
 #endif /* not lint */
 
 # include "sendmail.h"
@@ -356,18 +356,15 @@ recipient(a, sendq, e)
        }
        else if (m == FileMailer)
        {
        }
        else if (m == FileMailer)
        {
-               struct stat stb;
                extern bool writable();
 
                extern bool writable();
 
-               p = strrchr(buf, '/');
                /* check if writable or creatable */
                if (a->q_alias == NULL)
                {
                        a->q_flags |= QBADADDR;
                        usrerr("550 Cannot mail directly to files");
                }
                /* check if writable or creatable */
                if (a->q_alias == NULL)
                {
                        a->q_flags |= QBADADDR;
                        usrerr("550 Cannot mail directly to files");
                }
-               else if ((stat(buf, &stb) >= 0) ? (!writable(buf, &stb)) :
-                   (*p = '\0', safefile(buf, RealUid, RealGid, NULL, TRUE, S_IWRITE|S_IEXEC) != 0))
+               else if (!writable(buf))
                {
                        a->q_flags |= QBADADDR;
                        giveresponse(EX_CANTCREAT, m, NULL, a->q_alias, e);
                {
                        a->q_flags |= QBADADDR;
                        giveresponse(EX_CANTCREAT, m, NULL, a->q_alias, e);
@@ -624,42 +621,73 @@ finduser(name, fuzzyp)
 */
 
 bool
 */
 
 bool
-writable(filename, s)
+writable(filename)
        char *filename;
        char *filename;
-       register struct stat *s;
 {
        uid_t euid;
        gid_t egid;
        int bits;
 {
        uid_t euid;
        gid_t egid;
        int bits;
+       register char *p;
+       char *uname;
+       struct stat stb;
+       extern char RealUserName[];
 
        if (tTd(29, 5))
 
        if (tTd(29, 5))
-               printf("writable(%s) mode=%o\n", filename, s->st_mode);
-       if (bitset(0111, s->st_mode))
+               printf("writable(%s)\n", filename);
+
+#ifdef HASLSTAT
+       if (lstat(filename, &stb) < 0)
+#else
+       if (stat(filename, &stb) < 0)
+#endif
+       {
+               /* file does not exist -- see if directory is safe */
+               p = strrchr(filename, '/');
+               if (p == NULL)
+                       return FALSE;
+               *p = '\0';
+               if (safefile(filename, RealUid, RealGid, RealUserName,
+                            SF_MUSTOWN, S_IWRITE|S_IEXEC) != 0)
+               {
+                       *p = '/';
+                       return FALSE;
+               }
+               *p = '/';
+       }
+
+       /*
+       **  File does exist -- check that it is writable.
+       */
+
+       if (bitset(0111, stb.st_mode))
                return (FALSE);
                return (FALSE);
+
        euid = RealUid;
        euid = RealUid;
+       uname = RealUserName;
+       if (euid == 0)
+       {
+               euid = DefUid;
+               uname = DefUser;
+       }
        egid = RealGid;
        egid = RealGid;
+       if (egid == 0)
+               egid = DefGid;
        if (geteuid() == 0)
        {
        if (geteuid() == 0)
        {
-               if (bitset(S_ISUID, s->st_mode))
-                       euid = s->st_uid;
-               if (bitset(S_ISGID, s->st_mode))
-                       egid = s->st_gid;
+               if (bitset(S_ISUID, stb.st_mode))
+               {
+                       euid = stb.st_uid;
+                       uname = NULL;
+               }
+               if (bitset(S_ISGID, stb.st_mode))
+                       egid = stb.st_gid;
        }
 
        if (tTd(29, 5))
                printf("\teu/gid=%d/%d, st_u/gid=%d/%d\n",
        }
 
        if (tTd(29, 5))
                printf("\teu/gid=%d/%d, st_u/gid=%d/%d\n",
-                       euid, egid, s->st_uid, s->st_gid);
+                       euid, egid, stb.st_uid, stb.st_gid);
 
 
-       if (euid == 0)
-               return (TRUE);
-       bits = S_IWRITE;
-       if (euid != s->st_uid)
-       {
-               bits >>= 3;
-               if (egid != s->st_gid)
-                       bits >>= 3;
-       }
-       return ((s->st_mode & bits) != 0);
+       return safefile(filename, euid, egid, uname, SF_NOSLINK, S_IWRITE);
 }
 \f/*
 **  INCLUDE -- handle :include: specification.
 }
 \f/*
 **  INCLUDE -- handle :include: specification.
@@ -704,6 +732,7 @@ include(fname, forwarding, ctladdr, sendq, e)
        gid_t savedgid, gid;
        char *uname;
        int rval = 0;
        gid_t savedgid, gid;
        char *uname;
        int rval = 0;
+       int sfflags = forwarding ? SF_MUSTOWN : 0;
        char buf[MAXLINE];
 
        if (tTd(27, 2))
        char buf[MAXLINE];
 
        if (tTd(27, 2))
@@ -765,7 +794,7 @@ include(fname, forwarding, ctladdr, sendq, e)
        ev = setevent((time_t) 60, includetimeout, 0);
 
        /* the input file must be marked safe */
        ev = setevent((time_t) 60, includetimeout, 0);
 
        /* the input file must be marked safe */
-       rval = safefile(fname, uid, gid, uname, forwarding, S_IREAD);
+       rval = safefile(fname, uid, gid, uname, sfflags, S_IREAD);
        if (rval != 0)
        {
                /* don't use this :include: file */
        if (rval != 0)
        {
                /* don't use this :include: file */
index 6c4c942..9ab79a3 100644 (file)
@@ -5,7 +5,7 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)sendmail.h  8.27 (Berkeley) %G%
+ *     @(#)sendmail.h  8.28 (Berkeley) %G%
  */
 
 /*
  */
 
 /*
@@ -15,7 +15,7 @@
 # ifdef _DEFINE
 # define EXTERN
 # ifndef lint
 # ifdef _DEFINE
 # define EXTERN
 # ifndef lint
-static char SmailSccsId[] =    "@(#)sendmail.h 8.27            %G%";
+static char SmailSccsId[] =    "@(#)sendmail.h 8.28            %G%";
 # endif
 # else /*  _DEFINE */
 # define EXTERN extern
 # endif
 # else /*  _DEFINE */
 # define EXTERN extern
@@ -712,6 +712,15 @@ struct prival
 #define RF_COPYALL             (RF_COPYPARSE|RF_COPYPADDR)
 #define RF_COPYNONE            0
 
 #define RF_COPYALL             (RF_COPYPARSE|RF_COPYPADDR)
 #define RF_COPYNONE            0
 
+
+/*
+**  Flags passed to safefile.
+*/
+
+#define SF_MUSTOWN             0x0001  /* user must own this file */
+#define SF_NOSLINK             0x0002  /* file cannot be a symbolic link */
+
+
 /*
 **  Regular UNIX sockaddrs are too small to handle ISO addresses, so
 **  we are forced to declare a supertype here.
 /*
 **  Regular UNIX sockaddrs are too small to handle ISO addresses, so
 **  we are forced to declare a supertype here.
index 5015de1..dac5c8b 100644 (file)
@@ -7,7 +7,7 @@
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)util.c     8.16 (Berkeley) %G%";
+static char sccsid[] = "@(#)util.c     8.17 (Berkeley) %G%";
 #endif /* not lint */
 
 # include "sendmail.h"
 #endif /* not lint */
 
 # include "sendmail.h"
@@ -378,7 +378,9 @@ fullname(pw, buf)
 **             gid -- group id to compare against.
 **             uname -- user name to compare against (used for group
 **                     sets).
 **             gid -- group id to compare against.
 **             uname -- user name to compare against (used for group
 **                     sets).
-**             mustown -- to be safe, this uid must own the file.
+**             flags -- modifiers:
+**                     SF_MUSTOWN -- "uid" must own this file.
+**                     SF_NOSLINK -- file cannot be a symbolic link.
 **             mode -- mode bits that must match.
 **
 **     Returns:
 **             mode -- mode bits that must match.
 **
 **     Returns:
@@ -404,12 +406,12 @@ fullname(pw, buf)
 #endif
 
 int
 #endif
 
 int
-safefile(fn, uid, gid, uname, mustown, mode)
+safefile(fn, uid, gid, uname, flags, mode)
        char *fn;
        uid_t uid;
        gid_t gid;
        char *uname;
        char *fn;
        uid_t uid;
        gid_t gid;
        char *uname;
-       bool mustown;
+       int flags;
        int mode;
 {
        register char *p;
        int mode;
 {
        register char *p;
@@ -417,8 +419,8 @@ safefile(fn, uid, gid, uname, mustown, mode)
        struct stat stbuf;
 
        if (tTd(54, 4))
        struct stat stbuf;
 
        if (tTd(54, 4))
-               printf("safefile(%s, uid=%d, gid=%d, mustown=%d, mode=%o):\n",
-                       fn, uid, gid, mustown, mode);
+               printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n",
+                       fn, uid, gid, flags, mode);
        errno = 0;
 
        for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/')
        errno = 0;
 
        for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/')
@@ -459,7 +461,12 @@ safefile(fn, uid, gid, uname, mustown, mode)
                return ret;
        }
 
                return ret;
        }
 
+#ifdef HASLSTAT
+       if ((bitset(SF_NOSLINK, flags) ? lstat(fn, &stbuf)
+                                      : stat(fn, &stbuf)) < 0)
+#else
        if (stat(fn, &stbuf) < 0)
        if (stat(fn, &stbuf) < 0)
+#endif
        {
                int ret = errno;
 
        {
                int ret = errno;
 
@@ -469,6 +476,16 @@ safefile(fn, uid, gid, uname, mustown, mode)
                errno = 0;
                return ret;
        }
                errno = 0;
                return ret;
        }
+
+#ifdef S_ISLNK
+       if (bitset(SF_NOSLINK, flags) && S_ISLNK(stbuf.st_mode))
+       {
+               if (tTd(54, 4))
+                       printf("\t[mode %o]\tEPERM\n");
+               return EPERM;
+       }
+#endif
+
        if (uid == 0)
                mode >>= 6;
        else if (stbuf.st_uid != uid)
        if (uid == 0)
                mode >>= 6;
        else if (stbuf.st_uid != uid)
@@ -496,7 +513,8 @@ safefile(fn, uid, gid, uname, mustown, mode)
        if (tTd(54, 4))
                printf("\t[uid %d, stat %o, mode %o] ",
                        stbuf.st_uid, stbuf.st_mode, mode);
        if (tTd(54, 4))
                printf("\t[uid %d, stat %o, mode %o] ",
                        stbuf.st_uid, stbuf.st_mode, mode);
-       if ((stbuf.st_uid == uid || stbuf.st_uid == 0 || !mustown) &&
+       if ((stbuf.st_uid == uid || stbuf.st_uid == 0 ||
+            !bitset(SF_MUSTOWN, flags)) &&
            (stbuf.st_mode & mode) == mode)
        {
                if (tTd(54, 4))
            (stbuf.st_mode & mode) == mode)
        {
                if (tTd(54, 4))