]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/libsmutil/safefile.c
Merge sendmail 8.16.1 to HEAD: See contrib/sendmail/RELEASE_NOTES for details
[FreeBSD/FreeBSD.git] / contrib / sendmail / libsmutil / safefile.c
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 }