bpf.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Andrii Nakryiko <andrii@kernel.org>
To: <bpf@vger.kernel.org>, <ast@kernel.org>, <daniel@iogearbox.net>,
	<kpsingh@kernel.org>, <keescook@chromium.org>,
	<paul@paul-moore.com>
Cc: <linux-security-module@vger.kernel.org>,
	Andrii Nakryiko <andrii@kernel.org>
Subject: [PATCH bpf-next 4/8] bpf, lsm: implement bpf_map_create_security LSM hook
Date: Tue, 11 Apr 2023 21:32:56 -0700	[thread overview]
Message-ID: <20230412043300.360803-5-andrii@kernel.org> (raw)
In-Reply-To: <20230412043300.360803-1-andrii@kernel.org>

Add new LSM hook, bpf_map_create_security, that allows custom LSM
security policies controlling BPF map creation permissions granularly
and precisely.

This new LSM hook allows to implement both LSM policy that could enforce
more granular and restrictive decisions about which processes can create
which BPF maps, by rejecting BPF map creation based on passed in
bpf_attr attributes. But also it allows to bypass CAP_BPF and
CAP_NET_ADMIN restrictions, normally enforced by kernel, for
applications that LSM policy deems trusted. Trustworthiness
determination of the process/user/cgroup/etc is left up to custom LSM
hook implementation and will dependon particular production setup of
each individual use case.

If LSM policy wants to rely on default kernel logic, it can return
0 to delegate back to kernel. If it returns >0 return code,
kernel will bypass its normal checks. This way it's possible to perform
a delegation of trust (specifically for BPF map creation) from
privileged LSM custom policy implementation to unprivileged user
process, verifier and trusted by custom LSM policy.

Such model allows flexible and secure-by-default approach where user
processes that need to use BPF features (BPF map creation, in this case)
are left unprivileged with no CAP_BPF, CAP_NET_ADMIN, CAP_PERFMON, etc.
capabilities, but specific exceptions are implemented (usually in
a centralized server fleet-wide fashion) for trusted
processes/containers/users, allowing them to manipulate BPF facilities,
as long as they are allowed and known apriori.

This patch implements first required part for full-fledged BPF usage:
map creation. The other one, BPF program load, will be addressed in
follow up patches.

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
 include/linux/lsm_hook_defs.h |  1 +
 include/linux/lsm_hooks.h     | 12 ++++++++++++
 include/linux/security.h      |  6 ++++++
 kernel/bpf/bpf_lsm.c          |  1 +
 kernel/bpf/syscall.c          | 19 ++++++++++++++++---
 security/security.c           |  4 ++++
 6 files changed, 40 insertions(+), 3 deletions(-)

diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 094b76dc7164..b4fe9ed7021a 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -396,6 +396,7 @@ LSM_HOOK(void, LSM_RET_VOID, audit_rule_free, void *lsmrule)
 LSM_HOOK(int, 0, bpf, int cmd, union bpf_attr *attr, unsigned int size)
 LSM_HOOK(int, 0, bpf_map, struct bpf_map *map, fmode_t fmode)
 LSM_HOOK(int, 0, bpf_prog, struct bpf_prog *prog)
+LSM_HOOK(int, 0, bpf_map_create_security, const union bpf_attr *attr)
 LSM_HOOK(int, 0, bpf_map_alloc_security, struct bpf_map *map)
 LSM_HOOK(void, LSM_RET_VOID, bpf_map_free_security, struct bpf_map *map)
 LSM_HOOK(int, 0, bpf_prog_alloc_security, struct bpf_prog_aux *aux)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 6e156d2acffc..42bf7c0aa4d8 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1598,6 +1598,18 @@
  *	@prog: bpf prog that userspace want to use.
  *	Return 0 if permission is granted.
  *
+ * @bpf_map_create_security:
+ *	Do a check to determine permission to create requested BPF map.
+ *	Implementation can override kernel capabilities checks according to
+ *	the rules below:
+ *	  - 0 should be returned to delegate permission checks to other
+ *	    installed LSM callbacks and/or hard-wired kernel logic, which
+ *	    would enforce CAP_BPF/CAP_NET_ADMIN capabilities;
+ *	  - reject BPF map creation by returning -EPERM or any other
+ *	    negative error code;
+ *	  - allow BPF map creation, overriding kernel checks, by returning
+ *	    a positive result.
+ *
  * @bpf_map_alloc_security:
  *	Initialize the security field inside bpf map.
  *	Return 0 on success, error on failure.
