Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0e727c32 authored by Zhang Tianci's avatar Zhang Tianci Committed by Greg Kroah-Hartman
Browse files

ovl: Use ovl mounter's fsuid and fsgid in ovl_link()

commit 5b0db51215e895a361bc63132caa7cca36a53d6a upstream.

There is a wrong case of link() on overlay:
  $ mkdir /lower /fuse /merge
  $ mount -t fuse /fuse
  $ mkdir /fuse/upper /fuse/work
  $ mount -t overlay /merge -o lowerdir=/lower,upperdir=/fuse/upper,\
    workdir=work
  $ touch /merge/file
  $ chown bin.bin /merge/file // the file's caller becomes "bin"
  $ ln /merge/file /merge/lnkfile

Then we will get an error(EACCES) because fuse daemon checks the link()'s
caller is "bin", it denied this request.

In the changing history of ovl_link(), there are two key commits:

The first is commit bb0d2b8a ("ovl: fix sgid on directory") which
overrides the cred's fsuid/fsgid using the new inode. The new inode's
owner is initialized by inode_init_owner(), and inode->fsuid is
assigned to the current user. So the override fsuid becomes the
current user. We know link() is actually modifying the directory, so
the caller must have the MAY_WRITE permission on the directory. The
current caller may should have this permission. This is acceptable
to use the caller's fsuid.

The second is commit 51f7e52d ("ovl: share inode for hard link")
which removed the inode creation in ovl_link(). This commit move
inode_init_owner() into ovl_create_object(), so the ovl_link() just
give the old inode to ovl_create_or_link(). Then the override fsuid
becomes the old inode's fsuid, neither the caller nor the overlay's
mounter! So this is incorrect.

Fix this bug by using ovl mounter's fsuid/fsgid to do underlying
fs's link().

Link: https://lore.kernel.org/all/20220817102952.xnvesg3a7rbv576x@wittgenstein/T
Link: https://lore.kernel.org/lkml/20220825130552.29587-1-zhangtianci.1997@bytedance.com/t


Signed-off-by: default avatarZhang Tianci <zhangtianci.1997@bytedance.com>
Signed-off-by: default avatarJiachen Zhang <zhangjiachen.jaycee@bytedance.com>
Reviewed-by: default avatarChristian Brauner (Microsoft) <brauner@kernel.org>
Fixes: 51f7e52d ("ovl: share inode for hard link")
Cc: <stable@vger.kernel.org> # v4.8
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent cae6ddde
Loading
Loading
Loading
Loading
+30 −16
Original line number Diff line number Diff line
@@ -557,12 +557,26 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
			goto out_revert_creds;
	}

	if (!attr->hardlink) {
		err = -ENOMEM;
		override_cred = prepare_creds();
	if (override_cred) {
		if (!override_cred)
			goto out_revert_creds;
		/*
		 * In the creation cases(create, mkdir, mknod, symlink),
		 * ovl should transfer current's fs{u,g}id to underlying
		 * fs. Because underlying fs want to initialize its new
		 * inode owner using current's fs{u,g}id. And in this
		 * case, the @inode is a new inode that is initialized
		 * in inode_init_owner() to current's fs{u,g}id. So use
		 * the inode's i_{u,g}id to override the cred's fs{u,g}id.
		 *
		 * But in the other hardlink case, ovl_link() does not
		 * create a new inode, so just use the ovl mounter's
		 * fs{u,g}id.
		 */
		override_cred->fsuid = inode->i_uid;
		override_cred->fsgid = inode->i_gid;
		if (!attr->hardlink) {
		err = security_dentry_create_files_as(dentry,
				attr->mode, &dentry->d_name, old_cred,
				override_cred);
@@ -570,15 +584,15 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
			put_cred(override_cred);
			goto out_revert_creds;
		}
		}
		put_cred(override_creds(override_cred));
		put_cred(override_cred);
	}

	if (!ovl_dentry_is_whiteout(dentry))
		err = ovl_create_upper(dentry, inode, attr);
	else
		err = ovl_create_over_whiteout(dentry, inode, attr);
	}

out_revert_creds:
	revert_creds(old_cred);
	return err;