** Summary changed:

- [linux-azure] overlayfs regression - internal getxattr operations without 
sepolicy checking
+ overlayfs regression - internal getxattr operations without sepolicy checking

-- 
You received this bug notification because you are a member of Kernel
Packages, which is subscribed to linux-aws in Ubuntu.
https://bugs.launchpad.net/bugs/1864669

Title:
  overlayfs regression - internal getxattr operations without sepolicy
  checking

Status in linux-aws package in Ubuntu:
  New
Status in linux-azure package in Ubuntu:
  Fix Released
Status in linux-azure-4.15 package in Ubuntu:
  Invalid
Status in linux-aws source package in Xenial:
  Invalid
Status in linux-azure source package in Xenial:
  Fix Released
Status in linux-azure-4.15 source package in Xenial:
  Invalid
Status in linux-aws source package in Bionic:
  In Progress
Status in linux-azure source package in Bionic:
  Fix Committed
Status in linux-azure-4.15 source package in Bionic:
  Fix Released
Status in linux-aws source package in Eoan:
  In Progress
Status in linux-azure source package in Eoan:
  Fix Released
Status in linux-azure-4.15 source package in Eoan:
  Invalid
Status in linux-aws source package in Focal:
  In Progress
Status in linux-azure source package in Focal:
  Fix Released
Status in linux-azure-4.15 source package in Focal:
  Invalid

Bug description:
  Bug description and repro:

  Run the following commands on host instances:

  Prepare the overlayfs directories:
  $ cd /tmp
  $ mkdir -p base/dir1/dir2 upper olwork merged
  $ touch base/dir1/dir2/file
  $ chown -R 100000:100000 base upper olwork merged

  Verify that the directory is owned by user 100000:
  $ ls -al merged/ 
  total 8
  drwxr-xr-x  2 100000 100000 4096 Nov  1 07:08 .
  drwxrwxrwt 16 root   root   4096 Nov  1 07:08 ..

  We use lxc-usernsexec to start a new shell as user 100000.
  $ lxc-usernsexec -m b:0:100000:1 -- /bin/bash
  $$ ls -al merged/
  total 8
  drwxr-xr-x  2 root   root    4096 Nov  1 07:08 .
  drwxrwxrwt 16 nobody nogroup 4096 Nov  1 07:08 ..

  Notice that the ownership of . and .. has changed because the new shell is 
running as the remapped user.
  Now, mount the overlayfs as an unprivileged user in the new shell. This is 
the key to trigger the bug.
  $$ mount -t overlay -o lowerdir=base,upperdir=upper,workdir=olwork none merged
  $$ ls -al merged/dir1/dir2/file 
  -rw-r--r-- 1 root root 0 Nov  1 07:09 merged/dir1/dir2/file

  We can see the file in the base layer from the mount directory. Now trigger 
the bug:
  $$ rm -rf merged/dir1/dir2/
  $$ mkdir merged/dir1/dir2
  $$ ls -al merged/dir1/dir2
  total 12
  drwxr-xr-x 2 root root 4096 Nov  1 07:10 .
  drwxr-xr-x 1 root root 4096 Nov  1 07:10 ..

  File does not show up in the newly created dir2 as expected. But it will 
reappear after we remount the filesystem (or any other means that might evict 
the cached dentry, such as attempt to delete the parent directory):
  $$ umount merged
  $$ mount -t overlay -o lowerdir=base,upperdir=upper,workdir=olwork none merged
  $$ ls -al merged/dir1/dir2
  total 12
  drwxr-xr-x 1 root root 4096 Nov  1 07:10 .
  drwxr-xr-x 1 root root 4096 Nov  1 07:10 ..
  -rw-r--r-- 1 root root    0 Nov  1 07:09 file
  $$ exit
  $

  This is a recent kernel regression. I tried the above step on an old
  kernel (4.4.0-1072-aws) but cannot reproduce.


  I looked up linux source code and figured out where the "regression" is 
coming from. The issue lies in how overlayfs checks the "opaque" flag from the 
underlying upper-level filesystem. It checks the "trusted.overlay.opaque" 
extended attribute to decide whether to hide the directory content from the 
lower level. The logic are different in 4.4 and 4.15 kernel.
  In 4.4: https://elixir.bootlin.com/linux/v4.4/source/fs/overlayfs/super.c#L255
  static bool ovl_is_opaquedir(struct dentry *dentry)
  {
        int res;
        char val;
        struct inode *inode = dentry->d_inode;

        if (!S_ISDIR(inode->i_mode) || !inode->i_op->getxattr)
                return false;

        res = inode->i_op->getxattr(dentry, OVL_XATTR_OPAQUE, &val, 1);
        if (res == 1 && val == 'y')
                return true;

        return false;
  }

  In 4.15: 
