From 9c1b68d4f206274dc8a0d19d7964abd97c2e10fe Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 28 Apr 2005 09:55:09 +0000 Subject: [PATCH] fix --- ChangeLog | 9 +++++++++ kernel/dir.c | 49 +++++++++++++++++++++++++++++++++++++++++------ kernel/fuse_i.h | 3 +++ kernel/inode.c | 12 ++++++++++++ util/fusermount.c | 8 +++++--- 5 files changed, 72 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1800d7d..d2ce216 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2005-04-28 Miklos Szeredi + + * Check for hard-linked directories in lookup. This could cause + problems in the VFS, which assumes that such objects never exist. + + * Make checking of permission for other users more strict. Now + the same privilege is required for the mount owner as for ptrace + on the process performing the filesystem operation. + 2005-04-23 Miklos Szeredi * Released 2.3-pre5 diff --git a/kernel/dir.c b/kernel/dir.c index 947d538..4f7a83f 100644 --- a/kernel/dir.c +++ b/kernel/dir.c @@ -433,17 +433,45 @@ int fuse_do_getattr(struct inode *inode) return err; } +static int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task) +{ + if (fc->flags & FUSE_ALLOW_OTHER) + return 1; + + /* 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. */ + 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) { struct inode *inode = entry->d_inode; struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_conn *fc = get_fuse_conn(inode); - if (get_node_id(inode) == FUSE_ROOT_ID) { - if (!(fc->flags & FUSE_ALLOW_OTHER) && - current->fsuid != fc->user_id) - return -EACCES; - } else if (time_before_eq(jiffies, fi->i_time)) + if (!fuse_allow_task(fc, current)) + return -EACCES; + if (get_node_id(inode) != FUSE_ROOT_ID && + time_before_eq(jiffies, fi->i_time)) return 0; return fuse_do_getattr(inode); @@ -453,7 +481,7 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) { 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; else if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { #ifdef KERNEL_2_6_10_PLUS @@ -772,6 +800,15 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, int err = fuse_lookup_iget(dir, entry, &inode); if (err) return ERR_PTR(err); + if (inode && S_ISDIR(inode->i_mode)) { + /* Don't allow creating an alias to a directory */ + struct dentry *alias = d_find_alias(inode); + if (alias && !(alias->d_flags & DCACHE_DISCONNECTED)) { + dput(alias); + iput(inode); + return ERR_PTR(-EIO); + } + } return d_splice_alias(inode, entry); } #else /* KERNEL_2_6 */ diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h index 164eaa7..25724b4 100644 --- a/kernel/fuse_i.h +++ b/kernel/fuse_i.h @@ -262,6 +262,9 @@ struct fuse_conn { /** The user id for this mount */ uid_t user_id; + /** The group id for this mount */ + gid_t group_id; + /** The fuse mount flags for this mount */ unsigned flags; diff --git a/kernel/inode.c b/kernel/inode.c index 9b894f5..6ef9491 100644 --- a/kernel/inode.c +++ b/kernel/inode.c @@ -43,6 +43,7 @@ struct fuse_mount_data { int fd; unsigned rootmode; unsigned user_id; + unsigned group_id; unsigned flags; unsigned max_read; }; @@ -262,6 +263,7 @@ static void fuse_put_super(struct super_block *sb) spin_lock(&fuse_lock); fc->mounted = 0; fc->user_id = 0; + fc->group_id = 0; fc->flags = 0; /* Flush all readers on this fs */ wake_up_all(&fc->waitq); @@ -311,6 +313,7 @@ enum { OPT_FD, OPT_ROOTMODE, OPT_USER_ID, + OPT_GROUP_ID, OPT_DEFAULT_PERMISSIONS, OPT_ALLOW_OTHER, OPT_KERNEL_CACHE, @@ -326,6 +329,7 @@ static match_table_t tokens = { {OPT_FD, "fd=%u"}, {OPT_ROOTMODE, "rootmode=%o"}, {OPT_USER_ID, "user_id=%u"}, + {OPT_GROUP_ID, "group_id=%u"}, {OPT_DEFAULT_PERMISSIONS, "default_permissions"}, {OPT_ALLOW_OTHER, "allow_other"}, {OPT_KERNEL_CACHE, "kernel_cache"}, @@ -371,6 +375,12 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d) d->user_id = value; break; + case OPT_GROUP_ID: + if (match_int(&args[0], &value)) + return 0; + d->group_id = value; + break; + case OPT_DEFAULT_PERMISSIONS: d->flags |= FUSE_DEFAULT_PERMISSIONS; break; @@ -413,6 +423,7 @@ static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt) struct fuse_conn *fc = get_fuse_conn_super(mnt->mnt_sb); seq_printf(m, ",user_id=%u", fc->user_id); + seq_printf(m, ",group_id=%u", fc->group_id); if (fc->flags & FUSE_DEFAULT_PERMISSIONS) seq_puts(m, ",default_permissions"); if (fc->flags & FUSE_ALLOW_OTHER) @@ -619,6 +630,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) fc->flags = d.flags; fc->user_id = d.user_id; + fc->group_id = d.group_id; fc->max_read = d.max_read; #ifdef KERNEL_2_6 if (fc->max_read / PAGE_CACHE_SIZE < fc->bdi.ra_pages) diff --git a/util/fusermount.c b/util/fusermount.c index 1b79446..f55d92b 100644 --- a/util/fusermount.c +++ b/util/fusermount.c @@ -491,7 +491,7 @@ static int do_mount(const char *mnt, const char *type, mode_t rootmode, char *d; char *fsname = NULL; - optbuf = malloc(strlen(opts) + 64); + optbuf = malloc(strlen(opts) + 128); if (!optbuf) { fprintf(stderr, "%s: failed to allocate memory\n", progname); return -1; @@ -515,7 +515,8 @@ static int do_mount(const char *mnt, const char *type, mode_t rootmode, fsname[len - fsname_str_len] = '\0'; } else if (!begins_with(s, "fd=") && !begins_with(s, "rootmode=") && - !begins_with(s, "user_id=")) { + !begins_with(s, "user_id=") && + !begins_with(s, "group_id=")) { int on; int flag; int skip_option = 0; @@ -561,7 +562,8 @@ static int do_mount(const char *mnt, const char *type, mode_t rootmode, free(optbuf); return -1; } - sprintf(d, "fd=%i,rootmode=%o,user_id=%i", fd, rootmode, getuid()); + sprintf(d, "fd=%i,rootmode=%o,user_id=%i,group_id=%i", + fd, rootmode, getuid(), getgid()); if (fsname == NULL) { fsname = strdup(dev); if (!fsname) { -- 2.30.2