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