From: "Mickaël Salaün" <mic@digikod.net>
To: "Casey Schaufler" <casey@schaufler-ca.com>,
"Georgia Garcia" <georgia.garcia@canonical.com>,
"Günther Noack" <gnoack@google.com>,
"James Morris" <jmorris@namei.org>,
"John Johansen" <john.johansen@canonical.com>,
"Ondrej Mosnáček" <omosnacek@gmail.com>,
"Paul Moore" <paul@paul-moore.com>,
"Serge Hallyn" <serge@hallyn.com>,
"Stephen Smalley" <stephen.smalley.work@gmail.com>
Cc: "Mickaël Salaün" <mic@digikod.net>,
"Christian Brauner" <brauner@kernel.org>,
"KP Singh" <kpsingh@kernel.org>,
"Matt Bobrowski" <matt@bobrowski.net>,
apparmor@lists.ubuntu.com, bpf@vger.kernel.org,
kernel-team@cloudflare.com, linux-kernel@vger.kernel.org,
linux-security-module@vger.kernel.org, selinux@vger.kernel.org
Subject: [PATCH 1/6] lsm: Add a capable_lsm hook for capabilities guarding an LSM's policy
Date: Tue, 10 Nov 2026 21:30:35 +0100 [thread overview]
Message-ID: <20261110203042.2410140-2-mic@digikod.net> (raw)
In-Reply-To: <20261110203042.2410140-1-mic@digikod.net>
AppArmor, Smack and SELinux check the capabilities that guard their own
policy (e.g. CAP_MAC_ADMIN, CAP_MAC_OVERRIDE, or CAP_SYS_PTRACE for
Smack's ptrace rule) with cap_capable(), usually followed by their own
internal check. They use cap_capable() instead of capable() on purpose:
other stacked LSMs should not have to authorize these capabilities,
which may mean something else for their security model. This also hides
these capability uses from LSMs that restrict capability use in general:
a Landlock domain that denies CAP_MAC_ADMIN does not prevent a process
from managing AppArmor policy.
Add security_capable_lsm() for these checks, and a capable_lsm hook that
it calls after cap_capable(). Only LSMs that implement this new hook
see these checks, so LSMs that only implement the capable hook are still
not asked about another LSM's policy, and the calling LSM's own capable
hook is not called. The hook gets the LSM_ID_* of the LSM whose policy
the capability guards, so that BPF LSM programs can tell these checks
apart, and so that an LSM calling the helper can recognize its own
checks. Landlock implements this hook in the next commit.
The new hook changes nothing for current users of the capable hook, and
only adds a new BPF LSM attach point. Calling every capable hook except
the caller's would not keep this property: BPF LSM programs attached to
the capable hook would start seeing, and could deny, these checks after
a kernel update, and the other MAC LSMs would be asked about another
LSM's policy again. A new CAP_OPT_* flag for these checks would have
the same effect on BPF LSM programs, and would require every MAC LSM to
explicitly ignore the flagged checks.
Cc: Casey Schaufler <casey@schaufler-ca.com>
Cc: Günther Noack <gnoack@google.com>
Cc: John Johansen <john.johansen@canonical.com>
Cc: KP Singh <kpsingh@kernel.org>
Cc: Matt Bobrowski <matt@bobrowski.net>
Cc: Paul Moore <paul@paul-moore.com>
Cc: Serge Hallyn <serge@hallyn.com>
Cc: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Mickaël Salaün <mic@digikod.net>
---
include/linux/lsm_hook_defs.h | 2 ++
include/linux/security.h | 9 +++++++
security/security.c | 47 +++++++++++++++++++++++++++++++++++
3 files changed, 58 insertions(+)
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 87d9d06710cb..dc24e19f7eea 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -43,6 +43,8 @@ LSM_HOOK(int, 0, capset, struct cred *new, const struct cred *old,
const kernel_cap_t *permitted)
LSM_HOOK(int, 0, capable, const struct cred *cred, struct user_namespace *ns,
int cap, unsigned int opts)
+LSM_HOOK(int, 0, capable_lsm, const struct cred *cred,
+ struct user_namespace *ns, int cap, unsigned int opts, u64 lsm_id)
LSM_HOOK(int, 0, quotactl, int cmds, int type, int id, const struct super_block *sb)
LSM_HOOK(int, 0, quota_on, struct dentry *dentry)
LSM_HOOK(int, 0, syslog, int type)
diff --git a/include/linux/security.h b/include/linux/security.h
index 40831fab5470..1e89bc1fec17 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -352,6 +352,8 @@ int security_capable(const struct cred *cred,
struct user_namespace *ns,
int cap,
unsigned int opts);
+int security_capable_lsm(const struct cred *cred, struct user_namespace *ns,
+ int cap, unsigned int opts, u64 lsm_id);
int security_quotactl(int cmds, int type, int id, const struct super_block *sb);
int security_quota_on(struct dentry *dentry);
int security_syslog(int type);
@@ -710,6 +712,13 @@ static inline int security_capable(const struct cred *cred,
return cap_capable(cred, ns, cap, opts);
}
+static inline int security_capable_lsm(const struct cred *cred,
+ struct user_namespace *ns, int cap,
+ unsigned int opts, u64 lsm_id)
+{
+ return cap_capable(cred, ns, cap, opts);
+}
+
static inline int security_quotactl(int cmds, int type, int id,
const struct super_block *sb)
{
diff --git a/security/security.c b/security/security.c
index 4727b8347fc4..36365fac5801 100644
--- a/security/security.c
+++ b/security/security.c
@@ -664,6 +664,9 @@ int security_capset(struct cred *new, const struct cred *old,
* credentials. @cap contains the capability <include/linux/capability.h>.
* @opts contains options for the capable check <include/linux/security.h>.
*
+ * An LSM checking a capability that guards its own policy uses
+ * security_capable_lsm() instead.
+ *
* Return: Returns 0 if the capability is granted.
*/
int security_capable(const struct cred *cred,
@@ -674,6 +677,50 @@ int security_capable(const struct cred *cred,
return call_int_hook(capable, cred, ns, cap, opts);
}
+/**
+ * security_capable_lsm() - Check a capability that guards an LSM's policy
+ * @cred: credentials to examine
+ * @ns: user namespace
+ * @cap: capability requested
+ * @opts: capability check options
+ * @lsm_id: LSM_ID_* of the LSM whose policy the capability guards
+ *
+ * Check whether @cred has the @cap capability, when @cap guards the policy of
+ * the LSM identified by @lsm_id (e.g. CAP_MAC_ADMIN to manage this policy).
+ * @cap is first checked with commoncap, and then the LSMs implementing the
+ * capable_lsm hook may restrict this use of @cap. LSMs that only implement the
+ * capable hook are not asked, so they do not have to authorize capabilities
+ * that guard another LSM's policy.
+ *
+ * Callers: An LSM uses this function instead of cap_capable() or capable() for
+ * a capability that guards its own policy, with its own LSM_ID_* as @lsm_id,
+ * and then makes its own decision, without calling its own capable hook. This
+ * function must not be used for other capability checks.
+ *
+ * Implementations: A capable_lsm hook can only deny a capability that commoncap
+ * granted. It may be called in atomic context, including with interrupts
+ * disabled or from an interrupt handler (e.g. Smack's SIGIO check), and @cred
+ * might be another task's credentials (e.g. a ptrace tracer or a SIGIO
+ * recipient). An LSM that restricts capability use in general should implement
+ * both the capable and the capable_lsm hooks. This function calls every
+ * capable_lsm hook, so an LSM implementing this hook and calling this function
+ * should ignore checks for its own @lsm_id.
+ *
+ * Return: Returns 0 if the capability is granted, or a negative error code
+ * (e.g. -EPERM) otherwise.
+ */
+int security_capable_lsm(const struct cred *cred, struct user_namespace *ns,
+ int cap, unsigned int opts, u64 lsm_id)
+{
+ int rc;
+
+ rc = cap_capable(cred, ns, cap, opts);
+ if (rc)
+ return rc;
+
+ return call_int_hook(capable_lsm, cred, ns, cap, opts, lsm_id);
+}
+
/**
* security_quotactl() - Check if a quotactl() syscall is allowed for this fs
* @cmds: commands
--
2.56.0
next prev parent reply other threads:[~2026-11-10 20:30 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 20:30 [PATCH 0/6] Fix Landlock mediation of capabilities guarding LSM policies Mickaël Salaün
2026-11-10 20:30 ` Mickaël Salaün [this message]
2026-11-10 20:30 ` [PATCH 2/6] landlock: Restrict capabilities guarding other LSMs' policies Mickaël Salaün
2026-11-10 20:30 ` [PATCH 3/6] apparmor: Use security_capable_lsm() for policy capabilities Mickaël Salaün
2026-11-10 20:30 ` [PATCH 4/6] smack: Use security_capable_lsm() for privilege checks Mickaël Salaün
2026-11-10 20:30 ` [PATCH 5/6] selinux: Use security_capable_lsm() for CAP_MAC_ADMIN checks Mickaël Salaün
2026-11-10 20:30 ` [PATCH 6/6] selftests/landlock: Test capabilities guarding other LSMs' policies Mickaël Salaün
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=20261110203042.2410140-2-mic@digikod.net \
--to=mic@digikod.net \
--cc=apparmor@lists.ubuntu.com \
--cc=bpf@vger.kernel.org \
--cc=brauner@kernel.org \
--cc=casey@schaufler-ca.com \
--cc=georgia.garcia@canonical.com \
--cc=gnoack@google.com \
--cc=jmorris@namei.org \
--cc=john.johansen@canonical.com \
--cc=kernel-team@cloudflare.com \
--cc=kpsingh@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=matt@bobrowski.net \
--cc=omosnacek@gmail.com \
--cc=paul@paul-moore.com \
--cc=selinux@vger.kernel.org \
--cc=serge@hallyn.com \
--cc=stephen.smalley.work@gmail.com \
/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
all inboxes | Powered by JetHome®