All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kees Cook <keescook@chromium.org>
To: Andrii Nakryiko <andrii@kernel.org>
Cc: bpf@vger.kernel.org, ast@kernel.org, daniel@iogearbox.net,
	kpsingh@kernel.org, paul@paul-moore.com,
	linux-security-module@vger.kernel.org
Subject: Re: [PATCH bpf-next 5/8] selftests/bpf: validate new bpf_map_create_security LSM hook
Date: Wed, 12 Apr 2023 11:23:25 -0700	[thread overview]
Message-ID: <6436f71e.170a0220.75de.385b@mx.google.com> (raw)
In-Reply-To: <20230412043300.360803-6-andrii@kernel.org>

On Tue, Apr 11, 2023 at 09:32:57PM -0700, Andrii Nakryiko wrote:
> Add selftests that goes over every known map type and validates that
> a combination of privileged/unprivileged modes and allow/reject/pass-through
> LSM policy decisions behave as expected.
> 
> Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
> ---
>  .../selftests/bpf/prog_tests/lsm_map_create.c | 143 ++++++++++++++++++
>  .../selftests/bpf/progs/lsm_map_create.c      |  32 ++++
>  tools/testing/selftests/bpf/test_progs.h      |   6 +
>  3 files changed, 181 insertions(+)
>  create mode 100644 tools/testing/selftests/bpf/prog_tests/lsm_map_create.c
>  create mode 100644 tools/testing/selftests/bpf/progs/lsm_map_create.c
> 
> diff --git a/tools/testing/selftests/bpf/prog_tests/lsm_map_create.c b/tools/testing/selftests/bpf/prog_tests/lsm_map_create.c
> new file mode 100644
> index 000000000000..fee78b0448c3
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/lsm_map_create.c
> @@ -0,0 +1,143 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
> +#include "linux/bpf.h"
> +#include <test_progs.h>
> +#include <bpf/btf.h>
> +#include "cap_helpers.h"
> +#include "lsm_map_create.skel.h"
> +
> +static int drop_priv_caps(__u64 *old_caps)
> +{
> +	return cap_disable_effective((1ULL << CAP_BPF) |
> +				     (1ULL << CAP_PERFMON) |
> +				     (1ULL << CAP_NET_ADMIN) |
> +				     (1ULL << CAP_SYS_ADMIN), old_caps);
> +}
> +
> +static int restore_priv_caps(__u64 old_caps)
> +{
> +	return cap_enable_effective(old_caps, NULL);
> +}
> +
> +void test_lsm_map_create(void)
> +{
> +	struct btf *btf = NULL;
> +	struct lsm_map_create *skel = NULL;
> +	const struct btf_type *t;
> +	const struct btf_enum *e;
> +	int i, n, id, err, ret;
> +
> +	skel = lsm_map_create__open_and_load();
> +	if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
> +		return;
> +
> +	skel->bss->my_tid = syscall(SYS_gettid);
> +	skel->bss->decision = 0;
> +
> +	err = lsm_map_create__attach(skel);
> +	if (!ASSERT_OK(err, "skel_attach"))
> +		goto cleanup;
> +
> +	btf = btf__parse("/sys/kernel/btf/vmlinux", NULL);
> +	if (!ASSERT_OK_PTR(btf, "btf_parse"))
> +		goto cleanup;
> +
> +	/* find enum bpf_map_type and enumerate each value */
> +	id = btf__find_by_name_kind(btf, "bpf_map_type", BTF_KIND_ENUM);
> +	if (!ASSERT_GT(id, 0, "bpf_map_type_id"))
> +		goto cleanup;
> +
> +	t = btf__type_by_id(btf, id);
> +	e = btf_enum(t);
> +	n = btf_vlen(t);
> +	for (i = 0; i < n; e++, i++) {
> +		enum bpf_map_type map_type = (enum bpf_map_type)e->val;
> +		const char *map_type_name;
> +		__u64 orig_caps;
> +		bool is_map_priv;
> +		bool needs_btf;
> +
> +		if (map_type == BPF_MAP_TYPE_UNSPEC)
> +			continue;
> +
> +		/* this will show which map type we are working with in verbose log */
> +		map_type_name = btf__str_by_offset(btf, e->name_off);
> +		ASSERT_OK_PTR(map_type_name, map_type_name);
> +
> +		switch (map_type) {
> +		case BPF_MAP_TYPE_ARRAY:
> +		case BPF_MAP_TYPE_PERCPU_ARRAY:
> +		case BPF_MAP_TYPE_PROG_ARRAY:
> +		case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
> +		case BPF_MAP_TYPE_CGROUP_ARRAY:
> +		case BPF_MAP_TYPE_ARRAY_OF_MAPS:
> +		case BPF_MAP_TYPE_HASH:
> +		case BPF_MAP_TYPE_PERCPU_HASH:
> +		case BPF_MAP_TYPE_HASH_OF_MAPS:
> +		case BPF_MAP_TYPE_RINGBUF:
> +		case BPF_MAP_TYPE_USER_RINGBUF:
> +		case BPF_MAP_TYPE_CGROUP_STORAGE:
> +		case BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE:
> +			is_map_priv = false;
> +			needs_btf = false;
> +			break;
> +		case BPF_MAP_TYPE_SK_STORAGE:
> +		case BPF_MAP_TYPE_INODE_STORAGE:
> +		case BPF_MAP_TYPE_TASK_STORAGE:
> +		case BPF_MAP_TYPE_CGRP_STORAGE:
> +			is_map_priv = true;
> +			needs_btf = true;
> +			break;
> +		default:
> +			is_map_priv = true;
> +			needs_btf = false;
> +		}
> +
> +		/* make sure we delegate to kernel for final decision */
> +		skel->bss->decision = 0;
> +
> +		/* we are normally under sudo, so all maps should succeed */
> +		ret = libbpf_probe_bpf_map_type(map_type, NULL);
> +		ASSERT_EQ(ret, 1, "default_priv_mode");
> +
> +		/* local storage needs custom BTF to be loaded, which we
> +		 * currently can't do once we drop privileges, so skip few
> +		 * checks for such maps
> +		 */
> +		if (needs_btf)
> +			goto skip_if_needs_btf;
> +
> +		/* now let's drop privileges, and chech that unpriv maps are
> +		 * still possible to create
> +		 */
> +		if (!ASSERT_OK(drop_priv_caps(&orig_caps), "drop_caps"))
> +			goto cleanup;
> +
> +		ret = libbpf_probe_bpf_map_type(map_type, NULL);
> +		ASSERT_EQ(ret, is_map_priv ? 0 : 1,  "default_unpriv_mode");
> +
> +		/* allow any map creation for our thread */
> +		skel->bss->decision = 1;
> +		ret = libbpf_probe_bpf_map_type(map_type, NULL);
> +		ASSERT_EQ(ret, 1, "lsm_allow_unpriv_mode");
> +
> +		/* reject any map creation for our thread */
> +		skel->bss->decision = -1;
> +		ret = libbpf_probe_bpf_map_type(map_type, NULL);
> +		ASSERT_EQ(ret, 0, "lsm_reject_unpriv_mode");
> +
> +		/* restore privileges, but keep reject LSM policy */
> +		if (!ASSERT_OK(restore_priv_caps(orig_caps), "restore_caps"))
> +			goto cleanup;
> +
> +skip_if_needs_btf:
> +		/* even with all caps map create will fail */
> +		skel->bss->decision = -1;
> +		ret = libbpf_probe_bpf_map_type(map_type, NULL);
> +		ASSERT_EQ(ret, 0, "lsm_reject_priv_mode");
> +	}
> +
> +cleanup:
> +	btf__free(btf);
> +	lsm_map_create__destroy(skel);
> +}

