xref: /freebsd/contrib/sendmail/libsmutil/safefile.c (revision 5dd76dd0cc19450133aa379ce0ce4a68ae07fb39)
106f25ae9SGregory Neil Shapiro /*
2*5dd76dd0SGregory 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 
18*5dd76dd0SGregory 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.
2806f25ae9SGregory Neil Shapiro **		user -- user name to compare against (used for group
2906f25ae9SGregory Neil Shapiro **			sets).
3006f25ae9SGregory Neil Shapiro **		flags -- modifiers:
3106f25ae9SGregory Neil Shapiro **			SFF_MUSTOWN -- "uid" must own this file.
3206f25ae9SGregory Neil Shapiro **			SFF_NOSLINK -- file cannot be a symbolic link.
3306f25ae9SGregory Neil Shapiro **		mode -- mode bits that must match.
3406f25ae9SGregory Neil Shapiro **		st -- if set, points to a stat structure that will
3506f25ae9SGregory Neil Shapiro **			get the stat info for the file.
3606f25ae9SGregory Neil Shapiro **
3706f25ae9SGregory Neil Shapiro **	Returns:
3806f25ae9SGregory Neil Shapiro **		0 if fn exists, is owned by uid, and matches mode.
3906f25ae9SGregory Neil Shapiro **		An errno otherwise.  The actual errno is cleared.
4006f25ae9SGregory Neil Shapiro **
4106f25ae9SGregory Neil Shapiro **	Side Effects:
4206f25ae9SGregory Neil Shapiro **		none.
4306f25ae9SGregory Neil Shapiro */
4406f25ae9SGregory Neil Shapiro 
4506f25ae9SGregory Neil Shapiro int
4606f25ae9SGregory Neil Shapiro safefile(fn, uid, gid, user, flags, mode, st)
4706f25ae9SGregory Neil Shapiro 	char *fn;
4806f25ae9SGregory Neil Shapiro 	UID_T uid;
4906f25ae9SGregory Neil Shapiro 	GID_T gid;
5006f25ae9SGregory Neil Shapiro 	char *user;
5106f25ae9SGregory Neil Shapiro 	long flags;
5206f25ae9SGregory Neil Shapiro 	int mode;
5306f25ae9SGregory Neil Shapiro 	struct stat *st;
5406f25ae9SGregory Neil Shapiro {
5506f25ae9SGregory Neil Shapiro 	register char *p;
5606f25ae9SGregory Neil Shapiro 	register struct group *gr = NULL;
5706f25ae9SGregory Neil Shapiro 	int file_errno = 0;
5806f25ae9SGregory Neil Shapiro 	bool checkpath;
5906f25ae9SGregory Neil Shapiro 	struct stat stbuf;
6006f25ae9SGregory Neil Shapiro 	struct stat fstbuf;
6194c01205SGregory Neil Shapiro 	char fbuf[MAXPATHLEN];
6206f25ae9SGregory Neil Shapiro 
6306f25ae9SGregory Neil Shapiro 	if (tTd(44, 4))
6440266059SGregory Neil Shapiro 		sm_dprintf("safefile(%s, uid=%d, gid=%d, flags=%lx, mode=%o):\n",
6506f25ae9SGregory Neil Shapiro 			fn, (int) uid, (int) gid, flags, mode);
6606f25ae9SGregory Neil Shapiro 	errno = 0;
6740266059SGregory Neil Shapiro 	if (sm_strlcpy(fbuf, fn, sizeof fbuf) >= sizeof fbuf)
6806f25ae9SGregory Neil Shapiro 	{
6906f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
7040266059SGregory Neil Shapiro 			sm_dprintf("\tpathname too long\n");
7106f25ae9SGregory Neil Shapiro 		return ENAMETOOLONG;
7206f25ae9SGregory Neil Shapiro 	}
7306f25ae9SGregory Neil Shapiro 	fn = fbuf;
7440266059SGregory Neil Shapiro 	if (st == NULL)
7540266059SGregory Neil Shapiro 		st = &fstbuf;
7606f25ae9SGregory Neil Shapiro 
7706f25ae9SGregory Neil Shapiro 	/* ignore SFF_SAFEDIRPATH if we are debugging */
7806f25ae9SGregory Neil Shapiro 	if (RealUid != 0 && RunAsUid == RealUid)
7906f25ae9SGregory Neil Shapiro 		flags &= ~SFF_SAFEDIRPATH;
8006f25ae9SGregory Neil Shapiro 
8106f25ae9SGregory Neil Shapiro 	/* first check to see if the file exists at all */
8206f25ae9SGregory Neil Shapiro # if HASLSTAT
8306f25ae9SGregory Neil Shapiro 	if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st)
8406f25ae9SGregory Neil Shapiro 					: stat(fn, st)) < 0)
8506f25ae9SGregory Neil Shapiro # else /* HASLSTAT */
8606f25ae9SGregory Neil Shapiro 	if (stat(fn, st) < 0)
8706f25ae9SGregory Neil Shapiro # endif /* HASLSTAT */
8806f25ae9SGregory Neil Shapiro 	{
8906f25ae9SGregory Neil Shapiro 		file_errno = errno;
9006f25ae9SGregory Neil Shapiro 	}
9106f25ae9SGregory Neil Shapiro 	else if (bitset(SFF_SETUIDOK, flags) &&
9206f25ae9SGregory Neil Shapiro 		 !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) &&
9306f25ae9SGregory Neil Shapiro 		 S_ISREG(st->st_mode))
9406f25ae9SGregory Neil Shapiro 	{
9506f25ae9SGregory Neil Shapiro 		/*
9640266059SGregory Neil Shapiro 		**  If final file is set-user-ID, run as the owner of that
9706f25ae9SGregory Neil Shapiro 		**  file.  Gotta be careful not to reveal anything too
9806f25ae9SGregory Neil Shapiro 		**  soon here!
9906f25ae9SGregory Neil Shapiro 		*/
10006f25ae9SGregory Neil Shapiro 
10106f25ae9SGregory Neil Shapiro # ifdef SUID_ROOT_FILES_OK
10206f25ae9SGregory Neil Shapiro 		if (bitset(S_ISUID, st->st_mode))
10306f25ae9SGregory Neil Shapiro # else /* SUID_ROOT_FILES_OK */
10406f25ae9SGregory Neil Shapiro 		if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 &&
10506f25ae9SGregory Neil Shapiro 		    st->st_uid != TrustedUid)
10606f25ae9SGregory Neil Shapiro # endif /* SUID_ROOT_FILES_OK */
10706f25ae9SGregory Neil Shapiro 		{
10806f25ae9SGregory Neil Shapiro 			uid = st->st_uid;
10906f25ae9SGregory Neil Shapiro 			user = NULL;
11006f25ae9SGregory Neil Shapiro 		}
11106f25ae9SGregory Neil Shapiro # ifdef SUID_ROOT_FILES_OK
11206f25ae9SGregory Neil Shapiro 		if (bitset(S_ISGID, st->st_mode))
11306f25ae9SGregory Neil Shapiro # else /* SUID_ROOT_FILES_OK */
11406f25ae9SGregory Neil Shapiro 		if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0)
11506f25ae9SGregory Neil Shapiro # endif /* SUID_ROOT_FILES_OK */
11606f25ae9SGregory Neil Shapiro 			gid = st->st_gid;
11706f25ae9SGregory Neil Shapiro 	}
11806f25ae9SGregory Neil Shapiro 
11906f25ae9SGregory Neil Shapiro 	checkpath = !bitset(SFF_NOPATHCHECK, flags) ||
12006f25ae9SGregory Neil Shapiro 		    (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags));
12106f25ae9SGregory Neil Shapiro 	if (bitset(SFF_NOWLINK, flags) && !bitset(SFF_SAFEDIRPATH, flags))
12206f25ae9SGregory Neil Shapiro 	{
12306f25ae9SGregory Neil Shapiro 		int ret;
12406f25ae9SGregory Neil Shapiro 
12506f25ae9SGregory Neil Shapiro 		/* check the directory */
12606f25ae9SGregory Neil Shapiro 		p = strrchr(fn, '/');
12706f25ae9SGregory Neil Shapiro 		if (p == NULL)
12806f25ae9SGregory Neil Shapiro 		{
12906f25ae9SGregory Neil Shapiro 			ret = safedirpath(".", uid, gid, user,
13006f25ae9SGregory Neil Shapiro 					  flags|SFF_SAFEDIRPATH, 0, 0);
13106f25ae9SGregory Neil Shapiro 		}
13206f25ae9SGregory Neil Shapiro 		else
13306f25ae9SGregory Neil Shapiro 		{
13406f25ae9SGregory Neil Shapiro 			*p = '\0';
13506f25ae9SGregory Neil Shapiro 			ret = safedirpath(fn, uid, gid, user,
13606f25ae9SGregory Neil Shapiro 					  flags|SFF_SAFEDIRPATH, 0, 0);
13706f25ae9SGregory Neil Shapiro 			*p = '/';
13806f25ae9SGregory Neil Shapiro 		}
13906f25ae9SGregory Neil Shapiro 		if (ret == 0)
14006f25ae9SGregory Neil Shapiro 		{
14106f25ae9SGregory Neil Shapiro 			/* directory is safe */
14240266059SGregory Neil Shapiro 			checkpath = false;
14306f25ae9SGregory Neil Shapiro 		}
14406f25ae9SGregory Neil Shapiro 		else
14506f25ae9SGregory Neil Shapiro 		{
14606f25ae9SGregory Neil Shapiro # if HASLSTAT
14706f25ae9SGregory Neil Shapiro 			/* Need lstat() information if called stat() before */
14806f25ae9SGregory Neil Shapiro 			if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0)
14906f25ae9SGregory Neil Shapiro 			{
15006f25ae9SGregory Neil Shapiro 				ret = errno;
15106f25ae9SGregory Neil Shapiro 				if (tTd(44, 4))
15240266059SGregory Neil Shapiro 					sm_dprintf("\t%s\n", sm_errstring(ret));
15306f25ae9SGregory Neil Shapiro 				return ret;
15406f25ae9SGregory Neil Shapiro 			}
15506f25ae9SGregory Neil Shapiro # endif /* HASLSTAT */
15606f25ae9SGregory Neil Shapiro 			/* directory is writable: disallow links */
15706f25ae9SGregory Neil Shapiro 			flags |= SFF_NOLINK;
15806f25ae9SGregory Neil Shapiro 		}
15906f25ae9SGregory Neil Shapiro 	}
16006f25ae9SGregory Neil Shapiro 
16106f25ae9SGregory Neil Shapiro 	if (checkpath)
16206f25ae9SGregory Neil Shapiro 	{
16306f25ae9SGregory Neil Shapiro 		int ret;
16406f25ae9SGregory Neil Shapiro 
16506f25ae9SGregory Neil Shapiro 		p = strrchr(fn, '/');
16606f25ae9SGregory Neil Shapiro 		if (p == NULL)
16706f25ae9SGregory Neil Shapiro 		{
16806f25ae9SGregory Neil Shapiro 			ret = safedirpath(".", uid, gid, user, flags, 0, 0);
16906f25ae9SGregory Neil Shapiro 		}
17006f25ae9SGregory Neil Shapiro 		else
17106f25ae9SGregory Neil Shapiro 		{
17206f25ae9SGregory Neil Shapiro 			*p = '\0';
17306f25ae9SGregory Neil Shapiro 			ret = safedirpath(fn, uid, gid, user, flags, 0, 0);
17406f25ae9SGregory Neil Shapiro 			*p = '/';
17506f25ae9SGregory Neil Shapiro 		}
17606f25ae9SGregory Neil Shapiro 		if (ret != 0)
17706f25ae9SGregory Neil Shapiro 			return ret;
17806f25ae9SGregory Neil Shapiro 	}
17906f25ae9SGregory Neil Shapiro 
18006f25ae9SGregory Neil Shapiro 	/*
18106f25ae9SGregory Neil Shapiro 	**  If the target file doesn't exist, check the directory to
18206f25ae9SGregory Neil Shapiro 	**  ensure that it is writable by this user.
18306f25ae9SGregory Neil Shapiro 	*/
18406f25ae9SGregory Neil Shapiro 
18506f25ae9SGregory Neil Shapiro 	if (file_errno != 0)
18606f25ae9SGregory Neil Shapiro 	{
18706f25ae9SGregory Neil Shapiro 		int ret = file_errno;
18806f25ae9SGregory Neil Shapiro 		char *dir = fn;
18906f25ae9SGregory Neil Shapiro 
19006f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
19140266059SGregory Neil Shapiro 			sm_dprintf("\t%s\n", sm_errstring(ret));
19206f25ae9SGregory Neil Shapiro 
19306f25ae9SGregory Neil Shapiro 		errno = 0;
19406f25ae9SGregory Neil Shapiro 		if (!bitset(SFF_CREAT, flags) || file_errno != ENOENT)
19506f25ae9SGregory Neil Shapiro 			return ret;
19606f25ae9SGregory Neil Shapiro 
19706f25ae9SGregory Neil Shapiro 		/* check to see if legal to create the file */
19806f25ae9SGregory Neil Shapiro 		p = strrchr(dir, '/');
19906f25ae9SGregory Neil Shapiro 		if (p == NULL)
20006f25ae9SGregory Neil Shapiro 			dir = ".";
20106f25ae9SGregory Neil Shapiro 		else if (p == dir)
20206f25ae9SGregory Neil Shapiro 			dir = "/";
20306f25ae9SGregory Neil Shapiro 		else
20406f25ae9SGregory Neil Shapiro 			*p = '\0';
20506f25ae9SGregory Neil Shapiro 		if (stat(dir, &stbuf) >= 0)
20606f25ae9SGregory Neil Shapiro 		{
20706f25ae9SGregory Neil Shapiro 			int md = S_IWRITE|S_IEXEC;
20806f25ae9SGregory Neil Shapiro 
20940266059SGregory Neil Shapiro 			ret = 0;
21006f25ae9SGregory Neil Shapiro 			if (stbuf.st_uid == uid)
21106f25ae9SGregory Neil Shapiro 				/* EMPTY */
21206f25ae9SGregory Neil Shapiro 				;
21306f25ae9SGregory Neil Shapiro 			else if (uid == 0 && stbuf.st_uid == TrustedUid)
21406f25ae9SGregory Neil Shapiro 				/* EMPTY */
21506f25ae9SGregory Neil Shapiro 				;
21606f25ae9SGregory Neil Shapiro 			else
21706f25ae9SGregory Neil Shapiro 			{
21806f25ae9SGregory Neil Shapiro 				md >>= 3;
21906f25ae9SGregory Neil Shapiro 				if (stbuf.st_gid == gid)
22006f25ae9SGregory Neil Shapiro 					/* EMPTY */
22106f25ae9SGregory Neil Shapiro 					;
22206f25ae9SGregory Neil Shapiro # ifndef NO_GROUP_SET
22306f25ae9SGregory Neil Shapiro 				else if (user != NULL && !DontInitGroups &&
22406f25ae9SGregory Neil Shapiro 					 ((gr != NULL &&
22506f25ae9SGregory Neil Shapiro 					   gr->gr_gid == stbuf.st_gid) ||
22606f25ae9SGregory Neil Shapiro 					  (gr = getgrgid(stbuf.st_gid)) != NULL))
22706f25ae9SGregory Neil Shapiro 				{
22806f25ae9SGregory Neil Shapiro 					register char **gp;
22906f25ae9SGregory Neil Shapiro 
23006f25ae9SGregory Neil Shapiro 					for (gp = gr->gr_mem; *gp != NULL; gp++)
23106f25ae9SGregory Neil Shapiro 						if (strcmp(*gp, user) == 0)
23206f25ae9SGregory Neil Shapiro 							break;
23306f25ae9SGregory Neil Shapiro 					if (*gp == NULL)
23406f25ae9SGregory Neil Shapiro 						md >>= 3;
23506f25ae9SGregory Neil Shapiro 				}
23606f25ae9SGregory Neil Shapiro # endif /* ! NO_GROUP_SET */
23706f25ae9SGregory Neil Shapiro 				else
23806f25ae9SGregory Neil Shapiro 					md >>= 3;
23906f25ae9SGregory Neil Shapiro 			}
24006f25ae9SGregory Neil Shapiro 			if ((stbuf.st_mode & md) != md)
24140266059SGregory Neil Shapiro 				ret = errno = EACCES;
24206f25ae9SGregory Neil Shapiro 		}
24340266059SGregory Neil Shapiro 		else
24406f25ae9SGregory Neil Shapiro 			ret = errno;
24506f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
24640266059SGregory Neil Shapiro 			sm_dprintf("\t[final dir %s uid %d mode %lo] %s\n",
24740266059SGregory Neil Shapiro 				dir, (int) stbuf.st_uid,
24840266059SGregory Neil Shapiro 				(unsigned long) stbuf.st_mode,
24940266059SGregory Neil Shapiro 				sm_errstring(ret));
25006f25ae9SGregory Neil Shapiro 		if (p != NULL)
25106f25ae9SGregory Neil Shapiro 			*p = '/';
25206f25ae9SGregory Neil Shapiro 		st->st_mode = ST_MODE_NOFILE;
25306f25ae9SGregory Neil Shapiro 		return ret;
25406f25ae9SGregory Neil Shapiro 	}
25506f25ae9SGregory Neil Shapiro 
25606f25ae9SGregory Neil Shapiro # ifdef S_ISLNK
25706f25ae9SGregory Neil Shapiro 	if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode))
25806f25ae9SGregory Neil Shapiro 	{
25906f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
26040266059SGregory Neil Shapiro 			sm_dprintf("\t[slink mode %lo]\tE_SM_NOSLINK\n",
26140266059SGregory Neil Shapiro 				(unsigned long) st->st_mode);
26206f25ae9SGregory Neil Shapiro 		return E_SM_NOSLINK;
26306f25ae9SGregory Neil Shapiro 	}
26406f25ae9SGregory Neil Shapiro # endif /* S_ISLNK */
26506f25ae9SGregory Neil Shapiro 	if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode))
26606f25ae9SGregory Neil Shapiro 	{
26706f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
26840266059SGregory Neil Shapiro 			sm_dprintf("\t[non-reg mode %lo]\tE_SM_REGONLY\n",
26940266059SGregory Neil Shapiro 				(unsigned long) st->st_mode);
27006f25ae9SGregory Neil Shapiro 		return E_SM_REGONLY;
27106f25ae9SGregory Neil Shapiro 	}
27206f25ae9SGregory Neil Shapiro 	if (bitset(SFF_NOGWFILES, flags) &&
27306f25ae9SGregory Neil Shapiro 	    bitset(S_IWGRP, st->st_mode))
27406f25ae9SGregory Neil Shapiro 	{
27506f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
27640266059SGregory Neil Shapiro 			sm_dprintf("\t[write bits %lo]\tE_SM_GWFILE\n",
27740266059SGregory Neil Shapiro 				(unsigned long) st->st_mode);
27806f25ae9SGregory Neil Shapiro 		return E_SM_GWFILE;
27906f25ae9SGregory Neil Shapiro 	}
28006f25ae9SGregory Neil Shapiro 	if (bitset(SFF_NOWWFILES, flags) &&
28106f25ae9SGregory Neil Shapiro 	    bitset(S_IWOTH, st->st_mode))
28206f25ae9SGregory Neil Shapiro 	{
28306f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
28440266059SGregory Neil Shapiro 			sm_dprintf("\t[write bits %lo]\tE_SM_WWFILE\n",
28540266059SGregory Neil Shapiro 				(unsigned long) st->st_mode);
28606f25ae9SGregory Neil Shapiro 		return E_SM_WWFILE;
28706f25ae9SGregory Neil Shapiro 	}
28806f25ae9SGregory Neil Shapiro 	if (bitset(SFF_NOGRFILES, flags) && bitset(S_IRGRP, st->st_mode))
28906f25ae9SGregory Neil Shapiro 	{
29006f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
29140266059SGregory Neil Shapiro 			sm_dprintf("\t[read bits %lo]\tE_SM_GRFILE\n",
29240266059SGregory Neil Shapiro 				(unsigned long) st->st_mode);
29306f25ae9SGregory Neil Shapiro 		return E_SM_GRFILE;
29406f25ae9SGregory Neil Shapiro 	}
29506f25ae9SGregory Neil Shapiro 	if (bitset(SFF_NOWRFILES, flags) && bitset(S_IROTH, st->st_mode))
29606f25ae9SGregory Neil Shapiro 	{
29706f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
29840266059SGregory Neil Shapiro 			sm_dprintf("\t[read bits %lo]\tE_SM_WRFILE\n",
29940266059SGregory Neil Shapiro 				(unsigned long) st->st_mode);
30006f25ae9SGregory Neil Shapiro 		return E_SM_WRFILE;
30106f25ae9SGregory Neil Shapiro 	}
30206f25ae9SGregory Neil Shapiro 	if (!bitset(SFF_EXECOK, flags) &&
30306f25ae9SGregory Neil Shapiro 	    bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) &&
30406f25ae9SGregory Neil Shapiro 	    bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode))
30506f25ae9SGregory Neil Shapiro 	{
30606f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
307e92d3f3fSGregory Neil Shapiro 			sm_dprintf("\t[exec bits %lo]\tE_SM_ISEXEC\n",
30840266059SGregory Neil Shapiro 				(unsigned long) st->st_mode);
30906f25ae9SGregory Neil Shapiro 		return E_SM_ISEXEC;
31006f25ae9SGregory Neil Shapiro 	}
31106f25ae9SGregory Neil Shapiro 	if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1)
31206f25ae9SGregory Neil Shapiro 	{
31306f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
31440266059SGregory Neil Shapiro 			sm_dprintf("\t[link count %d]\tE_SM_NOHLINK\n",
31506f25ae9SGregory Neil Shapiro 				(int) st->st_nlink);
31606f25ae9SGregory Neil Shapiro 		return E_SM_NOHLINK;
31706f25ae9SGregory Neil Shapiro 	}
31806f25ae9SGregory Neil Shapiro 
31906f25ae9SGregory Neil Shapiro 	if (uid == 0 && bitset(SFF_OPENASROOT, flags))
32006f25ae9SGregory Neil Shapiro 		/* EMPTY */
32106f25ae9SGregory Neil Shapiro 		;
32206f25ae9SGregory Neil Shapiro 	else if (uid == 0 && !bitset(SFF_ROOTOK, flags))
32306f25ae9SGregory Neil Shapiro 		mode >>= 6;
32406f25ae9SGregory Neil Shapiro 	else if (st->st_uid == uid)
32506f25ae9SGregory Neil Shapiro 		/* EMPTY */
32606f25ae9SGregory Neil Shapiro 		;
32706f25ae9SGregory Neil Shapiro 	else if (uid == 0 && st->st_uid == TrustedUid)
32806f25ae9SGregory Neil Shapiro 		/* EMPTY */
32906f25ae9SGregory Neil Shapiro 		;
33006f25ae9SGregory Neil Shapiro 	else
33106f25ae9SGregory Neil Shapiro 	{
33206f25ae9SGregory Neil Shapiro 		mode >>= 3;
33306f25ae9SGregory Neil Shapiro 		if (st->st_gid == gid)
33406f25ae9SGregory Neil Shapiro 			/* EMPTY */
33506f25ae9SGregory Neil Shapiro 			;
33606f25ae9SGregory Neil Shapiro # ifndef NO_GROUP_SET
33706f25ae9SGregory Neil Shapiro 		else if (user != NULL && !DontInitGroups &&
33806f25ae9SGregory Neil Shapiro 			 ((gr != NULL && gr->gr_gid == st->st_gid) ||
33906f25ae9SGregory Neil Shapiro 			  (gr = getgrgid(st->st_gid)) != NULL))
34006f25ae9SGregory Neil Shapiro 		{
34106f25ae9SGregory Neil Shapiro 			register char **gp;
34206f25ae9SGregory Neil Shapiro 
34306f25ae9SGregory Neil Shapiro 			for (gp = gr->gr_mem; *gp != NULL; gp++)
34406f25ae9SGregory Neil Shapiro 				if (strcmp(*gp, user) == 0)
34506f25ae9SGregory Neil Shapiro 					break;
34606f25ae9SGregory Neil Shapiro 			if (*gp == NULL)
34706f25ae9SGregory Neil Shapiro 				mode >>= 3;
34806f25ae9SGregory Neil Shapiro 		}
34906f25ae9SGregory Neil Shapiro # endif /* ! NO_GROUP_SET */
35006f25ae9SGregory Neil Shapiro 		else
35106f25ae9SGregory Neil Shapiro 			mode >>= 3;
35206f25ae9SGregory Neil Shapiro 	}
35306f25ae9SGregory Neil Shapiro 	if (tTd(44, 4))
35440266059SGregory Neil Shapiro 		sm_dprintf("\t[uid %d, nlink %d, stat %lo, mode %lo] ",
35506f25ae9SGregory Neil Shapiro 			(int) st->st_uid, (int) st->st_nlink,
35640266059SGregory Neil Shapiro 			(unsigned long) st->st_mode, (unsigned long) mode);
35706f25ae9SGregory Neil Shapiro 	if ((st->st_uid == uid || st->st_uid == 0 ||
35806f25ae9SGregory Neil Shapiro 	     st->st_uid == TrustedUid ||
35906f25ae9SGregory Neil Shapiro 	     !bitset(SFF_MUSTOWN, flags)) &&
36006f25ae9SGregory Neil Shapiro 	    (st->st_mode & mode) == mode)
36106f25ae9SGregory Neil Shapiro 	{
36206f25ae9SGregory Neil Shapiro 		if (tTd(44, 4))
36340266059SGregory Neil Shapiro 			sm_dprintf("\tOK\n");
36406f25ae9SGregory Neil Shapiro 		return 0;
36506f25ae9SGregory Neil Shapiro 	}
36606f25ae9SGregory Neil Shapiro 	if (tTd(44, 4))
36740266059SGregory Neil Shapiro 		sm_dprintf("\tEACCES\n");
36806f25ae9SGregory Neil Shapiro 	return EACCES;
36906f25ae9SGregory Neil Shapiro }
37040266059SGregory Neil Shapiro /*
37106f25ae9SGregory Neil Shapiro **  SAFEDIRPATH -- check to make sure a path to a directory is safe
37206f25ae9SGregory Neil Shapiro **
37306f25ae9SGregory Neil Shapiro **	Safe means not writable and owned by the right folks.
37406f25ae9SGregory Neil Shapiro **
37506f25ae9SGregory Neil Shapiro **	Parameters:
37606f25ae9SGregory Neil Shapiro **		fn -- filename to check.
37706f25ae9SGregory Neil Shapiro **		uid -- user id to compare against.
37806f25ae9SGregory Neil Shapiro **		gid -- group id to compare against.
37906f25ae9SGregory Neil Shapiro **		user -- user name to compare against (used for group
38006f25ae9SGregory Neil Shapiro **			sets).
38106f25ae9SGregory Neil Shapiro **		flags -- modifiers:
38206f25ae9SGregory Neil Shapiro **			SFF_ROOTOK -- ok to use root permissions to open.
38306f25ae9SGregory Neil Shapiro **			SFF_SAFEDIRPATH -- writable directories are considered
38406f25ae9SGregory Neil Shapiro **				to be fatal errors.
38506f25ae9SGregory Neil Shapiro **		level -- symlink recursive level.
38606f25ae9SGregory Neil Shapiro **		offset -- offset into fn to start checking from.
38706f25ae9SGregory Neil Shapiro **
38806f25ae9SGregory Neil Shapiro **	Returns:
38906f25ae9SGregory Neil Shapiro **		0 -- if the directory path is "safe".
39006f25ae9SGregory Neil Shapiro **		else -- an error number associated with the path.
39106f25ae9SGregory Neil Shapiro */
39206f25ae9SGregory Neil Shapiro 
39306f25ae9SGregory Neil Shapiro int
39406f25ae9SGregory Neil Shapiro safedirpath(fn, uid, gid, user, flags, level, offset)
39506f25ae9SGregory Neil Shapiro 	char *fn;
39606f25ae9SGregory Neil Shapiro 	UID_T uid;
39706f25ae9SGregory Neil Shapiro 	GID_T gid;
39806f25ae9SGregory Neil Shapiro 	char *user;
39906f25ae9SGregory Neil Shapiro 	long flags;
40006f25ae9SGregory Neil Shapiro 	int level;
40106f25ae9SGregory Neil Shapiro 	int offset;
40206f25ae9SGregory Neil Shapiro {
40306f25ae9SGregory Neil Shapiro 	int ret = 0;
40406f25ae9SGregory Neil Shapiro 	int mode = S_IWOTH;
40506f25ae9SGregory Neil Shapiro 	char save = '\0';
40606f25ae9SGregory Neil Shapiro 	char *saveptr = NULL;
40706f25ae9SGregory Neil Shapiro 	char *p, *enddir;
40806f25ae9SGregory Neil Shapiro 	register struct group *gr = NULL;
40994c01205SGregory Neil Shapiro 	char s[MAXLINKPATHLEN];
41006f25ae9SGregory Neil Shapiro 	struct stat stbuf;
41106f25ae9SGregory Neil Shapiro 
41206f25ae9SGregory Neil Shapiro 	/* make sure we aren't in a symlink loop */
41306f25ae9SGregory Neil Shapiro 	if (level > MAXSYMLINKS)
41406f25ae9SGregory Neil Shapiro 		return ELOOP;
41506f25ae9SGregory Neil Shapiro 
41640266059SGregory Neil Shapiro 	if (level < 0 || offset < 0 || offset > strlen(fn))
41740266059SGregory Neil Shapiro 		return EINVAL;
41840266059SGregory Neil Shapiro 
41906f25ae9SGregory Neil Shapiro 	/* special case root directory */
42006f25ae9SGregory Neil Shapiro 	if (*fn == '\0')
42106f25ae9SGregory Neil Shapiro 		fn = "/";
42206f25ae9SGregory Neil Shapiro 
42306f25ae9SGregory Neil Shapiro 	if (tTd(44, 4))
42440266059SGregory Neil Shapiro 		sm_dprintf("safedirpath(%s, uid=%ld, gid=%ld, flags=%lx, level=%d, offset=%d):\n",
42506f25ae9SGregory Neil Shapiro 			fn, (long) uid, (long) gid, flags, level, offset);
42606f25ae9SGregory Neil Shapiro 
42706f25ae9SGregory Neil Shapiro 	if (!bitnset(DBS_GROUPWRITABLEDIRPATHSAFE, DontBlameSendmail))
42806f25ae9SGregory Neil Shapiro 		mode |= S_IWGRP;
42906f25ae9SGregory Neil Shapiro 
43006f25ae9SGregory Neil Shapiro 	/* Make a modifiable copy of the filename */
43140266059SGregory Neil Shapiro 	if (sm_strlcpy(s, fn, sizeof s) >= sizeof s)
43206f25ae9SGregory Neil Shapiro 		return EINVAL;
43306f25ae9SGregory Neil Shapiro 
43406f25ae9SGregory Neil Shapiro 	p = s + offset;
43506f25ae9SGregory Neil Shapiro 	while (p != NULL)
43606f25ae9SGregory Neil Shapiro 	{
43706f25ae9SGregory Neil Shapiro 		/* put back character */
43806f25ae9SGregory Neil Shapiro 		if (saveptr != NULL)
43906f25ae9SGregory Neil Shapiro 		{
44006f25ae9SGregory Neil Shapiro 			*saveptr = save;
44106f25ae9SGregory Neil Shapiro 			saveptr = NULL;
44206f25ae9SGregory Neil Shapiro 			p++;
44306f25ae9SGregory Neil Shapiro 		}
44406f25ae9SGregory Neil Shapiro 
44506f25ae9SGregory Neil Shapiro 		if (*p == '\0')
44606f25ae9SGregory Neil Shapiro 			break;
44706f25ae9SGregory Neil Shapiro 
44806f25ae9SGregory Neil Shapiro 		p = strchr(p, '/');
44906f25ae9SGregory Neil Shapiro 
45006f25ae9SGregory Neil Shapiro 		/* Special case for root directory */
45106f25ae9SGregory Neil Shapiro 		if (p == s)
45206f25ae9SGregory Neil Shapiro 		{
45306f25ae9SGregory Neil Shapiro 			save = *(p + 1);
45406f25ae9SGregory Neil Shapiro 			saveptr = p + 1;
45506f25ae9SGregory Neil Shapiro 			*(p + 1) = '\0';
45606f25ae9SGregory Neil Shapiro 		}
45706f25ae9SGregory Neil Shapiro 		else if (p != NULL)
45806f25ae9SGregory Neil Shapiro 		{
45906f25ae9SGregory Neil Shapiro 			save = *p;
46006f25ae9SGregory Neil Shapiro 			saveptr = p;
46106f25ae9SGregory Neil Shapiro 			*p = '\0';
46206f25ae9SGregory Neil Shapiro 		}
46306f25ae9SGregory Neil Shapiro 
46406f25ae9SGregory Neil Shapiro 		/* Heuristic: . and .. have already been checked */
46506f25ae9SGregory Neil Shapiro 		enddir = strrchr(s, '/');
46606f25ae9SGregory Neil Shapiro 		if (enddir != NULL &&
46706f25ae9SGregory Neil Shapiro 		    (strcmp(enddir, "/..") == 0 ||
46806f25ae9SGregory Neil Shapiro 		     strcmp(enddir, "/.") == 0))
46906f25ae9SGregory Neil Shapiro 			continue;
47006f25ae9SGregory Neil Shapiro 
47106f25ae9SGregory Neil Shapiro 		if (tTd(44, 20))
47240266059SGregory Neil Shapiro 			sm_dprintf("\t[dir %s]\n", s);
47306f25ae9SGregory Neil Shapiro 
47406f25ae9SGregory Neil Shapiro # if HASLSTAT
47506f25ae9SGregory Neil Shapiro 		ret = lstat(s, &stbuf);
47606f25ae9SGregory Neil Shapiro # else /* HASLSTAT */
47706f25ae9SGregory Neil Shapiro 		ret = stat(s, &stbuf);
47806f25ae9SGregory Neil Shapiro # endif /* HASLSTAT */
47906f25ae9SGregory Neil Shapiro 		if (ret < 0)
48006f25ae9SGregory Neil Shapiro 		{
48106f25ae9SGregory Neil Shapiro 			ret = errno;
48206f25ae9SGregory Neil Shapiro 			break;
48306f25ae9SGregory Neil Shapiro 		}
48406f25ae9SGregory Neil Shapiro 
48506f25ae9SGregory Neil Shapiro # ifdef S_ISLNK
48606f25ae9SGregory Neil Shapiro 		/* Follow symlinks */
48706f25ae9SGregory Neil Shapiro 		if (S_ISLNK(stbuf.st_mode))
48806f25ae9SGregory Neil Shapiro 		{
48994c01205SGregory Neil Shapiro 			int linklen;
49006f25ae9SGregory Neil Shapiro 			char *target;
49194c01205SGregory Neil Shapiro 			char buf[MAXPATHLEN];
492b6bacd31SGregory Neil Shapiro 			char fullbuf[MAXLINKPATHLEN];
49306f25ae9SGregory Neil Shapiro 
49406f25ae9SGregory Neil Shapiro 			memset(buf, '\0', sizeof buf);
49594c01205SGregory Neil Shapiro 			linklen = readlink(s, buf, sizeof buf);
49694c01205SGregory Neil Shapiro 			if (linklen < 0)
49706f25ae9SGregory Neil Shapiro 			{
49806f25ae9SGregory Neil Shapiro 				ret = errno;
49906f25ae9SGregory Neil Shapiro 				break;
50006f25ae9SGregory Neil Shapiro 			}
50194c01205SGregory Neil Shapiro 			if (linklen >= sizeof buf)
50294c01205SGregory Neil Shapiro 			{
50394c01205SGregory Neil Shapiro 				/* file name too long for buffer */
50494c01205SGregory Neil Shapiro 				ret = errno = EINVAL;
50594c01205SGregory Neil Shapiro 				break;
50694c01205SGregory Neil Shapiro 			}
50706f25ae9SGregory Neil Shapiro 
50806f25ae9SGregory Neil Shapiro 			offset = 0;
50906f25ae9SGregory Neil Shapiro 			if (*buf == '/')
51006f25ae9SGregory Neil Shapiro 			{
51106f25ae9SGregory Neil Shapiro 				target = buf;
51206f25ae9SGregory Neil Shapiro 
51306f25ae9SGregory Neil Shapiro 				/* If path is the same, avoid rechecks */
51406f25ae9SGregory Neil Shapiro 				while (s[offset] == buf[offset] &&
51506f25ae9SGregory Neil Shapiro 				       s[offset] != '\0')
51606f25ae9SGregory Neil Shapiro 					offset++;
51706f25ae9SGregory Neil Shapiro 
51806f25ae9SGregory Neil Shapiro 				if (s[offset] == '\0' && buf[offset] == '\0')
51906f25ae9SGregory Neil Shapiro 				{
52006f25ae9SGregory Neil Shapiro 					/* strings match, symlink loop */
52106f25ae9SGregory Neil Shapiro 					return ELOOP;
52206f25ae9SGregory Neil Shapiro 				}
52306f25ae9SGregory Neil Shapiro 
52406f25ae9SGregory Neil Shapiro 				/* back off from the mismatch */
52506f25ae9SGregory Neil Shapiro 				if (offset > 0)
52606f25ae9SGregory Neil Shapiro 					offset--;
52706f25ae9SGregory Neil Shapiro 
52806f25ae9SGregory Neil Shapiro 				/* Make sure we are at a directory break */
52906f25ae9SGregory Neil Shapiro 				if (offset > 0 &&
53006f25ae9SGregory Neil Shapiro 				    s[offset] != '/' &&
53106f25ae9SGregory Neil Shapiro 				    s[offset] != '\0')
53206f25ae9SGregory Neil Shapiro 				{
53306f25ae9SGregory Neil Shapiro 					while (buf[offset] != '/' &&
53406f25ae9SGregory Neil Shapiro 					       offset > 0)
53506f25ae9SGregory Neil Shapiro 						offset--;
53606f25ae9SGregory Neil Shapiro 				}
53706f25ae9SGregory Neil Shapiro 				if (offset > 0 &&
53806f25ae9SGregory Neil Shapiro 				    s[offset] == '/' &&
53906f25ae9SGregory Neil Shapiro 				    buf[offset] == '/')
54006f25ae9SGregory Neil Shapiro 				{
54106f25ae9SGregory Neil Shapiro 					/* Include the trailing slash */
54206f25ae9SGregory Neil Shapiro 					offset++;
54306f25ae9SGregory Neil Shapiro 				}
54406f25ae9SGregory Neil Shapiro 			}
54506f25ae9SGregory Neil Shapiro 			else
54606f25ae9SGregory Neil Shapiro 			{
54706f25ae9SGregory Neil Shapiro 				char *sptr;
54806f25ae9SGregory Neil Shapiro 
54906f25ae9SGregory Neil Shapiro 				sptr = strrchr(s, '/');
55006f25ae9SGregory Neil Shapiro 				if (sptr != NULL)
55106f25ae9SGregory Neil Shapiro 				{
55206f25ae9SGregory Neil Shapiro 					*sptr = '\0';
55306f25ae9SGregory Neil Shapiro 					offset = sptr + 1 - s;
55440266059SGregory Neil Shapiro 					if (sm_strlcpyn(fullbuf,
55540266059SGregory Neil Shapiro 							sizeof fullbuf, 2,
55640266059SGregory Neil Shapiro 							s, "/") >=
55740266059SGregory Neil Shapiro 						sizeof fullbuf ||
55840266059SGregory Neil Shapiro 					    sm_strlcat(fullbuf, buf,
55940266059SGregory Neil Shapiro 						       sizeof fullbuf) >=
56040266059SGregory Neil Shapiro 						sizeof fullbuf)
56106f25ae9SGregory Neil Shapiro 					{
56206f25ae9SGregory Neil Shapiro 						ret = EINVAL;
56306f25ae9SGregory Neil Shapiro 						break;
56406f25ae9SGregory Neil Shapiro 					}
56506f25ae9SGregory Neil Shapiro 					*sptr = '/';
56606f25ae9SGregory Neil Shapiro 				}
56706f25ae9SGregory Neil Shapiro 				else
56806f25ae9SGregory Neil Shapiro 				{
56940266059SGregory Neil Shapiro 					if (sm_strlcpy(fullbuf, buf,
57040266059SGregory Neil Shapiro 						       sizeof fullbuf) >=
57140266059SGregory Neil Shapiro 						sizeof fullbuf)
57206f25ae9SGregory Neil Shapiro 					{
57306f25ae9SGregory Neil Shapiro 						ret = EINVAL;
57406f25ae9SGregory Neil Shapiro 						break;
57506f25ae9SGregory Neil Shapiro 					}
57606f25ae9SGregory Neil Shapiro 				}
57706f25ae9SGregory Neil Shapiro 				target = fullbuf;
57806f25ae9SGregory Neil Shapiro 			}
57906f25ae9SGregory Neil Shapiro 			ret = safedirpath(target, uid, gid, user, flags,
58006f25ae9SGregory Neil Shapiro 					  level + 1, offset);
58106f25ae9SGregory Neil Shapiro 			if (ret != 0)
58206f25ae9SGregory Neil Shapiro 				break;
58306f25ae9SGregory Neil Shapiro 
58406f25ae9SGregory Neil Shapiro 			/* Don't check permissions on the link file itself */
58506f25ae9SGregory Neil Shapiro 			continue;
58606f25ae9SGregory Neil Shapiro 		}
58706f25ae9SGregory Neil Shapiro #endif /* S_ISLNK */
58806f25ae9SGregory Neil Shapiro 
58906f25ae9SGregory Neil Shapiro 		if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) &&
59006f25ae9SGregory Neil Shapiro #ifdef S_ISVTX
59106f25ae9SGregory Neil Shapiro 		    !(bitnset(DBS_TRUSTSTICKYBIT, DontBlameSendmail) &&
59206f25ae9SGregory Neil Shapiro 		      bitset(S_ISVTX, stbuf.st_mode)) &&
59306f25ae9SGregory Neil Shapiro #endif /* S_ISVTX */
59406f25ae9SGregory Neil Shapiro 		    bitset(mode, stbuf.st_mode))
59506f25ae9SGregory Neil Shapiro 		{
59606f25ae9SGregory Neil Shapiro 			if (tTd(44, 4))
59740266059SGregory Neil Shapiro 				sm_dprintf("\t[dir %s] mode %lo ",
59840266059SGregory Neil Shapiro 					s, (unsigned long) stbuf.st_mode);
59906f25ae9SGregory Neil Shapiro 			if (bitset(SFF_SAFEDIRPATH, flags))
60006f25ae9SGregory Neil Shapiro 			{
60106f25ae9SGregory Neil Shapiro 				if (bitset(S_IWOTH, stbuf.st_mode))
60206f25ae9SGregory Neil Shapiro 					ret = E_SM_WWDIR;
60306f25ae9SGregory Neil Shapiro 				else
60406f25ae9SGregory Neil Shapiro 					ret = E_SM_GWDIR;
60506f25ae9SGregory Neil Shapiro 				if (tTd(44, 4))
60640266059SGregory Neil Shapiro 					sm_dprintf("FATAL\n");
60706f25ae9SGregory Neil Shapiro 				break;
60806f25ae9SGregory Neil Shapiro 			}
60906f25ae9SGregory Neil Shapiro 			if (tTd(44, 4))
61040266059SGregory Neil Shapiro 				sm_dprintf("WARNING\n");
61106f25ae9SGregory Neil Shapiro 			if (Verbose > 1)
61206f25ae9SGregory Neil Shapiro 				message("051 WARNING: %s writable directory %s",
61306f25ae9SGregory Neil Shapiro 					bitset(S_IWOTH, stbuf.st_mode)
61406f25ae9SGregory Neil Shapiro 					   ? "World"
61506f25ae9SGregory Neil Shapiro 					   : "Group",
61606f25ae9SGregory Neil Shapiro 					s);
61706f25ae9SGregory Neil Shapiro 		}
61806f25ae9SGregory Neil Shapiro 		if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags))
61906f25ae9SGregory Neil Shapiro 		{
62006f25ae9SGregory Neil Shapiro 			if (bitset(S_IXOTH, stbuf.st_mode))
62106f25ae9SGregory Neil Shapiro 				continue;
62206f25ae9SGregory Neil Shapiro 			ret = EACCES;
62306f25ae9SGregory Neil Shapiro 			break;
62406f25ae9SGregory Neil Shapiro 		}
62506f25ae9SGregory Neil Shapiro 
62606f25ae9SGregory Neil Shapiro 		/*
62706f25ae9SGregory Neil Shapiro 		**  Let OS determine access to file if we are not
62806f25ae9SGregory Neil Shapiro 		**  running as a privileged user.  This allows ACLs
62906f25ae9SGregory Neil Shapiro 		**  to work.  Also, if opening as root, assume we can
63006f25ae9SGregory Neil Shapiro 		**  scan the directory.
63106f25ae9SGregory Neil Shapiro 		*/
63206f25ae9SGregory Neil Shapiro 		if (geteuid() != 0 || bitset(SFF_OPENASROOT, flags))
63306f25ae9SGregory Neil Shapiro 			continue;
63406f25ae9SGregory Neil Shapiro 
63506f25ae9SGregory Neil Shapiro 		if (stbuf.st_uid == uid &&
63606f25ae9SGregory Neil Shapiro 		    bitset(S_IXUSR, stbuf.st_mode))
63706f25ae9SGregory Neil Shapiro 			continue;
63806f25ae9SGregory Neil Shapiro 		if (stbuf.st_gid == gid &&
63906f25ae9SGregory Neil Shapiro 		    bitset(S_IXGRP, stbuf.st_mode))
64006f25ae9SGregory Neil Shapiro 			continue;
64106f25ae9SGregory Neil Shapiro # ifndef NO_GROUP_SET
64206f25ae9SGregory Neil Shapiro 		if (user != NULL && !DontInitGroups &&
64306f25ae9SGregory Neil Shapiro 		    ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
64406f25ae9SGregory Neil Shapiro 		     (gr = getgrgid(stbuf.st_gid)) != NULL))
64506f25ae9SGregory Neil Shapiro 		{
64606f25ae9SGregory Neil Shapiro 			register char **gp;
64706f25ae9SGregory Neil Shapiro 
64806f25ae9SGregory Neil Shapiro 			for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
64906f25ae9SGregory Neil Shapiro 				if (strcmp(*gp, user) == 0)
65006f25ae9SGregory Neil Shapiro 					break;
65106f25ae9SGregory Neil Shapiro 			if (gp != NULL && *gp != NULL &&
65206f25ae9SGregory Neil Shapiro 			    bitset(S_IXGRP, stbuf.st_mode))
65306f25ae9SGregory Neil Shapiro 				continue;
65406f25ae9SGregory Neil Shapiro 		}
65506f25ae9SGregory Neil Shapiro # endif /* ! NO_GROUP_SET */
65606f25ae9SGregory Neil Shapiro 		if (!bitset(S_IXOTH, stbuf.st_mode))
65706f25ae9SGregory Neil Shapiro 		{
65806f25ae9SGregory Neil Shapiro 			ret = EACCES;
65906f25ae9SGregory Neil Shapiro 			break;
66006f25ae9SGregory Neil Shapiro 		}
66106f25ae9SGregory Neil Shapiro 	}
66206f25ae9SGregory Neil Shapiro 	if (tTd(44, 4))
66340266059SGregory Neil Shapiro 		sm_dprintf("\t[dir %s] %s\n", fn,
66440266059SGregory Neil Shapiro 			ret == 0 ? "OK" : sm_errstring(ret));
66506f25ae9SGregory Neil Shapiro 	return ret;
66606f25ae9SGregory Neil Shapiro }
66740266059SGregory Neil Shapiro /*
66806f25ae9SGregory Neil Shapiro **  SAFEOPEN -- do a file open with extra checking
66906f25ae9SGregory Neil Shapiro **
67006f25ae9SGregory Neil Shapiro **	Parameters:
67106f25ae9SGregory Neil Shapiro **		fn -- the file name to open.
67206f25ae9SGregory Neil Shapiro **		omode -- the open-style mode flags.
67306f25ae9SGregory Neil Shapiro **		cmode -- the create-style mode flags.
67406f25ae9SGregory Neil Shapiro **		sff -- safefile flags.
67506f25ae9SGregory Neil Shapiro **
67606f25ae9SGregory Neil Shapiro **	Returns:
67706f25ae9SGregory Neil Shapiro **		Same as open.
67806f25ae9SGregory Neil Shapiro */
67906f25ae9SGregory Neil Shapiro 
68006f25ae9SGregory Neil Shapiro int
68106f25ae9SGregory Neil Shapiro safeopen(fn, omode, cmode, sff)
68206f25ae9SGregory Neil Shapiro 	char *fn;
68306f25ae9SGregory Neil Shapiro 	int omode;
68406f25ae9SGregory Neil Shapiro 	int cmode;
68506f25ae9SGregory Neil Shapiro 	long sff;
68606f25ae9SGregory Neil Shapiro {
687e92d3f3fSGregory Neil Shapiro #if !NOFTRUNCATE
688e92d3f3fSGregory Neil Shapiro 	bool truncate;
689e92d3f3fSGregory Neil Shapiro #endif /* !NOFTRUNCATE */
69006f25ae9SGregory Neil Shapiro 	int rval;
69106f25ae9SGregory Neil Shapiro 	int fd;
69206f25ae9SGregory Neil Shapiro 	int smode;
69306f25ae9SGregory Neil Shapiro 	struct stat stb;
69406f25ae9SGregory Neil Shapiro 
69506f25ae9SGregory Neil Shapiro 	if (tTd(44, 10))
69640266059SGregory Neil Shapiro 		sm_dprintf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n",
69706f25ae9SGregory Neil Shapiro 			   fn, omode, cmode, sff);
69806f25ae9SGregory Neil Shapiro 
69906f25ae9SGregory Neil Shapiro 	if (bitset(O_CREAT, omode))
70006f25ae9SGregory Neil Shapiro 		sff |= SFF_CREAT;
70106f25ae9SGregory Neil Shapiro 	omode &= ~O_CREAT;
70206f25ae9SGregory Neil Shapiro 	switch (omode & O_ACCMODE)
70306f25ae9SGregory Neil Shapiro 	{
70406f25ae9SGregory Neil Shapiro 	  case O_RDONLY:
70506f25ae9SGregory Neil Shapiro 		smode = S_IREAD;
70606f25ae9SGregory Neil Shapiro 		break;
70706f25ae9SGregory Neil Shapiro 
70806f25ae9SGregory Neil Shapiro 	  case O_WRONLY:
70906f25ae9SGregory Neil Shapiro 		smode = S_IWRITE;
71006f25ae9SGregory Neil Shapiro 		break;
71106f25ae9SGregory Neil Shapiro 
71206f25ae9SGregory Neil Shapiro 	  case O_RDWR:
71306f25ae9SGregory Neil Shapiro 		smode = S_IREAD|S_IWRITE;
71406f25ae9SGregory Neil Shapiro 		break;
71506f25ae9SGregory Neil Shapiro 
71606f25ae9SGregory Neil Shapiro 	  default:
71706f25ae9SGregory Neil Shapiro 		smode = 0;
71806f25ae9SGregory Neil Shapiro 		break;
71906f25ae9SGregory Neil Shapiro 	}
72006f25ae9SGregory Neil Shapiro 	if (bitset(SFF_OPENASROOT, sff))
72106f25ae9SGregory Neil Shapiro 		rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName,
72206f25ae9SGregory Neil Shapiro 				sff, smode, &stb);
72306f25ae9SGregory Neil Shapiro 	else
72406f25ae9SGregory Neil Shapiro 		rval = safefile(fn, RealUid, RealGid, RealUserName,
72506f25ae9SGregory Neil Shapiro 				sff, smode, &stb);
72606f25ae9SGregory Neil Shapiro 	if (rval != 0)
72706f25ae9SGregory Neil Shapiro 	{
72806f25ae9SGregory Neil Shapiro 		errno = rval;
72906f25ae9SGregory Neil Shapiro 		return -1;
73006f25ae9SGregory Neil Shapiro 	}
73106f25ae9SGregory Neil Shapiro 	if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff))
73206f25ae9SGregory Neil Shapiro 		omode |= O_CREAT | (bitset(SFF_NOTEXCL, sff) ? 0 : O_EXCL);
73306f25ae9SGregory Neil Shapiro 	else if (bitset(SFF_CREAT, sff) && bitset(O_EXCL, omode))
73406f25ae9SGregory Neil Shapiro 	{
73506f25ae9SGregory Neil Shapiro 		/* The file exists so an exclusive create would fail */
73606f25ae9SGregory Neil Shapiro 		errno = EEXIST;
73706f25ae9SGregory Neil Shapiro 		return -1;
73806f25ae9SGregory Neil Shapiro 	}
73906f25ae9SGregory Neil Shapiro 
740e92d3f3fSGregory Neil Shapiro #if !NOFTRUNCATE
741e92d3f3fSGregory Neil Shapiro 	truncate = bitset(O_TRUNC, omode);
742e92d3f3fSGregory Neil Shapiro 	if (truncate)
743e92d3f3fSGregory Neil Shapiro 		omode &= ~O_TRUNC;
744e92d3f3fSGregory Neil Shapiro #endif /* !NOFTRUNCATE */
745e92d3f3fSGregory Neil Shapiro 
74606f25ae9SGregory Neil Shapiro 	fd = dfopen(fn, omode, cmode, sff);
74706f25ae9SGregory Neil Shapiro 	if (fd < 0)
74806f25ae9SGregory Neil Shapiro 		return fd;
74906f25ae9SGregory Neil Shapiro 	if (filechanged(fn, fd, &stb))
75006f25ae9SGregory Neil Shapiro 	{
75106f25ae9SGregory Neil Shapiro 		syserr("554 5.3.0 cannot open: file %s changed after open", fn);
75206f25ae9SGregory Neil Shapiro 		(void) close(fd);
75306f25ae9SGregory Neil Shapiro 		errno = E_SM_FILECHANGE;
75406f25ae9SGregory Neil Shapiro 		return -1;
75506f25ae9SGregory Neil Shapiro 	}
756e92d3f3fSGregory Neil Shapiro 
757e92d3f3fSGregory Neil Shapiro #if !NOFTRUNCATE
758e92d3f3fSGregory Neil Shapiro 	if (truncate &&
759e92d3f3fSGregory Neil Shapiro 	    ftruncate(fd, (off_t) 0) < 0)
760e92d3f3fSGregory Neil Shapiro 	{
761e92d3f3fSGregory Neil Shapiro 		int save_errno;
762e92d3f3fSGregory Neil Shapiro 
763e92d3f3fSGregory Neil Shapiro 		save_errno = errno;
764e92d3f3fSGregory Neil Shapiro 		syserr("554 5.3.0 cannot open: file %s could not be truncated",
765e92d3f3fSGregory Neil Shapiro 		       fn);
766e92d3f3fSGregory Neil Shapiro 		(void) close(fd);
767e92d3f3fSGregory Neil Shapiro 		errno = save_errno;
768e92d3f3fSGregory Neil Shapiro 		return -1;
769e92d3f3fSGregory Neil Shapiro 	}
770e92d3f3fSGregory Neil Shapiro #endif /* !NOFTRUNCATE */
771e92d3f3fSGregory Neil Shapiro 
77206f25ae9SGregory Neil Shapiro 	return fd;
77306f25ae9SGregory Neil Shapiro }
77440266059SGregory Neil Shapiro /*
77540266059SGregory Neil Shapiro **  SAFEFOPEN -- do a file open with extra checking
77640266059SGregory Neil Shapiro **
77740266059SGregory Neil Shapiro **	Parameters:
77840266059SGregory Neil Shapiro **		fn -- the file name to open.
77940266059SGregory Neil Shapiro **		omode -- the open-style mode flags.
78040266059SGregory Neil Shapiro **		cmode -- the create-style mode flags.
78140266059SGregory Neil Shapiro **		sff -- safefile flags.
78240266059SGregory Neil Shapiro **
78340266059SGregory Neil Shapiro **	Returns:
78440266059SGregory Neil Shapiro **		Same as fopen.
78540266059SGregory Neil Shapiro */
78640266059SGregory Neil Shapiro 
78740266059SGregory Neil Shapiro SM_FILE_T *
78840266059SGregory Neil Shapiro safefopen(fn, omode, cmode, sff)
78940266059SGregory Neil Shapiro 	char *fn;
79040266059SGregory Neil Shapiro 	int omode;
79140266059SGregory Neil Shapiro 	int cmode;
79240266059SGregory Neil Shapiro 	long sff;
79340266059SGregory Neil Shapiro {
79440266059SGregory Neil Shapiro 	int fd;
79540266059SGregory Neil Shapiro 	int save_errno;
79640266059SGregory Neil Shapiro 	SM_FILE_T *fp;
79740266059SGregory Neil Shapiro 	int fmode;
79840266059SGregory Neil Shapiro 
79940266059SGregory Neil Shapiro 	switch (omode & O_ACCMODE)
80040266059SGregory Neil Shapiro 	{
80140266059SGregory Neil Shapiro 	  case O_RDONLY:
80240266059SGregory Neil Shapiro 		fmode = SM_IO_RDONLY;
80340266059SGregory Neil Shapiro 		break;
80440266059SGregory Neil Shapiro 
80540266059SGregory Neil Shapiro 	  case O_WRONLY:
80640266059SGregory Neil Shapiro 		if (bitset(O_APPEND, omode))
80740266059SGregory Neil Shapiro 			fmode = SM_IO_APPEND;
80840266059SGregory Neil Shapiro 		else
80940266059SGregory Neil Shapiro 			fmode = SM_IO_WRONLY;
81040266059SGregory Neil Shapiro 		break;
81140266059SGregory Neil Shapiro 
81240266059SGregory Neil Shapiro 	  case O_RDWR:
81340266059SGregory Neil Shapiro 		if (bitset(O_TRUNC, omode))
81440266059SGregory Neil Shapiro 			fmode = SM_IO_RDWRTR;
81540266059SGregory Neil Shapiro 		else if (bitset(O_APPEND, omode))
81640266059SGregory Neil Shapiro 			fmode = SM_IO_APPENDRW;
81740266059SGregory Neil Shapiro 		else
81840266059SGregory Neil Shapiro 			fmode = SM_IO_RDWR;
81940266059SGregory Neil Shapiro 		break;
82040266059SGregory Neil Shapiro 
82140266059SGregory Neil Shapiro 	  default:
82240266059SGregory Neil Shapiro 		syserr("554 5.3.5 safefopen: unknown omode %o", omode);
82340266059SGregory Neil Shapiro 		fmode = 0;
82440266059SGregory Neil Shapiro 	}
82540266059SGregory Neil Shapiro 	fd = safeopen(fn, omode, cmode, sff);
82640266059SGregory Neil Shapiro 	if (fd < 0)
82740266059SGregory Neil Shapiro 	{
82840266059SGregory Neil Shapiro 		save_errno = errno;
82940266059SGregory Neil Shapiro 		if (tTd(44, 10))
83040266059SGregory Neil Shapiro 			sm_dprintf("safefopen: safeopen failed: %s\n",
83140266059SGregory Neil Shapiro 				   sm_errstring(errno));
83240266059SGregory Neil Shapiro 		errno = save_errno;
83340266059SGregory Neil Shapiro 		return NULL;
83440266059SGregory Neil Shapiro 	}
83540266059SGregory Neil Shapiro 	fp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
83640266059SGregory Neil Shapiro 			(void *) &fd, fmode, NULL);
83740266059SGregory Neil Shapiro 	if (fp != NULL)
83840266059SGregory Neil Shapiro 		return fp;
83940266059SGregory Neil Shapiro 
84040266059SGregory Neil Shapiro 	save_errno = errno;
84140266059SGregory Neil Shapiro 	if (tTd(44, 10))
84240266059SGregory Neil Shapiro 	{
84340266059SGregory Neil Shapiro 		sm_dprintf("safefopen: fdopen(%s, %d) failed: omode=%x, sff=%lx, err=%s\n",
84440266059SGregory Neil Shapiro 			   fn, fmode, omode, sff, sm_errstring(errno));
84540266059SGregory Neil Shapiro 	}
84640266059SGregory Neil Shapiro 	(void) close(fd);
84740266059SGregory Neil Shapiro 	errno = save_errno;
84840266059SGregory Neil Shapiro 	return NULL;
84940266059SGregory Neil Shapiro }
85040266059SGregory Neil Shapiro /*
85106f25ae9SGregory Neil Shapiro **  FILECHANGED -- check to see if file changed after being opened
85206f25ae9SGregory Neil Shapiro **
85306f25ae9SGregory Neil Shapiro **	Parameters:
85406f25ae9SGregory Neil Shapiro **		fn -- pathname of file to check.
85506f25ae9SGregory Neil Shapiro **		fd -- file descriptor to check.
85606f25ae9SGregory Neil Shapiro **		stb -- stat structure from before open.
85706f25ae9SGregory Neil Shapiro **
85806f25ae9SGregory Neil Shapiro **	Returns:
85940266059SGregory Neil Shapiro **		true -- if a problem was detected.
86040266059SGregory Neil Shapiro **		false -- if this file is still the same.
86106f25ae9SGregory Neil Shapiro */
86206f25ae9SGregory Neil Shapiro 
86306f25ae9SGregory Neil Shapiro bool
86406f25ae9SGregory Neil Shapiro filechanged(fn, fd, stb)
86506f25ae9SGregory Neil Shapiro 	char *fn;
86606f25ae9SGregory Neil Shapiro 	int fd;
86706f25ae9SGregory Neil Shapiro 	struct stat *stb;
86806f25ae9SGregory Neil Shapiro {
86906f25ae9SGregory Neil Shapiro 	struct stat sta;
87006f25ae9SGregory Neil Shapiro 
87106f25ae9SGregory Neil Shapiro 	if (stb->st_mode == ST_MODE_NOFILE)
87206f25ae9SGregory Neil Shapiro 	{
87306f25ae9SGregory Neil Shapiro # if HASLSTAT && BOGUS_O_EXCL
87406f25ae9SGregory Neil Shapiro 		/* only necessary if exclusive open follows symbolic links */
87506f25ae9SGregory Neil Shapiro 		if (lstat(fn, stb) < 0 || stb->st_nlink != 1)
87640266059SGregory Neil Shapiro 			return true;
87706f25ae9SGregory Neil Shapiro # else /* HASLSTAT && BOGUS_O_EXCL */
87840266059SGregory Neil Shapiro 		return false;
87906f25ae9SGregory Neil Shapiro # endif /* HASLSTAT && BOGUS_O_EXCL */
88006f25ae9SGregory Neil Shapiro 	}
88106f25ae9SGregory Neil Shapiro 	if (fstat(fd, &sta) < 0)
88240266059SGregory Neil Shapiro 		return true;
88306f25ae9SGregory Neil Shapiro 
88406f25ae9SGregory Neil Shapiro 	if (sta.st_nlink != stb->st_nlink ||
88506f25ae9SGregory Neil Shapiro 	    sta.st_dev != stb->st_dev ||
88606f25ae9SGregory Neil Shapiro 	    sta.st_ino != stb->st_ino ||
88706f25ae9SGregory Neil Shapiro # if HAS_ST_GEN && 0		/* AFS returns garbage in st_gen */
88806f25ae9SGregory Neil Shapiro 	    sta.st_gen != stb->st_gen ||
88906f25ae9SGregory Neil Shapiro # endif /* HAS_ST_GEN && 0 */
89006f25ae9SGregory Neil Shapiro 	    sta.st_uid != stb->st_uid ||
89106f25ae9SGregory Neil Shapiro 	    sta.st_gid != stb->st_gid)
89206f25ae9SGregory Neil Shapiro 	{
89306f25ae9SGregory Neil Shapiro 		if (tTd(44, 8))
89406f25ae9SGregory Neil Shapiro 		{
89540266059SGregory Neil Shapiro 			sm_dprintf("File changed after opening:\n");
89640266059SGregory Neil Shapiro 			sm_dprintf(" nlink	= %ld/%ld\n",
89706f25ae9SGregory Neil Shapiro 				(long) stb->st_nlink, (long) sta.st_nlink);
89840266059SGregory Neil Shapiro 			sm_dprintf(" dev	= %ld/%ld\n",
89906f25ae9SGregory Neil Shapiro 				(long) stb->st_dev, (long) sta.st_dev);
90040266059SGregory Neil Shapiro 			sm_dprintf(" ino	= %llu/%llu\n",
90140266059SGregory Neil Shapiro 				(ULONGLONG_T) stb->st_ino,
90240266059SGregory Neil Shapiro 				(ULONGLONG_T) sta.st_ino);
90306f25ae9SGregory Neil Shapiro # if HAS_ST_GEN
90440266059SGregory Neil Shapiro 			sm_dprintf(" gen	= %ld/%ld\n",
90506f25ae9SGregory Neil Shapiro 				(long) stb->st_gen, (long) sta.st_gen);
90606f25ae9SGregory Neil Shapiro # endif /* HAS_ST_GEN */
90740266059SGregory Neil Shapiro 			sm_dprintf(" uid	= %ld/%ld\n",
90806f25ae9SGregory Neil Shapiro 				(long) stb->st_uid, (long) sta.st_uid);
90940266059SGregory Neil Shapiro 			sm_dprintf(" gid	= %ld/%ld\n",
91006f25ae9SGregory Neil Shapiro 				(long) stb->st_gid, (long) sta.st_gid);
91106f25ae9SGregory Neil Shapiro 		}
91240266059SGregory Neil Shapiro 		return true;
91306f25ae9SGregory Neil Shapiro 	}
91406f25ae9SGregory Neil Shapiro 
91540266059SGregory Neil Shapiro 	return false;
91606f25ae9SGregory Neil Shapiro }
91740266059SGregory Neil Shapiro /*
91806f25ae9SGregory Neil Shapiro **  DFOPEN -- determined file open
91906f25ae9SGregory Neil Shapiro **
92006f25ae9SGregory Neil Shapiro **	This routine has the semantics of open, except that it will
92106f25ae9SGregory Neil Shapiro **	keep trying a few times to make this happen.  The idea is that
92206f25ae9SGregory Neil Shapiro **	on very loaded systems, we may run out of resources (inodes,
92306f25ae9SGregory Neil Shapiro **	whatever), so this tries to get around it.
92406f25ae9SGregory Neil Shapiro */
92506f25ae9SGregory Neil Shapiro 
92606f25ae9SGregory Neil Shapiro int
92706f25ae9SGregory Neil Shapiro dfopen(filename, omode, cmode, sff)
92806f25ae9SGregory Neil Shapiro 	char *filename;
92906f25ae9SGregory Neil Shapiro 	int omode;
93006f25ae9SGregory Neil Shapiro 	int cmode;
93106f25ae9SGregory Neil Shapiro 	long sff;
93206f25ae9SGregory Neil Shapiro {
93306f25ae9SGregory Neil Shapiro 	register int tries;
93406f25ae9SGregory Neil Shapiro 	int fd = -1;
93506f25ae9SGregory Neil Shapiro 	struct stat st;
93606f25ae9SGregory Neil Shapiro 
93706f25ae9SGregory Neil Shapiro 	for (tries = 0; tries < 10; tries++)
93806f25ae9SGregory Neil Shapiro 	{
93906f25ae9SGregory Neil Shapiro 		(void) sleep((unsigned) (10 * tries));
94006f25ae9SGregory Neil Shapiro 		errno = 0;
94106f25ae9SGregory Neil Shapiro 		fd = open(filename, omode, cmode);
94206f25ae9SGregory Neil Shapiro 		if (fd >= 0)
94306f25ae9SGregory Neil Shapiro 			break;
94406f25ae9SGregory Neil Shapiro 		switch (errno)
94506f25ae9SGregory Neil Shapiro 		{
94606f25ae9SGregory Neil Shapiro 		  case ENFILE:		/* system file table full */
94706f25ae9SGregory Neil Shapiro 		  case EINTR:		/* interrupted syscall */
94806f25ae9SGregory Neil Shapiro #ifdef ETXTBSY
94906f25ae9SGregory Neil Shapiro 		  case ETXTBSY:		/* Apollo: net file locked */
95006f25ae9SGregory Neil Shapiro #endif /* ETXTBSY */
95106f25ae9SGregory Neil Shapiro 			continue;
95206f25ae9SGregory Neil Shapiro 		}
95306f25ae9SGregory Neil Shapiro 		break;
95406f25ae9SGregory Neil Shapiro 	}
95506f25ae9SGregory Neil Shapiro 	if (!bitset(SFF_NOLOCK, sff) &&
95606f25ae9SGregory Neil Shapiro 	    fd >= 0 &&
95706f25ae9SGregory Neil Shapiro 	    fstat(fd, &st) >= 0 &&
95806f25ae9SGregory Neil Shapiro 	    S_ISREG(st.st_mode))
95906f25ae9SGregory Neil Shapiro 	{
96006f25ae9SGregory Neil Shapiro 		int locktype;
96106f25ae9SGregory Neil Shapiro 
96206f25ae9SGregory Neil Shapiro 		/* lock the file to avoid accidental conflicts */
96306f25ae9SGregory Neil Shapiro 		if ((omode & O_ACCMODE) != O_RDONLY)
96406f25ae9SGregory Neil Shapiro 			locktype = LOCK_EX;
96506f25ae9SGregory Neil Shapiro 		else
96606f25ae9SGregory Neil Shapiro 			locktype = LOCK_SH;
967e92d3f3fSGregory Neil Shapiro 		if (bitset(SFF_NBLOCK, sff))
968e92d3f3fSGregory Neil Shapiro 			locktype |= LOCK_NB;
969e92d3f3fSGregory Neil Shapiro 
97006f25ae9SGregory Neil Shapiro 		if (!lockfile(fd, filename, NULL, locktype))
97106f25ae9SGregory Neil Shapiro 		{
97206f25ae9SGregory Neil Shapiro 			int save_errno = errno;
97306f25ae9SGregory Neil Shapiro 
97406f25ae9SGregory Neil Shapiro 			(void) close(fd);
97506f25ae9SGregory Neil Shapiro 			fd = -1;
97606f25ae9SGregory Neil Shapiro 			errno = save_errno;
97706f25ae9SGregory Neil Shapiro 		}
97806f25ae9SGregory Neil Shapiro 		else
97906f25ae9SGregory Neil Shapiro 			errno = 0;
98006f25ae9SGregory Neil Shapiro 	}
98106f25ae9SGregory Neil Shapiro 	return fd;
98206f25ae9SGregory Neil Shapiro }
983