Commit 87729a55 authored by Miklos Szeredi's avatar Miklos Szeredi Committed by Linus Torvalds

[PATCH] FUSE: tighten check for processes allowed access

This patch tightens the check for allowing processes to access non-privileged
mounts.  The rational is that the filesystem implementation can control the
behavior or get otherwise unavailable information of the filesystem user.  If
the filesystem user process has the same uid, gid, and is not suid or sgid
application, then access is safe.  Otherwise access is not allowed unless the
"allow_other" mount option is given (for which policy is controlled by the
userspace mount utility).

Thanks to everyone linux-fsdevel, especially Martin Mares who helped uncover
problems with the previous approach.
Signed-off-by: default avatarMiklos Szeredi <miklos@szeredi.hu>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent db50b96c
...@@ -411,17 +411,45 @@ int fuse_do_getattr(struct inode *inode) ...@@ -411,17 +411,45 @@ int fuse_do_getattr(struct inode *inode)
return err; return err;
} }
/*
* Calling into a user-controlled filesystem gives the filesystem
* daemon ptrace-like capabilities over the requester process. This
* means, that the filesystem daemon is able to record the exact
* filesystem operations performed, and can also control the behavior
* of the requester process in otherwise impossible ways. For example
* it can delay the operation for arbitrary length of time allowing
* DoS against the requester.
*
* For this reason only those processes can call into the filesystem,
* for which the owner of the mount has ptrace privilege. This
* excludes processes started by other users, suid or sgid processes.
*/
static int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task)
{
if (fc->flags & FUSE_ALLOW_OTHER)
return 1;
if (task->euid == fc->user_id &&
task->suid == fc->user_id &&
task->uid == fc->user_id &&
task->egid == fc->group_id &&
task->sgid == fc->group_id &&
task->gid == fc->group_id)
return 1;
return 0;
}
static int fuse_revalidate(struct dentry *entry) static int fuse_revalidate(struct dentry *entry)
{ {
struct inode *inode = entry->d_inode; struct inode *inode = entry->d_inode;
struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_inode *fi = get_fuse_inode(inode);
struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_conn *fc = get_fuse_conn(inode);
if (get_node_id(inode) == FUSE_ROOT_ID) { if (!fuse_allow_task(fc, current))
if (!(fc->flags & FUSE_ALLOW_OTHER) &&
current->fsuid != fc->user_id)
return -EACCES; return -EACCES;
} else if (time_before_eq(jiffies, fi->i_time)) if (get_node_id(inode) != FUSE_ROOT_ID &&
time_before_eq(jiffies, fi->i_time))
return 0; return 0;
return fuse_do_getattr(inode); return fuse_do_getattr(inode);
...@@ -431,7 +459,7 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) ...@@ -431,7 +459,7 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd)
{ {
struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_conn *fc = get_fuse_conn(inode);
if (!(fc->flags & FUSE_ALLOW_OTHER) && current->fsuid != fc->user_id) if (!fuse_allow_task(fc, current))
return -EACCES; return -EACCES;
else if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { else if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
int err = generic_permission(inode, mask, NULL); int err = generic_permission(inode, mask, NULL);
......
...@@ -198,6 +198,9 @@ struct fuse_conn { ...@@ -198,6 +198,9 @@ struct fuse_conn {
/** The user id for this mount */ /** The user id for this mount */
uid_t user_id; uid_t user_id;
/** The group id for this mount */
gid_t group_id;
/** The fuse mount flags for this mount */ /** The fuse mount flags for this mount */
unsigned flags; unsigned flags;
......
...@@ -31,6 +31,7 @@ struct fuse_mount_data { ...@@ -31,6 +31,7 @@ struct fuse_mount_data {
int fd; int fd;
unsigned rootmode; unsigned rootmode;
unsigned user_id; unsigned user_id;
unsigned group_id;
unsigned flags; unsigned flags;
unsigned max_read; unsigned max_read;
}; };
...@@ -199,6 +200,7 @@ static void fuse_put_super(struct super_block *sb) ...@@ -199,6 +200,7 @@ static void fuse_put_super(struct super_block *sb)
spin_lock(&fuse_lock); spin_lock(&fuse_lock);
fc->mounted = 0; fc->mounted = 0;
fc->user_id = 0; fc->user_id = 0;
fc->group_id = 0;
fc->flags = 0; fc->flags = 0;
/* Flush all readers on this fs */ /* Flush all readers on this fs */
wake_up_all(&fc->waitq); wake_up_all(&fc->waitq);
...@@ -248,6 +250,7 @@ enum { ...@@ -248,6 +250,7 @@ enum {
OPT_FD, OPT_FD,
OPT_ROOTMODE, OPT_ROOTMODE,
OPT_USER_ID, OPT_USER_ID,
OPT_GROUP_ID,
OPT_DEFAULT_PERMISSIONS, OPT_DEFAULT_PERMISSIONS,
OPT_ALLOW_OTHER, OPT_ALLOW_OTHER,
OPT_KERNEL_CACHE, OPT_KERNEL_CACHE,
...@@ -259,6 +262,7 @@ static match_table_t tokens = { ...@@ -259,6 +262,7 @@ static match_table_t tokens = {
{OPT_FD, "fd=%u"}, {OPT_FD, "fd=%u"},
{OPT_ROOTMODE, "rootmode=%o"}, {OPT_ROOTMODE, "rootmode=%o"},
{OPT_USER_ID, "user_id=%u"}, {OPT_USER_ID, "user_id=%u"},
{OPT_GROUP_ID, "group_id=%u"},
{OPT_DEFAULT_PERMISSIONS, "default_permissions"}, {OPT_DEFAULT_PERMISSIONS, "default_permissions"},
{OPT_ALLOW_OTHER, "allow_other"}, {OPT_ALLOW_OTHER, "allow_other"},
{OPT_KERNEL_CACHE, "kernel_cache"}, {OPT_KERNEL_CACHE, "kernel_cache"},
...@@ -300,6 +304,12 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d) ...@@ -300,6 +304,12 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d)
d->user_id = value; d->user_id = value;
break; break;
case OPT_GROUP_ID:
if (match_int(&args[0], &value))
return 0;
d->group_id = value;
break;
case OPT_DEFAULT_PERMISSIONS: case OPT_DEFAULT_PERMISSIONS:
d->flags |= FUSE_DEFAULT_PERMISSIONS; d->flags |= FUSE_DEFAULT_PERMISSIONS;
break; break;
...@@ -333,6 +343,7 @@ static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt) ...@@ -333,6 +343,7 @@ static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt)
struct fuse_conn *fc = get_fuse_conn_super(mnt->mnt_sb); struct fuse_conn *fc = get_fuse_conn_super(mnt->mnt_sb);
seq_printf(m, ",user_id=%u", fc->user_id); seq_printf(m, ",user_id=%u", fc->user_id);
seq_printf(m, ",group_id=%u", fc->group_id);
if (fc->flags & FUSE_DEFAULT_PERMISSIONS) if (fc->flags & FUSE_DEFAULT_PERMISSIONS)
seq_puts(m, ",default_permissions"); seq_puts(m, ",default_permissions");
if (fc->flags & FUSE_ALLOW_OTHER) if (fc->flags & FUSE_ALLOW_OTHER)
...@@ -465,6 +476,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) ...@@ -465,6 +476,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
fc->flags = d.flags; fc->flags = d.flags;
fc->user_id = d.user_id; fc->user_id = d.user_id;
fc->group_id = d.group_id;
fc->max_read = d.max_read; fc->max_read = d.max_read;
if (fc->max_read / PAGE_CACHE_SIZE < fc->bdi.ra_pages) if (fc->max_read / PAGE_CACHE_SIZE < fc->bdi.ra_pages)
fc->bdi.ra_pages = fc->max_read / PAGE_CACHE_SIZE; fc->bdi.ra_pages = fc->max_read / PAGE_CACHE_SIZE;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment