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