goto out;
}
+ sbsec->creator_sid = current_sid();
+
if (strcmp(sb->s_type->name, "proc") == 0)
sbsec->flags |= SE_SBPROC | SE_SBGENFS;
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, "
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) {
sbsec->sid = SECINITSID_UNLABELED;
sbsec->def_sid = SECINITSID_FILE;
sbsec->mntpoint_sid = SECINITSID_UNLABELED;
+ sbsec->creator_sid = SECINITSID_UNLABELED;
return 0;
}
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,
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;
+}
+
+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_CAPABILITY : 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
#ifdef CONFIG_PERF_EVENTS
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),