~barry/ubuntu/natty/fuse/bug-697792

« back to all changes in this revision

Viewing changes to debian/patches/200-fix_mount_symlink_handling

  • Committer: Bazaar Package Importer
  • Author(s): Kees Cook
  • Date: 2010-01-25 17:10:52 UTC
  • Revision ID: james.westby@ubuntu.com-20100125171052-xmytp2c34gdoe7ij
Tags: 2.8.1-1.1ubuntu2
* SECURITY UPDATE: local attacker can trick fuse into unmounting a
  filesystem from the wrong location.
  - debian/patches/200-fix_mount_symlink_handling: upstream
    fixes.
  - CVE-2009-3297

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#! /bin/sh /usr/share/dpatch/dpatch-run
 
2
## 200-fix_mount_symlink_handling.dpatch by Kees Cook <kees@ubuntu.com>
 
3
##
 
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.
 
8
 
 
9
@DPATCH@
 
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
 
13
@@ -290,7 +290,7 @@
 
14
        }
 
15
 
 
16
        if (geteuid() == 0) {
 
17
-               fuse_mnt_umount("fuse", mountpoint, 1);
 
18
+               fuse_mnt_umount("fuse", mountpoint, mountpoint,  1);
 
19
                return;
 
20
        }
 
21
 
 
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
 
25
@@ -119,18 +119,19 @@
 
26
        return res;
 
27
 }
 
28
 
 
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)
 
32
 {
 
33
        int res;
 
34
        int status;
 
35
        sigset_t blockmask;
 
36
        sigset_t oldmask;
 
37
 
 
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);
 
42
                if (res == -1)
 
43
                        fprintf(stderr, "%s: failed to unmount %s: %s\n",
 
44
-                               progname, mnt, strerror(errno));
 
45
+                               progname, abs_mnt, strerror(errno));
 
46
                return res;
 
47
        }
 
48
 
 
49
@@ -150,7 +151,7 @@
 
50
        if (res == 0) {
 
51
                sigprocmask(SIG_SETMASK, &oldmask, NULL);
 
52
                setuid(geteuid());
 
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
 
61
@@ -10,7 +10,8 @@
 
62
 
 
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
 
74
@@ -26,6 +26,7 @@
 
75
 #include <sys/fsuid.h>
 
76
 #include <sys/socket.h>
 
77
 #include <sys/utsname.h>
 
78
+#include <sched.h>
 
79
 
 
80
 #define FUSE_COMMFD_ENV                "_FUSE_COMMFD"
 
81
 
 
82
@@ -37,6 +38,12 @@
 
83
 #ifndef MS_DIRSYNC
 
84
 #define MS_DIRSYNC 128
 
85
 #endif
 
86
+#ifndef MS_REC
 
87
+#define MS_REC 16384
 
88
+#endif
 
89
+#ifndef MS_SLAVE
 
90
+#define MS_SLAVE (1<<19)
 
91
+#endif
 
92
 
 
93
 static const char *progname;
 
94
 
 
95
@@ -74,77 +81,336 @@
 
96
 }
 
97
 
 
98
 #ifndef IGNORE_MTAB
 
99
+/*
 
100
+ * Make sure that /etc/mtab is checked and updated atomically
 
101
+ */
 
102
+static int lock_umount(void)
 
103
+{
 
104
+       const char *mtab_lock = _PATH_MOUNTED ".fuselock";
 
105
+       int mtablock;
 
106
+       int res;
 
107
+       struct stat mtab_stat;
 
108
+
 
109
+       /* /etc/mtab could be a symlink to /proc/mounts */
 
110
+       if (lstat(_PATH_MOUNTED, &mtab_stat) == 0 && S_ISLNK(mtab_stat.st_mode))
 
111
+               return -1;
 
112
+
 
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));
 
117
+               return -1;
 
118
+       }
 
119
+       res = lockf(mtablock, F_LOCK, 0);
 
120
+       if (res < 0) {
 
121
+               fprintf(stderr, "%s: error getting lock: %s\n", progname,
 
122
+                       strerror(errno));
 
123
+               close(mtablock);
 
124
+               return -1;
 
125
+       }
 
126
+
 
127
+       return mtablock;
 
128
+}
 
129
+
 
130
+static void unlock_umount(int mtablock)
 
131
+{
 
132
+       lockf(mtablock, F_ULOCK, 0);
 
133
+       close(mtablock);
 
134
+}
 