This test looks good! One meta-comment about testing would be: are you
sure each needs to be ASSERT instead of EXPECT? (i.e. should forward
progress through this test always be aborted when a check fails?)

> diff --git a/tools/testing/selftests/bpf/progs/lsm_map_create.c b/tools/testing/selftests/bpf/progs/lsm_map_create.c
> new file mode 100644
> index 000000000000..093825c68459
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/lsm_map_create.c
> @@ -0,0 +1,32 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
> +
> +#include "vmlinux.h"
> +#include <bpf/bpf_helpers.h>
> +#include <bpf/bpf_tracing.h>
> +#include <errno.h>
> +
> +char _license[] SEC("license") = "GPL";
> +
> +int my_tid;
> +/* LSM enforcement:
> + *   - 0, delegate to kernel;
> + *   - 1, allow;
> + *   - -1, reject.
> + */
> +int decision;
> +
> +SEC("lsm/bpf_map_create_security")
> +int BPF_PROG(allow_unpriv_maps, union bpf_attr *attr)
> +{
> +	if (!my_tid || (u32)bpf_get_current_pid_tgid() != my_tid)
> +		return 0; /* keep processing LSM hooks */
> +
> +	if (decision == 0)
> +		return 0;
> +
> +	if (decision > 0)
> +		return 1; /* allow */
> +
> +	return -EPERM;
> +}
> diff --git a/tools/testing/selftests/bpf/test_progs.h b/tools/testing/selftests/bpf/test_progs.h
> index 10ba43250668..12f9c6652d40 100644
> --- a/tools/testing/selftests/bpf/test_progs.h
> +++ b/tools/testing/selftests/bpf/test_progs.h
> @@ -23,6 +23,7 @@ typedef __u16 __sum16;
>  #include <linux/perf_event.h>
>  #include <linux/socket.h>
>  #include <linux/unistd.h>
> +#include <sys/syscall.h>
>  
>  #include <sys/ioctl.h>
>  #include <sys/wait.h>
> @@ -176,6 +177,11 @@ void test__skip(void);
>  void test__fail(void);
>  int test__join_cgroup(const char *path);
>  
> +static inline int gettid(void)
> +{
> +	return syscall(SYS_gettid);
> +}
> +
>  #define PRINT_FAIL(format...)                                                  \
>  	({                                                                     \
>  		test__fail();                                                  \
> -- 
> 2.34.1
> 

-- 
Kees Cook

  reply	other threads:[~2023-04-12 18:23 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 ` [PATCH bpf-next 4/8] bpf, lsm: implement bpf_map_create_security LSM hook Andrii Nakryiko
2023-04-12 18:20   ` 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 [this message]
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=6436f71e.170a0220.75de.385b@mx.google.com \
    --to=keescook@chromium.org \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.