summaryrefslogtreecommitdiffstats
path: root/usr.sbin/sendmail/src/safefile.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/sendmail/src/safefile.c')
-rw-r--r--usr.sbin/sendmail/src/safefile.c712
1 files changed, 0 insertions, 712 deletions
diff --git a/usr.sbin/sendmail/src/safefile.c b/usr.sbin/sendmail/src/safefile.c
deleted file mode 100644
index 842a09f..0000000
--- a/usr.sbin/sendmail/src/safefile.c
+++ /dev/null
@@ -1,712 +0,0 @@
-/*
- * Copyright (c) 1983, 1995-1997 Eric P. Allman
- * Copyright (c) 1988, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef lint
-static char sccsid[] = "@(#)safefile.c 8.18 (Berkeley) 8/1/97";
-#endif /* not lint */
-
-# include "sendmail.h"
- /*
-** SAFEFILE -- return true if a file exists and is safe for a user.
-**
-** Parameters:
-** fn -- filename to check.
-** uid -- user id to compare against.
-** gid -- group id to compare against.
-** uname -- user name to compare against (used for group
-** sets).
-** flags -- modifiers:
-** SFF_MUSTOWN -- "uid" must own this file.
-** SFF_NOSLINK -- file cannot be a symbolic link.
-** mode -- mode bits that must match.
-** st -- if set, points to a stat structure that will
-** get the stat info for the file.
-**
-** Returns:
-** 0 if fn exists, is owned by uid, and matches mode.
-** An errno otherwise. The actual errno is cleared.
-**
-** Side Effects:
-** none.
-*/
-
-#include <grp.h>
-
-#ifndef S_IXOTH
-# define S_IXOTH (S_IEXEC >> 6)
-#endif
-
-#ifndef S_IXGRP
-# define S_IXGRP (S_IEXEC >> 3)
-#endif
-
-#ifndef S_IXUSR
-# define S_IXUSR (S_IEXEC)
-#endif
-
-int
-safefile(fn, uid, gid, uname, flags, mode, st)
- char *fn;
- UID_T uid;
- GID_T gid;
- char *uname;
- int flags;
- int mode;
- struct stat *st;
-{
- register char *p;
- register struct group *gr = NULL;
- int file_errno = 0;
- bool checkpath;
- struct stat stbuf;
- struct stat fstbuf;
- char fbuf[MAXPATHLEN + 1];
-
- if (tTd(44, 4))
- printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n",
- fn, (int) uid, (int) gid, flags, mode);
- errno = 0;
- if (st == NULL)
- st = &fstbuf;
- if (strlen(fn) > sizeof fbuf - 1)
- {
- if (tTd(44, 4))
- printf("\tpathname too long\n");
- return ENAMETOOLONG;
- }
- strcpy(fbuf, fn);
- fn = fbuf;
-
- /* ignore SFF_SAFEDIRPATH if we are debugging */
- if (RealUid != 0 && RunAsUid == RealUid)
- flags &= ~SFF_SAFEDIRPATH;
-
- /* first check to see if the file exists at all */
-#ifdef HASLSTAT
- if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st)
- : stat(fn, st)) < 0)
-#else
- if (stat(fn, st) < 0)
-#endif
- {
- file_errno = errno;
- }
- else if (bitset(SFF_SETUIDOK, flags) &&
- !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) &&
- S_ISREG(st->st_mode))
- {
- /*
- ** If final file is setuid, run as the owner of that
- ** file. Gotta be careful not to reveal anything too
- ** soon here!
- */
-
-#ifdef SUID_ROOT_FILES_OK
- if (bitset(S_ISUID, st->st_mode))
-#else
- if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0)
-#endif
- {
- uid = st->st_uid;
- uname = NULL;
- }
-#ifdef SUID_ROOT_FILES_OK
- if (bitset(S_ISGID, st->st_mode))
-#else
- if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0)
-#endif
- gid = st->st_gid;
- }
-
- checkpath = !bitset(SFF_NOPATHCHECK, flags) ||
- (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags));
- if (bitset(SFF_NOWLINK, flags) && !bitset(SFF_SAFEDIRPATH, flags))
- {
- int ret;
-
- /* check the directory */
- p = strrchr(fn, '/');
- if (p == NULL)
- {
- ret = safedirpath(".", uid, gid, uname, flags|SFF_SAFEDIRPATH);
- }
- else
- {
- *p = '\0';
- ret = safedirpath(fn, uid, gid, uname, flags|SFF_SAFEDIRPATH);
- *p = '/';
- }
- if (ret == 0)
- {
- /* directory is safe */
- checkpath = FALSE;
- }
- else
- {
- /* directory is writable: disallow links */
- flags |= SFF_NOLINK;
- }
- }
-
- if (checkpath)
- {
- int ret;
-
- p = strrchr(fn, '/');
- if (p == NULL)
- {
- ret = safedirpath(".", uid, gid, uname, flags);
- }
- else
- {
- *p = '\0';
- ret = safedirpath(fn, uid, gid, uname, flags);
- *p = '/';
- }
- if (ret != 0)
- return ret;
- }
-
- /*
- ** If the target file doesn't exist, check the directory to
- ** ensure that it is writable by this user.
- */
-
- if (file_errno != 0)
- {
- int ret = file_errno;
- char *dir = fn;
-
- if (tTd(44, 4))
- printf("\t%s\n", errstring(ret));
-
- errno = 0;
- if (!bitset(SFF_CREAT, flags) || file_errno != ENOENT)
- return ret;
-
- /* check to see if legal to create the file */
- p = strrchr(dir, '/');
- if (p == NULL)
- dir = ".";
- else if (p == dir)
- dir = "/";
- else
- *p = '\0';
- if (stat(dir, &stbuf) >= 0)
- {
- int md = S_IWRITE|S_IEXEC;
- if (stbuf.st_uid != uid)
- md >>= 6;
- if ((stbuf.st_mode & md) != md)
- errno = EACCES;
- }
- ret = errno;
- if (tTd(44, 4))
- printf("\t[final dir %s uid %d mode %lo] %s\n",
- dir, (int) stbuf.st_uid, (u_long) stbuf.st_mode,
- errstring(ret));
- if (p != NULL)
- *p = '/';
- st->st_mode = ST_MODE_NOFILE;
- return ret;
- }
-
-#ifdef S_ISLNK
- if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode))
- {
- if (tTd(44, 4))
- printf("\t[slink mode %o]\tE_SM_NOSLINK\n",
- st->st_mode);
- return E_SM_NOSLINK;
- }
-#endif
- if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode))
- {
- if (tTd(44, 4))
- printf("\t[non-reg mode %o]\tE_SM_REGONLY\n",
- st->st_mode);
- return E_SM_REGONLY;
- }
- if (bitset(SFF_NOWFILES, flags) &&
- bitset(S_IWOTH | (UnsafeGroupWrites ? S_IWGRP : 0), st->st_mode))
- {
- if (tTd(44, 4))
- printf("\t[write bits %o]\tE_SM_%cWFILE\n",
- st->st_mode,
- bitset(S_IWOTH, st->st_mode) ? 'W' : 'G');
- return bitset(S_IWOTH, st->st_mode) ? E_SM_WWFILE : E_SM_GWFILE;
- }
- if (bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) &&
- bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode))
- {
- if (tTd(44, 4))
- printf("\t[exec bits %o]\tE_SM_ISEXEC]\n",
- st->st_mode);
- return E_SM_ISEXEC;
- }
- if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1)
- {
- if (tTd(44, 4))
- printf("\t[link count %d]\tE_SM_NOHLINK\n",
- st->st_nlink);
- return E_SM_NOHLINK;
- }
-
- if (uid == 0 && bitset(SFF_OPENASROOT, flags))
- ;
- else if (uid == 0 && !bitset(SFF_ROOTOK, flags))
- mode >>= 6;
- else if (st->st_uid != uid)
- {
- mode >>= 3;
- if (st->st_gid == gid)
- ;
-#ifndef NO_GROUP_SET
- else if (uname != NULL && !DontInitGroups &&
- ((gr != NULL && gr->gr_gid == st->st_gid) ||
- (gr = getgrgid(st->st_gid)) != NULL))
- {
- register char **gp;
-
- for (gp = gr->gr_mem; *gp != NULL; gp++)
- if (strcmp(*gp, uname) == 0)
- break;
- if (*gp == NULL)
- mode >>= 3;
- }
-#endif
- else
- mode >>= 3;
- }
- if (tTd(44, 4))
- printf("\t[uid %d, nlink %d, stat %lo, mode %lo] ",
- (int) st->st_uid, (int) st->st_nlink,
- (u_long) st->st_mode, (u_long) mode);
- if ((st->st_uid == uid || st->st_uid == 0 ||
- !bitset(SFF_MUSTOWN, flags)) &&
- (st->st_mode & mode) == mode)
- {
- if (tTd(44, 4))
- printf("\tOK\n");
- return 0;
- }
- if (tTd(44, 4))
- printf("\tEACCES\n");
- return EACCES;
-}
- /*
-** SAFEDIRPATH -- check to make sure a path to a directory is safe
-**
-** Safe means not writable and owned by the right folks.
-**
-** Parameters:
-** fn -- filename to check.
-** uid -- user id to compare against.
-** gid -- group id to compare against.
-** uname -- user name to compare against (used for group
-** sets).
-** flags -- modifiers:
-** SFF_ROOTOK -- ok to use root permissions to open.
-** SFF_SAFEDIRPATH -- writable directories are considered
-** to be fatal errors.
-**
-** Returns:
-** 0 -- if the directory path is "safe".
-** else -- an error number associated with the path.
-*/
-
-int
-safedirpath(fn, uid, gid, uname, flags)
- char *fn;
- UID_T uid;
- GID_T gid;
- char *uname;
- int flags;
-{
- char *p;
- register struct group *gr = NULL;
- int ret = 0;
- struct stat stbuf;
-
- /* special case root directory */
- if (*fn == '\0')
- fn = "/";
-
- if (tTd(44, 4))
- printf("safedirpath(%s, uid=%ld, gid=%ld, flags=%x):\n",
- fn, (long) uid, (long) gid, flags);
-
- p = fn;
- do
- {
- if (*p == '\0')
- *p = '/';
- p = strchr(++p, '/');
- if (p != NULL)
- *p = '\0';
- if (stat(fn, &stbuf) < 0)
- {
- ret = errno;
- break;
- }
- if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) &&
- bitset(S_IWGRP|S_IWOTH, stbuf.st_mode))
- {
- if (tTd(44, 4))
- printf("\t[dir %s] mode %o\n",
- fn, stbuf.st_mode);
- if (bitset(SFF_SAFEDIRPATH, flags))
- {
- if (bitset(S_IWOTH, stbuf.st_mode))
- ret = E_SM_WWDIR;
- else
- ret = E_SM_GWDIR;
- break;
- }
- if (Verbose > 1)
- message("051 WARNING: writable directory %s", fn);
- }
- if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags))
- {
- if (bitset(S_IXOTH, stbuf.st_mode))
- continue;
- ret = EACCES;
- break;
- }
- if (stbuf.st_uid == uid &&
- bitset(S_IXUSR, stbuf.st_mode))
- continue;
- if (stbuf.st_gid == gid &&
- bitset(S_IXGRP, stbuf.st_mode))
- continue;
-#ifndef NO_GROUP_SET
- if (uname != NULL && !DontInitGroups &&
- ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
- (gr = getgrgid(stbuf.st_gid)) != NULL))
- {
- register char **gp;
-
- for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
- if (strcmp(*gp, uname) == 0)
- break;
- if (gp != NULL && *gp != NULL &&
- bitset(S_IXGRP, stbuf.st_mode))
- continue;
- }
-#endif
- if (!bitset(S_IXOTH, stbuf.st_mode))
- {
- ret = EACCES;
- break;
- }
- } while (p != NULL);
- if (ret != 0 && tTd(44, 4))
- printf("\t[dir %s] %s\n", fn, errstring(ret));
- if (p != NULL)
- *p = '/';
- return ret;
-}
- /*
-** SAFEOPEN -- do a file open with extra checking
-**
-** Parameters:
-** fn -- the file name to open.
-** omode -- the open-style mode flags.
-** cmode -- the create-style mode flags.
-** sff -- safefile flags.
-**
-** Returns:
-** Same as open.
-*/
-
-#ifndef O_ACCMODE
-# define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
-#endif
-
-int
-safeopen(fn, omode, cmode, sff)
- char *fn;
- int omode;
- int cmode;
- int sff;
-{
- int rval;
- int fd;
- int smode;
- struct stat stb;
-
- if (bitset(O_CREAT, omode))
- sff |= SFF_CREAT;
- omode &= ~O_CREAT;
- smode = 0;
- switch (omode & O_ACCMODE)
- {
- case O_RDONLY:
- smode = S_IREAD;
- break;
-
- case O_WRONLY:
- smode = S_IWRITE;
- break;
-
- case O_RDWR:
- smode = S_IREAD|S_IWRITE;
- break;
-
- default:
- smode = 0;
- break;
- }
- if (bitset(SFF_OPENASROOT, sff))
- rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName,
- sff, smode, &stb);
- else
- rval = safefile(fn, RealUid, RealGid, RealUserName,
- sff, smode, &stb);
- if (rval != 0)
- {
- errno = rval;
- return -1;
- }
- if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff))
- omode |= O_EXCL|O_CREAT;
-
- fd = dfopen(fn, omode, cmode, sff);
- if (fd < 0)
- return fd;
- if (filechanged(fn, fd, &stb, sff))
- {
- syserr("554 cannot open: file %s changed after open", fn);
- close(fd);
- errno = E_SM_FILECHANGE;
- return -1;
- }
- return fd;
-}
- /*
-** SAFEFOPEN -- do a file open with extra checking
-**
-** Parameters:
-** fn -- the file name to open.
-** omode -- the open-style mode flags.
-** cmode -- the create-style mode flags.
-** sff -- safefile flags.
-**
-** Returns:
-** Same as fopen.
-*/
-
-FILE *
-safefopen(fn, omode, cmode, sff)
- char *fn;
- int omode;
- int cmode;
- int sff;
-{
- int fd;
- FILE *fp;
- char *fmode;
-
- switch (omode & O_ACCMODE)
- {
- case O_RDONLY:
- fmode = "r";
- break;
-
- case O_WRONLY:
- if (bitset(O_APPEND, omode))
- fmode = "a";
- else
- fmode = "w";
- break;
-
- case O_RDWR:
- if (bitset(O_TRUNC, omode))
- fmode = "w+";
- else if (bitset(O_APPEND, omode))
- fmode = "a+";
- else
- fmode = "r+";
- break;
-
- default:
- syserr("safefopen: unknown omode %o", omode);
- fmode = "x";
- }
- fd = safeopen(fn, omode, cmode, sff);
- if (fd < 0)
- {
- if (tTd(44, 10))
- printf("safefopen: safeopen failed: %s\n",
- errstring(errno));
- return NULL;
- }
- fp = fdopen(fd, fmode);
- if (fp != NULL)
- return fp;
-
- if (tTd(44, 10))
- {
- printf("safefopen: fdopen(%s, %s) failed: omode=%x, sff=%x, err=%s\n",
- fn, fmode, omode, sff, errstring(errno));
-#ifndef NOT_SENDMAIL
- dumpfd(fd, TRUE, FALSE);
-#endif
- }
- (void) close(fd);
- return NULL;
-}
- /*
-** FILECHANGED -- check to see if file changed after being opened
-**
-** Parameters:
-** fn -- pathname of file to check.
-** fd -- file descriptor to check.
-** stb -- stat structure from before open.
-** sff -- safe file flags.
-**
-** Returns:
-** TRUE -- if a problem was detected.
-** FALSE -- if this file is still the same.
-*/
-
-bool
-filechanged(fn, fd, stb, sff)
- char *fn;
- int fd;
- struct stat *stb;
- int sff;
-{
- struct stat sta;
-
- if (stb->st_mode == ST_MODE_NOFILE)
- {
-#if HASLSTAT && BOGUS_O_EXCL
- /* only necessary if exclusive open follows symbolic links */
- if (lstat(fn, stb) < 0 || stb->st_nlink != 1)
- return TRUE;
-#else
- return FALSE;
-#endif
- }
- if (fstat(fd, &sta) < 0)
- return TRUE;
-
- if (sta.st_nlink != stb->st_nlink ||
- sta.st_dev != stb->st_dev ||
- sta.st_ino != stb->st_ino ||
-#if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */
- sta.st_gen != stb->st_gen ||
-#endif
- sta.st_uid != stb->st_uid ||
- sta.st_gid != stb->st_gid)
- {
- if (tTd(44, 8))
- {
- printf("File changed after opening:\n");
- printf(" nlink = %ld/%ld\n",
- (long) stb->st_nlink, (long) sta.st_nlink);
- printf(" dev = %ld/%ld\n",
- (long) stb->st_dev, (long) sta.st_dev);
- printf(" ino = %ld/%ld\n",
- (long) stb->st_ino, (long) sta.st_ino);
-#if HAS_ST_GEN
- printf(" gen = %ld/%ld\n",
- (long) stb->st_gen, (long) sta.st_gen);
-#endif
- printf(" uid = %ld/%ld\n",
- (long) stb->st_uid, (long) sta.st_uid);
- printf(" gid = %ld/%ld\n",
- (long) stb->st_gid, (long) sta.st_gid);
- }
- return TRUE;
- }
-
- return FALSE;
-}
- /*
-** DFOPEN -- determined file open
-**
-** This routine has the semantics of open, except that it will
-** keep trying a few times to make this happen. The idea is that
-** on very loaded systems, we may run out of resources (inodes,
-** whatever), so this tries to get around it.
-*/
-
-int
-dfopen(filename, omode, cmode, sff)
- char *filename;
- int omode;
- int cmode;
- int sff;
-{
- register int tries;
- int fd;
- struct stat st;
-
- for (tries = 0; tries < 10; tries++)
- {
- sleep((unsigned) (10 * tries));
- errno = 0;
- fd = open(filename, omode, cmode);
- if (fd >= 0)
- break;
- switch (errno)
- {
- case ENFILE: /* system file table full */
- case EINTR: /* interrupted syscall */
-#ifdef ETXTBSY
- case ETXTBSY: /* Apollo: net file locked */
-#endif
- continue;
- }
- break;
- }
- if (!bitset(SFF_NOLOCK, sff) &&
- fd >= 0 &&
- fstat(fd, &st) >= 0 &&
- S_ISREG(st.st_mode))
- {
- int locktype;
-
- /* lock the file to avoid accidental conflicts */
- if ((omode & O_ACCMODE) != O_RDONLY)
- locktype = LOCK_EX;
- else
- locktype = LOCK_SH;
- (void) lockfile(fd, filename, NULL, locktype);
- errno = 0;
- }
- return fd;
-}
OpenPOWER on IntegriCloud