Linux Security Modules development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
Cc: linux-security-module@vger.kernel.org
Subject: Re: [PATCH v3 4/5] capabilities: limit fscaps to where CAP_SETFCAP reaches
Date: Wed, 21 Oct 2026 21:07:38 +0000	[thread overview]
Message-ID: <sashiko-outbox-180684@kernel.org> (raw)
In-Reply-To: <20261021-b4-setfcap-userns-v3-4-97c7ed4e43e1@toxicpanda.com>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] capabilities: v2 xattr bypasses setfcap_level check in cap_convert_nscap()
--

commit 06013f196f1052cfc90402268b0b1b3878c33f84
Author: Josef Bacik <josef@toxicpanda.com>

capabilities: limit fscaps to where CAP_SETFCAP reaches

This commit updates cap_convert_nscap() in security/commoncap.c to verify
that both the mount-mapped root uid (vfsrootid) and the filesystem root uid
(rootid) do not map to uid 0 in a user namespace ancestor above
task_ns->setfcap_level. This prevents a task that entered a user namespace
without CAP_SETFCAP from setting file capabilities for an ancestor's root
user when a privileged task has mapped that uid into the namespace.

> diff --git a/security/commoncap.c b/security/commoncap.c
> index b069142230395..1a419a4a872c8 100644
> --- a/security/commoncap.c
> +++ b/security/commoncap.c
> @@ -703,10 +703,24 @@ int cap_convert_nscap(const struct mnt_idmap *idmap, struct dentry *dentry,
>  	if (!vfsuid_valid(vfsrootid))
>  		return -EINVAL;
>  
> +	/*
> +	 * The root user may be the root user of ancestors of our namespace as
> +	 * well.  CAP_SETFCAP that we got for entering it doesn't cover those.
> +	 * On an idmapped mount the root user is vfsrootid as seen through
> +	 * this mount and rootid as seen through every other mount of the
> +	 * filesystem, so both have to stay within reach.
> +	 */
> +	if (cap_root_level(vfsuid_into_kuid(vfsrootid), task_ns) <
> +	    task_ns->setfcap_level)
> +		return -EPERM;
> +
>  	rootid = from_vfsuid(idmap, fs_ns, vfsrootid);
>  	if (!uid_valid(rootid))
>  		return -EINVAL;
>  
> +	if (cap_root_level(rootid, task_ns) < task_ns->setfcap_level)
> +		return -EPERM;

[Severity: High]
Can a v2 xattr bypass these new setfcap_level checks in
security/commoncap.c:cap_convert_nscap()?

Earlier in cap_convert_nscap(), v2 capabilities take an early return before
reaching rootid_from_xattr() and the new cap_root_level() checks:

	if (size == XATTR_CAPS_SZ_2 && (idmap == &nop_mnt_idmap))
		if (ns_capable(inode->i_sb->s_user_ns, CAP_SETFCAP))
			/* user is privileged, just write the v2 */
			return size;

Because a task in task_ns has CAP_SETFCAP in task_ns,
ns_capable(inode->i_sb->s_user_ns, CAP_SETFCAP) succeeds whenever fs_ns
(inode->i_sb->s_user_ns) is task_ns or a descendant of task_ns, regardless
of task_ns->setfcap_level.

In the scenario from the commit message (task B has uid 0 and CAP_SYS_ADMIN
without CAP_SETFCAP in init_user_ns, unshares ns1 with ns1->setfcap_level = 1,
and task A writes "0 0 1" to ns1's uid_map), task B can mount a filesystem
in ns1 (s_user_ns == ns1) and write a v2 security.capability xattr
(size == XATTR_CAPS_SZ_2). While a v3 xattr with rootid 0 is rejected with
-EPERM, the v2 write returns early and stores a raw v2 xattr whose root user
is make_kuid(ns1, 0), which is kuid 0.

If task B in init_user_ns then mounts overlayfs in init_user_ns
(ovl_sb->s_user_ns == &init_user_ns) with that ns1 directory as lowerdir and
executes the file in init_user_ns, get_file_caps() checks
current_in_userns(file->f_path.mnt->mnt_sb->s_user_ns) against
ovl_sb->s_user_ns (&init_user_ns) and reads the capability through:

  get_vfs_caps_from_disk()
    __vfs_getxattr()
      ovl_xattr_get()               [fs/overlayfs/xattrs.c]
        vfs_getxattr()              [under with_ovl_creds(dentry->d_sb)]
          cap_inode_getsecurity()   [security/commoncap.c]

In fs/overlayfs/xattrs.c:ovl_xattr_get() (and similarly during copy-up in
fs/overlayfs/copy_up.c:ovl_copy_xattr()):

	with_ovl_creds(dentry->d_sb)
		return vfs_getxattr(mnt_idmap(realpath.mnt), realpath.dentry, name, value, size);

And in security/commoncap.c:cap_inode_getsecurity():

	if (is_v2header(size, cap)) {
		root = 0;
	...
	kroot = make_kuid(fs_ns, root);

cap_inode_getsecurity() resolves kroot to make_kuid(ns1, 0) == kuid 0 and
returns a valid v2 capability to get_vfs_caps_from_disk(), granting the file
capabilities in init_user_ns.

Additionally, if task A maps kuid 0 as a non-zero uid in ns1 (for example,
"0 1000 1" and "1000 0 1" with ns1->setfcap_level = 1), task B in ns1 can
unshare a child namespace ns2 and map "0 1000 1" in ns2 (permitted by
verify_root_map() because lower_first is 1000 != 0 in ns1). On a filesystem
mounted in ns2 (fs_ns == ns2), a caller in ns1 or ns2 writing a v2 xattr
satisfies ns_capable(ns2, CAP_SETFCAP) and writes an unconverted v2 xattr
whose root user is make_kuid(ns2, 0) == kuid 0 rather than make_kuid(ns1, 0).

Should the v2 fast path in cap_convert_nscap() be moved after the vfsrootid
and rootid checks (or verify that vfsrootid and rootid match and satisfy
cap_root_level() >= task_ns->setfcap_level before returning size)?

> +
>  	nsrootid = from_kuid(fs_ns, rootid);
>  	if (nsrootid == -1)
>  		return -EINVAL;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261021-b4-setfcap-userns-v3-0-97c7ed4e43e1@toxicpanda.com?part=4

  reply	other threads:[~2026-10-21 21:07 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-21 20:37 [PATCH v3 0/5] capabilities: close the ways around the CAP_SETFCAP rule for uid 0 Josef Bacik
2026-10-21 20:37 ` [PATCH v3 1/5] userns: record how far up CAP_SETFCAP reaches Josef Bacik
2026-10-21 20:44   ` sashiko-bot
2026-10-21 20:37 ` [PATCH v3 2/5] userns: don't let setns() lend CAP_SETFCAP Josef Bacik
2026-10-21 20:48   ` sashiko-bot
2026-10-21 20:37 ` [PATCH v3 3/5] userns: check the opener and the writer before mapping uid 0 Josef Bacik
2026-10-21 20:57   ` sashiko-bot
2026-10-21 20:37 ` [PATCH v3 4/5] capabilities: limit fscaps to where CAP_SETFCAP reaches Josef Bacik
2026-10-21 21:07   ` sashiko-bot [this message]
2026-10-21 20:37 ` [PATCH v3 5/5] capabilities: don't let ptrace borrow CAP_SETFCAP Josef Bacik
2026-10-21 20:55   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-180684@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox