aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Miklos Szeredi <miklos@szeredi.hu>2001-11-09 14:49:18 +0000
committerGravatar Miklos Szeredi <miklos@szeredi.hu>2001-11-09 14:49:18 +0000
commit8cffdb9707f6d2b19a8cf639f1ec159bb5f55695 (patch)
treed4581e099df031e1b344350a25bd9d55d15eb267
parent552c2816d7c3f5ac4a909108c13bdbddf7c2fc48 (diff)
preparing for release
-rw-r--r--AUTHORS1
-rw-r--r--ChangeLog3
-rw-r--r--Makefile.am2
-rw-r--r--NEWS3
-rw-r--r--README101
-rw-r--r--configure.in4
-rw-r--r--example/Makefile.am2
-rw-r--r--example/fusexmp.c44
-rw-r--r--include/fuse.h32
-rw-r--r--include/linux/fuse.h22
-rw-r--r--kernel/dev.c5
-rw-r--r--kernel/dir.c9
-rw-r--r--kernel/fuse_i.h7
-rw-r--r--kernel/inode.c5
-rw-r--r--lib/Makefile.am1
-rw-r--r--lib/fuse.c18
-rw-r--r--lib/fuse_i.h4
-rw-r--r--lib/mount.c145
-rw-r--r--patch/.cvsignore2
-rw-r--r--patch/Makefile.am3
-rw-r--r--patch/ms_permission.patch62
-rw-r--r--util/Makefile.am4
-rw-r--r--util/fusermount.c141
23 files changed, 380 insertions, 240 deletions
diff --git a/AUTHORS b/AUTHORS
index e69de29..f74e6de 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -0,0 +1 @@
+Miklos Szeredi <mszeredi@inf.bme.hu>
diff --git a/ChangeLog b/ChangeLog
index e69de29..48cc699 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -0,0 +1,3 @@
+2001-11-09 Miklos Szeredi <mszeredi@inf.bme.hu>
+
+ * 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 <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;
}
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 <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;
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 <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
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 <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
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 <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));