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