rpms/fuse/devel fuse-CVE-2009-3297.patch, NONE, 1.1 fuse.spec, 1.40, 1.41 import.log, 1.6, 1.7

Peter Lemenkov peter at fedoraproject.org
Tue Jan 26 16:17:49 UTC 2010


Author: peter

Update of /cvs/pkgs/rpms/fuse/devel
In directory cvs1.fedora.phx.redhat.com:/tmp/cvs-serv10279/devel

Modified Files:
	fuse.spec import.log 
Added Files:
	fuse-CVE-2009-3297.patch 
Log Message:
Fixed CVE-2009-3297

fuse-CVE-2009-3297.patch:
 lib/mount.c       |    2 
 lib/mount_util.c  |   11 -
 lib/mount_util.h  |    3 
 util/fusermount.c |  376 ++++++++++++++++++++++++++++++++++++++++++++++--------
 4 files changed, 330 insertions(+), 62 deletions(-)

--- NEW FILE fuse-CVE-2009-3297.patch ---
---
 lib/mount.c       |    2 
 lib/mount_util.c  |   11 -
 lib/mount_util.h  |    3 
 util/fusermount.c |  376 ++++++++++++++++++++++++++++++++++++++++++++++--------
 4 files changed, 330 insertions(+), 62 deletions(-)

Index: util/fusermount.c
===================================================================
--- util/fusermount.c.orig	2009-07-02 14:48:53.000000000 +0200
+++ util/fusermount.c	2010-01-21 21:19:12.218633360 +0100
@@ -26,6 +26,7 @@
 #include <sys/fsuid.h>
 #include <sys/socket.h>
 #include <sys/utsname.h>
+#include <sched.h>
 
 #define FUSE_COMMFD_ENV		"_FUSE_COMMFD"
 
@@ -37,6 +38,12 @@
 #ifndef MS_DIRSYNC
 #define MS_DIRSYNC 128
 #endif
+#ifndef MS_REC
+#define MS_REC 16384
+#endif
+#ifndef MS_SLAVE
+#define MS_SLAVE (1<<19)
+#endif
 
 static const char *progname;
 
@@ -74,77 +81,336 @@ static void restore_privs(void)
 }
 
 #ifndef IGNORE_MTAB
+/*
+ * Make sure that /etc/mtab is checked and updated atomically
+ */
+static int lock_umount(void)
+{
+	const char *mtab_lock = _PATH_MOUNTED ".fuselock";
+	int mtablock;
+	int res;
+	struct stat mtab_stat;
+
+	/* /etc/mtab could be a symlink to /proc/mounts */
+	if (lstat(_PATH_MOUNTED, &mtab_stat) == 0 && S_ISLNK(mtab_stat.st_mode))
+		return -1;
+
+	mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600);
+	if (mtablock == -1) {
+		fprintf(stderr, "%s: unable to open fuse lock file: %s\n",
+			progname, strerror(errno));
+		return -1;
+	}
+	res = lockf(mtablock, F_LOCK, 0);
+	if (res < 0) {
+		fprintf(stderr, "%s: error getting lock: %s\n", progname,
+			strerror(errno));
+		close(mtablock);
+		return -1;
+	}
+
+	return mtablock;
+}
+
+static void unlock_umount(int mtablock)
+{
+	lockf(mtablock, F_ULOCK, 0);
+	close(mtablock);
+}
+
 static int add_mount(const char *source, const char *mnt, const char *type,
 		     const char *opts)
 {
 	return fuse_mnt_add_mount(progname, source, mnt, type, opts);
 }
 
-static int unmount_fuse(const char *mnt, int quiet, int lazy)
+static int may_unmount(const char *mnt, int quiet)
 {
-	if (getuid() != 0) {
-		struct mntent *entp;
-		FILE *fp;
-		const char *user = NULL;
-		char uidstr[32];
-		unsigned uidlen = 0;
-		int found;
-		const char *mtab = _PATH_MOUNTED;
+	struct mntent *entp;
+	FILE *fp;
+	const char *user = NULL;
+	char uidstr[32];
+	unsigned uidlen = 0;
+	int found;
+	const char *mtab = _PATH_MOUNTED;
 
-		user = get_user_name();
-		if (user == NULL)
-			return -1;
+	user = get_user_name();
+	if (user == NULL)
+		return -1;
 
-		fp = setmntent(mtab, "r");
-		if (fp == NULL) {
-			fprintf(stderr,
-				"%s: failed to open %s: %s\n", progname, mtab,
-				strerror(errno));
-			return -1;
-		}
+	fp = setmntent(mtab, "r");
+	if (fp == NULL) {
+		fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
+			strerror(errno));
+		return -1;
+	}
 
-		uidlen = sprintf(uidstr, "%u", getuid());
+	uidlen = sprintf(uidstr, "%u", getuid());
 
-		found = 0;
-		while ((entp = getmntent(fp)) != NULL) {
-			if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
-			    (strcmp(entp->mnt_type, "fuse") == 0 ||
-			     strcmp(entp->mnt_type, "fuseblk") == 0 ||
-			     strncmp(entp->mnt_type, "fuse.", 5) == 0 ||
-			     strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) {
-				char *p = strstr(entp->mnt_opts, "user=");
-				if (p &&
-				    (p == entp->mnt_opts || *(p-1) == ',') &&
-				    strcmp(p + 5, user) == 0) {
-					found = 1;
-					break;
-				}
-				/* /etc/mtab is a link pointing to
-				   /proc/mounts: */
-				else if ((p =
-					  strstr(entp->mnt_opts, "user_id=")) &&
-					 (p == entp->mnt_opts ||
-					  *(p-1) == ',') &&
-					 strncmp(p + 8, uidstr, uidlen) == 0 &&
-					 (*(p+8+uidlen) == ',' ||
-					  *(p+8+uidlen) == '\0')) {
-					found = 1;
-					break;
-				}
+	found = 0;
+	while ((entp = getmntent(fp)) != NULL) {
+		if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
+		    (strcmp(entp->mnt_type, "fuse") == 0 ||
+		     strcmp(entp->mnt_type, "fuseblk") == 0 ||
+		     strncmp(entp->mnt_type, "fuse.", 5) == 0 ||
+		     strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) {
+			char *p = strstr(entp->mnt_opts, "user=");
+			if (p &&
+			    (p == entp->mnt_opts || *(p-1) == ',') &&
+			    strcmp(p + 5, user) == 0) {
+				found = 1;
+				break;
+			}
+			/* /etc/mtab is a link pointing to
+			   /proc/mounts: */
+			else if ((p =
+				  strstr(entp->mnt_opts, "user_id=")) &&
+				 (p == entp->mnt_opts ||
+				  *(p-1) == ',') &&
+				 strncmp(p + 8, uidstr, uidlen) == 0 &&
+				 (*(p+8+uidlen) == ',' ||
+				  *(p+8+uidlen) == '\0')) {
+				found = 1;
+				break;
 			}
 		}
-		endmntent(fp);
+	}
+	endmntent(fp);
 
-		if (!found) {
-			if (!quiet)
-				fprintf(stderr,
-					"%s: entry for %s not found in %s\n",
-					progname, mnt, mtab);
-			return -1;
+	if (!found) {
+		if (!quiet)
+			fprintf(stderr,
+				"%s: entry for %s not found in %s\n",
+				progname, mnt, mtab);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Check whether the file specified in "fusermount -u" is really a
+ * mountpoint and not a symlink.  This is necessary otherwise the user
+ * could move the mountpoint away and replace it with a symlink
+ * pointing to an arbitrary mount, thereby tricking fusermount into
+ * unmounting that (umount(2) will follow symlinks).
+ *
+ * This is the child process running in a separate mount namespace, so
+ * we don't mess with the global namespace and if the process is
+ * killed for any reason, mounts are automatically cleaned up.
+ *
+ * First make sure nothing is propagated back into the parent
+ * namespace by marking all mounts "slave".
+ *
+ * Then bind mount parent onto a stable base where the user can't move
+ * it around.  Use "/tmp", since it will almost certainly exist, but
+ * anything similar would do as well.
+ *
+ * Finally check /proc/mounts for an entry matching the requested
+ * mountpoint.  If it's found then we are OK, and the user can't move
+ * it around within the parent directory as rename() will return EBUSY.
+ */
+static int check_is_mount_child(void *p)
+{
+	const char **a = p;
+	const char *last = a[0];
+	const char *mnt = a[1];
+	int res;
+	const char *procmounts = "/proc/mounts";
+	int found;
+	FILE *fp;
+	struct mntent *entp;
+
+	res = mount("", "/", "", MS_SLAVE | MS_REC, NULL);
+	if (res == -1) {
+		fprintf(stderr, "%s: failed to mark mounts slave: %s\n",
+			progname, strerror(errno));
+		return 1;
+	}
+
+	res = mount(".", "/tmp", "", MS_BIND | MS_REC, NULL);
+	if (res == -1) {
+		fprintf(stderr, "%s: failed to bind parent to /tmp: %s\n",
+			progname, strerror(errno));
+		return 1;
+	}
+
+	fp = setmntent(procmounts, "r");
+	if (fp == NULL) {
+		fprintf(stderr, "%s: failed to open %s: %s\n", progname,
+			procmounts, strerror(errno));
+		return 1;
+	}
+
+	found = 0;
+	while ((entp = getmntent(fp)) != NULL) {
+		if (strncmp(entp->mnt_dir, "/tmp/", 5) == 0 &&
+		    strcmp(entp->mnt_dir + 5, last) == 0) {
+			found = 1;
+			break;
 		}
 	}
+	endmntent(fp);
+
+	if (!found) {
+		fprintf(stderr, "%s: %s not mounted\n", progname, mnt);
+		return 1;
+	}
+
+	return 0;
+}
+
+static pid_t clone_newns(void *a)
+{
+	long long buf[16384];
+	size_t stacksize = sizeof(buf) / 2;
+	char *stack = ((char *) buf) + stacksize;
+
+#ifdef __ia64__
+	extern int __clone2(int (*fn)(void *),
+			    void *child_stack_base, size_t stack_size,
+			    int flags, void *arg, pid_t *ptid,
+			    void *tls, pid_t *ctid);
+
+	return __clone2(check_is_mount_child, stack, stacksize, CLONE_NEWNS, a,
+			NULL, NULL, NULL);
+#else
+	return clone(check_is_mount_child, stack, CLONE_NEWNS, a);
+#endif
+}
+
+static int check_is_mount(const char *last, const char *mnt)
+{
+	pid_t pid, p;
+	int status;
+	const char *a[2] = { last, mnt };
+
+	pid = clone_newns((void *) a);
+	if (pid == (pid_t) -1) {
+		fprintf(stderr, "%s: failed to clone namespace: %s\n",
+			progname, strerror(errno));
+		return -1;
+	}
+	p = waitpid(pid, &status, __WCLONE);
+	if (p == (pid_t) -1) {
+		fprintf(stderr, "%s: waitpid failed: %s\n",
+			progname, strerror(errno));
+		return -1;
+	}
+	if (!WIFEXITED(status)) {
+		fprintf(stderr, "%s: child terminated abnormally (status %i)\n",
+			progname, status);
+		return -1;
+	}
+	if (WEXITSTATUS(status) != 0)
+		return -1;
+
+	return 0;
+}
+
+static int chdir_to_parent(char *copy, const char **lastp, int *currdir_fd)
+{
+	char *tmp;
+	const char *parent;
+	char buf[65536];
+	int res;
+
+	tmp = strrchr(copy, '/');
+	if (tmp == NULL || tmp[1] == '\0') {
+		fprintf(stderr, "%s: internal error: invalid abs path: <%s>\n",
+			progname, copy);
+		return -1;
+	}
+	if (tmp != copy) {
+		*tmp = '\0';
+		parent = copy;
+		*lastp = tmp + 1;
+	} else if (tmp[1] != '\0') {
+		*lastp = tmp + 1;
+		parent = "/";
+	} else {
+		*lastp = ".";
+		parent = "/";
+	}
+
+	*currdir_fd = open(".", O_RDONLY);
+	if (*currdir_fd == -1) {
+		fprintf(stderr,
+			"%s: failed to open current directory: %s\n",
+			progname, strerror(errno));
+		return -1;
+	}
+
+	res = chdir(parent);
+	if (res == -1) {
+		fprintf(stderr, "%s: failed to chdir to %s: %s\n",
+			progname, parent, strerror(errno));
+		return -1;
+	}
+
+	if (getcwd(buf, sizeof(buf)) == NULL) {
+		fprintf(stderr, "%s: failed to obtain current directory: %s\n",
+			progname, strerror(errno));
+		return -1;
+	}
+	if (strcmp(buf, parent) != 0) {
+		fprintf(stderr, "%s: mountpoint moved (%s -> %s)\n", progname,
+			parent, buf);
+		return -1;
+
+	}
+
+	return 0;
+}
+
+static int unmount_fuse_locked(const char *mnt, int quiet, int lazy)
+{
+	int currdir_fd = -1;
+	char *copy;
+	const char *last;
+	int res;
+
+	if (getuid() != 0) {
+		res = may_unmount(mnt, quiet);
+		if (res == -1)
+			return -1;
+	}
+
+	copy = strdup(mnt);
+	if (copy == NULL) {
+		fprintf(stderr, "%s: failed to allocate memory\n", progname);
+		return -1;
+	}
+
+	res = chdir_to_parent(copy, &last, &currdir_fd);
+	if (res == -1)
+		goto out;
+
+	res = check_is_mount(last, mnt);
+	if (res == -1)
+		goto out;
+
+	res = fuse_mnt_umount(progname, mnt, last, lazy);
+
+out:
+	free(copy);
+	if (currdir_fd != -1) {
+		fchdir(currdir_fd);
+		close(currdir_fd);
+	}
+
+	return res;
+}
+
+static int unmount_fuse(const char *mnt, int quiet, int lazy)
+{
+	int res;
+	int mtablock = lock_umount();
+
+	res = unmount_fuse_locked(mnt, quiet, lazy);
+	unlock_umount(mtablock);
 
-	return fuse_mnt_umount(progname, mnt, lazy);
+	return res;
 }
 
 static int count_fuse_fs(void)
@@ -186,7 +452,7 @@ static int add_mount(const char *source,
 
 static int unmount_fuse(const char *mnt, int quiet, int lazy)
 {
-	return fuse_mnt_umount(progname, mnt, lazy);
+	return fuse_mnt_umount(progname, mnt, mnt, lazy);
 }
 #endif /* IGNORE_MTAB */
 
Index: lib/mount.c
===================================================================
--- lib/mount.c.orig	2009-01-28 10:46:45.000000000 +0100
+++ lib/mount.c	2010-01-21 21:19:12.218633360 +0100
@@ -290,7 +290,7 @@ void fuse_kern_unmount(const char *mount
 	}
 
 	if (geteuid() == 0) {
-		fuse_mnt_umount("fuse", mountpoint, 1);
+		fuse_mnt_umount("fuse", mountpoint, mountpoint,  1);
 		return;
 	}
 
Index: lib/mount_util.c
===================================================================
--- lib/mount_util.c.orig	2008-07-10 21:35:39.000000000 +0200
+++ lib/mount_util.c	2010-01-21 21:19:12.219633417 +0100
@@ -119,18 +119,19 @@ int fuse_mnt_add_mount(const char *progn
 	return res;
 }
 
-int fuse_mnt_umount(const char *progname, const char *mnt, int lazy)
+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(mnt)) {
-		res = umount2(mnt, lazy ? 2 : 0);
+	if (!mtab_needs_update(abs_mnt)) {
+		res = umount2(rel_mnt, lazy ? 2 : 0);
 		if (res == -1)
 			fprintf(stderr, "%s: failed to unmount %s: %s\n",
-				progname, mnt, strerror(errno));
+				progname, abs_mnt, strerror(errno));
 		return res;
 	}
 
@@ -150,7 +151,7 @@ int fuse_mnt_umount(const char *progname
 	if (res == 0) {
 		sigprocmask(SIG_SETMASK, &oldmask, NULL);
 		setuid(geteuid());
-		execl("/bin/umount", "/bin/umount", "-i", mnt,
+		execl("/bin/umount", "/bin/umount", "-i", rel_mnt,
 		      lazy ? "-l" : NULL, NULL);
 		fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
 			progname, strerror(errno));
Index: lib/mount_util.h
===================================================================
--- lib/mount_util.h.orig	2007-12-12 13:58:00.000000000 +0100
+++ lib/mount_util.h	2010-01-21 21:19:12.219633417 +0100
@@ -10,7 +10,8 @@
 
 int fuse_mnt_add_mount(const char *progname, const char *fsname,
 		       const char *mnt, const char *type, const char *opts);
-int fuse_mnt_umount(const char *progname, const char *mnt, int lazy);
+int fuse_mnt_umount(const char *progname, const char *abs_mnt,
+		    const char *rel_mnt, int lazy);
 char *fuse_mnt_resolve_path(const char *progname, const char *orig);
 int fuse_mnt_check_empty(const char *progname, const char *mnt,
 			 mode_t rootmode, off_t rootsize);



Index: fuse.spec
===================================================================
RCS file: /cvs/pkgs/rpms/fuse/devel/fuse.spec,v
retrieving revision 1.40
retrieving revision 1.41
diff -u -p -r1.40 -r1.41
--- fuse.spec	19 Nov 2009 19:13:52 -0000	1.40
+++ fuse.spec	26 Jan 2010 16:17:48 -0000	1.41
@@ -1,6 +1,6 @@
 Name:           fuse
 Version:        2.8.1
-Release:        3%{?dist}
+Release:        4%{?dist}
 Summary:        File System in Userspace (FUSE) utilities
 
 Group:          System Environment/Base
@@ -10,6 +10,7 @@ Source0:        http://downloads.sourcef
 
 Patch0:         fuse-udev_rules.patch
 Patch1:         fuse-openfix.patch
+Patch2:		fuse-CVE-2009-3297.patch
 BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 Requires:       kernel >= 2.6.14
 Requires:       which
@@ -52,6 +53,7 @@ pgk-config) to develop FUSE based applic
 sed -i 's|mknod|echo Disabled: mknod |g' util/Makefile.in
 %patch0 -p0 -b .patch0
 %patch1 -p0 -b .patch1
+%patch2 -p0 -b .CVE-2009-3297
 
 %build
 # Can't pass --disable-static here, or else the utils don't build
@@ -125,6 +127,9 @@ fi
 %{_includedir}/fuse
 
 %changelog
+* Tue Jan 26 2010 Peter Lemenkov <lemenkov at gmail.com> 2.8.1-4
+- Fixed CVE-2009-3297 (rhbz #558833)
+
 * Thu Nov 19 2009 Peter Lemenkov <lemenkov at gmail.com> 2.8.1-3
 - Fixed udev rules (bz# 538606)
 


Index: import.log
===================================================================
RCS file: /cvs/pkgs/rpms/fuse/devel/import.log,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -p -r1.6 -r1.7
--- import.log	19 Nov 2009 19:13:52 -0000	1.6
+++ import.log	26 Jan 2010 16:17:48 -0000	1.7
@@ -4,3 +4,4 @@ fuse-2_7_4-2_fc10:HEAD:fuse-2.7.4-2.fc10
 fuse-2_8_0-1_fc11:HEAD:fuse-2.8.0-1.fc11.src.rpm:1253089638
 fuse-2_8_1-1_fc11:HEAD:fuse-2.8.1-1.fc11.src.rpm:1254129320
 fuse-2_8_1-3_fc12:HEAD:fuse-2.8.1-3.fc12.src.rpm:1258657884
+fuse-2_8_1-4_fc12:HEAD:fuse-2.8.1-4.fc12.src.rpm:1264522586



More information about the scm-commits mailing list