preparing for release
authorMiklos Szeredi <miklos@szeredi.hu>
Fri, 9 Nov 2001 14:49:18 +0000 (14:49 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Fri, 9 Nov 2001 14:49:18 +0000 (14:49 +0000)
23 files changed:
AUTHORS
ChangeLog
Makefile.am
NEWS
README
configure.in
example/Makefile.am
example/fusexmp.c
include/fuse.h
include/linux/fuse.h
kernel/dev.c
kernel/dir.c
kernel/fuse_i.h
kernel/inode.c
lib/Makefile.am
lib/fuse.c
lib/fuse_i.h
lib/mount.c [deleted file]
patch/.cvsignore [new file with mode: 0644]
patch/Makefile.am [new file with mode: 0644]
patch/ms_permission.patch [new file with mode: 0644]
util/Makefile.am
util/fusermount.c

diff --git a/AUTHORS b/AUTHORS
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f74e6de1050bcf7b40c758c21e5c23eb21eed815 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -0,0 +1 @@
+Miklos Szeredi     <mszeredi@inf.bme.hu>
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..48cc69994b61e06737cbfd8ff85546e3010783cf 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -0,0 +1,3 @@
+2001-11-09  Miklos Szeredi <mszeredi@inf.bme.hu>
+
+       * Started ChangeLog
index 027dd8a41efd65588242fe29b0bb2119ac000a3d..21ec71aadf8c09e0c571df5df800d58bbc083a26 100644 (file)
@@ -1,3 +1,3 @@
 ## Process this file with automake to produce Makefile.in
 
-SUBDIRS = kernel lib util example include
+SUBDIRS = kernel lib util example include patch
diff --git a/NEWS b/NEWS
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a0258c7a524b0eca4a78e5882ea71f4573e25baa 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -0,0 +1,3 @@
+What is new in 0.9
+
+* Everything
diff --git a/README b/README
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4c1e64b16a000059728a5772c7aad141cfe33248 100644 (file)
--- a/README
+++ b/README
@@ -0,0 +1,101 @@
+General Information
+===================
+
+FUSE (Filesystem in USErspace) is a simple interface for userspace
+programs to export a virtual filesystem to the linux kernel.  FUSE
+also aims to provide a secure method for non privileged users to
+create and mount their own filesystem implementations.
+
+You can download the source code releases from
+
+  http://sourceforge.net/projects/avf
+
+or alternatively you can use CVS to get the very latest development
+version: set the cvsroot to
+
+  :pserver:anonymous@cvs.avf.sourceforge.net:/cvsroot/avf
+
+and check out the 'fuse' module.
+
+Installation
+============
+
+See the file 'INSTALL'
+
+IMPORTANT NOTE: If you run a system with untrusted users, installing
+this program is not recommended, as it could be used to breach
+security (see the 'Security' section for explanation).
+
+How To Use
+==========
+
+FUSE is made up of three main parts:
+
+ - A kernel filesystem module (kernel/fuse.o)
+
+ - A userspace library (lib/libfuse.a)
+
+ - A mount/unmount program (util/fusermount)
+
+
+Here's how to create your very own virtual filesystem in five easy
+steps:
+
+  1) Edit the file example/fusexmp.c to do whatever you want...
+
+  2) Build the fusexmp program
+
+  3) run 'util/fusermount /mnt/whatever example/fusexmp -d'
+
+  4) ls -al /mnt/whatever
+
+  5) Be glad!
+
+If it doesn't work out, you can ask the me. (Oh yeah, and you need to
+do 'insmod kernel/fuse.o' before running your program, in case you
+forgot).
+
+See the file 'include/fuse.h' for documentation of the library interface.
+
+
+Security
+========
+
+If you run 'make install', the fusermount program is installed
+set-user-id to root.  This is done to allow normal users to mount
+their own filesystem implementations. 
+
+There must however be some limitations to forbid the Bad User to do
+Naughty Things with your Beautiful system.  Currently those
+limitations are:
+
+  - The user can only mount on a mountpoint, for which it has write
+    permission
+
+  - The mountpoint is not a sticky directory which isn't owned by the
+    user (like /tmp usually is)
+
+  - If the user doing the mount is not root, then no other user
+    (including root) can access the contents of the mounted
+    filesystem.
+
+When linux will have private namespaces (as soon as version 2.5 comes
+out) then this third condition is useless and can be gotten rid of.
+
+Currently the first two conditions are checked by the fusermount
+program before doing the mount.  This has the nice feature, that it's
+totally useless. Here's why:
+
+   - user creates /tmp/mydir
+   - user starts fusermount
+   - user removes /tmp/mydir just after fusermount checked that it is OK
+   - user creates symlink: ln -s / /tmp/mydir
+   - fusermount actually mounts user's filesystem on '/'
+   - this is bad :(
+
+So to make this secure, the checks must be done by the kernel.  And so
+there is a patch (patch/ms_permission.patch) which does exactly this.
+This is against 2.4.14, but applies to some earlier kernels (not too
+much earlier though), and possibly some later (I couldn't know, could
+I?).
+
index 247d1cf23f4ebd8ec4d1c44a5ba6feb6d0a27531..6a0b1bdfa3ff58e0463d6989f968c62a05ff4752 100644 (file)
@@ -1,5 +1,5 @@
 AC_INIT(lib/fuse.c)
-AM_INIT_AUTOMAKE(fuse, 1.0)
+AM_INIT_AUTOMAKE(fuse, 0.9)
 AM_CONFIG_HEADER(include/config.h)
 
 AC_PROG_CC
@@ -49,6 +49,6 @@ AC_SUBST(KERNINCLUDE)
 kmoduledir=/lib/modules/$kernsrcver
 AC_SUBST(kmoduledir)
 
-AC_OUTPUT([Makefile kernel/Makefile lib/Makefile util/Makefile example/Makefile include/Makefile include/linux/Makefile])
+AC_OUTPUT([Makefile kernel/Makefile lib/Makefile util/Makefile example/Makefile include/Makefile include/linux/Makefile patch/Makefile])
 
 
index 4d89206de9abd1a8ab6caf340f606c8cb1dccf44..71748f5d120530d153e0cb9a57959e395bb42ae5 100644 (file)
@@ -1,6 +1,6 @@
 ## Process this file with automake to produce Makefile.in
 
-bin_PROGRAMS = fusexmp
+noinst_PROGRAMS = fusexmp
 
 fusexmp_SOURCES = fusexmp.c
 
index 4c831dccee63f3fa89e5e0c7b2617da44075473e..7c17cf0883b6b3fe9a6dc41945ac79f666f84a68 100644 (file)
@@ -18,7 +18,7 @@
 #include <grp.h>
 #include <sys/fsuid.h>
 
-static struct fuse *xmp_fuse;
+static char *mount_point;
 
 static int set_creds(struct fuse_cred *cred)
 {
@@ -359,12 +359,6 @@ static void set_signal_handlers()
     }
 }
 
