xref: /freebsd/contrib/sendmail/libsmutil/safefile.c (revision 2357939bc239bd5334a169b62313806178dd8f30)
1 /*
2  * Copyright (c) 1998-2002 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 
18 SM_RCSID("@(#)$Id: safefile.c,v 8.124 2002/05/24 20:50:15 gshapiro 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 
45 int
46 safefile(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 
393 int
394 safedirpath(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 
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 				char fullbuf[MAXLINKPATHLEN];
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 
680 int
681 safeopen(fn, omode, cmode, sff)
682 	char *fn;
683 	int omode;
684 	int cmode;
685 	long sff;
686 {
687 	int rval;
688 	int fd;
689 	int smode;
690 	struct stat stb;
691 
692 	if (tTd(44, 10))
693 		sm_dprintf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n",
694 			   fn, omode, cmode, sff);
695 
696 	if (bitset(O_CREAT, omode))
697 		sff |= SFF_CREAT;
698 	omode &= ~O_CREAT;
699 	smode = 0;
700 	switch (omode & O_ACCMODE)
701 	{
702 	  case O_RDONLY:
703 		smode = S_IREAD;
704 		break;
705 
706 	  case O_WRONLY:
707 		smode = S_IWRITE;
708 		break;
709 
710 	  case O_RDWR:
711 		smode = S_IREAD|S_IWRITE;
712 		break;
713 
714 	  default:
715 		smode = 0;
716 		break;
717 	}
718 	if (bitset(SFF_OPENASROOT, sff))
719 		rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName,
720 				sff, smode, &stb);
721 	else
722 		rval = safefile(fn, RealUid, RealGid, RealUserName,
723 				sff, smode, &stb);
724 	if (rval != 0)
725 	{
726 		errno = rval;
727 		return -1;
728 	}
729 	if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff))
730 		omode |= O_CREAT | (bitset(SFF_NOTEXCL, sff) ? 0 : O_EXCL);
731 	else if (bitset(SFF_CREAT, sff) && bitset(O_EXCL, omode))
732 	{
733 		/* The file exists so an exclusive create would fail */
734 		errno = EEXIST;
735 		return -1;
736 	}
737 
738 	fd = dfopen(fn, omode, cmode, sff);
739 	if (fd < 0)
740 		return fd;
741 	if (filechanged(fn, fd, &stb))
742 	{
743 		syserr("554 5.3.0 cannot open: file %s changed after open", fn);
744 		(void) close(fd);
745 		errno = E_SM_FILECHANGE;
746 		return -1;
747 	}
748 	return fd;
749 }
750 /*
751 **  SAFEFOPEN -- do a file open with extra checking
752 **
753 **	Parameters:
754 **		fn -- the file name to open.
755 **		omode -- the open-style mode flags.
756 **		cmode -- the create-style mode flags.
757 **		sff -- safefile flags.
758 **
759 **	Returns:
760 **		Same as fopen.
761 */
762 
763 SM_FILE_T *
764 safefopen(fn, omode, cmode, sff)
765 	char *fn;
766 	int omode;
767 	int cmode;
768 	long sff;
769 {
770 	int fd;
771 	int save_errno;
772 	SM_FILE_T *fp;
773 	int fmode;
774 
775 	switch (omode & O_ACCMODE)
776 	{
777 	  case O_RDONLY:
778 		fmode = SM_IO_RDONLY;
779 		break;
780 
781 	  case O_WRONLY:
782 		if (bitset(O_APPEND, omode))
783 			fmode = SM_IO_APPEND;
784 		else
785 			fmode = SM_IO_WRONLY;
786 		break;
787 
788 	  case O_RDWR:
789 		if (bitset(O_TRUNC, omode))
790 			fmode = SM_IO_RDWRTR;
791 		else if (bitset(O_APPEND, omode))
792 			fmode = SM_IO_APPENDRW;
793 		else
794 			fmode = SM_IO_RDWR;
795 		break;
796 
797 	  default:
798 		syserr("554 5.3.5 safefopen: unknown omode %o", omode);
799 		fmode = 0;
800 	}
801 	fd = safeopen(fn, omode, cmode, sff);
802 	if (fd < 0)
803 	{
804 		save_errno = errno;
805 		if (tTd(44, 10))
806 			sm_dprintf("safefopen: safeopen failed: %s\n",
807 				   sm_errstring(errno));
808 		errno = save_errno;
809 		return NULL;
810 	}
811 	fp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
812 			(void *) &fd, fmode, NULL);
813 	if (fp != NULL)
814 		return fp;
815 
816 	save_errno = errno;
817 	if (tTd(44, 10))
818 	{
819 		sm_dprintf("safefopen: fdopen(%s, %d) failed: omode=%x, sff=%lx, err=%s\n",
820 			   fn, fmode, omode, sff, sm_errstring(errno));
821 	}
822 	(void) close(fd);
823 	errno = save_errno;
824 	return NULL;
825 }
826 /*
827 **  FILECHANGED -- check to see if file changed after being opened
828 **
829 **	Parameters:
830 **		fn -- pathname of file to check.
831 **		fd -- file descriptor to check.
832 **		stb -- stat structure from before open.
833 **
834 **	Returns:
835 **		true -- if a problem was detected.
836 **		false -- if this file is still the same.
837 */
838 
839 bool
840 filechanged(fn, fd, stb)
841 	char *fn;
842 	int fd;
843 	struct stat *stb;
844 {
845 	struct stat sta;
846 
847 	if (stb->st_mode == ST_MODE_NOFILE)
848 	{
849 # if HASLSTAT && BOGUS_O_EXCL
850 		/* only necessary if exclusive open follows symbolic links */
851 		if (lstat(fn, stb) < 0 || stb->st_nlink != 1)
852 			return true;
853 # else /* HASLSTAT && BOGUS_O_EXCL */
854 		return false;
855 # endif /* HASLSTAT && BOGUS_O_EXCL */
856 	}
857 	if (fstat(fd, &sta) < 0)
858 		return true;
859 
860 	if (sta.st_nlink != stb->st_nlink ||
861 	    sta.st_dev != stb->st_dev ||
862 	    sta.st_ino != stb->st_ino ||
863 # if HAS_ST_GEN && 0		/* AFS returns garbage in st_gen */
864 	    sta.st_gen != stb->st_gen ||
865 # endif /* HAS_ST_GEN && 0 */
866 	    sta.st_uid != stb->st_uid ||
867 	    sta.st_gid != stb->st_gid)
868 	{
869 		if (tTd(44, 8))
870 		{
871 			sm_dprintf("File changed after opening:\n");
872 			sm_dprintf(" nlink	= %ld/%ld\n",
873 				(long) stb->st_nlink, (long) sta.st_nlink);
874 			sm_dprintf(" dev	= %ld/%ld\n",
875 				(long) stb->st_dev, (long) sta.st_dev);
876 			sm_dprintf(" ino	= %llu/%llu\n",
877 				(ULONGLONG_T) stb->st_ino,
878 				(ULONGLONG_T) sta.st_ino);
879 # if HAS_ST_GEN
880 			sm_dprintf(" gen	= %ld/%ld\n",
881 				(long) stb->st_gen, (long) sta.st_gen);
882 # endif /* HAS_ST_GEN */
883 			sm_dprintf(" uid	= %ld/%ld\n",
884 				(long) stb->st_uid, (long) sta.st_uid);
885 			sm_dprintf(" gid	= %ld/%ld\n",
886 				(long) stb->st_gid, (long) sta.st_gid);
887 		}
888 		return true;
889 	}
890 
891 	return false;
892 }
893 /*
894 **  DFOPEN -- determined file open
895 **
896 **	This routine has the semantics of open, except that it will
897 **	keep trying a few times to make this happen.  The idea is that
898 **	on very loaded systems, we may run out of resources (inodes,
899 **	whatever), so this tries to get around it.
900 */
901 
902 int
903 dfopen(filename, omode, cmode, sff)
904 	char *filename;
905 	int omode;
906 	int cmode;
907 	long sff;
908 {
909 	register int tries;
910 	int fd = -1;
911 	struct stat st;
912 
913 	for (tries = 0; tries < 10; tries++)
914 	{
915 		(void) sleep((unsigned) (10 * tries));
916 		errno = 0;
917 		fd = open(filename, omode, cmode);
918 		if (fd >= 0)
919 			break;
920 		switch (errno)
921 		{
922 		  case ENFILE:		/* system file table full */
923 		  case EINTR:		/* interrupted syscall */
924 #ifdef ETXTBSY
925 		  case ETXTBSY:		/* Apollo: net file locked */
926 #endif /* ETXTBSY */
927 			continue;
928 		}
929 		break;
930 	}
931 	if (!bitset(SFF_NOLOCK, sff) &&
932 	    fd >= 0 &&
933 	    fstat(fd, &st) >= 0 &&
934 	    S_ISREG(st.st_mode))
935 	{
936 		int locktype;
937 
938 		/* lock the file to avoid accidental conflicts */
939 		if ((omode & O_ACCMODE) != O_RDONLY)
940 			locktype = LOCK_EX;
941 		else
942 			locktype = LOCK_SH;
943 		if (!lockfile(fd, filename, NULL, locktype))
944 		{
945 			int save_errno = errno;
946 
947 			(void) close(fd);
948 			fd = -1;
949 			errno = save_errno;
950 		}
951 		else
952 			errno = 0;
953 	}
954 	return fd;
955 }
956