1
#! /bin/sh /usr/share/dpatch/dpatch-run
2
## 200-fix_mount_symlink_handling.dpatch by Kees Cook <kees@ubuntu.com>
4
## All lines beginning with `## DP:' are a description of the patch.
5
## DP: Description: Fix CVE-2009-3297, stop from following symlinks
6
## DP: when mounting a fuse filesystem.
7
## DP: Origin: Upstream fixes.
10
diff -urNad fuse-2.8.1~/lib/mount.c fuse-2.8.1/lib/mount.c
11
--- fuse-2.8.1~/lib/mount.c 2009-01-28 01:46:45.000000000 -0800
12
+++ fuse-2.8.1/lib/mount.c 2010-01-25 13:34:26.554222913 -0800
17
- fuse_mnt_umount("fuse", mountpoint, 1);
18
+ fuse_mnt_umount("fuse", mountpoint, mountpoint, 1);
22
diff -urNad fuse-2.8.1~/lib/mount_util.c fuse-2.8.1/lib/mount_util.c
23
--- fuse-2.8.1~/lib/mount_util.c 2008-07-10 12:35:39.000000000 -0700
24
+++ fuse-2.8.1/lib/mount_util.c 2010-01-25 13:34:26.554222913 -0800
29
-int fuse_mnt_umount(const char *progname, const char *mnt, int lazy)
30
+int fuse_mnt_umount(const char *progname, const char *abs_mnt,
31
+ const char *rel_mnt, int lazy)
38
- if (!mtab_needs_update(mnt)) {
39
- res = umount2(mnt, lazy ? 2 : 0);
40
+ if (!mtab_needs_update(abs_mnt)) {
41
+ res = umount2(rel_mnt, lazy ? 2 : 0);
43
fprintf(stderr, "%s: failed to unmount %s: %s\n",
44
- progname, mnt, strerror(errno));
45
+ progname, abs_mnt, strerror(errno));
51
sigprocmask(SIG_SETMASK, &oldmask, NULL);
53
- execl("/bin/umount", "/bin/umount", "-i", mnt,
54
+ execl("/bin/umount", "/bin/umount", "-i", rel_mnt,
55
lazy ? "-l" : NULL, NULL);
56
fprintf(stderr, "%s: failed to execute /bin/umount: %s\n",
57
progname, strerror(errno));
58
diff -urNad fuse-2.8.1~/lib/mount_util.h fuse-2.8.1/lib/mount_util.h
59
--- fuse-2.8.1~/lib/mount_util.h 2007-12-12 04:58:00.000000000 -0800
60
+++ fuse-2.8.1/lib/mount_util.h 2010-01-25 13:34:26.554222913 -0800
63
int fuse_mnt_add_mount(const char *progname, const char *fsname,
64
const char *mnt, const char *type, const char *opts);
65
-int fuse_mnt_umount(const char *progname, const char *mnt, int lazy);
66
+int fuse_mnt_umount(const char *progname, const char *abs_mnt,
67
+ const char *rel_mnt, int lazy);
68
char *fuse_mnt_resolve_path(const char *progname, const char *orig);
69
int fuse_mnt_check_empty(const char *progname, const char *mnt,
70
mode_t rootmode, off_t rootsize);
71
diff -urNad fuse-2.8.1~/util/fusermount.c fuse-2.8.1/util/fusermount.c
72
--- fuse-2.8.1~/util/fusermount.c 2009-07-02 05:48:53.000000000 -0700
73
+++ fuse-2.8.1/util/fusermount.c 2010-01-25 13:34:26.554222913 -0800
75
#include <sys/fsuid.h>
76
#include <sys/socket.h>
77
#include <sys/utsname.h>
80
#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
84
#define MS_DIRSYNC 128
90
+#define MS_SLAVE (1<<19)
93
static const char *progname;
100
+ * Make sure that /etc/mtab is checked and updated atomically
102
+static int lock_umount(void)
104
+ const char *mtab_lock = _PATH_MOUNTED ".fuselock";
107
+ struct stat mtab_stat;
109
+ /* /etc/mtab could be a symlink to /proc/mounts */
110
+ if (lstat(_PATH_MOUNTED, &mtab_stat) == 0 && S_ISLNK(mtab_stat.st_mode))
113
+ mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600);
114
+ if (mtablock == -1) {
115
+ fprintf(stderr, "%s: unable to open fuse lock file: %s\n",
116
+ progname, strerror(errno));
119
+ res = lockf(mtablock, F_LOCK, 0);
121
+ fprintf(stderr, "%s: error getting lock: %s\n", progname,
130
+static void unlock_umount(int mtablock)
132
+ lockf(mtablock, F_ULOCK, 0);
136
static int add_mount(const char *source, const char *mnt, const char *type,
139
return fuse_mnt_add_mount(progname, source, mnt, type, opts);
142
-static int unmount_fuse(const char *mnt, int quiet, int lazy)
143
+static int may_unmount(const char *mnt, int quiet)
145
- if (getuid() != 0) {
146
- struct mntent *entp;
148
- const char *user = NULL;
150
- unsigned uidlen = 0;
152
- const char *mtab = _PATH_MOUNTED;
153
+ struct mntent *entp;
155
+ const char *user = NULL;
157
+ unsigned uidlen = 0;
159
+ const char *mtab = _PATH_MOUNTED;
161
- user = get_user_name();
164
+ user = get_user_name();
168
- fp = setmntent(mtab, "r");
171
- "%s: failed to open %s: %s\n", progname, mtab,
175
+ fp = setmntent(mtab, "r");
177
+ fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
182
- uidlen = sprintf(uidstr, "%u", getuid());
183
+ uidlen = sprintf(uidstr, "%u", getuid());
186
- while ((entp = getmntent(fp)) != NULL) {
187
- if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
188
- (strcmp(entp->mnt_type, "fuse") == 0 ||
189
- strcmp(entp->mnt_type, "fuseblk") == 0 ||
190
- strncmp(entp->mnt_type, "fuse.", 5) == 0 ||
191
- strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) {
192
- char *p = strstr(entp->mnt_opts, "user=");
194
- (p == entp->mnt_opts || *(p-1) == ',') &&
195
- strcmp(p + 5, user) == 0) {
199
- /* /etc/mtab is a link pointing to
202
- strstr(entp->mnt_opts, "user_id=")) &&
203
- (p == entp->mnt_opts ||
205
- strncmp(p + 8, uidstr, uidlen) == 0 &&
206
- (*(p+8+uidlen) == ',' ||
207
- *(p+8+uidlen) == '\0')) {
212
+ while ((entp = getmntent(fp)) != NULL) {
213
+ if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
214
+ (strcmp(entp->mnt_type, "fuse") == 0 ||
215
+ strcmp(entp->mnt_type, "fuseblk") == 0 ||
216
+ strncmp(entp->mnt_type, "fuse.", 5) == 0 ||
217
+ strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) {
218
+ char *p = strstr(entp->mnt_opts, "user=");
220
+ (p == entp->mnt_opts || *(p-1) == ',') &&
221
+ strcmp(p + 5, user) == 0) {
225
+ /* /etc/mtab is a link pointing to
228
+ strstr(entp->mnt_opts, "user_id=")) &&
229
+ (p == entp->mnt_opts ||
231
+ strncmp(p + 8, uidstr, uidlen) == 0 &&
232
+ (*(p+8+uidlen) == ',' ||
233
+ *(p+8+uidlen) == '\0')) {
245
- "%s: entry for %s not found in %s\n",
246
- progname, mnt, mtab);
251
+ "%s: entry for %s not found in %s\n",
252
+ progname, mnt, mtab);
260
+ * Check whether the file specified in "fusermount -u" is really a
261
+ * mountpoint and not a symlink. This is necessary otherwise the user
262
+ * could move the mountpoint away and replace it with a symlink
263
+ * pointing to an arbitrary mount, thereby tricking fusermount into
264
+ * unmounting that (umount(2) will follow symlinks).
266
+ * This is the child process running in a separate mount namespace, so
267
+ * we don't mess with the global namespace and if the process is
268
+ * killed for any reason, mounts are automatically cleaned up.
270
+ * First make sure nothing is propagated back into the parent
271
+ * namespace by marking all mounts "slave".
273
+ * Then bind mount parent onto a stable base where the user can't move
274
+ * it around. Use "/tmp", since it will almost certainly exist, but
275
+ * anything similar would do as well.
277
+ * Finally check /proc/mounts for an entry matching the requested
278
+ * mountpoint. If it's found then we are OK, and the user can't move
279
+ * it around within the parent directory as rename() will return EBUSY.
281
+static int check_is_mount_child(void *p)
283
+ const char **a = p;
284
+ const char *last = a[0];
285
+ const char *mnt = a[1];
287
+ const char *procmounts = "/proc/mounts";
290
+ struct mntent *entp;
292
+ res = mount("", "/", "", MS_SLAVE | MS_REC, NULL);
294
+ fprintf(stderr, "%s: failed to mark mounts slave: %s\n",
295
+ progname, strerror(errno));
299
+ res = mount(".", "/tmp", "", MS_BIND | MS_REC, NULL);
301
+ fprintf(stderr, "%s: failed to bind parent to /tmp: %s\n",
302
+ progname, strerror(errno));
306
+ fp = setmntent(procmounts, "r");
308
+ fprintf(stderr, "%s: failed to open %s: %s\n", progname,
309
+ procmounts, strerror(errno));
314
+ while ((entp = getmntent(fp)) != NULL) {
315
+ if (strncmp(entp->mnt_dir, "/tmp/", 5) == 0 &&
316
+ strcmp(entp->mnt_dir + 5, last) == 0) {
323
- return fuse_mnt_umount(progname, mnt, lazy);
325
+ fprintf(stderr, "%s: %s not mounted\n", progname, mnt);
332
+static pid_t clone_newns(void *a)
334
+ long long buf[16384];
335
+ size_t stacksize = sizeof(buf) / 2;
336
+ char *stack = ((char *) buf) + stacksize;
339
+ extern int __clone2(int (*fn)(void *),
340
+ void *child_stack_base, size_t stack_size,
341
+ int flags, void *arg, pid_t *ptid,
342
+ void *tls, pid_t *ctid);
344
+ return __clone2(check_is_mount_child, stack, stacksize, CLONE_NEWNS, a,
347
+ return clone(check_is_mount_child, stack, CLONE_NEWNS, a);
351
+static int check_is_mount(const char *last, const char *mnt)
355
+ const char *a[2] = { last, mnt };
357
+ pid = clone_newns((void *) a);
358
+ if (pid == (pid_t) -1) {
359
+ fprintf(stderr, "%s: failed to clone namespace: %s\n",
360
+ progname, strerror(errno));
363
+ p = waitpid(pid, &status, __WCLONE);
364
+ if (p == (pid_t) -1) {
365
+ fprintf(stderr, "%s: waitpid failed: %s\n",
366
+ progname, strerror(errno));
369
+ if (!WIFEXITED(status)) {
370
+ fprintf(stderr, "%s: child terminated abnormally (status %i)\n",
374
+ if (WEXITSTATUS(status) != 0)
380
+static int chdir_to_parent(char *copy, const char **lastp, int *currdir_fd)
383
+ const char *parent;
387
+ tmp = strrchr(copy, '/');
388
+ if (tmp == NULL || tmp[1] == '\0') {
389
+ fprintf(stderr, "%s: internal error: invalid abs path: <%s>\n",
397
+ } else if (tmp[1] != '\0') {
405
+ *currdir_fd = open(".", O_RDONLY);
406
+ if (*currdir_fd == -1) {
408
+ "%s: failed to open current directory: %s\n",
409
+ progname, strerror(errno));
413
+ res = chdir(parent);
415
+ fprintf(stderr, "%s: failed to chdir to %s: %s\n",
416
+ progname, parent, strerror(errno));
420
+ if (getcwd(buf, sizeof(buf)) == NULL) {
421
+ fprintf(stderr, "%s: failed to obtain current directory: %s\n",
422
+ progname, strerror(errno));
425
+ if (strcmp(buf, parent) != 0) {
426
+ fprintf(stderr, "%s: mountpoint moved (%s -> %s)\n", progname,
435
+static int unmount_fuse_locked(const char *mnt, int quiet, int lazy)
437
+ int currdir_fd = -1;
442
+ if (getuid() != 0) {
443
+ res = may_unmount(mnt, quiet);
448
+ copy = strdup(mnt);
449
+ if (copy == NULL) {
450
+ fprintf(stderr, "%s: failed to allocate memory\n", progname);
454
+ res = chdir_to_parent(copy, &last, &currdir_fd);
458
+ res = check_is_mount(last, mnt);
462
+ res = fuse_mnt_umount(progname, mnt, last, lazy);
466
+ if (currdir_fd != -1) {
467
+ fchdir(currdir_fd);
474
+static int unmount_fuse(const char *mnt, int quiet, int lazy)
477
+ int mtablock = lock_umount();
479
+ res = unmount_fuse_locked(mnt, quiet, lazy);
480
+ unlock_umount(mtablock);
485
static int count_fuse_fs(void)
488
static int unmount_fuse(const char *mnt, int quiet, int lazy)
490
- return fuse_mnt_umount(progname, mnt, lazy);
491
+ return fuse_mnt_umount(progname, mnt, mnt, lazy);
493
#endif /* IGNORE_MTAB */