-static void cleanup()
-{
-    fuse_unmount(xmp_fuse);
-    fuse_destroy(xmp_fuse);
-}
-
 static struct fuse_operations xmp_oper = {
     getattr:   xmp_getattr,
     readlink:  xmp_readlink,
@@ -385,16 +379,23 @@ static struct fuse_operations xmp_oper = {
     write:     xmp_write,
 };
 
+static void cleanup()
+{
+    char *buf = (char *) malloc(strlen(mount_point) + 128);
+    sprintf(buf, "fusermount -u %s", mount_point);
+    system(buf);
+    free(buf);
+}
+
 int main(int argc, char *argv[])
 {
-    int res;
     int argctr;
-    char *mnt;
     int flags;
+    struct fuse *fuse;
 
     if(argc < 2) {
         fprintf(stderr,
-                "usage: %s [options] mount_dir\n"
+                "usage: %s mount_dir [options] \n"
                 "Options:\n"
                 "    -d      enable debug output\n"
                 "    -s      disable multithreaded operation\n",
@@ -402,8 +403,14 @@ int main(int argc, char *argv[])
         exit(1);
     }
 
+    argctr = 1;
+    mount_point = argv[argctr++];
+
+    set_signal_handlers();
+    atexit(cleanup);
+
     flags = FUSE_MULTITHREAD;
-    for(argctr = 1; argctr < argc && argv[argctr][0] == '-'; argctr ++) {
+    for(; argctr < argc && argv[argctr][0] == '-'; argctr ++) {
         switch(argv[argctr][1]) {
         case 'd':
             flags |= FUSE_DEBUG;
@@ -418,23 +425,16 @@ int main(int argc, char *argv[])
             exit(1);
         }
     }
-    if(argctr != argc - 1) {
+    if(argctr != argc) {
         fprintf(stderr, "missing or surplus argument\n");
         exit(1);
     }
-    mnt = argv[argctr];
 
-    set_signal_handlers();
-    atexit(cleanup);
     setgroups(0, NULL);
 
-    xmp_fuse = fuse_new(flags, 0);
-    res = fuse_mount(xmp_fuse, mnt);
-    if(res == -1)
-        exit(1);
-        
-    fuse_set_operations(xmp_fuse, &xmp_oper);
-    fuse_loop(xmp_fuse);
+    fuse = fuse_new(0, flags);
+    fuse_set_operations(fuse, &xmp_oper);
+    fuse_loop(fuse);
 
     return 0;
 }
index 8407f26deaa512d3594a177d9e3f1159fb3a2ca6..3ff3be62fab80144cc3a9ac3d8c6bafeccd3cbeb 100644 (file)
@@ -92,29 +92,19 @@ struct fuse_operations {
 #define FUSE_DEBUG       (1 << 1)
 
 /**
- * Create a new FUSE filesystem. The filesystem is not yet mounted
+ * Create a new FUSE filesystem.
  *
+ * @param fd the control file descriptor
  * @param flags any combination of the FUSE flags defined above, or 0
- * @param root the file type of the root node. 0 is the default (directory).
  * @return the created FUSE handle
  */
-struct fuse *fuse_new(int flags, mode_t root);
-
-/**
- * Connect to the kernel and mount the filesystem.
- * 
- * @param f the FUSE handle
- * @param mnt the mount point
- * @return 0 on success -1 on failure
- */
-int fuse_mount(struct fuse *f, const char *mnt);
+struct fuse *fuse_new(int fd, int flags);
 
 /**
  * Set the filesystem operations. 
  * 
  * Operations which are initialised to NULL will return ENOSYS to the
- * calling process.  This function can be called anytime after
- * fuse_new() and before fuse_loop().
+ * calling process.
  * 
  * @param f the FUSE handle
  * @param op the operations
@@ -132,19 +122,9 @@ void fuse_set_operations(struct fuse *f, const struct fuse_operations *op);
 void fuse_loop(struct fuse *f);
 
 /**
- * Disconnect from the kernel and unmount the filesystem
- *
- * @param f the FUSE handle
- */
-int fuse_unmount(struct fuse *f);
-
-/**
- * Destroy the filesystem. 
+ * Destroy the FUSE handle. 
  *
- * The filesystem is not unmounted (call fuse_unmount() for that).
- * After a fork() system call it is possible to call fuse_destroy() in
- * one process, and leave the other process to service the filesystem
- * requests.
+ * The filesystem is not unmounted.
  *
  * @param f the FUSE handle
  */
index 4f6433602af42bd2265b8a39c8ef1b0825fef7ff..035d0be53286e8d030b9114d732c76408bbe8a1f 100644 (file)
@@ -8,16 +8,32 @@
 
 /* This file defines the kernel interface of FUSE */
 
-
+/** Version number of this interface */
 #define FUSE_KERNEL_VERSION 1
 
+/** The inode number of the root indode */
+#define FUSE_ROOT_INO 1
+
+/** Opening this will yield a new control file */
+#define FUSE_DEV "/proc/fs/fuse/dev"
+
+/** Data passed to mount */
 struct fuse_mount_data {
+       /** Must be set to FUSE_KERNEL_VERSION */
        int version;
+       
+       /** The control file descriptor */
        int fd;
+       
+       /** The file type of the root inode */
        unsigned int rootmode;
-};
 
-#define FUSE_ROOT_INO 1
+       /** The user ID of the user initiating this mount */
+       unsigned int uid;
+       
+       /** FUSE specific mount flags */
+       unsigned int flags;
+};
 
 struct fuse_attr {
        unsigned int        mode;
index 1ff634252e9e30a17919ee4d84f0c368a80991bd..d183616745ca05d1e27e9bf44dbc2b345ed7ba47 100644 (file)
@@ -325,6 +325,8 @@ static struct fuse_conn *new_conn(void)
        if(fc != NULL) {
                fc->sb = NULL;
                fc->file = NULL;
+               fc->flags = 0;
+               fc->uid = 0;
                init_waitqueue_head(&fc->waitq);
                INIT_LIST_HEAD(&fc->pending);
                INIT_LIST_HEAD(&fc->processing);
@@ -397,8 +399,7 @@ int fuse_dev_init()
        }
 
        proc_fs_fuse->owner = THIS_MODULE;
-       proc_fuse_dev = create_proc_entry("dev", S_IFSOCK | S_IRUGO | S_IWUGO,
-                                         proc_fs_fuse);
+       proc_fuse_dev = create_proc_entry("dev", S_IFSOCK | 0600, proc_fs_fuse);
        if(!proc_fuse_dev) {
                printk("fuse: failed to create entry in /proc/fs/fuse\n");
                goto err;
index 87f72417738ac12bf5408aaa224e6f7e9b9a3c7b..9a3d4eeb7e8c9cd528e323280c61bde3fa17b1b1 100644 (file)
@@ -303,6 +303,15 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
 
 static int fuse_permission(struct inode *inode, int mask)
 {
+       struct fuse_conn *fc = INO_FC(inode);
+
+       /* (too) simple protection for non-privileged mounts */
+       if(fc->uid) {
+               if(current->fsuid == fc->uid)
+                       return 0;
+               else
+                       return -EACCES;
+       }
        return 0;
 }
 
index 721af5557d25384a704ceb37f4c11a74140b8057..32106afefe992f20b24bdf0495b7a17cf91c8248 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/fs.h>
 #include <linux/list.h>
 #include <linux/spinlock.h>
-#include <linux/rwsem.h>
 
 #define MAX_CLEARED 256
 
@@ -31,6 +30,12 @@ struct fuse_conn {
        /** The opened client device */
        struct file *file;
 
+       /** The user id for this mount */
+       uid_t uid;
+
+       /** The fuse mount flags for this mount */
+       unsigned int flags;
+
        /** The client wait queue */
        wait_queue_head_t waitq;
 
index d4d573aba06c7bb6184f3669b92edf7054c82b0c..3a780403f2fdeb359150a55b1c88ebe29824cac6 100644 (file)
@@ -41,9 +41,10 @@ static void fuse_put_super(struct super_block *sb)
 
        spin_lock(&fuse_lock);
        fc->sb = NULL;
+       fc->uid = 0;
+       fc->flags = 0;
        fuse_release_conn(fc);
        spin_unlock(&fuse_lock);
-
 }
 
 static struct super_operations fuse_super_operations = {
@@ -130,6 +131,8 @@ static struct super_block *fuse_read_super(struct super_block *sb,
                goto err;
 
        fc->sb = sb;
+       fc->flags = d->flags;
+       fc->uid = d->uid;
        spin_unlock(&fuse_lock);
        
        return sb;
index 98bfedc164edb73d1e2a099cf56b4f17d892f51e..f970b112fa4b85b597ef25c56124132f9419e8ea 100644 (file)
@@ -4,5 +4,4 @@ lib_LIBRARIES = libfuse.a
 
 libfuse_a_SOURCES =    \
        fuse.c          \
-       mount.c         \
        fuse_i.h
index 50ba52edae3e433eb1072c55179e61c3af28da57..e645a71ca358d8a258746625548b639654aa8c78 100644 (file)
@@ -932,26 +932,15 @@ void fuse_loop(struct fuse *f)
     }
 }
 
-struct fuse *fuse_new(int flags, mode_t rootmode)
+struct fuse *fuse_new(int fd, int flags)
 {
     struct fuse *f;
     struct node *root;
 
     f = (struct fuse *) calloc(1, sizeof(struct fuse));
 
-    if(!rootmode)
-        rootmode = S_IFDIR;
-
-    if(!S_ISDIR(rootmode) && !S_ISREG(rootmode)) {
-        fprintf(stderr, "Invalid mode for root: 0%o\n", rootmode);
-        rootmode = S_IFDIR;
-    }
-    rootmode &= S_IFMT;
-
     f->flags = flags;
-    f->rootmode = rootmode;
-    f->fd = -1;
-    f->mnt = NULL;
+    f->fd = fd;
     f->ctr = 0;
     f->name_table_size = 14057;
     f->name_table = (struct node **)
@@ -962,7 +951,7 @@ struct fuse *fuse_new(int flags, mode_t rootmode)
     pthread_mutex_init(&f->lock, NULL);
 
     root = (struct node *) calloc(1, sizeof(struct node));
-    root->mode = rootmode;
+    root->mode = 0;
     root->rdev = 0;
     root->name = strdup("/");
     root->parent = 0;
@@ -979,7 +968,6 @@ void fuse_set_operations(struct fuse *f, const struct fuse_operations *op)
 void fuse_destroy(struct fuse *f)
 {
     size_t i;
-    close(f->fd);
     for(i = 0; i < f->ino_table_size; i++) {
         struct node *node;
         struct node *next;
index 263ebe25383f011796c0049054715a20d8e0b1b9..4d3e0422096307059892ebdef0eef510f64ddab0 100644 (file)
@@ -10,8 +10,6 @@
 #include <stdio.h>
 #include <pthread.h>
 
-#define FUSE_DEV "/proc/fs/fuse/dev"
-
 typedef unsigned long fino_t;
 
 struct node {
@@ -27,8 +25,6 @@ struct node {
 
 struct fuse {
     int flags;
-    char *mnt;
-    mode_t rootmode;
     int fd;
     struct fuse_operations op;
     struct node **name_table;
diff --git a/lib/mount.c b/lib/mount.c
deleted file mode 100644 (file)
index d191a7d..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
-    FUSE: Filesystem in Userspace
-    Copyright (C) 2001  Miklos Szeredi (mszeredi@inf.bme.hu)
-
-    This program can be distributed under the terms of the GNU GPL.
-    See the file COPYING.
-*/
-
-#include "fuse_i.h"
-#include <linux/fuse.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <fcntl.h>
-#include <sys/mount.h>
-#include <mntent.h>
-
-static int do_mount(const char *dev, const char *dir, const char *type,
-                    mode_t rootmode, int fd)
-{
-    int res;
-    struct fuse_mount_data data;
-    
-    data.version = FUSE_KERNEL_VERSION;
-    data.fd = fd;
-    data.rootmode = rootmode;
-
-    res = mount(dev, dir, type, MS_MGC_VAL | MS_NOSUID | MS_NODEV, &data);
-    if(res == -1) {
-        perror("mount failed");
-       return -1;
-    }
-    
-    return 0;
-}
-
-static void add_mntent(const char *dev, const char *dir, const char *type)
-{
-    int res;
-    FILE *fp;
-    struct mntent ent;
-    
-    fp = setmntent("/etc/mtab", "a");
-    if(fp == NULL) {
-        perror("setmntent");
-       return;
-    }
-    
-    ent.mnt_fsname = (char *) dev;
-    ent.mnt_dir = (char *) dir;
-    ent.mnt_type = (char *) type;
-    ent.mnt_opts = "rw,nosuid,nodev";
-    ent.mnt_freq = 0;
-    ent.mnt_passno = 0;
-    res = addmntent(fp, & ent);
-    if(res != 0)
-       perror("addmntent");
-    
-    endmntent(fp);
-
-}
-
-static void remove_mntent(const char *dir)
-{
-    int res;
-    FILE *fdold, *fdnew;
-    struct mntent *entp;
-        
-    fdold = setmntent("/etc/mtab", "r");
-    if(fdold == NULL) {
-        perror("/etc/mtab");
-       return;
-    }
-
-    fdnew = setmntent("/etc/mtab~", "w");
-    if(fdnew == NULL) {
-        perror("/etc/mtab~");
-       return;
-    }
-
-    do {
-       entp = getmntent(fdold);
-       if(entp != NULL && strcmp(entp->mnt_dir, dir) != 0) {
-            res = addmntent(fdnew, entp);
-            if(res != 0)
-                perror("addmntent");
-        }
-    } while(entp != NULL);
-
-    endmntent(fdold);
-    endmntent(fdnew);
-
-    res = rename("/etc/mtab~", "/etc/mtab");
-    if(res == -1)
-        perror("renameing /etc/mtab~ to /etc/mtab");
-}
-
-int fuse_mount(struct fuse *f, const char *dir)
-{
-    int res;
-    const char *dev = FUSE_DEV;
-    const char *type = "fuse";
-
-    if(f->mnt != NULL)
-        return 0;
-
-    f->fd = open(dev, O_RDWR);
-    if(f->fd == -1) {
-        perror(dev);
-        return -1;
-    }
-    
-    res = do_mount(dev, dir, type, f->rootmode, f->fd);
-    if(res == -1)
-        return -1;
-
-    add_mntent(dev, dir, type);
-    f->mnt = strdup(dir);
-    
-    return 0;
-}
-
-int fuse_unmount(struct fuse *f)
-{
-    int res;
-
-    if(f->mnt == NULL)
-        return 0;
-
-    close(f->fd);
-    f->fd = -1;
-
-    res = umount(f->mnt);
-    if(res == -1)
-        perror("umount failed");
-    else
-        remove_mntent(f->mnt);
-
-    free(f->mnt);
-    f->mnt = NULL;
-
-    return res;
-}
diff --git a/patch/.cvsignore b/patch/.cvsignore
new file mode 100644 (file)
index 0000000..3dda729
--- /dev/null
@@ -0,0 +1,2 @@
+Makefile.in
+Makefile
diff --git a/patch/Makefile.am b/patch/Makefile.am
new file mode 100644 (file)
index 0000000..84434a5
--- /dev/null
@@ -0,0 +1,3 @@
+## Process this file with automake to produce Makefile.in
+
+EXTRA_DIST = *.patch
diff --git a/patch/ms_permission.patch b/patch/ms_permission.patch
new file mode 100644 (file)
index 0000000..6aef171
--- /dev/null
@@ -0,0 +1,62 @@
+--- /store/linux/linux-2.4.14.tar.gz#/linux/fs/namespace.c     Fri Nov  9 08:58:10 2001
++++ linux/fs/namespace.c       Fri Nov  9 09:31:04 2001
+@@ -478,20 +478,15 @@
+ static int mount_is_safe(struct nameidata *nd)
+ {
+-      if (capable(CAP_SYS_ADMIN))
+-              return 0;
+-      return -EPERM;
+-#ifdef notyet
+       if (S_ISLNK(nd->dentry->d_inode->i_mode))
+               return -EPERM;
+       if (nd->dentry->d_inode->i_mode & S_ISVTX) {
+-              if (current->uid != nd->dentry->d_inode->i_uid)
++              if (current->fsuid != nd->dentry->d_inode->i_uid)
+                       return -EPERM;
+       }
+       if (permission(nd->dentry->d_inode, MAY_WRITE))
+               return -EPERM;
+       return 0;
+-#endif
+ }
+ static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry)
+@@ -570,9 +565,10 @@
+ {
+       struct nameidata old_nd;
+       struct vfsmount *mnt = NULL;
+-      int err = mount_is_safe(nd);
+-      if (err)
+-              return err;
++      int err = 0;
++
++      if (!capable(CAP_SYS_ADMIN))
++              return -EPERM;
+       if (!old_name || !*old_name)
+               return -EINVAL;
+       if (path_init(old_name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &old_nd))
+@@ -741,6 +737,13 @@
+               retval = path_walk(dir_name, &nd);
+       if (retval)
+               return retval;
++
++      if (flags & MS_PERMISSION) {
++              retval = mount_is_safe(&nd);
++              if(retval)
++                      return retval;
++      }
++      flags &= ~MS_PERMISSION;
+       if (flags & MS_REMOUNT)
+               retval = do_remount(&nd, flags & ~MS_REMOUNT, mnt_flags,
+--- /store/linux/linux-2.4.14.tar.gz#/linux/include/linux/fs.h Fri Nov  9 08:58:51 2001
++++ linux/include/linux/fs.h   Fri Nov  9 09:24:21 2001
+@@ -105,6 +105,7 @@
+ #define MS_SYNCHRONOUS        16      /* Writes are synced at once */
+ #define MS_REMOUNT    32      /* Alter flags of a mounted FS */
+ #define MS_MANDLOCK   64      /* Allow mandatory locks on an FS */
++#define MS_PERMISSION 128     /* Check write permission on mount target */
+ #define MS_NOATIME    1024    /* Do not update access times. */
+ #define MS_NODIRATIME 2048    /* Do not update directory access times */
+ #define MS_BIND               4096
index e40103ecf2064e549d87012d811b4f153f974170..af47297185c9f380061478f61cfe8d9e763433b1 100644 (file)
@@ -3,3 +3,7 @@
 bin_PROGRAMS = fusermount
 
 fusermount_SOURCES = fusermount.c
+
+install-exec-hook:
+       chown root $(DESTDIR)$(bindir)/fusermount
+       chmod u+s $(DESTDIR)$(bindir)/fusermount
index df3830716912522af597187fd40e015e51f9d518..72fa673ff4267845c0d9d5eac3044f5893bea125 100644 (file)
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <sys/mount.h>
+#include <sys/fsuid.h>
 #include <linux/fuse.h>
-#include <sys/capability.h>
+
+#define CHECK_PERMISSION 1
+
+#ifndef MS_PERMISSION
+#define MS_PERMISSION  128
+#endif
 
 #define FUSE_DEV "/proc/fs/fuse/dev"
 
@@ -149,8 +155,16 @@ static int remove_mount(const char *mnt)
         }
         *p = '\0';
         p++;
-        if(strcmp(user, buf) == 0 && strcmp(mnt, p) == 0)
+        if(!found && strcmp(user, buf) == 0 && strcmp(mnt, p) == 0) {
+            int res = umount(mnt);
+            if(res == -1) {
+                found = -1;
+                fprintf(stderr, "%s: umount of %s failed: %s\n", progname,
+                        mnt, strerror(errno));
+                break;
+            }
             found = 1;
+        }
         else
             fprintf(newfp, "%s %s\n", buf, p);
     }
@@ -158,7 +172,7 @@ static int remove_mount(const char *mnt)
     fclose(fp);
     fclose(newfp);
 
-    if(found) {
+    if(found == 1) {
         int res;
         res = rename(fusermnt_temp, fusermnt);
         if(res == -1) {
@@ -169,8 +183,9 @@ static int remove_mount(const char *mnt)
         }
     }
     else {
-        fprintf(stderr, "%s: entry for %s not found in %s\n", progname, mnt,
-                fusermnt);
+        if(!found)
+            fprintf(stderr, "%s: entry for %s not found in %s\n", progname,
+                    mnt, fusermnt);
         unlink(fusermnt_temp);
         fusermnt_unlock(lockfd);
         return -1;
@@ -180,24 +195,105 @@ static int remove_mount(const char *mnt)
     return 0;
 }
 
+#define _LINUX_CAPABILITY_VERSION  0x19980330
+
+typedef struct __user_cap_header_struct {
+    unsigned int version;
+    int pid;
+} *cap_user_header_t;
+typedef struct __user_cap_data_struct {
+    unsigned int effective;
+    unsigned int permitted;
+    unsigned int inheritable;
+} *cap_user_data_t;
+  
+int capget(cap_user_header_t header, cap_user_data_t data);
+int capset(cap_user_header_t header, cap_user_data_t data);
+
+#define CAP_SYS_ADMIN        21
+
+static uid_t oldfsuid;
+static gid_t oldfsgid;
+static struct __user_cap_data_struct oldcaps;
+
+static int drop_privs()
+{
+    int res;
+    struct __user_cap_header_struct head;
+    struct __user_cap_data_struct newcaps;
+
+    head.version = _LINUX_CAPABILITY_VERSION;
+    head.pid = 0;
+    res = capget(&head, &oldcaps);
+    if(res == -1) {
+        fprintf(stderr, "%s: failed to get capabilities: %s\n", progname,
+                strerror(errno));
+        return -1;
+    }
+
+    oldfsuid = setfsuid(getuid());
+    oldfsgid = setfsgid(getgid());
+    newcaps = oldcaps;
+    /* Keep CAP_SYS_ADMIN for mount */
+    newcaps.effective &= (1 << CAP_SYS_ADMIN);
+
+    head.version = _LINUX_CAPABILITY_VERSION;
+    head.pid = 0;
+    res = capset(&head, &newcaps);
+    if(res == -1) {
+        fprintf(stderr, "%s: failed to set capabilities: %s\n", progname,
+                strerror(errno));
+        return -1;
+    }
+    return 0;
+}
+
+static void restore_privs()
+{
+    struct __user_cap_header_struct head;
+    int res;
+
+    head.version = _LINUX_CAPABILITY_VERSION;
+    head.pid = 0;
+    res = capset(&head, &oldcaps);
+    if(res == -1)
+        fprintf(stderr, "%s: failed to restore capabilities: %s\n", progname,
+                strerror(errno));
+    
+    setfsuid(oldfsuid);
+    setfsgid(oldfsgid);
+}
 
 static int do_mount(const char *dev, const char *mnt, const char *type,
                     mode_t rootmode, int fd)
 {
     int res;
     struct fuse_mount_data data;
+    int flags = MS_NOSUID | MS_NODEV;
+
+    if(getuid() != 0) {
+        res = drop_privs();
+        if(res == -1)
+            return -1;
+
+        flags |= MS_PERMISSION;
+    }
     
     data.version = FUSE_KERNEL_VERSION;
     data.fd = fd;
     data.rootmode = rootmode;
+    data.uid = getuid();
+    data.flags = 0;
 
-    res = mount(dev, mnt, type, MS_MGC_VAL | MS_NOSUID | MS_NODEV, &data);
-    if(res == -1) {
+    res = mount(dev, mnt, type, flags, &data);
+    if(res == -1)
         fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno));
-       return -1;
-    }
+
+    if(getuid() != 0)
+        restore_privs();
     
-    return 0;
+    return res;
 }
 
 static int check_perm(const char *mnt, struct stat *stbuf)
@@ -217,21 +313,24 @@ static int check_perm(const char *mnt, struct stat *stbuf)
         return -1;
     }
 
+/* Should be done by the kernel */
+#ifdef CHECK_PERMISSION
     if(getuid() != 0) {
-        if(stbuf->st_uid != getuid()) {
+        if((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
             fprintf(stderr, "%s: mountpoint %s not owned by user\n",
                     progname, mnt);
             return -1;
         }
 
-        res = access(mnt, R_OK | W_OK | (S_ISDIR(stbuf->st_mode) ? X_OK : 0));
+        res = access(mnt, W_OK);
         if(res == -1) {
-            fprintf(stderr, "%s: user has no full access to mountpoint %s\n",
+            fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
                     progname, mnt);
             return -1;
         }
     }
-    
+#endif    
+
     return 0;
 }
 
@@ -295,9 +394,11 @@ int main(int argc, char *argv[])
     int a;
     int fd;
     int res;
-    const char *mnt = NULL;
+    char *mnt = NULL;
     int umount = 0;
     char **userprog;
+    int numargs;
+    char **newargv;
 
     progname = argv[0];
     
@@ -341,6 +442,7 @@ int main(int argc, char *argv[])
     }
     
     userprog = argv + a;
+    numargs = argc - a;
     
     fd = mount_fuse(mnt);
     if(fd == -1)
@@ -356,7 +458,14 @@ int main(int argc, char *argv[])
     setuid(getuid());
     setgid(getgid());
     
-    execv(userprog[0], userprog);
+    newargv = (char **) malloc(sizeof(char *) * (numargs + 2));
+    newargv[0] = userprog[0];
+    newargv[1] = mnt;
+    for(a = 1; a < numargs; a++)
+        newargv[a+1] = userprog[a];
+    newargv[numargs+1] = NULL;
+
+    execv(userprog[0], newargv);
     fprintf(stderr, "%s: failed to exec %s: %s\n", progname, userprog[0],
             strerror(errno));