]> git.baikalelectronics.ru Git - kernel.git/commitdiff
btrfs: allow idmapped INO_LOOKUP_USER ioctl
authorChristian Brauner <christian.brauner@ubuntu.com>
Tue, 27 Jul 2021 10:48:57 +0000 (12:48 +0200)
committerDavid Sterba <dsterba@suse.com>
Mon, 23 Aug 2021 11:19:15 +0000 (13:19 +0200)
The INO_LOOKUP_USER is an unprivileged version of the INO_LOOKUP ioctl
and has the following restrictions. The main difference between the two
is that INO_LOOKUP is filesystem wide operation wheres INO_LOOKUP_USER
is scoped beneath the file descriptor passed with the ioctl.
Specifically, INO_LOOKUP_USER must adhere to the following restrictions:

- The caller must be privileged over each inode of each path component
  for the path they are trying to lookup.

- The path for the subvolume the caller is trying to lookup must be reachable
  from the inode associated with the file descriptor passed with the ioctl.

The second condition makes it possible to scope the lookup of the path
to the mount identified by the file descriptor passed with the ioctl.
This allows us to enable this ioctl on idmapped mounts.

Specifically, this is possible because all child subvolumes of a parent
subvolume are reachable when the parent subvolume is mounted. So if the
user had access to open the parent subvolume or has been given the fd
then they can lookup the path if they had access to it provided they
were privileged over each path component.

Note, the INO_LOOKUP_USER ioctl allows a user to learn the path and name
of a subvolume even though they would otherwise be restricted from doing
so via regular VFS-based lookup.

So think about a parent subvolume with multiple child subvolumes.
Someone could mount he parent subvolume and restrict access to the child
subvolumes by overmounting them with empty directories. At this point
the user can't traverse the child subvolumes and they can't open files
in the child subvolumes.  However, they can still learn the path of
child subvolumes as long as they have access to the parent subvolume by
using the INO_LOOKUP_USER ioctl.

The underlying assumption here is that it's ok that the lookup ioctls
can't really take mounts into account other than the original mount the
fd belongs to during lookup. Since this assumption is baked into the
original INO_LOOKUP_USER ioctl we can extend it to idmapped mounts.

Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/ioctl.c

index 9d54149bad6ecbd77aeb7bafb70075500970435a..41524f9aeac324e25970e1c53cce8316ff898cdc 100644 (file)
@@ -2439,7 +2439,8 @@ out:
        return ret;
 }
 
-static int btrfs_search_path_in_tree_user(struct inode *inode,
+static int btrfs_search_path_in_tree_user(struct user_namespace *mnt_userns,
+                               struct inode *inode,
                                struct btrfs_ioctl_ino_lookup_user_args *args)
 {
        struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info;
@@ -2530,7 +2531,7 @@ static int btrfs_search_path_in_tree_user(struct inode *inode,
                                ret = PTR_ERR(temp_inode);
                                goto out_put;
                        }
-                       ret = inode_permission(&init_user_ns, temp_inode,
+                       ret = inode_permission(mnt_userns, temp_inode,
                                               MAY_READ | MAY_EXEC);
                        iput(temp_inode);
                        if (ret) {
@@ -2672,7 +2673,7 @@ static int btrfs_ioctl_ino_lookup_user(struct file *file, void __user *argp)
                return -EACCES;
        }
 
-       ret = btrfs_search_path_in_tree_user(inode, args);
+       ret = btrfs_search_path_in_tree_user(file_mnt_user_ns(file), inode, args);
 
        if (ret == 0 && copy_to_user(argp, args, sizeof(*args)))
                ret = -EFAULT;