xref: /freebsd/contrib/sendmail/libsmutil/safefile.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
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
15 static char id[] = "@(#)$Id: safefile.c,v 8.81.4.7 2000/09/01 21:09:23 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 
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 #ifndef O_ACCMODE
664 # define O_ACCMODE	(O_RDONLY|O_WRONLY|O_RDWR)
665 #endif /* ! O_ACCMODE */
666 
667 int
668 safeopen(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 
750 FILE *
751 safefopen(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 
825 bool
826 filechanged(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 
896 int
897 dfopen(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