Linux Security Modules development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Josef Bacik" <josef@toxicpanda.com>,
	"Serge Hallyn" <serge@hallyn.com>,
	"Paul Moore" <paul@paul-moore.com>,
	"Christian Brauner" <brauner@kernel.org>
Cc: "James Morris" <jmorris@namei.org>,
	"David Howells" <dhowells@redhat.com>,
	"Jarkko Sakkinen" <jarkko@kernel.org>,
	"Andrew G. Morgan" <morgan@kernel.org>,
	"Serge Hallyn" <sergeh@kernel.org>,
	linux-security-module@vger.kernel.org, keyrings@vger.kernel.org
Subject: Re: [PATCH v5 4/5] capabilities: limit fscaps to where CAP_SETFCAP reaches
Date: Fri, 23 Oct 2026 16:56:57 +0000	[thread overview]
Message-ID: <sashiko-outbox-182974@kernel.org> (raw)
In-Reply-To: <20261023-b4-setfcap-userns-v5-4-0620223be951@toxicpanda.com>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] capabilities: unconverted v2 fscap bypass in cap_convert_nscap()
--

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

capabilities: limit fscaps to where CAP_SETFCAP reaches

This commit restricts writing file capabilities in cap_convert_nscap() so
that the root user named by a v2 or v3 security.capability xattr (both as
seen through an idmapped mount and on the underlying filesystem) cannot be
uid 0 in an ancestor user namespace beyond task_ns->setfcap_level.

> diff --git a/security/commoncap.c b/security/commoncap.c
> index 8a9d998b18972..3551ce9b046ad 100644
> --- a/security/commoncap.c
> +++ b/security/commoncap.c
> @@ -216,6 +216,29 @@ bool cred_may_setfcap_userns(const struct cred *cred, struct user_namespace *ns)
>  	return !top || cred_setfcap_reaches(cred, ns->parent, top->level);
>  }
>  
> +/**
> + * cap_root_level - Find the topmost namespace in which a kuid is the root user
> + * @kuid: The kuid to look for
> + * @ns: The user namespace to start from
> + *
> + * Return: the lowest ->level among @ns and its ancestors in which @kuid is
> + * uid 0, which is how far up file capabilities with that root user are
> + * honoured; INT_MAX if there is no such namespace.
> + */
> +static int cap_root_level(kuid_t kuid, struct user_namespace *ns)
> +{
> +	int level = INT_MAX;
> +
> +	for (;; ns = ns->parent) {
> +		if (from_kuid(ns, kuid) == 0)
> +			level = ns->level;
> +		if (ns == &init_user_ns)
> +			break;
> +	}
> +
> +	return level;
> +}
> +
>  /**
>   * cap_settime - Determine whether the current process may set the system clock
>   * @ts: The time to set
> @@ -671,19 +694,41 @@ int cap_convert_nscap(const struct mnt_idmap *idmap, struct dentry *dentry,
>  		return -EINVAL;
>  	if (!capable_wrt_inode_uidgid(idmap, inode, CAP_SETFCAP))
>  		return -EPERM;
> -	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;
> +	/*
> +	 * A v2 xattr belongs to the root user of the filesystem's namespace,
> +	 * which may be the root user of ancestors of our namespace as well.
> +	 * Only write it unconverted if that root user is within reach, the
> +	 * checks below handle everything else.
> +	 */
> +	if (size == XATTR_CAPS_SZ_2 && idmap == &nop_mnt_idmap &&
> +	    ns_capable(fs_ns, CAP_SETFCAP) &&
> +	    cap_root_level(make_kuid(fs_ns, 0), task_ns) >=
> +	    task_ns->setfcap_level)
> +		/* user is privileged, just write the v2 */
> +		return size;

[Severity: High]
What happens in cap_convert_nscap() if uid 0 is not mapped in fs_ns when
writing a v2 capability xattr (size == XATTR_CAPS_SZ_2) on a non-idmapped
mount (idmap == &nop_mnt_idmap)?

Unlike the v3 conversion path further down in cap_convert_nscap(), which
checks vfsuid_valid(vfsrootid) and uid_valid(rootid) before calling
cap_root_level(), this v2 fast-path passes make_kuid(fs_ns, 0) directly to
cap_root_level() without checking uid_valid().

When uid 0 is not mapped in fs_ns, make_kuid(fs_ns, 0) returns INVALID_UID.
In cap_root_level(INVALID_UID, task_ns), from_kuid(ns, INVALID_UID) returns
(uid_t)-1 for every namespace up to init_user_ns, so cap_root_level() returns
INT_MAX. Because INT_MAX >= task_ns->setfcap_level is always true,
cap_convert_nscap() returns size and writes the unconverted v2 xattr.

For example, if a task without CAP_SETFCAP unshares a user namespace
(setting setfcap_level to 1) and maps only a non-zero UID (or creates an
unmapped child user namespace and mounts a tmpfs in it), make_kuid(fs_ns, 0)
is INVALID_UID. Wouldn't that allow the task to write an unconverted v2
security.capability xattr on the tmpfs inode, which could then be honoured
through an overlay mounted in init_user_ns, or honoured if fs_ns->uid_map is
later written to map uid 0? And when task_ns == fs_ns has no uid 0 mapping,
writing a v2 xattr writes an unconverted v2 xattr instead of falling through
to rootid_from_xattr() and failing with -EINVAL.

Should the v2 check verify uid_valid(make_kuid(fs_ns, 0)) (or should
cap_root_level() reject !uid_valid(kuid)) before allowing the unconverted v2
xattr to be written?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261023-b4-setfcap-userns-v5-0-0620223be951@toxicpanda.com?part=4

  reply	other threads:[~2026-10-23 16:56 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-23 16:30 [PATCH v5 0/5] capabilities: close the ways around the CAP_SETFCAP rule for uid 0 Josef Bacik
2026-10-23 16:30 ` [PATCH v5 1/5] userns: record how far up CAP_SETFCAP reaches Josef Bacik
2026-10-23 16:44   ` sashiko-bot
2026-10-23 16:30 ` [PATCH v5 2/5] userns: don't let setns() lend CAP_SETFCAP Josef Bacik
2026-10-23 16:46   ` sashiko-bot
2026-10-23 16:30 ` [PATCH v5 3/5] userns: check the opener and the writer before mapping uid 0 Josef Bacik
2026-10-23 17:03   ` sashiko-bot
2026-10-23 18:42     ` Jarkko Sakkinen
2026-10-23 16:30 ` [PATCH v5 4/5] capabilities: limit fscaps to where CAP_SETFCAP reaches Josef Bacik
2026-10-23 16:56   ` sashiko-bot [this message]
2026-10-23 16:30 ` [PATCH v5 5/5] capabilities: don't let ptrace borrow CAP_SETFCAP Josef Bacik
2026-10-23 16:47   ` 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-182974@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=brauner@kernel.org \
    --cc=dhowells@redhat.com \
    --cc=jarkko@kernel.org \
    --cc=jmorris@namei.org \
    --cc=josef@toxicpanda.com \
    --cc=keyrings@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=morgan@kernel.org \
    --cc=paul@paul-moore.com \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=serge@hallyn.com \
    --cc=sergeh@kernel.org \
    /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