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