135
+
 
136
 static int add_mount(const char *source, const char *mnt, const char *type,
 
137
                     const char *opts)
 
138
 {
 
139
        return fuse_mnt_add_mount(progname, source, mnt, type, opts);
 
140
 }
 
141
 
 
142
-static int unmount_fuse(const char *mnt, int quiet, int lazy)
 
143
+static int may_unmount(const char *mnt, int quiet)
 
144
 {
 
145
-       if (getuid() != 0) {
 
146
-               struct mntent *entp;
 
147
-               FILE *fp;
 
148
-               const char *user = NULL;
 
149
-               char uidstr[32];
 
150
-               unsigned uidlen = 0;
 
151
-               int found;
 
152
-               const char *mtab = _PATH_MOUNTED;
 
153
+       struct mntent *entp;
 
154
+       FILE *fp;
 
155
+       const char *user = NULL;
 
156
+       char uidstr[32];
 
157
+       unsigned uidlen = 0;
 
158
+       int found;
 
159
+       const char *mtab = _PATH_MOUNTED;
 
160
 
 
161
-               user = get_user_name();
 
162
-               if (user == NULL)
 
163
-                       return -1;
 
164
+       user = get_user_name();
 
165
+       if (user == NULL)
 
166
+               return -1;
 
167
 
 
168
-               fp = setmntent(mtab, "r");
 
169
-               if (fp == NULL) {
 
170
-                       fprintf(stderr,
 
171
-                               "%s: failed to open %s: %s\n", progname, mtab,
 
172
-                               strerror(errno));
 
173
-                       return -1;
 
174
-               }
 
175
+       fp = setmntent(mtab, "r");
 
176
+       if (fp == NULL) {
 
177
+               fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
 
178
+                       strerror(errno));
 
179
+               return -1;
 
180
+       }
 
181
 
 
182
-               uidlen = sprintf(uidstr, "%u", getuid());
 
183
+       uidlen = sprintf(uidstr, "%u", getuid());
 
184
 
 
185
-               found = 0;
 
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=");
 
193
-                               if (p &&
 
194
-                                   (p == entp->mnt_opts || *(p-1) == ',') &&
 
195
-                                   strcmp(p + 5, user) == 0) {
 
196
-                                       found = 1;
 
197
-                                       break;
 
198
-                               }
 
199
-                               /* /etc/mtab is a link pointing to
 
200
-                                  /proc/mounts: */
 
201
-                               else if ((p =
 
202
-                                         strstr(entp->mnt_opts, "user_id=")) &&
 
203
-                                        (p == entp->mnt_opts ||
 
204
-                                         *(p-1) == ',') &&
 
205
-                                        strncmp(p + 8, uidstr, uidlen) == 0 &&
 
206
-                                        (*(p+8+uidlen) == ',' ||
 
207
-                                         *(p+8+uidlen) == '\0')) {
 
208
-                                       found = 1;
 
209
-                                       break;
 
210
-                               }
 
211
+       found = 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=");
 
219
+                       if (p &&
 
220
+                           (p == entp->mnt_opts || *(p-1) == ',') &&
 
221
+                           strcmp(p + 5, user) == 0) {
 
222
+                               found = 1;
 
223
+                               break;
 
224
+                       }
 
225
+                       /* /etc/mtab is a link pointing to
 
226
+                          /proc/mounts: */
 
227
+                       else if ((p =
 
228
+                                 strstr(entp->mnt_opts, "user_id=")) &&
 
229
+                                (p == entp->mnt_opts ||
 
230
+                                 *(p-1) == ',') &&
 
231
+                                strncmp(p + 8, uidstr, uidlen) == 0 &&
 
232
+                                (*(p+8+uidlen) == ',' ||
 
233
+                                 *(p+8+uidlen) == '\0')) {
 
234
+                               found = 1;
 
235
+                               break;
 
236
                        }
 
237
                }
 
238
-               endmntent(fp);
 
239
+       }
 
240
+       endmntent(fp);
 
241
 
 
242
-               if (!found) {
 
243
-                       if (!quiet)
 
244
-                               fprintf(stderr,
 
245
-                                       "%s: entry for %s not found in %s\n",
 
246
-                                       progname, mnt, mtab);
 
247
-                       return -1;
 
248
+       if (!found) {
 
249
+               if (!quiet)
 
250
+                       fprintf(stderr,
 
251
+                               "%s: entry for %s not found in %s\n",
 
252
+                               progname, mnt, mtab);
 
253
+               return -1;
 
254
+       }
 
255
+
 
256
+       return 0;
 
257
+}
 
