glusterfs/contrib/fuse-lib/mount-common.c
Prasanna Kumar Kalever b5ceb1a9de fuse: fix return value check for setuid
setuid() sets the effective user ID of the calling process. If the
effective UID of the caller is root, the real UID and saved set-user-ID
are also set. On success, zero is returned.  On error, -1 is returned,
and errno is set appropriately.

there are cases where setuid() can fail even when the caller is UID 0;
it is a grave security error to omit checking for a failure return from
setuid(). if an environment limits the number of processes a user can
have, setuid() might fail if the target uid already is at the limit.

Fix is to check return value of setuid.

Change-Id: I7aa5ab5e347603c69dc93188417cc4f4c81ffc75
BUG: 1221490
Signed-off-by: Prasanna Kumar Kalever <prasanna.kalever@redhat.com>
Reviewed-on: http://review.gluster.org/10780
Reviewed-by: Prasanna Kumar Kalever
Tested-by: Prasanna Kumar Kalever
Reviewed-by: Niels de Vos <ndevos@redhat.com>
Tested-by: Gluster Build System <jenkins@build.gluster.com>
Reviewed-by: Gaurav Kumar Garg <ggarg@redhat.com>
2015-05-16 00:19:09 -07:00

283 lines
9.0 KiB
C

/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
Copyright (c) 2010 Gluster, Inc. <http://www.gluster.com>
This program can be distributed under the terms of the GNU LGPLv2.
See the file COPYING.LIB.
*/
#include "mount-gluster-compat.h"
/*
* These functions (and gf_fuse_umount() in mount.c)
* were originally taken from libfuse as of commit 7960e99e
* (http://fuse.git.sourceforge.net/git/gitweb.cgi?p=fuse/fuse;a=commit;h=7960e99e)
* almost verbatim. What has been changed upon adoption:
* - style adopted to that of glusterfs
* - s/fprintf/gf_log/
* - s/free/FREE/, s/malloc/MALLOC/
* - there are some other minor things
*
* For changes that were made later and syncs with upstream,
* see the commit log and per-function comments.
*/
#ifdef GF_LINUX_HOST_OS
/* FUSE: cherry-picked bd99f9cf */
static int
mtab_needs_update (const char *mnt)
{
int res;
struct stat stbuf;
/* If mtab is within new mount, don't touch it */
if (strncmp (mnt, _PATH_MOUNTED, strlen (mnt)) == 0 &&
_PATH_MOUNTED[strlen (mnt)] == '/')
return 0;
/*
* Skip mtab update if /etc/mtab:
*
* - doesn't exist,
* - is a symlink,
* - is on a read-only filesystem.
*/
res = lstat (_PATH_MOUNTED, &stbuf);
if (res == -1) {
if (errno == ENOENT)
return 0;
} else {
uid_t ruid;
int err;
if (S_ISLNK (stbuf.st_mode))
return 0;
ruid = getuid ();
if (ruid != 0)
setreuid (0, -1);
res = access (_PATH_MOUNTED, W_OK);
err = (res == -1) ? errno : 0;
if (ruid != 0)
setreuid (ruid, -1);
if (err == EROFS)
return 0;
}
return 1;
}
#else /* GF_LINUX_HOST_OS */
#define mtab_needs_update(x) 1
#endif /* GF_LINUX_HOST_OS */
/* FUSE: called add_mount_legacy(); R.I.P. as of cbd3a2a8 */
int
fuse_mnt_add_mount (const char *progname, const char *fsname,
const char *mnt, const char *type, const char *opts)
{
int res;
int status;
sigset_t blockmask;
sigset_t oldmask;
if (!mtab_needs_update (mnt))
return 0;
sigemptyset (&blockmask);
sigaddset (&blockmask, SIGCHLD);
res = sigprocmask (SIG_BLOCK, &blockmask, &oldmask);
if (res == -1) {
GFFUSE_LOGERR ("%s: sigprocmask: %s",
progname, strerror (errno));
return -1;
}
res = fork ();
if (res == -1) {
GFFUSE_LOGERR ("%s: fork: %s", progname, strerror (errno));
goto out_restore;
}
if (res == 0) {
char templ[] = "/tmp/fusermountXXXXXX";
char *tmp;
sigprocmask (SIG_SETMASK, &oldmask, NULL);
res = setuid (geteuid ());
if (res != 0) {
GFFUSE_LOGERR ("%s: setuid: %s", progname, strerror (errno));
exit (1);
}
/*
* hide in a directory, where mount isn't able to resolve
* fsname as a valid path
*/
tmp = mkdtemp (templ);
if (!tmp) {
GFFUSE_LOGERR ("%s: failed to create temporary directory",
progname);
exit (1);
}
if (chdir (tmp)) {
GFFUSE_LOGERR ("%s: failed to chdir to %s: %s",
progname, tmp, strerror (errno));
exit (1);
}
rmdir (tmp);
execl (_PATH_MOUNT, _PATH_MOUNT, "-i", "-f", "-t", type,
"-o", opts, fsname, mnt, NULL);
GFFUSE_LOGERR ("%s: failed to execute %s: %s",
progname, _PATH_MOUNT, strerror (errno));
exit (1);
}
res = waitpid (res, &status, 0);
if (res == -1)
GFFUSE_LOGERR ("%s: waitpid: %s", progname, strerror (errno));
res = (res != -1 && status == 0) ? 0 : -1;
out_restore:
sigprocmask (SIG_SETMASK, &oldmask, NULL);
return res;
}
char *
fuse_mnt_resolve_path (const char *progname, const char *orig)
{
char buf[PATH_MAX];
char *copy;
char *dst;
char *end;
char *lastcomp;
const char *toresolv;
if (!orig[0]) {
GFFUSE_LOGERR ("%s: invalid mountpoint '%s'", progname, orig);
return NULL;
}
copy = strdup (orig);
if (copy == NULL) {
GFFUSE_LOGERR ("%s: failed to allocate memory", progname);
return NULL;
}
toresolv = copy;
lastcomp = NULL;
for (end = copy + strlen (copy) - 1; end > copy && *end == '/'; end --);
if (end[0] != '/') {
char *tmp;
end[1] = '\0';
tmp = strrchr (copy, '/');
if (tmp == NULL) {
lastcomp = copy;
toresolv = ".";
} else {
lastcomp = tmp + 1;
if (tmp == copy)
toresolv = "/";
}
if (strcmp (lastcomp, ".") == 0 || strcmp (lastcomp, "..") == 0) {
lastcomp = NULL;
toresolv = copy;
}
else if (tmp)
tmp[0] = '\0';
}
if (realpath (toresolv, buf) == NULL) {
GFFUSE_LOGERR ("%s: bad mount point %s: %s", progname, orig,
strerror (errno));
FREE (copy);
return NULL;
}
if (lastcomp == NULL)
dst = strdup (buf);
else {
dst = (char *) MALLOC (strlen (buf) + 1 + strlen (lastcomp) + 1);
if (dst) {
unsigned buflen = strlen (buf);
if (buflen && buf[buflen-1] == '/')
sprintf (dst, "%s%s", buf, lastcomp);
else
sprintf (dst, "%s/%s", buf, lastcomp);
}
}
FREE (copy);
if (dst == NULL)
GFFUSE_LOGERR ("%s: failed to allocate memory", progname);
return dst;
}
/* FUSE: to support some changes that were reverted since
* then, it was split in two (fuse_mnt_umount() and
* exec_umount()); however the actual code is same as here
* since 0197ce40
*/
int
fuse_mnt_umount (const char *progname, const char *abs_mnt,
const char *rel_mnt, int lazy)
{
int res;
int status;
sigset_t blockmask;
sigset_t oldmask;
if (!mtab_needs_update (abs_mnt)) {
res = umount2 (rel_mnt, lazy ? 2 : 0);
if (res == -1)
GFFUSE_LOGERR ("%s: failed to unmount %s: %s",
progname, abs_mnt, strerror (errno));
return res;
}
sigemptyset (&blockmask);
sigaddset (&blockmask, SIGCHLD);
res = sigprocmask (SIG_BLOCK, &blockmask, &oldmask);
if (res == -1) {
GFFUSE_LOGERR ("%s: sigprocmask: %s", progname,
strerror (errno));
return -1;
}
res = fork ();
if (res == -1) {
GFFUSE_LOGERR ("%s: fork: %s", progname, strerror (errno));
goto out_restore;
}
if (res == 0) {
sigprocmask (SIG_SETMASK, &oldmask, NULL);
res = setuid (geteuid ());
if (res != 0) {
GFFUSE_LOGERR ("%s: setuid: %s", progname, strerror (errno));
exit (1);
}
#ifdef GF_LINUX_HOST_OS
execl ("/bin/umount", "/bin/umount", "-i", rel_mnt,
lazy ? "-l" : NULL, NULL);
GFFUSE_LOGERR ("%s: failed to execute /bin/umount: %s",
progname, strerror (errno));
#elif __NetBSD__
/* exitting the filesystem causes the umount */
exit (0);
#else
execl ("/sbin/umount", "/sbin/umount", "-f", rel_mnt, NULL);
GFFUSE_LOGERR ("%s: failed to execute /sbin/umount: %s",
progname, strerror (errno));
#endif /* GF_LINUX_HOST_OS */
exit (1);
}
res = waitpid (res, &status, 0);
if (res == -1)
GFFUSE_LOGERR ("%s: waitpid: %s", progname, strerror (errno));
if (status != 0)
res = -1;
out_restore:
sigprocmask (SIG_SETMASK, &oldmask, NULL);
return res;
}