⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 safefile.c

📁 < linux网络编程工具>>配套源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/*
 * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
 * Copyright (c) 1988, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 *
 */

#ifndef lint
static char id[] = "@(#)$Id: safefile.c,v 8.81.4.7 2000/09/01 21:09:23 ca Exp $";
#endif /* ! lint */

#include <sendmail.h>


/*
**  SAFEFILE -- return 0 if a file exists and is safe for a user.
**
**	Parameters:
**		fn -- filename to check.
**		uid -- user id to compare against.
**		gid -- group id to compare against.
**		user -- user name to compare against (used for group
**			sets).
**		flags -- modifiers:
**			SFF_MUSTOWN -- "uid" must own this file.
**			SFF_NOSLINK -- file cannot be a symbolic link.
**		mode -- mode bits that must match.
**		st -- if set, points to a stat structure that will
**			get the stat info for the file.
**
**	Returns:
**		0 if fn exists, is owned by uid, and matches mode.
**		An errno otherwise.  The actual errno is cleared.
**
**	Side Effects:
**		none.
*/

int
safefile(fn, uid, gid, user, flags, mode, st)
	char *fn;
	UID_T uid;
	GID_T gid;
	char *user;
	long flags;
	int mode;
	struct stat *st;
{
	register char *p;
	register struct group *gr = NULL;
	int file_errno = 0;
	bool checkpath;
	struct stat stbuf;
	struct stat fstbuf;
	char fbuf[MAXPATHLEN + 1];

	if (tTd(44, 4))
		dprintf("safefile(%s, uid=%d, gid=%d, flags=%lx, mode=%o):\n",
			fn, (int) uid, (int) gid, flags, mode);
	errno = 0;
	if (st == NULL)
		st = &fstbuf;
	if (strlcpy(fbuf, fn, sizeof fbuf) >= sizeof fbuf)
	{
		if (tTd(44, 4))
			dprintf("\tpathname too long\n");
		return ENAMETOOLONG;
	}
	fn = fbuf;

	/* ignore SFF_SAFEDIRPATH if we are debugging */
	if (RealUid != 0 && RunAsUid == RealUid)
		flags &= ~SFF_SAFEDIRPATH;

	/* first check to see if the file exists at all */
# if HASLSTAT
	if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st)
					: stat(fn, st)) < 0)
# else /* HASLSTAT */
	if (stat(fn, st) < 0)
# endif /* HASLSTAT */
	{
		file_errno = errno;
	}
	else if (bitset(SFF_SETUIDOK, flags) &&
		 !bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode) &&
		 S_ISREG(st->st_mode))
	{
		/*
		**  If final file is setuid, run as the owner of that
		**  file.  Gotta be careful not to reveal anything too
		**  soon here!
		*/

# ifdef SUID_ROOT_FILES_OK
		if (bitset(S_ISUID, st->st_mode))
# else /* SUID_ROOT_FILES_OK */
		if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0 &&
		    st->st_uid != TrustedUid)
# endif /* SUID_ROOT_FILES_OK */
		{
			uid = st->st_uid;
			user = NULL;
		}
# ifdef SUID_ROOT_FILES_OK
		if (bitset(S_ISGID, st->st_mode))
# else /* SUID_ROOT_FILES_OK */
		if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0)
