xref: /freebsd/contrib/sendmail/libsmutil/safefile.c (revision ee2ea5ceafed78a5bd9810beb9e3ca927180c226)
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 #include <sendmail.h>
15 #include <sm/io.h>
16 #include <sm/errstring.h>
17 
18 SM_RCSID("@(#)$Id: safefile.c,v 1.1.1.4 2002/02/17 21:56:42 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 + 1];
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 + 1];
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 			char *target;
490 			char buf[MAXPATHLEN + 1];
491 
492 			memset(buf, '\0', sizeof buf);
493 			if (readlink(s, buf, sizeof buf) < 0)
494 			{
495 				ret = errno;
496 				break;
497 			}
498 
499 			offset = 0;
500 			if (*buf == '/')
501 			{
502 				target = buf;
503 
504 				/* If path is the same, avoid rechecks */
505 				while (s[offset] == buf[offset] &&
506 				       s[offset] != '\0')
507 					offset++;
508 
509 				if (s[offset] == '\0' && buf[offset] == '\0')
510 				{
511 					/* strings match, symlink loop */
512 					return ELOOP;
513 				}
514 
515 				/* back off from the mismatch */
516 				if (offset > 0)
517 					offset--;
518 
519 				/* Make sure we are at a directory break */
520 				if (offset > 0 &&
521 				    s[offset] != '/' &&
522 				    s[offset] != '\0')
523 				{
524 					while (buf[offset] != '/' &&
525 					       offset > 0)
526 						offset--;
527 				}
528 				if (offset > 0 &&
529 				    s[offset] == '/' &&
530 				    buf[offset] == '/')
531 				{
532 					/* Include the trailing slash */
533 					offset++;
534 				}
535 			}
536 			else
537 			{
538 				char *sptr;
539 				char fullbuf[MAXLINKPATHLEN + 1];
540 
541 				sptr = strrchr(s, '/');
542 				if (sptr != NULL)
543 				{
544 					*sptr = '\0';
545 					offset = sptr + 1 - s;
546 					if (sm_strlcpyn(fullbuf,
547 							sizeof fullbuf, 2,
548 							s, "/") >=
549 						sizeof fullbuf ||
550 					    sm_strlcat(fullbuf, buf,
551 						       sizeof fullbuf) >=
552 						sizeof fullbuf)
553 					{
554 						ret = EINVAL;
555 						break;
556 					}
557 					*sptr = '/';
558 				}
559 				else
560 				{
561 					if (sm_strlcpy(fullbuf, buf,
562 						       sizeof fullbuf) >=
563 						sizeof fullbuf)
564 					{
565 						ret = EINVAL;
566 						break;
567 					}
568 				}
569 				target = fullbuf;
570 			}
571 			ret = safedirpath(target, uid, gid, user, flags,
572 					  level + 1, offset);
573 			if (ret != 0)
574 				break;
575 
576 			/* Don't check permissions on the link file itself */
577 			continue;
578 		}
579 #endif /* S_ISLNK */
580 
581 		if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) &&
582 #ifdef S_ISVTX
583 		    !(bitnset(DBS_TRUSTSTICKYBIT, DontBlameSendmail) &&
584 		      bitset(S_ISVTX, stbuf.st_mode)) &&
585 #endif /* S_ISVTX */
586 		    bitset(mode, stbuf.st_mode))
587 		{
588 			if (tTd(44, 4))
589 				sm_dprintf("\t[dir %s] mode %lo ",
590 					s, (unsigned long) stbuf.st_mode);
591 			if (bitset(SFF_SAFEDIRPATH, flags))
592 			{
593 				if (bitset(S_IWOTH, stbuf.st_mode))
594 					ret = E_SM_WWDIR;
595 				else
596 					ret = E_SM_GWDIR;
597 				if (tTd(44, 4))
598 					sm_dprintf("FATAL\n");
599 				break;
600 			}
601 			if (tTd(44, 4))
602 				sm_dprintf("WARNING\n");
603 			if (Verbose > 1)
604 				message("051 WARNING: %s writable directory %s",
605 					bitset(S_IWOTH, stbuf.st_mode)
606 					   ? "World"
607 					   : "Group",
608 					s);
609 		}
610 		if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags))
611 		{
612 			if (bitset(S_IXOTH, stbuf.st_mode))
613 				continue;
614 			ret = EACCES;
615 			break;
616 		}
617 
618 		/*
619 		**  Let OS determine access to file if we are not
620 		**  running as a privileged user.  This allows ACLs
621 		**  to work.  Also, if opening as root, assume we can
622 		**  scan the directory.
623 		*/
624 		if (geteuid() != 0 || bitset(SFF_OPENASROOT, flags))
625 			continue;
626 
627 		if (stbuf.st_uid == uid &&
628 		    bitset(S_IXUSR, stbuf.st_mode))
629 			continue;
630 		if (stbuf.st_gid == gid &&
631 		    bitset(S_IXGRP, stbuf.st_mode))
632 			continue;
633 # ifndef NO_GROUP_SET
634 		if (user != NULL && !DontInitGroups &&
635 		    ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
636 		     (gr = getgrgid(stbuf.st_gid)) != NULL))
637 		{
638 			register char **gp;
639 
640 			for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
641 				if (strcmp(*gp, user) == 0)
642 					break;
643 			if (gp != NULL && *gp != NULL &&
644 			    bitset(S_IXGRP, stbuf.st_mode))
645 				continue;
646 		}
647 # endif /* ! NO_GROUP_SET */
648 		if (!bitset(S_IXOTH, stbuf.st_mode))
649 		{
650 			ret = EACCES;
651 			break;
652 		}
653 	}
654 	if (tTd(44, 4))
655 		sm_dprintf("\t[dir %s] %s\n", fn,
656 			ret == 0 ? "OK" : sm_errstring(ret));
657 	return ret;
658 }
659 /*
660 **  SAFEOPEN -- do a file open with extra checking
661 **
662 **	Parameters:
663 **		fn -- the file name to open.
664 **		omode -- the open-style mode flags.
665 **		cmode -- the create-style mode flags.
666 **		sff -- safefile flags.
667 **
668 **	Returns:
669 **		Same as open.
670 */
671 
672 int
673 safeopen(fn, omode, cmode, sff)
674 	char *fn;
675 	int omode;
676 	int cmode;
677 	long sff;
678 {
679 	int rval;
680 	int fd;
681 	int smode;
682 	struct stat stb;
683 
684 	if (tTd(44, 10))
685 		sm_dprintf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n",
686 			   fn, omode, cmode, sff);
687 
688 	if (bitset(O_CREAT, omode))
689 		sff |= SFF_CREAT;
690 	omode &= ~O_CREAT;
691 	smode = 0;
692 	switch (omode & O_ACCMODE)
693 	{
694 	  case O_RDONLY:
695 		smode = S_IREAD;
696 		break;
697 
698 	  case O_WRONLY:
699 		smode = S_IWRITE;
700 		break;
701 
702 	  case O_RDWR:
703 		smode = S_IREAD|S_IWRITE;
704 		break;
705 
706 	  default:
707 		smode = 0;
708 		break;
709 	}
710 	if (bitset(SFF_OPENASROOT, sff))
711 		rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName,
712 				sff, smode, &stb);
713 	else
714 		rval = safefile(fn, RealUid, RealGid, RealUserName,
715 				sff, smode, &stb);
716 	if (rval != 0)
717 	{
718 		errno = rval;
719 		return -1;
720 	}
721 	if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff))
722 		omode |= O_CREAT | (bitset(SFF_NOTEXCL, sff) ? 0 : O_EXCL);
723 	else if (bitset(SFF_CREAT, sff) && bitset(O_EXCL, omode))
724 	{
725 		/* The file exists so an exclusive create would fail */
726 		errno = EEXIST;
727 		return -1;
728 	}
729 
730 	fd = dfopen(fn, omode, cmode, sff);
731 	if (fd < 0)
732 		return fd;
733 	if (filechanged(fn, fd, &stb))
734 	{
735 		syserr("554 5.3.0 cannot open: file %s changed after open", fn);
736 		(void) close(fd);
737 		errno = E_SM_FILECHANGE;
738 		return -1;
739 	}
740 	return fd;
741 }
742 /*
743 **  SAFEFOPEN -- do a file open with extra checking
744 **
745 **	Parameters:
746 **		fn -- the file name to open.
747 **		omode -- the open-style mode flags.
748 **		cmode -- the create-style mode flags.
749 **		sff -- safefile flags.
750 **
751 **	Returns:
752 **		Same as fopen.
753 */
754 
755 SM_FILE_T *
756 safefopen(fn, omode, cmode, sff)
757 	char *fn;
758 	int omode;
759 	int cmode;
760 	long sff;
761 {
762 	int fd;
763 	int save_errno;
764 	SM_FILE_T *fp;
765 	int fmode;
766 
767 	switch (omode & O_ACCMODE)
768 	{
769 	  case O_RDONLY:
770 		fmode = SM_IO_RDONLY;
771 		break;
772 
773 	  case O_WRONLY:
774 		if (bitset(O_APPEND, omode))
775 			fmode = SM_IO_APPEND;
776 		else
777 			fmode = SM_IO_WRONLY;
778 		break;
779 
780 	  case O_RDWR:
781 		if (bitset(O_TRUNC, omode))
782 			fmode = SM_IO_RDWRTR;
783 		else if (bitset(O_APPEND, omode))
784 			fmode = SM_IO_APPENDRW;
785 		else
786 			fmode = SM_IO_RDWR;
787 		break;
788 
789 	  default:
790 		syserr("554 5.3.5 safefopen: unknown omode %o", omode);
791 		fmode = 0;
792 	}
793 	fd = safeopen(fn, omode, cmode, sff);
794 	if (fd < 0)
795 	{
796 		save_errno = errno;
797 		if (tTd(44, 10))
798 			sm_dprintf("safefopen: safeopen failed: %s\n",
799 				   sm_errstring(errno));
800 		errno = save_errno;
801 		return NULL;
802 	}
803 	fp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
804 			(void *) &fd, fmode, NULL);
805 	if (fp != NULL)
806 		return fp;
807 
808 	save_errno = errno;
809 	if (tTd(44, 10))
810 	{
811 		sm_dprintf("safefopen: fdopen(%s, %d) failed: omode=%x, sff=%lx, err=%s\n",
812 			   fn, fmode, omode, sff, sm_errstring(errno));
813 	}
814 	(void) close(fd);
815 	errno = save_errno;
816 	return NULL;
817 }
818 /*
819 **  FILECHANGED -- check to see if file changed after being opened
820 **
821 **	Parameters:
822 **		fn -- pathname of file to check.
823 **		fd -- file descriptor to check.
824 **		stb -- stat structure from before open.
825 **
826 **	Returns:
827 **		true -- if a problem was detected.
828 **		false -- if this file is still the same.
829 */
830 
831 bool
832 filechanged(fn, fd, stb)
833 	char *fn;
834 	int fd;
835 	struct stat *stb;
836 {
837 	struct stat sta;
838 
839 	if (stb->st_mode == ST_MODE_NOFILE)
840 	{
841 # if HASLSTAT && BOGUS_O_EXCL
842 		/* only necessary if exclusive open follows symbolic links */
843 		if (lstat(fn, stb) < 0 || stb->st_nlink != 1)
844 			return true;
845 # else /* HASLSTAT && BOGUS_O_EXCL */
846 		return false;
847 # endif /* HASLSTAT && BOGUS_O_EXCL */
848 	}
849 	if (fstat(fd, &sta) < 0)
850 		return true;
851 
852 	if (sta.st_nlink != stb->st_nlink ||
853 	    sta.st_dev != stb->st_dev ||
854 	    sta.st_ino != stb->st_ino ||
855 # if HAS_ST_GEN && 0		/* AFS returns garbage in st_gen */
856 	    sta.st_gen != stb->st_gen ||
857 # endif /* HAS_ST_GEN && 0 */
858 	    sta.st_uid != stb->st_uid ||
859 	    sta.st_gid != stb->st_gid)
860 	{
861 		if (tTd(44, 8))
862 		{
863 			sm_dprintf("File changed after opening:\n");
864 			sm_dprintf(" nlink	= %ld/%ld\n",
865 				(long) stb->st_nlink, (long) sta.st_nlink);
866 			sm_dprintf(" dev	= %ld/%ld\n",
867 				(long) stb->st_dev, (long) sta.st_dev);
868 			sm_dprintf(" ino	= %llu/%llu\n",
869 				(ULONGLONG_T) stb->st_ino,
870 				(ULONGLONG_T) sta.st_ino);
871 # if HAS_ST_GEN
872 			sm_dprintf(" gen	= %ld/%ld\n",
873 				(long) stb->st_gen, (long) sta.st_gen);
874 # endif /* HAS_ST_GEN */
875 			sm_dprintf(" uid	= %ld/%ld\n",
876 				(long) stb->st_uid, (long) sta.st_uid);
877 			sm_dprintf(" gid	= %ld/%ld\n",
878 				(long) stb->st_gid, (long) sta.st_gid);
879 		}
880 		return true;
881 	}
882 
883 	return false;
884 }
885 /*
886 **  DFOPEN -- determined file open
887 **
888 **	This routine has the semantics of open, except that it will
889 **	keep trying a few times to make this happen.  The idea is that
890 **	on very loaded systems, we may run out of resources (inodes,
891 **	whatever), so this tries to get around it.
892 */
893 
894 int
895 dfopen(filename, omode, cmode, sff)
896 	char *filename;
897 	int omode;
898 	int cmode;
899 	long sff;
900 {
901 	register int tries;
902 	int fd = -1;
903 	struct stat st;
904 
905 	for (tries = 0; tries < 10; tries++)
906 	{
907 		(void) sleep((unsigned) (10 * tries));
908 		errno = 0;
909 		fd = open(filename, omode, cmode);
910 		if (fd >= 0)
911 			break;
912 		switch (errno)
913 		{
914 		  case ENFILE:		/* system file table full */
915 		  case EINTR:		/* interrupted syscall */
916 #ifdef ETXTBSY
917 		  case ETXTBSY:		/* Apollo: net file locked */
918 #endif /* ETXTBSY */
919 			continue;
920 		}
921 		break;
922 	}
923 	if (!bitset(SFF_NOLOCK, sff) &&
924 	    fd >= 0 &&
925 	    fstat(fd, &st) >= 0 &&
926 	    S_ISREG(st.st_mode))
927 	{
928 		int locktype;
929 
930 		/* lock the file to avoid accidental conflicts */
931 		if ((omode & O_ACCMODE) != O_RDONLY)
932 			locktype = LOCK_EX;
933 		else
934 			locktype = LOCK_SH;
935 		if (!lockfile(fd, filename, NULL, locktype))
936 		{
937 			int save_errno = errno;
938 
939 			(void) close(fd);
940 			fd = -1;
941 			errno = save_errno;
942 		}
943 		else
944 			errno = 0;
945 	}
946 	return fd;
947 }
948