|
@@ -411,17 +411,45 @@ int fuse_do_getattr(struct inode *inode)
|
|
|
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)
|
|
|
{
|
|
|
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);
|
|
@@ -431,7 +459,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) {
|
|
|
int err = generic_permission(inode, mask, NULL);
|