https://elixir.bootlin.com/linux/v4.15/source/fs/overlayfs/util.c#L349
  static bool ovl_is_opaquedir(struct dentry *dentry)
  {
        return ovl_check_dir_xattr(dentry, OVL_XATTR_OPAQUE);
  }

  bool ovl_check_dir_xattr(struct dentry *dentry, const char *name)
  {
        int res;
        char val;

        if (!d_is_dir(dentry))
                return false;

        res = vfs_getxattr(dentry, name, &val, 1);
        if (res == 1 && val == 'y')
                return true;

        return false;
  }

  The 4.4 version simply uses the internal i_node callback 
inode->i_op->getxattr from the host filesystem, which doesn't perform any 
permission check. While the 4.15 version calls the VFS interface vfs_getxattr 
that performs bunch of permission checks before the calling the internal 
insecure callback __vfs_getxattr:
  See https://elixir.bootlin.com/linux/v4.15/source/fs/xattr.c#L317
  ssize_t
  vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t 
size)
  {
        struct inode *inode = dentry->d_inode;
        int error;

        error = xattr_permission(inode, name, MAY_READ);
        if (error)
                return error;

        error = security_inode_getxattr(dentry, name);
        if (error)
                return error;

        if (!strncmp(name, XATTR_SECURITY_PREFIX,
                                XATTR_SECURITY_PREFIX_LEN)) {
                const char *suffix = name + XATTR_SECURITY_PREFIX_LEN;
                int ret = xattr_getsecurity(inode, suffix, value, size);
                /*
                 * Only overwrite the return value if a security module
                 * is actually active.
                 */
                if (ret == -EOPNOTSUPP)
                        goto nolsm;
                return ret;
        }
  nolsm:
        return __vfs_getxattr(dentry, inode, name, value, size);
  }

  In 4.15, ovl_is_opaquedir is called by the following caller:
  ovl_is_opaquedir <-
  ovl_lookup_single() <-
  ovl_lookup_layer <-
  ovl_lookup,
  ovl_lookup is the entry point for directory listing in overlayfs. 
Importantly, it assumes the filesystem mounter's credential to perform all 
internal lookup operations:
  struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
                          unsigned int flags)
  {
     old_cred = ovl_override_creds(dentry->d_sb);
     // perform lookups
     // ....
     revert_creds(old_cred);   
  }

  The "credential switching" logic also does not exist in the 4.4 kernel: 
https://elixir.bootlin.com/linux/v4.4/source/fs/overlayfs/super.c#L397
  That means, on 4.15, overlayfs uses the file system mounter's credential to 
fetch the "trusted.overlay.opaque" xattr from the underlying filesystem. This 
can fail the permission check if the overlayfs is mounted by a remapped user, 
who doesn't have CAP_SYS_ADMIN capability
  See https://elixir.bootlin.com/linux/v4.15/source/fs/xattr.c#L115:
  static int xattr_permission(struct inode *inode, const char *name, int mask)
  {
   ....
        /*
         * The trusted.* namespace can only be accessed by privileged users.
         */
        if (!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN)) {
                if (!capable(CAP_SYS_ADMIN))
                        return (mask & MAY_WRITE) ? -EPERM : -ENODATA;
                return 0;
        }
  ....
  }

  When this call fails, overlayfs assumes the upper directory is not
  "opaque" and combines the content from the lower directory in the
  result.

  
  There's a proposed patch to fix this issue: 
https://lkml.org/lkml/2019/7/30/787
  The patch calls the insecure __vfs_getxattr to fetch the opaque flag so that 
it can bypass the permission check even if the other lookup operation is done 
under the mounter's credential.
  However, the patch hasn't been merged to the upstream linux kernel as of 
today (see 
https://elixir.bootlin.com/linux/v5.4-rc5/source/fs/overlayfs/util.c#L551).

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/linux-aws/+bug/1864669/+subscriptions

-- 
Mailing list: https://launchpad.net/~kernel-packages
Post to     : kernel-packages@lists.launchpad.net
Unsubscribe : https://launchpad.net/~kernel-packages
More help   : https://help.launchpad.net/ListHelp

Reply via email to