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