xref: /titanic_50/usr/src/cmd/sendmail/libsmutil/safefile.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.
3*7c478bd9Sstevel@tonic-gate  *	All rights reserved.
4*7c478bd9Sstevel@tonic-gate  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5*7c478bd9Sstevel@tonic-gate  * Copyright (c) 1988, 1993
6*7c478bd9Sstevel@tonic-gate  *	The Regents of the University of California.  All rights reserved.
7*7c478bd9Sstevel@tonic-gate  *
8*7c478bd9Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
9*7c478bd9Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
10*7c478bd9Sstevel@tonic-gate  * the sendmail distribution.
11*7c478bd9Sstevel@tonic-gate  *
12*7c478bd9Sstevel@tonic-gate  */
13*7c478bd9Sstevel@tonic-gate 
14*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
15*7c478bd9Sstevel@tonic-gate 
16*7c478bd9Sstevel@tonic-gate #include <sendmail.h>
17*7c478bd9Sstevel@tonic-gate #include <sm/io.h>
18*7c478bd9Sstevel@tonic-gate #include <sm/errstring.h>
19*7c478bd9Sstevel@tonic-gate 
20*7c478bd9Sstevel@tonic-gate SM_RCSID("@(#)$Id: safefile.c,v 8.128 2004/09/30 18:15:49 ca Exp $")
21*7c478bd9Sstevel@tonic-gate 
22*7c478bd9Sstevel@tonic-gate 
23*7c478bd9Sstevel@tonic-gate /*
24*7c478bd9Sstevel@tonic-gate **  SAFEFILE -- return 0 if a file exists and is safe for a user.
25*7c478bd9Sstevel@tonic-gate **
26*7c478bd9Sstevel@tonic-gate **	Parameters:
27*7c478bd9Sstevel@tonic-gate **		fn -- filename to check.
28*7c478bd9Sstevel@tonic-gate **		uid -- user id to compare against.
29*7c478bd9Sstevel@tonic-gate **		gid -- group id to compare against.
30*7c478bd9Sstevel@tonic-gate **		user -- user name to compare against (used for group
31*7c478bd9Sstevel@tonic-gate **			sets).
32*7c478bd9Sstevel@tonic-gate **		flags -- modifiers:
33*7c478bd9Sstevel@tonic-gate **			SFF_MUSTOWN -- "uid" must own this file.
34*7c478bd9Sstevel@tonic-gate **			SFF_NOSLINK -- file cannot be a symbolic link.
35*7c478bd9Sstevel@tonic-gate **		mode -- mode bits that must match.
36*7c478bd9Sstevel@tonic-gate **		st -- if set, points to a stat structure that will
37*7c478bd9Sstevel@tonic-gate **			get the stat info for the file.
38*7c478bd9Sstevel@tonic-gate **
39*7c478bd9Sstevel@tonic-gate **	Returns:
40*7c478bd9Sstevel@tonic-gate **		0 if fn exists, is owned by uid, and matches mode.
41*7c478bd9Sstevel@tonic-gate **		An errno otherwise.  The actual errno is cleared.
42*7c478bd9Sstevel@tonic-gate **
43*7c478bd9Sstevel@tonic-gate **	Side Effects:
44*7c478bd9Sstevel@tonic-gate **		none.
45*7c478bd9Sstevel@tonic-gate */
46*7c478bd9Sstevel@tonic-gate 
47*7c478bd9Sstevel@tonic-gate int
48*7c478bd9Sstevel@tonic-gate safefile(fn, uid, gid, user, flags, mode, st)
49*7c478bd9Sstevel@tonic-gate 	char *fn;
50*7c478bd9Sstevel@tonic-gate 	UID_T uid;
51*7c478bd9Sstevel@tonic-gate 	GID_T gid;
52*7c478bd9Sstevel@tonic-gate 	char *user;
53*7c478bd9Sstevel@tonic-gate 	long flags;
54*7c478bd9Sstevel@tonic-gate 	int mode;
55*7c478bd9Sstevel@tonic-gate 	struct stat *st;
56*7c478bd9Sstevel@tonic-gate {
57*7c478bd9Sstevel@tonic-gate 	register char *p;
58*7c478bd9Sstevel@tonic-gate 	register struct group *gr = NULL;
59*7c478bd9Sstevel@tonic-gate 	int file_errno = 0;
60*7c478bd9Sstevel@tonic-gate 	bool checkpath;
61*7c478bd9Sstevel@tonic-gate 	struct stat stbuf;
62*7c478bd9Sstevel@tonic-gate 	struct stat fstbuf;
63*7c478bd9Sstevel@tonic-gate 	char fbuf[MAXPATHLEN];
64*7c478bd9Sstevel@tonic-gate 
65*7c478bd9Sstevel@tonic-gate 	if (tTd(44, 4))
66*7c478bd9Sstevel@tonic-gate 		sm_dprintf("safefile(%s, uid=%d, gid=%d, flags=%lx, mode=%o):\n",
67*7c478bd9Sstevel@tonic-gate 			fn, (int) uid, (int) gid, flags, mode);
68*7c478bd9Sstevel@tonic-gate 	errno = 0;
69*7c478bd9Sstevel@tonic-gate 	if (sm_strlcpy(fbuf, fn, sizeof fbuf) >= sizeof fbuf)
70*7c478bd9Sstevel@tonic-gate 	{
71*7c478bd9Sstevel@tonic-gate 		if (tTd(44, 4))
72*7c478bd9Sstevel@tonic-gate 			sm_dprintf("\tpathname too long\n");
73*7c478bd9Sstevel@tonic-gate 		return ENAMETOOLONG;
74*7c478bd9Sstevel@tonic-gate 	}
75*7c478bd9Sstevel@tonic-gate 	fn = fbuf;
76*7c478bd9Sstevel@tonic-gate 	if (st == NULL)
77*7c478bd9Sstevel@tonic-gate 		st = &fstbuf;
78*7c478bd9Sstevel@tonic-gate 
79*7c478bd9Sstevel@tonic-gate 	/* ignore SFF_SAFEDIRPATH if we are debugging */
80*7c478bd9Sstevel@tonic-gate 	if (RealUid != 0 && RunAsUid == RealUid)
81*7c478bd9Sstevel@tonic-gate 		flags &= ~SFF_SAFEDIRPATH;
82*7c478bd9Sstevel@tonic-gate 
83*7c478bd9Sstevel@tonic-gate 	/* first check to see if the file exists at all */
84*7c478bd9Sstevel@tonic-gate # if HASLSTAT
85*7c478bd9Sstevel@tonic-gate 	if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st)
86*7c478bd9Sstevel@tonic-gate 					: stat(fn, st)) < 0)
87*7c478bd9Sstevel@tonic-gate # else /* HASLSTAT */
88*7c478bd9Sstevel@tonic-gate 	if (stat(fn, st) < 0)
89*7c478bd9Sstevel@tonic-gate # endif /* HASLSTAT */
90*7c478bd9Sstevel@tonic-gate 	{
91*7c478bd9Sstevel@tonic-gate 		file_errno = errno;
92*7c478bd9Sstevel@tonic-gate 	}
93*7c478bd9Sstevel@tonic-gate 	else if (bitset(SFF_SETUIDOK, flags) &&
94*7c478bd9Sstevel@tonic-gate 		 !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) &&
95*7c478bd9Sstevel@tonic-gate 		 S_ISREG(st->st_mode))
96*7c478bd9Sstevel@tonic-gate 	{
97*7c478bd9Sstevel@tonic-gate 		/*
98*7c478bd9Sstevel@tonic-gate 		**  If final file is set-user-ID, run as the owner of that
99*7c478bd9Sstevel@tonic-gate 		**  file.  Gotta be careful not to reveal anything too
100*7c478bd9Sstevel@tonic-gate 		**  soon here!
101*7c478bd9Sstevel@tonic-gate 		*/
102*7c478bd9Sstevel@tonic-gate 
103*7c478bd9Sstevel@tonic-gate # ifdef SUID_ROOT_FILES_OK
104*7c478bd9Sstevel@tonic-gate 		if (bitset(S_ISUID, st->st_mode))
105*7c478bd9Sstevel@tonic-gate # else /* SUID_ROOT_FILES_OK */
106*7c478bd9Sstevel@tonic-gate 		if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 &&
107*7c478bd9Sstevel@tonic-gate 		    st->st_uid != TrustedUid)
108*7c478bd9Sstevel@tonic-gate # endif /* SUID_ROOT_FILES_OK */
109*7c478bd9Sstevel@tonic-gate 		{
110*7c478bd9Sstevel@tonic-gate 			uid = st->st_uid;
111*7c478bd9Sstevel@tonic-gate 			user = NULL;
112*7c478bd9Sstevel@tonic-gate 		}
113*7c478bd9Sstevel@tonic-gate # ifdef SUID_ROOT_FILES_OK
114*7c478bd9Sstevel@tonic-gate 		if (bitset(S_ISGID, st->st_mode))
115*7c478bd9Sstevel@tonic-gate # else /* SUID_ROOT_FILES_OK */
116*7c478bd9Sstevel@tonic-gate 		if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0)
117*7c478bd9Sstevel@tonic-gate # endif /* SUID_ROOT_FILES_OK */
118*7c478bd9Sstevel@tonic-gate 			gid = st->st_gid;
119*7c478bd9Sstevel@tonic-gate 	}
120*7c478bd9Sstevel@tonic-gate 
121*7c478bd9Sstevel@tonic-gate 	checkpath = !bitset(SFF_NOPATHCHECK, flags) ||
122*7c478bd9Sstevel@tonic-gate 		    (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags));
123*7c478bd9Sstevel@tonic-gate 	if (bitset(SFF_NOWLINK, flags) && !bitset(SFF_SAFEDIRPATH, flags))
124*7c478bd9Sstevel@tonic-gate 	{
125*7c478bd9Sstevel@tonic-gate 		int ret;
126*7c478bd9Sstevel@tonic-gate 
127*7c478bd9Sstevel@tonic-gate 		/* check the directory */
128*7c478bd9Sstevel@tonic-gate 		p = strrchr(fn, '/');
129*7c478bd9Sstevel@tonic-gate 		if (p == NULL)
130*7c478bd9Sstevel@tonic-gate 		{
131*7c478bd9Sstevel@tonic-gate 			ret = safedirpath(".", uid, gid, user,
132*7c478bd9Sstevel@tonic-gate 					  flags|SFF_SAFEDIRPATH, 0, 0);
133*7c478bd9Sstevel@tonic-gate 		}
134*7c478bd9Sstevel@tonic-gate 		else
135*7c478bd9Sstevel@tonic-gate 		{
136*7c478bd9Sstevel@tonic-gate 			*p = '\0';
137*7c478bd9Sstevel@tonic-gate 			ret = safedirpath(fn, uid, gid, user,
138*7c478bd9Sstevel@tonic-gate 					  flags|SFF_SAFEDIRPATH, 0, 0);
139*7c478bd9Sstevel@tonic-gate 			*p = '/';
140*7c478bd9Sstevel@tonic-gate 		}
141*7c478bd9Sstevel@tonic-gate 		if (ret == 0)
142*7c478bd9Sstevel@tonic-gate 		{
143*7c478bd9Sstevel@tonic-gate 			/* directory is safe */
144*7c478bd9Sstevel@tonic-gate 			checkpath = false;
145*7c478bd9Sstevel@tonic-gate 		}
146*7c478bd9Sstevel@tonic-gate 		else
147*7c478bd9Sstevel@tonic-gate 		{
148*7c478bd9Sstevel@tonic-gate # if HASLSTAT
149*7c478bd9Sstevel@tonic-gate 			/* Need lstat() information if called stat() before */
150*7c478bd9Sstevel@tonic-gate 			if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0)
151*7c478bd9Sstevel@tonic-gate 			{
152*7c478bd9Sstevel@tonic-gate 				ret = errno;
153*7c478bd9Sstevel@tonic-gate 				if (tTd(44, 4))
154*7c478bd9Sstevel@tonic-gate 					sm_dprintf("\t%s\n", sm_errstring(ret));
155*7c478bd9Sstevel@tonic-gate 				return ret;
156*7c478bd9Sstevel@tonic-gate 			}
157*7c478bd9Sstevel@tonic-gate # endif /* HASLSTAT */
158*7c478bd9Sstevel@tonic-gate 			/* directory is writable: disallow links */
159*7c478bd9Sstevel@tonic-gate 			flags |= SFF_NOLINK;
160*7c478bd9Sstevel@tonic-gate 		}
161*7c478bd9Sstevel@tonic-gate 	}
162*7c478bd9Sstevel@tonic-gate 
163*7c478bd9Sstevel@tonic-gate 	if (checkpath)
164*7c478bd9Sstevel@tonic-gate 	{
165*7c478bd9Sstevel@tonic-gate 		int ret;
166*7c478bd9Sstevel@tonic-gate 
167*7c478bd9Sstevel@tonic-gate 		p = strrchr(fn, '/');
168*7c478bd9Sstevel@tonic-gate 		if (p == NULL)
169*7c478bd9Sstevel@tonic-gate 		{
170*7c478bd9Sstevel@tonic-gate 			ret = safedirpath(".", uid, gid, user, flags, 0, 0);
171*7c478bd9Sstevel@tonic-gate 		}
172*7c478bd9Sstevel@tonic-gate 		else
173*7c478bd9Sstevel@tonic-gate 		{
174*7c478bd9Sstevel@tonic-gate 			*p = '\0';
175*7c478bd9Sstevel@tonic-gate 			ret = safedirpath(fn, uid, gid, user, flags, 0, 0);
176*7c478bd9Sstevel@tonic-gate 			*p = '/';
177*7c478bd9Sstevel@tonic-gate 		}
178*7c478bd9Sstevel@tonic-gate 		if (ret != 0)
179*7c478bd9Sstevel@tonic-gate 			return ret;
180*7c478bd9Sstevel@tonic-gate 	}
181*7c478bd9Sstevel@tonic-gate 
182*7c478bd9Sstevel@tonic-gate 	/*
183*7c478bd9Sstevel@tonic-gate 	**  If the target file doesn't exist, check the directory to
184*7c478bd9Sstevel@tonic-gate 	**  ensure that it is writable by this user.
185*7c478bd9Sstevel@tonic-gate 	*/
186*7c478bd9Sstevel@tonic-gate 
187*7c478bd9Sstevel@tonic-gate 	if (file_errno != 0)
188*7c478bd9Sstevel@tonic-gate 	{
189*7c478bd9Sstevel@tonic-gate 		int ret = file_errno;
190*7c478bd9Sstevel@tonic-gate 		char *dir = fn;
191*7c478bd9Sstevel@tonic-gate 
192*7c478bd9Sstevel@tonic-gate 		if (tTd(44, 4))
193*7c478bd9Sstevel@tonic-gate 			sm_dprintf("\t%s\n", sm_errstring(ret));
194*7c478bd9Sstevel@tonic-gate 
195*7c478bd9Sstevel@tonic-gate 		errno = 0;
196*7c478bd9Sstevel@tonic-gate 		if (!bitset(SFF_CREAT, flags) || file_errno != ENOENT)
197*7c478bd9Sstevel@tonic-gate 			return ret;
198*7c478bd9Sstevel@tonic-gate 
199*7c478bd9Sstevel@tonic-gate 		/* check to see if legal to create the file */
200*7c478bd9Sstevel@tonic-gate 		p = strrchr(dir, '/');
201*7c478bd9Sstevel@tonic-gate 		if (p == NULL)
202*7c478bd9Sstevel@tonic-gate 			dir = ".";
203*7c478bd9Sstevel@tonic-gate 		else if (p == dir)
204*7c478bd9Sstevel@tonic-gate 			dir = "/";
205*7c478bd9Sstevel@tonic-gate 		else
206*7c478bd9Sstevel@tonic-gate 			*p = '\0';
207*7c478bd9Sstevel@tonic-gate 		if (stat(dir, &stbuf) >= 0)
208*7c478bd9Sstevel@tonic-gate 		{
209*7c478bd9Sstevel@tonic-gate 			int md = S_IWRITE|S_IEXEC;
210*7c478bd9Sstevel@tonic-gate 
211*7c478bd9Sstevel@tonic-gate 			ret = 0;
212*7c478bd9Sstevel@tonic-gate 			if (stbuf.st_uid == uid)
213*7c478bd9Sstevel@tonic-gate 				/* EMPTY */
214*7c478bd9Sstevel@tonic-gate 				;
215*7c478bd9Sstevel@tonic-gate 			else if (uid == 0 && stbuf.st_uid == TrustedUid)
216*7c478bd9Sstevel@tonic-gate 				/* EMPTY */
217*7c478bd9Sstevel@tonic-gate 				;
218*7c478bd9Sstevel@tonic-gate 			else
219*7c478bd9Sstevel@tonic-gate 			{
220*7c478bd9Sstevel@tonic-gate 				md >>= 3;
221*7c478bd9Sstevel@tonic-gate 				if (stbuf.st_gid == gid)
222*7c478bd9Sstevel@tonic-gate 					/* EMPTY */
223*7c478bd9Sstevel@tonic-gate 					;
224*7c478bd9Sstevel@tonic-gate # ifndef NO_GROUP_SET
225*7c478bd9Sstevel@tonic-gate 				else if (user != NULL && !DontInitGroups &&
226*7c478bd9Sstevel@tonic-gate 					 ((gr != NULL &&
227*7c478bd9Sstevel@tonic-gate 					   gr->gr_gid == stbuf.st_gid) ||
228*7c478bd9Sstevel@tonic-gate 					  (gr = getgrgid(stbuf.st_gid)) != NULL))
229*7c478bd9Sstevel@tonic-gate 				{
230*7c478bd9Sstevel@tonic-gate 					register char **gp;
231*7c478bd9Sstevel@tonic-gate 
232*7c478bd9Sstevel@tonic-gate 					for (gp = gr->gr_mem; *gp != NULL; gp++)
233*7c478bd9Sstevel@tonic-gate 						if (strcmp(*gp, user) == 0)
234*7c478bd9Sstevel@tonic-gate 							break;
235*7c478bd9Sstevel@tonic-gate 					if (*gp == NULL)
236*7c478bd9Sstevel@tonic-gate 						md >>= 3;
237*7c478bd9Sstevel@tonic-gate 				}
238*7c478bd9Sstevel@tonic-gate # endif /* ! NO_GROUP_SET */
239*7c478bd9Sstevel@tonic-gate 				else
240*7c478bd9Sstevel@tonic-gate 					md >>= 3;
241*7c478bd9Sstevel@tonic-gate 			}
242*7c478bd9Sstevel@tonic-gate 			if ((stbuf.st_mode & md) != md)
243*7c478bd9Sstevel@tonic-gate 				ret = errno = EACCES;
244*7c478bd9Sstevel@tonic-gate 		}
245*7c478bd9Sstevel@tonic-gate 		else
246*7c478bd9Sstevel@tonic-gate 			ret = errno;
247*7c478bd9Sstevel@tonic-gate 		if (tTd(44, 4))
248*7c478bd9Sstevel@tonic-gate 			sm_dprintf("\t[final dir %s uid %d mode %lo] %s\n",
249*7c478bd9Sstevel@tonic-gate 				dir, (int) stbuf.st_uid,
250*7c478bd9Sstevel@tonic-gate 				(unsigned long) stbuf.st_mode,
251*7c478bd9Sstevel@tonic-gate 				sm_errstring(ret));
252*7c478bd9Sstevel@tonic-gate 		if (p != NULL)
253*7c478bd9Sstevel@tonic-gate 			*p = '/';
254*7c478bd9Sstevel@tonic-gate 		st->st_mode = ST_MODE_NOFILE;
255*7c478bd9Sstevel@tonic-gate 		return ret;
256*7c478bd9Sstevel@tonic-gate 	}
257*7c478bd9Sstevel@tonic-gate 
258*7c478bd9Sstevel@tonic-gate # ifdef S_ISLNK
259*7c478bd9Sstevel@tonic-gate 	if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode))
260*7c478bd9Sstevel@tonic-gate 	{
261*7c478bd9Sstevel@tonic-gate 		if (tTd(44, 4))
262*7c478bd9Sstevel@tonic-gate 			sm_dprintf("\t[slink mode %lo]\tE_SM_NOSLINK\n",
263*7c478bd9Sstevel@tonic-gate 				(unsigned long) st->st_mode);
264*7c478bd9Sstevel@tonic-gate 		return E_SM_NOSLINK;
265*7c478bd9Sstevel@tonic-gate 	}
266*7c478bd9Sstevel@tonic-gate # endif /* S_ISLNK */
267*7c478bd9Sstevel@tonic-gate 	if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode))
268*7c478bd9Sstevel@tonic-gate 	{
269*7c478bd9Sstevel@tonic-gate 		if (tTd(44, 4))
270*7c478bd9Sstevel@tonic-gate 			sm_dprintf("\t[non-reg mode %lo]\tE_SM_REGONLY\n",
271*7c478bd9Sstevel@tonic-gate 				(unsigned long) st->st_mode);
272*7c478bd9Sstevel@tonic-gate 		return E_SM_REGONLY;
273*7c478bd9Sstevel@tonic-gate 	}
274*7c478bd9Sstevel@tonic-gate 	if (bitset(SFF_NOGWFILES, flags) &&
275*7c478bd9Sstevel@tonic-gate 	    bitset(S_IWGRP, st->st_mode))
276*7c478bd9Sstevel@tonic-gate 	{
277*7c478bd9Sstevel@tonic-gate 		if (tTd(44, 4))
278*7c478bd9Sstevel@tonic-gate 			sm_dprintf("\t[write bits %lo]\tE_SM_GWFILE\n",
279*7c478bd9Sstevel@tonic-gate 				(unsigned long) st->st_mode);
280*7c478bd9Sstevel@tonic-gate 		return E_SM_GWFILE;
281*7c478bd9Sstevel@tonic-gate 	}
282*7c478bd9Sstevel@tonic-gate 	if (bitset(SFF_NOWWFILES, flags) &&
283*7c478bd9Sstevel@tonic-gate 	    bitset(S_IWOTH, st->st_mode))
284*7c478bd9Sstevel@tonic-gate 	{
285*7c478bd9Sstevel@tonic-gate 		if (tTd(44, 4))
286*7c478bd9Sstevel@tonic-gate 			sm_dprintf("\t[write bits %lo]\tE_SM_WWFILE\n",
287*7c478bd9Sstevel@tonic-gate 				(unsigned long) st->st_mode);
288*7c478bd9Sstevel@tonic-gate 		return E_SM_WWFILE;
289*7c478bd9Sstevel@tonic-gate 	}
290*7c478bd9Sstevel@tonic-gate 	if (bitset(SFF_NOGRFILES, flags) && bitset(S_IRGRP, st->st_mode))
291*7c478bd9Sstevel@tonic-gate 	{
292*7c478bd9Sstevel@tonic-gate 		if (tTd(44, 4))
293*7c478bd9Sstevel@tonic-gate 			sm_dprintf("\t[read bits %lo]\tE_SM_GRFILE\n",
294*7c478bd9Sstevel@tonic-gate 				(unsigned long) st->st_mode);
295*7c478bd9Sstevel@tonic-gate 		return E_SM_GRFILE;
296*7c478bd9Sstevel@tonic-gate 	}
297*7c478bd9Sstevel@tonic-gate 	if (bitset(SFF_NOWRFILES, flags) && bitset(S_IROTH, st->st_mode))
298*7c478bd9Sstevel@tonic-gate 	{
299*7c478bd9Sstevel@tonic-gate 		if (tTd(44, 4))
300*7c478bd9Sstevel@tonic-gate 			sm_dprintf("\t[read bits %lo]\tE_SM_WRFILE\n",
301*7c478bd9Sstevel@tonic-gate 				(unsigned long) st->st_mode);
302*7c478bd9Sstevel@tonic-gate 		return E_SM_WRFILE;
303*7c478bd9Sstevel@tonic-gate 	}
304*7c478bd9Sstevel@tonic-gate 	if (!bitset(SFF_EXECOK, flags) &&
305*7c478bd9Sstevel@tonic-gate 	    bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) &&
306*7c478bd9Sstevel@tonic-gate 	    bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode))
307*7c478bd9Sstevel@tonic-gate 	{
308*7c478bd9Sstevel@tonic-gate 		if (tTd(44, 4))
309*7c478bd9Sstevel@tonic-gate 			sm_dprintf("\t[exec bits %lo]\tE_SM_ISEXEC\n",
310*7c478bd9Sstevel@tonic-gate 				(unsigned long) st->st_mode);
311*7c478bd9Sstevel@tonic-gate 		return E_SM_ISEXEC;
312*7c478bd9Sstevel@tonic-gate 	}
313*7c478bd9Sstevel@tonic-gate 	if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1)
314*7c478bd9Sstevel@tonic-gate 	{
315*7c478bd9Sstevel@tonic-gate 		if (tTd(44, 4))
316*7c478bd9Sstevel@tonic-gate 			sm_dprintf("\t[link count %d]\tE_SM_NOHLINK\n",
317*7c478bd9Sstevel@tonic-gate 				(int) st->st_nlink);
318*7c478bd9Sstevel@tonic-gate 		return E_SM_NOHLINK;
319*7c478bd9Sstevel@tonic-gate 	}
320*7c478bd9Sstevel@tonic-gate 
321*7c478bd9Sstevel@tonic-gate 	if (uid == 0 && bitset(SFF_OPENASROOT, flags))
322*7c478bd9Sstevel@tonic-gate 		/* EMPTY */
323*7c478bd9Sstevel@tonic-gate 		;
324*7c478bd9Sstevel@tonic-gate 	else if (uid == 0 && !bitset(SFF_ROOTOK, flags))
325*7c478bd9Sstevel@tonic-gate 		mode >>= 6;
326*7c478bd9Sstevel@tonic-gate 	else if (st->st_uid == uid)
327*7c478bd9Sstevel@tonic-gate 		/* EMPTY */
328*7c478bd9Sstevel@tonic-gate 		;
329*7c478bd9Sstevel@tonic-gate 	else if (uid == 0 && st->st_uid == TrustedUid)
330*7c478bd9Sstevel@tonic-gate 		/* EMPTY */
331*7c478bd9Sstevel@tonic-gate 		;
332*7c478bd9Sstevel@tonic-gate 	else
333*7c478bd9Sstevel@tonic-gate 	{
334*7c478bd9Sstevel@tonic-gate 		mode >>= 3;
335*7c478bd9Sstevel@tonic-gate 		if (st->st_gid == gid)
336*7c478bd9Sstevel@tonic-gate 			/* EMPTY */
337*7c478bd9Sstevel@tonic-gate 			;
338*7c478bd9Sstevel@tonic-gate # ifndef NO_GROUP_SET
339*7c478bd9Sstevel@tonic-gate 		else if (user != NULL && !DontInitGroups &&
340*7c478bd9Sstevel@tonic-gate 			 ((gr != NULL && gr->gr_gid == st->st_gid) ||
341*7c478bd9Sstevel@tonic-gate 			  (gr = getgrgid(st->st_gid)) != NULL))
342*7c478bd9Sstevel@tonic-gate 		{
343*7c478bd9Sstevel@tonic-gate 			register char **gp;
344*7c478bd9Sstevel@tonic-gate 
345*7c478bd9Sstevel@tonic-gate 			for (gp = gr->gr_mem; *gp != NULL; gp++)
346*7c478bd9Sstevel@tonic-gate 				if (strcmp(*gp, user) == 0)
347*7c478bd9Sstevel@tonic-gate 					break;
348*7c478bd9Sstevel@tonic-gate 			if (*gp == NULL)
349*7c478bd9Sstevel@tonic-gate 				mode >>= 3;
350*7c478bd9Sstevel@tonic-gate 		}
351*7c478bd9Sstevel@tonic-gate # endif /* ! NO_GROUP_SET */
352*7c478bd9Sstevel@tonic-gate 		else
353*7c478bd9Sstevel@tonic-gate 			mode >>= 3;
354*7c478bd9Sstevel@tonic-gate 	}
355*7c478bd9Sstevel@tonic-gate 	if (tTd(44, 4))
356*7c478bd9Sstevel@tonic-gate 		sm_dprintf("\t[uid %d, nlink %d, stat %lo, mode %lo] ",
357*7c478bd9Sstevel@tonic-gate 			(int) st->st_uid, (int) st->st_nlink,
358*7c478bd9Sstevel@tonic-gate 			(unsigned long) st->st_mode, (unsigned long) mode);
359*7c478bd9Sstevel@tonic-gate 	if ((st->st_uid == uid || st->st_uid == 0 ||
360*7c478bd9Sstevel@tonic-gate 	     st->st_uid == TrustedUid ||
361*7c478bd9Sstevel@tonic-gate 	     !bitset(SFF_MUSTOWN, flags)) &&
362*7c478bd9Sstevel@tonic-gate 	    (st->st_mode & mode) == mode)
363*7c478bd9Sstevel@tonic-gate 	{
364*7c478bd9Sstevel@tonic-gate 		if (tTd(44, 4))
365*7c478bd9Sstevel@tonic-gate 			sm_dprintf("\tOK\n");
366*7c478bd9Sstevel@tonic-gate 		return 0;
367*7c478bd9Sstevel@tonic-gate 	}
368*7c478bd9Sstevel@tonic-gate 	if (tTd(44, 4))
369*7c478bd9Sstevel@tonic-gate 		sm_dprintf("\tEACCES\n");
370*7c478bd9Sstevel@tonic-gate 	return EACCES;
371*7c478bd9Sstevel@tonic-gate }
372*7c478bd9Sstevel@tonic-gate /*
373*7c478bd9Sstevel@tonic-gate **  SAFEDIRPATH -- check to make sure a path to a directory is safe
374*7c478bd9Sstevel@tonic-gate **
375*7c478bd9Sstevel@tonic-gate **	Safe means not writable and owned by the right folks.
376*7c478bd9Sstevel@tonic-gate **
377*7c478bd9Sstevel@tonic-gate **	Parameters:
378*7c478bd9Sstevel@tonic-gate **		fn -- filename to check.
379*7c478bd9Sstevel@tonic-gate **		uid -- user id to compare against.
380*7c478bd9Sstevel@tonic-gate **		gid -- group id to compare against.
381*7c478bd9Sstevel@tonic-gate **		user -- user name to compare against (used for group
382*7c478bd9Sstevel@tonic-gate **			sets).
383*7c478bd9Sstevel@tonic-gate **		flags -- modifiers:
384*7c478bd9Sstevel@tonic-gate **			SFF_ROOTOK -- ok to use root permissions to open.
385*7c478bd9Sstevel@tonic-gate **			SFF_SAFEDIRPATH -- writable directories are considered
386*7c478bd9Sstevel@tonic-gate **				to be fatal errors.
387*7c478bd9Sstevel@tonic-gate **		level -- symlink recursive level.
388*7c478bd9Sstevel@tonic-gate **		offset -- offset into fn to start checking from.
389*7c478bd9Sstevel@tonic-gate **
390*7c478bd9Sstevel@tonic-gate **	Returns:
391*7c478bd9Sstevel@tonic-gate **		0 -- if the directory path is "safe".
392*7c478bd9Sstevel@tonic-gate **		else -- an error number associated with the path.
393*7c478bd9Sstevel@tonic-gate */
394*7c478bd9Sstevel@tonic-gate 
395*7c478bd9Sstevel@tonic-gate int
396*7c478bd9Sstevel@tonic-gate safedirpath(fn, uid, gid, user, flags, level, offset)
397*7c478bd9Sstevel@tonic-gate 	char *fn;
398*7c478bd9Sstevel@tonic-gate 	UID_T uid;
399*7c478bd9Sstevel@tonic-gate 	GID_T gid;
400*7c478bd9Sstevel@tonic-gate 	char *user;
401*7c478bd9Sstevel@tonic-gate 	long flags;
402*7c478bd9Sstevel@tonic-gate 	int level;
403*7c478bd9Sstevel@tonic-gate 	int offset;
404*7c478bd9Sstevel@tonic-gate {
405*7c478bd9Sstevel@tonic-gate 	int ret = 0;
406*7c478bd9Sstevel@tonic-gate 	int mode = S_IWOTH;
407*7c478bd9Sstevel@tonic-gate 	char save = '\0';
408*7c478bd9Sstevel@tonic-gate 	char *saveptr = NULL;
409*7c478bd9Sstevel@tonic-gate 	char *p, *enddir;
410*7c478bd9Sstevel@tonic-gate 	register struct group *gr = NULL;
411*7c478bd9Sstevel@tonic-gate 	char s[MAXLINKPATHLEN];
412*7c478bd9Sstevel@tonic-gate 	struct stat stbuf;
413*7c478bd9Sstevel@tonic-gate 
414*7c478bd9Sstevel@tonic-gate 	/* make sure we aren't in a symlink loop */
415*7c478bd9Sstevel@tonic-gate 	if (level > MAXSYMLINKS)
416*7c478bd9Sstevel@tonic-gate 		return ELOOP;
417*7c478bd9Sstevel@tonic-gate 
418*7c478bd9Sstevel@tonic-gate 	if (level < 0 || offset < 0 || offset > strlen(fn))
419*7c478bd9Sstevel@tonic-gate 		return EINVAL;
420*7c478bd9Sstevel@tonic-gate 
421*7c478bd9Sstevel@tonic-gate 	/* special case root directory */
422*7c478bd9Sstevel@tonic-gate 	if (*fn == '\0')
423*7c478bd9Sstevel@tonic-gate 		fn = "/";
424*7c478bd9Sstevel@tonic-gate 
425*7c478bd9Sstevel@tonic-gate 	if (tTd(44, 4))
426*7c478bd9Sstevel@tonic-gate 		sm_dprintf("safedirpath(%s, uid=%ld, gid=%ld, flags=%lx, level=%d, offset=%d):\n",
427*7c478bd9Sstevel@tonic-gate 			fn, (long) uid, (long) gid, flags, level, offset);
428*7c478bd9Sstevel@tonic-gate 
429*7c478bd9Sstevel@tonic-gate 	if (!bitnset(DBS_GROUPWRITABLEDIRPATHSAFE, DontBlameSendmail))
430*7c478bd9Sstevel@tonic-gate 		mode |= S_IWGRP;
431*7c478bd9Sstevel@tonic-gate 
432*7c478bd9Sstevel@tonic-gate 	/* Make a modifiable copy of the filename */
433*7c478bd9Sstevel@tonic-gate 	if (sm_strlcpy(s, fn, sizeof s) >= sizeof s)
434*7c478bd9Sstevel@tonic-gate 		return EINVAL;
435*7c478bd9Sstevel@tonic-gate 
436*7c478bd9Sstevel@tonic-gate 	p = s + offset;
437*7c478bd9Sstevel@tonic-gate 	while (p != NULL)
438*7c478bd9Sstevel@tonic-gate 	{
439*7c478bd9Sstevel@tonic-gate 		/* put back character */
440*7c478bd9Sstevel@tonic-gate 		if (saveptr != NULL)
441*7c478bd9Sstevel@tonic-gate 		{
442*7c478bd9Sstevel@tonic-gate 			*saveptr = save;
443*7c478bd9Sstevel@tonic-gate 			saveptr = NULL;
444*7c478bd9Sstevel@tonic-gate 			p++;
445*7c478bd9Sstevel@tonic-gate 		}
446*7c478bd9Sstevel@tonic-gate 
447*7c478bd9Sstevel@tonic-gate 		if (*p == '\0')
448*7c478bd9Sstevel@tonic-gate 			break;
449*7c478bd9Sstevel@tonic-gate 
450*7c478bd9Sstevel@tonic-gate 		p = strchr(p, '/');
451*7c478bd9Sstevel@tonic-gate 
452*7c478bd9Sstevel@tonic-gate 		/* Special case for root directory */
453*7c478bd9Sstevel@tonic-gate 		if (p == s)
454*7c478bd9Sstevel@tonic-gate 		{
455*7c478bd9Sstevel@tonic-gate 			save = *(p + 1);
456*7c478bd9Sstevel@tonic-gate 			saveptr = p + 1;
457*7c478bd9Sstevel@tonic-gate 			*(p + 1) = '\0';
458*7c478bd9Sstevel@tonic-gate 		}
459*7c478bd9Sstevel@tonic-gate 		else if (p != NULL)
460*7c478bd9Sstevel@tonic-gate 		{
461*7c478bd9Sstevel@tonic-gate 			save = *p;
462*7c478bd9Sstevel@tonic-gate 			saveptr = p;
463*7c478bd9Sstevel@tonic-gate 			*p = '\0';
464*7c478bd9Sstevel@tonic-gate 		}
465*7c478bd9Sstevel@tonic-gate 
466*7c478bd9Sstevel@tonic-gate 		/* Heuristic: . and .. have already been checked */
467*7c478bd9Sstevel@tonic-gate 		enddir = strrchr(s, '/');
468*7c478bd9Sstevel@tonic-gate 		if (enddir != NULL &&
469*7c478bd9Sstevel@tonic-gate 		    (strcmp(enddir, "/..") == 0 ||
470*7c478bd9Sstevel@tonic-gate 		     strcmp(enddir, "/.") == 0))
471*7c478bd9Sstevel@tonic-gate 			continue;
472*7c478bd9Sstevel@tonic-gate 
473*7c478bd9Sstevel@tonic-gate 		if (tTd(44, 20))
474*7c478bd9Sstevel@tonic-gate 			sm_dprintf("\t[dir %s]\n", s);
475*7c478bd9Sstevel@tonic-gate 
476*7c478bd9Sstevel@tonic-gate # if HASLSTAT
477*7c478bd9Sstevel@tonic-gate 		ret = lstat(s, &stbuf);
478*7c478bd9Sstevel@tonic-gate # else /* HASLSTAT */
479*7c478bd9Sstevel@tonic-gate 		ret = stat(s, &stbuf);
480*7c478bd9Sstevel@tonic-gate # endif /* HASLSTAT */
481*7c478bd9Sstevel@tonic-gate 		if (ret < 0)
482*7c478bd9Sstevel@tonic-gate 		{
483*7c478bd9Sstevel@tonic-gate 			ret = errno;
484*7c478bd9Sstevel@tonic-gate 			break;
485*7c478bd9Sstevel@tonic-gate 		}
486*7c478bd9Sstevel@tonic-gate 
487*7c478bd9Sstevel@tonic-gate # ifdef S_ISLNK
488*7c478bd9Sstevel@tonic-gate 		/* Follow symlinks */
489*7c478bd9Sstevel@tonic-gate 		if (S_ISLNK(stbuf.st_mode))
490*7c478bd9Sstevel@tonic-gate 		{
491*7c478bd9Sstevel@tonic-gate 			int linklen;
492*7c478bd9Sstevel@tonic-gate 			char *target;
493*7c478bd9Sstevel@tonic-gate 			char buf[MAXPATHLEN];
494*7c478bd9Sstevel@tonic-gate 			char fullbuf[MAXLINKPATHLEN];
495*7c478bd9Sstevel@tonic-gate 
496*7c478bd9Sstevel@tonic-gate 			memset(buf, '\0', sizeof buf);
497*7c478bd9Sstevel@tonic-gate 			linklen = readlink(s, buf, sizeof buf);
498*7c478bd9Sstevel@tonic-gate 			if (linklen < 0)
499*7c478bd9Sstevel@tonic-gate 			{
500*7c478bd9Sstevel@tonic-gate 				ret = errno;
501*7c478bd9Sstevel@tonic-gate 				break;
502*7c478bd9Sstevel@tonic-gate 			}
503*7c478bd9Sstevel@tonic-gate 			if (linklen >= sizeof buf)
504*7c478bd9Sstevel@tonic-gate 			{
505*7c478bd9Sstevel@tonic-gate 				/* file name too long for buffer */
506*7c478bd9Sstevel@tonic-gate 				ret = errno = EINVAL;
507*7c478bd9Sstevel@tonic-gate 				break;
508*7c478bd9Sstevel@tonic-gate 			}
509*7c478bd9Sstevel@tonic-gate 
510*7c478bd9Sstevel@tonic-gate 			offset = 0;
511*7c478bd9Sstevel@tonic-gate 			if (*buf == '/')
512*7c478bd9Sstevel@tonic-gate 			{
513*7c478bd9Sstevel@tonic-gate 				target = buf;
514*7c478bd9Sstevel@tonic-gate 
515*7c478bd9Sstevel@tonic-gate 				/* If path is the same, avoid rechecks */
516*7c478bd9Sstevel@tonic-gate 				while (s[offset] == buf[offset] &&
517*7c478bd9Sstevel@tonic-gate 				       s[offset] != '\0')
518*7c478bd9Sstevel@tonic-gate 					offset++;
519*7c478bd9Sstevel@tonic-gate 
520*7c478bd9Sstevel@tonic-gate 				if (s[offset] == '\0' && buf[offset] == '\0')
521*7c478bd9Sstevel@tonic-gate 				{
522*7c478bd9Sstevel@tonic-gate 					/* strings match, symlink loop */
523*7c478bd9Sstevel@tonic-gate 					return ELOOP;
524*7c478bd9Sstevel@tonic-gate 				}
525*7c478bd9Sstevel@tonic-gate 
526*7c478bd9Sstevel@tonic-gate 				/* back off from the mismatch */
527*7c478bd9Sstevel@tonic-gate 				if (offset > 0)
528*7c478bd9Sstevel@tonic-gate 					offset--;
529*7c478bd9Sstevel@tonic-gate 
530*7c478bd9Sstevel@tonic-gate 				/* Make sure we are at a directory break */
531*7c478bd9Sstevel@tonic-gate 				if (offset > 0 &&
532*7c478bd9Sstevel@tonic-gate 				    s[offset] != '/' &&
533*7c478bd9Sstevel@tonic-gate 				    s[offset] != '\0')
534*7c478bd9Sstevel@tonic-gate 				{
535*7c478bd9Sstevel@tonic-gate 					while (buf[offset] != '/' &&
536*7c478bd9Sstevel@tonic-gate 					       offset > 0)
537*7c478bd9Sstevel@tonic-gate 						offset--;
538*7c478bd9Sstevel@tonic-gate 				}
539*7c478bd9Sstevel@tonic-gate 				if (offset > 0 &&
540*7c478bd9Sstevel@tonic-gate 				    s[offset] == '/' &&
541*7c478bd9Sstevel@tonic-gate 				    buf[offset] == '/')
542*7c478bd9Sstevel@tonic-gate 				{
543*7c478bd9Sstevel@tonic-gate 					/* Include the trailing slash */
544*7c478bd9Sstevel@tonic-gate 					offset++;
545*7c478bd9Sstevel@tonic-gate 				}
546*7c478bd9Sstevel@tonic-gate 			}
547*7c478bd9Sstevel@tonic-gate 			else
548*7c478bd9Sstevel@tonic-gate 			{
549*7c478bd9Sstevel@tonic-gate 				char *sptr;
550*7c478bd9Sstevel@tonic-gate 
551*7c478bd9Sstevel@tonic-gate 				sptr = strrchr(s, '/');
552*7c478bd9Sstevel@tonic-gate 				if (sptr != NULL)
553*7c478bd9Sstevel@tonic-gate 				{
554*7c478bd9Sstevel@tonic-gate 					*sptr = '\0';
555*7c478bd9Sstevel@tonic-gate 					offset = sptr + 1 - s;
556*7c478bd9Sstevel@tonic-gate 					if (sm_strlcpyn(fullbuf,
557*7c478bd9Sstevel@tonic-gate 							sizeof fullbuf, 2,
558*7c478bd9Sstevel@tonic-gate 							s, "/") >=
559*7c478bd9Sstevel@tonic-gate 						sizeof fullbuf ||
560*7c478bd9Sstevel@tonic-gate 					    sm_strlcat(fullbuf, buf,
561*7c478bd9Sstevel@tonic-gate 						       sizeof fullbuf) >=
562*7c478bd9Sstevel@tonic-gate 						sizeof fullbuf)
563*7c478bd9Sstevel@tonic-gate 					{
564*7c478bd9Sstevel@tonic-gate 						ret = EINVAL;
565*7c478bd9Sstevel@tonic-gate 						break;
566*7c478bd9Sstevel@tonic-gate 					}
567*7c478bd9Sstevel@tonic-gate 					*sptr = '/';
568*7c478bd9Sstevel@tonic-gate 				}
569*7c478bd9Sstevel@tonic-gate 				else
570*7c478bd9Sstevel@tonic-gate 				{
571*7c478bd9Sstevel@tonic-gate 					if (sm_strlcpy(fullbuf, buf,
572*7c478bd9Sstevel@tonic-gate 						       sizeof fullbuf) >=
573*7c478bd9Sstevel@tonic-gate 						sizeof fullbuf)
574*7c478bd9Sstevel@tonic-gate 					{
575*7c478bd9Sstevel@tonic-gate 						ret = EINVAL;
576*7c478bd9Sstevel@tonic-gate 						break;
577*7c478bd9Sstevel@tonic-gate 					}
578*7c478bd9Sstevel@tonic-gate 				}
579*7c478bd9Sstevel@tonic-gate 				target = fullbuf;
580*7c478bd9Sstevel@tonic-gate 			}
581*7c478bd9Sstevel@tonic-gate 			ret = safedirpath(target, uid, gid, user, flags,
582*7c478bd9Sstevel@tonic-gate 					  level + 1, offset);
583*7c478bd9Sstevel@tonic-gate 			if (ret != 0)
584*7c478bd9Sstevel@tonic-gate 				break;
585*7c478bd9Sstevel@tonic-gate 
586*7c478bd9Sstevel@tonic-gate 			/* Don't check permissions on the link file itself */
587*7c478bd9Sstevel@tonic-gate 			continue;
588*7c478bd9Sstevel@tonic-gate 		}
589*7c478bd9Sstevel@tonic-gate #endif /* S_ISLNK */
590*7c478bd9Sstevel@tonic-gate 
591*7c478bd9Sstevel@tonic-gate 		if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) &&
592*7c478bd9Sstevel@tonic-gate #ifdef S_ISVTX
593*7c478bd9Sstevel@tonic-gate 		    !(bitnset(DBS_TRUSTSTICKYBIT, DontBlameSendmail) &&
594*7c478bd9Sstevel@tonic-gate 		      bitset(S_ISVTX, stbuf.st_mode)) &&
595*7c478bd9Sstevel@tonic-gate #endif /* S_ISVTX */
596*7c478bd9Sstevel@tonic-gate 		    bitset(mode, stbuf.st_mode))
597*7c478bd9Sstevel@tonic-gate 		{
598*7c478bd9Sstevel@tonic-gate 			if (tTd(44, 4))
599*7c478bd9Sstevel@tonic-gate 				sm_dprintf("\t[dir %s] mode %lo ",
600*7c478bd9Sstevel@tonic-gate 					s, (unsigned long) stbuf.st_mode);
601*7c478bd9Sstevel@tonic-gate 			if (bitset(SFF_SAFEDIRPATH, flags))
602*7c478bd9Sstevel@tonic-gate 			{
603*7c478bd9Sstevel@tonic-gate 				if (bitset(S_IWOTH, stbuf.st_mode))
604*7c478bd9Sstevel@tonic-gate 					ret = E_SM_WWDIR;
605*7c478bd9Sstevel@tonic-gate 				else
606*7c478bd9Sstevel@tonic-gate 					ret = E_SM_GWDIR;
607*7c478bd9Sstevel@tonic-gate 				if (tTd(44, 4))
608*7c478bd9Sstevel@tonic-gate 					sm_dprintf("FATAL\n");
609*7c478bd9Sstevel@tonic-gate 				break;
610*7c478bd9Sstevel@tonic-gate 			}
611*7c478bd9Sstevel@tonic-gate 			if (tTd(44, 4))
612*7c478bd9Sstevel@tonic-gate 				sm_dprintf("WARNING\n");
613*7c478bd9Sstevel@tonic-gate 			if (Verbose > 1)
614*7c478bd9Sstevel@tonic-gate 				message("051 WARNING: %s writable directory %s",
615*7c478bd9Sstevel@tonic-gate 					bitset(S_IWOTH, stbuf.st_mode)
616*7c478bd9Sstevel@tonic-gate 					   ? "World"
617*7c478bd9Sstevel@tonic-gate 					   : "Group",
618*7c478bd9Sstevel@tonic-gate 					s);
619*7c478bd9Sstevel@tonic-gate 		}
620*7c478bd9Sstevel@tonic-gate 		if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags))
621*7c478bd9Sstevel@tonic-gate 		{
622*7c478bd9Sstevel@tonic-gate 			if (bitset(S_IXOTH, stbuf.st_mode))
623*7c478bd9Sstevel@tonic-gate 				continue;
624*7c478bd9Sstevel@tonic-gate 			ret = EACCES;
625*7c478bd9Sstevel@tonic-gate 			break;
626*7c478bd9Sstevel@tonic-gate 		}
627*7c478bd9Sstevel@tonic-gate 
628*7c478bd9Sstevel@tonic-gate 		/*
629*7c478bd9Sstevel@tonic-gate 		**  Let OS determine access to file if we are not
630*7c478bd9Sstevel@tonic-gate 		**  running as a privileged user.  This allows ACLs
631*7c478bd9Sstevel@tonic-gate 		**  to work.  Also, if opening as root, assume we can
632*7c478bd9Sstevel@tonic-gate 		**  scan the directory.
633*7c478bd9Sstevel@tonic-gate 		*/
634*7c478bd9Sstevel@tonic-gate 		if (geteuid() != 0 || bitset(SFF_OPENASROOT, flags))
635*7c478bd9Sstevel@tonic-gate 			continue;
636*7c478bd9Sstevel@tonic-gate 
637*7c478bd9Sstevel@tonic-gate 		if (stbuf.st_uid == uid &&
638*7c478bd9Sstevel@tonic-gate 		    bitset(S_IXUSR, stbuf.st_mode))
639*7c478bd9Sstevel@tonic-gate 			continue;
640*7c478bd9Sstevel@tonic-gate 		if (stbuf.st_gid == gid &&
641*7c478bd9Sstevel@tonic-gate 		    bitset(S_IXGRP, stbuf.st_mode))
642*7c478bd9Sstevel@tonic-gate 			continue;
643*7c478bd9Sstevel@tonic-gate # ifndef NO_GROUP_SET
644*7c478bd9Sstevel@tonic-gate 		if (user != NULL && !DontInitGroups &&
645*7c478bd9Sstevel@tonic-gate 		    ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
646*7c478bd9Sstevel@tonic-gate 		     (gr = getgrgid(stbuf.st_gid)) != NULL))
647*7c478bd9Sstevel@tonic-gate 		{
648*7c478bd9Sstevel@tonic-gate 			register char **gp;
649*7c478bd9Sstevel@tonic-gate 
650*7c478bd9Sstevel@tonic-gate 			for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
651*7c478bd9Sstevel@tonic-gate 				if (strcmp(*gp, user) == 0)
652*7c478bd9Sstevel@tonic-gate 					break;
653*7c478bd9Sstevel@tonic-gate 			if (gp != NULL && *gp != NULL &&
654*7c478bd9Sstevel@tonic-gate 			    bitset(S_IXGRP, stbuf.st_mode))
655*7c478bd9Sstevel@tonic-gate 				continue;
656*7c478bd9Sstevel@tonic-gate 		}
657*7c478bd9Sstevel@tonic-gate # endif /* ! NO_GROUP_SET */
658*7c478bd9Sstevel@tonic-gate 		if (!bitset(S_IXOTH, stbuf.st_mode))
659*7c478bd9Sstevel@tonic-gate 		{
660*7c478bd9Sstevel@tonic-gate 			ret = EACCES;
661*7c478bd9Sstevel@tonic-gate 			break;
662*7c478bd9Sstevel@tonic-gate 		}
663*7c478bd9Sstevel@tonic-gate 	}
664*7c478bd9Sstevel@tonic-gate 	if (tTd(44, 4))
665*7c478bd9Sstevel@tonic-gate 		sm_dprintf("\t[dir %s] %s\n", fn,
666*7c478bd9Sstevel@tonic-gate 			ret == 0 ? "OK" : sm_errstring(ret));
667*7c478bd9Sstevel@tonic-gate 	return ret;
668*7c478bd9Sstevel@tonic-gate }
669*7c478bd9Sstevel@tonic-gate /*
670*7c478bd9Sstevel@tonic-gate **  SAFEOPEN -- do a file open with extra checking
671*7c478bd9Sstevel@tonic-gate **
672*7c478bd9Sstevel@tonic-gate **	Parameters:
673*7c478bd9Sstevel@tonic-gate **		fn -- the file name to open.
674*7c478bd9Sstevel@tonic-gate **		omode -- the open-style mode flags.
675*7c478bd9Sstevel@tonic-gate **		cmode -- the create-style mode flags.
676*7c478bd9Sstevel@tonic-gate **		sff -- safefile flags.
677*7c478bd9Sstevel@tonic-gate **
678*7c478bd9Sstevel@tonic-gate **	Returns:
679*7c478bd9Sstevel@tonic-gate **		Same as open.
680*7c478bd9Sstevel@tonic-gate */
681*7c478bd9Sstevel@tonic-gate 
682*7c478bd9Sstevel@tonic-gate int
683*7c478bd9Sstevel@tonic-gate safeopen(fn, omode, cmode, sff)
684*7c478bd9Sstevel@tonic-gate 	char *fn;
685*7c478bd9Sstevel@tonic-gate 	int omode;
686*7c478bd9Sstevel@tonic-gate 	int cmode;
687*7c478bd9Sstevel@tonic-gate 	long sff;
688*7c478bd9Sstevel@tonic-gate {
689*7c478bd9Sstevel@tonic-gate #if !NOFTRUNCATE
690*7c478bd9Sstevel@tonic-gate 	bool truncate;
691*7c478bd9Sstevel@tonic-gate #endif /* !NOFTRUNCATE */
692*7c478bd9Sstevel@tonic-gate 	int rval;
693*7c478bd9Sstevel@tonic-gate 	int fd;
694*7c478bd9Sstevel@tonic-gate 	int smode;
695*7c478bd9Sstevel@tonic-gate 	struct stat stb;
696*7c478bd9Sstevel@tonic-gate 
697*7c478bd9Sstevel@tonic-gate 	if (tTd(44, 10))
698*7c478bd9Sstevel@tonic-gate 		sm_dprintf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n",
699*7c478bd9Sstevel@tonic-gate 			   fn, omode, cmode, sff);
700*7c478bd9Sstevel@tonic-gate 
701*7c478bd9Sstevel@tonic-gate 	if (bitset(O_CREAT, omode))
702*7c478bd9Sstevel@tonic-gate 		sff |= SFF_CREAT;
703*7c478bd9Sstevel@tonic-gate 	omode &= ~O_CREAT;
704*7c478bd9Sstevel@tonic-gate 	smode = 0;
705*7c478bd9Sstevel@tonic-gate 	switch (omode & O_ACCMODE)
706*7c478bd9Sstevel@tonic-gate 	{
707*7c478bd9Sstevel@tonic-gate 	  case O_RDONLY:
708*7c478bd9Sstevel@tonic-gate 		smode = S_IREAD;
709*7c478bd9Sstevel@tonic-gate 		break;
710*7c478bd9Sstevel@tonic-gate 
711*7c478bd9Sstevel@tonic-gate 	  case O_WRONLY:
712*7c478bd9Sstevel@tonic-gate 		smode = S_IWRITE;
713*7c478bd9Sstevel@tonic-gate 		break;
714*7c478bd9Sstevel@tonic-gate 
715*7c478bd9Sstevel@tonic-gate 	  case O_RDWR:
716*7c478bd9Sstevel@tonic-gate 		smode = S_IREAD|S_IWRITE;
717*7c478bd9Sstevel@tonic-gate 		break;
718*7c478bd9Sstevel@tonic-gate 
719*7c478bd9Sstevel@tonic-gate 	  default:
720*7c478bd9Sstevel@tonic-gate 		smode = 0;
721*7c478bd9Sstevel@tonic-gate 		break;
722*7c478bd9Sstevel@tonic-gate 	}
723*7c478bd9Sstevel@tonic-gate 	if (bitset(SFF_OPENASROOT, sff))
724*7c478bd9Sstevel@tonic-gate 		rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName,
725*7c478bd9Sstevel@tonic-gate 				sff, smode, &stb);
726*7c478bd9Sstevel@tonic-gate 	else
727*7c478bd9Sstevel@tonic-gate 		rval = safefile(fn, RealUid, RealGid, RealUserName,
728*7c478bd9Sstevel@tonic-gate 				sff, smode, &stb);
729*7c478bd9Sstevel@tonic-gate 	if (rval != 0)
730*7c478bd9Sstevel@tonic-gate 	{
731*7c478bd9Sstevel@tonic-gate 		errno = rval;
732*7c478bd9Sstevel@tonic-gate 		return -1;
733*7c478bd9Sstevel@tonic-gate 	}
734*7c478bd9Sstevel@tonic-gate 	if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff))
735*7c478bd9Sstevel@tonic-gate 		omode |= O_CREAT | (bitset(SFF_NOTEXCL, sff) ? 0 : O_EXCL);
736*7c478bd9Sstevel@tonic-gate 	else if (bitset(SFF_CREAT, sff) && bitset(O_EXCL, omode))
737*7c478bd9Sstevel@tonic-gate 	{
738*7c478bd9Sstevel@tonic-gate 		/* The file exists so an exclusive create would fail */
739*7c478bd9Sstevel@tonic-gate 		errno = EEXIST;
740*7c478bd9Sstevel@tonic-gate 		return -1;
741*7c478bd9Sstevel@tonic-gate 	}
742*7c478bd9Sstevel@tonic-gate 
743*7c478bd9Sstevel@tonic-gate #if !NOFTRUNCATE
744*7c478bd9Sstevel@tonic-gate 	truncate = bitset(O_TRUNC, omode);
745*7c478bd9Sstevel@tonic-gate 	if (truncate)
746*7c478bd9Sstevel@tonic-gate 		omode &= ~O_TRUNC;
747*7c478bd9Sstevel@tonic-gate #endif /* !NOFTRUNCATE */
748*7c478bd9Sstevel@tonic-gate 
749*7c478bd9Sstevel@tonic-gate 	fd = dfopen(fn, omode, cmode, sff);
750*7c478bd9Sstevel@tonic-gate 	if (fd < 0)
751*7c478bd9Sstevel@tonic-gate 		return fd;
752*7c478bd9Sstevel@tonic-gate 	if (filechanged(fn, fd, &stb))
753*7c478bd9Sstevel@tonic-gate 	{
754*7c478bd9Sstevel@tonic-gate 		syserr("554 5.3.0 cannot open: file %s changed after open", fn);
755*7c478bd9Sstevel@tonic-gate 		(void) close(fd);
756*7c478bd9Sstevel@tonic-gate 		errno = E_SM_FILECHANGE;
757*7c478bd9Sstevel@tonic-gate 		return -1;
758*7c478bd9Sstevel@tonic-gate 	}
759*7c478bd9Sstevel@tonic-gate 
760*7c478bd9Sstevel@tonic-gate #if !NOFTRUNCATE
761*7c478bd9Sstevel@tonic-gate 	if (truncate &&
762*7c478bd9Sstevel@tonic-gate 	    ftruncate(fd, (off_t) 0) < 0)
763*7c478bd9Sstevel@tonic-gate 	{
764*7c478bd9Sstevel@tonic-gate 		int save_errno;
765*7c478bd9Sstevel@tonic-gate 
766*7c478bd9Sstevel@tonic-gate 		save_errno = errno;
767*7c478bd9Sstevel@tonic-gate 		syserr("554 5.3.0 cannot open: file %s could not be truncated",
768*7c478bd9Sstevel@tonic-gate 		       fn);
769*7c478bd9Sstevel@tonic-gate 		(void) close(fd);
770*7c478bd9Sstevel@tonic-gate 		errno = save_errno;
771*7c478bd9Sstevel@tonic-gate 		return -1;
772*7c478bd9Sstevel@tonic-gate 	}
773*7c478bd9Sstevel@tonic-gate #endif /* !NOFTRUNCATE */
774*7c478bd9Sstevel@tonic-gate 
775*7c478bd9Sstevel@tonic-gate 	return fd;
776*7c478bd9Sstevel@tonic-gate }
777*7c478bd9Sstevel@tonic-gate /*
778*7c478bd9Sstevel@tonic-gate **  SAFEFOPEN -- do a file open with extra checking
779*7c478bd9Sstevel@tonic-gate **
780*7c478bd9Sstevel@tonic-gate **	Parameters:
781*7c478bd9Sstevel@tonic-gate **		fn -- the file name to open.
782*7c478bd9Sstevel@tonic-gate **		omode -- the open-style mode flags.
783*7c478bd9Sstevel@tonic-gate **		cmode -- the create-style mode flags.
784*7c478bd9Sstevel@tonic-gate **		sff -- safefile flags.
785*7c478bd9Sstevel@tonic-gate **
786*7c478bd9Sstevel@tonic-gate **	Returns:
787*7c478bd9Sstevel@tonic-gate **		Same as fopen.
788*7c478bd9Sstevel@tonic-gate */
789*7c478bd9Sstevel@tonic-gate 
790*7c478bd9Sstevel@tonic-gate SM_FILE_T *
791*7c478bd9Sstevel@tonic-gate safefopen(fn, omode, cmode, sff)
792*7c478bd9Sstevel@tonic-gate 	char *fn;
793*7c478bd9Sstevel@tonic-gate 	int omode;
794*7c478bd9Sstevel@tonic-gate 	int cmode;
795*7c478bd9Sstevel@tonic-gate 	long sff;
796*7c478bd9Sstevel@tonic-gate {
797*7c478bd9Sstevel@tonic-gate 	int fd;
798*7c478bd9Sstevel@tonic-gate 	int save_errno;
799*7c478bd9Sstevel@tonic-gate 	SM_FILE_T *fp;
800*7c478bd9Sstevel@tonic-gate 	int fmode;
801*7c478bd9Sstevel@tonic-gate 
802*7c478bd9Sstevel@tonic-gate 	switch (omode & O_ACCMODE)
803*7c478bd9Sstevel@tonic-gate 	{
804*7c478bd9Sstevel@tonic-gate 	  case O_RDONLY:
805*7c478bd9Sstevel@tonic-gate 		fmode = SM_IO_RDONLY;
806*7c478bd9Sstevel@tonic-gate 		break;
807*7c478bd9Sstevel@tonic-gate 
808*7c478bd9Sstevel@tonic-gate 	  case O_WRONLY:
809*7c478bd9Sstevel@tonic-gate 		if (bitset(O_APPEND, omode))
810*7c478bd9Sstevel@tonic-gate 			fmode = SM_IO_APPEND;
811*7c478bd9Sstevel@tonic-gate 		else
812*7c478bd9Sstevel@tonic-gate 			fmode = SM_IO_WRONLY;
813*7c478bd9Sstevel@tonic-gate 		break;
814*7c478bd9Sstevel@tonic-gate 
815*7c478bd9Sstevel@tonic-gate 	  case O_RDWR:
816*7c478bd9Sstevel@tonic-gate 		if (bitset(O_TRUNC, omode))
817*7c478bd9Sstevel@tonic-gate 			fmode = SM_IO_RDWRTR;
818*7c478bd9Sstevel@tonic-gate 		else if (bitset(O_APPEND, omode))
819*7c478bd9Sstevel@tonic-gate 			fmode = SM_IO_APPENDRW;
820*7c478bd9Sstevel@tonic-gate 		else
821*7c478bd9Sstevel@tonic-gate 			fmode = SM_IO_RDWR;
822*7c478bd9Sstevel@tonic-gate 		break;
823*7c478bd9Sstevel@tonic-gate 
824*7c478bd9Sstevel@tonic-gate 	  default:
825*7c478bd9Sstevel@tonic-gate 		syserr("554 5.3.5 safefopen: unknown omode %o", omode);
826*7c478bd9Sstevel@tonic-gate 		fmode = 0;
827*7c478bd9Sstevel@tonic-gate 	}
828*7c478bd9Sstevel@tonic-gate 	fd = safeopen(fn, omode, cmode, sff);
829*7c478bd9Sstevel@tonic-gate 	if (fd < 0)
830*7c478bd9Sstevel@tonic-gate 	{
831*7c478bd9Sstevel@tonic-gate 		save_errno = errno;
832*7c478bd9Sstevel@tonic-gate 		if (tTd(44, 10))
833*7c478bd9Sstevel@tonic-gate 			sm_dprintf("safefopen: safeopen failed: %s\n",
834*7c478bd9Sstevel@tonic-gate 				   sm_errstring(errno));
835*7c478bd9Sstevel@tonic-gate 		errno = save_errno;
836*7c478bd9Sstevel@tonic-gate 		return NULL;
837*7c478bd9Sstevel@tonic-gate 	}
838*7c478bd9Sstevel@tonic-gate 	fp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
839*7c478bd9Sstevel@tonic-gate 			(void *) &fd, fmode, NULL);
840*7c478bd9Sstevel@tonic-gate 	if (fp != NULL)
841*7c478bd9Sstevel@tonic-gate 		return fp;
842*7c478bd9Sstevel@tonic-gate 
843*7c478bd9Sstevel@tonic-gate 	save_errno = errno;
844*7c478bd9Sstevel@tonic-gate 	if (tTd(44, 10))
845*7c478bd9Sstevel@tonic-gate 	{
846*7c478bd9Sstevel@tonic-gate 		sm_dprintf("safefopen: fdopen(%s, %d) failed: omode=%x, sff=%lx, err=%s\n",
847*7c478bd9Sstevel@tonic-gate 			   fn, fmode, omode, sff, sm_errstring(errno));
848*7c478bd9Sstevel@tonic-gate 	}
849*7c478bd9Sstevel@tonic-gate 	(void) close(fd);
850*7c478bd9Sstevel@tonic-gate 	errno = save_errno;
851*7c478bd9Sstevel@tonic-gate 	return NULL;
852*7c478bd9Sstevel@tonic-gate }
853*7c478bd9Sstevel@tonic-gate /*
854*7c478bd9Sstevel@tonic-gate **  FILECHANGED -- check to see if file changed after being opened
855*7c478bd9Sstevel@tonic-gate **
856*7c478bd9Sstevel@tonic-gate **	Parameters:
857*7c478bd9Sstevel@tonic-gate **		fn -- pathname of file to check.
858*7c478bd9Sstevel@tonic-gate **		fd -- file descriptor to check.
859*7c478bd9Sstevel@tonic-gate **		stb -- stat structure from before open.
860*7c478bd9Sstevel@tonic-gate **
861*7c478bd9Sstevel@tonic-gate **	Returns:
862*7c478bd9Sstevel@tonic-gate **		true -- if a problem was detected.
863*7c478bd9Sstevel@tonic-gate **		false -- if this file is still the same.
864*7c478bd9Sstevel@tonic-gate */
865*7c478bd9Sstevel@tonic-gate 
866*7c478bd9Sstevel@tonic-gate bool
867*7c478bd9Sstevel@tonic-gate filechanged(fn, fd, stb)
868*7c478bd9Sstevel@tonic-gate 	char *fn;
869*7c478bd9Sstevel@tonic-gate 	int fd;
870*7c478bd9Sstevel@tonic-gate 	struct stat *stb;
871*7c478bd9Sstevel@tonic-gate {
872*7c478bd9Sstevel@tonic-gate 	struct stat sta;
873*7c478bd9Sstevel@tonic-gate 
874*7c478bd9Sstevel@tonic-gate 	if (stb->st_mode == ST_MODE_NOFILE)
875*7c478bd9Sstevel@tonic-gate 	{
876*7c478bd9Sstevel@tonic-gate # if HASLSTAT && BOGUS_O_EXCL
877*7c478bd9Sstevel@tonic-gate 		/* only necessary if exclusive open follows symbolic links */
878*7c478bd9Sstevel@tonic-gate 		if (lstat(fn, stb) < 0 || stb->st_nlink != 1)
879*7c478bd9Sstevel@tonic-gate 			return true;
880*7c478bd9Sstevel@tonic-gate # else /* HASLSTAT && BOGUS_O_EXCL */
881*7c478bd9Sstevel@tonic-gate 		return false;
882*7c478bd9Sstevel@tonic-gate # endif /* HASLSTAT && BOGUS_O_EXCL */
883*7c478bd9Sstevel@tonic-gate 	}
884*7c478bd9Sstevel@tonic-gate 	if (fstat(fd, &sta) < 0)
885*7c478bd9Sstevel@tonic-gate 		return true;
886*7c478bd9Sstevel@tonic-gate 
887*7c478bd9Sstevel@tonic-gate 	if (sta.st_nlink != stb->st_nlink ||
888*7c478bd9Sstevel@tonic-gate 	    sta.st_dev != stb->st_dev ||
889*7c478bd9Sstevel@tonic-gate 	    sta.st_ino != stb->st_ino ||
890*7c478bd9Sstevel@tonic-gate # if HAS_ST_GEN && 0		/* AFS returns garbage in st_gen */
891*7c478bd9Sstevel@tonic-gate 	    sta.st_gen != stb->st_gen ||
892*7c478bd9Sstevel@tonic-gate # endif /* HAS_ST_GEN && 0 */
893*7c478bd9Sstevel@tonic-gate 	    sta.st_uid != stb->st_uid ||
894*7c478bd9Sstevel@tonic-gate 	    sta.st_gid != stb->st_gid)
895*7c478bd9Sstevel@tonic-gate 	{
896*7c478bd9Sstevel@tonic-gate 		if (tTd(44, 8))
897*7c478bd9Sstevel@tonic-gate 		{
898*7c478bd9Sstevel@tonic-gate 			sm_dprintf("File changed after opening:\n");
899*7c478bd9Sstevel@tonic-gate 			sm_dprintf(" nlink	= %ld/%ld\n",
900*7c478bd9Sstevel@tonic-gate 				(long) stb->st_nlink, (long) sta.st_nlink);
901*7c478bd9Sstevel@tonic-gate 			sm_dprintf(" dev	= %ld/%ld\n",
902*7c478bd9Sstevel@tonic-gate 				(long) stb->st_dev, (long) sta.st_dev);
903*7c478bd9Sstevel@tonic-gate 			sm_dprintf(" ino	= %llu/%llu\n",
904*7c478bd9Sstevel@tonic-gate 				(ULONGLONG_T) stb->st_ino,
905*7c478bd9Sstevel@tonic-gate 				(ULONGLONG_T) sta.st_ino);
906*7c478bd9Sstevel@tonic-gate # if HAS_ST_GEN
907*7c478bd9Sstevel@tonic-gate 			sm_dprintf(" gen	= %ld/%ld\n",
908*7c478bd9Sstevel@tonic-gate 				(long) stb->st_gen, (long) sta.st_gen);
909*7c478bd9Sstevel@tonic-gate # endif /* HAS_ST_GEN */
910*7c478bd9Sstevel@tonic-gate 			sm_dprintf(" uid	= %ld/%ld\n",
911*7c478bd9Sstevel@tonic-gate 				(long) stb->st_uid, (long) sta.st_uid);
912*7c478bd9Sstevel@tonic-gate 			sm_dprintf(" gid	= %ld/%ld\n",
913*7c478bd9Sstevel@tonic-gate 				(long) stb->st_gid, (long) sta.st_gid);
914*7c478bd9Sstevel@tonic-gate 		}
915*7c478bd9Sstevel@tonic-gate 		return true;
916*7c478bd9Sstevel@tonic-gate 	}
917*7c478bd9Sstevel@tonic-gate 
918*7c478bd9Sstevel@tonic-gate 	return false;
919*7c478bd9Sstevel@tonic-gate }
920*7c478bd9Sstevel@tonic-gate /*
921*7c478bd9Sstevel@tonic-gate **  DFOPEN -- determined file open
922*7c478bd9Sstevel@tonic-gate **
923*7c478bd9Sstevel@tonic-gate **	This routine has the semantics of open, except that it will
924*7c478bd9Sstevel@tonic-gate **	keep trying a few times to make this happen.  The idea is that
925*7c478bd9Sstevel@tonic-gate **	on very loaded systems, we may run out of resources (inodes,
926*7c478bd9Sstevel@tonic-gate **	whatever), so this tries to get around it.
927*7c478bd9Sstevel@tonic-gate */
928*7c478bd9Sstevel@tonic-gate 
929*7c478bd9Sstevel@tonic-gate int
930*7c478bd9Sstevel@tonic-gate dfopen(filename, omode, cmode, sff)
931*7c478bd9Sstevel@tonic-gate 	char *filename;
932*7c478bd9Sstevel@tonic-gate 	int omode;
933*7c478bd9Sstevel@tonic-gate 	int cmode;
934*7c478bd9Sstevel@tonic-gate 	long sff;
935*7c478bd9Sstevel@tonic-gate {
936*7c478bd9Sstevel@tonic-gate 	register int tries;
937*7c478bd9Sstevel@tonic-gate 	int fd = -1;
938*7c478bd9Sstevel@tonic-gate 	struct stat st;
939*7c478bd9Sstevel@tonic-gate 
940*7c478bd9Sstevel@tonic-gate 	for (tries = 0; tries < 10; tries++)
941*7c478bd9Sstevel@tonic-gate 	{
942*7c478bd9Sstevel@tonic-gate 		(void) sleep((unsigned) (10 * tries));
943*7c478bd9Sstevel@tonic-gate 		errno = 0;
944*7c478bd9Sstevel@tonic-gate 		fd = open(filename, omode, cmode);
945*7c478bd9Sstevel@tonic-gate 		if (fd >= 0)
946*7c478bd9Sstevel@tonic-gate 			break;
947*7c478bd9Sstevel@tonic-gate 		switch (errno)
948*7c478bd9Sstevel@tonic-gate 		{
949*7c478bd9Sstevel@tonic-gate 		  case ENFILE:		/* system file table full */
950*7c478bd9Sstevel@tonic-gate 		  case EINTR:		/* interrupted syscall */
951*7c478bd9Sstevel@tonic-gate #ifdef ETXTBSY
952*7c478bd9Sstevel@tonic-gate 		  case ETXTBSY:		/* Apollo: net file locked */
953*7c478bd9Sstevel@tonic-gate #endif /* ETXTBSY */
954*7c478bd9Sstevel@tonic-gate 			continue;
955*7c478bd9Sstevel@tonic-gate 		}
956*7c478bd9Sstevel@tonic-gate 		break;
957*7c478bd9Sstevel@tonic-gate 	}
958*7c478bd9Sstevel@tonic-gate 	if (!bitset(SFF_NOLOCK, sff) &&
959*7c478bd9Sstevel@tonic-gate 	    fd >= 0 &&
960*7c478bd9Sstevel@tonic-gate 	    fstat(fd, &st) >= 0 &&
961*7c478bd9Sstevel@tonic-gate 	    S_ISREG(st.st_mode))
962*7c478bd9Sstevel@tonic-gate 	{
963*7c478bd9Sstevel@tonic-gate 		int locktype;
964*7c478bd9Sstevel@tonic-gate 
965*7c478bd9Sstevel@tonic-gate 		/* lock the file to avoid accidental conflicts */
966*7c478bd9Sstevel@tonic-gate 		if ((omode & O_ACCMODE) != O_RDONLY)
967*7c478bd9Sstevel@tonic-gate 			locktype = LOCK_EX;
968*7c478bd9Sstevel@tonic-gate 		else
969*7c478bd9Sstevel@tonic-gate 			locktype = LOCK_SH;
970*7c478bd9Sstevel@tonic-gate 		if (bitset(SFF_NBLOCK, sff))
971*7c478bd9Sstevel@tonic-gate 			locktype |= LOCK_NB;
972*7c478bd9Sstevel@tonic-gate 
973*7c478bd9Sstevel@tonic-gate 		if (!lockfile(fd, filename, NULL, locktype))
974*7c478bd9Sstevel@tonic-gate 		{
975*7c478bd9Sstevel@tonic-gate 			int save_errno = errno;
976*7c478bd9Sstevel@tonic-gate 
977*7c478bd9Sstevel@tonic-gate 			(void) close(fd);
978*7c478bd9Sstevel@tonic-gate 			fd = -1;
979*7c478bd9Sstevel@tonic-gate 			errno = save_errno;
980*7c478bd9Sstevel@tonic-gate 		}
981*7c478bd9Sstevel@tonic-gate 		else
982*7c478bd9Sstevel@tonic-gate 			errno = 0;
983*7c478bd9Sstevel@tonic-gate 	}
984*7c478bd9Sstevel@tonic-gate 	return fd;
985*7c478bd9Sstevel@tonic-gate }
986