xref: /freebsd/contrib/sendmail/libsmutil/safefile.c (revision 2fb4f839f3fc72ce2bab12f9ba4760f97f73e97f)
106f25ae9SGregory Neil Shapiro /*
25dd76dd0SGregory Neil Shapiro  * Copyright (c) 1998-2004 Proofpoint, Inc. and its suppliers.
306f25ae9SGregory Neil Shapiro  *	All rights reserved.
406f25ae9SGregory Neil Shapiro  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
506f25ae9SGregory Neil Shapiro  * Copyright (c) 1988, 1993
606f25ae9SGregory Neil Shapiro  *	The Regents of the University of California.  All rights reserved.
706f25ae9SGregory Neil Shapiro  *
806f25ae9SGregory Neil Shapiro  * By using this file, you agree to the terms and conditions set
906f25ae9SGregory Neil Shapiro  * forth in the LICENSE file which can be found at the top level of
1006f25ae9SGregory Neil Shapiro  * the sendmail distribution.
1106f25ae9SGregory Neil Shapiro  *
1206f25ae9SGregory Neil Shapiro  */
1306f25ae9SGregory Neil Shapiro 
1406f25ae9SGregory Neil Shapiro #include <sendmail.h>
1540266059SGregory Neil Shapiro #include <sm/io.h>
1640266059SGregory Neil Shapiro #include <sm/errstring.h>
1740266059SGregory Neil Shapiro 
184313cc83SGregory Neil Shapiro SM_RCSID("@(#)$Id: safefile.c,v 8.130 2013-11-22 20:51:50 ca Exp $")
1906f25ae9SGregory Neil Shapiro 
2006f25ae9SGregory Neil Shapiro 
2140266059SGregory Neil Shapiro /*
2206f25ae9SGregory Neil Shapiro **  SAFEFILE -- return 0 if a file exists and is safe for a user.
2306f25ae9SGregory Neil Shapiro **
2406f25ae9SGregory Neil Shapiro **	Parameters:
2506f25ae9SGregory Neil Shapiro **		fn -- filename to check.
2606f25ae9SGregory Neil Shapiro **		uid -- user id to compare against.
2706f25ae9SGregory Neil Shapiro **		gid -- group id to compare against.
28*5b0945b5SGregory Neil Shapiro **		user -- user name to compare against (used for group sets).
2906f25ae9SGregory Neil Shapiro **		flags -- modifiers:
3006f25ae9SGregory Neil Shapiro **			SFF_MUSTOWN -- "uid" must own this file.
3106f25ae9SGregory Neil Shapiro **			SFF_NOSLINK -- file cannot be a symbolic link.
3206f25ae9SGregory Neil Shapiro **		mode -- mode bits that must match.
3306f25ae9SGregory Neil Shapiro **		st -- if set, points to a stat structure that will
3406f25ae9SGregory Neil Shapiro **			get the stat info for the file.
3506f25ae9SGregory Neil Shapiro **
3606f25ae9SGregory Neil Shapiro **	Returns:
3706f25ae9SGregory Neil Shapiro **		0 if fn exists, is owned by uid, and matches mode.
3806f25ae9SGregory Neil Shapiro **		An errno otherwise.  The actual errno is cleared.
3906f25ae9SGregory Neil Shapiro **
4006f25ae9SGregory Neil Shapiro **	Side Effects:
4106f25ae9SGregory Neil Shapiro **		none.
4206f25ae9SGregory Neil Shapiro */
4306f25ae9SGregory Neil Shapiro 
4406f25ae9SGregory Neil Shapiro int
4506f25ae9SGregory Neil Shapiro safefile(fn, uid, gid, user, flags, mode, st)
4606f25ae9SGregory Neil Shapiro 	char *fn;
4706f25ae9SGregory Neil Shapiro 	UID_T uid;
4806f25ae9SGregory Neil Shapiro 	GID_T gid;
4906f25ae9SGregory Neil Shapiro 	char *user;
5006f25ae9SGregory Neil Shapiro 	long flags;
5106f25ae9SGregory Neil Shapiro 	int mode;
5206f25ae9SGregory Neil Shapiro 	struct stat *st;
5306f25ae9SGregory Neil Shapiro {
5406f25ae9SGregory Neil Shapiro 	register char *p;
5506f25ae9SGregory Neil Shapiro 	register struct group *gr = NULL;
5606f25ae9SGregory Neil Shapiro 	int file_errno = 0;
5706f25ae9SGregory Neil Shapiro 	bool checkpath;
5806f25ae9SGregory Neil Shapiro 	struct stat stbuf;
5906f25ae9SGregory Neil Shapiro 	struct stat fstbuf;
6094c01205SGregory Neil Shapiro 	char fbuf[MAXPATHLEN];
6106f25ae9SGregory Neil Shapiro 
6206f25ae9SGregory Neil Shapiro 	if (tTd(44, 4))
6340266059SGregory Neil Shapiro 		sm_dprintf("safefile(%s, uid=%d, gid=%d, flags=%lx, mode=%o):\n",
6406f25ae9SGregory Neil Shapiro 			fn, (int) uid, (int) gid, flags, mode);
6506f25ae9SGregory Neil Shapiro 	errno = 0;
6640266059SGregory Neil Shapiro 	if (sm_strlcpy(fbuf, fn, sizeof fbuf) >= sizeof fbuf)
6706f25ae9SGregory Neil Shapiro 	{
6806f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
6940266059SGregory Neil Shapiro 			sm_dprintf("\tpathname too long\n");
7006f25ae9SGregory Neil Shapiro 		return ENAMETOOLONG;
7106f25ae9SGregory Neil Shapiro 	}
7206f25ae9SGregory Neil Shapiro 	fn = fbuf;
7340266059SGregory Neil Shapiro 	if (st == NULL)
7440266059SGregory Neil Shapiro 		st = &fstbuf;
7506f25ae9SGregory Neil Shapiro 
7606f25ae9SGregory Neil Shapiro 	/* ignore SFF_SAFEDIRPATH if we are debugging */
7706f25ae9SGregory Neil Shapiro 	if (RealUid != 0 && RunAsUid == RealUid)
7806f25ae9SGregory Neil Shapiro 		flags &= ~SFF_SAFEDIRPATH;
7906f25ae9SGregory Neil Shapiro 
8006f25ae9SGregory Neil Shapiro 	/* first check to see if the file exists at all */
8106f25ae9SGregory Neil Shapiro #if HASLSTAT
8206f25ae9SGregory Neil Shapiro 	if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st)
8306f25ae9SGregory Neil Shapiro 					: stat(fn, st)) < 0)
84*5b0945b5SGregory Neil Shapiro #else
8506f25ae9SGregory Neil Shapiro 	if (stat(fn, st) < 0)
86*5b0945b5SGregory Neil Shapiro #endif
8706f25ae9SGregory Neil Shapiro 	{
8806f25ae9SGregory Neil Shapiro 		file_errno = errno;
8906f25ae9SGregory Neil Shapiro 	}
9006f25ae9SGregory Neil Shapiro 	else if (bitset(SFF_SETUIDOK, flags) &&
9106f25ae9SGregory Neil Shapiro 		 !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) &&
9206f25ae9SGregory Neil Shapiro 		 S_ISREG(st->st_mode))
9306f25ae9SGregory Neil Shapiro 	{
9406f25ae9SGregory Neil Shapiro 		/*
9540266059SGregory Neil Shapiro 		**  If final file is set-user-ID, run as the owner of that
9606f25ae9SGregory Neil Shapiro 		**  file.  Gotta be careful not to reveal anything too
9706f25ae9SGregory Neil Shapiro 		**  soon here!
9806f25ae9SGregory Neil Shapiro 		*/
9906f25ae9SGregory Neil Shapiro 
10006f25ae9SGregory Neil Shapiro #ifdef SUID_ROOT_FILES_OK
10106f25ae9SGregory Neil Shapiro 		if (bitset(S_ISUID, st->st_mode))
102*5b0945b5SGregory Neil Shapiro #else
10306f25ae9SGregory Neil Shapiro 		if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 &&
10406f25ae9SGregory Neil Shapiro 		    st->st_uid != TrustedUid)
105*5b0945b5SGregory Neil Shapiro #endif
10606f25ae9SGregory Neil Shapiro 		{
10706f25ae9SGregory Neil Shapiro 			uid = st->st_uid;
10806f25ae9SGregory Neil Shapiro 			user = NULL;
10906f25ae9SGregory Neil Shapiro 		}
11006f25ae9SGregory Neil Shapiro #ifdef SUID_ROOT_FILES_OK
11106f25ae9SGregory Neil Shapiro 		if (bitset(S_ISGID, st->st_mode))
112*5b0945b5SGregory Neil Shapiro #else
11306f25ae9SGregory Neil Shapiro 		if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0)
114*5b0945b5SGregory Neil Shapiro #endif
11506f25ae9SGregory Neil Shapiro 			gid = st->st_gid;
11606f25ae9SGregory Neil Shapiro 	}
11706f25ae9SGregory Neil Shapiro 
11806f25ae9SGregory Neil Shapiro 	checkpath = !bitset(SFF_NOPATHCHECK, flags) ||
11906f25ae9SGregory Neil Shapiro 		    (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags));
12006f25ae9SGregory Neil Shapiro 	if (bitset(SFF_NOWLINK, flags) && !bitset(SFF_SAFEDIRPATH, flags))
12106f25ae9SGregory Neil Shapiro 	{
12206f25ae9SGregory Neil Shapiro 		int ret;
12306f25ae9SGregory Neil Shapiro 
12406f25ae9SGregory Neil Shapiro 		/* check the directory */
12506f25ae9SGregory Neil Shapiro 		p = strrchr(fn, '/');
12606f25ae9SGregory Neil Shapiro 		if (p == NULL)
12706f25ae9SGregory Neil Shapiro 		{
12806f25ae9SGregory Neil Shapiro 			ret = safedirpath(".", uid, gid, user,
12906f25ae9SGregory Neil Shapiro 					  flags|SFF_SAFEDIRPATH, 0, 0);
13006f25ae9SGregory Neil Shapiro 		}
13106f25ae9SGregory Neil Shapiro 		else
13206f25ae9SGregory Neil Shapiro 		{
13306f25ae9SGregory Neil Shapiro 			*p = '\0';
13406f25ae9SGregory Neil Shapiro 			ret = safedirpath(fn, uid, gid, user,
13506f25ae9SGregory Neil Shapiro 					  flags|SFF_SAFEDIRPATH, 0, 0);
13606f25ae9SGregory Neil Shapiro 			*p = '/';
13706f25ae9SGregory Neil Shapiro 		}
13806f25ae9SGregory Neil Shapiro 		if (ret == 0)
13906f25ae9SGregory Neil Shapiro 		{
14006f25ae9SGregory Neil Shapiro 			/* directory is safe */
14140266059SGregory Neil Shapiro 			checkpath = false;
14206f25ae9SGregory Neil Shapiro 		}
14306f25ae9SGregory Neil Shapiro 		else
14406f25ae9SGregory Neil Shapiro 		{
14506f25ae9SGregory Neil Shapiro #if HASLSTAT
14606f25ae9SGregory Neil Shapiro 			/* Need lstat() information if called stat() before */
14706f25ae9SGregory Neil Shapiro 			if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0)
14806f25ae9SGregory Neil Shapiro 			{
14906f25ae9SGregory Neil Shapiro 				ret = errno;
15006f25ae9SGregory Neil Shapiro 				if (tTd(44, 4))
15140266059SGregory Neil Shapiro 					sm_dprintf("\t%s\n", sm_errstring(ret));
15206f25ae9SGregory Neil Shapiro 				return ret;
15306f25ae9SGregory Neil Shapiro 			}
15406f25ae9SGregory Neil Shapiro #endif /* HASLSTAT */
15506f25ae9SGregory Neil Shapiro 			/* directory is writable: disallow links */
15606f25ae9SGregory Neil Shapiro 			flags |= SFF_NOLINK;
15706f25ae9SGregory Neil Shapiro 		}
15806f25ae9SGregory Neil Shapiro 	}
15906f25ae9SGregory Neil Shapiro 
16006f25ae9SGregory Neil Shapiro 	if (checkpath)
16106f25ae9SGregory Neil Shapiro 	{
16206f25ae9SGregory Neil Shapiro 		int ret;
16306f25ae9SGregory Neil Shapiro 
16406f25ae9SGregory Neil Shapiro 		p = strrchr(fn, '/');
16506f25ae9SGregory Neil Shapiro 		if (p == NULL)
16606f25ae9SGregory Neil Shapiro 		{
16706f25ae9SGregory Neil Shapiro 			ret = safedirpath(".", uid, gid, user, flags, 0, 0);
16806f25ae9SGregory Neil Shapiro 		}
16906f25ae9SGregory Neil Shapiro 		else
17006f25ae9SGregory Neil Shapiro 		{
17106f25ae9SGregory Neil Shapiro 			*p = '\0';
17206f25ae9SGregory Neil Shapiro 			ret = safedirpath(fn, uid, gid, user, flags, 0, 0);
17306f25ae9SGregory Neil Shapiro 			*p = '/';
17406f25ae9SGregory Neil Shapiro 		}
17506f25ae9SGregory Neil Shapiro 		if (ret != 0)
17606f25ae9SGregory Neil Shapiro 			return ret;
17706f25ae9SGregory Neil Shapiro 	}
17806f25ae9SGregory Neil Shapiro 
17906f25ae9SGregory Neil Shapiro 	/*
18006f25ae9SGregory Neil Shapiro 	**  If the target file doesn't exist, check the directory to
18106f25ae9SGregory Neil Shapiro 	**  ensure that it is writable by this user.
18206f25ae9SGregory Neil Shapiro 	*/
18306f25ae9SGregory Neil Shapiro 
18406f25ae9SGregory Neil Shapiro 	if (file_errno != 0)
18506f25ae9SGregory Neil Shapiro 	{
18606f25ae9SGregory Neil Shapiro 		int ret = file_errno;
18706f25ae9SGregory Neil Shapiro 		char *dir = fn;
18806f25ae9SGregory Neil Shapiro 
18906f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
19040266059SGregory Neil Shapiro 			sm_dprintf("\t%s\n", sm_errstring(ret));
19106f25ae9SGregory Neil Shapiro 
19206f25ae9SGregory Neil Shapiro 		errno = 0;
19306f25ae9SGregory Neil Shapiro 		if (!bitset(SFF_CREAT, flags) || file_errno != ENOENT)
19406f25ae9SGregory Neil Shapiro 			return ret;
19506f25ae9SGregory Neil Shapiro 
19606f25ae9SGregory Neil Shapiro 		/* check to see if legal to create the file */
19706f25ae9SGregory Neil Shapiro 		p = strrchr(dir, '/');
19806f25ae9SGregory Neil Shapiro 		if (p == NULL)
19906f25ae9SGregory Neil Shapiro 			dir = ".";
20006f25ae9SGregory Neil Shapiro 		else if (p == dir)
20106f25ae9SGregory Neil Shapiro 			dir = "/";
20206f25ae9SGregory Neil Shapiro 		else
20306f25ae9SGregory Neil Shapiro 			*p = '\0';
20406f25ae9SGregory Neil Shapiro 		if (stat(dir, &stbuf) >= 0)
20506f25ae9SGregory Neil Shapiro 		{
20606f25ae9SGregory Neil Shapiro 			int md = S_IWRITE|S_IEXEC;
20706f25ae9SGregory Neil Shapiro 
20840266059SGregory Neil Shapiro 			ret = 0;
20906f25ae9SGregory Neil Shapiro 			if (stbuf.st_uid == uid)
21006f25ae9SGregory Neil Shapiro 				/* EMPTY */
21106f25ae9SGregory Neil Shapiro 				;
21206f25ae9SGregory Neil Shapiro 			else if (uid == 0 && stbuf.st_uid == TrustedUid)
21306f25ae9SGregory Neil Shapiro 				/* EMPTY */
21406f25ae9SGregory Neil Shapiro 				;
21506f25ae9SGregory Neil Shapiro 			else
21606f25ae9SGregory Neil Shapiro 			{
21706f25ae9SGregory Neil Shapiro 				md >>= 3;
21806f25ae9SGregory Neil Shapiro 				if (stbuf.st_gid == gid)
21906f25ae9SGregory Neil Shapiro 					/* EMPTY */
22006f25ae9SGregory Neil Shapiro 					;
22106f25ae9SGregory Neil Shapiro #ifndef NO_GROUP_SET
22206f25ae9SGregory Neil Shapiro 				else if (user != NULL && !DontInitGroups &&
22306f25ae9SGregory Neil Shapiro 					 ((gr != NULL &&
22406f25ae9SGregory Neil Shapiro 					   gr->gr_gid == stbuf.st_gid) ||
22506f25ae9SGregory Neil Shapiro 					  (gr = getgrgid(stbuf.st_gid)) != NULL))
22606f25ae9SGregory Neil Shapiro 				{
22706f25ae9SGregory Neil Shapiro 					register char **gp;
22806f25ae9SGregory Neil Shapiro 
22906f25ae9SGregory Neil Shapiro 					for (gp = gr->gr_mem; *gp != NULL; gp++)
23006f25ae9SGregory Neil Shapiro 						if (strcmp(*gp, user) == 0)
23106f25ae9SGregory Neil Shapiro 							break;
23206f25ae9SGregory Neil Shapiro 					if (*gp == NULL)
23306f25ae9SGregory Neil Shapiro 						md >>= 3;
23406f25ae9SGregory Neil Shapiro 				}
23506f25ae9SGregory Neil Shapiro #endif /* ! NO_GROUP_SET */
23606f25ae9SGregory Neil Shapiro 				else
23706f25ae9SGregory Neil Shapiro 					md >>= 3;
23806f25ae9SGregory Neil Shapiro 			}
23906f25ae9SGregory Neil Shapiro 			if ((stbuf.st_mode & md) != md)
24040266059SGregory Neil Shapiro 				ret = errno = EACCES;
24106f25ae9SGregory Neil Shapiro 		}
24240266059SGregory Neil Shapiro 		else
24306f25ae9SGregory Neil Shapiro 			ret = errno;
24406f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
24540266059SGregory Neil Shapiro 			sm_dprintf("\t[final dir %s uid %d mode %lo] %s\n",
24640266059SGregory Neil Shapiro 				dir, (int) stbuf.st_uid,
24740266059SGregory Neil Shapiro 				(unsigned long) stbuf.st_mode,
24840266059SGregory Neil Shapiro 				sm_errstring(ret));
24906f25ae9SGregory Neil Shapiro 		if (p != NULL)
25006f25ae9SGregory Neil Shapiro 			*p = '/';
25106f25ae9SGregory Neil Shapiro 		st->st_mode = ST_MODE_NOFILE;
25206f25ae9SGregory Neil Shapiro 		return ret;
25306f25ae9SGregory Neil Shapiro 	}
25406f25ae9SGregory Neil Shapiro 
25506f25ae9SGregory Neil Shapiro #ifdef S_ISLNK
25606f25ae9SGregory Neil Shapiro 	if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode))
25706f25ae9SGregory Neil Shapiro 	{
25806f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
25940266059SGregory Neil Shapiro 			sm_dprintf("\t[slink mode %lo]\tE_SM_NOSLINK\n",
26040266059SGregory Neil Shapiro 				(unsigned long) st->st_mode);
26106f25ae9SGregory Neil Shapiro 		return E_SM_NOSLINK;
26206f25ae9SGregory Neil Shapiro 	}
26306f25ae9SGregory Neil Shapiro #endif /* S_ISLNK */
26406f25ae9SGregory Neil Shapiro 	if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode))
26506f25ae9SGregory Neil Shapiro 	{
26606f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
26740266059SGregory Neil Shapiro 			sm_dprintf("\t[non-reg mode %lo]\tE_SM_REGONLY\n",
26840266059SGregory Neil Shapiro 				(unsigned long) st->st_mode);
26906f25ae9SGregory Neil Shapiro 		return E_SM_REGONLY;
27006f25ae9SGregory Neil Shapiro 	}
27106f25ae9SGregory Neil Shapiro 	if (bitset(SFF_NOGWFILES, flags) &&
27206f25ae9SGregory Neil Shapiro 	    bitset(S_IWGRP, st->st_mode))
27306f25ae9SGregory Neil Shapiro 	{
27406f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
27540266059SGregory Neil Shapiro 			sm_dprintf("\t[write bits %lo]\tE_SM_GWFILE\n",
27640266059SGregory Neil Shapiro 				(unsigned long) st->st_mode);
27706f25ae9SGregory Neil Shapiro 		return E_SM_GWFILE;
27806f25ae9SGregory Neil Shapiro 	}
27906f25ae9SGregory Neil Shapiro 	if (bitset(SFF_NOWWFILES, flags) &&
28006f25ae9SGregory Neil Shapiro 	    bitset(S_IWOTH, st->st_mode))
28106f25ae9SGregory Neil Shapiro 	{
28206f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
28340266059SGregory Neil Shapiro 			sm_dprintf("\t[write bits %lo]\tE_SM_WWFILE\n",
28440266059SGregory Neil Shapiro 				(unsigned long) st->st_mode);
28506f25ae9SGregory Neil Shapiro 		return E_SM_WWFILE;
28606f25ae9SGregory Neil Shapiro 	}
28706f25ae9SGregory Neil Shapiro 	if (bitset(SFF_NOGRFILES, flags) && bitset(S_IRGRP, st->st_mode))
28806f25ae9SGregory Neil Shapiro 	{
28906f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
29040266059SGregory Neil Shapiro 			sm_dprintf("\t[read bits %lo]\tE_SM_GRFILE\n",
29140266059SGregory Neil Shapiro 				(unsigned long) st->st_mode);
29206f25ae9SGregory Neil Shapiro 		return E_SM_GRFILE;
29306f25ae9SGregory Neil Shapiro 	}
29406f25ae9SGregory Neil Shapiro 	if (bitset(SFF_NOWRFILES, flags) && bitset(S_IROTH, st->st_mode))
29506f25ae9SGregory Neil Shapiro 	{
29606f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
29740266059SGregory Neil Shapiro 			sm_dprintf("\t[read bits %lo]\tE_SM_WRFILE\n",
29840266059SGregory Neil Shapiro 				(unsigned long) st->st_mode);
29906f25ae9SGregory Neil Shapiro 		return E_SM_WRFILE;
30006f25ae9SGregory Neil Shapiro 	}
30106f25ae9SGregory Neil Shapiro 	if (!bitset(SFF_EXECOK, flags) &&
30206f25ae9SGregory Neil Shapiro 	    bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) &&
30306f25ae9SGregory Neil Shapiro 	    bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode))
30406f25ae9SGregory Neil Shapiro 	{
30506f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
306e92d3f3fSGregory Neil Shapiro 			sm_dprintf("\t[exec bits %lo]\tE_SM_ISEXEC\n",
30740266059SGregory Neil Shapiro 				(unsigned long) st->st_mode);
30806f25ae9SGregory Neil Shapiro 		return E_SM_ISEXEC;
30906f25ae9SGregory Neil Shapiro 	}
31006f25ae9SGregory Neil Shapiro 	if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1)
31106f25ae9SGregory Neil Shapiro 	{
31206f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
31340266059SGregory Neil Shapiro 			sm_dprintf("\t[link count %d]\tE_SM_NOHLINK\n",
31406f25ae9SGregory Neil Shapiro 				(int) st->st_nlink);
31506f25ae9SGregory Neil Shapiro 		return E_SM_NOHLINK;
31606f25ae9SGregory Neil Shapiro 	}
31706f25ae9SGregory Neil Shapiro 
31806f25ae9SGregory Neil Shapiro 	if (uid == 0 && bitset(SFF_OPENASROOT, flags))
31906f25ae9SGregory Neil Shapiro 		/* EMPTY */
32006f25ae9SGregory Neil Shapiro 		;
32106f25ae9SGregory Neil Shapiro 	else if (uid == 0 && !bitset(SFF_ROOTOK, flags))
32206f25ae9SGregory Neil Shapiro 		mode >>= 6;
32306f25ae9SGregory Neil Shapiro 	else if (st->st_uid == uid)
32406f25ae9SGregory Neil Shapiro 		/* EMPTY */
32506f25ae9SGregory Neil Shapiro 		;
32606f25ae9SGregory Neil Shapiro 	else if (uid == 0 && st->st_uid == TrustedUid)
32706f25ae9SGregory Neil Shapiro 		/* EMPTY */
32806f25ae9SGregory Neil Shapiro 		;
32906f25ae9SGregory Neil Shapiro 	else
33006f25ae9SGregory Neil Shapiro 	{
33106f25ae9SGregory Neil Shapiro 		mode >>= 3;
33206f25ae9SGregory Neil Shapiro 		if (st->st_gid == gid)
33306f25ae9SGregory Neil Shapiro 			/* EMPTY */
33406f25ae9SGregory Neil Shapiro 			;
33506f25ae9SGregory Neil Shapiro #ifndef NO_GROUP_SET
33606f25ae9SGregory Neil Shapiro 		else if (user != NULL && !DontInitGroups &&
33706f25ae9SGregory Neil Shapiro 			 ((gr != NULL && gr->gr_gid == st->st_gid) ||
33806f25ae9SGregory Neil Shapiro 			  (gr = getgrgid(st->st_gid)) != NULL))
33906f25ae9SGregory Neil Shapiro 		{
34006f25ae9SGregory Neil Shapiro 			register char **gp;
34106f25ae9SGregory Neil Shapiro 
34206f25ae9SGregory Neil Shapiro 			for (gp = gr->gr_mem; *gp != NULL; gp++)
34306f25ae9SGregory Neil Shapiro 				if (strcmp(*gp, user) == 0)
34406f25ae9SGregory Neil Shapiro 					break;
34506f25ae9SGregory Neil Shapiro 			if (*gp == NULL)
34606f25ae9SGregory Neil Shapiro 				mode >>= 3;
34706f25ae9SGregory Neil Shapiro 		}
34806f25ae9SGregory Neil Shapiro #endif /* ! NO_GROUP_SET */
34906f25ae9SGregory Neil Shapiro 		else
35006f25ae9SGregory Neil Shapiro 			mode >>= 3;
35106f25ae9SGregory Neil Shapiro 	}
35206f25ae9SGregory Neil Shapiro 	if (tTd(44, 4))
35340266059SGregory Neil Shapiro 		sm_dprintf("\t[uid %d, nlink %d, stat %lo, mode %lo] ",
35406f25ae9SGregory Neil Shapiro 			(int) st->st_uid, (int) st->st_nlink,
35540266059SGregory Neil Shapiro 			(unsigned long) st->st_mode, (unsigned long) mode);
35606f25ae9SGregory Neil Shapiro 	if ((st->st_uid == uid || st->st_uid == 0 ||
35706f25ae9SGregory Neil Shapiro 	     st->st_uid == TrustedUid ||
35806f25ae9SGregory Neil Shapiro 	     !bitset(SFF_MUSTOWN, flags)) &&
35906f25ae9SGregory Neil Shapiro 	    (st->st_mode & mode) == mode)
36006f25ae9SGregory Neil Shapiro 	{
36106f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
36240266059SGregory Neil Shapiro 			sm_dprintf("\tOK\n");
36306f25ae9SGregory Neil Shapiro 		return 0;
36406f25ae9SGregory Neil Shapiro 	}
36506f25ae9SGregory Neil Shapiro 	if (tTd(44, 4))
36640266059SGregory Neil Shapiro 		sm_dprintf("\tEACCES\n");
36706f25ae9SGregory Neil Shapiro 	return EACCES;
36806f25ae9SGregory Neil Shapiro }
36940266059SGregory Neil Shapiro /*
37006f25ae9SGregory Neil Shapiro **  SAFEDIRPATH -- check to make sure a path to a directory is safe
37106f25ae9SGregory Neil Shapiro **
37206f25ae9SGregory Neil Shapiro **	Safe means not writable and owned by the right folks.
37306f25ae9SGregory Neil Shapiro **
37406f25ae9SGregory Neil Shapiro **	Parameters:
37506f25ae9SGregory Neil Shapiro **		fn -- filename to check.
37606f25ae9SGregory Neil Shapiro **		uid -- user id to compare against.
37706f25ae9SGregory Neil Shapiro **		gid -- group id to compare against.
37806f25ae9SGregory Neil Shapiro **		user -- user name to compare against (used for group
37906f25ae9SGregory Neil Shapiro **			sets).
38006f25ae9SGregory Neil Shapiro **		flags -- modifiers:
38106f25ae9SGregory Neil Shapiro **			SFF_ROOTOK -- ok to use root permissions to open.
38206f25ae9SGregory Neil Shapiro **			SFF_SAFEDIRPATH -- writable directories are considered
38306f25ae9SGregory Neil Shapiro **				to be fatal errors.
38406f25ae9SGregory Neil Shapiro **		level -- symlink recursive level.
38506f25ae9SGregory Neil Shapiro **		offset -- offset into fn to start checking from.
38606f25ae9SGregory Neil Shapiro **
38706f25ae9SGregory Neil Shapiro **	Returns:
38806f25ae9SGregory Neil Shapiro **		0 -- if the directory path is "safe".
38906f25ae9SGregory Neil Shapiro **		else -- an error number associated with the path.
39006f25ae9SGregory Neil Shapiro */
39106f25ae9SGregory Neil Shapiro 
39206f25ae9SGregory Neil Shapiro int
safedirpath(fn,uid,gid,user,flags,level,offset)39306f25ae9SGregory Neil Shapiro safedirpath(fn, uid, gid, user, flags, level, offset)
39406f25ae9SGregory Neil Shapiro 	char *fn;
39506f25ae9SGregory Neil Shapiro 	UID_T uid;
39606f25ae9SGregory Neil Shapiro 	GID_T gid;
39706f25ae9SGregory Neil Shapiro 	char *user;
39806f25ae9SGregory Neil Shapiro 	long flags;
39906f25ae9SGregory Neil Shapiro 	int level;
40006f25ae9SGregory Neil Shapiro 	int offset;
40106f25ae9SGregory Neil Shapiro {
40206f25ae9SGregory Neil Shapiro 	int ret = 0;
40306f25ae9SGregory Neil Shapiro 	int mode = S_IWOTH;
40406f25ae9SGregory Neil Shapiro 	char save = '\0';
40506f25ae9SGregory Neil Shapiro 	char *saveptr = NULL;
40606f25ae9SGregory Neil Shapiro 	char *p, *enddir;
40706f25ae9SGregory Neil Shapiro 	register struct group *gr = NULL;
40894c01205SGregory Neil Shapiro 	char s[MAXLINKPATHLEN];
40906f25ae9SGregory Neil Shapiro 	struct stat stbuf;
41006f25ae9SGregory Neil Shapiro 
41106f25ae9SGregory Neil Shapiro 	/* make sure we aren't in a symlink loop */
41206f25ae9SGregory Neil Shapiro 	if (level > MAXSYMLINKS)
41306f25ae9SGregory Neil Shapiro 		return ELOOP;
41406f25ae9SGregory Neil Shapiro 
41540266059SGregory Neil Shapiro 	if (level < 0 || offset < 0 || offset > strlen(fn))
41640266059SGregory Neil Shapiro 		return EINVAL;
41740266059SGregory Neil Shapiro 
41806f25ae9SGregory Neil Shapiro 	/* special case root directory */
41906f25ae9SGregory Neil Shapiro 	if (*fn == '\0')
42006f25ae9SGregory Neil Shapiro 		fn = "/";
42106f25ae9SGregory Neil Shapiro 
42206f25ae9SGregory Neil Shapiro 	if (tTd(44, 4))
42340266059SGregory Neil Shapiro 		sm_dprintf("safedirpath(%s, uid=%ld, gid=%ld, flags=%lx, level=%d, offset=%d):\n",
42406f25ae9SGregory Neil Shapiro 			fn, (long) uid, (long) gid, flags, level, offset);
42506f25ae9SGregory Neil Shapiro 
42606f25ae9SGregory Neil Shapiro 	if (!bitnset(DBS_GROUPWRITABLEDIRPATHSAFE, DontBlameSendmail))
42706f25ae9SGregory Neil Shapiro 		mode |= S_IWGRP;
42806f25ae9SGregory Neil Shapiro 
42906f25ae9SGregory Neil Shapiro 	/* Make a modifiable copy of the filename */
43040266059SGregory Neil Shapiro 	if (sm_strlcpy(s, fn, sizeof s) >= sizeof s)
43106f25ae9SGregory Neil Shapiro 		return EINVAL;
43206f25ae9SGregory Neil Shapiro 
43306f25ae9SGregory Neil Shapiro 	p = s + offset;
43406f25ae9SGregory Neil Shapiro 	while (p != NULL)
43506f25ae9SGregory Neil Shapiro 	{
43606f25ae9SGregory Neil Shapiro 		/* put back character */
43706f25ae9SGregory Neil Shapiro 		if (saveptr != NULL)
43806f25ae9SGregory Neil Shapiro 		{
43906f25ae9SGregory Neil Shapiro 			*saveptr = save;
44006f25ae9SGregory Neil Shapiro 			saveptr = NULL;
44106f25ae9SGregory Neil Shapiro 			p++;
44206f25ae9SGregory Neil Shapiro 		}
44306f25ae9SGregory Neil Shapiro 
44406f25ae9SGregory Neil Shapiro 		if (*p == '\0')
44506f25ae9SGregory Neil Shapiro 			break;
44606f25ae9SGregory Neil Shapiro 
44706f25ae9SGregory Neil Shapiro 		p = strchr(p, '/');
44806f25ae9SGregory Neil Shapiro 
44906f25ae9SGregory Neil Shapiro 		/* Special case for root directory */
45006f25ae9SGregory Neil Shapiro 		if (p == s)
45106f25ae9SGregory Neil Shapiro 		{
45206f25ae9SGregory Neil Shapiro 			save = *(p + 1);
45306f25ae9SGregory Neil Shapiro 			saveptr = p + 1;
45406f25ae9SGregory Neil Shapiro 			*(p + 1) = '\0';
45506f25ae9SGregory Neil Shapiro 		}
45606f25ae9SGregory Neil Shapiro 		else if (p != NULL)
45706f25ae9SGregory Neil Shapiro 		{
45806f25ae9SGregory Neil Shapiro 			save = *p;
45906f25ae9SGregory Neil Shapiro 			saveptr = p;
46006f25ae9SGregory Neil Shapiro 			*p = '\0';
46106f25ae9SGregory Neil Shapiro 		}
46206f25ae9SGregory Neil Shapiro 
46306f25ae9SGregory Neil Shapiro 		/* Heuristic: . and .. have already been checked */
46406f25ae9SGregory Neil Shapiro 		enddir = strrchr(s, '/');
46506f25ae9SGregory Neil Shapiro 		if (enddir != NULL &&
46606f25ae9SGregory Neil Shapiro 		    (strcmp(enddir, "/..") == 0 ||
46706f25ae9SGregory Neil Shapiro 		     strcmp(enddir, "/.") == 0))
46806f25ae9SGregory Neil Shapiro 			continue;
46906f25ae9SGregory Neil Shapiro 
47006f25ae9SGregory Neil Shapiro 		if (tTd(44, 20))
47140266059SGregory Neil Shapiro 			sm_dprintf("\t[dir %s]\n", s);
47206f25ae9SGregory Neil Shapiro 
47306f25ae9SGregory Neil Shapiro #if HASLSTAT
47406f25ae9SGregory Neil Shapiro 		ret = lstat(s, &stbuf);
475*5b0945b5SGregory Neil Shapiro #else
47606f25ae9SGregory Neil Shapiro 		ret = stat(s, &stbuf);
477*5b0945b5SGregory Neil Shapiro #endif
47806f25ae9SGregory Neil Shapiro 		if (ret < 0)
47906f25ae9SGregory Neil Shapiro 		{
48006f25ae9SGregory Neil Shapiro 			ret = errno;
48106f25ae9SGregory Neil Shapiro 			break;
48206f25ae9SGregory Neil Shapiro 		}
48306f25ae9SGregory Neil Shapiro 
48406f25ae9SGregory Neil Shapiro #ifdef S_ISLNK
48506f25ae9SGregory Neil Shapiro 		/* Follow symlinks */
48606f25ae9SGregory Neil Shapiro 		if (S_ISLNK(stbuf.st_mode))
48706f25ae9SGregory Neil Shapiro 		{
48894c01205SGregory Neil Shapiro 			int linklen;
48906f25ae9SGregory Neil Shapiro 			char *target;
49094c01205SGregory Neil Shapiro 			char buf[MAXPATHLEN];
491b6bacd31SGregory Neil Shapiro 			char fullbuf[MAXLINKPATHLEN];
49206f25ae9SGregory Neil Shapiro 
49306f25ae9SGregory Neil Shapiro 			memset(buf, '\0', sizeof buf);
49494c01205SGregory Neil Shapiro 			linklen = readlink(s, buf, sizeof buf);
49594c01205SGregory Neil Shapiro 			if (linklen < 0)
49606f25ae9SGregory Neil Shapiro 			{
49706f25ae9SGregory Neil Shapiro 				ret = errno;
49806f25ae9SGregory Neil Shapiro 				break;
49906f25ae9SGregory Neil Shapiro 			}
50094c01205SGregory Neil Shapiro 			if (linklen >= sizeof buf)
50194c01205SGregory Neil Shapiro 			{
50294c01205SGregory Neil Shapiro 				/* file name too long for buffer */
50394c01205SGregory Neil Shapiro 				ret = errno = EINVAL;
50494c01205SGregory Neil Shapiro 				break;
50594c01205SGregory Neil Shapiro 			}
50606f25ae9SGregory Neil Shapiro 
50706f25ae9SGregory Neil Shapiro 			offset = 0;
50806f25ae9SGregory Neil Shapiro 			if (*buf == '/')
50906f25ae9SGregory Neil Shapiro 			{
51006f25ae9SGregory Neil Shapiro 				target = buf;
51106f25ae9SGregory Neil Shapiro 
51206f25ae9SGregory Neil Shapiro 				/* If path is the same, avoid rechecks */
51306f25ae9SGregory Neil Shapiro 				while (s[offset] == buf[offset] &&
51406f25ae9SGregory Neil Shapiro 				       s[offset] != '\0')
51506f25ae9SGregory Neil Shapiro 					offset++;
51606f25ae9SGregory Neil Shapiro 
51706f25ae9SGregory Neil Shapiro 				if (s[offset] == '\0' && buf[offset] == '\0')
51806f25ae9SGregory Neil Shapiro 				{
51906f25ae9SGregory Neil Shapiro 					/* strings match, symlink loop */
52006f25ae9SGregory Neil Shapiro 					return ELOOP;
52106f25ae9SGregory Neil Shapiro 				}
52206f25ae9SGregory Neil Shapiro 
52306f25ae9SGregory Neil Shapiro 				/* back off from the mismatch */
52406f25ae9SGregory Neil Shapiro 				if (offset > 0)
52506f25ae9SGregory Neil Shapiro 					offset--;
52606f25ae9SGregory Neil Shapiro 
52706f25ae9SGregory Neil Shapiro 				/* Make sure we are at a directory break */
52806f25ae9SGregory Neil Shapiro 				if (offset > 0 &&
52906f25ae9SGregory Neil Shapiro 				    s[offset] != '/' &&
53006f25ae9SGregory Neil Shapiro 				    s[offset] != '\0')
53106f25ae9SGregory Neil Shapiro 				{
53206f25ae9SGregory Neil Shapiro 					while (buf[offset] != '/' &&
53306f25ae9SGregory Neil Shapiro 					       offset > 0)
53406f25ae9SGregory Neil Shapiro 						offset--;
53506f25ae9SGregory Neil Shapiro 				}
53606f25ae9SGregory Neil Shapiro 				if (offset > 0 &&
53706f25ae9SGregory Neil Shapiro 				    s[offset] == '/' &&
53806f25ae9SGregory Neil Shapiro 				    buf[offset] == '/')
53906f25ae9SGregory Neil Shapiro 				{
54006f25ae9SGregory Neil Shapiro 					/* Include the trailing slash */
54106f25ae9SGregory Neil Shapiro 					offset++;
54206f25ae9SGregory Neil Shapiro 				}
54306f25ae9SGregory Neil Shapiro 			}
54406f25ae9SGregory Neil Shapiro 			else
54506f25ae9SGregory Neil Shapiro 			{
54606f25ae9SGregory Neil Shapiro 				char *sptr;
54706f25ae9SGregory Neil Shapiro 
54806f25ae9SGregory Neil Shapiro 				sptr = strrchr(s, '/');
54906f25ae9SGregory Neil Shapiro 				if (sptr != NULL)
55006f25ae9SGregory Neil Shapiro 				{
55106f25ae9SGregory Neil Shapiro 					*sptr = '\0';
55206f25ae9SGregory Neil Shapiro 					offset = sptr + 1 - s;
55340266059SGregory Neil Shapiro 					if (sm_strlcpyn(fullbuf,
55440266059SGregory Neil Shapiro 							sizeof fullbuf, 2,
55540266059SGregory Neil Shapiro 							s, "/") >=
55640266059SGregory Neil Shapiro 						sizeof fullbuf ||
55740266059SGregory Neil Shapiro 					    sm_strlcat(fullbuf, buf,
55840266059SGregory Neil Shapiro 						       sizeof fullbuf) >=
55940266059SGregory Neil Shapiro 						sizeof fullbuf)
56006f25ae9SGregory Neil Shapiro 					{
56106f25ae9SGregory Neil Shapiro 						ret = EINVAL;
56206f25ae9SGregory Neil Shapiro 						break;
56306f25ae9SGregory Neil Shapiro 					}
56406f25ae9SGregory Neil Shapiro 					*sptr = '/';
56506f25ae9SGregory Neil Shapiro 				}
56606f25ae9SGregory Neil Shapiro 				else
56706f25ae9SGregory Neil Shapiro 				{
56840266059SGregory Neil Shapiro 					if (sm_strlcpy(fullbuf, buf,
56940266059SGregory Neil Shapiro 						       sizeof fullbuf) >=
57040266059SGregory Neil Shapiro 						sizeof fullbuf)
57106f25ae9SGregory Neil Shapiro 					{
57206f25ae9SGregory Neil Shapiro 						ret = EINVAL;
57306f25ae9SGregory Neil Shapiro 						break;
57406f25ae9SGregory Neil Shapiro 					}
57506f25ae9SGregory Neil Shapiro 				}
57606f25ae9SGregory Neil Shapiro 				target = fullbuf;
57706f25ae9SGregory Neil Shapiro 			}
57806f25ae9SGregory Neil Shapiro 			ret = safedirpath(target, uid, gid, user, flags,
57906f25ae9SGregory Neil Shapiro 					  level + 1, offset);
58006f25ae9SGregory Neil Shapiro 			if (ret != 0)
58106f25ae9SGregory Neil Shapiro 				break;
58206f25ae9SGregory Neil Shapiro 
58306f25ae9SGregory Neil Shapiro 			/* Don't check permissions on the link file itself */
58406f25ae9SGregory Neil Shapiro 			continue;
58506f25ae9SGregory Neil Shapiro 		}
58606f25ae9SGregory Neil Shapiro #endif /* S_ISLNK */
58706f25ae9SGregory Neil Shapiro 
58806f25ae9SGregory Neil Shapiro 		if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) &&
58906f25ae9SGregory Neil Shapiro #ifdef S_ISVTX
59006f25ae9SGregory Neil Shapiro 		    !(bitnset(DBS_TRUSTSTICKYBIT, DontBlameSendmail) &&
59106f25ae9SGregory Neil Shapiro 		      bitset(S_ISVTX, stbuf.st_mode)) &&
592*5b0945b5SGregory Neil Shapiro #endif
59306f25ae9SGregory Neil Shapiro 		    bitset(mode, stbuf.st_mode))
59406f25ae9SGregory Neil Shapiro 		{
59506f25ae9SGregory Neil Shapiro 			if (tTd(44, 4))
59640266059SGregory Neil Shapiro 				sm_dprintf("\t[dir %s] mode %lo ",
59740266059SGregory Neil Shapiro 					s, (unsigned long) stbuf.st_mode);
59806f25ae9SGregory Neil Shapiro 			if (bitset(SFF_SAFEDIRPATH, flags))
59906f25ae9SGregory Neil Shapiro 			{
60006f25ae9SGregory Neil Shapiro 				if (bitset(S_IWOTH, stbuf.st_mode))
60106f25ae9SGregory Neil Shapiro 					ret = E_SM_WWDIR;
60206f25ae9SGregory Neil Shapiro 				else
60306f25ae9SGregory Neil Shapiro 					ret = E_SM_GWDIR;
60406f25ae9SGregory Neil Shapiro 				if (tTd(44, 4))
60540266059SGregory Neil Shapiro 					sm_dprintf("FATAL\n");
60606f25ae9SGregory Neil Shapiro 				break;
60706f25ae9SGregory Neil Shapiro 			}
60806f25ae9SGregory Neil Shapiro 			if (tTd(44, 4))
60940266059SGregory Neil Shapiro 				sm_dprintf("WARNING\n");
61006f25ae9SGregory Neil Shapiro 			if (Verbose > 1)
61106f25ae9SGregory Neil Shapiro 				message("051 WARNING: %s writable directory %s",
61206f25ae9SGregory Neil Shapiro 					bitset(S_IWOTH, stbuf.st_mode)
61306f25ae9SGregory Neil Shapiro 					   ? "World"
61406f25ae9SGregory Neil Shapiro 					   : "Group",
61506f25ae9SGregory Neil Shapiro 					s);
61606f25ae9SGregory Neil Shapiro 		}
61706f25ae9SGregory Neil Shapiro 		if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags))
61806f25ae9SGregory Neil Shapiro 		{
61906f25ae9SGregory Neil Shapiro 			if (bitset(S_IXOTH, stbuf.st_mode))
62006f25ae9SGregory Neil Shapiro 				continue;
62106f25ae9SGregory Neil Shapiro 			ret = EACCES;
62206f25ae9SGregory Neil Shapiro 			break;
62306f25ae9SGregory Neil Shapiro 		}
62406f25ae9SGregory Neil Shapiro 
62506f25ae9SGregory Neil Shapiro 		/*
62606f25ae9SGregory Neil Shapiro 		**  Let OS determine access to file if we are not
62706f25ae9SGregory Neil Shapiro 		**  running as a privileged user.  This allows ACLs
62806f25ae9SGregory Neil Shapiro 		**  to work.  Also, if opening as root, assume we can
62906f25ae9SGregory Neil Shapiro 		**  scan the directory.
63006f25ae9SGregory Neil Shapiro 		*/
63106f25ae9SGregory Neil Shapiro 		if (geteuid() != 0 || bitset(SFF_OPENASROOT, flags))
63206f25ae9SGregory Neil Shapiro 			continue;
63306f25ae9SGregory Neil Shapiro 
63406f25ae9SGregory Neil Shapiro 		if (stbuf.st_uid == uid &&
63506f25ae9SGregory Neil Shapiro 		    bitset(S_IXUSR, stbuf.st_mode))
63606f25ae9SGregory Neil Shapiro 			continue;
63706f25ae9SGregory Neil Shapiro 		if (stbuf.st_gid == gid &&
63806f25ae9SGregory Neil Shapiro 		    bitset(S_IXGRP, stbuf.st_mode))
63906f25ae9SGregory Neil Shapiro 			continue;
64006f25ae9SGregory Neil Shapiro #ifndef NO_GROUP_SET
64106f25ae9SGregory Neil Shapiro 		if (user != NULL && !DontInitGroups &&
64206f25ae9SGregory Neil Shapiro 		    ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
64306f25ae9SGregory Neil Shapiro 		     (gr = getgrgid(stbuf.st_gid)) != NULL))
64406f25ae9SGregory Neil Shapiro 		{
64506f25ae9SGregory Neil Shapiro 			register char **gp;
64606f25ae9SGregory Neil Shapiro 
64706f25ae9SGregory Neil Shapiro 			for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
64806f25ae9SGregory Neil Shapiro 				if (strcmp(*gp, user) == 0)
64906f25ae9SGregory Neil Shapiro 					break;
65006f25ae9SGregory Neil Shapiro 			if (gp != NULL && *gp != NULL &&
65106f25ae9SGregory Neil Shapiro 			    bitset(S_IXGRP, stbuf.st_mode))
65206f25ae9SGregory Neil Shapiro 				continue;
65306f25ae9SGregory Neil Shapiro 		}
65406f25ae9SGregory Neil Shapiro #endif /* ! NO_GROUP_SET */
65506f25ae9SGregory Neil Shapiro 		if (!bitset(S_IXOTH, stbuf.st_mode))
65606f25ae9SGregory Neil Shapiro 		{
65706f25ae9SGregory Neil Shapiro 			ret = EACCES;
65806f25ae9SGregory Neil Shapiro 			break;
65906f25ae9SGregory Neil Shapiro 		}
66006f25ae9SGregory Neil Shapiro 	}
66106f25ae9SGregory Neil Shapiro 	if (tTd(44, 4))
66240266059SGregory Neil Shapiro 		sm_dprintf("\t[dir %s] %s\n", fn,
66340266059SGregory Neil Shapiro 			ret == 0 ? "OK" : sm_errstring(ret));
66406f25ae9SGregory Neil Shapiro 	return ret;
66506f25ae9SGregory Neil Shapiro }
66640266059SGregory Neil Shapiro /*
66706f25ae9SGregory Neil Shapiro **  SAFEOPEN -- do a file open with extra checking
66806f25ae9SGregory Neil Shapiro **
66906f25ae9SGregory Neil Shapiro **	Parameters:
67006f25ae9SGregory Neil Shapiro **		fn -- the file name to open.
67106f25ae9SGregory Neil Shapiro **		omode -- the open-style mode flags.
67206f25ae9SGregory Neil Shapiro **		cmode -- the create-style mode flags.
67306f25ae9SGregory Neil Shapiro **		sff -- safefile flags.
67406f25ae9SGregory Neil Shapiro **
67506f25ae9SGregory Neil Shapiro **	Returns:
67606f25ae9SGregory Neil Shapiro **		Same as open.
67706f25ae9SGregory Neil Shapiro */
67806f25ae9SGregory Neil Shapiro 
67906f25ae9SGregory Neil Shapiro int
safeopen(fn,omode,cmode,sff)68006f25ae9SGregory Neil Shapiro safeopen(fn, omode, cmode, sff)
68106f25ae9SGregory Neil Shapiro 	char *fn;
68206f25ae9SGregory Neil Shapiro 	int omode;
68306f25ae9SGregory Neil Shapiro 	int cmode;
68406f25ae9SGregory Neil Shapiro 	long sff;
68506f25ae9SGregory Neil Shapiro {
686e92d3f3fSGregory Neil Shapiro #if !NOFTRUNCATE
687e92d3f3fSGregory Neil Shapiro 	bool truncate;
688*5b0945b5SGregory Neil Shapiro #endif
68906f25ae9SGregory Neil Shapiro 	int rval;
69006f25ae9SGregory Neil Shapiro 	int fd;
69106f25ae9SGregory Neil Shapiro 	int smode;
69206f25ae9SGregory Neil Shapiro 	struct stat stb;
69306f25ae9SGregory Neil Shapiro 
69406f25ae9SGregory Neil Shapiro 	if (tTd(44, 10))
69540266059SGregory Neil Shapiro 		sm_dprintf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n",
69606f25ae9SGregory Neil Shapiro 			   fn, omode, cmode, sff);
69706f25ae9SGregory Neil Shapiro 
69806f25ae9SGregory Neil Shapiro 	if (bitset(O_CREAT, omode))
69906f25ae9SGregory Neil Shapiro 		sff |= SFF_CREAT;
70006f25ae9SGregory Neil Shapiro 	omode &= ~O_CREAT;
70106f25ae9SGregory Neil Shapiro 	switch (omode & O_ACCMODE)
70206f25ae9SGregory Neil Shapiro 	{
70306f25ae9SGregory Neil Shapiro 	  case O_RDONLY:
70406f25ae9SGregory Neil Shapiro 		smode = S_IREAD;
70506f25ae9SGregory Neil Shapiro 		break;
70606f25ae9SGregory Neil Shapiro 
70706f25ae9SGregory Neil Shapiro 	  case O_WRONLY:
70806f25ae9SGregory Neil Shapiro 		smode = S_IWRITE;
70906f25ae9SGregory Neil Shapiro 		break;
71006f25ae9SGregory Neil Shapiro 
71106f25ae9SGregory Neil Shapiro 	  case O_RDWR:
71206f25ae9SGregory Neil Shapiro 		smode = S_IREAD|S_IWRITE;
71306f25ae9SGregory Neil Shapiro 		break;
71406f25ae9SGregory Neil Shapiro 
71506f25ae9SGregory Neil Shapiro 	  default:
71606f25ae9SGregory Neil Shapiro 		smode = 0;
71706f25ae9SGregory Neil Shapiro 		break;
71806f25ae9SGregory Neil Shapiro 	}
71906f25ae9SGregory Neil Shapiro 	if (bitset(SFF_OPENASROOT, sff))
72006f25ae9SGregory Neil Shapiro 		rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName,
72106f25ae9SGregory Neil Shapiro 				sff, smode, &stb);
72206f25ae9SGregory Neil Shapiro 	else
72306f25ae9SGregory Neil Shapiro 		rval = safefile(fn, RealUid, RealGid, RealUserName,
72406f25ae9SGregory Neil Shapiro 				sff, smode, &stb);
72506f25ae9SGregory Neil Shapiro 	if (rval != 0)
72606f25ae9SGregory Neil Shapiro 	{
72706f25ae9SGregory Neil Shapiro 		errno = rval;
72806f25ae9SGregory Neil Shapiro 		return -1;
72906f25ae9SGregory Neil Shapiro 	}
73006f25ae9SGregory Neil Shapiro 	if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff))
73106f25ae9SGregory Neil Shapiro 		omode |= O_CREAT | (bitset(SFF_NOTEXCL, sff) ? 0 : O_EXCL);
73206f25ae9SGregory Neil Shapiro 	else if (bitset(SFF_CREAT, sff) && bitset(O_EXCL, omode))
73306f25ae9SGregory Neil Shapiro 	{
73406f25ae9SGregory Neil Shapiro 		/* The file exists so an exclusive create would fail */
73506f25ae9SGregory Neil Shapiro 		errno = EEXIST;
73606f25ae9SGregory Neil Shapiro 		return -1;
73706f25ae9SGregory Neil Shapiro 	}
73806f25ae9SGregory Neil Shapiro 
739e92d3f3fSGregory Neil Shapiro #if !NOFTRUNCATE
740e92d3f3fSGregory Neil Shapiro 	truncate = bitset(O_TRUNC, omode);
741e92d3f3fSGregory Neil Shapiro 	if (truncate)
742e92d3f3fSGregory Neil Shapiro 		omode &= ~O_TRUNC;
743*5b0945b5SGregory Neil Shapiro #endif
744e92d3f3fSGregory Neil Shapiro 
74506f25ae9SGregory Neil Shapiro 	fd = dfopen(fn, omode, cmode, sff);
74606f25ae9SGregory Neil Shapiro 	if (fd < 0)
74706f25ae9SGregory Neil Shapiro 		return fd;
74806f25ae9SGregory Neil Shapiro 	if (filechanged(fn, fd, &stb))
74906f25ae9SGregory Neil Shapiro 	{
75006f25ae9SGregory Neil Shapiro 		syserr("554 5.3.0 cannot open: file %s changed after open", fn);
75106f25ae9SGregory Neil Shapiro 		(void) close(fd);
75206f25ae9SGregory Neil Shapiro 		errno = E_SM_FILECHANGE;
75306f25ae9SGregory Neil Shapiro 		return -1;
75406f25ae9SGregory Neil Shapiro 	}
755e92d3f3fSGregory Neil Shapiro 
756e92d3f3fSGregory Neil Shapiro #if !NOFTRUNCATE
757e92d3f3fSGregory Neil Shapiro 	if (truncate &&
758e92d3f3fSGregory Neil Shapiro 	    ftruncate(fd, (off_t) 0) < 0)
759e92d3f3fSGregory Neil Shapiro 	{
760e92d3f3fSGregory Neil Shapiro 		int save_errno;
761e92d3f3fSGregory Neil Shapiro 
762e92d3f3fSGregory Neil Shapiro 		save_errno = errno;
763e92d3f3fSGregory Neil Shapiro 		syserr("554 5.3.0 cannot open: file %s could not be truncated",
764e92d3f3fSGregory Neil Shapiro 		       fn);
765e92d3f3fSGregory Neil Shapiro 		(void) close(fd);
766e92d3f3fSGregory Neil Shapiro 		errno = save_errno;
767e92d3f3fSGregory Neil Shapiro 		return -1;
768e92d3f3fSGregory Neil Shapiro 	}
769e92d3f3fSGregory Neil Shapiro #endif /* !NOFTRUNCATE */
770e92d3f3fSGregory Neil Shapiro 
77106f25ae9SGregory Neil Shapiro 	return fd;
77206f25ae9SGregory Neil Shapiro }
77340266059SGregory Neil Shapiro /*
77440266059SGregory Neil Shapiro **  SAFEFOPEN -- do a file open with extra checking
77540266059SGregory Neil Shapiro **
77640266059SGregory Neil Shapiro **	Parameters:
77740266059SGregory Neil Shapiro **		fn -- the file name to open.
77840266059SGregory Neil Shapiro **		omode -- the open-style mode flags.
77940266059SGregory Neil Shapiro **		cmode -- the create-style mode flags.
78040266059SGregory Neil Shapiro **		sff -- safefile flags.
78140266059SGregory Neil Shapiro **
78240266059SGregory Neil Shapiro **	Returns:
78340266059SGregory Neil Shapiro **		Same as fopen.
78440266059SGregory Neil Shapiro */
78540266059SGregory Neil Shapiro 
78640266059SGregory Neil Shapiro SM_FILE_T *
safefopen(fn,omode,cmode,sff)78740266059SGregory Neil Shapiro safefopen(fn, omode, cmode, sff)
78840266059SGregory Neil Shapiro 	char *fn;
78940266059SGregory Neil Shapiro 	int omode;
79040266059SGregory Neil Shapiro 	int cmode;
79140266059SGregory Neil Shapiro 	long sff;
79240266059SGregory Neil Shapiro {
79340266059SGregory Neil Shapiro 	int fd;
79440266059SGregory Neil Shapiro 	int save_errno;
79540266059SGregory Neil Shapiro 	SM_FILE_T *fp;
79640266059SGregory Neil Shapiro 	int fmode;
79740266059SGregory Neil Shapiro 
79840266059SGregory Neil Shapiro 	switch (omode & O_ACCMODE)
79940266059SGregory Neil Shapiro 	{
80040266059SGregory Neil Shapiro 	  case O_RDONLY:
80140266059SGregory Neil Shapiro 		fmode = SM_IO_RDONLY;
80240266059SGregory Neil Shapiro 		break;
80340266059SGregory Neil Shapiro 
80440266059SGregory Neil Shapiro 	  case O_WRONLY:
80540266059SGregory Neil Shapiro 		if (bitset(O_APPEND, omode))
80640266059SGregory Neil Shapiro 			fmode = SM_IO_APPEND;
80740266059SGregory Neil Shapiro 		else
80840266059SGregory Neil Shapiro 			fmode = SM_IO_WRONLY;
80940266059SGregory Neil Shapiro 		break;
81040266059SGregory Neil Shapiro 
81140266059SGregory Neil Shapiro 	  case O_RDWR:
81240266059SGregory Neil Shapiro 		if (bitset(O_TRUNC, omode))
81340266059SGregory Neil Shapiro 			fmode = SM_IO_RDWRTR;
81440266059SGregory Neil Shapiro 		else if (bitset(O_APPEND, omode))
81540266059SGregory Neil Shapiro 			fmode = SM_IO_APPENDRW;
81640266059SGregory Neil Shapiro 		else
81740266059SGregory Neil Shapiro 			fmode = SM_IO_RDWR;
81840266059SGregory Neil Shapiro 		break;
81940266059SGregory Neil Shapiro 
82040266059SGregory Neil Shapiro 	  default:
82140266059SGregory Neil Shapiro 		syserr("554 5.3.5 safefopen: unknown omode %o", omode);
82240266059SGregory Neil Shapiro 		fmode = 0;
82340266059SGregory Neil Shapiro 	}
82440266059SGregory Neil Shapiro 	fd = safeopen(fn, omode, cmode, sff);
82540266059SGregory Neil Shapiro 	if (fd < 0)
82640266059SGregory Neil Shapiro 	{
82740266059SGregory Neil Shapiro 		save_errno = errno;
82840266059SGregory Neil Shapiro 		if (tTd(44, 10))
82940266059SGregory Neil Shapiro 			sm_dprintf("safefopen: safeopen failed: %s\n",
83040266059SGregory Neil Shapiro 				   sm_errstring(errno));
83140266059SGregory Neil Shapiro 		errno = save_errno;
83240266059SGregory Neil Shapiro 		return NULL;
83340266059SGregory Neil Shapiro 	}
83440266059SGregory Neil Shapiro 	fp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
83540266059SGregory Neil Shapiro 			(void *) &fd, fmode, NULL);
83640266059SGregory Neil Shapiro 	if (fp != NULL)
83740266059SGregory Neil Shapiro 		return fp;
83840266059SGregory Neil Shapiro 
83940266059SGregory Neil Shapiro 	save_errno = errno;
84040266059SGregory Neil Shapiro 	if (tTd(44, 10))
84140266059SGregory Neil Shapiro 	{
84240266059SGregory Neil Shapiro 		sm_dprintf("safefopen: fdopen(%s, %d) failed: omode=%x, sff=%lx, err=%s\n",
84340266059SGregory Neil Shapiro 			   fn, fmode, omode, sff, sm_errstring(errno));
84440266059SGregory Neil Shapiro 	}
84540266059SGregory Neil Shapiro 	(void) close(fd);
84640266059SGregory Neil Shapiro 	errno = save_errno;
84740266059SGregory Neil Shapiro 	return NULL;
84840266059SGregory Neil Shapiro }
84940266059SGregory Neil Shapiro /*
85006f25ae9SGregory Neil Shapiro **  FILECHANGED -- check to see if file changed after being opened
85106f25ae9SGregory Neil Shapiro **
85206f25ae9SGregory Neil Shapiro **	Parameters:
85306f25ae9SGregory Neil Shapiro **		fn -- pathname of file to check.
85406f25ae9SGregory Neil Shapiro **		fd -- file descriptor to check.
85506f25ae9SGregory Neil Shapiro **		stb -- stat structure from before open.
85606f25ae9SGregory Neil Shapiro **
85706f25ae9SGregory Neil Shapiro **	Returns:
85840266059SGregory Neil Shapiro **		true -- if a problem was detected.
85940266059SGregory Neil Shapiro **		false -- if this file is still the same.
86006f25ae9SGregory Neil Shapiro */
86106f25ae9SGregory Neil Shapiro 
86206f25ae9SGregory Neil Shapiro bool
filechanged(fn,fd,stb)86306f25ae9SGregory Neil Shapiro filechanged(fn, fd, stb)
86406f25ae9SGregory Neil Shapiro 	char *fn;
86506f25ae9SGregory Neil Shapiro 	int fd;
86606f25ae9SGregory Neil Shapiro 	struct stat *stb;
86706f25ae9SGregory Neil Shapiro {
86806f25ae9SGregory Neil Shapiro 	struct stat sta;
86906f25ae9SGregory Neil Shapiro 
87006f25ae9SGregory Neil Shapiro 	if (stb->st_mode == ST_MODE_NOFILE)
87106f25ae9SGregory Neil Shapiro 	{
87206f25ae9SGregory Neil Shapiro #if HASLSTAT && BOGUS_O_EXCL
87306f25ae9SGregory Neil Shapiro 		/* only necessary if exclusive open follows symbolic links */
87406f25ae9SGregory Neil Shapiro 		if (lstat(fn, stb) < 0 || stb->st_nlink != 1)
87540266059SGregory Neil Shapiro 			return true;
876*5b0945b5SGregory Neil Shapiro #else
87740266059SGregory Neil Shapiro 		return false;
878*5b0945b5SGregory Neil Shapiro #endif
87906f25ae9SGregory Neil Shapiro 	}
88006f25ae9SGregory Neil Shapiro 	if (fstat(fd, &sta) < 0)
88140266059SGregory Neil Shapiro 		return true;
88206f25ae9SGregory Neil Shapiro 
88306f25ae9SGregory Neil Shapiro 	if (sta.st_nlink != stb->st_nlink ||
88406f25ae9SGregory Neil Shapiro 	    sta.st_dev != stb->st_dev ||
88506f25ae9SGregory Neil Shapiro 	    sta.st_ino != stb->st_ino ||
88606f25ae9SGregory Neil Shapiro #if HAS_ST_GEN && 0		/* AFS returns garbage in st_gen */
88706f25ae9SGregory Neil Shapiro 	    sta.st_gen != stb->st_gen ||
888*5b0945b5SGregory Neil Shapiro #endif
88906f25ae9SGregory Neil Shapiro 	    sta.st_uid != stb->st_uid ||
89006f25ae9SGregory Neil Shapiro 	    sta.st_gid != stb->st_gid)
89106f25ae9SGregory Neil Shapiro 	{
89206f25ae9SGregory Neil Shapiro 		if (tTd(44, 8))
89306f25ae9SGregory Neil Shapiro 		{
89440266059SGregory Neil Shapiro 			sm_dprintf("File changed after opening:\n");
89540266059SGregory Neil Shapiro 			sm_dprintf(" nlink	= %ld/%ld\n",
89606f25ae9SGregory Neil Shapiro 				(long) stb->st_nlink, (long) sta.st_nlink);
89740266059SGregory Neil Shapiro 			sm_dprintf(" dev	= %ld/%ld\n",
89806f25ae9SGregory Neil Shapiro 				(long) stb->st_dev, (long) sta.st_dev);
89940266059SGregory Neil Shapiro 			sm_dprintf(" ino	= %llu/%llu\n",
90040266059SGregory Neil Shapiro 				(ULONGLONG_T) stb->st_ino,
90140266059SGregory Neil Shapiro 				(ULONGLONG_T) sta.st_ino);
90206f25ae9SGregory Neil Shapiro #if HAS_ST_GEN
90340266059SGregory Neil Shapiro 			sm_dprintf(" gen	= %ld/%ld\n",
90406f25ae9SGregory Neil Shapiro 				(long) stb->st_gen, (long) sta.st_gen);
905*5b0945b5SGregory Neil Shapiro #endif
90640266059SGregory Neil Shapiro 			sm_dprintf(" uid	= %ld/%ld\n",
90706f25ae9SGregory Neil Shapiro 				(long) stb->st_uid, (long) sta.st_uid);
90840266059SGregory Neil Shapiro 			sm_dprintf(" gid	= %ld/%ld\n",
90906f25ae9SGregory Neil Shapiro 				(long) stb->st_gid, (long) sta.st_gid);
91006f25ae9SGregory Neil Shapiro 		}
91140266059SGregory Neil Shapiro 		return true;
91206f25ae9SGregory Neil Shapiro 	}
91306f25ae9SGregory Neil Shapiro 
91440266059SGregory Neil Shapiro 	return false;
91506f25ae9SGregory Neil Shapiro }
91640266059SGregory Neil Shapiro /*
91706f25ae9SGregory Neil Shapiro **  DFOPEN -- determined file open
91806f25ae9SGregory Neil Shapiro **
91906f25ae9SGregory Neil Shapiro **	This routine has the semantics of open, except that it will
92006f25ae9SGregory Neil Shapiro **	keep trying a few times to make this happen.  The idea is that
92106f25ae9SGregory Neil Shapiro **	on very loaded systems, we may run out of resources (inodes,
92206f25ae9SGregory Neil Shapiro **	whatever), so this tries to get around it.
92306f25ae9SGregory Neil Shapiro */
92406f25ae9SGregory Neil Shapiro 
92506f25ae9SGregory Neil Shapiro int
dfopen(filename,omode,cmode,sff)92606f25ae9SGregory Neil Shapiro dfopen(filename, omode, cmode, sff)
92706f25ae9SGregory Neil Shapiro 	char *filename;
92806f25ae9SGregory Neil Shapiro 	int omode;
92906f25ae9SGregory Neil Shapiro 	int cmode;
93006f25ae9SGregory Neil Shapiro 	long sff;
93106f25ae9SGregory Neil Shapiro {
93206f25ae9SGregory Neil Shapiro 	register int tries;
93306f25ae9SGregory Neil Shapiro 	int fd = -1;
93406f25ae9SGregory Neil Shapiro 	struct stat st;
93506f25ae9SGregory Neil Shapiro 
93606f25ae9SGregory Neil Shapiro 	for (tries = 0; tries < 10; tries++)
93706f25ae9SGregory Neil Shapiro 	{
93806f25ae9SGregory Neil Shapiro 		(void) sleep((unsigned) (10 * tries));
93906f25ae9SGregory Neil Shapiro 		errno = 0;
94006f25ae9SGregory Neil Shapiro 		fd = open(filename, omode, cmode);
94106f25ae9SGregory Neil Shapiro 		if (fd >= 0)
94206f25ae9SGregory Neil Shapiro 			break;
94306f25ae9SGregory Neil Shapiro 		switch (errno)
94406f25ae9SGregory Neil Shapiro 		{
94506f25ae9SGregory Neil Shapiro 		  case ENFILE:		/* system file table full */
94606f25ae9SGregory Neil Shapiro 		  case EINTR:		/* interrupted syscall */
94706f25ae9SGregory Neil Shapiro #ifdef ETXTBSY
94806f25ae9SGregory Neil Shapiro 		  case ETXTBSY:		/* Apollo: net file locked */
949*5b0945b5SGregory Neil Shapiro #endif
95006f25ae9SGregory Neil Shapiro 			continue;
95106f25ae9SGregory Neil Shapiro 		}
95206f25ae9SGregory Neil Shapiro 		break;
95306f25ae9SGregory Neil Shapiro 	}
95406f25ae9SGregory Neil Shapiro 	if (!bitset(SFF_NOLOCK, sff) &&
95506f25ae9SGregory Neil Shapiro 	    fd >= 0 &&
95606f25ae9SGregory Neil Shapiro 	    fstat(fd, &st) >= 0 &&
95706f25ae9SGregory Neil Shapiro 	    S_ISREG(st.st_mode))
95806f25ae9SGregory Neil Shapiro 	{
95906f25ae9SGregory Neil Shapiro 		int locktype;
96006f25ae9SGregory Neil Shapiro 
96106f25ae9SGregory Neil Shapiro 		/* lock the file to avoid accidental conflicts */
96206f25ae9SGregory Neil Shapiro 		if ((omode & O_ACCMODE) != O_RDONLY)
96306f25ae9SGregory Neil Shapiro 			locktype = LOCK_EX;
96406f25ae9SGregory Neil Shapiro 		else
96506f25ae9SGregory Neil Shapiro 			locktype = LOCK_SH;
966e92d3f3fSGregory Neil Shapiro 		if (bitset(SFF_NBLOCK, sff))
967e92d3f3fSGregory Neil Shapiro 			locktype |= LOCK_NB;
968e92d3f3fSGregory Neil Shapiro 
96906f25ae9SGregory Neil Shapiro 		if (!lockfile(fd, filename, NULL, locktype))
97006f25ae9SGregory Neil Shapiro 		{
97106f25ae9SGregory Neil Shapiro 			int save_errno = errno;
97206f25ae9SGregory Neil Shapiro 
97306f25ae9SGregory Neil Shapiro 			(void) close(fd);
97406f25ae9SGregory Neil Shapiro 			fd = -1;
97506f25ae9SGregory Neil Shapiro 			errno = save_errno;
97606f25ae9SGregory Neil Shapiro 		}
97706f25ae9SGregory Neil Shapiro 		else
97806f25ae9SGregory Neil Shapiro 			errno = 0;
97906f25ae9SGregory Neil Shapiro 	}
98006f25ae9SGregory Neil Shapiro 	return fd;
98106f25ae9SGregory Neil Shapiro }
982