106f25ae9SGregory Neil Shapiro /*
25dd76dd0SGregory Neil Shapiro * Copyright (c) 1998-2004 Proofpoint, Inc. and its suppliers.
306f25ae9SGregory Neil Shapiro * All rights reserved.
406f25ae9SGregory Neil Shapiro * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
506f25ae9SGregory Neil Shapiro * Copyright (c) 1988, 1993
606f25ae9SGregory Neil Shapiro * The Regents of the University of California. All rights reserved.
706f25ae9SGregory Neil Shapiro *
806f25ae9SGregory Neil Shapiro * By using this file, you agree to the terms and conditions set
906f25ae9SGregory Neil Shapiro * forth in the LICENSE file which can be found at the top level of
1006f25ae9SGregory Neil Shapiro * the sendmail distribution.
1106f25ae9SGregory Neil Shapiro *
1206f25ae9SGregory Neil Shapiro */
1306f25ae9SGregory Neil Shapiro
1406f25ae9SGregory Neil Shapiro #include <sendmail.h>
1540266059SGregory Neil Shapiro #include <sm/io.h>
1640266059SGregory Neil Shapiro #include <sm/errstring.h>
1740266059SGregory Neil Shapiro
184313cc83SGregory Neil Shapiro SM_RCSID("@(#)$Id: safefile.c,v 8.130 2013-11-22 20:51:50 ca Exp $")
1906f25ae9SGregory Neil Shapiro
2006f25ae9SGregory Neil Shapiro
2140266059SGregory Neil Shapiro /*
2206f25ae9SGregory Neil Shapiro ** SAFEFILE -- return 0 if a file exists and is safe for a user.
2306f25ae9SGregory Neil Shapiro **
2406f25ae9SGregory Neil Shapiro ** Parameters:
2506f25ae9SGregory Neil Shapiro ** fn -- filename to check.
2606f25ae9SGregory Neil Shapiro ** uid -- user id to compare against.
2706f25ae9SGregory Neil Shapiro ** gid -- group id to compare against.
28*5b0945b5SGregory Neil Shapiro ** user -- user name to compare against (used for group sets).
2906f25ae9SGregory Neil Shapiro ** flags -- modifiers:
3006f25ae9SGregory Neil Shapiro ** SFF_MUSTOWN -- "uid" must own this file.
3106f25ae9SGregory Neil Shapiro ** SFF_NOSLINK -- file cannot be a symbolic link.
3206f25ae9SGregory Neil Shapiro ** mode -- mode bits that must match.
3306f25ae9SGregory Neil Shapiro ** st -- if set, points to a stat structure that will
3406f25ae9SGregory Neil Shapiro ** get the stat info for the file.
3506f25ae9SGregory Neil Shapiro **
3606f25ae9SGregory Neil Shapiro ** Returns:
3706f25ae9SGregory Neil Shapiro ** 0 if fn exists, is owned by uid, and matches mode.
3806f25ae9SGregory Neil Shapiro ** An errno otherwise. The actual errno is cleared.
3906f25ae9SGregory Neil Shapiro **
4006f25ae9SGregory Neil Shapiro ** Side Effects:
4106f25ae9SGregory Neil Shapiro ** none.
4206f25ae9SGregory Neil Shapiro */
4306f25ae9SGregory Neil Shapiro
4406f25ae9SGregory Neil Shapiro int
4506f25ae9SGregory Neil Shapiro safefile(fn, uid, gid, user, flags, mode, st)
4606f25ae9SGregory Neil Shapiro char *fn;
4706f25ae9SGregory Neil Shapiro UID_T uid;
4806f25ae9SGregory Neil Shapiro GID_T gid;
4906f25ae9SGregory Neil Shapiro char *user;
5006f25ae9SGregory Neil Shapiro long flags;
5106f25ae9SGregory Neil Shapiro int mode;
5206f25ae9SGregory Neil Shapiro struct stat *st;
5306f25ae9SGregory Neil Shapiro {
5406f25ae9SGregory Neil Shapiro register char *p;
5506f25ae9SGregory Neil Shapiro register struct group *gr = NULL;
5606f25ae9SGregory Neil Shapiro int file_errno = 0;
5706f25ae9SGregory Neil Shapiro bool checkpath;
5806f25ae9SGregory Neil Shapiro struct stat stbuf;
5906f25ae9SGregory Neil Shapiro struct stat fstbuf;
6094c01205SGregory Neil Shapiro char fbuf[MAXPATHLEN];
6106f25ae9SGregory Neil Shapiro
6206f25ae9SGregory Neil Shapiro if (tTd(44, 4))
6340266059SGregory Neil Shapiro sm_dprintf("safefile(%s, uid=%d, gid=%d, flags=%lx, mode=%o):\n",
6406f25ae9SGregory Neil Shapiro fn, (int) uid, (int) gid, flags, mode);
6506f25ae9SGregory Neil Shapiro errno = 0;
6640266059SGregory Neil Shapiro if (sm_strlcpy(fbuf, fn, sizeof fbuf) >= sizeof fbuf)
6706f25ae9SGregory Neil Shapiro {
6806f25ae9SGregory Neil Shapiro if (tTd(44, 4))
6940266059SGregory Neil Shapiro sm_dprintf("\tpathname too long\n");
7006f25ae9SGregory Neil Shapiro return ENAMETOOLONG;
7106f25ae9SGregory Neil Shapiro }
7206f25ae9SGregory Neil Shapiro fn = fbuf;
7340266059SGregory Neil Shapiro if (st == NULL)
7440266059SGregory Neil Shapiro st = &fstbuf;
7506f25ae9SGregory Neil Shapiro
7606f25ae9SGregory Neil Shapiro /* ignore SFF_SAFEDIRPATH if we are debugging */
7706f25ae9SGregory Neil Shapiro if (RealUid != 0 && RunAsUid == RealUid)
7806f25ae9SGregory Neil Shapiro flags &= ~SFF_SAFEDIRPATH;
7906f25ae9SGregory Neil Shapiro
8006f25ae9SGregory Neil Shapiro /* first check to see if the file exists at all */
8106f25ae9SGregory Neil Shapiro #if HASLSTAT
8206f25ae9SGregory Neil Shapiro if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st)
8306f25ae9SGregory Neil Shapiro : stat(fn, st)) < 0)
84*5b0945b5SGregory Neil Shapiro #else
8506f25ae9SGregory Neil Shapiro if (stat(fn, st) < 0)
86*5b0945b5SGregory Neil Shapiro #endif
8706f25ae9SGregory Neil Shapiro {
8806f25ae9SGregory Neil Shapiro file_errno = errno;
8906f25ae9SGregory Neil Shapiro }
9006f25ae9SGregory Neil Shapiro else if (bitset(SFF_SETUIDOK, flags) &&
9106f25ae9SGregory Neil Shapiro !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) &&
9206f25ae9SGregory Neil Shapiro S_ISREG(st->st_mode))
9306f25ae9SGregory Neil Shapiro {
9406f25ae9SGregory Neil Shapiro /*
9540266059SGregory Neil Shapiro ** If final file is set-user-ID, run as the owner of that
9606f25ae9SGregory Neil Shapiro ** file. Gotta be careful not to reveal anything too
9706f25ae9SGregory Neil Shapiro ** soon here!
9806f25ae9SGregory Neil Shapiro */
9906f25ae9SGregory Neil Shapiro
10006f25ae9SGregory Neil Shapiro #ifdef SUID_ROOT_FILES_OK
10106f25ae9SGregory Neil Shapiro if (bitset(S_ISUID, st->st_mode))
102*5b0945b5SGregory Neil Shapiro #else
10306f25ae9SGregory Neil Shapiro if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 &&
10406f25ae9SGregory Neil Shapiro st->st_uid != TrustedUid)
105*5b0945b5SGregory Neil Shapiro #endif
10606f25ae9SGregory Neil Shapiro {
10706f25ae9SGregory Neil Shapiro uid = st->st_uid;
10806f25ae9SGregory Neil Shapiro user = NULL;
10906f25ae9SGregory Neil Shapiro }
11006f25ae9SGregory Neil Shapiro #ifdef SUID_ROOT_FILES_OK
11106f25ae9SGregory Neil Shapiro if (bitset(S_ISGID, st->st_mode))
112*5b0945b5SGregory Neil Shapiro #else
11306f25ae9SGregory Neil Shapiro if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0)
114*5b0945b5SGregory Neil Shapiro #endif
11506f25ae9SGregory Neil Shapiro gid = st->st_gid;
11606f25ae9SGregory Neil Shapiro }
11706f25ae9SGregory Neil Shapiro
11806f25ae9SGregory Neil Shapiro checkpath = !bitset(SFF_NOPATHCHECK, flags) ||
11906f25ae9SGregory Neil Shapiro (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags));
12006f25ae9SGregory Neil Shapiro if (bitset(SFF_NOWLINK, flags) && !bitset(SFF_SAFEDIRPATH, flags))
12106f25ae9SGregory Neil Shapiro {
12206f25ae9SGregory Neil Shapiro int ret;
12306f25ae9SGregory Neil Shapiro
12406f25ae9SGregory Neil Shapiro /* check the directory */
12506f25ae9SGregory Neil Shapiro p = strrchr(fn, '/');
12606f25ae9SGregory Neil Shapiro if (p == NULL)
12706f25ae9SGregory Neil Shapiro {
12806f25ae9SGregory Neil Shapiro ret = safedirpath(".", uid, gid, user,
12906f25ae9SGregory Neil Shapiro flags|SFF_SAFEDIRPATH, 0, 0);
13006f25ae9SGregory Neil Shapiro }
13106f25ae9SGregory Neil Shapiro else
13206f25ae9SGregory Neil Shapiro {
13306f25ae9SGregory Neil Shapiro *p = '\0';
13406f25ae9SGregory Neil Shapiro ret = safedirpath(fn, uid, gid, user,
13506f25ae9SGregory Neil Shapiro flags|SFF_SAFEDIRPATH, 0, 0);
13606f25ae9SGregory Neil Shapiro *p = '/';
13706f25ae9SGregory Neil Shapiro }
13806f25ae9SGregory Neil Shapiro if (ret == 0)
13906f25ae9SGregory Neil Shapiro {
14006f25ae9SGregory Neil Shapiro /* directory is safe */
14140266059SGregory Neil Shapiro checkpath = false;
14206f25ae9SGregory Neil Shapiro }
14306f25ae9SGregory Neil Shapiro else
14406f25ae9SGregory Neil Shapiro {
14506f25ae9SGregory Neil Shapiro #if HASLSTAT
14606f25ae9SGregory Neil Shapiro /* Need lstat() information if called stat() before */
14706f25ae9SGregory Neil Shapiro if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0)
14806f25ae9SGregory Neil Shapiro {
14906f25ae9SGregory Neil Shapiro ret = errno;
15006f25ae9SGregory Neil Shapiro if (tTd(44, 4))
15140266059SGregory Neil Shapiro sm_dprintf("\t%s\n", sm_errstring(ret));
15206f25ae9SGregory Neil Shapiro return ret;
15306f25ae9SGregory Neil Shapiro }
15406f25ae9SGregory Neil Shapiro #endif /* HASLSTAT */
15506f25ae9SGregory Neil Shapiro /* directory is writable: disallow links */
15606f25ae9SGregory Neil Shapiro flags |= SFF_NOLINK;
15706f25ae9SGregory Neil Shapiro }
15806f25ae9SGregory Neil Shapiro }
15906f25ae9SGregory Neil Shapiro
16006f25ae9SGregory Neil Shapiro if (checkpath)
16106f25ae9SGregory Neil Shapiro {
16206f25ae9SGregory Neil Shapiro int ret;
16306f25ae9SGregory Neil Shapiro
16406f25ae9SGregory Neil Shapiro p = strrchr(fn, '/');
16506f25ae9SGregory Neil Shapiro if (p == NULL)
16606f25ae9SGregory Neil Shapiro {
16706f25ae9SGregory Neil Shapiro ret = safedirpath(".", uid, gid, user, flags, 0, 0);
16806f25ae9SGregory Neil Shapiro }
16906f25ae9SGregory Neil Shapiro else
17006f25ae9SGregory Neil Shapiro {
17106f25ae9SGregory Neil Shapiro *p = '\0';
17206f25ae9SGregory Neil Shapiro ret = safedirpath(fn, uid, gid, user, flags, 0, 0);
17306f25ae9SGregory Neil Shapiro *p = '/';
17406f25ae9SGregory Neil Shapiro }
17506f25ae9SGregory Neil Shapiro if (ret != 0)
17606f25ae9SGregory Neil Shapiro return ret;
17706f25ae9SGregory Neil Shapiro }
17806f25ae9SGregory Neil Shapiro
17906f25ae9SGregory Neil Shapiro /*
18006f25ae9SGregory Neil Shapiro ** If the target file doesn't exist, check the directory to
18106f25ae9SGregory Neil Shapiro ** ensure that it is writable by this user.
18206f25ae9SGregory Neil Shapiro */
18306f25ae9SGregory Neil Shapiro
18406f25ae9SGregory Neil Shapiro if (file_errno != 0)
18506f25ae9SGregory Neil Shapiro {
18606f25ae9SGregory Neil Shapiro int ret = file_errno;
18706f25ae9SGregory Neil Shapiro char *dir = fn;
18806f25ae9SGregory Neil Shapiro
18906f25ae9SGregory Neil Shapiro if (tTd(44, 4))
19040266059SGregory Neil Shapiro sm_dprintf("\t%s\n", sm_errstring(ret));
19106f25ae9SGregory Neil Shapiro
19206f25ae9SGregory Neil Shapiro errno = 0;
19306f25ae9SGregory Neil Shapiro if (!bitset(SFF_CREAT, flags) || file_errno != ENOENT)
19406f25ae9SGregory Neil Shapiro return ret;
19506f25ae9SGregory Neil Shapiro
19606f25ae9SGregory Neil Shapiro /* check to see if legal to create the file */
19706f25ae9SGregory Neil Shapiro p = strrchr(dir, '/');
19806f25ae9SGregory Neil Shapiro if (p == NULL)
19906f25ae9SGregory Neil Shapiro dir = ".";
20006f25ae9SGregory Neil Shapiro else if (p == dir)
20106f25ae9SGregory Neil Shapiro dir = "/";
20206f25ae9SGregory Neil Shapiro else
20306f25ae9SGregory Neil Shapiro *p = '\0';
20406f25ae9SGregory Neil Shapiro if (stat(dir, &stbuf) >= 0)
20506f25ae9SGregory Neil Shapiro {
20606f25ae9SGregory Neil Shapiro int md = S_IWRITE|S_IEXEC;
20706f25ae9SGregory Neil Shapiro
20840266059SGregory Neil Shapiro ret = 0;
20906f25ae9SGregory Neil Shapiro if (stbuf.st_uid == uid)
21006f25ae9SGregory Neil Shapiro /* EMPTY */
21106f25ae9SGregory Neil Shapiro ;
21206f25ae9SGregory Neil Shapiro else if (uid == 0 && stbuf.st_uid == TrustedUid)
21306f25ae9SGregory Neil Shapiro /* EMPTY */
21406f25ae9SGregory Neil Shapiro ;
21506f25ae9SGregory Neil Shapiro else
21606f25ae9SGregory Neil Shapiro {
21706f25ae9SGregory Neil Shapiro md >>= 3;
21806f25ae9SGregory Neil Shapiro if (stbuf.st_gid == gid)
21906f25ae9SGregory Neil Shapiro /* EMPTY */
22006f25ae9SGregory Neil Shapiro ;
22106f25ae9SGregory Neil Shapiro #ifndef NO_GROUP_SET
22206f25ae9SGregory Neil Shapiro else if (user != NULL && !DontInitGroups &&
22306f25ae9SGregory Neil Shapiro ((gr != NULL &&
22406f25ae9SGregory Neil Shapiro gr->gr_gid == stbuf.st_gid) ||
22506f25ae9SGregory Neil Shapiro (gr = getgrgid(stbuf.st_gid)) != NULL))
22606f25ae9SGregory Neil Shapiro {
22706f25ae9SGregory Neil Shapiro register char **gp;
22806f25ae9SGregory Neil Shapiro
22906f25ae9SGregory Neil Shapiro for (gp = gr->gr_mem; *gp != NULL; gp++)
23006f25ae9SGregory Neil Shapiro if (strcmp(*gp, user) == 0)
23106f25ae9SGregory Neil Shapiro break;
23206f25ae9SGregory Neil Shapiro if (*gp == NULL)
23306f25ae9SGregory Neil Shapiro md >>= 3;
23406f25ae9SGregory Neil Shapiro }
23506f25ae9SGregory Neil Shapiro #endif /* ! NO_GROUP_SET */
23606f25ae9SGregory Neil Shapiro else
23706f25ae9SGregory Neil Shapiro md >>= 3;
23806f25ae9SGregory Neil Shapiro }
23906f25ae9SGregory Neil Shapiro if ((stbuf.st_mode & md) != md)
24040266059SGregory Neil Shapiro ret = errno = EACCES;
24106f25ae9SGregory Neil Shapiro }
24240266059SGregory Neil Shapiro else
24306f25ae9SGregory Neil Shapiro ret = errno;
24406f25ae9SGregory Neil Shapiro if (tTd(44, 4))
24540266059SGregory Neil Shapiro sm_dprintf("\t[final dir %s uid %d mode %lo] %s\n",
24640266059SGregory Neil Shapiro dir, (int) stbuf.st_uid,
24740266059SGregory Neil Shapiro (unsigned long) stbuf.st_mode,
24840266059SGregory Neil Shapiro sm_errstring(ret));
24906f25ae9SGregory Neil Shapiro if (p != NULL)
25006f25ae9SGregory Neil Shapiro *p = '/';
25106f25ae9SGregory Neil Shapiro st->st_mode = ST_MODE_NOFILE;
25206f25ae9SGregory Neil Shapiro return ret;
25306f25ae9SGregory Neil Shapiro }
25406f25ae9SGregory Neil Shapiro
25506f25ae9SGregory Neil Shapiro #ifdef S_ISLNK
25606f25ae9SGregory Neil Shapiro if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode))
25706f25ae9SGregory Neil Shapiro {
25806f25ae9SGregory Neil Shapiro if (tTd(44, 4))
25940266059SGregory Neil Shapiro sm_dprintf("\t[slink mode %lo]\tE_SM_NOSLINK\n",
26040266059SGregory Neil Shapiro (unsigned long) st->st_mode);
26106f25ae9SGregory Neil Shapiro return E_SM_NOSLINK;
26206f25ae9SGregory Neil Shapiro }
26306f25ae9SGregory Neil Shapiro #endif /* S_ISLNK */
26406f25ae9SGregory Neil Shapiro if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode))
26506f25ae9SGregory Neil Shapiro {
26606f25ae9SGregory Neil Shapiro if (tTd(44, 4))
26740266059SGregory Neil Shapiro sm_dprintf("\t[non-reg mode %lo]\tE_SM_REGONLY\n",
26840266059SGregory Neil Shapiro (unsigned long) st->st_mode);
26906f25ae9SGregory Neil Shapiro return E_SM_REGONLY;
27006f25ae9SGregory Neil Shapiro }
27106f25ae9SGregory Neil Shapiro if (bitset(SFF_NOGWFILES, flags) &&
27206f25ae9SGregory Neil Shapiro bitset(S_IWGRP, st->st_mode))
27306f25ae9SGregory Neil Shapiro {
27406f25ae9SGregory Neil Shapiro if (tTd(44, 4))
27540266059SGregory Neil Shapiro sm_dprintf("\t[write bits %lo]\tE_SM_GWFILE\n",
27640266059SGregory Neil Shapiro (unsigned long) st->st_mode);
27706f25ae9SGregory Neil Shapiro return E_SM_GWFILE;
27806f25ae9SGregory Neil Shapiro }
27906f25ae9SGregory Neil Shapiro if (bitset(SFF_NOWWFILES, flags) &&
28006f25ae9SGregory Neil Shapiro bitset(S_IWOTH, st->st_mode))
28106f25ae9SGregory Neil Shapiro {
28206f25ae9SGregory Neil Shapiro if (tTd(44, 4))
28340266059SGregory Neil Shapiro sm_dprintf("\t[write bits %lo]\tE_SM_WWFILE\n",
28440266059SGregory Neil Shapiro (unsigned long) st->st_mode);
28506f25ae9SGregory Neil Shapiro return E_SM_WWFILE;
28606f25ae9SGregory Neil Shapiro }
28706f25ae9SGregory Neil Shapiro if (bitset(SFF_NOGRFILES, flags) && bitset(S_IRGRP, st->st_mode))
28806f25ae9SGregory Neil Shapiro {
28906f25ae9SGregory Neil Shapiro if (tTd(44, 4))
29040266059SGregory Neil Shapiro sm_dprintf("\t[read bits %lo]\tE_SM_GRFILE\n",
29140266059SGregory Neil Shapiro (unsigned long) st->st_mode);
29206f25ae9SGregory Neil Shapiro return E_SM_GRFILE;
29306f25ae9SGregory Neil Shapiro }
29406f25ae9SGregory Neil Shapiro if (bitset(SFF_NOWRFILES, flags) && bitset(S_IROTH, st->st_mode))
29506f25ae9SGregory Neil Shapiro {
29606f25ae9SGregory Neil Shapiro if (tTd(44, 4))
29740266059SGregory Neil Shapiro sm_dprintf("\t[read bits %lo]\tE_SM_WRFILE\n",
29840266059SGregory Neil Shapiro (unsigned long) st->st_mode);
29906f25ae9SGregory Neil Shapiro return E_SM_WRFILE;
30006f25ae9SGregory Neil Shapiro }
30106f25ae9SGregory Neil Shapiro if (!bitset(SFF_EXECOK, flags) &&
30206f25ae9SGregory Neil Shapiro bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) &&
30306f25ae9SGregory Neil Shapiro bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode))
30406f25ae9SGregory Neil Shapiro {
30506f25ae9SGregory Neil Shapiro if (tTd(44, 4))
306e92d3f3fSGregory Neil Shapiro sm_dprintf("\t[exec bits %lo]\tE_SM_ISEXEC\n",
30740266059SGregory Neil Shapiro (unsigned long) st->st_mode);
30806f25ae9SGregory Neil Shapiro return E_SM_ISEXEC;
30906f25ae9SGregory Neil Shapiro }
31006f25ae9SGregory Neil Shapiro if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1)
31106f25ae9SGregory Neil Shapiro {
31206f25ae9SGregory Neil Shapiro if (tTd(44, 4))
31340266059SGregory Neil Shapiro sm_dprintf("\t[link count %d]\tE_SM_NOHLINK\n",
31406f25ae9SGregory Neil Shapiro (int) st->st_nlink);
31506f25ae9SGregory Neil Shapiro return E_SM_NOHLINK;
31606f25ae9SGregory Neil Shapiro }
31706f25ae9SGregory Neil Shapiro
31806f25ae9SGregory Neil Shapiro if (uid == 0 && bitset(SFF_OPENASROOT, flags))
31906f25ae9SGregory Neil Shapiro /* EMPTY */
32006f25ae9SGregory Neil Shapiro ;
32106f25ae9SGregory Neil Shapiro else if (uid == 0 && !bitset(SFF_ROOTOK, flags))
32206f25ae9SGregory Neil Shapiro mode >>= 6;
32306f25ae9SGregory Neil Shapiro else if (st->st_uid == uid)
32406f25ae9SGregory Neil Shapiro /* EMPTY */
32506f25ae9SGregory Neil Shapiro ;
32606f25ae9SGregory Neil Shapiro else if (uid == 0 && st->st_uid == TrustedUid)
32706f25ae9SGregory Neil Shapiro /* EMPTY */
32806f25ae9SGregory Neil Shapiro ;
32906f25ae9SGregory Neil Shapiro else
33006f25ae9SGregory Neil Shapiro {
33106f25ae9SGregory Neil Shapiro mode >>= 3;
33206f25ae9SGregory Neil Shapiro if (st->st_gid == gid)
33306f25ae9SGregory Neil Shapiro /* EMPTY */
33406f25ae9SGregory Neil Shapiro ;
33506f25ae9SGregory Neil Shapiro #ifndef NO_GROUP_SET
33606f25ae9SGregory Neil Shapiro else if (user != NULL && !DontInitGroups &&
33706f25ae9SGregory Neil Shapiro ((gr != NULL && gr->gr_gid == st->st_gid) ||
33806f25ae9SGregory Neil Shapiro (gr = getgrgid(st->st_gid)) != NULL))
33906f25ae9SGregory Neil Shapiro {
34006f25ae9SGregory Neil Shapiro register char **gp;
34106f25ae9SGregory Neil Shapiro
34206f25ae9SGregory Neil Shapiro for (gp = gr->gr_mem; *gp != NULL; gp++)
34306f25ae9SGregory Neil Shapiro if (strcmp(*gp, user) == 0)
34406f25ae9SGregory Neil Shapiro break;
34506f25ae9SGregory Neil Shapiro if (*gp == NULL)
34606f25ae9SGregory Neil Shapiro mode >>= 3;
34706f25ae9SGregory Neil Shapiro }
34806f25ae9SGregory Neil Shapiro #endif /* ! NO_GROUP_SET */
34906f25ae9SGregory Neil Shapiro else
35006f25ae9SGregory Neil Shapiro mode >>= 3;
35106f25ae9SGregory Neil Shapiro }
35206f25ae9SGregory Neil Shapiro if (tTd(44, 4))
35340266059SGregory Neil Shapiro sm_dprintf("\t[uid %d, nlink %d, stat %lo, mode %lo] ",
35406f25ae9SGregory Neil Shapiro (int) st->st_uid, (int) st->st_nlink,
35540266059SGregory Neil Shapiro (unsigned long) st->st_mode, (unsigned long) mode);
35606f25ae9SGregory Neil Shapiro if ((st->st_uid == uid || st->st_uid == 0 ||
35706f25ae9SGregory Neil Shapiro st->st_uid == TrustedUid ||
35806f25ae9SGregory Neil Shapiro !bitset(SFF_MUSTOWN, flags)) &&
35906f25ae9SGregory Neil Shapiro (st->st_mode & mode) == mode)
36006f25ae9SGregory Neil Shapiro {
36106f25ae9SGregory Neil Shapiro if (tTd(44, 4))
36240266059SGregory Neil Shapiro sm_dprintf("\tOK\n");
36306f25ae9SGregory Neil Shapiro return 0;
36406f25ae9SGregory Neil Shapiro }
36506f25ae9SGregory Neil Shapiro if (tTd(44, 4))
36640266059SGregory Neil Shapiro sm_dprintf("\tEACCES\n");
36706f25ae9SGregory Neil Shapiro return EACCES;
36806f25ae9SGregory Neil Shapiro }
36940266059SGregory Neil Shapiro /*
37006f25ae9SGregory Neil Shapiro ** SAFEDIRPATH -- check to make sure a path to a directory is safe
37106f25ae9SGregory Neil Shapiro **
37206f25ae9SGregory Neil Shapiro ** Safe means not writable and owned by the right folks.
37306f25ae9SGregory Neil Shapiro **
37406f25ae9SGregory Neil Shapiro ** Parameters:
37506f25ae9SGregory Neil Shapiro ** fn -- filename to check.
37606f25ae9SGregory Neil Shapiro ** uid -- user id to compare against.
37706f25ae9SGregory Neil Shapiro ** gid -- group id to compare against.
37806f25ae9SGregory Neil Shapiro ** user -- user name to compare against (used for group
37906f25ae9SGregory Neil Shapiro ** sets).
38006f25ae9SGregory Neil Shapiro ** flags -- modifiers:
38106f25ae9SGregory Neil Shapiro ** SFF_ROOTOK -- ok to use root permissions to open.
38206f25ae9SGregory Neil Shapiro ** SFF_SAFEDIRPATH -- writable directories are considered
38306f25ae9SGregory Neil Shapiro ** to be fatal errors.
38406f25ae9SGregory Neil Shapiro ** level -- symlink recursive level.
38506f25ae9SGregory Neil Shapiro ** offset -- offset into fn to start checking from.
38606f25ae9SGregory Neil Shapiro **
38706f25ae9SGregory Neil Shapiro ** Returns:
38806f25ae9SGregory Neil Shapiro ** 0 -- if the directory path is "safe".
38906f25ae9SGregory Neil Shapiro ** else -- an error number associated with the path.
39006f25ae9SGregory Neil Shapiro */
39106f25ae9SGregory Neil Shapiro
39206f25ae9SGregory Neil Shapiro int
safedirpath(fn,uid,gid,user,flags,level,offset)39306f25ae9SGregory Neil Shapiro safedirpath(fn, uid, gid, user, flags, level, offset)
39406f25ae9SGregory Neil Shapiro char *fn;
39506f25ae9SGregory Neil Shapiro UID_T uid;
39606f25ae9SGregory Neil Shapiro GID_T gid;
39706f25ae9SGregory Neil Shapiro char *user;
39806f25ae9SGregory Neil Shapiro long flags;
39906f25ae9SGregory Neil Shapiro int level;
40006f25ae9SGregory Neil Shapiro int offset;
40106f25ae9SGregory Neil Shapiro {
40206f25ae9SGregory Neil Shapiro int ret = 0;
40306f25ae9SGregory Neil Shapiro int mode = S_IWOTH;
40406f25ae9SGregory Neil Shapiro char save = '\0';
40506f25ae9SGregory Neil Shapiro char *saveptr = NULL;
40606f25ae9SGregory Neil Shapiro char *p, *enddir;
40706f25ae9SGregory Neil Shapiro register struct group *gr = NULL;
40894c01205SGregory Neil Shapiro char s[MAXLINKPATHLEN];
40906f25ae9SGregory Neil Shapiro struct stat stbuf;
41006f25ae9SGregory Neil Shapiro
41106f25ae9SGregory Neil Shapiro /* make sure we aren't in a symlink loop */
41206f25ae9SGregory Neil Shapiro if (level > MAXSYMLINKS)
41306f25ae9SGregory Neil Shapiro return ELOOP;
41406f25ae9SGregory Neil Shapiro
41540266059SGregory Neil Shapiro if (level < 0 || offset < 0 || offset > strlen(fn))
41640266059SGregory Neil Shapiro return EINVAL;
41740266059SGregory Neil Shapiro
41806f25ae9SGregory Neil Shapiro /* special case root directory */
41906f25ae9SGregory Neil Shapiro if (*fn == '\0')
42006f25ae9SGregory Neil Shapiro fn = "/";
42106f25ae9SGregory Neil Shapiro
42206f25ae9SGregory Neil Shapiro if (tTd(44, 4))
42340266059SGregory Neil Shapiro sm_dprintf("safedirpath(%s, uid=%ld, gid=%ld, flags=%lx, level=%d, offset=%d):\n",
42406f25ae9SGregory Neil Shapiro fn, (long) uid, (long) gid, flags, level, offset);
42506f25ae9SGregory Neil Shapiro
42606f25ae9SGregory Neil Shapiro if (!bitnset(DBS_GROUPWRITABLEDIRPATHSAFE, DontBlameSendmail))
42706f25ae9SGregory Neil Shapiro mode |= S_IWGRP;
42806f25ae9SGregory Neil Shapiro
42906f25ae9SGregory Neil Shapiro /* Make a modifiable copy of the filename */
43040266059SGregory Neil Shapiro if (sm_strlcpy(s, fn, sizeof s) >= sizeof s)
43106f25ae9SGregory Neil Shapiro return EINVAL;
43206f25ae9SGregory Neil Shapiro
43306f25ae9SGregory Neil Shapiro p = s + offset;
43406f25ae9SGregory Neil Shapiro while (p != NULL)
43506f25ae9SGregory Neil Shapiro {
43606f25ae9SGregory Neil Shapiro /* put back character */
43706f25ae9SGregory Neil Shapiro if (saveptr != NULL)
43806f25ae9SGregory Neil Shapiro {
43906f25ae9SGregory Neil Shapiro *saveptr = save;
44006f25ae9SGregory Neil Shapiro saveptr = NULL;
44106f25ae9SGregory Neil Shapiro p++;
44206f25ae9SGregory Neil Shapiro }
44306f25ae9SGregory Neil Shapiro
44406f25ae9SGregory Neil Shapiro if (*p == '\0')
44506f25ae9SGregory Neil Shapiro break;
44606f25ae9SGregory Neil Shapiro
44706f25ae9SGregory Neil Shapiro p = strchr(p, '/');
44806f25ae9SGregory Neil Shapiro
44906f25ae9SGregory Neil Shapiro /* Special case for root directory */
45006f25ae9SGregory Neil Shapiro if (p == s)
45106f25ae9SGregory Neil Shapiro {
45206f25ae9SGregory Neil Shapiro save = *(p + 1);
45306f25ae9SGregory Neil Shapiro saveptr = p + 1;
45406f25ae9SGregory Neil Shapiro *(p + 1) = '\0';
45506f25ae9SGregory Neil Shapiro }
45606f25ae9SGregory Neil Shapiro else if (p != NULL)
45706f25ae9SGregory Neil Shapiro {
45806f25ae9SGregory Neil Shapiro save = *p;
45906f25ae9SGregory Neil Shapiro saveptr = p;
46006f25ae9SGregory Neil Shapiro *p = '\0';
46106f25ae9SGregory Neil Shapiro }
46206f25ae9SGregory Neil Shapiro
46306f25ae9SGregory Neil Shapiro /* Heuristic: . and .. have already been checked */
46406f25ae9SGregory Neil Shapiro enddir = strrchr(s, '/');
46506f25ae9SGregory Neil Shapiro if (enddir != NULL &&
46606f25ae9SGregory Neil Shapiro (strcmp(enddir, "/..") == 0 ||
46706f25ae9SGregory Neil Shapiro strcmp(enddir, "/.") == 0))
46806f25ae9SGregory Neil Shapiro continue;
46906f25ae9SGregory Neil Shapiro
47006f25ae9SGregory Neil Shapiro if (tTd(44, 20))
47140266059SGregory Neil Shapiro sm_dprintf("\t[dir %s]\n", s);
47206f25ae9SGregory Neil Shapiro
47306f25ae9SGregory Neil Shapiro #if HASLSTAT
47406f25ae9SGregory Neil Shapiro ret = lstat(s, &stbuf);
475*5b0945b5SGregory Neil Shapiro #else
47606f25ae9SGregory Neil Shapiro ret = stat(s, &stbuf);
477*5b0945b5SGregory Neil Shapiro #endif
47806f25ae9SGregory Neil Shapiro if (ret < 0)
47906f25ae9SGregory Neil Shapiro {
48006f25ae9SGregory Neil Shapiro ret = errno;
48106f25ae9SGregory Neil Shapiro break;
48206f25ae9SGregory Neil Shapiro }
48306f25ae9SGregory Neil Shapiro
48406f25ae9SGregory Neil Shapiro #ifdef S_ISLNK
48506f25ae9SGregory Neil Shapiro /* Follow symlinks */
48606f25ae9SGregory Neil Shapiro if (S_ISLNK(stbuf.st_mode))
48706f25ae9SGregory Neil Shapiro {
48894c01205SGregory Neil Shapiro int linklen;
48906f25ae9SGregory Neil Shapiro char *target;
49094c01205SGregory Neil Shapiro char buf[MAXPATHLEN];
491b6bacd31SGregory Neil Shapiro char fullbuf[MAXLINKPATHLEN];
49206f25ae9SGregory Neil Shapiro
49306f25ae9SGregory Neil Shapiro memset(buf, '\0', sizeof buf);
49494c01205SGregory Neil Shapiro linklen = readlink(s, buf, sizeof buf);
49594c01205SGregory Neil Shapiro if (linklen < 0)
49606f25ae9SGregory Neil Shapiro {
49706f25ae9SGregory Neil Shapiro ret = errno;
49806f25ae9SGregory Neil Shapiro break;
49906f25ae9SGregory Neil Shapiro }
50094c01205SGregory Neil Shapiro if (linklen >= sizeof buf)
50194c01205SGregory Neil Shapiro {
50294c01205SGregory Neil Shapiro /* file name too long for buffer */
50394c01205SGregory Neil Shapiro ret = errno = EINVAL;
50494c01205SGregory Neil Shapiro break;
50594c01205SGregory Neil Shapiro }
50606f25ae9SGregory Neil Shapiro
50706f25ae9SGregory Neil Shapiro offset = 0;
50806f25ae9SGregory Neil Shapiro if (*buf == '/')
50906f25ae9SGregory Neil Shapiro {
51006f25ae9SGregory Neil Shapiro target = buf;
51106f25ae9SGregory Neil Shapiro
51206f25ae9SGregory Neil Shapiro /* If path is the same, avoid rechecks */
51306f25ae9SGregory Neil Shapiro while (s[offset] == buf[offset] &&
51406f25ae9SGregory Neil Shapiro s[offset] != '\0')
51506f25ae9SGregory Neil Shapiro offset++;
51606f25ae9SGregory Neil Shapiro
51706f25ae9SGregory Neil Shapiro if (s[offset] == '\0' && buf[offset] == '\0')
51806f25ae9SGregory Neil Shapiro {
51906f25ae9SGregory Neil Shapiro /* strings match, symlink loop */
52006f25ae9SGregory Neil Shapiro return ELOOP;
52106f25ae9SGregory Neil Shapiro }
52206f25ae9SGregory Neil Shapiro
52306f25ae9SGregory Neil Shapiro /* back off from the mismatch */
52406f25ae9SGregory Neil Shapiro if (offset > 0)
52506f25ae9SGregory Neil Shapiro offset--;
52606f25ae9SGregory Neil Shapiro
52706f25ae9SGregory Neil Shapiro /* Make sure we are at a directory break */
52806f25ae9SGregory Neil Shapiro if (offset > 0 &&
52906f25ae9SGregory Neil Shapiro s[offset] != '/' &&
53006f25ae9SGregory Neil Shapiro s[offset] != '\0')
53106f25ae9SGregory Neil Shapiro {
53206f25ae9SGregory Neil Shapiro while (buf[offset] != '/' &&
53306f25ae9SGregory Neil Shapiro offset > 0)
53406f25ae9SGregory Neil Shapiro offset--;
53506f25ae9SGregory Neil Shapiro }
53606f25ae9SGregory Neil Shapiro if (offset > 0 &&
53706f25ae9SGregory Neil Shapiro s[offset] == '/' &&
53806f25ae9SGregory Neil Shapiro buf[offset] == '/')
53906f25ae9SGregory Neil Shapiro {
54006f25ae9SGregory Neil Shapiro /* Include the trailing slash */
54106f25ae9SGregory Neil Shapiro offset++;
54206f25ae9SGregory Neil Shapiro }
54306f25ae9SGregory Neil Shapiro }
54406f25ae9SGregory Neil Shapiro else
54506f25ae9SGregory Neil Shapiro {
54606f25ae9SGregory Neil Shapiro char *sptr;
54706f25ae9SGregory Neil Shapiro
54806f25ae9SGregory Neil Shapiro sptr = strrchr(s, '/');
54906f25ae9SGregory Neil Shapiro if (sptr != NULL)
55006f25ae9SGregory Neil Shapiro {
55106f25ae9SGregory Neil Shapiro *sptr = '\0';
55206f25ae9SGregory Neil Shapiro offset = sptr + 1 - s;
55340266059SGregory Neil Shapiro if (sm_strlcpyn(fullbuf,
55440266059SGregory Neil Shapiro sizeof fullbuf, 2,
55540266059SGregory Neil Shapiro s, "/") >=
55640266059SGregory Neil Shapiro sizeof fullbuf ||
55740266059SGregory Neil Shapiro sm_strlcat(fullbuf, buf,
55840266059SGregory Neil Shapiro sizeof fullbuf) >=
55940266059SGregory Neil Shapiro sizeof fullbuf)
56006f25ae9SGregory Neil Shapiro {
56106f25ae9SGregory Neil Shapiro ret = EINVAL;
56206f25ae9SGregory Neil Shapiro break;
56306f25ae9SGregory Neil Shapiro }
56406f25ae9SGregory Neil Shapiro *sptr = '/';
56506f25ae9SGregory Neil Shapiro }
56606f25ae9SGregory Neil Shapiro else
56706f25ae9SGregory Neil Shapiro {
56840266059SGregory Neil Shapiro if (sm_strlcpy(fullbuf, buf,
56940266059SGregory Neil Shapiro sizeof fullbuf) >=
57040266059SGregory Neil Shapiro sizeof fullbuf)
57106f25ae9SGregory Neil Shapiro {
57206f25ae9SGregory Neil Shapiro ret = EINVAL;
57306f25ae9SGregory Neil Shapiro break;
57406f25ae9SGregory Neil Shapiro }
57506f25ae9SGregory Neil Shapiro }
57606f25ae9SGregory Neil Shapiro target = fullbuf;
57706f25ae9SGregory Neil Shapiro }
57806f25ae9SGregory Neil Shapiro ret = safedirpath(target, uid, gid, user, flags,
57906f25ae9SGregory Neil Shapiro level + 1, offset);
58006f25ae9SGregory Neil Shapiro if (ret != 0)
58106f25ae9SGregory Neil Shapiro break;
58206f25ae9SGregory Neil Shapiro
58306f25ae9SGregory Neil Shapiro /* Don't check permissions on the link file itself */
58406f25ae9SGregory Neil Shapiro continue;
58506f25ae9SGregory Neil Shapiro }
58606f25ae9SGregory Neil Shapiro #endif /* S_ISLNK */
58706f25ae9SGregory Neil Shapiro
58806f25ae9SGregory Neil Shapiro if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) &&
58906f25ae9SGregory Neil Shapiro #ifdef S_ISVTX
59006f25ae9SGregory Neil Shapiro !(bitnset(DBS_TRUSTSTICKYBIT, DontBlameSendmail) &&
59106f25ae9SGregory Neil Shapiro bitset(S_ISVTX, stbuf.st_mode)) &&
592*5b0945b5SGregory Neil Shapiro #endif
59306f25ae9SGregory Neil Shapiro bitset(mode, stbuf.st_mode))
59406f25ae9SGregory Neil Shapiro {
59506f25ae9SGregory Neil Shapiro if (tTd(44, 4))
59640266059SGregory Neil Shapiro sm_dprintf("\t[dir %s] mode %lo ",
59740266059SGregory Neil Shapiro s, (unsigned long) stbuf.st_mode);
59806f25ae9SGregory Neil Shapiro if (bitset(SFF_SAFEDIRPATH, flags))
59906f25ae9SGregory Neil Shapiro {
60006f25ae9SGregory Neil Shapiro if (bitset(S_IWOTH, stbuf.st_mode))
60106f25ae9SGregory Neil Shapiro ret = E_SM_WWDIR;
60206f25ae9SGregory Neil Shapiro else
60306f25ae9SGregory Neil Shapiro ret = E_SM_GWDIR;
60406f25ae9SGregory Neil Shapiro if (tTd(44, 4))
60540266059SGregory Neil Shapiro sm_dprintf("FATAL\n");
60606f25ae9SGregory Neil Shapiro break;
60706f25ae9SGregory Neil Shapiro }
60806f25ae9SGregory Neil Shapiro if (tTd(44, 4))
60940266059SGregory Neil Shapiro sm_dprintf("WARNING\n");
61006f25ae9SGregory Neil Shapiro if (Verbose > 1)
61106f25ae9SGregory Neil Shapiro message("051 WARNING: %s writable directory %s",
61206f25ae9SGregory Neil Shapiro bitset(S_IWOTH, stbuf.st_mode)
61306f25ae9SGregory Neil Shapiro ? "World"
61406f25ae9SGregory Neil Shapiro : "Group",
61506f25ae9SGregory Neil Shapiro s);
61606f25ae9SGregory Neil Shapiro }
61706f25ae9SGregory Neil Shapiro if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags))
61806f25ae9SGregory Neil Shapiro {
61906f25ae9SGregory Neil Shapiro if (bitset(S_IXOTH, stbuf.st_mode))
62006f25ae9SGregory Neil Shapiro continue;
62106f25ae9SGregory Neil Shapiro ret = EACCES;
62206f25ae9SGregory Neil Shapiro break;
62306f25ae9SGregory Neil Shapiro }
62406f25ae9SGregory Neil Shapiro
62506f25ae9SGregory Neil Shapiro /*
62606f25ae9SGregory Neil Shapiro ** Let OS determine access to file if we are not
62706f25ae9SGregory Neil Shapiro ** running as a privileged user. This allows ACLs
62806f25ae9SGregory Neil Shapiro ** to work. Also, if opening as root, assume we can
62906f25ae9SGregory Neil Shapiro ** scan the directory.
63006f25ae9SGregory Neil Shapiro */
63106f25ae9SGregory Neil Shapiro if (geteuid() != 0 || bitset(SFF_OPENASROOT, flags))
63206f25ae9SGregory Neil Shapiro continue;
63306f25ae9SGregory Neil Shapiro
63406f25ae9SGregory Neil Shapiro if (stbuf.st_uid == uid &&
63506f25ae9SGregory Neil Shapiro bitset(S_IXUSR, stbuf.st_mode))
63606f25ae9SGregory Neil Shapiro continue;
63706f25ae9SGregory Neil Shapiro if (stbuf.st_gid == gid &&
63806f25ae9SGregory Neil Shapiro bitset(S_IXGRP, stbuf.st_mode))
63906f25ae9SGregory Neil Shapiro continue;
64006f25ae9SGregory Neil Shapiro #ifndef NO_GROUP_SET
64106f25ae9SGregory Neil Shapiro if (user != NULL && !DontInitGroups &&
64206f25ae9SGregory Neil Shapiro ((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
64306f25ae9SGregory Neil Shapiro (gr = getgrgid(stbuf.st_gid)) != NULL))
64406f25ae9SGregory Neil Shapiro {
64506f25ae9SGregory Neil Shapiro register char **gp;
64606f25ae9SGregory Neil Shapiro
64706f25ae9SGregory Neil Shapiro for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
64806f25ae9SGregory Neil Shapiro if (strcmp(*gp, user) == 0)
64906f25ae9SGregory Neil Shapiro break;
65006f25ae9SGregory Neil Shapiro if (gp != NULL && *gp != NULL &&
65106f25ae9SGregory Neil Shapiro bitset(S_IXGRP, stbuf.st_mode))
65206f25ae9SGregory Neil Shapiro continue;
65306f25ae9SGregory Neil Shapiro }
65406f25ae9SGregory Neil Shapiro #endif /* ! NO_GROUP_SET */
65506f25ae9SGregory Neil Shapiro if (!bitset(S_IXOTH, stbuf.st_mode))
65606f25ae9SGregory Neil Shapiro {
65706f25ae9SGregory Neil Shapiro ret = EACCES;
65806f25ae9SGregory Neil Shapiro break;
65906f25ae9SGregory Neil Shapiro }
66006f25ae9SGregory Neil Shapiro }
66106f25ae9SGregory Neil Shapiro if (tTd(44, 4))
66240266059SGregory Neil Shapiro sm_dprintf("\t[dir %s] %s\n", fn,
66340266059SGregory Neil Shapiro ret == 0 ? "OK" : sm_errstring(ret));
66406f25ae9SGregory Neil Shapiro return ret;
66506f25ae9SGregory Neil Shapiro }
66640266059SGregory Neil Shapiro /*
66706f25ae9SGregory Neil Shapiro ** SAFEOPEN -- do a file open with extra checking
66806f25ae9SGregory Neil Shapiro **
66906f25ae9SGregory Neil Shapiro ** Parameters:
67006f25ae9SGregory Neil Shapiro ** fn -- the file name to open.
67106f25ae9SGregory Neil Shapiro ** omode -- the open-style mode flags.
67206f25ae9SGregory Neil Shapiro ** cmode -- the create-style mode flags.
67306f25ae9SGregory Neil Shapiro ** sff -- safefile flags.
67406f25ae9SGregory Neil Shapiro **
67506f25ae9SGregory Neil Shapiro ** Returns:
67606f25ae9SGregory Neil Shapiro ** Same as open.
67706f25ae9SGregory Neil Shapiro */
67806f25ae9SGregory Neil Shapiro
67906f25ae9SGregory Neil Shapiro int
safeopen(fn,omode,cmode,sff)68006f25ae9SGregory Neil Shapiro safeopen(fn, omode, cmode, sff)
68106f25ae9SGregory Neil Shapiro char *fn;
68206f25ae9SGregory Neil Shapiro int omode;
68306f25ae9SGregory Neil Shapiro int cmode;
68406f25ae9SGregory Neil Shapiro long sff;
68506f25ae9SGregory Neil Shapiro {
686e92d3f3fSGregory Neil Shapiro #if !NOFTRUNCATE
687e92d3f3fSGregory Neil Shapiro bool truncate;
688*5b0945b5SGregory Neil Shapiro #endif
68906f25ae9SGregory Neil Shapiro int rval;
69006f25ae9SGregory Neil Shapiro int fd;
69106f25ae9SGregory Neil Shapiro int smode;
69206f25ae9SGregory Neil Shapiro struct stat stb;
69306f25ae9SGregory Neil Shapiro
69406f25ae9SGregory Neil Shapiro if (tTd(44, 10))
69540266059SGregory Neil Shapiro sm_dprintf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n",
69606f25ae9SGregory Neil Shapiro fn, omode, cmode, sff);
69706f25ae9SGregory Neil Shapiro
69806f25ae9SGregory Neil Shapiro if (bitset(O_CREAT, omode))
69906f25ae9SGregory Neil Shapiro sff |= SFF_CREAT;
70006f25ae9SGregory Neil Shapiro omode &= ~O_CREAT;
70106f25ae9SGregory Neil Shapiro switch (omode & O_ACCMODE)
70206f25ae9SGregory Neil Shapiro {
70306f25ae9SGregory Neil Shapiro case O_RDONLY:
70406f25ae9SGregory Neil Shapiro smode = S_IREAD;
70506f25ae9SGregory Neil Shapiro break;
70606f25ae9SGregory Neil Shapiro
70706f25ae9SGregory Neil Shapiro case O_WRONLY:
70806f25ae9SGregory Neil Shapiro smode = S_IWRITE;
70906f25ae9SGregory Neil Shapiro break;
71006f25ae9SGregory Neil Shapiro
71106f25ae9SGregory Neil Shapiro case O_RDWR:
71206f25ae9SGregory Neil Shapiro smode = S_IREAD|S_IWRITE;
71306f25ae9SGregory Neil Shapiro break;
71406f25ae9SGregory Neil Shapiro
71506f25ae9SGregory Neil Shapiro default:
71606f25ae9SGregory Neil Shapiro smode = 0;
71706f25ae9SGregory Neil Shapiro break;
71806f25ae9SGregory Neil Shapiro }
71906f25ae9SGregory Neil Shapiro if (bitset(SFF_OPENASROOT, sff))
72006f25ae9SGregory Neil Shapiro rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName,
72106f25ae9SGregory Neil Shapiro sff, smode, &stb);
72206f25ae9SGregory Neil Shapiro else
72306f25ae9SGregory Neil Shapiro rval = safefile(fn, RealUid, RealGid, RealUserName,
72406f25ae9SGregory Neil Shapiro sff, smode, &stb);
72506f25ae9SGregory Neil Shapiro if (rval != 0)
72606f25ae9SGregory Neil Shapiro {
72706f25ae9SGregory Neil Shapiro errno = rval;
72806f25ae9SGregory Neil Shapiro return -1;
72906f25ae9SGregory Neil Shapiro }
73006f25ae9SGregory Neil Shapiro if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff))
73106f25ae9SGregory Neil Shapiro omode |= O_CREAT | (bitset(SFF_NOTEXCL, sff) ? 0 : O_EXCL);
73206f25ae9SGregory Neil Shapiro else if (bitset(SFF_CREAT, sff) && bitset(O_EXCL, omode))
73306f25ae9SGregory Neil Shapiro {
73406f25ae9SGregory Neil Shapiro /* The file exists so an exclusive create would fail */
73506f25ae9SGregory Neil Shapiro errno = EEXIST;
73606f25ae9SGregory Neil Shapiro return -1;
73706f25ae9SGregory Neil Shapiro }
73806f25ae9SGregory Neil Shapiro
739e92d3f3fSGregory Neil Shapiro #if !NOFTRUNCATE
740e92d3f3fSGregory Neil Shapiro truncate = bitset(O_TRUNC, omode);
741e92d3f3fSGregory Neil Shapiro if (truncate)
742e92d3f3fSGregory Neil Shapiro omode &= ~O_TRUNC;
743*5b0945b5SGregory Neil Shapiro #endif
744e92d3f3fSGregory Neil Shapiro
74506f25ae9SGregory Neil Shapiro fd = dfopen(fn, omode, cmode, sff);
74606f25ae9SGregory Neil Shapiro if (fd < 0)
74706f25ae9SGregory Neil Shapiro return fd;
74806f25ae9SGregory Neil Shapiro if (filechanged(fn, fd, &stb))
74906f25ae9SGregory Neil Shapiro {
75006f25ae9SGregory Neil Shapiro syserr("554 5.3.0 cannot open: file %s changed after open", fn);
75106f25ae9SGregory Neil Shapiro (void) close(fd);
75206f25ae9SGregory Neil Shapiro errno = E_SM_FILECHANGE;
75306f25ae9SGregory Neil Shapiro return -1;
75406f25ae9SGregory Neil Shapiro }
755e92d3f3fSGregory Neil Shapiro
756e92d3f3fSGregory Neil Shapiro #if !NOFTRUNCATE
757e92d3f3fSGregory Neil Shapiro if (truncate &&
758e92d3f3fSGregory Neil Shapiro ftruncate(fd, (off_t) 0) < 0)
759e92d3f3fSGregory Neil Shapiro {
760e92d3f3fSGregory Neil Shapiro int save_errno;
761e92d3f3fSGregory Neil Shapiro
762e92d3f3fSGregory Neil Shapiro save_errno = errno;
763e92d3f3fSGregory Neil Shapiro syserr("554 5.3.0 cannot open: file %s could not be truncated",
764e92d3f3fSGregory Neil Shapiro fn);
765e92d3f3fSGregory Neil Shapiro (void) close(fd);
766e92d3f3fSGregory Neil Shapiro errno = save_errno;
767e92d3f3fSGregory Neil Shapiro return -1;
768e92d3f3fSGregory Neil Shapiro }
769e92d3f3fSGregory Neil Shapiro #endif /* !NOFTRUNCATE */
770e92d3f3fSGregory Neil Shapiro
77106f25ae9SGregory Neil Shapiro return fd;
77206f25ae9SGregory Neil Shapiro }
77340266059SGregory Neil Shapiro /*
77440266059SGregory Neil Shapiro ** SAFEFOPEN -- do a file open with extra checking
77540266059SGregory Neil Shapiro **
77640266059SGregory Neil Shapiro ** Parameters:
77740266059SGregory Neil Shapiro ** fn -- the file name to open.
77840266059SGregory Neil Shapiro ** omode -- the open-style mode flags.
77940266059SGregory Neil Shapiro ** cmode -- the create-style mode flags.
78040266059SGregory Neil Shapiro ** sff -- safefile flags.
78140266059SGregory Neil Shapiro **
78240266059SGregory Neil Shapiro ** Returns:
78340266059SGregory Neil Shapiro ** Same as fopen.
78440266059SGregory Neil Shapiro */
78540266059SGregory Neil Shapiro
78640266059SGregory Neil Shapiro SM_FILE_T *
safefopen(fn,omode,cmode,sff)78740266059SGregory Neil Shapiro safefopen(fn, omode, cmode, sff)
78840266059SGregory Neil Shapiro char *fn;
78940266059SGregory Neil Shapiro int omode;
79040266059SGregory Neil Shapiro int cmode;
79140266059SGregory Neil Shapiro long sff;
79240266059SGregory Neil Shapiro {
79340266059SGregory Neil Shapiro int fd;
79440266059SGregory Neil Shapiro int save_errno;
79540266059SGregory Neil Shapiro SM_FILE_T *fp;
79640266059SGregory Neil Shapiro int fmode;
79740266059SGregory Neil Shapiro
79840266059SGregory Neil Shapiro switch (omode & O_ACCMODE)
79940266059SGregory Neil Shapiro {
80040266059SGregory Neil Shapiro case O_RDONLY:
80140266059SGregory Neil Shapiro fmode = SM_IO_RDONLY;
80240266059SGregory Neil Shapiro break;
80340266059SGregory Neil Shapiro
80440266059SGregory Neil Shapiro case O_WRONLY:
80540266059SGregory Neil Shapiro if (bitset(O_APPEND, omode))
80640266059SGregory Neil Shapiro fmode = SM_IO_APPEND;
80740266059SGregory Neil Shapiro else
80840266059SGregory Neil Shapiro fmode = SM_IO_WRONLY;
80940266059SGregory Neil Shapiro break;
81040266059SGregory Neil Shapiro
81140266059SGregory Neil Shapiro case O_RDWR:
81240266059SGregory Neil Shapiro if (bitset(O_TRUNC, omode))
81340266059SGregory Neil Shapiro fmode = SM_IO_RDWRTR;
81440266059SGregory Neil Shapiro else if (bitset(O_APPEND, omode))
81540266059SGregory Neil Shapiro fmode = SM_IO_APPENDRW;
81640266059SGregory Neil Shapiro else
81740266059SGregory Neil Shapiro fmode = SM_IO_RDWR;
81840266059SGregory Neil Shapiro break;
81940266059SGregory Neil Shapiro
82040266059SGregory Neil Shapiro default:
82140266059SGregory Neil Shapiro syserr("554 5.3.5 safefopen: unknown omode %o", omode);
82240266059SGregory Neil Shapiro fmode = 0;
82340266059SGregory Neil Shapiro }
82440266059SGregory Neil Shapiro fd = safeopen(fn, omode, cmode, sff);
82540266059SGregory Neil Shapiro if (fd < 0)
82640266059SGregory Neil Shapiro {
82740266059SGregory Neil Shapiro save_errno = errno;
82840266059SGregory Neil Shapiro if (tTd(44, 10))
82940266059SGregory Neil Shapiro sm_dprintf("safefopen: safeopen failed: %s\n",
83040266059SGregory Neil Shapiro sm_errstring(errno));
83140266059SGregory Neil Shapiro errno = save_errno;
83240266059SGregory Neil Shapiro return NULL;
83340266059SGregory Neil Shapiro }
83440266059SGregory Neil Shapiro fp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
83540266059SGregory Neil Shapiro (void *) &fd, fmode, NULL);
83640266059SGregory Neil Shapiro if (fp != NULL)
83740266059SGregory Neil Shapiro return fp;
83840266059SGregory Neil Shapiro
83940266059SGregory Neil Shapiro save_errno = errno;
84040266059SGregory Neil Shapiro if (tTd(44, 10))
84140266059SGregory Neil Shapiro {
84240266059SGregory Neil Shapiro sm_dprintf("safefopen: fdopen(%s, %d) failed: omode=%x, sff=%lx, err=%s\n",
84340266059SGregory Neil Shapiro fn, fmode, omode, sff, sm_errstring(errno));
84440266059SGregory Neil Shapiro }
84540266059SGregory Neil Shapiro (void) close(fd);
84640266059SGregory Neil Shapiro errno = save_errno;
84740266059SGregory Neil Shapiro return NULL;
84840266059SGregory Neil Shapiro }
84940266059SGregory Neil Shapiro /*
85006f25ae9SGregory Neil Shapiro ** FILECHANGED -- check to see if file changed after being opened
85106f25ae9SGregory Neil Shapiro **
85206f25ae9SGregory Neil Shapiro ** Parameters:
85306f25ae9SGregory Neil Shapiro ** fn -- pathname of file to check.
85406f25ae9SGregory Neil Shapiro ** fd -- file descriptor to check.
85506f25ae9SGregory Neil Shapiro ** stb -- stat structure from before open.
85606f25ae9SGregory Neil Shapiro **
85706f25ae9SGregory Neil Shapiro ** Returns:
85840266059SGregory Neil Shapiro ** true -- if a problem was detected.
85940266059SGregory Neil Shapiro ** false -- if this file is still the same.
86006f25ae9SGregory Neil Shapiro */
86106f25ae9SGregory Neil Shapiro
86206f25ae9SGregory Neil Shapiro bool
filechanged(fn,fd,stb)86306f25ae9SGregory Neil Shapiro filechanged(fn, fd, stb)
86406f25ae9SGregory Neil Shapiro char *fn;
86506f25ae9SGregory Neil Shapiro int fd;
86606f25ae9SGregory Neil Shapiro struct stat *stb;
86706f25ae9SGregory Neil Shapiro {
86806f25ae9SGregory Neil Shapiro struct stat sta;
86906f25ae9SGregory Neil Shapiro
87006f25ae9SGregory Neil Shapiro if (stb->st_mode == ST_MODE_NOFILE)
87106f25ae9SGregory Neil Shapiro {
87206f25ae9SGregory Neil Shapiro #if HASLSTAT && BOGUS_O_EXCL
87306f25ae9SGregory Neil Shapiro /* only necessary if exclusive open follows symbolic links */
87406f25ae9SGregory Neil Shapiro if (lstat(fn, stb) < 0 || stb->st_nlink != 1)
87540266059SGregory Neil Shapiro return true;
876*5b0945b5SGregory Neil Shapiro #else
87740266059SGregory Neil Shapiro return false;
878*5b0945b5SGregory Neil Shapiro #endif
87906f25ae9SGregory Neil Shapiro }
88006f25ae9SGregory Neil Shapiro if (fstat(fd, &sta) < 0)
88140266059SGregory Neil Shapiro return true;
88206f25ae9SGregory Neil Shapiro
88306f25ae9SGregory Neil Shapiro if (sta.st_nlink != stb->st_nlink ||
88406f25ae9SGregory Neil Shapiro sta.st_dev != stb->st_dev ||
88506f25ae9SGregory Neil Shapiro sta.st_ino != stb->st_ino ||
88606f25ae9SGregory Neil Shapiro #if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */
88706f25ae9SGregory Neil Shapiro sta.st_gen != stb->st_gen ||
888*5b0945b5SGregory Neil Shapiro #endif
88906f25ae9SGregory Neil Shapiro sta.st_uid != stb->st_uid ||
89006f25ae9SGregory Neil Shapiro sta.st_gid != stb->st_gid)
89106f25ae9SGregory Neil Shapiro {
89206f25ae9SGregory Neil Shapiro if (tTd(44, 8))
89306f25ae9SGregory Neil Shapiro {
89440266059SGregory Neil Shapiro sm_dprintf("File changed after opening:\n");
89540266059SGregory Neil Shapiro sm_dprintf(" nlink = %ld/%ld\n",
89606f25ae9SGregory Neil Shapiro (long) stb->st_nlink, (long) sta.st_nlink);
89740266059SGregory Neil Shapiro sm_dprintf(" dev = %ld/%ld\n",
89806f25ae9SGregory Neil Shapiro (long) stb->st_dev, (long) sta.st_dev);
89940266059SGregory Neil Shapiro sm_dprintf(" ino = %llu/%llu\n",
90040266059SGregory Neil Shapiro (ULONGLONG_T) stb->st_ino,
90140266059SGregory Neil Shapiro (ULONGLONG_T) sta.st_ino);
90206f25ae9SGregory Neil Shapiro #if HAS_ST_GEN
90340266059SGregory Neil Shapiro sm_dprintf(" gen = %ld/%ld\n",
90406f25ae9SGregory Neil Shapiro (long) stb->st_gen, (long) sta.st_gen);
905*5b0945b5SGregory Neil Shapiro #endif
90640266059SGregory Neil Shapiro sm_dprintf(" uid = %ld/%ld\n",
90706f25ae9SGregory Neil Shapiro (long) stb->st_uid, (long) sta.st_uid);
90840266059SGregory Neil Shapiro sm_dprintf(" gid = %ld/%ld\n",
90906f25ae9SGregory Neil Shapiro (long) stb->st_gid, (long) sta.st_gid);
91006f25ae9SGregory Neil Shapiro }
91140266059SGregory Neil Shapiro return true;
91206f25ae9SGregory Neil Shapiro }
91306f25ae9SGregory Neil Shapiro
91440266059SGregory Neil Shapiro return false;
91506f25ae9SGregory Neil Shapiro }
91640266059SGregory Neil Shapiro /*
91706f25ae9SGregory Neil Shapiro ** DFOPEN -- determined file open
91806f25ae9SGregory Neil Shapiro **
91906f25ae9SGregory Neil Shapiro ** This routine has the semantics of open, except that it will
92006f25ae9SGregory Neil Shapiro ** keep trying a few times to make this happen. The idea is that
92106f25ae9SGregory Neil Shapiro ** on very loaded systems, we may run out of resources (inodes,
92206f25ae9SGregory Neil Shapiro ** whatever), so this tries to get around it.
92306f25ae9SGregory Neil Shapiro */
92406f25ae9SGregory Neil Shapiro
92506f25ae9SGregory Neil Shapiro int
dfopen(filename,omode,cmode,sff)92606f25ae9SGregory Neil Shapiro dfopen(filename, omode, cmode, sff)
92706f25ae9SGregory Neil Shapiro char *filename;
92806f25ae9SGregory Neil Shapiro int omode;
92906f25ae9SGregory Neil Shapiro int cmode;
93006f25ae9SGregory Neil Shapiro long sff;
93106f25ae9SGregory Neil Shapiro {
93206f25ae9SGregory Neil Shapiro register int tries;
93306f25ae9SGregory Neil Shapiro int fd = -1;
93406f25ae9SGregory Neil Shapiro struct stat st;
93506f25ae9SGregory Neil Shapiro
93606f25ae9SGregory Neil Shapiro for (tries = 0; tries < 10; tries++)
93706f25ae9SGregory Neil Shapiro {
93806f25ae9SGregory Neil Shapiro (void) sleep((unsigned) (10 * tries));
93906f25ae9SGregory Neil Shapiro errno = 0;
94006f25ae9SGregory Neil Shapiro fd = open(filename, omode, cmode);
94106f25ae9SGregory Neil Shapiro if (fd >= 0)
94206f25ae9SGregory Neil Shapiro break;
94306f25ae9SGregory Neil Shapiro switch (errno)
94406f25ae9SGregory Neil Shapiro {
94506f25ae9SGregory Neil Shapiro case ENFILE: /* system file table full */
94606f25ae9SGregory Neil Shapiro case EINTR: /* interrupted syscall */
94706f25ae9SGregory Neil Shapiro #ifdef ETXTBSY
94806f25ae9SGregory Neil Shapiro case ETXTBSY: /* Apollo: net file locked */
949*5b0945b5SGregory Neil Shapiro #endif
95006f25ae9SGregory Neil Shapiro continue;
95106f25ae9SGregory Neil Shapiro }
95206f25ae9SGregory Neil Shapiro break;
95306f25ae9SGregory Neil Shapiro }
95406f25ae9SGregory Neil Shapiro if (!bitset(SFF_NOLOCK, sff) &&
95506f25ae9SGregory Neil Shapiro fd >= 0 &&
95606f25ae9SGregory Neil Shapiro fstat(fd, &st) >= 0 &&
95706f25ae9SGregory Neil Shapiro S_ISREG(st.st_mode))
95806f25ae9SGregory Neil Shapiro {
95906f25ae9SGregory Neil Shapiro int locktype;
96006f25ae9SGregory Neil Shapiro
96106f25ae9SGregory Neil Shapiro /* lock the file to avoid accidental conflicts */
96206f25ae9SGregory Neil Shapiro if ((omode & O_ACCMODE) != O_RDONLY)
96306f25ae9SGregory Neil Shapiro locktype = LOCK_EX;
96406f25ae9SGregory Neil Shapiro else
96506f25ae9SGregory Neil Shapiro locktype = LOCK_SH;
966e92d3f3fSGregory Neil Shapiro if (bitset(SFF_NBLOCK, sff))
967e92d3f3fSGregory Neil Shapiro locktype |= LOCK_NB;
968e92d3f3fSGregory Neil Shapiro
96906f25ae9SGregory Neil Shapiro if (!lockfile(fd, filename, NULL, locktype))
97006f25ae9SGregory Neil Shapiro {
97106f25ae9SGregory Neil Shapiro int save_errno = errno;
97206f25ae9SGregory Neil Shapiro
97306f25ae9SGregory Neil Shapiro (void) close(fd);
97406f25ae9SGregory Neil Shapiro fd = -1;
97506f25ae9SGregory Neil Shapiro errno = save_errno;
97606f25ae9SGregory Neil Shapiro }
97706f25ae9SGregory Neil Shapiro else
97806f25ae9SGregory Neil Shapiro errno = 0;
97906f25ae9SGregory Neil Shapiro }
98006f25ae9SGregory Neil Shapiro return fd;
98106f25ae9SGregory Neil Shapiro }
982