# endif /* SUID_ROOT_FILES_OK */
			gid = st->st_gid;
	}

	checkpath = !bitset(SFF_NOPATHCHECK, flags) ||
		    (uid == 0 && !bitset(SFF_ROOTOK|SFF_OPENASROOT, flags));
	if (bitset(SFF_NOWLINK, flags) && !bitset(SFF_SAFEDIRPATH, flags))
	{
		int ret;

		/* check the directory */
		p = strrchr(fn, '/');
		if (p == NULL)
		{
			ret = safedirpath(".", uid, gid, user,
					  flags|SFF_SAFEDIRPATH, 0, 0);
		}
		else
		{
			*p = '\0';
			ret = safedirpath(fn, uid, gid, user,
					  flags|SFF_SAFEDIRPATH, 0, 0);
			*p = '/';
		}
		if (ret == 0)
		{
			/* directory is safe */
			checkpath = FALSE;
		}
		else
		{
# if HASLSTAT
			/* Need lstat() information if called stat() before */
			if (!bitset(SFF_NOSLINK, flags) && lstat(fn, st) < 0)
			{
				ret = errno;
				if (tTd(44, 4))
					dprintf("\t%s\n", errstring(ret));
				return ret;
			}
# endif /* HASLSTAT */
			/* directory is writable: disallow links */
			flags |= SFF_NOLINK;
		}
	}

	if (checkpath)
	{
		int ret;

		p = strrchr(fn, '/');
		if (p == NULL)
		{
			ret = safedirpath(".", uid, gid, user, flags, 0, 0);
		}
		else
		{
			*p = '\0';
			ret = safedirpath(fn, uid, gid, user, flags, 0, 0);
			*p = '/';
		}
		if (ret != 0)
			return ret;
	}

	/*
	**  If the target file doesn't exist, check the directory to
	**  ensure that it is writable by this user.
	*/

	if (file_errno != 0)
	{
		int ret = file_errno;
		char *dir = fn;

		if (tTd(44, 4))
			dprintf("\t%s\n", errstring(ret));

		errno = 0;
		if (!bitset(SFF_CREAT, flags) || file_errno != ENOENT)
			return ret;

		/* check to see if legal to create the file */
		p = strrchr(dir, '/');
		if (p == NULL)
			dir = ".";
		else if (p == dir)
			dir = "/";
		else
			*p = '\0';
		if (stat(dir, &stbuf) >= 0)
		{
			int md = S_IWRITE|S_IEXEC;

			if (stbuf.st_uid == uid)
				/* EMPTY */
				;
			else if (uid == 0 && stbuf.st_uid == TrustedUid)
				/* EMPTY */
				;
			else
			{
				md >>= 3;
				if (stbuf.st_gid == gid)
					/* EMPTY */
					;
# ifndef NO_GROUP_SET
				else 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++)
						if (strcmp(*gp, user) == 0)
							break;
					if (*gp == NULL)
						md >>= 3;
				}
# endif /* ! NO_GROUP_SET */
				else
					md >>= 3;
			}
			if ((stbuf.st_mode & md) != md)
				errno = EACCES;
		}
		ret = errno;
		if (tTd(44, 4))
			dprintf("\t[final dir %s uid %d mode %lo] %s\n",
				dir, (int) stbuf.st_uid, (u_long) stbuf.st_mode,
				errstring(ret));
		if (p != NULL)
			*p = '/';
		st->st_mode = ST_MODE_NOFILE;
		return ret;
	}

# ifdef S_ISLNK
	if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode))
	{
		if (tTd(44, 4))
			dprintf("\t[slink mode %lo]\tE_SM_NOSLINK\n",
				(u_long) st->st_mode);
		return E_SM_NOSLINK;
	}
