📄 safefile.c
字号:
break;
}
# ifdef S_ISLNK
/* Follow symlinks */
if (S_ISLNK(stbuf.st_mode))
{
char *target;
char buf[MAXPATHLEN + 1];
memset(buf, '\0', sizeof buf);
if (readlink(s, buf, sizeof buf) < 0)
{
ret = errno;
break;
}
offset = 0;
if (*buf == '/')
{
target = buf;
/* If path is the same, avoid rechecks */
while (s[offset] == buf[offset] &&
s[offset] != '\0')
offset++;
if (s[offset] == '\0' && buf[offset] == '\0')
{
/* strings match, symlink loop */
return ELOOP;
}
/* back off from the mismatch */
if (offset > 0)
offset--;
/* Make sure we are at a directory break */
if (offset > 0 &&
s[offset] != '/' &&
s[offset] != '\0')
{
while (buf[offset] != '/' &&
offset > 0)
offset--;
}
if (offset > 0 &&
s[offset] == '/' &&
buf[offset] == '/')
{
/* Include the trailing slash */
offset++;
}
}
else
{
char *sptr;
char fullbuf[MAXLINKPATHLEN + 1];
sptr = strrchr(s, '/');
if (sptr != NULL)
{
*sptr = '\0';
offset = sptr + 1 - s;
if ((strlen(s) + 1 +
strlen(buf) + 1) > sizeof fullbuf)
{
ret = EINVAL;
break;
}
snprintf(fullbuf, sizeof fullbuf,
"%s/%s", s, buf);
*sptr = '/';
}
else
{
if (strlen(buf) + 1 > sizeof fullbuf)
{
ret = EINVAL;
break;
}
(void) strlcpy(fullbuf, buf,
sizeof fullbuf);
}
target = fullbuf;
}
ret = safedirpath(target, uid, gid, user, flags,
level + 1, offset);
if (ret != 0)
break;
/* Don't check permissions on the link file itself */
continue;
}
#endif /* S_ISLNK */
if ((uid == 0 || bitset(SFF_SAFEDIRPATH, flags)) &&
#ifdef S_ISVTX
!(bitnset(DBS_TRUSTSTICKYBIT, DontBlameSendmail) &&
bitset(S_ISVTX, stbuf.st_mode)) &&
#endif /* S_ISVTX */
bitset(mode, stbuf.st_mode))
{
if (tTd(44, 4))
dprintf("\t[dir %s] mode %lo ",
s, (u_long) stbuf.st_mode);
if (bitset(SFF_SAFEDIRPATH, flags))
{
if (bitset(S_IWOTH, stbuf.st_mode))
ret = E_SM_WWDIR;
else
ret = E_SM_GWDIR;
if (tTd(44, 4))
dprintf("FATAL\n");
break;
}
if (tTd(44, 4))
dprintf("WARNING\n");
if (Verbose > 1)
message("051 WARNING: %s writable directory %s",
bitset(S_IWOTH, stbuf.st_mode)
? "World"
: "Group",
s);
}
if (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags))
{
if (bitset(S_IXOTH, stbuf.st_mode))
continue;
ret = EACCES;
break;
}
/*
** Let OS determine access to file if we are not
** running as a privileged user. This allows ACLs
** to work. Also, if opening as root, assume we can
** scan the directory.
*/
if (geteuid() != 0 || bitset(SFF_OPENASROOT, flags))
continue;
if (stbuf.st_uid == uid &&
bitset(S_IXUSR, stbuf.st_mode))
continue;
if (stbuf.st_gid == gid &&
bitset(S_IXGRP, stbuf.st_mode))
continue;
# ifndef NO_GROUP_SET
if (user != NULL && !DontInitGroups &&
((gr != NULL && gr->gr_gid == stbuf.st_gid) ||
(gr = getgrgid(stbuf.st_gid)) != NULL))
{
register char **gp;
for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++)
if (strcmp(*gp, user) == 0)
break;
if (gp != NULL && *gp != NULL &&
bitset(S_IXGRP, stbuf.st_mode))
continue;
}
# endif /* ! NO_GROUP_SET */
if (!bitset(S_IXOTH, stbuf.st_mode))
{
ret = EACCES;
break;
}
}
if (tTd(44, 4))
dprintf("\t[dir %s] %s\n", fn,
ret == 0 ? "OK" : errstring(ret));
return ret;
}
/*
** SAFEOPEN -- do a file open with extra checking
**
** Parameters:
** fn -- the file name to open.
** omode -- the open-style mode flags.
** cmode -- the create-style mode flags.
** sff -- safefile flags.
**
** Returns:
** Same as open.
*/
#ifndef O_ACCMODE
# define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR)
#endif /* ! O_ACCMODE */
int
safeopen(fn, omode, cmode, sff)
char *fn;
int omode;
int cmode;
long sff;
{
int rval;
int fd;
int smode;
struct stat stb;
if (tTd(44, 10))
printf("safeopen: fn=%s, omode=%x, cmode=%x, sff=%lx\n",
fn, omode, cmode, sff);
if (bitset(O_CREAT, omode))
sff |= SFF_CREAT;
omode &= ~O_CREAT;
smode = 0;
switch (omode & O_ACCMODE)
{
case O_RDONLY:
smode = S_IREAD;
break;
case O_WRONLY:
smode = S_IWRITE;
break;
case O_RDWR:
smode = S_IREAD|S_IWRITE;
break;
default:
smode = 0;
break;
}
if (bitset(SFF_OPENASROOT, sff))
rval = safefile(fn, RunAsUid, RunAsGid, RunAsUserName,
sff, smode, &stb);
else
rval = safefile(fn, RealUid, RealGid, RealUserName,
sff, smode, &stb);
if (rval != 0)
{
errno = rval;
return -1;
}
if (stb.st_mode == ST_MODE_NOFILE && bitset(SFF_CREAT, sff))
omode |= O_CREAT | (bitset(SFF_NOTEXCL, sff) ? 0 : O_EXCL);
else if (bitset(SFF_CREAT, sff) && bitset(O_EXCL, omode))
{
/* The file exists so an exclusive create would fail */
errno = EEXIST;
return -1;
}
fd = dfopen(fn, omode, cmode, sff);
if (fd < 0)
return fd;
if (filechanged(fn, fd, &stb))
{
syserr("554 5.3.0 cannot open: file %s changed after open", fn);
(void) close(fd);
errno = E_SM_FILECHANGE;
return -1;
}
return fd;
}
/*
** SAFEFOPEN -- do a file open with extra checking
**
** Parameters:
** fn -- the file name to open.
** omode -- the open-style mode flags.
** cmode -- the create-style mode flags.
** sff -- safefile flags.
**
** Returns:
** Same as fopen.
*/
FILE *
safefopen(fn, omode, cmode, sff)
char *fn;
int omode;
int cmode;
long sff;
{
int fd;
int save_errno;
FILE *fp;
char *fmode;
switch (omode & O_ACCMODE)
{
case O_RDONLY:
fmode = "r";
break;
case O_WRONLY:
if (bitset(O_APPEND, omode))
fmode = "a";
else
fmode = "w";
break;
case O_RDWR:
if (bitset(O_TRUNC, omode))
fmode = "w+";
else if (bitset(O_APPEND, omode))
fmode = "a+";
else
fmode = "r+";
break;
default:
syserr("554 5.3.5 safefopen: unknown omode %o", omode);
fmode = "x";
}
fd = safeopen(fn, omode, cmode, sff);
if (fd < 0)
{
save_errno = errno;
if (tTd(44, 10))
dprintf("safefopen: safeopen failed: %s\n",
errstring(errno));
errno = save_errno;
return NULL;
}
fp = fdopen(fd, fmode);
if (fp != NULL)
return fp;
save_errno = errno;
if (tTd(44, 10))
{
dprintf("safefopen: fdopen(%s, %s) failed: omode=%x, sff=%lx, err=%s\n",
fn, fmode, omode, sff, errstring(errno));
}
(void) close(fd);
errno = save_errno;
return NULL;
}
/*
** FILECHANGED -- check to see if file changed after being opened
**
** Parameters:
** fn -- pathname of file to check.
** fd -- file descriptor to check.
** stb -- stat structure from before open.
**
** Returns:
** TRUE -- if a problem was detected.
** FALSE -- if this file is still the same.
*/
bool
filechanged(fn, fd, stb)
char *fn;
int fd;
struct stat *stb;
{
struct stat sta;
if (stb->st_mode == ST_MODE_NOFILE)
{
# if HASLSTAT && BOGUS_O_EXCL
/* only necessary if exclusive open follows symbolic links */
if (lstat(fn, stb) < 0 || stb->st_nlink != 1)
return TRUE;
# else /* HASLSTAT && BOGUS_O_EXCL */
return FALSE;
# endif /* HASLSTAT && BOGUS_O_EXCL */
}
if (fstat(fd, &sta) < 0)
return TRUE;
if (sta.st_nlink != stb->st_nlink ||
sta.st_dev != stb->st_dev ||
sta.st_ino != stb->st_ino ||
# if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */
sta.st_gen != stb->st_gen ||
# endif /* HAS_ST_GEN && 0 */
sta.st_uid != stb->st_uid ||
sta.st_gid != stb->st_gid)
{
if (tTd(44, 8))
{
dprintf("File changed after opening:\n");
dprintf(" nlink = %ld/%ld\n",
(long) stb->st_nlink, (long) sta.st_nlink);
dprintf(" dev = %ld/%ld\n",
(long) stb->st_dev, (long) sta.st_dev);
if (sizeof sta.st_ino > sizeof (long))
{
dprintf(" ino = %s/",
quad_to_string(stb->st_ino));
dprintf("%s\n",
quad_to_string(sta.st_ino));
}
else
dprintf(" ino = %lu/%lu\n",
(unsigned long) stb->st_ino,
(unsigned long) sta.st_ino);
# if HAS_ST_GEN
dprintf(" gen = %ld/%ld\n",
(long) stb->st_gen, (long) sta.st_gen);
# endif /* HAS_ST_GEN */
dprintf(" uid = %ld/%ld\n",
(long) stb->st_uid, (long) sta.st_uid);
dprintf(" gid = %ld/%ld\n",
(long) stb->st_gid, (long) sta.st_gid);
}
return TRUE;
}
return FALSE;
}
/*
** DFOPEN -- determined file open
**
** This routine has the semantics of open, except that it will
** keep trying a few times to make this happen. The idea is that
** on very loaded systems, we may run out of resources (inodes,
** whatever), so this tries to get around it.
*/
int
dfopen(filename, omode, cmode, sff)
char *filename;
int omode;
int cmode;
long sff;
{
register int tries;
int fd = -1;
struct stat st;
for (tries = 0; tries < 10; tries++)
{
(void) sleep((unsigned) (10 * tries));
errno = 0;
fd = open(filename, omode, cmode);
if (fd >= 0)
break;
switch (errno)
{
case ENFILE: /* system file table full */
case EINTR: /* interrupted syscall */
#ifdef ETXTBSY
case ETXTBSY: /* Apollo: net file locked */
#endif /* ETXTBSY */
continue;
}
break;
}
if (!bitset(SFF_NOLOCK, sff) &&
fd >= 0 &&
fstat(fd, &st) >= 0 &&
S_ISREG(st.st_mode))
{
int locktype;
/* lock the file to avoid accidental conflicts */
if ((omode & O_ACCMODE) != O_RDONLY)
locktype = LOCK_EX;
else
locktype = LOCK_SH;
if (!lockfile(fd, filename, NULL, locktype))
{
int save_errno = errno;
(void) close(fd);
fd = -1;
errno = save_errno;
}
else
errno = 0;
}
return fd;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -