aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2005-04-28 09:55:09 +0000
committerMiklos Szeredi <miklos@szeredi.hu>2005-04-28 09:55:09 +0000
commit9c1b68d4f206274dc8a0d19d7964abd97c2e10fe (patch)
tree56a9a8f93dd51d5c8997db37ac724da296b715c9
parent3efcb5658dced829d07253b973e2395d2affbccf (diff)
downloadandroid_external_fuse-9c1b68d4f206274dc8a0d19d7964abd97c2e10fe.tar.gz
android_external_fuse-9c1b68d4f206274dc8a0d19d7964abd97c2e10fe.tar.bz2
android_external_fuse-9c1b68d4f206274dc8a0d19d7964abd97c2e10fe.zip
fix
-rw-r--r--ChangeLog9
-rw-r--r--kernel/dir.c49
-rw-r--r--kernel/fuse_i.h3
-rw-r--r--kernel/inode.c12
-rw-r--r--util/fusermount.c8
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 <miklos@szeredi.hu>
+
+ * 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 <miklos@szeredi.hu>
* 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) {