258
+
 
259
+/*
 
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).
 
265
+ *
 
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.
 
269
+ *
 
270
+ * First make sure nothing is propagated back into the parent
 
271
+ * namespace by marking all mounts "slave".
 
272
+ *
 
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.
 
276
+ *
 
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.
 
280
+ */
 
281
+static int check_is_mount_child(void *p)
 
282
+{
 
283
+       const char **a = p;
 
284
+       const char *last = a[0];
 
285
+       const char *mnt = a[1];
 
286
+       int res;
 
287
+       const char *procmounts = "/proc/mounts";
 
288
+       int found;
 
289
+       FILE *fp;
 
290
+       struct mntent *entp;
 
291
+
 
292
+       res = mount("", "/", "", MS_SLAVE | MS_REC, NULL);
 
293
+       if (res == -1) {
 
294
+               fprintf(stderr, "%s: failed to mark mounts slave: %s\n",
 
295
+                       progname, strerror(errno));
 
296
+               return 1;
 
297
+       }
 
298
+
 
299
+       res = mount(".", "/tmp", "", MS_BIND | MS_REC, NULL);
 
300
+       if (res == -1) {
 
301
+               fprintf(stderr, "%s: failed to bind parent to /tmp: %s\n",
 
302
+                       progname, strerror(errno));
 
303
+               return 1;
 
304
+       }
 
305
+
 
306
+       fp = setmntent(procmounts, "r");
 
307
+       if (fp == NULL) {
 
308
+               fprintf(stderr, "%s: failed to open %s: %s\n", progname,
 
309
+                       procmounts, strerror(errno));
 
310
+               return 1;
 
311
+       }
 
312
+
 
313
+       found = 0;
 
314
+       while ((entp = getmntent(fp)) != NULL) {
 
315
+               if (strncmp(entp->mnt_dir, "/tmp/", 5) == 0 &&
 
316
+                   strcmp(entp->mnt_dir + 5, last) == 0) {
 
317
+                       found = 1;
 
318
+                       break;
 
319
                }
 
320
        }
 
321
+       endmntent(fp);
 
322
 
 
323
-       return fuse_mnt_umount(progname, mnt, lazy);
 
324
+       if (!found) {
 
325
+               fprintf(stderr, "%s: %s not mounted\n", progname, mnt);
 
326
+               return 1;
 
327
+       }
 
328
+
 
329
+       return 0;
 
330
+}
 
331
+
 
332
+static pid_t clone_newns(void *a)
 
333
+{
 
334
+       long long buf[16384];
 
335
+       size_t stacksize = sizeof(buf) / 2;
 
336
+       char *stack = ((char *) buf) + stacksize;
 
337
+
 
338
+#ifdef __ia64__
 
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);
 
343
+
 
344
+       return __clone2(check_is_mount_child, stack, stacksize, CLONE_NEWNS, a,
 
345
+                       NULL, NULL, NULL);
 
346
+#else
 
347
+       return clone(check_is_mount_child, stack, CLONE_NEWNS, a);
 
348
+#endif
 
349
+}
 
350
+
 
351
+static int check_is_mount(const char *last, const char *mnt)
 
352
+{
 
353
+       pid_t pid, p;
 
354
+       int status;
 
355
+       const char *a[2] = { last, mnt };
 
356
+
 
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));
 
361
+               return -1;
 
362
+       }
 
363
+       p = waitpid(pid, &status, __WCLONE);
 
364
+       if (p == (pid_t) -1) {
 
365
+               fprintf(stderr, "%s: waitpid failed: %s\n",
 
366
+                       progname, strerror(errno));
 
367
+               return -1;
 
368
+       }
 
369
+       if (!WIFEXITED(status)) {
 
370
+               fprintf(stderr, "%s: child terminated abnormally (status %i)\n",
 
371
+                       progname, status);
 
372
+               return -1;
 
373
+       }
 
374
+       if (WEXITSTATUS(status) != 0)
 
375
+               return -1;
 
376
+
 
377
+       return 0;
 
378
+}
 
379
+
 
380
+static int chdir_to_parent(char *copy, const char **lastp, int *currdir_fd)
 