# endif /* S_ISLNK */
	if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode))
	{
		if (tTd(44, 4))
			dprintf("\t[non-reg mode %lo]\tE_SM_REGONLY\n",
				(u_long) st->st_mode);
		return E_SM_REGONLY;
	}
	if (bitset(SFF_NOGWFILES, flags) &&
	    bitset(S_IWGRP, st->st_mode))
	{
		if (tTd(44, 4))
			dprintf("\t[write bits %lo]\tE_SM_GWFILE\n",
				(u_long) st->st_mode);
		return E_SM_GWFILE;
	}
	if (bitset(SFF_NOWWFILES, flags) &&
	    bitset(S_IWOTH, st->st_mode))
	{
		if (tTd(44, 4))
			dprintf("\t[write bits %lo]\tE_SM_WWFILE\n",
				(u_long) st->st_mode);
		return E_SM_WWFILE;
	}
	if (bitset(SFF_NOGRFILES, flags) && bitset(S_IRGRP, st->st_mode))
	{
		if (tTd(44, 4))
			dprintf("\t[read bits %lo]\tE_SM_GRFILE\n",
				(u_long) st->st_mode);
		return E_SM_GRFILE;
	}
	if (bitset(SFF_NOWRFILES, flags) && bitset(S_IROTH, st->st_mode))
	{
		if (tTd(44, 4))
			dprintf("\t[read bits %lo]\tE_SM_WRFILE\n",
				(u_long) st->st_mode);
		return E_SM_WRFILE;
	}
	if (!bitset(SFF_EXECOK, flags) &&
	    bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) &&
	    bitset(S_IXUSR|S_IXGRP|S_IXOTH, st->st_mode))
	{
		if (tTd(44, 4))
			dprintf("\t[exec bits %lo]\tE_SM_ISEXEC]\n",
				(u_long) st->st_mode);
		return E_SM_ISEXEC;
	}
	if (bitset(SFF_NOHLINK, flags) && st->st_nlink != 1)
	{
		if (tTd(44, 4))
			dprintf("\t[link count %d]\tE_SM_NOHLINK\n",
				(int) st->st_nlink);
		return E_SM_NOHLINK;
	}

	if (uid == 0 && bitset(SFF_OPENASROOT, flags))
		/* EMPTY */
		;
	else if (uid == 0 && !bitset(SFF_ROOTOK, flags))
		mode >>= 6;
	else if (st->st_uid == uid)
		/* EMPTY */
		;
	else if (uid == 0 && st->st_uid == TrustedUid)
		/* EMPTY */
		;
	else
	{
		mode >>= 3;
		if (st->st_gid == gid)
			/* EMPTY */
			;
# ifndef NO_GROUP_SET
		else if (user != NULL && !DontInitGroups &&
			 ((gr != NULL && gr->gr_gid == st->st_gid) ||
			  (gr = getgrgid(st->st_gid)) != NULL))
		{
			register char **gp;

			for (gp = gr->gr_mem; *gp != NULL; gp++)
				if (strcmp(*gp, user) == 0)
					break;
			if (*gp == NULL)
				mode >>= 3;
		}
# endif /* ! NO_GROUP_SET */
		else
			mode >>= 3;
	}
	if (tTd(44, 4))
		dprintf("\t[uid %d, nlink %d, stat %lo, mode %lo] ",
			(int) st->st_uid, (int) st->st_nlink,
			(u_long) st->st_mode, (u_long) mode);
	if ((st->st_uid == uid || st->st_uid == 0 ||
	     st->st_uid == TrustedUid ||
	     !bitset(SFF_MUSTOWN, flags)) &&
	    (st->st_mode & mode) == mode)
	{
		if (tTd(44, 4))
			dprintf("\tOK\n");
		return 0;
	}
	if (tTd(44, 4))
		dprintf("\tEACCES\n");
	return EACCES;
}
/*
**  SAFEDIRPATH -- check to make sure a path to a directory is safe
**
**	Safe means not writable and owned by the right folks.
**
**	Parameters:
**		fn -- filename to check.
**		uid -- user id to compare against.
**		gid -- group id to compare against.
**		user -- user name to compare against (used for group
**			sets).
**		flags -- modifiers:
**			SFF_ROOTOK -- ok to use root permissions to open.
**			SFF_SAFEDIRPATH -- writable directories are considered
**				to be fatal errors.
**		level -- symlink recursive level.
**		offset -- offset into fn to start checking from.
**
**	Returns:
**		0 -- if the directory path is "safe".
**		else -- an error number associated with the path.
*/

int
safedirpath(fn, uid, gid, user, flags, level, offset)
	char *fn;
	UID_T uid;
	GID_T gid;
	char *user;
	long flags;
	int level;
	int offset;
{
	int ret = 0;
	int mode = S_IWOTH;
	char save = '\0';
	char *saveptr = NULL;
	char *p, *enddir;
	register struct group *gr = NULL;
	char s[MAXLINKPATHLEN + 1];
	struct stat stbuf;

	/* make sure we aren't in a symlink loop */
	if (level > MAXSYMLINKS)
		return ELOOP;

	/* special case root directory */
	if (*fn == '\0')
		fn = "/";

	if (tTd(44, 4))
		dprintf("safedirpath(%s, uid=%ld, gid=%ld, flags=%lx, level=%d, offset=%d):\n",
			fn, (long) uid, (long) gid, flags, level, offset);

	if (!bitnset(DBS_GROUPWRITABLEDIRPATHSAFE, DontBlameSendmail))
		mode |= S_IWGRP;

	/* Make a modifiable copy of the filename */
	if (strlcpy(s, fn, sizeof s) >= sizeof s)
		return EINVAL;

	p = s + offset;
	while (p != NULL)
	{
		/* put back character */
		if (saveptr != NULL)
		{
			*saveptr = save;
			saveptr = NULL;
			p++;
		}

		if (*p == '\0')
			break;

		p = strchr(p, '/');

		/* Special case for root directory */
		if (p == s)
		{
			save = *(p + 1);
			saveptr = p + 1;
			*(p + 1) = '\0';
		}
		else if (p != NULL)
		{
			save = *p;
			saveptr = p;
			*p = '\0';
		}

		/* Heuristic: . and .. have already been checked */
		enddir = strrchr(s, '/');
		if (enddir != NULL &&
		    (strcmp(enddir, "/..") == 0 ||
		     strcmp(enddir, "/.") == 0))
			continue;

		if (tTd(44, 20))
			dprintf("\t[dir %s]\n", s);

# if HASLSTAT
		ret = lstat(s, &stbuf);
# else /* HASLSTAT */
		ret = stat(s, &stbuf);
# endif /* HASLSTAT */
		if (ret < 0)
		{
			ret = errno;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -