xref: /titanic_51/usr/src/cmd/sendmail/libsmutil/safefile.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
15 
16 #include <sendmail.h>
17 #include <sm/io.h>
18 #include <sm/errstring.h>
19 
20 SM_RCSID("@(#)$Id: safefile.c,v 8.128 2004/09/30 18:15:49 ca Exp $")
21 
22 
23 /*
24 **  SAFEFILE -- return 0 if a file exists and is safe for a user.
25 **
26 **	Parameters:
27 **		fn -- filename to check.
28 **		uid -- user id to compare against.
29 **		gid -- group id to compare against.
30 **		user -- user name to compare against (used for group
31 **			sets).
32 **		flags -- modifiers:
33 **			SFF_MUSTOWN -- "uid" must own this file.
34 **			SFF_NOSLINK -- file cannot be a symbolic link.
35 **		mode -- mode bits that must match.
36 **		st -- if set, points to a stat structure that will
37 **			get the stat info for the file.
38 **
39 **	Returns:
40 **		0 if fn exists, is owned by uid, and matches mode.
41 **		An errno otherwise.  The actual errno is cleared.
42 **
43 **	Side Effects:
44 **		none.
45 */
46 
47 int
48 safefile(fn, uid, gid, user, flags, mode, st)
49 	char *fn;
50 	UID_T uid;
51 	GID_T gid;
52 	char *user;
53 	long flags;
54 	int mode;
55 	struct stat *st;
56 {
57 	register char *p;
58 	register struct group *gr = NULL;
59 	int file_errno = 0;
60 	bool checkpath;
61 	struct stat stbuf;
62 	struct stat fstbuf;
63 	char fbuf[MAXPATHLEN];
64 
65 	if (tTd(44, 4))
66 		sm_dprintf("safefile(%s, uid=%d, gid=%d, flags=%lx, mode=%o):\n",
67 			fn, (int) uid, (int) gid, flags, mode);
68 	errno = 0;
69 	if (sm_strlcpy(fbuf, fn, sizeof fbuf) >= sizeof fbuf)
70 	{
71 		if (tTd(44, 4))
72 			sm_dprintf("\tpathname too long\n");
73 		return ENAMETOOLONG;
74 	}
75 	fn = fbuf;
76 	if (st == NULL)
77 		st = &fstbuf;
78 
79 	/* ignore SFF_SAFEDIRPATH if we are debugging */
80 	if (RealUid != 0 && RunAsUid == RealUid)
81 		flags &= ~SFF_SAFEDIRPATH;
82 
83 	/* first check to see if the file exists at all */
84 # if HASLSTAT
85 	if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st)
86 					: stat(fn, st)) < 0)
87 # else /* HASLSTAT */
88 	if (stat(fn, st) < 0)
89 # endif /* HASLSTAT */
90 	{
91 		file_errno = errno;
92 	}
93 	else if (bitset(SFF_SETUIDOK, flags) &&
94 		 !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) &&
95 		 S_ISREG(st->st_mode))
96 	{
97 		/*
98 		**  If final file is set-user-ID, run as the owner of that
99 		**  file.  Gotta be careful not to reveal anything too
100 		**  soon here!
101 		*/
102 
103 # ifdef SUID_ROOT_FILES_OK
104 		if (bitset(S_ISUID, st->st_mode))
105 # else /* SUID_ROOT_FILES_OK */
106 		if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 &&
107 		    st->st_uid != TrustedUid)
108 # endif /* SUID_ROOT_FILES_OK */
109 		{
110 			uid = st->st_uid;
111 			user = NULL;
112 		}
113 # ifdef SUID_ROOT_FILES_OK
114 		if (bitset(S_ISGID, st->st_mode))
115 # else /* SUID_ROOT_FILES_OK */
116 		if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0)
117 # endif /* SUID_ROOT_FILES_OK */
118 			gid = st->st_gid;
119 	}
120 
121 	checkpath = !bitset(SFF_NOPATHCHECK, flags) ||
122 		    (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags));
123 	if (bitset(SFF_NOWLINK, flags) && !bitset(SFF_SAFEDIRPATH, flags))
124 	{
125 		int ret;
126 
127 		/* check the directory */
128 		p = strrchr(fn, '/');
129 		if (p == NULL)
130 		{
131 			ret = safedirpath(".", uid, gid, user,
132 					  flags|SFF_SAFEDIRPATH, 0, 0);
133 		}
134 		else
135 		{
136 			*p = '\0';
137 			ret = safedirpath(fn, uid, gid, user,
138 					  flags|SFF_SAFEDIRPATH, 0, 0);
139 			*p = '/';
140 		}
141 		if (ret == 0)
142 		{
143 			/* directory is safe */
144 			checkpath = false;
145 		}
146 		else
147 		{
148 # if HASLSTAT
149 			/* Need lstat() information if called stat() before */
150 			if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0)
151 			{
152 				ret = errno;
153 				if (tTd(44, 4))
154 					sm_dprintf("\t%s\n", sm_errstring(ret));
155 				return ret;
156 			}
157 # endif /* HASLSTAT */
158 			/* directory is writable: disallow links */
159 			flags |= SFF_NOLINK;
160 		}
161 	}
162 
163 	if (checkpath)
164 	{
165 		int ret;
166 
167 		p = strrchr(fn, '/');
168 		if (p == NULL)
169 		{
170 			ret = safedirpath(".", uid, gid, user, flags, 0, 0);
171 		}
172 		else
173 		{
174 			*p = '\0';
175 			ret = safedirpath(fn, uid, gid, user, flags, 0, 0);
176 			*p = '/';
177 		}
178 		if (ret != 0)
179 			return ret;
180 	}
181 
182 	/*
183 	**  If the target file doesn't exist, check the directory to
184 	**  ensure that it is writable by this user.
185 	*/
186 
187 	if (file_errno != 0)
188 	{
189 		int ret = file_errno;
190 		char *dir = fn;
191 
192 		if (tTd(44, 4))
193 			sm_dprintf("\t%s\n", sm_errstring(ret));
194 
195 		errno = 0;
196 		if (!bitset(SFF_CREAT, flags) || file_errno != ENOENT)
197 			return ret;
198 
199 		/* check to see if legal to create the file */
200 		p = strrchr(dir, '/');
201 		if (p == NULL)
202 			dir = ".";
203 		else if (p == dir)
204 			dir = "/";
205 		else
206 			*p = '\0';
207 		if (stat(dir, &stbuf) >= 0)
208 		{
209 			int md = S_IWRITE|S_IEXEC;
210 
211 			ret = 0;
212 			if (stbuf.st_uid == uid)
213 				/* EMPTY */
214 				;
215 			else if (uid == 0 && stbuf.st_uid == TrustedUid)
216 				/* EMPTY */
217 				;
218 			else
219 			{
220 				md >>= 3;
221 				if (stbuf.st_gid == gid)
222 					/* EMPTY */
223 					;
224 # ifndef NO_GROUP_SET
225 				else if (user != NULL && !DontInitGroups &&
226 					 ((gr != NULL &&
227 					   gr->gr_gid == stbuf.st_gid) ||
228 					  (gr = getgrgid(stbuf.st_gid)) != NULL))
229 				{
230 					register char **gp;
231 
232 					for (gp = gr->gr_mem; *gp != NULL; gp++)
233 						if (strcmp(*gp, user) == 0)
234 							break;
235 					if (*gp == NULL)
236 						md >>= 3;
237 				}
238 # endif /* ! NO_GROUP_SET */
239 				else
240 					md >>= 3;
241 			}
242 			if ((stbuf.st_mode & md) != md)
243 				ret = errno = EACCES;
244 		}
245 		else
246 			ret = errno;
247 		if (tTd(44, 4))
248 			sm_dprintf("\t[final dir %s uid %d mode %lo] %s\n",
249 				dir, (int) stbuf.st_uid,
250 				(unsigned long) stbuf.st_mode,
251 				sm_errstring(ret));
252 		if (p != NULL)
253 			*p = '/';
254 		st->st_mode = ST_MODE_NOFILE;
255 		return ret;
256 	}
257 
258 # ifdef S_ISLNK
259 	if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode))
260 	{
261 		if (tTd(44, 4))
262 			sm_dprintf("\t[slink mode %lo]\tE_SM_NOSLINK\n",
263 				(unsigned long) st->st_mode);
264 		return E_SM_NOSLINK;
265 	}
266 # endif /* S_ISLNK */
267 	if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode))
268 	{
269 		if (tTd(44, 4))
270 			sm_dprintf("\t[non-reg mode %lo]\tE_SM_REGONLY\n",
271 				(unsigned long) st->st_mode);
272 		return E_SM_REGONLY;
273 	}
274 	if (bitset(SFF_NOGWFILES, flags) &&
275 	    bitset(S_IWGRP, st->st_mode))
276 	{
277 		if (tTd(44, 4))
278 			sm_dprintf("\t[write bits %lo]\tE_SM_GWFILE\n",
279 				(unsigned long) st->st_mode);
280 		return E_SM_GWFILE;
281 	}
282 	if (bitset(SFF_NOWWFILES, flags) &&
283 	    bitset(S_IWOTH, st->st_mode))
284 	{
285 		if (tTd(44, 4))
286 			sm_dprintf("\t[write bits %lo]\tE_SM_WWFILE\n",
287 				(unsigned long) st->st_mode);
288 		return E_SM_WWFILE;
289 	}
290 	if (bitset(SFF_NOGRFILES, flags) && bitset(S_IRGRP, st->st_mode))
291 	{
292 		if (tTd(44, 4))
293 			sm_dprintf("\t[read bits %lo]\tE_SM_GRFILE\n",
294 				(unsigned long) st->st_mode);
295 		return E_SM_GRFILE;
296 	}
297 	if (bitset(SFF_NOWRFILES, flags) && bitset(S_IROTH, st->st_mode))
298 	{
299 		if (tTd(44, 4))
300 			sm_dprintf("\t[read bits %lo]\tE_SM_WRFILE\n",
301 				(unsigned long) st->st_mode);
302 		return E_SM_WRFILE;
303 	}
304 	if (!bitset(SFF_EXECOK, flags) &&
305 	    bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) &&
306 	    bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode))
307 	{
308 		if (tTd(44, 4))
309 			sm_dprintf("\t[exec bits %lo]\tE_SM_ISEXEC\n",
310 				(unsigned long) st->st_mode);
311 		return E_SM_ISEXEC;
312 	}
313 	if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1)
314 	{
315 		if (tTd(44, 4))
316 			sm_dprintf("\t[link count %d]\tE_SM_NOHLINK\n",
317 				(int) st->st_nlink);
318 		return E_SM_NOHLINK;
319 	}
320 
321 	if (uid == 0 && bitset(SFF_OPENASROOT, flags))
322 		/* EMPTY */
323 		;
324 	else if (uid == 0 && !bitset(SFF_ROOTOK, flags))
325 		mode >>= 6;
326 	else if (st->st_uid == uid)
327 		/* EMPTY */
328 		;
329 	else if (uid == 0 && st->st_uid == TrustedUid)
330 		/* EMPTY */
331 		;
332 	else
333 	{
334 		mode >>= 3;
335 		if (st->st_gid == gid)
336 			/* EMPTY */
337 			;
338 # ifndef NO_GROUP_SET
339 		else if (user != NULL && !DontInitGroups &&
340 			 ((gr != NULL && gr->gr_gid == st->st_gid) ||
341 			  (gr = getgrgid(st->st_gid)) != NULL))
342 		{
343 			register char **gp;
344 
345 			for (gp = gr->gr_mem; *gp != NULL; gp++)
346 				if (strcmp(*gp, user) == 0)
347 					break;
348 			if (*gp == NULL)
349 				mode >>= 3;
350 		}
351 # endif /* ! NO_GROUP_SET */
352 		else
353 			mode >>= 3;
354 	}
355 	if (tTd(44, 4))
356 		sm_dprintf("\t[uid %d, nlink %d, stat %lo, mode %lo] ",
357 			(int) st->st_uid, (int) st->st_nlink,
358 			(unsigned long) st->st_mode, (unsigned long) mode);
359 	if ((st->st_uid == uid || st->st_uid == 0 ||
360 	     st->st_uid == TrustedUid ||
361 	     !bitset(SFF_MUSTOWN, flags)) &&
362 	    (st->st_mode & mode) == mode)
363 	{
364 		if (tTd(44, 4))
365 			sm_dprintf("\tOK\n");
366 		return 0;
367 	}
368 	if (tTd(44, 4))
369 		sm_dprintf("\tEACCES\n");
370 	return EACCES;
371 }
372 /*
373 **  SAFEDIRPATH -- check to make sure a path to a directory is safe
374 **
375 **	Safe means not writable and owned by the right folks.
376 **
377 **	Parameters:
378 **		fn -- filename to check.
379 **		uid -- user id to compare against.
380 **		gid -- group id to compare against.
381 **		user -- user name to compare against (used for group
382 **			sets).
383 **		flags -- modifiers:
384 **			SFF_ROOTOK -- ok to use root permissions to open.
385 **			SFF_SAFEDIRPATH -- writable directories are considered
386 **				to be fatal errors.
387 **		level -- symlink recursive level.
388 **		offset -- offset into fn to start checking from.
389 **
390 **	Returns:
391 **		0 -- if the directory path is "safe".
392 **		else -- an error number associated with the path.
393 */
394 
395 int
396 safedirpath(fn, uid, gid, user, flags, level, offset)
397 	char *fn;
398 	UID_T uid;
399 	GID_T gid;
400 	char *user;
401 	long flags;
402 	int level;
403 	int offset;
404 {
405 	int ret = 0;
406 	int mode = S_IWOTH;
407 	char save = '\0';
408 	char *saveptr = NULL;
409 	char *p, *enddir;
410 	register struct group *gr = NULL;
411 	char s[MAXLINKPATHLEN];
412 	struct stat stbuf;
413 
414 	/* make sure we aren't in a symlink loop */
415 	if (level > MAXSYMLINKS)
416 		return ELOOP;
417 
418 	if (level < 0 || offset < 0 || offset > strlen(fn))
419 		return EINVAL;
420 
421 	/* special case root directory */
422 	if (*fn == '\0')
423 		fn = "/";
424 
425 	if (tTd(44, 4))
426 		sm_dprintf("safedirpath(%s, uid=%ld, gid=%ld, flags=%lx, level=%d, offset=%d):\n",
427 			fn, (long) uid, (long) gid, flags, level, offset);
428 
429 	if (!bitnset(DBS_GROUPWRITABLEDIRPATHSAFE, DontBlameSendmail))
430 		mode |= S_IWGRP;
431 
432 	/* Make a modifiable copy of the filename */
433 	if (sm_strlcpy(s, fn, sizeof s) >= sizeof s)
434 		return EINVAL;
435 
436 	p = s + offset;
437 	while (p != NULL)
438 	{
439 		/* put back character */
440 		if (saveptr != NULL)
441 		{
442 			*saveptr = save;
443 			saveptr = NULL;
444 			p++;
445 		}
446 
447 		if (*p == '\0')
448 			break;
449 
450 		p = strchr(p, '/');
451 
452 		/* Special case for root directory */
453 		if (p == s)
454 		{
455 			save = *(p + 1);
456 			saveptr = p + 1;
457 			*(p + 1) = '\0';
458 		}
459 		else if (p != NULL)
460 		{
461 			save = *p;
462 			saveptr = p;
463 			*p = '\0';
464 		}
465 
466 		/* Heuristic: . and .. have already been checked */
467 		enddir = strrchr(s, '/');
468 		if (enddir != NULL &&
469 		    (strcmp(enddir, "/..") == 0 ||
470 		     strcmp(enddir, "/.") == 0))
471 			continue;
472 
473 		if (tTd(44, 20))
474 			sm_dprintf("\t[dir %s]\n", s);
475 
476 # if HASLSTAT
477 		ret = lstat(s, &stbuf);
478 # else /* HASLSTAT */
479 		ret = stat(s, &stbuf);
480 # endif /* HASLSTAT */
481 		if (ret < 0)
482 		{
483 			ret = errno;
484 			break;
485 		}
486 
487 # ifdef S_ISLNK
488 		/* Follow symlinks */
489 		if (S_ISLNK(stbuf.st_mode))
490 		{
491 			int linklen;
492 			char *target;
493 			char buf[MAXPATHLEN];
494 			char fullbuf[MAXLINKPATHLEN];
495 
496 			memset(buf, '\0', sizeof buf);
497 			linklen = readlink(s, buf, sizeof buf);
498 			if (linklen < 0)
499 			{
500 				ret = errno;
501 				break;
502 			}
503 			if (linklen >= sizeof buf)
504 			{
505 				/* file name too long for buffer */
506 				ret = errno = EINVAL;
507 				break;
508 			}
509 
510 			offset = 0;
511 			if (*buf == '/')
512 			{
513 				target = buf;
514 
515 				/* If path is the same, avoid rechecks */
516 				while (s[offset] == buf[offset] &&
517 				       s[offset] != '\0')
518 					offset++;
519 
520 				if (s[offset] == '\0' && buf[offset] == '\0')
521 				{
522 					/* strings match, symlink loop */
523 					return ELOOP;
524 				}
525 
526 				/* back off from the mismatch */
527 				if (offset > 0)
528 					offset--;
529 
530 				/* Make sure we are at a directory break */
531 				if (offset > 0 &&
532 				    s[offset] != '/' &&
533 				    s[offset] != '\0')
534 				{
535 					while (buf[offset] != '/' &&
536 					       offset > 0)
537 						offset--;
538 				}
539 				if (offset > 0 &&
540 				    s[offset] == '/' &&
541 				    buf[offset] == '/')
542 				{
543 					/* Include the trailing slash */
544 					offset++;
545 				}
546 			}
547 			else
548 			{
549 				char *sptr;
550 
551 				sptr = strrchr(s, '/');
552 				if (sptr != NULL)
553 				{
554 					*sptr = '\0';
555 					offset = sptr + 1 - s;
556 					if (sm_strlcpyn(fullbuf,
557 							sizeof fullbuf, 2,
558 							s, "/") >=
559 						sizeof fullbuf ||
560 					    sm_strlcat(fullbuf, buf,
561 						       sizeof fullbuf) >=
562 						sizeof fullbuf)
563 					{
564 						ret = EINVAL;
565 						break;
566 					}
567 					*sptr = '/';
568 				}
569 				else
570 				{
571 					if (sm_strlcpy(fullbuf, buf,
572 						       sizeof fullbuf) >=
573 						sizeof fullbuf)
574 					{
575 						ret = EINVAL;
576 						break;
577 					}
578 				}
579 				target = fullbuf;
580 			}
581 			ret = safedirpath(target, uid, gid, user, flags,
582 					  level + 1, offset);
583 			if (ret != 0)
584 				break;
585 
586 			/* Don't check permissions on the link file itself */
587 			continue;
588 		}
589 #endif /* S_ISLNK */
590 
591 		if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) &&
592 #ifdef S_ISVTX
593 		    !(bitnset(DBS_TRUSTSTICKYBIT, DontBlameSendmail) &&
594 		      bitset(S_ISVTX, stbuf.st_mode)) &&
595 #endif /* S_ISVTX */
596 		    bitset(mode, stbuf.st_mode))
597 		{
598 			if (tTd(44, 4))
599 				sm_dprintf("\t[dir %s] mode %lo ",
600 					s, (unsigned long) stbuf.st_mode);
601 			if (bitset(SFF_SAFEDIRPATH, flags))
602 			{
603 				if (bitset(S_IWOTH, stbuf.st_mode))
604 					ret = E_SM_WWDIR;
605 				else
606 					ret = E_SM_GWDIR;
607 				if (tTd(44, 4))
608 					sm_dprintf("FATAL\n");
609 				break;
610 			}
611 			if (tTd(44, 4))
612 				sm_dprintf("WARNING\n");
613 			if (Verbose > 1)
614 				message("051 WARNING: %s writable directory %s",
615 					bitset(S_IWOTH, stbuf.st_mode)
616 					   ? "World"
617 					   : "Group",
618 					s);
619 		}
620 		if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags))
621 		{
622 			if (bitset(S_IXOTH, stbuf.st_mode))
623 				continue;
624 			ret = EACCES;
625 			break;
626 		}
627 
628 		/*
629 		**  Let OS determine access to file if we are not
630 		**  running as a privileged user.  This allows ACLs
631 		**  to work.  Also, if opening as root, assume we can
632 		**  scan the directory.
633 		*/
634 		if (geteuid() != 0 || bitset(SFF_OPENASROOT, flags))
635 			continue;
636 
637 		if (stbuf.st_uid == uid &&
638 		    bitset(S_IXUSR, stbuf.st_mode))
639 			continue;
640 		if (stbuf.st_gid == gid &&
641 		    bitset(S_IXGRP, stbuf.st_mode))
642 			continue;
643 # ifndef NO_GROUP_SET
644 		if (user != NULL && !DontInitGroups &&
645 		    ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
646 		     (gr = getgrgid(stbuf.st_gid)) != NULL))
647 		{
648 			register char **gp;
649 
650 			for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
651 				if (strcmp(*gp, user) == 0)
652 					break;
653 			if (gp != NULL && *gp != NULL &&
654 			    bitset(S_IXGRP, stbuf.st_mode))
655 				continue;
656 		}
657 # endif /* ! NO_GROUP_SET */
658 		if (!bitset(S_IXOTH, stbuf.st_mode))
659 		{
660 			ret = EACCES;
661 			break;
662 		}
663 	}
664 	if (tTd(44, 4))
665 		sm_dprintf("\t[dir %s] %s\n", fn,
666 			ret == 0 ? "OK" : sm_errstring(ret));
667 	return ret;
668 }
669 /*
670 **  SAFEOPEN -- do a file open with extra checking
671 **
672 **	Parameters:
673 **		fn -- the file name to open.
674 **		omode -- the open-style mode flags.
675 **		cmode -- the create-style mode flags.
676 **		sff -- safefile flags.
677 **
678 **	Returns:
679 **		Same as open.
680 */
681 
682 int
683 safeopen(fn, omode, cmode, sff)
684 	char *fn;
685 	int omode;
686 	int cmode;
687 	long sff;
688 {
689 #if !NOFTRUNCATE
690 	bool truncate;
691 #endif /* !NOFTRUNCATE */
692 	int rval;
693 	int fd;
694 	int smode;
695 	struct stat stb;
696 
697 	if (tTd(44, 10))
698 		sm_dprintf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n",
699 			   fn, omode, cmode, sff);
700 
701 	if (bitset(O_CREAT, omode))
702 		sff |= SFF_CREAT;
703 	omode &= ~O_CREAT;
704 	smode = 0;
705 	switch (omode & O_ACCMODE)
706 	{
707 	  case O_RDONLY:
708 		smode = S_IREAD;
709 		break;
710 
711 	  case O_WRONLY:
712 		smode = S_IWRITE;
713 		break;
714 
715 	  case O_RDWR:
716 		smode = S_IREAD|S_IWRITE;
717 		break;
718 
719 	  default:
720 		smode = 0;
721 		break;
722 	}
723 	if (bitset(SFF_OPENASROOT, sff))
724 		rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName,
725 				sff, smode, &stb);
726 	else
727 		rval = safefile(fn, RealUid, RealGid, RealUserName,
728 				sff, smode, &stb);
729 	if (rval != 0)
730 	{
731 		errno = rval;
732 		return -1;
733 	}
734 	if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff))
735 		omode |= O_CREAT | (bitset(SFF_NOTEXCL, sff) ? 0 : O_EXCL);
736 	else if (bitset(SFF_CREAT, sff) && bitset(O_EXCL, omode))
737 	{
738 		/* The file exists so an exclusive create would fail */
739 		errno = EEXIST;
740 		return -1;
741 	}
742 
743 #if !NOFTRUNCATE
744 	truncate = bitset(O_TRUNC, omode);
745 	if (truncate)
746 		omode &= ~O_TRUNC;
747 #endif /* !NOFTRUNCATE */
748 
749 	fd = dfopen(fn, omode, cmode, sff);
750 	if (fd < 0)
751 		return fd;
752 	if (filechanged(fn, fd, &stb))
753 	{
754 		syserr("554 5.3.0 cannot open: file %s changed after open", fn);
755 		(void) close(fd);
756 		errno = E_SM_FILECHANGE;
757 		return -1;
758 	}
759 
760 #if !NOFTRUNCATE
761 	if (truncate &&
762 	    ftruncate(fd, (off_t) 0) < 0)
763 	{
764 		int save_errno;
765 
766 		save_errno = errno;
767 		syserr("554 5.3.0 cannot open: file %s could not be truncated",
768 		       fn);
769 		(void) close(fd);
770 		errno = save_errno;
771 		return -1;
772 	}
773 #endif /* !NOFTRUNCATE */
774 
775 	return fd;
776 }
777 /*
778 **  SAFEFOPEN -- do a file open with extra checking
779 **
780 **	Parameters:
781 **		fn -- the file name to open.
782 **		omode -- the open-style mode flags.
783 **		cmode -- the create-style mode flags.
784 **		sff -- safefile flags.
785 **
786 **	Returns:
787 **		Same as fopen.
788 */
789 
790 SM_FILE_T *
791 safefopen(fn, omode, cmode, sff)
792 	char *fn;
793 	int omode;
794 	int cmode;
795 	long sff;
796 {
797 	int fd;
798 	int save_errno;
799 	SM_FILE_T *fp;
800 	int fmode;
801 
802 	switch (omode & O_ACCMODE)
803 	{
804 	  case O_RDONLY:
805 		fmode = SM_IO_RDONLY;
806 		break;
807 
808 	  case O_WRONLY:
809 		if (bitset(O_APPEND, omode))
810 			fmode = SM_IO_APPEND;
811 		else
812 			fmode = SM_IO_WRONLY;
813 		break;
814 
815 	  case O_RDWR:
816 		if (bitset(O_TRUNC, omode))
817 			fmode = SM_IO_RDWRTR;
818 		else if (bitset(O_APPEND, omode))
819 			fmode = SM_IO_APPENDRW;
820 		else
821 			fmode = SM_IO_RDWR;
822 		break;
823 
824 	  default:
825 		syserr("554 5.3.5 safefopen: unknown omode %o", omode);
826 		fmode = 0;
827 	}
828 	fd = safeopen(fn, omode, cmode, sff);
829 	if (fd < 0)
830 	{
831 		save_errno = errno;
832 		if (tTd(44, 10))
833 			sm_dprintf("safefopen: safeopen failed: %s\n",
834 				   sm_errstring(errno));
835 		errno = save_errno;
836 		return NULL;
837 	}
838 	fp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
839 			(void *) &fd, fmode, NULL);
840 	if (fp != NULL)
841 		return fp;
842 
843 	save_errno = errno;
844 	if (tTd(44, 10))
845 	{
846 		sm_dprintf("safefopen: fdopen(%s, %d) failed: omode=%x, sff=%lx, err=%s\n",
847 			   fn, fmode, omode, sff, sm_errstring(errno));
848 	}
849 	(void) close(fd);
850 	errno = save_errno;
851 	return NULL;
852 }
853 /*
854 **  FILECHANGED -- check to see if file changed after being opened
855 **
856 **	Parameters:
857 **		fn -- pathname of file to check.
858 **		fd -- file descriptor to check.
859 **		stb -- stat structure from before open.
860 **
861 **	Returns:
862 **		true -- if a problem was detected.
863 **		false -- if this file is still the same.
864 */
865 
866 bool
867 filechanged(fn, fd, stb)
868 	char *fn;
869 	int fd;
870 	struct stat *stb;
871 {
872 	struct stat sta;
873 
874 	if (stb->st_mode == ST_MODE_NOFILE)
875 	{
876 # if HASLSTAT && BOGUS_O_EXCL
877 		/* only necessary if exclusive open follows symbolic links */
878 		if (lstat(fn, stb) < 0 || stb->st_nlink != 1)
879 			return true;
880 # else /* HASLSTAT && BOGUS_O_EXCL */
881 		return false;
882 # endif /* HASLSTAT && BOGUS_O_EXCL */
883 	}
884 	if (fstat(fd, &sta) < 0)
885 		return true;
886 
887 	if (sta.st_nlink != stb->st_nlink ||
888 	    sta.st_dev != stb->st_dev ||
889 	    sta.st_ino != stb->st_ino ||
890 # if HAS_ST_GEN && 0		/* AFS returns garbage in st_gen */
891 	    sta.st_gen != stb->st_gen ||
892 # endif /* HAS_ST_GEN && 0 */
893 	    sta.st_uid != stb->st_uid ||
894 	    sta.st_gid != stb->st_gid)
895 	{
896 		if (tTd(44, 8))
897 		{
898 			sm_dprintf("File changed after opening:\n");
899 			sm_dprintf(" nlink	= %ld/%ld\n",
900 				(long) stb->st_nlink, (long) sta.st_nlink);
901 			sm_dprintf(" dev	= %ld/%ld\n",
902 				(long) stb->st_dev, (long) sta.st_dev);
903 			sm_dprintf(" ino	= %llu/%llu\n",
904 				(ULONGLONG_T) stb->st_ino,
905 				(ULONGLONG_T) sta.st_ino);
906 # if HAS_ST_GEN
907 			sm_dprintf(" gen	= %ld/%ld\n",
908 				(long) stb->st_gen, (long) sta.st_gen);
909 # endif /* HAS_ST_GEN */
910 			sm_dprintf(" uid	= %ld/%ld\n",
911 				(long) stb->st_uid, (long) sta.st_uid);
912 			sm_dprintf(" gid	= %ld/%ld\n",
913 				(long) stb->st_gid, (long) sta.st_gid);
914 		}
915 		return true;
916 	}
917 
918 	return false;
919 }
920 /*
921 **  DFOPEN -- determined file open
922 **
923 **	This routine has the semantics of open, except that it will
924 **	keep trying a few times to make this happen.  The idea is that
925 **	on very loaded systems, we may run out of resources (inodes,
926 **	whatever), so this tries to get around it.
927 */
928 
929 int
930 dfopen(filename, omode, cmode, sff)
931 	char *filename;
932 	int omode;
933 	int cmode;
934 	long sff;
935 {
936 	register int tries;
937 	int fd = -1;
938 	struct stat st;
939 
940 	for (tries = 0; tries < 10; tries++)
941 	{
942 		(void) sleep((unsigned) (10 * tries));
943 		errno = 0;
944 		fd = open(filename, omode, cmode);
945 		if (fd >= 0)
946 			break;
947 		switch (errno)
948 		{
949 		  case ENFILE:		/* system file table full */
950 		  case EINTR:		/* interrupted syscall */
951 #ifdef ETXTBSY
952 		  case ETXTBSY:		/* Apollo: net file locked */
953 #endif /* ETXTBSY */
954 			continue;
955 		}
956 		break;
957 	}
958 	if (!bitset(SFF_NOLOCK, sff) &&
959 	    fd >= 0 &&
960 	    fstat(fd, &st) >= 0 &&
961 	    S_ISREG(st.st_mode))
962 	{
963 		int locktype;
964 
965 		/* lock the file to avoid accidental conflicts */
966 		if ((omode & O_ACCMODE) != O_RDONLY)
967 			locktype = LOCK_EX;
968 		else
969 			locktype = LOCK_SH;
970 		if (bitset(SFF_NBLOCK, sff))
971 			locktype |= LOCK_NB;
972 
973 		if (!lockfile(fd, filename, NULL, locktype))
974 		{
975 			int save_errno = errno;
976 
977 			(void) close(fd);
978 			fd = -1;
979 			errno = save_errno;
980 		}
981 		else
982 			errno = 0;
983 	}
984 	return fd;
985 }
986