diff --git a/fuse-CVE-2009-3297.patch b/fuse-CVE-2009-3297.patch new file mode 100644 index 0000000..44e3a32 --- /dev/null +++ b/fuse-CVE-2009-3297.patch @@ -0,0 +1,497 @@ +--- + 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 + #include + #include ++#include + + #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); + diff --git a/fuse.spec b/fuse.spec index 06e7fbd..05a33c3 100644 --- a/fuse.spec +++ b/fuse.spec @@ -1,6 +1,6 @@ Name: fuse Version: 2.8.1 -Release: 1%{?dist} +Release: 2%{?dist} Summary: File System in Userspace (FUSE) utilities Group: System Environment/Base @@ -12,6 +12,7 @@ Source2: fuse-makedev.d-fuse 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 @@ -55,6 +56,7 @@ pgk-config) to develop FUSE based applications/filesystems. 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 @@ -80,6 +82,7 @@ mv $RPM_BUILD_ROOT/%{_lib}/pkgconfig $RPM_BUILD_ROOT%{_libdir} # Get rid of static libs rm -f $RPM_BUILD_ROOT/%{_lib}/*.a +# No need to create init-script rm -f $RPM_BUILD_ROOT%{_sysconfdir}/init.d/fuse # Compatibility symlinks @@ -134,6 +137,9 @@ fi %{_includedir}/fuse %changelog +* Tue Jan 26 2010 Peter Lemenkov 2.8.1-2 +- Fixed CVE-2009-3297 (rhbz #558833) + * Thu Sep 17 2009 Peter Lemenkov 2.8.1-1 - Ver. 2.8.1 diff --git a/import.log b/import.log index e4b7b56..44cb966 100644 --- a/import.log +++ b/import.log @@ -2,3 +2,4 @@ fuse-2_7_3-3_fc9:HEAD:fuse-2.7.3-3.fc9.src.rpm:1215842579 fuse-2_7_4-1_fc9:HEAD:fuse-2.7.4-1.fc9.src.rpm:1222597934 fuse-2_7_4-2_fc10:HEAD:fuse-2.7.4-2.fc10.src.rpm:1233157758 fuse-2_8_1-1_fc11:F-11:fuse-2.8.1-1.fc11.src.rpm:1254129459 +fuse-2_8_1-2_fc12:F-11:fuse-2.8.1-2.fc12.src.rpm:1264523133