121308Sache/*
221308Sache * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.
3136644Sache *	All rights reserved.
421308Sache * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
521308Sache * Copyright (c) 1988, 1993
621308Sache *	The Regents of the University of California.  All rights reserved.
721308Sache *
821308Sache * By using this file, you agree to the terms and conditions set
921308Sache * forth in the LICENSE file which can be found at the top level of
1058310Sache * the sendmail distribution.
1121308Sache *
1221308Sache */
1321308Sache
1421308Sache#include <sendmail.h>
1521308Sache#include <sm/io.h>
1621308Sache#include <sm/errstring.h>
1721308Sache
1821308SacheSM_RCSID("@(#)$Id: safefile.c,v 8.129 2008/08/04 18:07:04 gshapiro Exp $")
1921308Sache
2021308Sache
2158310Sache/*
2221308Sache**  SAFEFILE -- return 0 if a file exists and is safe for a user.
2321308Sache**
2421308Sache**	Parameters:
2521308Sache**		fn -- filename to check.
26136644Sache**		uid -- user id to compare against.
2721308Sache**		gid -- group id to compare against.
2821308Sache**		user -- user name to compare against (used for group
29136644Sache**			sets).
30136644Sache**		flags -- modifiers:
31136644Sache**			SFF_MUSTOWN -- "uid" must own this file.
32136644Sache**			SFF_NOSLINK -- file cannot be a symbolic link.
3321308Sache**		mode -- mode bits that must match.
3421308Sache**		st -- if set, points to a stat structure that will
3521308Sache**			get the stat info for the file.
3621308Sache**
3721308Sache**	Returns:
3821308Sache**		0 if fn exists, is owned by uid, and matches mode.
3921308Sache**		An errno otherwise.  The actual errno is cleared.
40136644Sache**
4135486Sache**	Side Effects:
4235486Sache**		none.
4358310Sache*/
4421308Sache
4521308Sacheint
4621308Sachesafefile(fn, uid, gid, user, flags, mode, st)
4721308Sache	char *fn;
4821308Sache	UID_T uid;
4921308Sache	GID_T gid;
5021308Sache	char *user;
5121308Sache	long flags;
5221308Sache	int mode;
5321308Sache	struct stat *st;
5421308Sache{
5521308Sache	register char *p;
56119610Sache	register struct group *gr = NULL;
57119610Sache	int file_errno = 0;
58119610Sache	bool checkpath;
5921308Sache	struct stat stbuf;
60136644Sache	struct stat fstbuf;
61119610Sache	char fbuf[MAXPATHLEN];
6258310Sache
63119610Sache	if (tTd(44, 4))
64119610Sache		sm_dprintf("safefile(%s, uid=%d, gid=%d, flags=%lx, mode=%o):\n",
65119610Sache			fn, (int) uid, (int) gid, flags, mode);
66119610Sache	errno = 0;
67119610Sache	if (sm_strlcpy(fbuf, fn, sizeof fbuf) >= sizeof fbuf)
68119610Sache	{
69119610Sache		if (tTd(44, 4))
70119610Sache			sm_dprintf("\tpathname too long\n");
71119610Sache		return ENAMETOOLONG;
72119610Sache	}
73119610Sache	fn = fbuf;
74119610Sache	if (st == NULL)
75136644Sache		st = &fstbuf;
76119610Sache
7758310Sache	/* ignore SFF_SAFEDIRPATH if we are debugging */
7858310Sache	if (RealUid != 0 && RunAsUid == RealUid)
7958310Sache		flags &= ~SFF_SAFEDIRPATH;
8058310Sache
8158310Sache	/* first check to see if the file exists at all */
8226497Sache# if HASLSTAT
8326497Sache	if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st)
8426497Sache					: stat(fn, st)) < 0)
8558310Sache# else /* HASLSTAT */
8626497Sache	if (stat(fn, st) < 0)
8726497Sache# endif /* HASLSTAT */
8858310Sache	{
8926497Sache		file_errno = errno;
9021308Sache	}
9121308Sache	else if (bitset(SFF_SETUIDOK, flags) &&
9221308Sache		 !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) &&
9321308Sache		 S_ISREG(st->st_mode))
9421308Sache	{
9521308Sache		/*
9621308Sache		**  If final file is set-user-ID, run as the owner of that
9721308Sache		**  file.  Gotta be careful not to reveal anything too
9858310Sache		**  soon here!
9958310Sache		*/
10026497Sache
101136644Sache# ifdef SUID_ROOT_FILES_OK
102136644Sache		if (bitset(S_ISUID, st->st_mode))
103136644Sache# else /* SUID_ROOT_FILES_OK */
104136644Sache		if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 &&
105136644Sache		    st->st_uid != TrustedUid)
106136644Sache# endif /* SUID_ROOT_FILES_OK */
107136644Sache		{
10821308Sache			uid = st->st_uid;
10921308Sache			user = NULL;
11021308Sache		}
11121308Sache# ifdef SUID_ROOT_FILES_OK
11221308Sache		if (bitset(S_ISGID, st->st_mode))
11375406Sache# else /* SUID_ROOT_FILES_OK */
11421308Sache		if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0)
11575406Sache# endif /* SUID_ROOT_FILES_OK */
11675406Sache			gid = st->st_gid;
11721308Sache	}
11821308Sache
11921308Sache	checkpath = !bitset(SFF_NOPATHCHECK, flags) ||
12021308Sache		    (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags));
12121308Sache	if (bitset(SFF_NOWLINK, flags) && !bitset(SFF_SAFEDIRPATH, flags))
12221308Sache	{
12321308Sache		int ret;
12475406Sache
12521308Sache		/* check the directory */
12621308Sache		p = strrchr(fn, '/');
12721308Sache		if (p == NULL)
12821308Sache		{
12921308Sache			ret = safedirpath(".", uid, gid, user,
13021308Sache					  flags|SFF_SAFEDIRPATH, 0, 0);
13121308Sache		}
13221308Sache		else
13321308Sache		{
134119610Sache			*p = '\0';
13521308Sache			ret = safedirpath(fn, uid, gid, user,
13621308Sache					  flags|SFF_SAFEDIRPATH, 0, 0);
13758310Sache			*p = '/';
13858310Sache		}
13958310Sache		if (ret == 0)
14021308Sache		{
14158310Sache			/* directory is safe */
14221308Sache			checkpath = false;
14321308Sache		}
14421308Sache		else
14521308Sache		{
14621308Sache# if HASLSTAT
14721308Sache			/* Need lstat() information if called stat() before */
14821308Sache			if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0)
14921308Sache			{
15021308Sache				ret = errno;
15175406Sache				if (tTd(44, 4))
15221308Sache					sm_dprintf("\t%s\n", sm_errstring(ret));
15321308Sache				return ret;
15421308Sache			}
15521308Sache# endif /* HASLSTAT */
15621308Sache			/* directory is writable: disallow links */
15721308Sache			flags |= SFF_NOLINK;
15821308Sache		}
15921308Sache	}
16021308Sache
16121308Sache	if (checkpath)
16221308Sache	{
16375406Sache		int ret;
16421308Sache
16521308Sache		p = strrchr(fn, '/');
166136644Sache		if (p == NULL)
167136644Sache		{
16858310Sache			ret = safedirpath(".", uid, gid, user, flags, 0, 0);
16921308Sache		}
17035486Sache		else
171136644Sache		{
172136644Sache			*p = '\0';
173136644Sache			ret = safedirpath(fn, uid, gid, user, flags, 0, 0);
174136644Sache			*p = '/';
175136644Sache		}
176136644Sache		if (ret != 0)
177136644Sache			return ret;
17821308Sache	}
179136644Sache
18021308Sache	/*
18126497Sache	**  If the target file doesn't exist, check the directory to
18221308Sache	**  ensure that it is writable by this user.
18321308Sache	*/
18421308Sache
18521308Sache	if (file_errno != 0)
18635486Sache	{
18721308Sache		int ret = file_errno;
18835486Sache		char *dir = fn;
18935486Sache
19021308Sache		if (tTd(44, 4))
191136644Sache			sm_dprintf("\t%s\n", sm_errstring(ret));
19235486Sache
19335486Sache		errno = 0;
19435486Sache		if (!bitset(SFF_CREAT, flags) || file_errno != ENOENT)
195136644Sache			return ret;
196119610Sache
197119610Sache		/* check to see if legal to create the file */
198119610Sache		p = strrchr(dir, '/');
199119610Sache		if (p == NULL)
200136644Sache			dir = ".";
201136644Sache		else if (p == dir)
202136644Sache			dir = "/";
203136644Sache		else
204119610Sache			*p = '\0';
205119610Sache		if (stat(dir, &stbuf) >= 0)
206119610Sache		{
207119610Sache			int md = S_IWRITE|S_IEXEC;
208136644Sache
209136644Sache			ret = 0;
210136644Sache			if (stbuf.st_uid == uid)
211136644Sache				/* EMPTY */
21258310Sache				;
21358310Sache			else if (uid == 0 && stbuf.st_uid == TrustedUid)
214119610Sache				/* EMPTY */
21558310Sache				;
21635486Sache			else
21721308Sache			{
218136644Sache				md >>= 3;
219136644Sache				if (stbuf.st_gid == gid)
220136644Sache					/* EMPTY */
221136644Sache					;
22221308Sache# ifndef NO_GROUP_SET
22321308Sache				else if (user != NULL && !DontInitGroups &&
22421308Sache					 ((gr != NULL &&
22521308Sache					   gr->gr_gid == stbuf.st_gid) ||
226136644Sache					  (gr = getgrgid(stbuf.st_gid)) != NULL))
22721308Sache				{
228119610Sache					register char **gp;
22921308Sache
230119610Sache					for (gp = gr->gr_mem; *gp != NULL; gp++)
23121308Sache						if (strcmp(*gp, user) == 0)
23221308Sache							break;
23321308Sache					if (*gp == NULL)
23421308Sache						md >>= 3;
23521308Sache				}
23621308Sache# endif /* ! NO_GROUP_SET */
23758310Sache				else
23821308Sache					md >>= 3;
23921308Sache			}
240119610Sache			if ((stbuf.st_mode & md) != md)
241119610Sache				ret = errno = EACCES;
24221308Sache		}
24321308Sache		else
244119610Sache			ret = errno;
245119610Sache		if (tTd(44, 4))
246119610Sache			sm_dprintf("\t[final dir %s uid %d mode %lo] %s\n",
247136644Sache				dir, (int) stbuf.st_uid,
248136644Sache				(unsigned long) stbuf.st_mode,
249136644Sache				sm_errstring(ret));
250136644Sache		if (p != NULL)
251136644Sache			*p = '/';
252136644Sache		st->st_mode = ST_MODE_NOFILE;
253119610Sache		return ret;
25421308Sache	}
25521308Sache
256119610Sache# ifdef S_ISLNK
257119610Sache	if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode))
25821308Sache	{
259165670Sache		if (tTd(44, 4))
260165670Sache			sm_dprintf("\t[slink mode %lo]\tE_SM_NOSLINK\n",
261165670Sache				(unsigned long) st->st_mode);
262165670Sache		return E_SM_NOSLINK;
263165670Sache	}
26421308Sache# endif /* S_ISLNK */
265119610Sache	if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode))
266136644Sache	{
267136644Sache		if (tTd(44, 4))
268136644Sache			sm_dprintf("\t[non-reg mode %lo]\tE_SM_REGONLY\n",
269136644Sache				(unsigned long) st->st_mode);
270136644Sache		return E_SM_REGONLY;
271136644Sache	}
272136644Sache	if (bitset(SFF_NOGWFILES, flags) &&
273136644Sache	    bitset(S_IWGRP, st->st_mode))
274136644Sache	{
275136644Sache		if (tTd(44, 4))
276136644Sache			sm_dprintf("\t[write bits %lo]\tE_SM_GWFILE\n",
277136644Sache				(unsigned long) st->st_mode);
278136644Sache		return E_SM_GWFILE;
279136644Sache	}
280136644Sache	if (bitset(SFF_NOWWFILES, flags) &&
281136644Sache	    bitset(S_IWOTH, st->st_mode))
28221308Sache	{
28321308Sache		if (tTd(44, 4))
28421308Sache			sm_dprintf("\t[write bits %lo]\tE_SM_WWFILE\n",
28521308Sache				(unsigned long) st->st_mode);
28621308Sache		return E_SM_WWFILE;
28721308Sache	}
28821308Sache	if (bitset(SFF_NOGRFILES, flags) && bitset(S_IRGRP, st->st_mode))
28921308Sache	{
29021308Sache		if (tTd(44, 4))
29121308Sache			sm_dprintf("\t[read bits %lo]\tE_SM_GRFILE\n",
292136644Sache				(unsigned long) st->st_mode);
29321308Sache		return E_SM_GRFILE;
294119610Sache	}
295119610Sache	if (bitset(SFF_NOWRFILES, flags) && bitset(S_IROTH, st->st_mode))
296119610Sache	{
29721308Sache		if (tTd(44, 4))
29821308Sache			sm_dprintf("\t[read bits %lo]\tE_SM_WRFILE\n",
29921308Sache				(unsigned long) st->st_mode);
30021308Sache		return E_SM_WRFILE;
30121308Sache	}
30275406Sache	if (!bitset(SFF_EXECOK, flags) &&
30375406Sache	    bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) &&
30421308Sache	    bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode))
30521308Sache	{
30675406Sache		if (tTd(44, 4))
30747558Sache			sm_dprintf("\t[exec bits %lo]\tE_SM_ISEXEC\n",
30821308Sache				(unsigned long) st->st_mode);
309136644Sache		return E_SM_ISEXEC;
31075406Sache	}
31121308Sache	if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1)
31235486Sache	{
31321308Sache		if (tTd(44, 4))
31426497Sache			sm_dprintf("\t[link count %d]\tE_SM_NOHLINK\n",
31521308Sache				(int) st->st_nlink);
31626497Sache		return E_SM_NOHLINK;
31775406Sache	}
31821308Sache
31975406Sache	if (uid == 0 && bitset(SFF_OPENASROOT, flags))
32021308Sache		/* EMPTY */
32175406Sache		;
32275406Sache	else if (uid == 0 && !bitset(SFF_ROOTOK, flags))
32375406Sache		mode >>= 6;
32475406Sache	else if (st->st_uid == uid)
32575406Sache		/* EMPTY */
32675406Sache		;
32721308Sache	else if (uid == 0 && st->st_uid == TrustedUid)
32875406Sache		/* EMPTY */
32975406Sache		;
33075406Sache	else
33175406Sache	{
33275406Sache		mode >>= 3;
33375406Sache		if (st->st_gid == gid)
33475406Sache			/* EMPTY */
33575406Sache			;
33675406Sache# ifndef NO_GROUP_SET
33775406Sache		else if (user != NULL && !DontInitGroups &&
33858310Sache			 ((gr != NULL && gr->gr_gid == st->st_gid) ||
33935486Sache			  (gr = getgrgid(st->st_gid)) != NULL))
34035486Sache		{
34135486Sache			register char **gp;
34235486Sache
34335486Sache			for (gp = gr->gr_mem; *gp != NULL; gp++)
34435486Sache				if (strcmp(*gp, user) == 0)
34535486Sache					break;
34675406Sache			if (*gp == NULL)
34775406Sache				mode >>= 3;
34875406Sache		}
34975406Sache# endif /* ! NO_GROUP_SET */
35075406Sache		else
35135486Sache			mode >>= 3;
35235486Sache	}
35335486Sache	if (tTd(44, 4))
35435486Sache		sm_dprintf("\t[uid %d, nlink %d, stat %lo, mode %lo] ",
355119610Sache			(int) st->st_uid, (int) st->st_nlink,
356119610Sache			(unsigned long) st->st_mode, (unsigned long) mode);
357119610Sache	if ((st->st_uid == uid || st->st_uid == 0 ||
358119610Sache	     st->st_uid == TrustedUid ||
359119610Sache	     !bitset(SFF_MUSTOWN, flags)) &&
360119610Sache	    (st->st_mode & mode) == mode)
361119610Sache	{
36235486Sache		if (tTd(44, 4))
36321308Sache			sm_dprintf("\tOK\n");
36421308Sache		return 0;
36521308Sache	}
36675406Sache	if (tTd(44, 4))
36775406Sache		sm_dprintf("\tEACCES\n");
36875406Sache	return EACCES;
36975406Sache}
37021308Sache/*
37121308Sache**  SAFEDIRPATH -- check to make sure a path to a directory is safe
372136644Sache**
373136644Sache**	Safe means not writable and owned by the right folks.
374136644Sache**
375136644Sache**	Parameters:
37621308Sache**		fn -- filename to check.
377136644Sache**		uid -- user id to compare against.
37821308Sache**		gid -- group id to compare against.
379136644Sache**		user -- user name to compare against (used for group
38021308Sache**			sets).
38121308Sache**		flags -- modifiers:
38221308Sache**			SFF_ROOTOK -- ok to use root permissions to open.
38321308Sache**			SFF_SAFEDIRPATH -- writable directories are considered
38421308Sache**				to be fatal errors.
38521308Sache**		level -- symlink recursive level.
38621308Sache**		offset -- offset into fn to start checking from.
387119610Sache**
388136644Sache**	Returns:
389136644Sache**		0 -- if the directory path is "safe".
390136644Sache**		else -- an error number associated with the path.
391136644Sache*/
392136644Sache
393136644Sacheint
394136644Sachesafedirpath(fn, uid, gid, user, flags, level, offset)
395136644Sache	char *fn;
39621308Sache	UID_T uid;
39721308Sache	GID_T gid;
39821308Sache	char *user;
399119610Sache	long flags;
40021308Sache	int level;
401119610Sache	int offset;
40247558Sache{
40347558Sache	int ret = 0;
40447558Sache	int mode = S_IWOTH;
405119610Sache	char save = '\0';
40647558Sache	char *saveptr = NULL;
40747558Sache	char *p, *enddir;
40821308Sache	register struct group *gr = NULL;
40921308Sache	char s[MAXLINKPATHLEN];
41021308Sache	struct stat stbuf;
41121308Sache
41221308Sache	/* make sure we aren't in a symlink loop */
41321308Sache	if (level > MAXSYMLINKS)
41421308Sache		return ELOOP;
41521308Sache
41675406Sache	if (level < 0 || offset < 0 || offset > strlen(fn))
41721308Sache		return EINVAL;
41821308Sache
41921308Sache	/* special case root directory */
42021308Sache	if (*fn == '\0')
42121308Sache		fn = "/";
42221308Sache
42321308Sache	if (tTd(44, 4))
42475406Sache		sm_dprintf("safedirpath(%s, uid=%ld, gid=%ld, flags=%lx, level=%d, offset=%d):\n",
42521308Sache			fn, (long) uid, (long) gid, flags, level, offset);
42621308Sache
42721308Sache	if (!bitnset(DBS_GROUPWRITABLEDIRPATHSAFE, DontBlameSendmail))
42826497Sache		mode |= S_IWGRP;
42975406Sache
430136644Sache	/* Make a modifiable copy of the filename */
431119610Sache	if (sm_strlcpy(s, fn, sizeof s) >= sizeof s)
43221308Sache		return EINVAL;
433119610Sache
434119610Sache	p = s + offset;
43526497Sache	while (p != NULL)
436119610Sache	{
43726497Sache		/* put back character */
43875406Sache		if (saveptr != NULL)
43921308Sache		{
44030971Sache			*saveptr = save;
44121308Sache			saveptr = NULL;
44221308Sache			p++;
44321308Sache		}
44421308Sache
44521308Sache		if (*p == '\0')
446136644Sache			break;
447119610Sache
448119610Sache		p = strchr(p, '/');
449119610Sache
45021308Sache		/* Special case for root directory */
45121308Sache		if (p == s)
45221308Sache		{
45321308Sache			save = *(p + 1);
45421308Sache			saveptr = p + 1;
45521308Sache			*(p + 1) = '\0';
45621308Sache		}
45721308Sache		else if (p != NULL)
45821308Sache		{
45921308Sache			save = *p;
46021308Sache			saveptr = p;
46121308Sache			*p = '\0';
46221308Sache		}
46321308Sache
464136644Sache		/* Heuristic: . and .. have already been checked */
465136644Sache		enddir = strrchr(s, '/');
466136644Sache		if (enddir != NULL &&
467136644Sache		    (strcmp(enddir, "/..") == 0 ||
468136644Sache		     strcmp(enddir, "/.") == 0))
469136644Sache			continue;
470136644Sache
471136644Sache		if (tTd(44, 20))
472136644Sache			sm_dprintf("\t[dir %s]\n", s);
47321308Sache
47421308Sache# if HASLSTAT
475136644Sache		ret = lstat(s, &stbuf);
476119610Sache# else /* HASLSTAT */
477119610Sache		ret = stat(s, &stbuf);
478119610Sache# endif /* HASLSTAT */
479119610Sache		if (ret < 0)
480119610Sache		{
481119610Sache			ret = errno;
482119610Sache			break;
483119610Sache		}
484119610Sache
485119610Sache# ifdef S_ISLNK
486119610Sache		/* Follow symlinks */
487119610Sache		if (S_ISLNK(stbuf.st_mode))
488119610Sache		{
489119610Sache			int linklen;
490119610Sache			char *target;
491119610Sache			char buf[MAXPATHLEN];
492119610Sache			char fullbuf[MAXLINKPATHLEN];
493119610Sache
494119610Sache			memset(buf, '\0', sizeof buf);
495119610Sache			linklen = readlink(s, buf, sizeof buf);
496119610Sache			if (linklen < 0)
49721308Sache			{
49821308Sache				ret = errno;
49921308Sache				break;
500136644Sache			}
501136644Sache			if (linklen >= sizeof buf)
502136644Sache			{
503136644Sache				/* file name too long for buffer */
504136644Sache				ret = errno = EINVAL;
505136644Sache				break;
50621308Sache			}
50721308Sache
50821308Sache			offset = 0;
50921308Sache			if (*buf == '/')
51021308Sache			{
511136644Sache				target = buf;
512119610Sache
513119610Sache				/* If path is the same, avoid rechecks */
514119610Sache				while (s[offset] == buf[offset] &&
51575406Sache				       s[offset] != '\0')
51675406Sache					offset++;
51721308Sache
518119610Sache				if (s[offset] == '\0' && buf[offset] == '\0')
51921308Sache				{
52021308Sache					/* strings match, symlink loop */
52121308Sache					return ELOOP;
52221308Sache				}
52321308Sache
52421308Sache				/* back off from the mismatch */
52575406Sache				if (offset > 0)
52621308Sache					offset--;
52721308Sache
52821308Sache				/* Make sure we are at a directory break */
52921308Sache				if (offset > 0 &&
53021308Sache				    s[offset] != '/' &&
53121308Sache				    s[offset] != '\0')
53221308Sache				{
53375406Sache					while (buf[offset] != '/' &&
53421308Sache					       offset > 0)
53521308Sache						offset--;
53621308Sache				}
53721308Sache				if (offset > 0 &&
53821308Sache				    s[offset] == '/' &&
53921308Sache				    buf[offset] == '/')
54021308Sache				{
54121308Sache					/* Include the trailing slash */
54221308Sache					offset++;
54375406Sache				}
54421308Sache			}
54521308Sache			else
54621308Sache			{
547				char *sptr;
548
549				sptr = strrchr(s, '/');
550				if (sptr != NULL)
551				{
552					*sptr = '\0';
553					offset = sptr + 1 - s;
554					if (sm_strlcpyn(fullbuf,
555							sizeof fullbuf, 2,
556							s, "/") >=
557						sizeof fullbuf ||
558					    sm_strlcat(fullbuf, buf,
559						       sizeof fullbuf) >=
560						sizeof fullbuf)
561					{
562						ret = EINVAL;
563						break;
564					}
565					*sptr = '/';
566				}
567				else
568				{
569					if (sm_strlcpy(fullbuf, buf,
570						       sizeof fullbuf) >=
571						sizeof fullbuf)
572					{
573						ret = EINVAL;
574						break;
575					}
576				}
577				target = fullbuf;
578			}
579			ret = safedirpath(target, uid, gid, user, flags,
580					  level + 1, offset);
581			if (ret != 0)
582				break;
583
584			/* Don't check permissions on the link file itself */
585			continue;
586		}
587#endif /* S_ISLNK */
588
589		if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) &&
590#ifdef S_ISVTX
591		    !(bitnset(DBS_TRUSTSTICKYBIT, DontBlameSendmail) &&
592		      bitset(S_ISVTX, stbuf.st_mode)) &&
593#endif /* S_ISVTX */
594		    bitset(mode, stbuf.st_mode))
595		{
596			if (tTd(44, 4))
597				sm_dprintf("\t[dir %s] mode %lo ",
598					s, (unsigned long) stbuf.st_mode);
599			if (bitset(SFF_SAFEDIRPATH, flags))
600			{
601				if (bitset(S_IWOTH, stbuf.st_mode))
602					ret = E_SM_WWDIR;
603				else
604					ret = E_SM_GWDIR;
605				if (tTd(44, 4))
606					sm_dprintf("FATAL\n");
607				break;
608			}
609			if (tTd(44, 4))
610				sm_dprintf("WARNING\n");
611			if (Verbose > 1)
612				message("051 WARNING: %s writable directory %s",
613					bitset(S_IWOTH, stbuf.st_mode)
614					   ? "World"
615					   : "Group",
616					s);
617		}
618		if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags))
619		{
620			if (bitset(S_IXOTH, stbuf.st_mode))
621				continue;
622			ret = EACCES;
623			break;
624		}
625
626		/*
627		**  Let OS determine access to file if we are not
628		**  running as a privileged user.  This allows ACLs
629		**  to work.  Also, if opening as root, assume we can
630		**  scan the directory.
631		*/
632		if (geteuid() != 0 || bitset(SFF_OPENASROOT, flags))
633			continue;
634
635		if (stbuf.st_uid == uid &&
636		    bitset(S_IXUSR, stbuf.st_mode))
637			continue;
638		if (stbuf.st_gid == gid &&
639		    bitset(S_IXGRP, stbuf.st_mode))
640			continue;
641# ifndef NO_GROUP_SET
642		if (user != NULL && !DontInitGroups &&
643		    ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
644		     (gr = getgrgid(stbuf.st_gid)) != NULL))
645		{
646			register char **gp;
647
648			for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
649				if (strcmp(*gp, user) == 0)
650					break;
651			if (gp != NULL && *gp != NULL &&
652			    bitset(S_IXGRP, stbuf.st_mode))
653				continue;
654		}
655# endif /* ! NO_GROUP_SET */
656		if (!bitset(S_IXOTH, stbuf.st_mode))
657		{
658			ret = EACCES;
659			break;
660		}
661	}
662	if (tTd(44, 4))
663		sm_dprintf("\t[dir %s] %s\n", fn,
664			ret == 0 ? "OK" : sm_errstring(ret));
665	return ret;
666}
667/*
668**  SAFEOPEN -- do a file open with extra checking
669**
670**	Parameters:
671**		fn -- the file name to open.
672**		omode -- the open-style mode flags.
673**		cmode -- the create-style mode flags.
674**		sff -- safefile flags.
675**
676**	Returns:
677**		Same as open.
678*/
679
680int
681safeopen(fn, omode, cmode, sff)
682	char *fn;
683	int omode;
684	int cmode;
685	long sff;
686{
687#if !NOFTRUNCATE
688	bool truncate;
689#endif /* !NOFTRUNCATE */
690	int rval;
691	int fd;
692	int smode;
693	struct stat stb;
694
695	if (tTd(44, 10))
696		sm_dprintf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n",
697			   fn, omode, cmode, sff);
698
699	if (bitset(O_CREAT, omode))
700		sff |= SFF_CREAT;
701	omode &= ~O_CREAT;
702	switch (omode & O_ACCMODE)
703	{
704	  case O_RDONLY:
705		smode = S_IREAD;
706		break;
707
708	  case O_WRONLY:
709		smode = S_IWRITE;
710		break;
711
712	  case O_RDWR:
713		smode = S_IREAD|S_IWRITE;
714		break;
715
716	  default:
717		smode = 0;
718		break;
719	}
720	if (bitset(SFF_OPENASROOT, sff))
721		rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName,
722				sff, smode, &stb);
723	else
724		rval = safefile(fn, RealUid, RealGid, RealUserName,
725				sff, smode, &stb);
726	if (rval != 0)
727	{
728		errno = rval;
729		return -1;
730	}
731	if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff))
732		omode |= O_CREAT | (bitset(SFF_NOTEXCL, sff) ? 0 : O_EXCL);
733	else if (bitset(SFF_CREAT, sff) && bitset(O_EXCL, omode))
734	{
735		/* The file exists so an exclusive create would fail */
736		errno = EEXIST;
737		return -1;
738	}
739
740#if !NOFTRUNCATE
741	truncate = bitset(O_TRUNC, omode);
742	if (truncate)
743		omode &= ~O_TRUNC;
744#endif /* !NOFTRUNCATE */
745
746	fd = dfopen(fn, omode, cmode, sff);
747	if (fd < 0)
748		return fd;
749	if (filechanged(fn, fd, &stb))
750	{
751		syserr("554 5.3.0 cannot open: file %s changed after open", fn);
752		(void) close(fd);
753		errno = E_SM_FILECHANGE;
754		return -1;
755	}
756
757#if !NOFTRUNCATE
758	if (truncate &&
759	    ftruncate(fd, (off_t) 0) < 0)
760	{
761		int save_errno;
762
763		save_errno = errno;
764		syserr("554 5.3.0 cannot open: file %s could not be truncated",
765		       fn);
766		(void) close(fd);
767		errno = save_errno;
768		return -1;
769	}
770#endif /* !NOFTRUNCATE */
771
772	return fd;
773}
774/*
775**  SAFEFOPEN -- do a file open with extra checking
776**
777**	Parameters:
778**		fn -- the file name to open.
779**		omode -- the open-style mode flags.
780**		cmode -- the create-style mode flags.
781**		sff -- safefile flags.
782**
783**	Returns:
784**		Same as fopen.
785*/
786
787SM_FILE_T *
788safefopen(fn, omode, cmode, sff)
789	char *fn;
790	int omode;
791	int cmode;
792	long sff;
793{
794	int fd;
795	int save_errno;
796	SM_FILE_T *fp;
797	int fmode;
798
799	switch (omode & O_ACCMODE)
800	{
801	  case O_RDONLY:
802		fmode = SM_IO_RDONLY;
803		break;
804
805	  case O_WRONLY:
806		if (bitset(O_APPEND, omode))
807			fmode = SM_IO_APPEND;
808		else
809			fmode = SM_IO_WRONLY;
810		break;
811
812	  case O_RDWR:
813		if (bitset(O_TRUNC, omode))
814			fmode = SM_IO_RDWRTR;
815		else if (bitset(O_APPEND, omode))
816			fmode = SM_IO_APPENDRW;
817		else
818			fmode = SM_IO_RDWR;
819		break;
820
821	  default:
822		syserr("554 5.3.5 safefopen: unknown omode %o", omode);
823		fmode = 0;
824	}
825	fd = safeopen(fn, omode, cmode, sff);
826	if (fd < 0)
827	{
828		save_errno = errno;
829		if (tTd(44, 10))
830			sm_dprintf("safefopen: safeopen failed: %s\n",
831				   sm_errstring(errno));
832		errno = save_errno;
833		return NULL;
834	}
835	fp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
836			(void *) &fd, fmode, NULL);
837	if (fp != NULL)
838		return fp;
839
840	save_errno = errno;
841	if (tTd(44, 10))
842	{
843		sm_dprintf("safefopen: fdopen(%s, %d) failed: omode=%x, sff=%lx, err=%s\n",
844			   fn, fmode, omode, sff, sm_errstring(errno));
845	}
846	(void) close(fd);
847	errno = save_errno;
848	return NULL;
849}
850/*
851**  FILECHANGED -- check to see if file changed after being opened
852**
853**	Parameters:
854**		fn -- pathname of file to check.
855**		fd -- file descriptor to check.
856**		stb -- stat structure from before open.
857**
858**	Returns:
859**		true -- if a problem was detected.
860**		false -- if this file is still the same.
861*/
862
863bool
864filechanged(fn, fd, stb)
865	char *fn;
866	int fd;
867	struct stat *stb;
868{
869	struct stat sta;
870
871	if (stb->st_mode == ST_MODE_NOFILE)
872	{
873# if HASLSTAT && BOGUS_O_EXCL
874		/* only necessary if exclusive open follows symbolic links */
875		if (lstat(fn, stb) < 0 || stb->st_nlink != 1)
876			return true;
877# else /* HASLSTAT && BOGUS_O_EXCL */
878		return false;
879# endif /* HASLSTAT && BOGUS_O_EXCL */
880	}
881	if (fstat(fd, &sta) < 0)
882		return true;
883
884	if (sta.st_nlink != stb->st_nlink ||
885	    sta.st_dev != stb->st_dev ||
886	    sta.st_ino != stb->st_ino ||
887# if HAS_ST_GEN && 0		/* AFS returns garbage in st_gen */
888	    sta.st_gen != stb->st_gen ||
889# endif /* HAS_ST_GEN && 0 */
890	    sta.st_uid != stb->st_uid ||
891	    sta.st_gid != stb->st_gid)
892	{
893		if (tTd(44, 8))
894		{
895			sm_dprintf("File changed after opening:\n");
896			sm_dprintf(" nlink	= %ld/%ld\n",
897				(long) stb->st_nlink, (long) sta.st_nlink);
898			sm_dprintf(" dev	= %ld/%ld\n",
899				(long) stb->st_dev, (long) sta.st_dev);
900			sm_dprintf(" ino	= %llu/%llu\n",
901				(ULONGLONG_T) stb->st_ino,
902				(ULONGLONG_T) sta.st_ino);
903# if HAS_ST_GEN
904			sm_dprintf(" gen	= %ld/%ld\n",
905				(long) stb->st_gen, (long) sta.st_gen);
906# endif /* HAS_ST_GEN */
907			sm_dprintf(" uid	= %ld/%ld\n",
908				(long) stb->st_uid, (long) sta.st_uid);
909			sm_dprintf(" gid	= %ld/%ld\n",
910				(long) stb->st_gid, (long) sta.st_gid);
911		}
912		return true;
913	}
914
915	return false;
916}
917/*
918**  DFOPEN -- determined file open
919**
920**	This routine has the semantics of open, except that it will
921**	keep trying a few times to make this happen.  The idea is that
922**	on very loaded systems, we may run out of resources (inodes,
923**	whatever), so this tries to get around it.
924*/
925
926int
927dfopen(filename, omode, cmode, sff)
928	char *filename;
929	int omode;
930	int cmode;
931	long sff;
932{
933	register int tries;
934	int fd = -1;
935	struct stat st;
936
937	for (tries = 0; tries < 10; tries++)
938	{
939		(void) sleep((unsigned) (10 * tries));
940		errno = 0;
941		fd = open(filename, omode, cmode);
942		if (fd >= 0)
943			break;
944		switch (errno)
945		{
946		  case ENFILE:		/* system file table full */
947		  case EINTR:		/* interrupted syscall */
948#ifdef ETXTBSY
949		  case ETXTBSY:		/* Apollo: net file locked */
950#endif /* ETXTBSY */
951			continue;
952		}
953		break;
954	}
955	if (!bitset(SFF_NOLOCK, sff) &&
956	    fd >= 0 &&
957	    fstat(fd, &st) >= 0 &&
958	    S_ISREG(st.st_mode))
959	{
960		int locktype;
961
962		/* lock the file to avoid accidental conflicts */
963		if ((omode & O_ACCMODE) != O_RDONLY)
964			locktype = LOCK_EX;
965		else
966			locktype = LOCK_SH;
967		if (bitset(SFF_NBLOCK, sff))
968			locktype |= LOCK_NB;
969
970		if (!lockfile(fd, filename, NULL, locktype))
971		{
972			int save_errno = errno;
973
974			(void) close(fd);
975			fd = -1;
976			errno = save_errno;
977		}
978		else
979			errno = 0;
980	}
981	return fd;
982}
983