381
+{
 
382
+       char *tmp;
 
383
+       const char *parent;
 
384
+       char buf[65536];
 
385
+       int res;
 
386
+
 
387
+       tmp = strrchr(copy, '/');
 
388
+       if (tmp == NULL || tmp[1] == '\0') {
 
389
+               fprintf(stderr, "%s: internal error: invalid abs path: <%s>\n",
 
390
+                       progname, copy);
 
391
+               return -1;
 
392
+       }
 
393
+       if (tmp != copy) {
 
394
+               *tmp = '\0';
 
395
+               parent = copy;
 
396
+               *lastp = tmp + 1;
 
397
+       } else if (tmp[1] != '\0') {
 
398
+               *lastp = tmp + 1;
 
399
+               parent = "/";
 
400
+       } else {
 
401
+               *lastp = ".";
 
402
+               parent = "/";
 
403
+       }
 
404
+
 
405
+       *currdir_fd = open(".", O_RDONLY);
 
406
+       if (*currdir_fd == -1) {
 
407
+               fprintf(stderr,
 
408
+                       "%s: failed to open current directory: %s\n",
 
409
+                       progname, strerror(errno));
 
410
+               return -1;
 
411
+       }
 
412
+
 
413
+       res = chdir(parent);
 
414
+       if (res == -1) {
 
415
+               fprintf(stderr, "%s: failed to chdir to %s: %s\n",
 
416
+                       progname, parent, strerror(errno));
 
417
+               return -1;
 
418
+       }
 
419
+
 
420
+       if (getcwd(buf, sizeof(buf)) == NULL) {
 
421
+               fprintf(stderr, "%s: failed to obtain current directory: %s\n",
 
422
+                       progname, strerror(errno));
 
423
+               return -1;
 
424
+       }
 
425
+       if (strcmp(buf, parent) != 0) {
 
426
+               fprintf(stderr, "%s: mountpoint moved (%s -> %s)\n", progname,
 
427
+                       parent, buf);
 
428
+               return -1;
 
429
+
 
430
+       }
 
431
+
 
432
+       return 0;
 
433
+}
 
434
+
 
435
+static int unmount_fuse_locked(const char *mnt, int quiet, int lazy)
 
436
+{
 
437
+       int currdir_fd = -1;
 
438
+       char *copy;
 
439
+       const char *last;
 
440
+       int res;
 
441
+
 
442
+       if (getuid() != 0) {
 
443
+               res = may_unmount(mnt, quiet);
 
444
+               if (res == -1)
 
445
+                       return -1;
 
446
+       }
 
447
+
 
448
+       copy = strdup(mnt);
 
449
+       if (copy == NULL) {
 
450
+               fprintf(stderr, "%s: failed to allocate memory\n", progname);
 
451
+               return -1;
 
452
+       }
 
453
+
 
454
+       res = chdir_to_parent(copy, &last, &currdir_fd);
 
455
+       if (res == -1)
 
456
+               goto out;
 
457
+
 
458
+       res = check_is_mount(last, mnt);
 
459
+       if (res == -1)
 
460
+               goto out;
 
461
+
 
462
+       res = fuse_mnt_umount(progname, mnt, last, lazy);
 
463
+
 
464
+out:
 
465
+       free(copy);
 
466
+       if (currdir_fd != -1) {
 
467
+               fchdir(currdir_fd);
 
468
+               close(currdir_fd);
 
469
+       }
 
470
+
 
471
+       return res;
 
472
+}
 
473
+
 
474
+static int unmount_fuse(const char *mnt, int quiet, int lazy)
 
475
+{
 
476
+       int res;
 
477
+       int mtablock = lock_umount();
 
478
+
 
479
+       res = unmount_fuse_locked(mnt, quiet, lazy);
 
480
+       unlock_umount(mtablock);
 
481
+
 
482
+       return res;
 
483
 }
 
484
 
 
485
 static int count_fuse_fs(void)
 
486
@@ -186,7 +452,7 @@
 
487
 
 
488
 static int unmount_fuse(const char *mnt, int quiet, int lazy)
 
489
 {
 
490
-       return fuse_mnt_umount(progname, mnt, lazy);
 
491
+       return fuse_mnt_umount(progname, mnt, mnt, lazy);
 
492
 }
 
493
 #endif /* IGNORE_MTAB */
 
494