diff --git a/include/linux/security.h b/include/linux/security.h
index 5984d0d550b4..e5374fe92ef6 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -2023,6 +2023,7 @@ struct bpf_prog_aux;
 extern int security_bpf(int cmd, union bpf_attr *attr, unsigned int size);
 extern int security_bpf_map(struct bpf_map *map, fmode_t fmode);
 extern int security_bpf_prog(struct bpf_prog *prog);
+extern int security_bpf_map_create(const union bpf_attr *attr);
 extern int security_bpf_map_alloc(struct bpf_map *map);
 extern void security_bpf_map_free(struct bpf_map *map);
 extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux);
@@ -2044,6 +2045,11 @@ static inline int security_bpf_prog(struct bpf_prog *prog)
 	return 0;
 }
 
+static inline int security_bpf_map_create(const union bpf_attr *attr)
+{
+	return 0;
+}
+
 static inline int security_bpf_map_alloc(struct bpf_map *map)
 {
 	return 0;
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index e14c822f8911..931d4dda5dac 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -260,6 +260,7 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 BTF_SET_START(sleepable_lsm_hooks)
 BTF_ID(func, bpf_lsm_bpf)
 BTF_ID(func, bpf_lsm_bpf_map)
+BTF_ID(func, bpf_lsm_bpf_map_create_security)
 BTF_ID(func, bpf_lsm_bpf_map_alloc_security)
 BTF_ID(func, bpf_lsm_bpf_map_free_security)
 BTF_ID(func, bpf_lsm_bpf_prog)
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index cbea4999e92f..7d1165814efc 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -980,7 +980,7 @@ int map_check_no_btf(const struct bpf_map *map,
 }
 
 static int map_check_btf(struct bpf_map *map, const struct btf *btf,
-			 u32 btf_key_id, u32 btf_value_id)
+			 u32 btf_key_id, u32 btf_value_id, bool priv_checked)
 {
 	const struct btf_type *key_type, *value_type;
 	u32 key_size, value_size;
@@ -1008,7 +1008,7 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf,
 	if (!IS_ERR_OR_NULL(map->record)) {
 		int i;
 
-		if (!bpf_capable()) {
+		if (!priv_checked && !bpf_capable()) {
 			ret = -EPERM;
 			goto free_map_tab;
 		}
@@ -1097,10 +1097,12 @@ static int map_create(union bpf_attr *attr)
 	int numa_node = bpf_map_attr_numa_node(attr);
 	u32 map_type = attr->map_type;
 	struct btf_field_offs *foffs;
+	bool priv_checked = false;
 	struct bpf_map *map;
 	int f_flags;
 	int err;
 
+	/* sanity checks */
 	err = CHECK_ATTR(BPF_MAP_CREATE);
 	if (err)
 		return -EINVAL;
@@ -1145,6 +1147,15 @@ static int map_create(union bpf_attr *attr)
 	if (!ops->map_mem_usage)
 		return -EINVAL;
 
+	/* security checks */
+	err = security_bpf_map_create(attr);
+	if (err < 0)
+		return err;
+	if (err > 0) {
+		priv_checked = true;
+		goto skip_priv_checks;
+	}
+
 	/* Intent here is for unprivileged_bpf_disabled to block key object
 	 * creation commands for unprivileged users; other actions depend
 	 * of fd availability and access to bpffs, so are dependent on
@@ -1203,6 +1214,8 @@ static int map_create(union bpf_attr *attr)
 		return -EPERM;
 	}
 
+skip_priv_checks:
+	/* create and init map */
 	map = ops->map_alloc(attr);
 	if (IS_ERR(map))
 		return PTR_ERR(map);
@@ -1243,7 +1256,7 @@ static int map_create(union bpf_attr *attr)
 
 		if (attr->btf_value_type_id) {
 			err = map_check_btf(map, btf, attr->btf_key_type_id,
-					    attr->btf_value_type_id);
+					    attr->btf_value_type_id, priv_checked);
 			if (err)
 				goto free_map;
 		}
diff --git a/security/security.c b/security/security.c
index cf6cc576736f..f9b885680966 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2682,6 +2682,10 @@ int security_bpf_prog(struct bpf_prog *prog)
 {
 	return call_int_hook(bpf_prog, 0, prog);
 }
+int security_bpf_map_create(const union bpf_attr *attr)
+{
+	return call_int_hook(bpf_map_create_security, 0, attr);
+}
 int security_bpf_map_alloc(struct bpf_map *map)
 {
 	return call_int_hook(bpf_map_alloc_security, 0, map);
-- 
2.34.1


  parent reply	other threads:[~2023-04-12  4:34 UTC|newest]

Thread overview: 52+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-04-12  4:32 [PATCH bpf-next 0/8] New BPF map and BTF security LSM hooks Andrii Nakryiko
2023-04-12  4:32 ` [PATCH bpf-next 1/8] bpf: move unprivileged checks into map_create() and bpf_prog_load() Andrii Nakryiko
2023-04-12 17:49   ` Kees Cook
2023-04-13  0:22     ` Andrii Nakryiko
2023-04-12  4:32 ` [PATCH bpf-next 2/8] bpf: inline map creation logic in map_create() function Andrii Nakryiko
2023-04-12 17:53   ` Kees Cook
2023-04-13  0:22     ` Andrii Nakryiko
2023-04-12  4:32 ` [PATCH bpf-next 3/8] bpf: centralize permissions checks for all BPF map types Andrii Nakryiko
2023-04-12 18:01   ` Kees Cook
2023-04-13  0:23     ` Andrii Nakryiko
2023-04-12  4:32 ` Andrii Nakryiko [this message]
2023-04-12 18:20   ` [PATCH bpf-next 4/8] bpf, lsm: implement bpf_map_create_security LSM hook Kees Cook
2023-04-13  0:23     ` Andrii Nakryiko
2023-04-12  4:32 ` [PATCH bpf-next 5/8] selftests/bpf: validate new " Andrii Nakryiko
2023-04-12 18:23   ` Kees Cook
2023-04-13  0:23     ` Andrii Nakryiko
2023-04-12  4:32 ` [PATCH bpf-next 6/8] bpf: drop unnecessary bpf_capable() check in BPF_MAP_FREEZE command Andrii Nakryiko
2023-04-12 18:24   ` Kees Cook
2023-04-13  0:17     ` Andrii Nakryiko
2023-04-12  4:32 ` [PATCH bpf-next 7/8] bpf, lsm: implement bpf_btf_load_security LSM hook Andrii Nakryiko
2023-04-12 16:52   ` Paul Moore
2023-04-13  1:43     ` Andrii Nakryiko
2023-04-13  2:47       ` Paul Moore
2023-04-12  4:33 ` [PATCH bpf-next 8/8] selftests/bpf: enhance lsm_map_create test with BTF LSM control Andrii Nakryiko
2023-04-12 16:49 ` [PATCH bpf-next 0/8] New BPF map and BTF security LSM hooks Paul Moore
2023-04-12 17:47   ` Kees Cook
2023-04-12 18:06     ` Paul Moore
2023-04-12 18:28       ` Kees Cook
2023-04-12 19:06         ` Paul Moore
2023-04-13  1:43           ` Andrii Nakryiko
2023-04-13  2:56             ` Paul Moore
2023-04-13  5:16               ` Andrii Nakryiko
2023-04-13 15:11                 ` Paul Moore
2023-04-17 23:29                   ` Andrii Nakryiko
2023-04-18  0:47                     ` Casey Schaufler
2023-04-21  0:00                       ` Andrii Nakryiko
2023-04-18 14:21                     ` Paul Moore
2023-04-21  0:00                       ` Andrii Nakryiko
2023-04-21 18:57                         ` Kees Cook
2023-04-13 16:54                 ` Casey Schaufler
2023-04-17 23:31                   ` Andrii Nakryiko
2023-04-13 19:03                 ` Jonathan Corbet
2023-04-17 23:28                   ` Andrii Nakryiko
2023-04-13 16:27             ` Casey Schaufler
2023-04-17 23:31               ` Andrii Nakryiko
2023-04-17 23:53                 ` Casey Schaufler
2023-04-18  0:28                   ` Andrii Nakryiko
2023-04-18  0:52                     ` Casey Schaufler
2023-04-12 18:38       ` Casey Schaufler
2023-04-14 20:23     ` Dr. Greg
2023-04-17 23:31       ` Andrii Nakryiko
2023-04-19 10:53         ` Dr. Greg

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230412043300.360803-5-andrii@kernel.org \
    --to=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=keescook@chromium.org \
    --cc=kpsingh@kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=paul@paul-moore.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).