summaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/selinux/hooks.c163
-rw-r--r--security/selinux/include/classmap.h2
-rw-r--r--security/selinux/include/objsec.h3
-rw-r--r--security/selinux/include/policycap.h1
-rw-r--r--security/selinux/include/policycap_names.h1
-rw-r--r--security/selinux/include/security.h6
6 files changed, 151 insertions, 25 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index d053ce562370..feda34b18d83 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -737,6 +737,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;
@@ -908,6 +910,8 @@ static int selinux_cmp_sb_context(const struct super_block *oldsb,
if (oldroot->sid != newroot->sid)
goto mismatch;
}
+ if (old->creator_sid != new->creator_sid)
+ goto mismatch;
return 0;
mismatch:
pr_warn("SELinux: mount invalid. Same superblock, "
@@ -967,6 +971,7 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
newsbsec->sid = oldsbsec->sid;
newsbsec->def_sid = oldsbsec->def_sid;
newsbsec->behavior = oldsbsec->behavior;
+ newsbsec->creator_sid = oldsbsec->creator_sid;
if (newsbsec->behavior == SECURITY_FS_USE_NATIVE &&
!(kern_flags & SECURITY_LSM_NATIVE_LABELS) && !set_context) {
@@ -1654,7 +1659,6 @@ static int cred_has_capability(const struct cred *cred,
break;
default:
pr_err("SELinux: out of range capability %d\n", cap);
- BUG();
return -EINVAL;
}
@@ -2586,6 +2590,7 @@ static int selinux_sb_alloc_security(struct super_block *sb)
sbsec->sid = SECINITSID_UNLABELED;
sbsec->def_sid = SECINITSID_FILE;
sbsec->mntpoint_sid = SECINITSID_UNLABELED;
+ sbsec->creator_sid = SECINITSID_UNLABELED;
return 0;
}
@@ -7043,6 +7048,9 @@ static int selinux_bpf(int cmd, union bpf_attr *attr,
u32 sid = current_sid();
int ret;
+ 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,
@@ -7124,60 +7132,144 @@ static int selinux_bpf_prog(struct bpf_prog *prog)
BPF__PROG_RUN, NULL);
}
+static u32 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 = selinux_bpf_map_security(map);
bpfsec->sid = current_sid();
- 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 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 = selinux_bpf_prog_security(prog);
bpfsec->sid = current_sid();
- 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 int selinux_bpf_token_create(struct bpf_token *token, union bpf_attr *attr,
+#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 = selinux_bpf_token_security(token);
bpfsec->sid = current_sid();
+ bpfsec->grantor_sid = sid;
+
+ 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;
}
-#endif
-struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = {
- .lbs_cred = sizeof(struct cred_security_struct),
- .lbs_task = sizeof(struct task_security_struct),
- .lbs_file = sizeof(struct file_security_struct),
- .lbs_inode = sizeof(struct inode_security_struct),
- .lbs_ipc = sizeof(struct ipc_security_struct),
- .lbs_key = sizeof(struct key_security_struct),
- .lbs_msg_msg = sizeof(struct msg_security_struct),
-#ifdef CONFIG_PERF_EVENTS
- .lbs_perf_event = sizeof(struct perf_event_security_struct),
+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))
+ return -EACCES;
+ break;
+ case BPF_PROG_LOAD:
+ if (!(bpfsec->perms & BPF__PROG_LOAD))
+ return -EACCES;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int selinux_bpf_token_capable(const struct bpf_token *token, int cap)
+{
+ u16 sclass;
+ struct bpf_security_struct *bpfsec = token->security;
+ bool initns = (token->userns == &init_user_ns);
+ u32 av = CAP_TO_MASK(cap);
+
+ switch (CAP_TO_INDEX(cap)) {
+ case 0:
+ sclass = initns ? SECCLASS_CAPABILITY : SECCLASS_CAP_USERNS;
+ break;
+ case 1:
+ sclass = initns ? SECCLASS_CAPABILITY2 : SECCLASS_CAP2_USERNS;
+ break;
+ default:
+ pr_err("SELinux: out of range capability %d\n", cap);
+ return -EINVAL;
+ }
+
+ return avc_has_perm(current_sid(), bpfsec->grantor_sid, sclass, av,
+ NULL);
+}
#endif
- .lbs_sock = sizeof(struct sk_security_struct),
- .lbs_superblock = sizeof(struct superblock_security_struct),
- .lbs_xattr_count = SELINUX_INODE_INIT_XATTRS,
- .lbs_tun_dev = sizeof(struct tun_security_struct),
- .lbs_ib = sizeof(struct ib_security_struct),
- .lbs_bpf_map = sizeof(struct bpf_security_struct),
- .lbs_bpf_prog = sizeof(struct bpf_security_struct),
- .lbs_bpf_token = sizeof(struct bpf_security_struct),
-};
#ifdef CONFIG_PERF_EVENTS
static int selinux_perf_event_open(int type)
@@ -7297,6 +7389,27 @@ static const struct lsm_id selinux_lsmid = {
.id = LSM_ID_SELINUX,
};
+struct lsm_blob_sizes selinux_blob_sizes __ro_after_init = {
+ .lbs_cred = sizeof(struct cred_security_struct),
+ .lbs_task = sizeof(struct task_security_struct),
+ .lbs_file = sizeof(struct file_security_struct),
+ .lbs_inode = sizeof(struct inode_security_struct),
+ .lbs_ipc = sizeof(struct ipc_security_struct),
+ .lbs_key = sizeof(struct key_security_struct),
+ .lbs_msg_msg = sizeof(struct msg_security_struct),
+#ifdef CONFIG_PERF_EVENTS
+ .lbs_perf_event = sizeof(struct perf_event_security_struct),
+#endif
+ .lbs_sock = sizeof(struct sk_security_struct),
+ .lbs_superblock = sizeof(struct superblock_security_struct),
+ .lbs_xattr_count = SELINUX_INODE_INIT_XATTRS,
+ .lbs_tun_dev = sizeof(struct tun_security_struct),
+ .lbs_ib = sizeof(struct ib_security_struct),
+ .lbs_bpf_map = sizeof(struct bpf_security_struct),
+ .lbs_bpf_prog = sizeof(struct bpf_security_struct),
+ .lbs_bpf_token = sizeof(struct bpf_security_struct),
+};
+
/*
* IMPORTANT NOTE: When adding new hooks, please be careful to keep this order:
* 1. any hooks that don't belong to (2.) or (3.) below,
@@ -7590,6 +7703,8 @@ 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),
+ LSM_HOOK_INIT(bpf_token_capable, selinux_bpf_token_capable),
#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 3ec85142771f..90cb61b16425 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 8fc3de5234ac..5bddd28ea5cb 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -92,6 +92,7 @@ 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 */
unsigned short behavior; /* labeling behavior */
unsigned short flags; /* which mount options were specified */
struct mutex lock;
@@ -169,6 +170,8 @@ struct pkey_security_struct {
struct bpf_security_struct {
u32 sid; /* SID of bpf obj creator */
+ u32 perms; /* permissions for allowed bpf token commands */
+ u32 grantor_sid; /* SID of token grantor */
};
struct perf_event_security_struct {
diff --git a/security/selinux/include/policycap.h b/security/selinux/include/policycap.h
index 231d02227e59..dbf39358ae6a 100644
--- a/security/selinux/include/policycap.h
+++ b/security/selinux/include/policycap.h
@@ -19,6 +19,7 @@ enum {
POLICYDB_CAP_GENFS_SECLABEL_WILDCARD,
POLICYDB_CAP_FUNCTIONFS_SECLABEL,
POLICYDB_CAP_MEMFD_CLASS,
+ 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 454dab37bda3..6e2b808e12e8 100644
--- a/security/selinux/include/policycap_names.h
+++ b/security/selinux/include/policycap_names.h
@@ -22,6 +22,7 @@ const char *const selinux_policycap_names[__POLICYDB_CAP_MAX] = {
"genfs_seclabel_wildcard",
"functionfs_seclabel",
"memfd_class",
+ "bpf_token_perms",
};
/* clang-format on */
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 5d1dad8058b1..d1f16d7f684d 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -214,6 +214,12 @@ static inline bool selinux_policycap_memfd_class(void)
return READ_ONCE(selinux_state.policycap[POLICYDB_CAP_MEMFD_CLASS]);
}
+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 {