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
safedirpath(fn,uid,gid,user,flags,level,offset)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
safeopen(fn,omode,cmode,sff)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 *
safefopen(fn,omode,cmode,sff)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
filechanged(fn,fd,stb)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
dfopen(filename,omode,cmode,sff)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