* [PATCH] SELinux: Add support for BPF token access control
@ 2025-08-06 18:01 ericsu
2025-08-06 18:33 ` Stephen Smalley
2025-08-07 18:00 ` Daniel Durning
0 siblings, 2 replies; 15+ messages in thread
From: ericsu @ 2025-08-06 18:01 UTC (permalink / raw)
To: selinux; +Cc: paul
From: Eric Suen <ericsu@linux.microsoft.com>
BPF token support was introduced to allow a privileged process to delegate
limited BPF functionality—such as map creation and program loading—to an
unprivileged process:
https://lore.kernel.org/linux-security-module/20231130185229.2688956-1-andrii@kernel.org/
This patch adds SELinux support for controlling BPF token access. With
this change, SELinux policies can now enforce constraints on BPF token
usage based on both the delegating (privileged) process and the recipient
(unprivileged) process.
Supported operations currently include:
- map_create
- prog_load
High-level workflow:
1. An unprivileged process creates a VFS context via `fsopen()` and
obtains a file descriptor.
2. This descriptor is passed to a privileged process, which configures
BPF token delegation options and mounts a BPF filesystem.
3. SELinux records the `creator_sid` of the privileged process during
mount setup.
4. The unprivileged process then uses this BPF fs mount to create a
token and attach it to subsequent BPF syscalls.
5. During verification of `map_create` and `prog_load`, SELinux uses
`creator_sid` and the current SID to check policy permissions via:
avc_has_perm(creator_sid, current_sid, SECCLASS_BPF,
BPF__MAP_CREATE, NULL);
To verify the logic introduced by this patch, my fork of the SELinux
testsuite with relevant test cases is available here:
https://github.com/havefuncoding1/selinux-testsuite
Signed-off-by: Eric Suen <ericsu@linux.microsoft.com>
---
security/selinux/hooks.c | 107 ++++++++++++++++++++-
security/selinux/include/classmap.h | 2 +-
security/selinux/include/objsec.h | 4 +
security/selinux/include/policycap.h | 1 +
security/selinux/include/policycap_names.h | 1 +
security/selinux/include/security.h | 6 ++
6 files changed, 118 insertions(+), 3 deletions(-)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 335fbf76cdd2..ef9771542737 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -733,6 +733,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
goto out;
}
+ sbsec->creator_sid = current_sid();
+
if (strcmp(sb->s_type->name, "proc") == 0)
sbsec->flags |= SE_SBPROC | SE_SBGENFS;
@@ -7002,9 +7004,13 @@ static int selinux_ib_alloc_security(void *ib_sec)
static int selinux_bpf(int cmd, union bpf_attr *attr,
unsigned int size, bool kernel)
{
+ bool bpf_token_perms = selinux_policycap_bpf_token_perms();
u32 sid = current_sid();
int ret;
+ if (bpf_token_perms)
+ return 0;
+
switch (cmd) {
case BPF_MAP_CREATE:
ret = avc_has_perm(sid, sid, SECCLASS_BPF, BPF__MAP_CREATE,
@@ -7086,10 +7092,34 @@ static int selinux_bpf_prog(struct bpf_prog *prog)
BPF__PROG_RUN, NULL);
}
+static int selinux_bpffs_creator_sid(const union bpf_attr *attr)
+{
+ struct path path;
+ struct super_block *sb;
+ struct superblock_security_struct *sbsec;
+
+ CLASS(fd, f)(attr->token_create.bpffs_fd);
+
+ if (!fd_file(f))
+ return SECSID_NULL;
+
+ path = fd_file(f)->f_path;
+ sb = path.dentry->d_sb;
+ if (!sb)
+ return SECSID_NULL;
+
+ sbsec = selinux_superblock(sb);
+ if (!sbsec)
+ return SECSID_NULL;
+
+ return sbsec->creator_sid;
+}
+
static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
struct bpf_token *token, bool kernel)
{
struct bpf_security_struct *bpfsec;
+ u32 ssid;
bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
if (!bpfsec)
@@ -7098,7 +7128,15 @@ static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
bpfsec->sid = current_sid();
map->security = bpfsec;
- return 0;
+ if (!token)
+ ssid = bpfsec->sid;
+ else {
+ ssid = selinux_bpffs_creator_sid(attr);
+ if (ssid == SECSID_NULL)
+ return -EPERM;
+ }
+
+ return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__MAP_CREATE, NULL);
}
static void selinux_bpf_map_free(struct bpf_map *map)
@@ -7113,6 +7151,7 @@ static int selinux_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
struct bpf_token *token, bool kernel)
{
struct bpf_security_struct *bpfsec;
+ u32 ssid;
bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
if (!bpfsec)
@@ -7121,7 +7160,15 @@ static int selinux_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
bpfsec->sid = current_sid();
prog->aux->security = bpfsec;
- return 0;
+ if (!token)
+ ssid = bpfsec->sid;
+ if (token) {
+ ssid = selinux_bpffs_creator_sid(attr);
+ if (ssid == SECSID_NULL)
+ return -EPERM;
+ }
+
+ return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__PROG_LOAD, NULL);
}
static void selinux_bpf_prog_free(struct bpf_prog *prog)
@@ -7132,10 +7179,18 @@ static void selinux_bpf_prog_free(struct bpf_prog *prog)
kfree(bpfsec);
}
+#define bpf_token_cmd(T, C) \
+ ((T)->allowed_cmds & (1ULL << (C)))
+
static int selinux_bpf_token_create(struct bpf_token *token, union bpf_attr *attr,
const struct path *path)
{
struct bpf_security_struct *bpfsec;
+ u32 sid = selinux_bpffs_creator_sid(attr);
+ int err;
+
+ if (sid == SECSID_NULL)
+ return -EPERM;
bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
if (!bpfsec)
@@ -7144,6 +7199,29 @@ static int selinux_bpf_token_create(struct bpf_token *token, union bpf_attr *att
bpfsec->sid = current_sid();
token->security = bpfsec;
+ bpfsec->perms = 0;
+
+ /**
+ * 'token->allowed_cmds' is a bit mask of allowed commands
+ * Convert the BPF command enum to a bitmask representing its position in the
+ * allowed_cmds bitmap.
+ */
+ if (bpf_token_cmd(token, BPF_MAP_CREATE)) {
+ err = avc_has_perm(bpfsec->sid, sid, SECCLASS_BPF, BPF__MAP_CREATE_AS, NULL);
+ if (err)
+ return err;
+
+ bpfsec->perms |= BPF__MAP_CREATE;
+ }
+
+ if (bpf_token_cmd(token, BPF_PROG_LOAD)) {
+ err = avc_has_perm(bpfsec->sid, sid, SECCLASS_BPF, BPF__PROG_LOAD_AS, NULL);
+ if (err)
+ return err;
+
+ bpfsec->perms |= BPF__PROG_LOAD;
+ }
+
return 0;
}
@@ -7154,6 +7232,30 @@ static void selinux_bpf_token_free(struct bpf_token *token)
token->security = NULL;
kfree(bpfsec);
}
+
+static int selinux_bpf_token_cmd(const struct bpf_token *token, enum bpf_cmd cmd)
+{
+ struct bpf_security_struct *bpfsec;
+
+ if (!token || !token->security)
+ return -EINVAL;
+
+ bpfsec = token->security;
+ switch (cmd) {
+ case BPF_MAP_CREATE:
+ if ((bpfsec->perms & BPF__MAP_CREATE) != BPF__MAP_CREATE)
+ return -EACCES;
+ break;
+ case BPF_PROG_LOAD:
+ if ((bpfsec->perms & BPF__PROG_LOAD) != BPF__PROG_LOAD)
+ return -EACCES;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
#endif
struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = {
@@ -7585,6 +7687,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(bpf_map_create, selinux_bpf_map_create),
LSM_HOOK_INIT(bpf_prog_load, selinux_bpf_prog_load),
LSM_HOOK_INIT(bpf_token_create, selinux_bpf_token_create),
+ LSM_HOOK_INIT(bpf_token_cmd, selinux_bpf_token_cmd),
#endif
#ifdef CONFIG_PERF_EVENTS
LSM_HOOK_INIT(perf_event_alloc, selinux_perf_event_alloc),
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 5665aa5e7853..a6ed864af64c 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -171,7 +171,7 @@ const struct security_class_mapping secclass_map[] = {
{ "infiniband_endport", { "manage_subnet", NULL } },
{ "bpf",
{ "map_create", "map_read", "map_write", "prog_load", "prog_run",
- NULL } },
+ "map_create_as", "prog_load_as", NULL } },
{ "xdp_socket", { COMMON_SOCK_PERMS, NULL } },
{ "mctp_socket", { COMMON_SOCK_PERMS, NULL } },
{ "perf_event",
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 1d7ac59015a1..b7e55e5c6d9c 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -87,6 +87,8 @@ struct superblock_security_struct {
u32 sid; /* SID of file system superblock */
u32 def_sid; /* default SID for labeling */
u32 mntpoint_sid; /* SECURITY_FS_USE_MNTPOINT context for files */
+ u32 creator_sid; /* SID of privileged process and is used to */
+ /* verify bpf operations */
unsigned short behavior; /* labeling behavior */
unsigned short flags; /* which mount options were specified */
struct mutex lock;
@@ -164,6 +166,8 @@ struct pkey_security_struct {
struct bpf_security_struct {
u32 sid; /* SID of bpf obj creator */
+ u32 perms; /* allowed AV permissions, e.g. BPF__MAP_CREATE, */
+ /* for requested bpf commands */
};
struct perf_event_security_struct {
diff --git a/security/selinux/include/policycap.h b/security/selinux/include/policycap.h
index 7405154e6c42..cde6aaf442cd 100644
--- a/security/selinux/include/policycap.h
+++ b/security/selinux/include/policycap.h
@@ -17,6 +17,7 @@ enum {
POLICYDB_CAP_NETLINK_XPERM,
POLICYDB_CAP_NETIF_WILDCARD,
POLICYDB_CAP_GENFS_SECLABEL_WILDCARD,
+ POLICYDB_CAP_BPF_TOKEN_PERMS,
__POLICYDB_CAP_MAX
};
#define POLICYDB_CAP_MAX (__POLICYDB_CAP_MAX - 1)
diff --git a/security/selinux/include/policycap_names.h b/security/selinux/include/policycap_names.h
index d8962fcf2ff9..cd5e73f992ea 100644
--- a/security/selinux/include/policycap_names.h
+++ b/security/selinux/include/policycap_names.h
@@ -20,6 +20,7 @@ const char *const selinux_policycap_names[__POLICYDB_CAP_MAX] = {
"netlink_xperm",
"netif_wildcard",
"genfs_seclabel_wildcard",
+ "bpf_token_perms",
};
/* clang-format on */
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 8201e6a3ac0f..d3832e4ad4fb 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -209,6 +209,12 @@ static inline bool selinux_policycap_netif_wildcard(void)
selinux_state.policycap[POLICYDB_CAP_NETIF_WILDCARD]);
}
+static inline bool selinux_policycap_bpf_token_perms(void)
+{
+ return READ_ONCE(
+ selinux_state.policycap[POLICYDB_CAP_BPF_TOKEN_PERMS]);
+}
+
struct selinux_policy_convert_data;
struct selinux_load_state {
--
2.50.1.windows.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH] SELinux: Add support for BPF token access control
2025-08-06 18:01 [PATCH] SELinux: Add support for BPF token access control ericsu
@ 2025-08-06 18:33 ` Stephen Smalley
2025-08-07 13:46 ` Stephen Smalley
2025-08-07 18:00 ` Daniel Durning
1 sibling, 1 reply; 15+ messages in thread
From: Stephen Smalley @ 2025-08-06 18:33 UTC (permalink / raw)
To: ericsu, danieldurning.work; +Cc: selinux, paul
On Wed, Aug 6, 2025 at 2:07 PM <ericsu@linux.microsoft.com> wrote:
>
> From: Eric Suen <ericsu@linux.microsoft.com>
>
> BPF token support was introduced to allow a privileged process to delegate
> limited BPF functionality—such as map creation and program loading—to an
> unprivileged process:
> https://lore.kernel.org/linux-security-module/20231130185229.2688956-1-andrii@kernel.org/
>
> This patch adds SELinux support for controlling BPF token access. With
> this change, SELinux policies can now enforce constraints on BPF token
> usage based on both the delegating (privileged) process and the recipient
> (unprivileged) process.
>
> Supported operations currently include:
> - map_create
> - prog_load
>
> High-level workflow:
> 1. An unprivileged process creates a VFS context via `fsopen()` and
> obtains a file descriptor.
> 2. This descriptor is passed to a privileged process, which configures
> BPF token delegation options and mounts a BPF filesystem.
> 3. SELinux records the `creator_sid` of the privileged process during
> mount setup.
> 4. The unprivileged process then uses this BPF fs mount to create a
> token and attach it to subsequent BPF syscalls.
> 5. During verification of `map_create` and `prog_load`, SELinux uses
> `creator_sid` and the current SID to check policy permissions via:
> avc_has_perm(creator_sid, current_sid, SECCLASS_BPF,
> BPF__MAP_CREATE, NULL);
>
> To verify the logic introduced by this patch, my fork of the SELinux
> testsuite with relevant test cases is available here:
> https://github.com/havefuncoding1/selinux-testsuite
Interesting approach. Added Daniel to the distribution. Can you please
also post your testsuite patch to the list for review as per
https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#submitting-a-testsuite-patch
>
> Signed-off-by: Eric Suen <ericsu@linux.microsoft.com>
> ---
> security/selinux/hooks.c | 107 ++++++++++++++++++++-
> security/selinux/include/classmap.h | 2 +-
> security/selinux/include/objsec.h | 4 +
> security/selinux/include/policycap.h | 1 +
> security/selinux/include/policycap_names.h | 1 +
> security/selinux/include/security.h | 6 ++
> 6 files changed, 118 insertions(+), 3 deletions(-)
>
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 335fbf76cdd2..ef9771542737 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -733,6 +733,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
> goto out;
> }
>
> + sbsec->creator_sid = current_sid();
> +
> if (strcmp(sb->s_type->name, "proc") == 0)
> sbsec->flags |= SE_SBPROC | SE_SBGENFS;
>
> @@ -7002,9 +7004,13 @@ static int selinux_ib_alloc_security(void *ib_sec)
> static int selinux_bpf(int cmd, union bpf_attr *attr,
> unsigned int size, bool kernel)
> {
> + bool bpf_token_perms = selinux_policycap_bpf_token_perms();
> u32 sid = current_sid();
> int ret;
>
> + if (bpf_token_perms)
> + return 0;
> +
> switch (cmd) {
> case BPF_MAP_CREATE:
> ret = avc_has_perm(sid, sid, SECCLASS_BPF, BPF__MAP_CREATE,
> @@ -7086,10 +7092,34 @@ static int selinux_bpf_prog(struct bpf_prog *prog)
> BPF__PROG_RUN, NULL);
> }
>
> +static int selinux_bpffs_creator_sid(const union bpf_attr *attr)
> +{
> + struct path path;
> + struct super_block *sb;
> + struct superblock_security_struct *sbsec;
> +
> + CLASS(fd, f)(attr->token_create.bpffs_fd);
> +
> + if (!fd_file(f))
> + return SECSID_NULL;
> +
> + path = fd_file(f)->f_path;
> + sb = path.dentry->d_sb;
> + if (!sb)
> + return SECSID_NULL;
> +
> + sbsec = selinux_superblock(sb);
> + if (!sbsec)
> + return SECSID_NULL;
> +
> + return sbsec->creator_sid;
> +}
> +
> static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
> struct bpf_token *token, bool kernel)
> {
> struct bpf_security_struct *bpfsec;
> + u32 ssid;
>
> bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> if (!bpfsec)
> @@ -7098,7 +7128,15 @@ static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
> bpfsec->sid = current_sid();
> map->security = bpfsec;
>
> - return 0;
> + if (!token)
> + ssid = bpfsec->sid;
> + else {
> + ssid = selinux_bpffs_creator_sid(attr);
> + if (ssid == SECSID_NULL)
> + return -EPERM;
> + }
> +
> + return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__MAP_CREATE, NULL);
> }
>
> static void selinux_bpf_map_free(struct bpf_map *map)
> @@ -7113,6 +7151,7 @@ static int selinux_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
> struct bpf_token *token, bool kernel)
> {
> struct bpf_security_struct *bpfsec;
> + u32 ssid;
>
> bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> if (!bpfsec)
> @@ -7121,7 +7160,15 @@ static int selinux_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
> bpfsec->sid = current_sid();
> prog->aux->security = bpfsec;
>
> - return 0;
> + if (!token)
> + ssid = bpfsec->sid;
> + if (token) {
> + ssid = selinux_bpffs_creator_sid(attr);
> + if (ssid == SECSID_NULL)
> + return -EPERM;
> + }
> +
> + return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__PROG_LOAD, NULL);
> }
>
> static void selinux_bpf_prog_free(struct bpf_prog *prog)
> @@ -7132,10 +7179,18 @@ static void selinux_bpf_prog_free(struct bpf_prog *prog)
> kfree(bpfsec);
> }
>
> +#define bpf_token_cmd(T, C) \
> + ((T)->allowed_cmds & (1ULL << (C)))
> +
> static int selinux_bpf_token_create(struct bpf_token *token, union bpf_attr *attr,
> const struct path *path)
> {
> struct bpf_security_struct *bpfsec;
> + u32 sid = selinux_bpffs_creator_sid(attr);
> + int err;
> +
> + if (sid == SECSID_NULL)
> + return -EPERM;
>
> bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> if (!bpfsec)
> @@ -7144,6 +7199,29 @@ static int selinux_bpf_token_create(struct bpf_token *token, union bpf_attr *att
> bpfsec->sid = current_sid();
> token->security = bpfsec;
>
> + bpfsec->perms = 0;
> +
> + /**
> + * 'token->allowed_cmds' is a bit mask of allowed commands
> + * Convert the BPF command enum to a bitmask representing its position in the
> + * allowed_cmds bitmap.
> + */
> + if (bpf_token_cmd(token, BPF_MAP_CREATE)) {
> + err = avc_has_perm(bpfsec->sid, sid, SECCLASS_BPF, BPF__MAP_CREATE_AS, NULL);
> + if (err)
> + return err;
> +
> + bpfsec->perms |= BPF__MAP_CREATE;
> + }
> +
> + if (bpf_token_cmd(token, BPF_PROG_LOAD)) {
> + err = avc_has_perm(bpfsec->sid, sid, SECCLASS_BPF, BPF__PROG_LOAD_AS, NULL);
> + if (err)
> + return err;
> +
> + bpfsec->perms |= BPF__PROG_LOAD;
> + }
> +
> return 0;
> }
>
> @@ -7154,6 +7232,30 @@ static void selinux_bpf_token_free(struct bpf_token *token)
> token->security = NULL;
> kfree(bpfsec);
> }
> +
> +static int selinux_bpf_token_cmd(const struct bpf_token *token, enum bpf_cmd cmd)
> +{
> + struct bpf_security_struct *bpfsec;
> +
> + if (!token || !token->security)
> + return -EINVAL;
> +
> + bpfsec = token->security;
> + switch (cmd) {
> + case BPF_MAP_CREATE:
> + if ((bpfsec->perms & BPF__MAP_CREATE) != BPF__MAP_CREATE)
> + return -EACCES;
> + break;
> + case BPF_PROG_LOAD:
> + if ((bpfsec->perms & BPF__PROG_LOAD) != BPF__PROG_LOAD)
> + return -EACCES;
> + break;
> + default:
> + break;
> + }
> +
> + return 0;
> +}
> #endif
>
> struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = {
> @@ -7585,6 +7687,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
> LSM_HOOK_INIT(bpf_map_create, selinux_bpf_map_create),
> LSM_HOOK_INIT(bpf_prog_load, selinux_bpf_prog_load),
> LSM_HOOK_INIT(bpf_token_create, selinux_bpf_token_create),
> + LSM_HOOK_INIT(bpf_token_cmd, selinux_bpf_token_cmd),
> #endif
> #ifdef CONFIG_PERF_EVENTS
> LSM_HOOK_INIT(perf_event_alloc, selinux_perf_event_alloc),
> diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
> index 5665aa5e7853..a6ed864af64c 100644
> --- a/security/selinux/include/classmap.h
> +++ b/security/selinux/include/classmap.h
> @@ -171,7 +171,7 @@ const struct security_class_mapping secclass_map[] = {
> { "infiniband_endport", { "manage_subnet", NULL } },
> { "bpf",
> { "map_create", "map_read", "map_write", "prog_load", "prog_run",
> - NULL } },
> + "map_create_as", "prog_load_as", NULL } },
> { "xdp_socket", { COMMON_SOCK_PERMS, NULL } },
> { "mctp_socket", { COMMON_SOCK_PERMS, NULL } },
> { "perf_event",
> diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
> index 1d7ac59015a1..b7e55e5c6d9c 100644
> --- a/security/selinux/include/objsec.h
> +++ b/security/selinux/include/objsec.h
> @@ -87,6 +87,8 @@ struct superblock_security_struct {
> u32 sid; /* SID of file system superblock */
> u32 def_sid; /* default SID for labeling */
> u32 mntpoint_sid; /* SECURITY_FS_USE_MNTPOINT context for files */
> + u32 creator_sid; /* SID of privileged process and is used to */
> + /* verify bpf operations */
> unsigned short behavior; /* labeling behavior */
> unsigned short flags; /* which mount options were specified */
> struct mutex lock;
> @@ -164,6 +166,8 @@ struct pkey_security_struct {
>
> struct bpf_security_struct {
> u32 sid; /* SID of bpf obj creator */
> + u32 perms; /* allowed AV permissions, e.g. BPF__MAP_CREATE, */
> + /* for requested bpf commands */
> };
>
> struct perf_event_security_struct {
> diff --git a/security/selinux/include/policycap.h b/security/selinux/include/policycap.h
> index 7405154e6c42..cde6aaf442cd 100644
> --- a/security/selinux/include/policycap.h
> +++ b/security/selinux/include/policycap.h
> @@ -17,6 +17,7 @@ enum {
> POLICYDB_CAP_NETLINK_XPERM,
> POLICYDB_CAP_NETIF_WILDCARD,
> POLICYDB_CAP_GENFS_SECLABEL_WILDCARD,
> + POLICYDB_CAP_BPF_TOKEN_PERMS,
> __POLICYDB_CAP_MAX
> };
> #define POLICYDB_CAP_MAX (__POLICYDB_CAP_MAX - 1)
> diff --git a/security/selinux/include/policycap_names.h b/security/selinux/include/policycap_names.h
> index d8962fcf2ff9..cd5e73f992ea 100644
> --- a/security/selinux/include/policycap_names.h
> +++ b/security/selinux/include/policycap_names.h
> @@ -20,6 +20,7 @@ const char *const selinux_policycap_names[__POLICYDB_CAP_MAX] = {
> "netlink_xperm",
> "netif_wildcard",
> "genfs_seclabel_wildcard",
> + "bpf_token_perms",
> };
> /* clang-format on */
>
> diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
> index 8201e6a3ac0f..d3832e4ad4fb 100644
> --- a/security/selinux/include/security.h
> +++ b/security/selinux/include/security.h
> @@ -209,6 +209,12 @@ static inline bool selinux_policycap_netif_wildcard(void)
> selinux_state.policycap[POLICYDB_CAP_NETIF_WILDCARD]);
> }
>
> +static inline bool selinux_policycap_bpf_token_perms(void)
> +{
> + return READ_ONCE(
> + selinux_state.policycap[POLICYDB_CAP_BPF_TOKEN_PERMS]);
> +}
> +
> struct selinux_policy_convert_data;
>
> struct selinux_load_state {
> --
> 2.50.1.windows.1
>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] SELinux: Add support for BPF token access control
2025-08-06 18:33 ` Stephen Smalley
@ 2025-08-07 13:46 ` Stephen Smalley
2025-08-08 18:57 ` Eric Suen
2025-08-11 17:47 ` Paul Moore
0 siblings, 2 replies; 15+ messages in thread
From: Stephen Smalley @ 2025-08-07 13:46 UTC (permalink / raw)
To: ericsu, danieldurning.work; +Cc: selinux, paul
On Wed, Aug 6, 2025 at 2:33 PM Stephen Smalley
<stephen.smalley.work@gmail.com> wrote:
>
> On Wed, Aug 6, 2025 at 2:07 PM <ericsu@linux.microsoft.com> wrote:
> >
> > From: Eric Suen <ericsu@linux.microsoft.com>
> >
> > BPF token support was introduced to allow a privileged process to delegate
> > limited BPF functionality—such as map creation and program loading—to an
> > unprivileged process:
> > https://lore.kernel.org/linux-security-module/20231130185229.2688956-1-andrii@kernel.org/
> >
> > This patch adds SELinux support for controlling BPF token access. With
> > this change, SELinux policies can now enforce constraints on BPF token
> > usage based on both the delegating (privileged) process and the recipient
> > (unprivileged) process.
> >
> > Supported operations currently include:
> > - map_create
> > - prog_load
> >
> > High-level workflow:
> > 1. An unprivileged process creates a VFS context via `fsopen()` and
> > obtains a file descriptor.
> > 2. This descriptor is passed to a privileged process, which configures
> > BPF token delegation options and mounts a BPF filesystem.
> > 3. SELinux records the `creator_sid` of the privileged process during
> > mount setup.
> > 4. The unprivileged process then uses this BPF fs mount to create a
> > token and attach it to subsequent BPF syscalls.
> > 5. During verification of `map_create` and `prog_load`, SELinux uses
> > `creator_sid` and the current SID to check policy permissions via:
> > avc_has_perm(creator_sid, current_sid, SECCLASS_BPF,
> > BPF__MAP_CREATE, NULL);
> >
> > To verify the logic introduced by this patch, my fork of the SELinux
> > testsuite with relevant test cases is available here:
> > https://github.com/havefuncoding1/selinux-testsuite
>
> Interesting approach. Added Daniel to the distribution. Can you please
> also post your testsuite patch to the list for review as per
> https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#submitting-a-testsuite-patch
Also, since you are introducing new permissions and a policy
capability, please include instructions in the commit description for
running your testsuite, see
https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#add-new-permissions
and
https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#adding-a-new-selinux-policy-capability
for instructions and links to example previous commits.
>
> >
> > Signed-off-by: Eric Suen <ericsu@linux.microsoft.com>
> > ---
> > security/selinux/hooks.c | 107 ++++++++++++++++++++-
> > security/selinux/include/classmap.h | 2 +-
> > security/selinux/include/objsec.h | 4 +
> > security/selinux/include/policycap.h | 1 +
> > security/selinux/include/policycap_names.h | 1 +
> > security/selinux/include/security.h | 6 ++
> > 6 files changed, 118 insertions(+), 3 deletions(-)
> >
> > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > index 335fbf76cdd2..ef9771542737 100644
> > --- a/security/selinux/hooks.c
> > +++ b/security/selinux/hooks.c
> > @@ -733,6 +733,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
> > goto out;
> > }
> >
> > + sbsec->creator_sid = current_sid();
> > +
> > if (strcmp(sb->s_type->name, "proc") == 0)
> > sbsec->flags |= SE_SBPROC | SE_SBGENFS;
> >
> > @@ -7002,9 +7004,13 @@ static int selinux_ib_alloc_security(void *ib_sec)
> > static int selinux_bpf(int cmd, union bpf_attr *attr,
> > unsigned int size, bool kernel)
> > {
> > + bool bpf_token_perms = selinux_policycap_bpf_token_perms();
> > u32 sid = current_sid();
> > int ret;
> >
> > + if (bpf_token_perms)
> > + return 0;
> > +
> > switch (cmd) {
> > case BPF_MAP_CREATE:
> > ret = avc_has_perm(sid, sid, SECCLASS_BPF, BPF__MAP_CREATE,
> > @@ -7086,10 +7092,34 @@ static int selinux_bpf_prog(struct bpf_prog *prog)
> > BPF__PROG_RUN, NULL);
> > }
> >
> > +static int selinux_bpffs_creator_sid(const union bpf_attr *attr)
> > +{
> > + struct path path;
> > + struct super_block *sb;
> > + struct superblock_security_struct *sbsec;
> > +
> > + CLASS(fd, f)(attr->token_create.bpffs_fd);
> > +
> > + if (!fd_file(f))
> > + return SECSID_NULL;
> > +
> > + path = fd_file(f)->f_path;
> > + sb = path.dentry->d_sb;
> > + if (!sb)
> > + return SECSID_NULL;
> > +
> > + sbsec = selinux_superblock(sb);
> > + if (!sbsec)
> > + return SECSID_NULL;
> > +
> > + return sbsec->creator_sid;
> > +}
> > +
> > static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
> > struct bpf_token *token, bool kernel)
> > {
> > struct bpf_security_struct *bpfsec;
> > + u32 ssid;
> >
> > bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> > if (!bpfsec)
> > @@ -7098,7 +7128,15 @@ static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
> > bpfsec->sid = current_sid();
> > map->security = bpfsec;
> >
> > - return 0;
> > + if (!token)
> > + ssid = bpfsec->sid;
> > + else {
> > + ssid = selinux_bpffs_creator_sid(attr);
> > + if (ssid == SECSID_NULL)
> > + return -EPERM;
> > + }
> > +
> > + return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__MAP_CREATE, NULL);
> > }
> >
> > static void selinux_bpf_map_free(struct bpf_map *map)
> > @@ -7113,6 +7151,7 @@ static int selinux_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
> > struct bpf_token *token, bool kernel)
> > {
> > struct bpf_security_struct *bpfsec;
> > + u32 ssid;
> >
> > bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> > if (!bpfsec)
> > @@ -7121,7 +7160,15 @@ static int selinux_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
> > bpfsec->sid = current_sid();
> > prog->aux->security = bpfsec;
> >
> > - return 0;
> > + if (!token)
> > + ssid = bpfsec->sid;
> > + if (token) {
> > + ssid = selinux_bpffs_creator_sid(attr);
> > + if (ssid == SECSID_NULL)
> > + return -EPERM;
> > + }
> > +
> > + return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__PROG_LOAD, NULL);
> > }
> >
> > static void selinux_bpf_prog_free(struct bpf_prog *prog)
> > @@ -7132,10 +7179,18 @@ static void selinux_bpf_prog_free(struct bpf_prog *prog)
> > kfree(bpfsec);
> > }
> >
> > +#define bpf_token_cmd(T, C) \
> > + ((T)->allowed_cmds & (1ULL << (C)))
> > +
> > static int selinux_bpf_token_create(struct bpf_token *token, union bpf_attr *attr,
> > const struct path *path)
> > {
> > struct bpf_security_struct *bpfsec;
> > + u32 sid = selinux_bpffs_creator_sid(attr);
> > + int err;
> > +
> > + if (sid == SECSID_NULL)
> > + return -EPERM;
> >
> > bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> > if (!bpfsec)
> > @@ -7144,6 +7199,29 @@ static int selinux_bpf_token_create(struct bpf_token *token, union bpf_attr *att
> > bpfsec->sid = current_sid();
> > token->security = bpfsec;
> >
> > + bpfsec->perms = 0;
> > +
> > + /**
> > + * 'token->allowed_cmds' is a bit mask of allowed commands
> > + * Convert the BPF command enum to a bitmask representing its position in the
> > + * allowed_cmds bitmap.
> > + */
> > + if (bpf_token_cmd(token, BPF_MAP_CREATE)) {
> > + err = avc_has_perm(bpfsec->sid, sid, SECCLASS_BPF, BPF__MAP_CREATE_AS, NULL);
> > + if (err)
> > + return err;
> > +
> > + bpfsec->perms |= BPF__MAP_CREATE;
> > + }
> > +
> > + if (bpf_token_cmd(token, BPF_PROG_LOAD)) {
> > + err = avc_has_perm(bpfsec->sid, sid, SECCLASS_BPF, BPF__PROG_LOAD_AS, NULL);
> > + if (err)
> > + return err;
> > +
> > + bpfsec->perms |= BPF__PROG_LOAD;
> > + }
> > +
> > return 0;
> > }
> >
> > @@ -7154,6 +7232,30 @@ static void selinux_bpf_token_free(struct bpf_token *token)
> > token->security = NULL;
> > kfree(bpfsec);
> > }
> > +
> > +static int selinux_bpf_token_cmd(const struct bpf_token *token, enum bpf_cmd cmd)
> > +{
> > + struct bpf_security_struct *bpfsec;
> > +
> > + if (!token || !token->security)
> > + return -EINVAL;
> > +
> > + bpfsec = token->security;
> > + switch (cmd) {
> > + case BPF_MAP_CREATE:
> > + if ((bpfsec->perms & BPF__MAP_CREATE) != BPF__MAP_CREATE)
> > + return -EACCES;
> > + break;
> > + case BPF_PROG_LOAD:
> > + if ((bpfsec->perms & BPF__PROG_LOAD) != BPF__PROG_LOAD)
> > + return -EACCES;
> > + break;
> > + default:
> > + break;
> > + }
> > +
> > + return 0;
> > +}
> > #endif
> >
> > struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = {
> > @@ -7585,6 +7687,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
> > LSM_HOOK_INIT(bpf_map_create, selinux_bpf_map_create),
> > LSM_HOOK_INIT(bpf_prog_load, selinux_bpf_prog_load),
> > LSM_HOOK_INIT(bpf_token_create, selinux_bpf_token_create),
> > + LSM_HOOK_INIT(bpf_token_cmd, selinux_bpf_token_cmd),
> > #endif
> > #ifdef CONFIG_PERF_EVENTS
> > LSM_HOOK_INIT(perf_event_alloc, selinux_perf_event_alloc),
> > diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
> > index 5665aa5e7853..a6ed864af64c 100644
> > --- a/security/selinux/include/classmap.h
> > +++ b/security/selinux/include/classmap.h
> > @@ -171,7 +171,7 @@ const struct security_class_mapping secclass_map[] = {
> > { "infiniband_endport", { "manage_subnet", NULL } },
> > { "bpf",
> > { "map_create", "map_read", "map_write", "prog_load", "prog_run",
> > - NULL } },
> > + "map_create_as", "prog_load_as", NULL } },
> > { "xdp_socket", { COMMON_SOCK_PERMS, NULL } },
> > { "mctp_socket", { COMMON_SOCK_PERMS, NULL } },
> > { "perf_event",
> > diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
> > index 1d7ac59015a1..b7e55e5c6d9c 100644
> > --- a/security/selinux/include/objsec.h
> > +++ b/security/selinux/include/objsec.h
> > @@ -87,6 +87,8 @@ struct superblock_security_struct {
> > u32 sid; /* SID of file system superblock */
> > u32 def_sid; /* default SID for labeling */
> > u32 mntpoint_sid; /* SECURITY_FS_USE_MNTPOINT context for files */
> > + u32 creator_sid; /* SID of privileged process and is used to */
> > + /* verify bpf operations */
> > unsigned short behavior; /* labeling behavior */
> > unsigned short flags; /* which mount options were specified */
> > struct mutex lock;
> > @@ -164,6 +166,8 @@ struct pkey_security_struct {
> >
> > struct bpf_security_struct {
> > u32 sid; /* SID of bpf obj creator */
> > + u32 perms; /* allowed AV permissions, e.g. BPF__MAP_CREATE, */
> > + /* for requested bpf commands */
> > };
> >
> > struct perf_event_security_struct {
> > diff --git a/security/selinux/include/policycap.h b/security/selinux/include/policycap.h
> > index 7405154e6c42..cde6aaf442cd 100644
> > --- a/security/selinux/include/policycap.h
> > +++ b/security/selinux/include/policycap.h
> > @@ -17,6 +17,7 @@ enum {
> > POLICYDB_CAP_NETLINK_XPERM,
> > POLICYDB_CAP_NETIF_WILDCARD,
> > POLICYDB_CAP_GENFS_SECLABEL_WILDCARD,
> > + POLICYDB_CAP_BPF_TOKEN_PERMS,
> > __POLICYDB_CAP_MAX
> > };
> > #define POLICYDB_CAP_MAX (__POLICYDB_CAP_MAX - 1)
> > diff --git a/security/selinux/include/policycap_names.h b/security/selinux/include/policycap_names.h
> > index d8962fcf2ff9..cd5e73f992ea 100644
> > --- a/security/selinux/include/policycap_names.h
> > +++ b/security/selinux/include/policycap_names.h
> > @@ -20,6 +20,7 @@ const char *const selinux_policycap_names[__POLICYDB_CAP_MAX] = {
> > "netlink_xperm",
> > "netif_wildcard",
> > "genfs_seclabel_wildcard",
> > + "bpf_token_perms",
> > };
> > /* clang-format on */
> >
> > diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
> > index 8201e6a3ac0f..d3832e4ad4fb 100644
> > --- a/security/selinux/include/security.h
> > +++ b/security/selinux/include/security.h
> > @@ -209,6 +209,12 @@ static inline bool selinux_policycap_netif_wildcard(void)
> > selinux_state.policycap[POLICYDB_CAP_NETIF_WILDCARD]);
> > }
> >
> > +static inline bool selinux_policycap_bpf_token_perms(void)
> > +{
> > + return READ_ONCE(
> > + selinux_state.policycap[POLICYDB_CAP_BPF_TOKEN_PERMS]);
> > +}
> > +
> > struct selinux_policy_convert_data;
> >
> > struct selinux_load_state {
> > --
> > 2.50.1.windows.1
> >
> >
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] SELinux: Add support for BPF token access control
2025-08-06 18:01 [PATCH] SELinux: Add support for BPF token access control ericsu
2025-08-06 18:33 ` Stephen Smalley
@ 2025-08-07 18:00 ` Daniel Durning
2025-08-11 19:30 ` Eric Suen
1 sibling, 1 reply; 15+ messages in thread
From: Daniel Durning @ 2025-08-07 18:00 UTC (permalink / raw)
To: ericsu; +Cc: selinux, paul
On Wed, Aug 6, 2025 at 2:07 PM <ericsu@linux.microsoft.com> wrote:
>
> From: Eric Suen <ericsu@linux.microsoft.com>
>
> BPF token support was introduced to allow a privileged process to delegate
> limited BPF functionality—such as map creation and program loading—to an
> unprivileged process:
> https://lore.kernel.org/linux-security-module/20231130185229.2688956-1-andrii@kernel.org/
>
> This patch adds SELinux support for controlling BPF token access. With
> this change, SELinux policies can now enforce constraints on BPF token
> usage based on both the delegating (privileged) process and the recipient
> (unprivileged) process.
>
> Supported operations currently include:
> - map_create
> - prog_load
>
> High-level workflow:
> 1. An unprivileged process creates a VFS context via `fsopen()` and
> obtains a file descriptor.
> 2. This descriptor is passed to a privileged process, which configures
> BPF token delegation options and mounts a BPF filesystem.
> 3. SELinux records the `creator_sid` of the privileged process during
> mount setup.
> 4. The unprivileged process then uses this BPF fs mount to create a
> token and attach it to subsequent BPF syscalls.
> 5. During verification of `map_create` and `prog_load`, SELinux uses
> `creator_sid` and the current SID to check policy permissions via:
> avc_has_perm(creator_sid, current_sid, SECCLASS_BPF,
> BPF__MAP_CREATE, NULL);
>
> To verify the logic introduced by this patch, my fork of the SELinux
> testsuite with relevant test cases is available here:
> https://github.com/havefuncoding1/selinux-testsuite
>
I like your approach. I have added some comments below. I think it
would be worth implementing and testing the bpf_token_capable hook in
your patch as well. You are welcome to reference my implementation.
> Signed-off-by: Eric Suen <ericsu@linux.microsoft.com>
> ---
> security/selinux/hooks.c | 107 ++++++++++++++++++++-
> security/selinux/include/classmap.h | 2 +-
> security/selinux/include/objsec.h | 4 +
> security/selinux/include/policycap.h | 1 +
> security/selinux/include/policycap_names.h | 1 +
> security/selinux/include/security.h | 6 ++
> 6 files changed, 118 insertions(+), 3 deletions(-)
>
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 335fbf76cdd2..ef9771542737 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -733,6 +733,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
> goto out;
> }
>
> + sbsec->creator_sid = current_sid();
> +
> if (strcmp(sb->s_type->name, "proc") == 0)
> sbsec->flags |= SE_SBPROC | SE_SBGENFS;
>
> @@ -7002,9 +7004,13 @@ static int selinux_ib_alloc_security(void *ib_sec)
> static int selinux_bpf(int cmd, union bpf_attr *attr,
> unsigned int size, bool kernel)
> {
> + bool bpf_token_perms = selinux_policycap_bpf_token_perms();
> u32 sid = current_sid();
> int ret;
>
> + if (bpf_token_perms)
> + return 0;
Why are you just returning 0 here instead of checking the permissions
of the token regardless? Since you appear to do that in
selinux_bpf_token_create, selinux_bpf_map_create and
selinux_bpf_prog_load.
> +
> switch (cmd) {
> case BPF_MAP_CREATE:
> ret = avc_has_perm(sid, sid, SECCLASS_BPF, BPF__MAP_CREATE,
> @@ -7086,10 +7092,34 @@ static int selinux_bpf_prog(struct bpf_prog *prog)
> BPF__PROG_RUN, NULL);
> }
>
> +static int selinux_bpffs_creator_sid(const union bpf_attr *attr)
> +{
> + struct path path;
> + struct super_block *sb;
> + struct superblock_security_struct *sbsec;
> +
> + CLASS(fd, f)(attr->token_create.bpffs_fd);
> +
> + if (!fd_file(f))
> + return SECSID_NULL;
Is it possible for this value to be null assuming there is not a bug
in the kernel? You would not want to do a runtime check for something
that would indicate a kernel bug lest you accidentally hide the bug.
> +
> + path = fd_file(f)->f_path;
> + sb = path.dentry->d_sb;
> + if (!sb)
> + return SECSID_NULL;
I do not think this value should ever be null barring a bug in the kernel.
> +
> + sbsec = selinux_superblock(sb);
> + if (!sbsec)
> + return SECSID_NULL;
Again, I do not think this value should ever be null barring a bug in
the kernel.
> +
> + return sbsec->creator_sid;
> +}
> +
> static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
> struct bpf_token *token, bool kernel)
> {
> struct bpf_security_struct *bpfsec;
> + u32 ssid;
>
> bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> if (!bpfsec)
> @@ -7098,7 +7128,15 @@ static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
> bpfsec->sid = current_sid();
> map->security = bpfsec;
>
> - return 0;
> + if (!token)
> + ssid = bpfsec->sid;
> + else {
> + ssid = selinux_bpffs_creator_sid(attr);
> + if (ssid == SECSID_NULL)
> + return -EPERM;
Should this ever be null? See above (for this and all repeated
occurrences of this check).
> + }
> +
> + return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__MAP_CREATE, NULL);
In the token case, why is this not already handled by selinux_bpf_token_create?
> }
>
> static void selinux_bpf_map_free(struct bpf_map *map)
> @@ -7113,6 +7151,7 @@ static int selinux_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
> struct bpf_token *token, bool kernel)
> {
> struct bpf_security_struct *bpfsec;
> + u32 ssid;
>
> bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> if (!bpfsec)
> @@ -7121,7 +7160,15 @@ static int selinux_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
> bpfsec->sid = current_sid();
> prog->aux->security = bpfsec;
>
> - return 0;
> + if (!token)
> + ssid = bpfsec->sid;
> + if (token) {
> + ssid = selinux_bpffs_creator_sid(attr);
> + if (ssid == SECSID_NULL)
> + return -EPERM;
> + }
> +
> + return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__PROG_LOAD, NULL);
> }
>
> static void selinux_bpf_prog_free(struct bpf_prog *prog)
> @@ -7132,10 +7179,18 @@ static void selinux_bpf_prog_free(struct bpf_prog *prog)
> kfree(bpfsec);
> }
>
> +#define bpf_token_cmd(T, C) \
> + ((T)->allowed_cmds & (1ULL << (C)))
> +
> static int selinux_bpf_token_create(struct bpf_token *token, union bpf_attr *attr,
> const struct path *path)
> {
> struct bpf_security_struct *bpfsec;
> + u32 sid = selinux_bpffs_creator_sid(attr);
> + int err;
> +
> + if (sid == SECSID_NULL)
> + return -EPERM;
>
> bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> if (!bpfsec)
> @@ -7144,6 +7199,29 @@ static int selinux_bpf_token_create(struct bpf_token *token, union bpf_attr *att
> bpfsec->sid = current_sid();
> token->security = bpfsec;
>
> + bpfsec->perms = 0;
> +
> + /**
> + * 'token->allowed_cmds' is a bit mask of allowed commands
> + * Convert the BPF command enum to a bitmask representing its position in the
> + * allowed_cmds bitmap.
> + */
> + if (bpf_token_cmd(token, BPF_MAP_CREATE)) {
> + err = avc_has_perm(bpfsec->sid, sid, SECCLASS_BPF, BPF__MAP_CREATE_AS, NULL);
> + if (err)
> + return err;
> +
> + bpfsec->perms |= BPF__MAP_CREATE;
> + }
> +
> + if (bpf_token_cmd(token, BPF_PROG_LOAD)) {
> + err = avc_has_perm(bpfsec->sid, sid, SECCLASS_BPF, BPF__PROG_LOAD_AS, NULL);
> + if (err)
> + return err;
> +
> + bpfsec->perms |= BPF__PROG_LOAD;
> + }
> +
> return 0;
> }
>
> @@ -7154,6 +7232,30 @@ static void selinux_bpf_token_free(struct bpf_token *token)
> token->security = NULL;
> kfree(bpfsec);
> }
> +
> +static int selinux_bpf_token_cmd(const struct bpf_token *token, enum bpf_cmd cmd)
> +{
> + struct bpf_security_struct *bpfsec;
> +
> + if (!token || !token->security)
> + return -EINVAL;
Not sure if these should ever be null barring a kernel bug either.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] SELinux: Add support for BPF token access control
2025-08-07 13:46 ` Stephen Smalley
@ 2025-08-08 18:57 ` Eric Suen
2025-08-08 19:08 ` Stephen Smalley
2025-08-11 17:47 ` Paul Moore
1 sibling, 1 reply; 15+ messages in thread
From: Eric Suen @ 2025-08-08 18:57 UTC (permalink / raw)
To: Stephen Smalley, danieldurning.work; +Cc: selinux, paul
On 8/7/2025 6:46 AM, Stephen Smalley wrote:
> On Wed, Aug 6, 2025 at 2:33 PM Stephen Smalley
> <stephen.smalley.work@gmail.com> wrote:
>> On Wed, Aug 6, 2025 at 2:07 PM <ericsu@linux.microsoft.com> wrote:
>>> From: Eric Suen <ericsu@linux.microsoft.com>
>>>
>>> BPF token support was introduced to allow a privileged process to delegate
>>> limited BPF functionality—such as map creation and program loading—to an
>>> unprivileged process:
>>> https://lore.kernel.org/linux-security-module/20231130185229.2688956-1-andrii@kernel.org/
>>>
>>> This patch adds SELinux support for controlling BPF token access. With
>>> this change, SELinux policies can now enforce constraints on BPF token
>>> usage based on both the delegating (privileged) process and the recipient
>>> (unprivileged) process.
>>>
>>> Supported operations currently include:
>>> - map_create
>>> - prog_load
>>>
>>> High-level workflow:
>>> 1. An unprivileged process creates a VFS context via `fsopen()` and
>>> obtains a file descriptor.
>>> 2. This descriptor is passed to a privileged process, which configures
>>> BPF token delegation options and mounts a BPF filesystem.
>>> 3. SELinux records the `creator_sid` of the privileged process during
>>> mount setup.
>>> 4. The unprivileged process then uses this BPF fs mount to create a
>>> token and attach it to subsequent BPF syscalls.
>>> 5. During verification of `map_create` and `prog_load`, SELinux uses
>>> `creator_sid` and the current SID to check policy permissions via:
>>> avc_has_perm(creator_sid, current_sid, SECCLASS_BPF,
>>> BPF__MAP_CREATE, NULL);
>>>
>>> To verify the logic introduced by this patch, my fork of the SELinux
>>> testsuite with relevant test cases is available here:
>>> https://github.com/havefuncoding1/selinux-testsuite
>> Interesting approach. Added Daniel to the distribution. Can you please
>> also post your testsuite patch to the list for review as per
>> https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#submitting-a-testsuite-patch
> Also, since you are introducing new permissions and a policy
> capability, please include instructions in the commit description for
> running your testsuite, see
> https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#add-new-permissions
> and
> https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#adding-a-new-selinux-policy-capability
> for instructions and links to example previous commits.
Hi Stephen, just posted patch for user space capability and it's
available here
https://lore.kernel.org/selinux/20250808183506.665-1-ericsu@linux.microsoft.com/
also posted patch for the testsuite
https://lore.kernel.org/selinux/20250808184711.291-1-ericsu@linux.microsoft.com/,
and yes I ran the tests with policy cap enabled in my environment.
>>> Signed-off-by: Eric Suen <ericsu@linux.microsoft.com>
>>> ---
>>> security/selinux/hooks.c | 107 ++++++++++++++++++++-
>>> security/selinux/include/classmap.h | 2 +-
>>> security/selinux/include/objsec.h | 4 +
>>> security/selinux/include/policycap.h | 1 +
>>> security/selinux/include/policycap_names.h | 1 +
>>> security/selinux/include/security.h | 6 ++
>>> 6 files changed, 118 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
>>> index 335fbf76cdd2..ef9771542737 100644
>>> --- a/security/selinux/hooks.c
>>> +++ b/security/selinux/hooks.c
>>> @@ -733,6 +733,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
>>> goto out;
>>> }
>>>
>>> + sbsec->creator_sid = current_sid();
>>> +
>>> if (strcmp(sb->s_type->name, "proc") == 0)
>>> sbsec->flags |= SE_SBPROC | SE_SBGENFS;
>>>
>>> @@ -7002,9 +7004,13 @@ static int selinux_ib_alloc_security(void *ib_sec)
>>> static int selinux_bpf(int cmd, union bpf_attr *attr,
>>> unsigned int size, bool kernel)
>>> {
>>> + bool bpf_token_perms = selinux_policycap_bpf_token_perms();
>>> u32 sid = current_sid();
>>> int ret;
>>>
>>> + if (bpf_token_perms)
>>> + return 0;
>>> +
>>> switch (cmd) {
>>> case BPF_MAP_CREATE:
>>> ret = avc_has_perm(sid, sid, SECCLASS_BPF, BPF__MAP_CREATE,
>>> @@ -7086,10 +7092,34 @@ static int selinux_bpf_prog(struct bpf_prog *prog)
>>> BPF__PROG_RUN, NULL);
>>> }
>>>
>>> +static int selinux_bpffs_creator_sid(const union bpf_attr *attr)
>>> +{
>>> + struct path path;
>>> + struct super_block *sb;
>>> + struct superblock_security_struct *sbsec;
>>> +
>>> + CLASS(fd, f)(attr->token_create.bpffs_fd);
>>> +
>>> + if (!fd_file(f))
>>> + return SECSID_NULL;
>>> +
>>> + path = fd_file(f)->f_path;
>>> + sb = path.dentry->d_sb;
>>> + if (!sb)
>>> + return SECSID_NULL;
>>> +
>>> + sbsec = selinux_superblock(sb);
>>> + if (!sbsec)
>>> + return SECSID_NULL;
>>> +
>>> + return sbsec->creator_sid;
>>> +}
>>> +
>>> static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
>>> struct bpf_token *token, bool kernel)
>>> {
>>> struct bpf_security_struct *bpfsec;
>>> + u32 ssid;
>>>
>>> bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
>>> if (!bpfsec)
>>> @@ -7098,7 +7128,15 @@ static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
>>> bpfsec->sid = current_sid();
>>> map->security = bpfsec;
>>>
>>> - return 0;
>>> + if (!token)
>>> + ssid = bpfsec->sid;
>>> + else {
>>> + ssid = selinux_bpffs_creator_sid(attr);
>>> + if (ssid == SECSID_NULL)
>>> + return -EPERM;
>>> + }
>>> +
>>> + return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__MAP_CREATE, NULL);
>>> }
>>>
>>> static void selinux_bpf_map_free(struct bpf_map *map)
>>> @@ -7113,6 +7151,7 @@ static int selinux_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
>>> struct bpf_token *token, bool kernel)
>>> {
>>> struct bpf_security_struct *bpfsec;
>>> + u32 ssid;
>>>
>>> bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
>>> if (!bpfsec)
>>> @@ -7121,7 +7160,15 @@ static int selinux_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
>>> bpfsec->sid = current_sid();
>>> prog->aux->security = bpfsec;
>>>
>>> - return 0;
>>> + if (!token)
>>> + ssid = bpfsec->sid;
>>> + if (token) {
>>> + ssid = selinux_bpffs_creator_sid(attr);
>>> + if (ssid == SECSID_NULL)
>>> + return -EPERM;
>>> + }
>>> +
>>> + return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__PROG_LOAD, NULL);
>>> }
>>>
>>> static void selinux_bpf_prog_free(struct bpf_prog *prog)
>>> @@ -7132,10 +7179,18 @@ static void selinux_bpf_prog_free(struct bpf_prog *prog)
>>> kfree(bpfsec);
>>> }
>>>
>>> +#define bpf_token_cmd(T, C) \
>>> + ((T)->allowed_cmds & (1ULL << (C)))
>>> +
>>> static int selinux_bpf_token_create(struct bpf_token *token, union bpf_attr *attr,
>>> const struct path *path)
>>> {
>>> struct bpf_security_struct *bpfsec;
>>> + u32 sid = selinux_bpffs_creator_sid(attr);
>>> + int err;
>>> +
>>> + if (sid == SECSID_NULL)
>>> + return -EPERM;
>>>
>>> bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
>>> if (!bpfsec)
>>> @@ -7144,6 +7199,29 @@ static int selinux_bpf_token_create(struct bpf_token *token, union bpf_attr *att
>>> bpfsec->sid = current_sid();
>>> token->security = bpfsec;
>>>
>>> + bpfsec->perms = 0;
>>> +
>>> + /**
>>> + * 'token->allowed_cmds' is a bit mask of allowed commands
>>> + * Convert the BPF command enum to a bitmask representing its position in the
>>> + * allowed_cmds bitmap.
>>> + */
>>> + if (bpf_token_cmd(token, BPF_MAP_CREATE)) {
>>> + err = avc_has_perm(bpfsec->sid, sid, SECCLASS_BPF, BPF__MAP_CREATE_AS, NULL);
>>> + if (err)
>>> + return err;
>>> +
>>> + bpfsec->perms |= BPF__MAP_CREATE;
>>> + }
>>> +
>>> + if (bpf_token_cmd(token, BPF_PROG_LOAD)) {
>>> + err = avc_has_perm(bpfsec->sid, sid, SECCLASS_BPF, BPF__PROG_LOAD_AS, NULL);
>>> + if (err)
>>> + return err;
>>> +
>>> + bpfsec->perms |= BPF__PROG_LOAD;
>>> + }
>>> +
>>> return 0;
>>> }
>>>
>>> @@ -7154,6 +7232,30 @@ static void selinux_bpf_token_free(struct bpf_token *token)
>>> token->security = NULL;
>>> kfree(bpfsec);
>>> }
>>> +
>>> +static int selinux_bpf_token_cmd(const struct bpf_token *token, enum bpf_cmd cmd)
>>> +{
>>> + struct bpf_security_struct *bpfsec;
>>> +
>>> + if (!token || !token->security)
>>> + return -EINVAL;
>>> +
>>> + bpfsec = token->security;
>>> + switch (cmd) {
>>> + case BPF_MAP_CREATE:
>>> + if ((bpfsec->perms & BPF__MAP_CREATE) != BPF__MAP_CREATE)
>>> + return -EACCES;
>>> + break;
>>> + case BPF_PROG_LOAD:
>>> + if ((bpfsec->perms & BPF__PROG_LOAD) != BPF__PROG_LOAD)
>>> + return -EACCES;
>>> + break;
>>> + default:
>>> + break;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> #endif
>>>
>>> struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = {
>>> @@ -7585,6 +7687,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
>>> LSM_HOOK_INIT(bpf_map_create, selinux_bpf_map_create),
>>> LSM_HOOK_INIT(bpf_prog_load, selinux_bpf_prog_load),
>>> LSM_HOOK_INIT(bpf_token_create, selinux_bpf_token_create),
>>> + LSM_HOOK_INIT(bpf_token_cmd, selinux_bpf_token_cmd),
>>> #endif
>>> #ifdef CONFIG_PERF_EVENTS
>>> LSM_HOOK_INIT(perf_event_alloc, selinux_perf_event_alloc),
>>> diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
>>> index 5665aa5e7853..a6ed864af64c 100644
>>> --- a/security/selinux/include/classmap.h
>>> +++ b/security/selinux/include/classmap.h
>>> @@ -171,7 +171,7 @@ const struct security_class_mapping secclass_map[] = {
>>> { "infiniband_endport", { "manage_subnet", NULL } },
>>> { "bpf",
>>> { "map_create", "map_read", "map_write", "prog_load", "prog_run",
>>> - NULL } },
>>> + "map_create_as", "prog_load_as", NULL } },
>>> { "xdp_socket", { COMMON_SOCK_PERMS, NULL } },
>>> { "mctp_socket", { COMMON_SOCK_PERMS, NULL } },
>>> { "perf_event",
>>> diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
>>> index 1d7ac59015a1..b7e55e5c6d9c 100644
>>> --- a/security/selinux/include/objsec.h
>>> +++ b/security/selinux/include/objsec.h
>>> @@ -87,6 +87,8 @@ struct superblock_security_struct {
>>> u32 sid; /* SID of file system superblock */
>>> u32 def_sid; /* default SID for labeling */
>>> u32 mntpoint_sid; /* SECURITY_FS_USE_MNTPOINT context for files */
>>> + u32 creator_sid; /* SID of privileged process and is used to */
>>> + /* verify bpf operations */
>>> unsigned short behavior; /* labeling behavior */
>>> unsigned short flags; /* which mount options were specified */
>>> struct mutex lock;
>>> @@ -164,6 +166,8 @@ struct pkey_security_struct {
>>>
>>> struct bpf_security_struct {
>>> u32 sid; /* SID of bpf obj creator */
>>> + u32 perms; /* allowed AV permissions, e.g. BPF__MAP_CREATE, */
>>> + /* for requested bpf commands */
>>> };
>>>
>>> struct perf_event_security_struct {
>>> diff --git a/security/selinux/include/policycap.h b/security/selinux/include/policycap.h
>>> index 7405154e6c42..cde6aaf442cd 100644
>>> --- a/security/selinux/include/policycap.h
>>> +++ b/security/selinux/include/policycap.h
>>> @@ -17,6 +17,7 @@ enum {
>>> POLICYDB_CAP_NETLINK_XPERM,
>>> POLICYDB_CAP_NETIF_WILDCARD,
>>> POLICYDB_CAP_GENFS_SECLABEL_WILDCARD,
>>> + POLICYDB_CAP_BPF_TOKEN_PERMS,
>>> __POLICYDB_CAP_MAX
>>> };
>>> #define POLICYDB_CAP_MAX (__POLICYDB_CAP_MAX - 1)
>>> diff --git a/security/selinux/include/policycap_names.h b/security/selinux/include/policycap_names.h
>>> index d8962fcf2ff9..cd5e73f992ea 100644
>>> --- a/security/selinux/include/policycap_names.h
>>> +++ b/security/selinux/include/policycap_names.h
>>> @@ -20,6 +20,7 @@ const char *const selinux_policycap_names[__POLICYDB_CAP_MAX] = {
>>> "netlink_xperm",
>>> "netif_wildcard",
>>> "genfs_seclabel_wildcard",
>>> + "bpf_token_perms",
>>> };
>>> /* clang-format on */
>>>
>>> diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
>>> index 8201e6a3ac0f..d3832e4ad4fb 100644
>>> --- a/security/selinux/include/security.h
>>> +++ b/security/selinux/include/security.h
>>> @@ -209,6 +209,12 @@ static inline bool selinux_policycap_netif_wildcard(void)
>>> selinux_state.policycap[POLICYDB_CAP_NETIF_WILDCARD]);
>>> }
>>>
>>> +static inline bool selinux_policycap_bpf_token_perms(void)
>>> +{
>>> + return READ_ONCE(
>>> + selinux_state.policycap[POLICYDB_CAP_BPF_TOKEN_PERMS]);
>>> +}
>>> +
>>> struct selinux_policy_convert_data;
>>>
>>> struct selinux_load_state {
>>> --
>>> 2.50.1.windows.1
>>>
>>>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] SELinux: Add support for BPF token access control
2025-08-08 18:57 ` Eric Suen
@ 2025-08-08 19:08 ` Stephen Smalley
0 siblings, 0 replies; 15+ messages in thread
From: Stephen Smalley @ 2025-08-08 19:08 UTC (permalink / raw)
To: Eric Suen; +Cc: danieldurning.work, selinux, paul
On Fri, Aug 8, 2025 at 2:57 PM Eric Suen <ericsu@linux.microsoft.com> wrote:
>
> On 8/7/2025 6:46 AM, Stephen Smalley wrote:
> > On Wed, Aug 6, 2025 at 2:33 PM Stephen Smalley
> > <stephen.smalley.work@gmail.com> wrote:
> >> On Wed, Aug 6, 2025 at 2:07 PM <ericsu@linux.microsoft.com> wrote:
> >>> From: Eric Suen <ericsu@linux.microsoft.com>
> >>>
> >>> BPF token support was introduced to allow a privileged process to delegate
> >>> limited BPF functionality—such as map creation and program loading—to an
> >>> unprivileged process:
> >>> https://lore.kernel.org/linux-security-module/20231130185229.2688956-1-andrii@kernel.org/
> >>>
> >>> This patch adds SELinux support for controlling BPF token access. With
> >>> this change, SELinux policies can now enforce constraints on BPF token
> >>> usage based on both the delegating (privileged) process and the recipient
> >>> (unprivileged) process.
> >>>
> >>> Supported operations currently include:
> >>> - map_create
> >>> - prog_load
> >>>
> >>> High-level workflow:
> >>> 1. An unprivileged process creates a VFS context via `fsopen()` and
> >>> obtains a file descriptor.
> >>> 2. This descriptor is passed to a privileged process, which configures
> >>> BPF token delegation options and mounts a BPF filesystem.
> >>> 3. SELinux records the `creator_sid` of the privileged process during
> >>> mount setup.
> >>> 4. The unprivileged process then uses this BPF fs mount to create a
> >>> token and attach it to subsequent BPF syscalls.
> >>> 5. During verification of `map_create` and `prog_load`, SELinux uses
> >>> `creator_sid` and the current SID to check policy permissions via:
> >>> avc_has_perm(creator_sid, current_sid, SECCLASS_BPF,
> >>> BPF__MAP_CREATE, NULL);
> >>>
> >>> To verify the logic introduced by this patch, my fork of the SELinux
> >>> testsuite with relevant test cases is available here:
> >>> https://github.com/havefuncoding1/selinux-testsuite
> >> Interesting approach. Added Daniel to the distribution. Can you please
> >> also post your testsuite patch to the list for review as per
> >> https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#submitting-a-testsuite-patch
> > Also, since you are introducing new permissions and a policy
> > capability, please include instructions in the commit description for
> > running your testsuite, see
> > https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#add-new-permissions
> > and
> > https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#adding-a-new-selinux-policy-capability
> > for instructions and links to example previous commits.
>
> Hi Stephen, just posted patch for user space capability and it's
> available here
> https://lore.kernel.org/selinux/20250808183506.665-1-ericsu@linux.microsoft.com/
>
> also posted patch for the testsuite
> https://lore.kernel.org/selinux/20250808184711.291-1-ericsu@linux.microsoft.com/,
> and yes I ran the tests with policy cap enabled in my environment.
No need to re-submit the testsuite patch just for this, but on future
iterations, please include the instructions
in the commit message for how to temporarily enable the policy
capability and define the new permissions for
testing purposes, as shown in this example linked from the SELinux
kernel Getting Started wiki page:
https://github.com/SELinuxProject/selinux-testsuite/commit/023b79b8319e5fe222fb5af892c579593e1cbc50
>
> >>> Signed-off-by: Eric Suen <ericsu@linux.microsoft.com>
> >>> ---
> >>> security/selinux/hooks.c | 107 ++++++++++++++++++++-
> >>> security/selinux/include/classmap.h | 2 +-
> >>> security/selinux/include/objsec.h | 4 +
> >>> security/selinux/include/policycap.h | 1 +
> >>> security/selinux/include/policycap_names.h | 1 +
> >>> security/selinux/include/security.h | 6 ++
> >>> 6 files changed, 118 insertions(+), 3 deletions(-)
> >>>
> >>> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> >>> index 335fbf76cdd2..ef9771542737 100644
> >>> --- a/security/selinux/hooks.c
> >>> +++ b/security/selinux/hooks.c
> >>> @@ -733,6 +733,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
> >>> goto out;
> >>> }
> >>>
> >>> + sbsec->creator_sid = current_sid();
> >>> +
> >>> if (strcmp(sb->s_type->name, "proc") == 0)
> >>> sbsec->flags |= SE_SBPROC | SE_SBGENFS;
> >>>
> >>> @@ -7002,9 +7004,13 @@ static int selinux_ib_alloc_security(void *ib_sec)
> >>> static int selinux_bpf(int cmd, union bpf_attr *attr,
> >>> unsigned int size, bool kernel)
> >>> {
> >>> + bool bpf_token_perms = selinux_policycap_bpf_token_perms();
> >>> u32 sid = current_sid();
> >>> int ret;
> >>>
> >>> + if (bpf_token_perms)
> >>> + return 0;
> >>> +
> >>> switch (cmd) {
> >>> case BPF_MAP_CREATE:
> >>> ret = avc_has_perm(sid, sid, SECCLASS_BPF, BPF__MAP_CREATE,
> >>> @@ -7086,10 +7092,34 @@ static int selinux_bpf_prog(struct bpf_prog *prog)
> >>> BPF__PROG_RUN, NULL);
> >>> }
> >>>
> >>> +static int selinux_bpffs_creator_sid(const union bpf_attr *attr)
> >>> +{
> >>> + struct path path;
> >>> + struct super_block *sb;
> >>> + struct superblock_security_struct *sbsec;
> >>> +
> >>> + CLASS(fd, f)(attr->token_create.bpffs_fd);
> >>> +
> >>> + if (!fd_file(f))
> >>> + return SECSID_NULL;
> >>> +
> >>> + path = fd_file(f)->f_path;
> >>> + sb = path.dentry->d_sb;
> >>> + if (!sb)
> >>> + return SECSID_NULL;
> >>> +
> >>> + sbsec = selinux_superblock(sb);
> >>> + if (!sbsec)
> >>> + return SECSID_NULL;
> >>> +
> >>> + return sbsec->creator_sid;
> >>> +}
> >>> +
> >>> static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
> >>> struct bpf_token *token, bool kernel)
> >>> {
> >>> struct bpf_security_struct *bpfsec;
> >>> + u32 ssid;
> >>>
> >>> bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> >>> if (!bpfsec)
> >>> @@ -7098,7 +7128,15 @@ static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
> >>> bpfsec->sid = current_sid();
> >>> map->security = bpfsec;
> >>>
> >>> - return 0;
> >>> + if (!token)
> >>> + ssid = bpfsec->sid;
> >>> + else {
> >>> + ssid = selinux_bpffs_creator_sid(attr);
> >>> + if (ssid == SECSID_NULL)
> >>> + return -EPERM;
> >>> + }
> >>> +
> >>> + return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__MAP_CREATE, NULL);
> >>> }
> >>>
> >>> static void selinux_bpf_map_free(struct bpf_map *map)
> >>> @@ -7113,6 +7151,7 @@ static int selinux_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
> >>> struct bpf_token *token, bool kernel)
> >>> {
> >>> struct bpf_security_struct *bpfsec;
> >>> + u32 ssid;
> >>>
> >>> bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> >>> if (!bpfsec)
> >>> @@ -7121,7 +7160,15 @@ static int selinux_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
> >>> bpfsec->sid = current_sid();
> >>> prog->aux->security = bpfsec;
> >>>
> >>> - return 0;
> >>> + if (!token)
> >>> + ssid = bpfsec->sid;
> >>> + if (token) {
> >>> + ssid = selinux_bpffs_creator_sid(attr);
> >>> + if (ssid == SECSID_NULL)
> >>> + return -EPERM;
> >>> + }
> >>> +
> >>> + return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__PROG_LOAD, NULL);
> >>> }
> >>>
> >>> static void selinux_bpf_prog_free(struct bpf_prog *prog)
> >>> @@ -7132,10 +7179,18 @@ static void selinux_bpf_prog_free(struct bpf_prog *prog)
> >>> kfree(bpfsec);
> >>> }
> >>>
> >>> +#define bpf_token_cmd(T, C) \
> >>> + ((T)->allowed_cmds & (1ULL << (C)))
> >>> +
> >>> static int selinux_bpf_token_create(struct bpf_token *token, union bpf_attr *attr,
> >>> const struct path *path)
> >>> {
> >>> struct bpf_security_struct *bpfsec;
> >>> + u32 sid = selinux_bpffs_creator_sid(attr);
> >>> + int err;
> >>> +
> >>> + if (sid == SECSID_NULL)
> >>> + return -EPERM;
> >>>
> >>> bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> >>> if (!bpfsec)
> >>> @@ -7144,6 +7199,29 @@ static int selinux_bpf_token_create(struct bpf_token *token, union bpf_attr *att
> >>> bpfsec->sid = current_sid();
> >>> token->security = bpfsec;
> >>>
> >>> + bpfsec->perms = 0;
> >>> +
> >>> + /**
> >>> + * 'token->allowed_cmds' is a bit mask of allowed commands
> >>> + * Convert the BPF command enum to a bitmask representing its position in the
> >>> + * allowed_cmds bitmap.
> >>> + */
> >>> + if (bpf_token_cmd(token, BPF_MAP_CREATE)) {
> >>> + err = avc_has_perm(bpfsec->sid, sid, SECCLASS_BPF, BPF__MAP_CREATE_AS, NULL);
> >>> + if (err)
> >>> + return err;
> >>> +
> >>> + bpfsec->perms |= BPF__MAP_CREATE;
> >>> + }
> >>> +
> >>> + if (bpf_token_cmd(token, BPF_PROG_LOAD)) {
> >>> + err = avc_has_perm(bpfsec->sid, sid, SECCLASS_BPF, BPF__PROG_LOAD_AS, NULL);
> >>> + if (err)
> >>> + return err;
> >>> +
> >>> + bpfsec->perms |= BPF__PROG_LOAD;
> >>> + }
> >>> +
> >>> return 0;
> >>> }
> >>>
> >>> @@ -7154,6 +7232,30 @@ static void selinux_bpf_token_free(struct bpf_token *token)
> >>> token->security = NULL;
> >>> kfree(bpfsec);
> >>> }
> >>> +
> >>> +static int selinux_bpf_token_cmd(const struct bpf_token *token, enum bpf_cmd cmd)
> >>> +{
> >>> + struct bpf_security_struct *bpfsec;
> >>> +
> >>> + if (!token || !token->security)
> >>> + return -EINVAL;
> >>> +
> >>> + bpfsec = token->security;
> >>> + switch (cmd) {
> >>> + case BPF_MAP_CREATE:
> >>> + if ((bpfsec->perms & BPF__MAP_CREATE) != BPF__MAP_CREATE)
> >>> + return -EACCES;
> >>> + break;
> >>> + case BPF_PROG_LOAD:
> >>> + if ((bpfsec->perms & BPF__PROG_LOAD) != BPF__PROG_LOAD)
> >>> + return -EACCES;
> >>> + break;
> >>> + default:
> >>> + break;
> >>> + }
> >>> +
> >>> + return 0;
> >>> +}
> >>> #endif
> >>>
> >>> struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = {
> >>> @@ -7585,6 +7687,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
> >>> LSM_HOOK_INIT(bpf_map_create, selinux_bpf_map_create),
> >>> LSM_HOOK_INIT(bpf_prog_load, selinux_bpf_prog_load),
> >>> LSM_HOOK_INIT(bpf_token_create, selinux_bpf_token_create),
> >>> + LSM_HOOK_INIT(bpf_token_cmd, selinux_bpf_token_cmd),
> >>> #endif
> >>> #ifdef CONFIG_PERF_EVENTS
> >>> LSM_HOOK_INIT(perf_event_alloc, selinux_perf_event_alloc),
> >>> diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
> >>> index 5665aa5e7853..a6ed864af64c 100644
> >>> --- a/security/selinux/include/classmap.h
> >>> +++ b/security/selinux/include/classmap.h
> >>> @@ -171,7 +171,7 @@ const struct security_class_mapping secclass_map[] = {
> >>> { "infiniband_endport", { "manage_subnet", NULL } },
> >>> { "bpf",
> >>> { "map_create", "map_read", "map_write", "prog_load", "prog_run",
> >>> - NULL } },
> >>> + "map_create_as", "prog_load_as", NULL } },
> >>> { "xdp_socket", { COMMON_SOCK_PERMS, NULL } },
> >>> { "mctp_socket", { COMMON_SOCK_PERMS, NULL } },
> >>> { "perf_event",
> >>> diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
> >>> index 1d7ac59015a1..b7e55e5c6d9c 100644
> >>> --- a/security/selinux/include/objsec.h
> >>> +++ b/security/selinux/include/objsec.h
> >>> @@ -87,6 +87,8 @@ struct superblock_security_struct {
> >>> u32 sid; /* SID of file system superblock */
> >>> u32 def_sid; /* default SID for labeling */
> >>> u32 mntpoint_sid; /* SECURITY_FS_USE_MNTPOINT context for files */
> >>> + u32 creator_sid; /* SID of privileged process and is used to */
> >>> + /* verify bpf operations */
> >>> unsigned short behavior; /* labeling behavior */
> >>> unsigned short flags; /* which mount options were specified */
> >>> struct mutex lock;
> >>> @@ -164,6 +166,8 @@ struct pkey_security_struct {
> >>>
> >>> struct bpf_security_struct {
> >>> u32 sid; /* SID of bpf obj creator */
> >>> + u32 perms; /* allowed AV permissions, e.g. BPF__MAP_CREATE, */
> >>> + /* for requested bpf commands */
> >>> };
> >>>
> >>> struct perf_event_security_struct {
> >>> diff --git a/security/selinux/include/policycap.h b/security/selinux/include/policycap.h
> >>> index 7405154e6c42..cde6aaf442cd 100644
> >>> --- a/security/selinux/include/policycap.h
> >>> +++ b/security/selinux/include/policycap.h
> >>> @@ -17,6 +17,7 @@ enum {
> >>> POLICYDB_CAP_NETLINK_XPERM,
> >>> POLICYDB_CAP_NETIF_WILDCARD,
> >>> POLICYDB_CAP_GENFS_SECLABEL_WILDCARD,
> >>> + POLICYDB_CAP_BPF_TOKEN_PERMS,
> >>> __POLICYDB_CAP_MAX
> >>> };
> >>> #define POLICYDB_CAP_MAX (__POLICYDB_CAP_MAX - 1)
> >>> diff --git a/security/selinux/include/policycap_names.h b/security/selinux/include/policycap_names.h
> >>> index d8962fcf2ff9..cd5e73f992ea 100644
> >>> --- a/security/selinux/include/policycap_names.h
> >>> +++ b/security/selinux/include/policycap_names.h
> >>> @@ -20,6 +20,7 @@ const char *const selinux_policycap_names[__POLICYDB_CAP_MAX] = {
> >>> "netlink_xperm",
> >>> "netif_wildcard",
> >>> "genfs_seclabel_wildcard",
> >>> + "bpf_token_perms",
> >>> };
> >>> /* clang-format on */
> >>>
> >>> diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
> >>> index 8201e6a3ac0f..d3832e4ad4fb 100644
> >>> --- a/security/selinux/include/security.h
> >>> +++ b/security/selinux/include/security.h
> >>> @@ -209,6 +209,12 @@ static inline bool selinux_policycap_netif_wildcard(void)
> >>> selinux_state.policycap[POLICYDB_CAP_NETIF_WILDCARD]);
> >>> }
> >>>
> >>> +static inline bool selinux_policycap_bpf_token_perms(void)
> >>> +{
> >>> + return READ_ONCE(
> >>> + selinux_state.policycap[POLICYDB_CAP_BPF_TOKEN_PERMS]);
> >>> +}
> >>> +
> >>> struct selinux_policy_convert_data;
> >>>
> >>> struct selinux_load_state {
> >>> --
> >>> 2.50.1.windows.1
> >>>
> >>>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] SELinux: Add support for BPF token access control
2025-08-07 13:46 ` Stephen Smalley
2025-08-08 18:57 ` Eric Suen
@ 2025-08-11 17:47 ` Paul Moore
2025-08-11 18:13 ` Stephen Smalley
1 sibling, 1 reply; 15+ messages in thread
From: Paul Moore @ 2025-08-11 17:47 UTC (permalink / raw)
To: Stephen Smalley; +Cc: ericsu, danieldurning.work, selinux
On Thu, Aug 7, 2025 at 9:46 AM Stephen Smalley
<stephen.smalley.work@gmail.com> wrote:
>
> Also, since you are introducing new permissions and a policy
> capability, please include instructions in the commit description for
> running your testsuite, see
> https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#add-new-permissions
> and
> https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#adding-a-new-selinux-policy-capability
> for instructions and links to example previous commits.
I think it's fair to simply call out the new permissions and policy
capability in the patch's description along with a simple explanation
that the new behavior is gated on the new policy capability.
Including instructions on how to enable a policy capability is
something that I think we can consider "an exercise left to the
reader", with documentation located outside the patch description.
The unfortunate reality is that there is no single right way to add a
policy capability to a system, and those instructions which are distro
independent are likely to also clash with the distro supplied policy
packages.
Unfortunately, while the process around adding policy capabilities
have improved somewhat over the years, it's still and ugly thing to
have to do and I'm not sure a commit description is the best place to
document that process. I still have hope that some of the new policy
work will improve this somewhat.
--
paul-moore.com
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] SELinux: Add support for BPF token access control
2025-08-11 17:47 ` Paul Moore
@ 2025-08-11 18:13 ` Stephen Smalley
2025-08-11 20:33 ` Paul Moore
0 siblings, 1 reply; 15+ messages in thread
From: Stephen Smalley @ 2025-08-11 18:13 UTC (permalink / raw)
To: Paul Moore; +Cc: ericsu, danieldurning.work, selinux
On Mon, Aug 11, 2025 at 1:47 PM Paul Moore <paul@paul-moore.com> wrote:
>
> On Thu, Aug 7, 2025 at 9:46 AM Stephen Smalley
> <stephen.smalley.work@gmail.com> wrote:
> >
> > Also, since you are introducing new permissions and a policy
> > capability, please include instructions in the commit description for
> > running your testsuite, see
> > https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#add-new-permissions
> > and
> > https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#adding-a-new-selinux-policy-capability
> > for instructions and links to example previous commits.
>
> I think it's fair to simply call out the new permissions and policy
> capability in the patch's description along with a simple explanation
> that the new behavior is gated on the new policy capability.
> Including instructions on how to enable a policy capability is
> something that I think we can consider "an exercise left to the
> reader", with documentation located outside the patch description.
> The unfortunate reality is that there is no single right way to add a
> policy capability to a system, and those instructions which are distro
> independent are likely to also clash with the distro supplied policy
> packages.
>
> Unfortunately, while the process around adding policy capabilities
> have improved somewhat over the years, it's still and ugly thing to
> have to do and I'm not sure a commit description is the best place to
> document that process. I still have hope that some of the new policy
> work will improve this somewhat.
My request and the linked example I provide in the wiki page is to put
this information into the testsuite patch description, not the kernel
patch description, which I think is reasonable to reduce the burden on
testsuite maintainers.
The instructions in the linked example are distro-agnostic and just
leveraging a CIL module, so nothing specialized there.
At the very least, the specific name of the new policy capability and
the names of any new permissions and what classes they should be added
to needs to be clearly identified in the description.
I will not review a testsuite patch without this information.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] SELinux: Add support for BPF token access control
2025-08-07 18:00 ` Daniel Durning
@ 2025-08-11 19:30 ` Eric Suen
2025-08-12 12:27 ` Stephen Smalley
0 siblings, 1 reply; 15+ messages in thread
From: Eric Suen @ 2025-08-11 19:30 UTC (permalink / raw)
To: Daniel Durning; +Cc: selinux, paul
On 8/7/2025 11:00 AM, Daniel Durning wrote:
> On Wed, Aug 6, 2025 at 2:07 PM<ericsu@linux.microsoft.com> wrote:
>> From: Eric Suen<ericsu@linux.microsoft.com>
>>
>> BPF token support was introduced to allow a privileged process to delegate
>> limited BPF functionality—such as map creation and program loading—to an
>> unprivileged process:
>> https://lore.kernel.org/linux-security-module/20231130185229.2688956-1-andrii@kernel.org/
>>
>> This patch adds SELinux support for controlling BPF token access. With
>> this change, SELinux policies can now enforce constraints on BPF token
>> usage based on both the delegating (privileged) process and the recipient
>> (unprivileged) process.
>>
>> Supported operations currently include:
>> - map_create
>> - prog_load
>>
>> High-level workflow:
>> 1. An unprivileged process creates a VFS context via `fsopen()` and
>> obtains a file descriptor.
>> 2. This descriptor is passed to a privileged process, which configures
>> BPF token delegation options and mounts a BPF filesystem.
>> 3. SELinux records the `creator_sid` of the privileged process during
>> mount setup.
>> 4. The unprivileged process then uses this BPF fs mount to create a
>> token and attach it to subsequent BPF syscalls.
>> 5. During verification of `map_create` and `prog_load`, SELinux uses
>> `creator_sid` and the current SID to check policy permissions via:
>> avc_has_perm(creator_sid, current_sid, SECCLASS_BPF,
>> BPF__MAP_CREATE, NULL);
>>
>> To verify the logic introduced by this patch, my fork of the SELinux
>> testsuite with relevant test cases is available here:
>> https://github.com/havefuncoding1/selinux-testsuite
>>
> I like your approach. I have added some comments below. I think it
> would be worth implementing and testing the bpf_token_capable hook in
> your patch as well. You are welcome to reference my implementation.
Let me look into this first and we can have further discussion later.
>> Signed-off-by: Eric Suen<ericsu@linux.microsoft.com>
>> ---
>> security/selinux/hooks.c | 107 ++++++++++++++++++++-
>> security/selinux/include/classmap.h | 2 +-
>> security/selinux/include/objsec.h | 4 +
>> security/selinux/include/policycap.h | 1 +
>> security/selinux/include/policycap_names.h | 1 +
>> security/selinux/include/security.h | 6 ++
>> 6 files changed, 118 insertions(+), 3 deletions(-)
>>
>> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
>> index 335fbf76cdd2..ef9771542737 100644
>> --- a/security/selinux/hooks.c
>> +++ b/security/selinux/hooks.c
>> @@ -733,6 +733,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
>> goto out;
>> }
>>
>> + sbsec->creator_sid = current_sid();
>> +
>> if (strcmp(sb->s_type->name, "proc") == 0)
>> sbsec->flags |= SE_SBPROC | SE_SBGENFS;
>>
>> @@ -7002,9 +7004,13 @@ static int selinux_ib_alloc_security(void *ib_sec)
>> static int selinux_bpf(int cmd, union bpf_attr *attr,
>> unsigned int size, bool kernel)
>> {
>> + bool bpf_token_perms = selinux_policycap_bpf_token_perms();
>> u32 sid = current_sid();
>> int ret;
>>
>> + if (bpf_token_perms)
>> + return 0;
> Why are you just returning 0 here instead of checking the permissions
> of the token regardless? Since you appear to do that in
> selinux_bpf_token_create, selinux_bpf_map_create and
> selinux_bpf_prog_load.
in case of of token is present, source and target SIDs we use to check
for avc_has_perm are different. This is current design. Whether or not
it's appropriate, we can discuss. However, if we go with this approach,
then it's the best to delay the permission check when actual function is
called, i.e. selinux_bpf_map_create and selinux_bpf_pro_load.
>> +
>> switch (cmd) {
>> case BPF_MAP_CREATE:
>> ret = avc_has_perm(sid, sid, SECCLASS_BPF, BPF__MAP_CREATE,
>> @@ -7086,10 +7092,34 @@ static int selinux_bpf_prog(struct bpf_prog *prog)
>> BPF__PROG_RUN, NULL);
>> }
>>
>> +static int selinux_bpffs_creator_sid(const union bpf_attr *attr)
>> +{
>> + struct path path;
>> + struct super_block *sb;
>> + struct superblock_security_struct *sbsec;
>> +
>> + CLASS(fd, f)(attr->token_create.bpffs_fd);
>> +
>> + if (!fd_file(f))
>> + return SECSID_NULL;
> Is it possible for this value to be null assuming there is not a bug
> in the kernel? You would not want to do a runtime check for something
> that would indicate a kernel bug lest you accidentally hide the bug.
I always think being defensive is a good thing in kernel development.
But you have a valid point, and I will review and update in next
iteration if appropriate. Same applies to all similar comments below.
Thank you.
>
>> +
>> + path = fd_file(f)->f_path;
>> + sb = path.dentry->d_sb;
>> + if (!sb)
>> + return SECSID_NULL;
> I do not think this value should ever be null barring a bug in the kernel.
>
>> +
>> + sbsec = selinux_superblock(sb);
>> + if (!sbsec)
>> + return SECSID_NULL;
> Again, I do not think this value should ever be null barring a bug in
> the kernel.
>
>> +
>> + return sbsec->creator_sid;
>> +}
>> +
>> static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
>> struct bpf_token *token, bool kernel)
>> {
>> struct bpf_security_struct *bpfsec;
>> + u32 ssid;
>>
>> bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
>> if (!bpfsec)
>> @@ -7098,7 +7128,15 @@ static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
>> bpfsec->sid = current_sid();
>> map->security = bpfsec;
>>
>> - return 0;
>> + if (!token)
>> + ssid = bpfsec->sid;
>> + else {
>> + ssid = selinux_bpffs_creator_sid(attr);
>> + if (ssid == SECSID_NULL)
>> + return -EPERM;
> Should this ever be null? See above (for this and all repeated
> occurrences of this check).
>
>> + }
>> +
>> + return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__MAP_CREATE, NULL);
> In the token case, why is this not already handled by selinux_bpf_token_create?
It's because source and target sids are different. The setup is that a
privileged process configures BPF token delegations, so logically we
want to see if the privileged process (creator_sid) is allowed to
perform map_create on object with current_sid (current unprivileged
process).
In selinux_bpf_token_create, we make sure current process is allowed to
perform map_create_as on object with creator_sid. You are right, there
is no need to call avc_has_perm here if creator_sid and current_sid are
identical (which is true in my test). However, this is not the case in
real world scenarios.
>> }
>>
>> static void selinux_bpf_map_free(struct bpf_map *map)
>> @@ -7113,6 +7151,7 @@ static int selinux_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
>> struct bpf_token *token, bool kernel)
>> {
>> struct bpf_security_struct *bpfsec;
>> + u32 ssid;
>>
>> bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
>> if (!bpfsec)
>> @@ -7121,7 +7160,15 @@ static int selinux_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
>> bpfsec->sid = current_sid();
>> prog->aux->security = bpfsec;
>>
>> - return 0;
>> + if (!token)
>> + ssid = bpfsec->sid;
>> + if (token) {
>> + ssid = selinux_bpffs_creator_sid(attr);
>> + if (ssid == SECSID_NULL)
>> + return -EPERM;
>> + }
>> +
>> + return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__PROG_LOAD, NULL);
>> }
>>
>> static void selinux_bpf_prog_free(struct bpf_prog *prog)
>> @@ -7132,10 +7179,18 @@ static void selinux_bpf_prog_free(struct bpf_prog *prog)
>> kfree(bpfsec);
>> }
>>
>> +#define bpf_token_cmd(T, C) \
>> + ((T)->allowed_cmds & (1ULL << (C)))
>> +
>> static int selinux_bpf_token_create(struct bpf_token *token, union bpf_attr *attr,
>> const struct path *path)
>> {
>> struct bpf_security_struct *bpfsec;
>> + u32 sid = selinux_bpffs_creator_sid(attr);
>> + int err;
>> +
>> + if (sid == SECSID_NULL)
>> + return -EPERM;
>>
>> bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
>> if (!bpfsec)
>> @@ -7144,6 +7199,29 @@ static int selinux_bpf_token_create(struct bpf_token *token, union bpf_attr *att
>> bpfsec->sid = current_sid();
>> token->security = bpfsec;
>>
>> + bpfsec->perms = 0;
>> +
>> + /**
>> + * 'token->allowed_cmds' is a bit mask of allowed commands
>> + * Convert the BPF command enum to a bitmask representing its position in the
>> + * allowed_cmds bitmap.
>> + */
>> + if (bpf_token_cmd(token, BPF_MAP_CREATE)) {
>> + err = avc_has_perm(bpfsec->sid, sid, SECCLASS_BPF, BPF__MAP_CREATE_AS, NULL);
>> + if (err)
>> + return err;
>> +
>> + bpfsec->perms |= BPF__MAP_CREATE;
>> + }
>> +
>> + if (bpf_token_cmd(token, BPF_PROG_LOAD)) {
>> + err = avc_has_perm(bpfsec->sid, sid, SECCLASS_BPF, BPF__PROG_LOAD_AS, NULL);
>> + if (err)
>> + return err;
>> +
>> + bpfsec->perms |= BPF__PROG_LOAD;
>> + }
>> +
>> return 0;
>> }
>>
>> @@ -7154,6 +7232,30 @@ static void selinux_bpf_token_free(struct bpf_token *token)
>> token->security = NULL;
>> kfree(bpfsec);
>> }
>> +
>> +static int selinux_bpf_token_cmd(const struct bpf_token *token, enum bpf_cmd cmd)
>> +{
>> + struct bpf_security_struct *bpfsec;
>> +
>> + if (!token || !token->security)
>> + return -EINVAL;
> Not sure if these should ever be null barring a kernel bug either.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] SELinux: Add support for BPF token access control
2025-08-11 18:13 ` Stephen Smalley
@ 2025-08-11 20:33 ` Paul Moore
2025-08-12 12:12 ` Stephen Smalley
0 siblings, 1 reply; 15+ messages in thread
From: Paul Moore @ 2025-08-11 20:33 UTC (permalink / raw)
To: Stephen Smalley; +Cc: ericsu, danieldurning.work, selinux
On Mon, Aug 11, 2025 at 2:13 PM Stephen Smalley
<stephen.smalley.work@gmail.com> wrote:
> On Mon, Aug 11, 2025 at 1:47 PM Paul Moore <paul@paul-moore.com> wrote:
> > On Thu, Aug 7, 2025 at 9:46 AM Stephen Smalley
> > <stephen.smalley.work@gmail.com> wrote:
> > >
> > > Also, since you are introducing new permissions and a policy
> > > capability, please include instructions in the commit description for
> > > running your testsuite, see
> > > https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#add-new-permissions
> > > and
> > > https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#adding-a-new-selinux-policy-capability
> > > for instructions and links to example previous commits.
> >
> > I think it's fair to simply call out the new permissions and policy
> > capability in the patch's description along with a simple explanation
> > that the new behavior is gated on the new policy capability.
> > Including instructions on how to enable a policy capability is
> > something that I think we can consider "an exercise left to the
> > reader", with documentation located outside the patch description.
> > The unfortunate reality is that there is no single right way to add a
> > policy capability to a system, and those instructions which are distro
> > independent are likely to also clash with the distro supplied policy
> > packages.
> >
> > Unfortunately, while the process around adding policy capabilities
> > have improved somewhat over the years, it's still and ugly thing to
> > have to do and I'm not sure a commit description is the best place to
> > document that process. I still have hope that some of the new policy
> > work will improve this somewhat.
>
> My request and the linked example I provide in the wiki page is to put
> this information into the testsuite patch description, not the kernel
> patch description ...
Your request was attached to the kernel patch thread, while you may
have linked to test suite documentation, I think the distinction was
unclear at best. Perhaps one could put forward an argument and
highlight portions of the discussion context, but I'm not going to
bother to argue that either way; my comments stand with respect to
kernel patch.
> The instructions in the linked example are distro-agnostic and just
> leveraging a CIL module, so nothing specialized there.
I suspect there might be issues relating to the distro provided
packages, but I'll leave that as an exercise for those with more time
to play with that, and my Debian VM is in a bit of a broken state at
the moment.
Since there is some documentation on policy capabilities in the wiki,
perhaps it would be good to provide some actual policy capability
commit description boilerplate in the wiki?
> At the very least, the specific name of the new policy capability and
> the names of any new permissions and what classes they should be added
> to needs to be clearly identified in the description.
That was the first sentence in my reply.
--
paul-moore.com
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] SELinux: Add support for BPF token access control
2025-08-11 20:33 ` Paul Moore
@ 2025-08-12 12:12 ` Stephen Smalley
2025-08-14 15:20 ` Stephen Smalley
0 siblings, 1 reply; 15+ messages in thread
From: Stephen Smalley @ 2025-08-12 12:12 UTC (permalink / raw)
To: Paul Moore; +Cc: ericsu, danieldurning.work, selinux
On Mon, Aug 11, 2025 at 4:34 PM Paul Moore <paul@paul-moore.com> wrote:
>
> On Mon, Aug 11, 2025 at 2:13 PM Stephen Smalley
> <stephen.smalley.work@gmail.com> wrote:
> > On Mon, Aug 11, 2025 at 1:47 PM Paul Moore <paul@paul-moore.com> wrote:
> > > On Thu, Aug 7, 2025 at 9:46 AM Stephen Smalley
> > > <stephen.smalley.work@gmail.com> wrote:
> > > >
> > > > Also, since you are introducing new permissions and a policy
> > > > capability, please include instructions in the commit description for
> > > > running your testsuite, see
> > > > https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#add-new-permissions
> > > > and
> > > > https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#adding-a-new-selinux-policy-capability
> > > > for instructions and links to example previous commits.
> > >
> > > I think it's fair to simply call out the new permissions and policy
> > > capability in the patch's description along with a simple explanation
> > > that the new behavior is gated on the new policy capability.
> > > Including instructions on how to enable a policy capability is
> > > something that I think we can consider "an exercise left to the
> > > reader", with documentation located outside the patch description.
> > > The unfortunate reality is that there is no single right way to add a
> > > policy capability to a system, and those instructions which are distro
> > > independent are likely to also clash with the distro supplied policy
> > > packages.
> > >
> > > Unfortunately, while the process around adding policy capabilities
> > > have improved somewhat over the years, it's still and ugly thing to
> > > have to do and I'm not sure a commit description is the best place to
> > > document that process. I still have hope that some of the new policy
> > > work will improve this somewhat.
> >
> > My request and the linked example I provide in the wiki page is to put
> > this information into the testsuite patch description, not the kernel
> > patch description ...
>
> Your request was attached to the kernel patch thread, while you may
> have linked to test suite documentation, I think the distinction was
> unclear at best. Perhaps one could put forward an argument and
> highlight portions of the discussion context, but I'm not going to
> bother to argue that either way; my comments stand with respect to
> kernel patch.
Ok, fair point - sorry for the confusion.
>
> > The instructions in the linked example are distro-agnostic and just
> > leveraging a CIL module, so nothing specialized there.
>
> I suspect there might be issues relating to the distro provided
> packages, but I'll leave that as an exercise for those with more time
> to play with that, and my Debian VM is in a bit of a broken state at
> the moment.
Yes, that could be true. I suppose a better requirement would be to
provide sufficient instructions that a reasonable third party could
re-create the conditions necessary to exercise the tests.
That could just be providing the steps that the patch author took to
run the tests themselves.
> Since there is some documentation on policy capabilities in the wiki,
> perhaps it would be good to provide some actual policy capability
> commit description boilerplate in the wiki?
Good idea - will do that.
> > At the very least, the specific name of the new policy capability and
> > the names of any new permissions and what classes they should be added
> > to needs to be clearly identified in the description.
>
> That was the first sentence in my reply.
Yep, but unfortunately was missing from the testsuite patch
description when it was posted.
>
> --
> paul-moore.com
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] SELinux: Add support for BPF token access control
2025-08-11 19:30 ` Eric Suen
@ 2025-08-12 12:27 ` Stephen Smalley
2025-08-13 15:02 ` Stephen Smalley
0 siblings, 1 reply; 15+ messages in thread
From: Stephen Smalley @ 2025-08-12 12:27 UTC (permalink / raw)
To: Eric Suen; +Cc: Daniel Durning, selinux, paul
On Mon, Aug 11, 2025 at 3:30 PM Eric Suen <ericsu@linux.microsoft.com> wrote:
>
> On 8/7/2025 11:00 AM, Daniel Durning wrote:
> > On Wed, Aug 6, 2025 at 2:07 PM<ericsu@linux.microsoft.com> wrote:
> >> From: Eric Suen<ericsu@linux.microsoft.com>
> >>
> >> BPF token support was introduced to allow a privileged process to delegate
> >> limited BPF functionality—such as map creation and program loading—to an
> >> unprivileged process:
> >> https://lore.kernel.org/linux-security-module/20231130185229.2688956-1-andrii@kernel.org/
> >>
> >> This patch adds SELinux support for controlling BPF token access. With
> >> this change, SELinux policies can now enforce constraints on BPF token
> >> usage based on both the delegating (privileged) process and the recipient
> >> (unprivileged) process.
> >>
> >> Supported operations currently include:
> >> - map_create
> >> - prog_load
> >>
> >> High-level workflow:
> >> 1. An unprivileged process creates a VFS context via `fsopen()` and
> >> obtains a file descriptor.
> >> 2. This descriptor is passed to a privileged process, which configures
> >> BPF token delegation options and mounts a BPF filesystem.
> >> 3. SELinux records the `creator_sid` of the privileged process during
> >> mount setup.
> >> 4. The unprivileged process then uses this BPF fs mount to create a
> >> token and attach it to subsequent BPF syscalls.
> >> 5. During verification of `map_create` and `prog_load`, SELinux uses
> >> `creator_sid` and the current SID to check policy permissions via:
> >> avc_has_perm(creator_sid, current_sid, SECCLASS_BPF,
> >> BPF__MAP_CREATE, NULL);
> >>
> >> To verify the logic introduced by this patch, my fork of the SELinux
> >> testsuite with relevant test cases is available here:
> >> https://github.com/havefuncoding1/selinux-testsuite
> >>
> >> @@ -7086,10 +7092,34 @@ static int selinux_bpf_prog(struct bpf_prog *prog)
> >> BPF__PROG_RUN, NULL);
> >> }
> >>
> >> +static int selinux_bpffs_creator_sid(const union bpf_attr *attr)
> >> +{
> >> + struct path path;
> >> + struct super_block *sb;
> >> + struct superblock_security_struct *sbsec;
> >> +
> >> + CLASS(fd, f)(attr->token_create.bpffs_fd);
> >> +
> >> + if (!fd_file(f))
> >> + return SECSID_NULL;
> > Is it possible for this value to be null assuming there is not a bug
> > in the kernel? You would not want to do a runtime check for something
> > that would indicate a kernel bug lest you accidentally hide the bug.
> I always think being defensive is a good thing in kernel development.
> But you have a valid point, and I will review and update in next
> iteration if appropriate. Same applies to all similar comments below.
> Thank you.
The Linux kernel way is to NOT check conditions that can only occur if
there is a kernel bug (or use BUG_ON/BUG/etc macros for such checks so
they can be compiled away for production, but adding new instances of
those is generally frowned upon). So unless you can identify a case
where these are userspace-triggerable, you shouldn't be checking them.
> >> @@ -7098,7 +7128,15 @@ static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
> >> bpfsec->sid = current_sid();
> >> map->security = bpfsec;
> >>
> >> - return 0;
> >> + if (!token)
> >> + ssid = bpfsec->sid;
> >> + else {
> >> + ssid = selinux_bpffs_creator_sid(attr);
> >> + if (ssid == SECSID_NULL)
> >> + return -EPERM;
> > Should this ever be null? See above (for this and all repeated
> > occurrences of this check).
If this check remains in your final patch, then returning -EPERM from
SELinux is uncommon - we usually return -EACCES on permission denials,
only using -EPERM for superuser/capability checks. Also silently
returning -EPERM or -EACCES is not ideal for SELinux since it provides
no insight to the user as to the cause and no way to override (ala
permissive mode) if needed. It might actually be better to just fall
through and let it pass SECSID_NULL to avc_has_perm(), which will
produce an SELinux avc audit message (with the unlabeled context,
since any unknown SID is mapped to that automatically) to the user and
the option for overriding via permissive mode.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] SELinux: Add support for BPF token access control
2025-08-12 12:27 ` Stephen Smalley
@ 2025-08-13 15:02 ` Stephen Smalley
2025-08-16 17:27 ` Eric Suen
0 siblings, 1 reply; 15+ messages in thread
From: Stephen Smalley @ 2025-08-13 15:02 UTC (permalink / raw)
To: Eric Suen; +Cc: Daniel Durning, selinux, paul
On Tue, Aug 12, 2025 at 8:27 AM Stephen Smalley
<stephen.smalley.work@gmail.com> wrote:
>
> On Mon, Aug 11, 2025 at 3:30 PM Eric Suen <ericsu@linux.microsoft.com> wrote:
> >
> > On 8/7/2025 11:00 AM, Daniel Durning wrote:
> > > On Wed, Aug 6, 2025 at 2:07 PM<ericsu@linux.microsoft.com> wrote:
> > >> From: Eric Suen<ericsu@linux.microsoft.com>
> > >>
> > >> BPF token support was introduced to allow a privileged process to delegate
> > >> limited BPF functionality—such as map creation and program loading—to an
> > >> unprivileged process:
> > >> https://lore.kernel.org/linux-security-module/20231130185229.2688956-1-andrii@kernel.org/
> > >>
> > >> This patch adds SELinux support for controlling BPF token access. With
> > >> this change, SELinux policies can now enforce constraints on BPF token
> > >> usage based on both the delegating (privileged) process and the recipient
> > >> (unprivileged) process.
> > >>
> > >> Supported operations currently include:
> > >> - map_create
> > >> - prog_load
> > >>
> > >> High-level workflow:
> > >> 1. An unprivileged process creates a VFS context via `fsopen()` and
> > >> obtains a file descriptor.
> > >> 2. This descriptor is passed to a privileged process, which configures
> > >> BPF token delegation options and mounts a BPF filesystem.
> > >> 3. SELinux records the `creator_sid` of the privileged process during
> > >> mount setup.
> > >> 4. The unprivileged process then uses this BPF fs mount to create a
> > >> token and attach it to subsequent BPF syscalls.
> > >> 5. During verification of `map_create` and `prog_load`, SELinux uses
> > >> `creator_sid` and the current SID to check policy permissions via:
> > >> avc_has_perm(creator_sid, current_sid, SECCLASS_BPF,
> > >> BPF__MAP_CREATE, NULL);
> > >>
> > >> To verify the logic introduced by this patch, my fork of the SELinux
> > >> testsuite with relevant test cases is available here:
> > >> https://github.com/havefuncoding1/selinux-testsuite
> > >>
>
> > >> @@ -7086,10 +7092,34 @@ static int selinux_bpf_prog(struct bpf_prog *prog)
> > >> BPF__PROG_RUN, NULL);
> > >> }
> > >>
> > >> +static int selinux_bpffs_creator_sid(const union bpf_attr *attr)
> > >> +{
> > >> + struct path path;
> > >> + struct super_block *sb;
> > >> + struct superblock_security_struct *sbsec;
> > >> +
> > >> + CLASS(fd, f)(attr->token_create.bpffs_fd);
> > >> +
> > >> + if (!fd_file(f))
> > >> + return SECSID_NULL;
> > > Is it possible for this value to be null assuming there is not a bug
> > > in the kernel? You would not want to do a runtime check for something
> > > that would indicate a kernel bug lest you accidentally hide the bug.
> > I always think being defensive is a good thing in kernel development.
> > But you have a valid point, and I will review and update in next
> > iteration if appropriate. Same applies to all similar comments below.
> > Thank you.
>
> The Linux kernel way is to NOT check conditions that can only occur if
> there is a kernel bug (or use BUG_ON/BUG/etc macros for such checks so
> they can be compiled away for production, but adding new instances of
> those is generally frowned upon). So unless you can identify a case
> where these are userspace-triggerable, you shouldn't be checking them.
On a quick look, the only other use of "CLASS(fd,
f)(attr->token_create.bpffs_fd);" that I saw was in
kernel/bpf/token.c:bpf_token_create(), and it checks for fd_empty(f)
rather than fd_file(f). The other question I have though is are we
guaranteed that attr->token_create.bpffs_fd is always set on all
callers of this selinux_bpffs_creator_sid() function outside the
context of bpf_token_create()?
>
> > >> @@ -7098,7 +7128,15 @@ static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
> > >> bpfsec->sid = current_sid();
> > >> map->security = bpfsec;
> > >>
> > >> - return 0;
> > >> + if (!token)
> > >> + ssid = bpfsec->sid;
> > >> + else {
> > >> + ssid = selinux_bpffs_creator_sid(attr);
> > >> + if (ssid == SECSID_NULL)
> > >> + return -EPERM;
> > > Should this ever be null? See above (for this and all repeated
> > > occurrences of this check).
>
> If this check remains in your final patch, then returning -EPERM from
> SELinux is uncommon - we usually return -EACCES on permission denials,
> only using -EPERM for superuser/capability checks. Also silently
> returning -EPERM or -EACCES is not ideal for SELinux since it provides
> no insight to the user as to the cause and no way to override (ala
> permissive mode) if needed. It might actually be better to just fall
> through and let it pass SECSID_NULL to avc_has_perm(), which will
> produce an SELinux avc audit message (with the unlabeled context,
> since any unknown SID is mapped to that automatically) to the user and
> the option for overriding via permissive mode.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] SELinux: Add support for BPF token access control
2025-08-12 12:12 ` Stephen Smalley
@ 2025-08-14 15:20 ` Stephen Smalley
0 siblings, 0 replies; 15+ messages in thread
From: Stephen Smalley @ 2025-08-14 15:20 UTC (permalink / raw)
To: Paul Moore; +Cc: ericsu, danieldurning.work, selinux
On Tue, Aug 12, 2025 at 8:12 AM Stephen Smalley
<stephen.smalley.work@gmail.com> wrote:
>
> On Mon, Aug 11, 2025 at 4:34 PM Paul Moore <paul@paul-moore.com> wrote:
> >
> > On Mon, Aug 11, 2025 at 2:13 PM Stephen Smalley
> > <stephen.smalley.work@gmail.com> wrote:
> > > On Mon, Aug 11, 2025 at 1:47 PM Paul Moore <paul@paul-moore.com> wrote:
> > > > On Thu, Aug 7, 2025 at 9:46 AM Stephen Smalley
> > > > <stephen.smalley.work@gmail.com> wrote:
> > > > >
> > > > > Also, since you are introducing new permissions and a policy
> > > > > capability, please include instructions in the commit description for
> > > > > running your testsuite, see
> > > > > https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#add-new-permissions
> > > > > and
> > > > > https://github.com/SELinuxProject/selinux-kernel/wiki/Getting-Started#adding-a-new-selinux-policy-capability
> > > > > for instructions and links to example previous commits.
> > > >
> > > > I think it's fair to simply call out the new permissions and policy
> > > > capability in the patch's description along with a simple explanation
> > > > that the new behavior is gated on the new policy capability.
> > > > Including instructions on how to enable a policy capability is
> > > > something that I think we can consider "an exercise left to the
> > > > reader", with documentation located outside the patch description.
> > > > The unfortunate reality is that there is no single right way to add a
> > > > policy capability to a system, and those instructions which are distro
> > > > independent are likely to also clash with the distro supplied policy
> > > > packages.
> > > >
> > > > Unfortunately, while the process around adding policy capabilities
> > > > have improved somewhat over the years, it's still and ugly thing to
> > > > have to do and I'm not sure a commit description is the best place to
> > > > document that process. I still have hope that some of the new policy
> > > > work will improve this somewhat.
> > >
> > > My request and the linked example I provide in the wiki page is to put
> > > this information into the testsuite patch description, not the kernel
> > > patch description ...
> >
> > Your request was attached to the kernel patch thread, while you may
> > have linked to test suite documentation, I think the distinction was
> > unclear at best. Perhaps one could put forward an argument and
> > highlight portions of the discussion context, but I'm not going to
> > bother to argue that either way; my comments stand with respect to
> > kernel patch.
>
> Ok, fair point - sorry for the confusion.
>
> >
> > > The instructions in the linked example are distro-agnostic and just
> > > leveraging a CIL module, so nothing specialized there.
> >
> > I suspect there might be issues relating to the distro provided
> > packages, but I'll leave that as an exercise for those with more time
> > to play with that, and my Debian VM is in a bit of a broken state at
> > the moment.
>
> Yes, that could be true. I suppose a better requirement would be to
> provide sufficient instructions that a reasonable third party could
> re-create the conditions necessary to exercise the tests.
> That could just be providing the steps that the patch author took to
> run the tests themselves.
>
> > Since there is some documentation on policy capabilities in the wiki,
> > perhaps it would be good to provide some actual policy capability
> > commit description boilerplate in the wiki?
>
> Good idea - will do that.
On second look, I think the commit to which I link there already is
sufficient, but I did add the following text to the wiki under
Submitting a Testsuite Patch:
"Provide enough context in the patch description to allow someone not
already familiar with it to replicate what you did to run the tests,
including citing any requisite dependencies for the kernel or
userspace and identifying any new policy capabilities or permissions
required. See https://github.com/SELinuxProject/selinux-testsuite/commit/023b79b8319e5fe222fb5af892c579593e1cbc50
for an example description."
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] SELinux: Add support for BPF token access control
2025-08-13 15:02 ` Stephen Smalley
@ 2025-08-16 17:27 ` Eric Suen
0 siblings, 0 replies; 15+ messages in thread
From: Eric Suen @ 2025-08-16 17:27 UTC (permalink / raw)
To: Stephen Smalley; +Cc: Daniel Durning, selinux, paul
On 8/13/2025 8:02 AM, Stephen Smalley wrote:
> On Tue, Aug 12, 2025 at 8:27 AM Stephen Smalley
> <stephen.smalley.work@gmail.com> wrote:
>> On Mon, Aug 11, 2025 at 3:30 PM Eric Suen <ericsu@linux.microsoft.com> wrote:
>>> On 8/7/2025 11:00 AM, Daniel Durning wrote:
>>>> On Wed, Aug 6, 2025 at 2:07 PM<ericsu@linux.microsoft.com> wrote:
>>>>> From: Eric Suen<ericsu@linux.microsoft.com>
>>>>>
>>>>> BPF token support was introduced to allow a privileged process to delegate
>>>>> limited BPF functionality—such as map creation and program loading—to an
>>>>> unprivileged process:
>>>>> https://lore.kernel.org/linux-security-module/20231130185229.2688956-1-andrii@kernel.org/
>>>>>
>>>>> This patch adds SELinux support for controlling BPF token access. With
>>>>> this change, SELinux policies can now enforce constraints on BPF token
>>>>> usage based on both the delegating (privileged) process and the recipient
>>>>> (unprivileged) process.
>>>>>
>>>>> Supported operations currently include:
>>>>> - map_create
>>>>> - prog_load
>>>>>
>>>>> High-level workflow:
>>>>> 1. An unprivileged process creates a VFS context via `fsopen()` and
>>>>> obtains a file descriptor.
>>>>> 2. This descriptor is passed to a privileged process, which configures
>>>>> BPF token delegation options and mounts a BPF filesystem.
>>>>> 3. SELinux records the `creator_sid` of the privileged process during
>>>>> mount setup.
>>>>> 4. The unprivileged process then uses this BPF fs mount to create a
>>>>> token and attach it to subsequent BPF syscalls.
>>>>> 5. During verification of `map_create` and `prog_load`, SELinux uses
>>>>> `creator_sid` and the current SID to check policy permissions via:
>>>>> avc_has_perm(creator_sid, current_sid, SECCLASS_BPF,
>>>>> BPF__MAP_CREATE, NULL);
>>>>>
>>>>> To verify the logic introduced by this patch, my fork of the SELinux
>>>>> testsuite with relevant test cases is available here:
>>>>> https://github.com/havefuncoding1/selinux-testsuite
>>>>>
>>>>> @@ -7086,10 +7092,34 @@ static int selinux_bpf_prog(struct bpf_prog *prog)
>>>>> BPF__PROG_RUN, NULL);
>>>>> }
>>>>>
>>>>> +static int selinux_bpffs_creator_sid(const union bpf_attr *attr)
>>>>> +{
>>>>> + struct path path;
>>>>> + struct super_block *sb;
>>>>> + struct superblock_security_struct *sbsec;
>>>>> +
>>>>> + CLASS(fd, f)(attr->token_create.bpffs_fd);
>>>>> +
>>>>> + if (!fd_file(f))
>>>>> + return SECSID_NULL;
>>>> Is it possible for this value to be null assuming there is not a bug
>>>> in the kernel? You would not want to do a runtime check for something
>>>> that would indicate a kernel bug lest you accidentally hide the bug.
>>> I always think being defensive is a good thing in kernel development.
>>> But you have a valid point, and I will review and update in next
>>> iteration if appropriate. Same applies to all similar comments below.
>>> Thank you.
>> The Linux kernel way is to NOT check conditions that can only occur if
>> there is a kernel bug (or use BUG_ON/BUG/etc macros for such checks so
>> they can be compiled away for production, but adding new instances of
>> those is generally frowned upon). So unless you can identify a case
>> where these are userspace-triggerable, you shouldn't be checking them.
> On a quick look, the only other use of "CLASS(fd,
> f)(attr->token_create.bpffs_fd);" that I saw was in
> kernel/bpf/token.c:bpf_token_create(), and it checks for fd_empty(f)
> rather than fd_file(f). The other question I have though is are we
> guaranteed that attr->token_create.bpffs_fd is always set on all
> callers of this selinux_bpffs_creator_sid() function outside the
> context of bpf_token_create()?
Stephen - you are absolutely right. After seeing your comment about
allow rule for 'kernel_t' in my test, I started to debug and
'token_create.bpffs_fd' was exactly the root cause. I have fixed it and
things look much better now. Thank you. I will send out new iteration soon.
>>>>> @@ -7098,7 +7128,15 @@ static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
>>>>> bpfsec->sid = current_sid();
>>>>> map->security = bpfsec;
>>>>>
>>>>> - return 0;
>>>>> + if (!token)
>>>>> + ssid = bpfsec->sid;
>>>>> + else {
>>>>> + ssid = selinux_bpffs_creator_sid(attr);
>>>>> + if (ssid == SECSID_NULL)
>>>>> + return -EPERM;
>>>> Should this ever be null? See above (for this and all repeated
>>>> occurrences of this check).
>> If this check remains in your final patch, then returning -EPERM from
>> SELinux is uncommon - we usually return -EACCES on permission denials,
>> only using -EPERM for superuser/capability checks. Also silently
>> returning -EPERM or -EACCES is not ideal for SELinux since it provides
>> no insight to the user as to the cause and no way to override (ala
>> permissive mode) if needed. It might actually be better to just fall
>> through and let it pass SECSID_NULL to avc_has_perm(), which will
>> produce an SELinux avc audit message (with the unlabeled context,
>> since any unknown SID is mapped to that automatically) to the user and
>> the option for overriding via permissive mode.
Got it. Thank you.
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2025-08-16 17:27 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-06 18:01 [PATCH] SELinux: Add support for BPF token access control ericsu
2025-08-06 18:33 ` Stephen Smalley
2025-08-07 13:46 ` Stephen Smalley
2025-08-08 18:57 ` Eric Suen
2025-08-08 19:08 ` Stephen Smalley
2025-08-11 17:47 ` Paul Moore
2025-08-11 18:13 ` Stephen Smalley
2025-08-11 20:33 ` Paul Moore
2025-08-12 12:12 ` Stephen Smalley
2025-08-14 15:20 ` Stephen Smalley
2025-08-07 18:00 ` Daniel Durning
2025-08-11 19:30 ` Eric Suen
2025-08-12 12:27 ` Stephen Smalley
2025-08-13 15:02 ` Stephen Smalley
2025-08-16 17:27 ` Eric Suen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).