xref: /freebsd/contrib/sendmail/libsmutil/safefile.c (revision 17d6c636720d00f77e5d098daf4c278f89d84f7b)
1 /*
2  * Copyright (c) 1998-2001 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
15 static char id[] = "@(#)$Id: safefile.c,v 8.81.4.10 2001/07/20 04:19:36 gshapiro 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 
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 + 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 
390 int
391 safedirpath(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 int
664 safeopen(fn, omode, cmode, sff)
665 	char *fn;
666 	int omode;
667 	int cmode;
668 	long sff;
669 {
670 	int rval;
671 	int fd;
672 	int smode;
673 	struct stat stb;
674 
675 	if (tTd(44, 10))
676 		printf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n",
677 		       fn, omode, cmode, sff);
678 
679 	if (bitset(O_CREAT, omode))
680 		sff |= SFF_CREAT;
681 	omode &= ~O_CREAT;
682 	smode = 0;
683 	switch (omode & O_ACCMODE)
684 	{
685 	  case O_RDONLY:
686 		smode = S_IREAD;
687 		break;
688 
689 	  case O_WRONLY:
690 		smode = S_IWRITE;
691 		break;
692 
693 	  case O_RDWR:
694 		smode = S_IREAD|S_IWRITE;
695 		break;
696 
697 	  default:
698 		smode = 0;
699 		break;
700 	}
701 	if (bitset(SFF_OPENASROOT, sff))
702 		rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName,
703 				sff, smode, &stb);
704 	else
705 		rval = safefile(fn, RealUid, RealGid, RealUserName,
706 				sff, smode, &stb);
707 	if (rval != 0)
708 	{
709 		errno = rval;
710 		return -1;
711 	}
712 	if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff))
713 		omode |= O_CREAT | (bitset(SFF_NOTEXCL, sff) ? 0 : O_EXCL);
714 	else if (bitset(SFF_CREAT, sff) && bitset(O_EXCL, omode))
715 	{
716 		/* The file exists so an exclusive create would fail */
717 		errno = EEXIST;
718 		return -1;
719 	}
720 
721 	fd = dfopen(fn, omode, cmode, sff);
722 	if (fd < 0)
723 		return fd;
724 	if (filechanged(fn, fd, &stb))
725 	{
726 		syserr("554 5.3.0 cannot open: file %s changed after open", fn);
727 		(void) close(fd);
728 		errno = E_SM_FILECHANGE;
729 		return -1;
730 	}
731 	return fd;
732 }
733 /*
734 **  FILECHANGED -- check to see if file changed after being opened
735 **
736 **	Parameters:
737 **		fn -- pathname of file to check.
738 **		fd -- file descriptor to check.
739 **		stb -- stat structure from before open.
740 **
741 **	Returns:
742 **		TRUE -- if a problem was detected.
743 **		FALSE -- if this file is still the same.
744 */
745 
746 bool
747 filechanged(fn, fd, stb)
748 	char *fn;
749 	int fd;
750 	struct stat *stb;
751 {
752 	struct stat sta;
753 
754 	if (stb->st_mode == ST_MODE_NOFILE)
755 	{
756 # if HASLSTAT && BOGUS_O_EXCL
757 		/* only necessary if exclusive open follows symbolic links */
758 		if (lstat(fn, stb) < 0 || stb->st_nlink != 1)
759 			return TRUE;
760 # else /* HASLSTAT && BOGUS_O_EXCL */
761 		return FALSE;
762 # endif /* HASLSTAT && BOGUS_O_EXCL */
763 	}
764 	if (fstat(fd, &sta) < 0)
765 		return TRUE;
766 
767 	if (sta.st_nlink != stb->st_nlink ||
768 	    sta.st_dev != stb->st_dev ||
769 	    sta.st_ino != stb->st_ino ||
770 # if HAS_ST_GEN && 0		/* AFS returns garbage in st_gen */
771 	    sta.st_gen != stb->st_gen ||
772 # endif /* HAS_ST_GEN && 0 */
773 	    sta.st_uid != stb->st_uid ||
774 	    sta.st_gid != stb->st_gid)
775 	{
776 		if (tTd(44, 8))
777 		{
778 			dprintf("File changed after opening:\n");
779 			dprintf(" nlink	= %ld/%ld\n",
780 				(long) stb->st_nlink, (long) sta.st_nlink);
781 			dprintf(" dev	= %ld/%ld\n",
782 				(long) stb->st_dev, (long) sta.st_dev);
783 			if (sizeof sta.st_ino > sizeof (long))
784 			{
785 				dprintf(" ino	= %s/",
786 					quad_to_string(stb->st_ino));
787 				dprintf("%s\n",
788 					quad_to_string(sta.st_ino));
789 			}
790 			else
791 				dprintf(" ino	= %lu/%lu\n",
792 					(unsigned long) stb->st_ino,
793 					(unsigned long) sta.st_ino);
794 # if HAS_ST_GEN
795 			dprintf(" gen	= %ld/%ld\n",
796 				(long) stb->st_gen, (long) sta.st_gen);
797 # endif /* HAS_ST_GEN */
798 			dprintf(" uid	= %ld/%ld\n",
799 				(long) stb->st_uid, (long) sta.st_uid);
800 			dprintf(" gid	= %ld/%ld\n",
801 				(long) stb->st_gid, (long) sta.st_gid);
802 		}
803 		return TRUE;
804 	}
805 
806 	return FALSE;
807 }
808 /*
809 **  DFOPEN -- determined file open
810 **
811 **	This routine has the semantics of open, except that it will
812 **	keep trying a few times to make this happen.  The idea is that
813 **	on very loaded systems, we may run out of resources (inodes,
814 **	whatever), so this tries to get around it.
815 */
816 
817 int
818 dfopen(filename, omode, cmode, sff)
819 	char *filename;
820 	int omode;
821 	int cmode;
822 	long sff;
823 {
824 	register int tries;
825 	int fd = -1;
826 	struct stat st;
827 
828 	for (tries = 0; tries < 10; tries++)
829 	{
830 		(void) sleep((unsigned) (10 * tries));
831 		errno = 0;
832 		fd = open(filename, omode, cmode);
833 		if (fd >= 0)
834 			break;
835 		switch (errno)
836 		{
837 		  case ENFILE:		/* system file table full */
838 		  case EINTR:		/* interrupted syscall */
839 #ifdef ETXTBSY
840 		  case ETXTBSY:		/* Apollo: net file locked */
841 #endif /* ETXTBSY */
842 			continue;
843 		}
844 		break;
845 	}
846 	if (!bitset(SFF_NOLOCK, sff) &&
847 	    fd >= 0 &&
848 	    fstat(fd, &st) >= 0 &&
849 	    S_ISREG(st.st_mode))
850 	{
851 		int locktype;
852 
853 		/* lock the file to avoid accidental conflicts */
854 		if ((omode & O_ACCMODE) != O_RDONLY)
855 			locktype = LOCK_EX;
856 		else
857 			locktype = LOCK_SH;
858 		if (!lockfile(fd, filename, NULL, locktype))
859 		{
860 			int save_errno = errno;
861 
862 			(void) close(fd);
863 			fd = -1;
864 			errno = save_errno;
865 		}
866 		else
867 			errno = 0;
868 	}
869 	return fd;
870 }
871