From 8cffdb9707f6d2b19a8cf639f1ec159bb5f55695 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Fri, 9 Nov 2001 14:49:18 +0000 Subject: preparing for release --- AUTHORS | 1 + ChangeLog | 3 + Makefile.am | 2 +- NEWS | 3 + README | 101 ++++++++++++++++++++++++++++++++ configure.in | 4 +- example/Makefile.am | 2 +- example/fusexmp.c | 44 +++++++------- include/fuse.h | 32 ++-------- include/linux/fuse.h | 22 ++++++- kernel/dev.c | 5 +- kernel/dir.c | 9 +++ kernel/fuse_i.h | 7 ++- kernel/inode.c | 5 +- lib/Makefile.am | 1 - lib/fuse.c | 18 +----- lib/fuse_i.h | 4 -- lib/mount.c | 145 ---------------------------------------------- patch/.cvsignore | 2 + patch/Makefile.am | 3 + patch/ms_permission.patch | 62 ++++++++++++++++++++ util/Makefile.am | 4 ++ util/fusermount.c | 141 +++++++++++++++++++++++++++++++++++++++----- 23 files changed, 380 insertions(+), 240 deletions(-) delete mode 100644 lib/mount.c create mode 100644 patch/.cvsignore create mode 100644 patch/Makefile.am create mode 100644 patch/ms_permission.patch diff --git a/AUTHORS b/AUTHORS index e69de29..f74e6de 100644 --- a/AUTHORS +++ b/AUTHORS @@ -0,0 +1 @@ +Miklos Szeredi diff --git a/ChangeLog b/ChangeLog index e69de29..48cc699 100644 --- a/ChangeLog +++ b/ChangeLog @@ -0,0 +1,3 @@ +2001-11-09 Miklos Szeredi + + * Started ChangeLog diff --git a/Makefile.am b/Makefile.am index 027dd8a..21ec71a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 e69de29..a0258c7 100644 --- a/NEWS +++ b/NEWS @@ -0,0 +1,3 @@ +What is new in 0.9 + +* Everything diff --git a/README b/README index e69de29..4c1e64b 100644 --- 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?). + diff --git a/configure.in b/configure.in index 247d1cf..6a0b1bd 100644 --- a/configure.in +++ b/configure.in @@ -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]) diff --git a/example/Makefile.am b/example/Makefile.am index 4d89206..71748f5 100644 --- a/example/Makefile.am +++ b/example/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in -bin_PROGRAMS = fusexmp +noinst_PROGRAMS = fusexmp fusexmp_SOURCES = fusexmp.c diff --git a/example/fusexmp.c b/example/fusexmp.c index 4c831dc..7c17cf0 100644 --- a/example/fusexmp.c +++ b/example/fusexmp.c @@ -18,7 +18,7 @@ #include #include -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; } diff --git a/include/fuse.h b/include/fuse.h index 8407f26..3ff3be6 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -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 */ diff --git a/include/linux/fuse.h b/include/linux/fuse.h index 4f64336..035d0be 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h @@ -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; diff --git a/kernel/dev.c b/kernel/dev.c index 1ff6342..d183616 100644 --- a/kernel/dev.c +++ b/kernel/dev.c @@ -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; diff --git a/kernel/dir.c b/kernel/dir.c index 87f7241..9a3d4ee 100644 --- a/kernel/dir.c +++ b/kernel/dir.c @@ -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; } diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h index 721af55..32106af 100644 --- a/kernel/fuse_i.h +++ b/kernel/fuse_i.h @@ -13,7 +13,6 @@ #include #include #include -#include #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; diff --git a/kernel/inode.c b/kernel/inode.c index d4d573a..3a78040 100644 --- a/kernel/inode.c +++ b/kernel/inode.c @@ -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; diff --git a/lib/Makefile.am b/lib/Makefile.am index 98bfedc..f970b11 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -4,5 +4,4 @@ lib_LIBRARIES = libfuse.a libfuse_a_SOURCES = \ fuse.c \ - mount.c \ fuse_i.h diff --git a/lib/fuse.c b/lib/fuse.c index 50ba52e..e645a71 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -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; diff --git a/lib/fuse_i.h b/lib/fuse_i.h index 263ebe2..4d3e042 100644 --- a/lib/fuse_i.h +++ b/lib/fuse_i.h @@ -10,8 +10,6 @@ #include #include -#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 index d191a7d..0000000 --- a/lib/mount.c +++ /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 - -#include -#include -#include -#include -#include -#include -#include - -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 index 0000000..3dda729 --- /dev/null +++ b/patch/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/patch/Makefile.am b/patch/Makefile.am new file mode 100644 index 0000000..84434a5 --- /dev/null +++ b/patch/Makefile.am @@ -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 index 0000000..6aef171 --- /dev/null +++ b/patch/ms_permission.patch @@ -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 diff --git a/util/Makefile.am b/util/Makefile.am index e40103e..af47297 100644 --- a/util/Makefile.am +++ b/util/Makefile.am @@ -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 diff --git a/util/fusermount.c b/util/fusermount.c index df38307..72fa673 100644 --- a/util/fusermount.c +++ b/util/fusermount.c @@ -16,8 +16,14 @@ #include #include #include +#include #include -#include + +#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)); -- cgit v1.2.3