* [PATCH v3] SELinux: Add support for BPF token access control
@ 2025-08-21 18:20 Eric Suen
2025-08-21 19:06 ` Stephen Smalley
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Eric Suen @ 2025-08-21 18:20 UTC (permalink / raw)
To: selinux; +Cc: paul, stephen.smalley.work, omosnace, danieldurning.work
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);
The implementation introduces two new permissions:
- map_create_as
- prog_load_as
At token creation time, SELinux verifies that the current process has the
appropriate `*_as` permission (depending on the `allowed_cmds` value in
the bpf_token) to act on behalf of the `creator_sid`.
Example SELinux policy:
allow test_bpf_t self:bpf {
map_create map_read map_write prog_load prog_run
map_create_as prog_load_as
};
Additionally, a new policy capability bpf_token_perms is added to ensure
backward compatibility. If disabled, previous behavior ((checks based on
current process SID)) is preserved.
Changes in v2:
- Fixed bug in selinux_bpffs_creator_sid(u32 fd) where it retrieved
creator_sid from wrong file descriptor
- Removed unnecessary checks for null, per review comments from
the first patch
Changes in v3:
- Removed check for 'sid == SECSID_NULL' in selinux_bpf_token_create and
allow it to fall through to the permission checks which will fail as
access denied to unlabeled_t
Signed-off-by: Eric Suen <ericsu@linux.microsoft.com>
---
security/selinux/hooks.c | 90 +++++++++++++++++++++-
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, 101 insertions(+), 3 deletions(-)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index e474cd7398ef..d949e9c5aa31 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;
@@ -7014,9 +7016,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,
@@ -7098,10 +7104,29 @@ static int selinux_bpf_prog(struct bpf_prog *prog)
BPF__PROG_RUN, NULL);
}
+static int selinux_bpffs_creator_sid(u32 fd)
+{
+ struct path path;
+ struct super_block *sb;
+ struct superblock_security_struct *sbsec;
+
+ CLASS(fd, f)(fd);
+
+ if (fd_empty(f))
+ return SECSID_NULL;
+
+ path = fd_file(f)->f_path;
+ sb = path.dentry->d_sb;
+ sbsec = selinux_superblock(sb);
+
+ 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)
@@ -7110,7 +7135,12 @@ 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->map_token_fd);
+
+ return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__MAP_CREATE, NULL);
}
static void selinux_bpf_map_free(struct bpf_map *map)
@@ -7125,6 +7155,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)
@@ -7133,7 +7164,12 @@ 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;
+ else
+ ssid = selinux_bpffs_creator_sid(attr->prog_token_fd);
+
+ return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__PROG_LOAD, NULL);
}
static void selinux_bpf_prog_free(struct bpf_prog *prog)
@@ -7144,10 +7180,15 @@ 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->token_create.bpffs_fd);
+ int err;
bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
if (!bpfsec)
@@ -7156,6 +7197,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;
}
@@ -7166,6 +7230,27 @@ 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;
+
+ 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 = {
@@ -7599,6 +7684,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 7f19972f7922..02f0789b6254 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -203,6 +203,12 @@ static inline bool selinux_policycap_netlink_xperm(void)
selinux_state.policycap[POLICYDB_CAP_NETLINK_XPERM]);
}
+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] 9+ messages in thread* Re: [PATCH v3] SELinux: Add support for BPF token access control
2025-08-21 18:20 [PATCH v3] SELinux: Add support for BPF token access control Eric Suen
@ 2025-08-21 19:06 ` Stephen Smalley
2025-08-21 19:41 ` Stephen Smalley
2025-08-22 17:57 ` Daniel Durning
2025-09-17 21:48 ` Paul Moore
2 siblings, 1 reply; 9+ messages in thread
From: Stephen Smalley @ 2025-08-21 19:06 UTC (permalink / raw)
To: Eric Suen; +Cc: selinux, paul, omosnace, danieldurning.work
On Thu, Aug 21, 2025 at 2:20 PM Eric Suen <ericsu@linux.microsoft.com> wrote:
>
> 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);
>
> The implementation introduces two new permissions:
> - map_create_as
> - prog_load_as
>
> At token creation time, SELinux verifies that the current process has the
> appropriate `*_as` permission (depending on the `allowed_cmds` value in
> the bpf_token) to act on behalf of the `creator_sid`.
>
> Example SELinux policy:
> allow test_bpf_t self:bpf {
> map_create map_read map_write prog_load prog_run
> map_create_as prog_load_as
> };
>
> Additionally, a new policy capability bpf_token_perms is added to ensure
> backward compatibility. If disabled, previous behavior ((checks based on
> current process SID)) is preserved.
>
> Changes in v2:
> - Fixed bug in selinux_bpffs_creator_sid(u32 fd) where it retrieved
> creator_sid from wrong file descriptor
> - Removed unnecessary checks for null, per review comments from
> the first patch
>
> Changes in v3:
> - Removed check for 'sid == SECSID_NULL' in selinux_bpf_token_create and
> allow it to fall through to the permission checks which will fail as
> access denied to unlabeled_t
>
> Signed-off-by: Eric Suen <ericsu@linux.microsoft.com>
> ---
> security/selinux/hooks.c | 90 +++++++++++++++++++++-
> 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, 101 insertions(+), 3 deletions(-)
>
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index e474cd7398ef..d949e9c5aa31 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -7110,7 +7135,12 @@ 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->map_token_fd);
> +
> + return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__MAP_CREATE, NULL);
> }
Apologies for not noticing this on earlier versions, but here and
below you are introducing potential error returns after an allocation
has taken place.
Hence, you need to free the security blob before returning if
avc_has_perm() returns non-zero here, or the caller needs to handle
it.
This may be partially moot given that there are separate patches that
went upstream in the lsm tree to move the blob allocation to the LSM
framework, but should double check that it handles this situation
correctly too.
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v3] SELinux: Add support for BPF token access control
2025-08-21 19:06 ` Stephen Smalley
@ 2025-08-21 19:41 ` Stephen Smalley
0 siblings, 0 replies; 9+ messages in thread
From: Stephen Smalley @ 2025-08-21 19:41 UTC (permalink / raw)
To: Eric Suen; +Cc: selinux, paul, omosnace, danieldurning.work
On Thu, Aug 21, 2025 at 3:06 PM Stephen Smalley
<stephen.smalley.work@gmail.com> wrote:
>
> On Thu, Aug 21, 2025 at 2:20 PM Eric Suen <ericsu@linux.microsoft.com> wrote:
> >
> > 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);
> >
> > The implementation introduces two new permissions:
> > - map_create_as
> > - prog_load_as
> >
> > At token creation time, SELinux verifies that the current process has the
> > appropriate `*_as` permission (depending on the `allowed_cmds` value in
> > the bpf_token) to act on behalf of the `creator_sid`.
> >
> > Example SELinux policy:
> > allow test_bpf_t self:bpf {
> > map_create map_read map_write prog_load prog_run
> > map_create_as prog_load_as
> > };
> >
> > Additionally, a new policy capability bpf_token_perms is added to ensure
> > backward compatibility. If disabled, previous behavior ((checks based on
> > current process SID)) is preserved.
> >
> > Changes in v2:
> > - Fixed bug in selinux_bpffs_creator_sid(u32 fd) where it retrieved
> > creator_sid from wrong file descriptor
> > - Removed unnecessary checks for null, per review comments from
> > the first patch
> >
> > Changes in v3:
> > - Removed check for 'sid == SECSID_NULL' in selinux_bpf_token_create and
> > allow it to fall through to the permission checks which will fail as
> > access denied to unlabeled_t
> >
> > Signed-off-by: Eric Suen <ericsu@linux.microsoft.com>
> > ---
> > security/selinux/hooks.c | 90 +++++++++++++++++++++-
> > 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, 101 insertions(+), 3 deletions(-)
> >
> > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > index e474cd7398ef..d949e9c5aa31 100644
> > --- a/security/selinux/hooks.c
> > +++ b/security/selinux/hooks.c
> > @@ -7110,7 +7135,12 @@ 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->map_token_fd);
> > +
> > + return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__MAP_CREATE, NULL);
> > }
>
> Apologies for not noticing this on earlier versions, but here and
> below you are introducing potential error returns after an allocation
> has taken place.
> Hence, you need to free the security blob before returning if
> avc_has_perm() returns non-zero here, or the caller needs to handle
> it.
> This may be partially moot given that there are separate patches that
> went upstream in the lsm tree to move the blob allocation to the LSM
> framework, but should double check that it handles this situation
> correctly too.
Actually, it appears that the core kernel handles this already by
calling the corresponding free hook on non-zero return from these
hooks, so never mind.
That's not typical for many hooks at least last I looked, but seems to
be the pattern for the bpf ones.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3] SELinux: Add support for BPF token access control
2025-08-21 18:20 [PATCH v3] SELinux: Add support for BPF token access control Eric Suen
2025-08-21 19:06 ` Stephen Smalley
@ 2025-08-22 17:57 ` Daniel Durning
2025-08-25 14:08 ` Stephen Smalley
2025-09-17 21:48 ` Paul Moore
2 siblings, 1 reply; 9+ messages in thread
From: Daniel Durning @ 2025-08-22 17:57 UTC (permalink / raw)
To: Eric Suen; +Cc: selinux, paul, stephen.smalley.work, omosnace
On Thu, Aug 21, 2025 at 2:20 PM Eric Suen <ericsu@linux.microsoft.com> wrote:
>
> 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);
>
> The implementation introduces two new permissions:
> - map_create_as
> - prog_load_as
>
> At token creation time, SELinux verifies that the current process has the
> appropriate `*_as` permission (depending on the `allowed_cmds` value in
> the bpf_token) to act on behalf of the `creator_sid`.
>
> Example SELinux policy:
> allow test_bpf_t self:bpf {
> map_create map_read map_write prog_load prog_run
> map_create_as prog_load_as
> };
>
> Additionally, a new policy capability bpf_token_perms is added to ensure
> backward compatibility. If disabled, previous behavior ((checks based on
> current process SID)) is preserved.
>
> Changes in v2:
> - Fixed bug in selinux_bpffs_creator_sid(u32 fd) where it retrieved
> creator_sid from wrong file descriptor
> - Removed unnecessary checks for null, per review comments from
> the first patch
>
> Changes in v3:
> - Removed check for 'sid == SECSID_NULL' in selinux_bpf_token_create and
> allow it to fall through to the permission checks which will fail as
> access denied to unlabeled_t
>
> Signed-off-by: Eric Suen <ericsu@linux.microsoft.com>
Tested-by: Daniel Durning <danieldurning.work@gmail.com>
Reviewed-by: Daniel Durning <danieldurning.work@gmail.com>
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v3] SELinux: Add support for BPF token access control
2025-08-22 17:57 ` Daniel Durning
@ 2025-08-25 14:08 ` Stephen Smalley
0 siblings, 0 replies; 9+ messages in thread
From: Stephen Smalley @ 2025-08-25 14:08 UTC (permalink / raw)
To: Daniel Durning; +Cc: Eric Suen, selinux, paul, omosnace
On Fri, Aug 22, 2025 at 1:57 PM Daniel Durning
<danieldurning.work@gmail.com> wrote:
>
> On Thu, Aug 21, 2025 at 2:20 PM Eric Suen <ericsu@linux.microsoft.com> wrote:
> >
> > 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);
> >
> > The implementation introduces two new permissions:
> > - map_create_as
> > - prog_load_as
> >
> > At token creation time, SELinux verifies that the current process has the
> > appropriate `*_as` permission (depending on the `allowed_cmds` value in
> > the bpf_token) to act on behalf of the `creator_sid`.
> >
> > Example SELinux policy:
> > allow test_bpf_t self:bpf {
> > map_create map_read map_write prog_load prog_run
> > map_create_as prog_load_as
> > };
> >
> > Additionally, a new policy capability bpf_token_perms is added to ensure
> > backward compatibility. If disabled, previous behavior ((checks based on
> > current process SID)) is preserved.
> >
> > Changes in v2:
> > - Fixed bug in selinux_bpffs_creator_sid(u32 fd) where it retrieved
> > creator_sid from wrong file descriptor
> > - Removed unnecessary checks for null, per review comments from
> > the first patch
> >
> > Changes in v3:
> > - Removed check for 'sid == SECSID_NULL' in selinux_bpf_token_create and
> > allow it to fall through to the permission checks which will fail as
> > access denied to unlabeled_t
> >
> > Signed-off-by: Eric Suen <ericsu@linux.microsoft.com>
>
> Tested-by: Daniel Durning <danieldurning.work@gmail.com>
> Reviewed-by: Daniel Durning <danieldurning.work@gmail.com>
Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v3] SELinux: Add support for BPF token access control
2025-08-21 18:20 [PATCH v3] SELinux: Add support for BPF token access control Eric Suen
2025-08-21 19:06 ` Stephen Smalley
2025-08-22 17:57 ` Daniel Durning
@ 2025-09-17 21:48 ` Paul Moore
2025-09-18 13:36 ` Stephen Smalley
2 siblings, 1 reply; 9+ messages in thread
From: Paul Moore @ 2025-09-17 21:48 UTC (permalink / raw)
To: Eric Suen, selinux; +Cc: stephen.smalley.work, omosnace, danieldurning.work
On Aug 21, 2025 Eric Suen <ericsu@linux.microsoft.com> wrote:
>
> 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);
>
> The implementation introduces two new permissions:
> - map_create_as
> - prog_load_as
>
> At token creation time, SELinux verifies that the current process has the
> appropriate `*_as` permission (depending on the `allowed_cmds` value in
> the bpf_token) to act on behalf of the `creator_sid`.
>
> Example SELinux policy:
> allow test_bpf_t self:bpf {
> map_create map_read map_write prog_load prog_run
> map_create_as prog_load_as
> };
>
> Additionally, a new policy capability bpf_token_perms is added to ensure
> backward compatibility. If disabled, previous behavior ((checks based on
> current process SID)) is preserved.
>
> Changes in v2:
> - Fixed bug in selinux_bpffs_creator_sid(u32 fd) where it retrieved
> creator_sid from wrong file descriptor
> - Removed unnecessary checks for null, per review comments from
> the first patch
>
> Changes in v3:
> - Removed check for 'sid == SECSID_NULL' in selinux_bpf_token_create and
> allow it to fall through to the permission checks which will fail as
> access denied to unlabeled_t
>
> Signed-off-by: Eric Suen <ericsu@linux.microsoft.com>
> Tested-by: Daniel Durning <danieldurning.work@gmail.com>
> Reviewed-by: Daniel Durning <danieldurning.work@gmail.com>
> Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
> ---
> security/selinux/hooks.c | 90 +++++++++++++++++++++-
> 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, 101 insertions(+), 3 deletions(-)
Thank you very much Eric for working on this, I've got a fit relatively
minor comments below, if you get stuck on any of these please let me
know.
Also thanks to Daniel for his work on an earlier patch as well as the
review and test work that has gone into this one.
... and of course thanks to Stephen too ;)
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index e474cd7398ef..d949e9c5aa31 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();
A few things come to mind as I'm looking at the new creator_sid field.
First, we should explicitly initialize @sbsec->creator_sid to
SECINITSID_UNLABELED in selinux_sb_alloc_security() just as we do the
other fields. Yes, the LSM framework does some basic clearing, but this
is safer long term and it isn't likely that superblock allocation is
going to be common enough to where the extra assignment is going to be
a concern.
While I don't think this is an immediate issue, I think it's a good idea
to ensure that the selinux_cmp_sb_context() function compares the
@sbsec->creator_sid fields and only returns true/"match" if they are the
same.
Similarly, we should probably copy over the creator_sid field in the
selinux_sb_clone_mnt_opts() function. Almost surely not an issue now,
just look at the callers, but it's very possible that something might
change in the VFS and/or how people use this new functionality and we
could get caught out with a missing/bad label for the creator.
> if (strcmp(sb->s_type->name, "proc") == 0)
> sbsec->flags |= SE_SBPROC | SE_SBGENFS;
>
> @@ -7014,9 +7016,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;
Since @bpf_token_perms is only used once in this function, we can skip
the local variable and just call the lookup function here.
if (selinux_policycap_bpf_token_perms())
return 0;
> switch (cmd) {
> case BPF_MAP_CREATE:
> ret = avc_has_perm(sid, sid, SECCLASS_BPF, BPF__MAP_CREATE,
...
> @@ -7144,10 +7180,15 @@ 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->token_create.bpffs_fd);
> + int err;
>
> bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> if (!bpfsec)
This isn't an issue with your code, and to be clear I'm not asking you
to fix this, but more as a FYI/TODO for me (or anyone else who would
care to do it), after the BPF token patch is merged, we should move the
@bpfsec allocation/lifecycle-management for tokens out to the LSM
framework so it can properly handle multiple LSMs.
> @@ -7166,6 +7230,27 @@ 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;
> +
> + bpfsec = token->security;
> + switch (cmd) {
> + case BPF_MAP_CREATE:
> + if ((bpfsec->perms & BPF__MAP_CREATE) != BPF__MAP_CREATE)
> + return -EACCES;
The @bpfsec->perms field is just a bitmap so we can simplify this check
as shown below. Yes, it is a bit nitpicky, but the resulting code is
much cleaner IMO.
if (!(bpfsec->perms & BPF__MAP_CREATE))
return -EACCES;
Same thing for the BPF_PROG_LOAD case below.
> + break;
> + case BPF_PROG_LOAD:
> + if ((bpfsec->perms & BPF__PROG_LOAD) != BPF__PROG_LOAD)
> + return -EACCES;
> + break;
> + default:
> + break;
> + }
> +
> + return 0;
> +}
> #endif
...
> 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 */
Drop the second comment line and only use a single space between the
field and the comment.
u32 creator_sid; /* ... */
> 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 */
Similar to above.
u32 perms; /* ... */
> };
--
paul-moore.com
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v3] SELinux: Add support for BPF token access control
2025-09-17 21:48 ` Paul Moore
@ 2025-09-18 13:36 ` Stephen Smalley
2025-09-18 14:59 ` Paul Moore
0 siblings, 1 reply; 9+ messages in thread
From: Stephen Smalley @ 2025-09-18 13:36 UTC (permalink / raw)
To: Paul Moore; +Cc: Eric Suen, selinux, omosnace, danieldurning.work
On Wed, Sep 17, 2025 at 5:48 PM Paul Moore <paul@paul-moore.com> wrote:
>
> On Aug 21, 2025 Eric Suen <ericsu@linux.microsoft.com> wrote:
> >
> > 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);
> >
> > The implementation introduces two new permissions:
> > - map_create_as
> > - prog_load_as
> >
> > At token creation time, SELinux verifies that the current process has the
> > appropriate `*_as` permission (depending on the `allowed_cmds` value in
> > the bpf_token) to act on behalf of the `creator_sid`.
> >
> > Example SELinux policy:
> > allow test_bpf_t self:bpf {
> > map_create map_read map_write prog_load prog_run
> > map_create_as prog_load_as
> > };
> >
> > Additionally, a new policy capability bpf_token_perms is added to ensure
> > backward compatibility. If disabled, previous behavior ((checks based on
> > current process SID)) is preserved.
> >
> > Changes in v2:
> > - Fixed bug in selinux_bpffs_creator_sid(u32 fd) where it retrieved
> > creator_sid from wrong file descriptor
> > - Removed unnecessary checks for null, per review comments from
> > the first patch
> >
> > Changes in v3:
> > - Removed check for 'sid == SECSID_NULL' in selinux_bpf_token_create and
> > allow it to fall through to the permission checks which will fail as
> > access denied to unlabeled_t
> >
> > Signed-off-by: Eric Suen <ericsu@linux.microsoft.com>
> > Tested-by: Daniel Durning <danieldurning.work@gmail.com>
> > Reviewed-by: Daniel Durning <danieldurning.work@gmail.com>
> > Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
> > ---
> > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > index e474cd7398ef..d949e9c5aa31 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();
>
> A few things come to mind as I'm looking at the new creator_sid field.
>
> First, we should explicitly initialize @sbsec->creator_sid to
> SECINITSID_UNLABELED in selinux_sb_alloc_security() just as we do the
> other fields. Yes, the LSM framework does some basic clearing, but this
> is safer long term and it isn't likely that superblock allocation is
> going to be common enough to where the extra assignment is going to be
> a concern.
>
> While I don't think this is an immediate issue, I think it's a good idea
> to ensure that the selinux_cmp_sb_context() function compares the
> @sbsec->creator_sid fields and only returns true/"match" if they are the
> same.
Hmm...that raises another question in my mind - what happens if
multiple processes mount bpffs filesystems currently - do they each
get their own superbock or is that shared?
If the latter, then making this part of the comparison will block
subsequent mounts by tasks with different SIDs.
But not doing something here would lead to using whoever was the last
mounter for the SID, so I agree something needs to happen.
> > @@ -7144,10 +7180,15 @@ 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->token_create.bpffs_fd);
> > + int err;
> >
> > bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> > if (!bpfsec)
>
> This isn't an issue with your code, and to be clear I'm not asking you
> to fix this, but more as a FYI/TODO for me (or anyone else who would
> care to do it), after the BPF token patch is merged, we should move the
> @bpfsec allocation/lifecycle-management for tokens out to the LSM
> framework so it can properly handle multiple LSMs.
This already happened in the LSM tree, just hasn't been merged to the
SELinux tree yet.
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v3] SELinux: Add support for BPF token access control
2025-09-18 13:36 ` Stephen Smalley
@ 2025-09-18 14:59 ` Paul Moore
2025-10-06 16:46 ` Eric Suen
0 siblings, 1 reply; 9+ messages in thread
From: Paul Moore @ 2025-09-18 14:59 UTC (permalink / raw)
To: Stephen Smalley; +Cc: Eric Suen, selinux, omosnace, danieldurning.work
On Thu, Sep 18, 2025 at 9:36 AM Stephen Smalley
<stephen.smalley.work@gmail.com> wrote:
> On Wed, Sep 17, 2025 at 5:48 PM Paul Moore <paul@paul-moore.com> wrote:
> > On Aug 21, 2025 Eric Suen <ericsu@linux.microsoft.com> wrote:
> > >
> > > 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);
> > >
> > > The implementation introduces two new permissions:
> > > - map_create_as
> > > - prog_load_as
> > >
> > > At token creation time, SELinux verifies that the current process has the
> > > appropriate `*_as` permission (depending on the `allowed_cmds` value in
> > > the bpf_token) to act on behalf of the `creator_sid`.
> > >
> > > Example SELinux policy:
> > > allow test_bpf_t self:bpf {
> > > map_create map_read map_write prog_load prog_run
> > > map_create_as prog_load_as
> > > };
> > >
> > > Additionally, a new policy capability bpf_token_perms is added to ensure
> > > backward compatibility. If disabled, previous behavior ((checks based on
> > > current process SID)) is preserved.
> > >
> > > Changes in v2:
> > > - Fixed bug in selinux_bpffs_creator_sid(u32 fd) where it retrieved
> > > creator_sid from wrong file descriptor
> > > - Removed unnecessary checks for null, per review comments from
> > > the first patch
> > >
> > > Changes in v3:
> > > - Removed check for 'sid == SECSID_NULL' in selinux_bpf_token_create and
> > > allow it to fall through to the permission checks which will fail as
> > > access denied to unlabeled_t
> > >
> > > Signed-off-by: Eric Suen <ericsu@linux.microsoft.com>
> > > Tested-by: Daniel Durning <danieldurning.work@gmail.com>
> > > Reviewed-by: Daniel Durning <danieldurning.work@gmail.com>
> > > Acked-by: Stephen Smalley <stephen.smalley.work@gmail.com>
> > > ---
>
> > > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > > index e474cd7398ef..d949e9c5aa31 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();
> >
> > A few things come to mind as I'm looking at the new creator_sid field.
> >
> > First, we should explicitly initialize @sbsec->creator_sid to
> > SECINITSID_UNLABELED in selinux_sb_alloc_security() just as we do the
> > other fields. Yes, the LSM framework does some basic clearing, but this
> > is safer long term and it isn't likely that superblock allocation is
> > going to be common enough to where the extra assignment is going to be
> > a concern.
> >
> > While I don't think this is an immediate issue, I think it's a good idea
> > to ensure that the selinux_cmp_sb_context() function compares the
> > @sbsec->creator_sid fields and only returns true/"match" if they are the
> > same.
>
> Hmm...that raises another question in my mind - what happens if
> multiple processes mount bpffs filesystems currently - do they each
> get their own superbock or is that shared?
Good question. If the superblock is shared among all mount instances
we need to do some extra work to determine the creator label/SID for
each instance.
> If the latter, then making this part of the comparison will block
> subsequent mounts by tasks with different SIDs.
> But not doing something here would lead to using whoever was the last
> mounter for the SID, so I agree something needs to happen.
Eric, can you look into the code and do some testing?
> > > @@ -7144,10 +7180,15 @@ 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->token_create.bpffs_fd);
> > > + int err;
> > >
> > > bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
> > > if (!bpfsec)
> >
> > This isn't an issue with your code, and to be clear I'm not asking you
> > to fix this, but more as a FYI/TODO for me (or anyone else who would
> > care to do it), after the BPF token patch is merged, we should move the
> > @bpfsec allocation/lifecycle-management for tokens out to the LSM
> > framework so it can properly handle multiple LSMs.
>
> This already happened in the LSM tree, just hasn't been merged to the
> SELinux tree yet.
Ah, yes, I forgot all about Blaise's patch! My apologies. At least
that is one less thing for the TODO list ;)
--
paul-moore.com
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v3] SELinux: Add support for BPF token access control
2025-09-18 14:59 ` Paul Moore
@ 2025-10-06 16:46 ` Eric Suen
0 siblings, 0 replies; 9+ messages in thread
From: Eric Suen @ 2025-10-06 16:46 UTC (permalink / raw)
To: Paul Moore, Stephen Smalley; +Cc: selinux, omosnace, danieldurning.work
On 9/18/2025 7:59 AM, Paul Moore wrote:
> On Thu, Sep 18, 2025 at 9:36 AM Stephen Smalley
> <stephen.smalley.work@gmail.com> wrote:
>> On Wed, Sep 17, 2025 at 5:48 PM Paul Moore<paul@paul-moore.com> wrote:
>>> On Aug 21, 2025 Eric Suen<ericsu@linux.microsoft.com> wrote:
>>>> 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);
>>>>
>>>> The implementation introduces two new permissions:
>>>> - map_create_as
>>>> - prog_load_as
>>>>
>>>> At token creation time, SELinux verifies that the current process has the
>>>> appropriate `*_as` permission (depending on the `allowed_cmds` value in
>>>> the bpf_token) to act on behalf of the `creator_sid`.
>>>>
>>>> Example SELinux policy:
>>>> allow test_bpf_t self:bpf {
>>>> map_create map_read map_write prog_load prog_run
>>>> map_create_as prog_load_as
>>>> };
>>>>
>>>> Additionally, a new policy capability bpf_token_perms is added to ensure
>>>> backward compatibility. If disabled, previous behavior ((checks based on
>>>> current process SID)) is preserved.
>>>>
>>>> Changes in v2:
>>>> - Fixed bug in selinux_bpffs_creator_sid(u32 fd) where it retrieved
>>>> creator_sid from wrong file descriptor
>>>> - Removed unnecessary checks for null, per review comments from
>>>> the first patch
>>>>
>>>> Changes in v3:
>>>> - Removed check for 'sid == SECSID_NULL' in selinux_bpf_token_create and
>>>> allow it to fall through to the permission checks which will fail as
>>>> access denied to unlabeled_t
>>>>
>>>> Signed-off-by: Eric Suen<ericsu@linux.microsoft.com>
>>>> Tested-by: Daniel Durning<danieldurning.work@gmail.com>
>>>> Reviewed-by: Daniel Durning<danieldurning.work@gmail.com>
>>>> Acked-by: Stephen Smalley<stephen.smalley.work@gmail.com>
>>>> ---
>>>> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
>>>> index e474cd7398ef..d949e9c5aa31 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();
>>> A few things come to mind as I'm looking at the new creator_sid field.
>>>
>>> First, we should explicitly initialize @sbsec->creator_sid to
>>> SECINITSID_UNLABELED in selinux_sb_alloc_security() just as we do the
>>> other fields. Yes, the LSM framework does some basic clearing, but this
>>> is safer long term and it isn't likely that superblock allocation is
>>> going to be common enough to where the extra assignment is going to be
>>> a concern.
>>>
>>> While I don't think this is an immediate issue, I think it's a good idea
>>> to ensure that the selinux_cmp_sb_context() function compares the
>>> @sbsec->creator_sid fields and only returns true/"match" if they are the
>>> same.
>> Hmm...that raises another question in my mind - what happens if
>> multiple processes mount bpffs filesystems currently - do they each
>> get their own superbock or is that shared?
> Good question. If the superblock is shared among all mount instances
> we need to do some extra work to determine the creator label/SID for
> each instance.
>
>> If the latter, then making this part of the comparison will block
>> subsequent mounts by tasks with different SIDs.
>> But not doing something here would lead to using whoever was the last
>> mounter for the SID, so I agree something needs to happen.
> Eric, can you look into the code and do some testing?
It doesn't appear the superblock is shared between processes. Here is
what I did to verify:
1. Modified test_bpf_map_create in testsuite so it forks/starts two
concurrent processes that each trigger bpf map creation.
2. Added debug printf in selinux_bpffs_creator_sid (in
'selinux/security/selinux/hooks.c') to print out current task pid/tgid,
super_block pointer, and superblock_security_struct pointer.
3. Observed that the printed super_block and superblock_security_struct
addresses were unique for each pid/tgid, indicating a distinct
superblock instance per mount.
From the code level, Paul also helped to review bpffs superblock
creation. Here is his response:
You can confirm this by looking at the bpf_get_tree() function which is
used to create a new bpffs tree. The bpf_get_tree() function calls into
get_tree_nodev(), which calls into vfs_get_super(), which calls into
sget_fc() with a NULL "test" parameter. When sget_fc() is called with a
NULL "test" parameter a new superblock is created regardless of if any
existing bpffs superblocks already exist.
>>>> @@ -7144,10 +7180,15 @@ 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->token_create.bpffs_fd);
>>>> + int err;
>>>>
>>>> bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL);
>>>> if (!bpfsec)
>>> This isn't an issue with your code, and to be clear I'm not asking you
>>> to fix this, but more as a FYI/TODO for me (or anyone else who would
>>> care to do it), after the BPF token patch is merged, we should move the
>>> @bpfsec allocation/lifecycle-management for tokens out to the LSM
>>> framework so it can properly handle multiple LSMs.
>> This already happened in the LSM tree, just hasn't been merged to the
>> SELinux tree yet.
> Ah, yes, I forgot all about Blaise's patch! My apologies. At least
> that is one less thing for the TODO list ;)
>
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2025-10-06 16:46 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-21 18:20 [PATCH v3] SELinux: Add support for BPF token access control Eric Suen
2025-08-21 19:06 ` Stephen Smalley
2025-08-21 19:41 ` Stephen Smalley
2025-08-22 17:57 ` Daniel Durning
2025-08-25 14:08 ` Stephen Smalley
2025-09-17 21:48 ` Paul Moore
2025-09-18 13:36 ` Stephen Smalley
2025-09-18 14:59 ` Paul Moore
2025-10-06 16:46 ` 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).