Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Add tests verifying bpf lsm userns_create hook

The LSM hook userns_create was introduced to provide LSM's an
opportunity to block or allow unprivileged user namespace creation. This
test serves two purposes: it provides a test eBPF implementation, and
tests the hook successfully blocks or allows user namespace creation.

This tests 3 cases:

1. Unattached bpf program does not block unpriv user namespace
creation.
2. Attached bpf program allows user namespace creation given
CAP_SYS_ADMIN privileges.
3. Attached bpf program denies user namespace creation for a
user without CAP_SYS_ADMIN.

Acked-by: KP Singh <kpsingh@kernel.org>
Signed-off-by: Frederick Lawler <fred@cloudflare.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>

authored by

Frederick Lawler and committed by
Paul Moore
d5810139 401e64b3

+135
+102
tools/testing/selftests/bpf/prog_tests/deny_namespace.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #define _GNU_SOURCE 3 + #include <test_progs.h> 4 + #include "test_deny_namespace.skel.h" 5 + #include <sched.h> 6 + #include "cap_helpers.h" 7 + #include <stdio.h> 8 + 9 + static int wait_for_pid(pid_t pid) 10 + { 11 + int status, ret; 12 + 13 + again: 14 + ret = waitpid(pid, &status, 0); 15 + if (ret == -1) { 16 + if (errno == EINTR) 17 + goto again; 18 + 19 + return -1; 20 + } 21 + 22 + if (!WIFEXITED(status)) 23 + return -1; 24 + 25 + return WEXITSTATUS(status); 26 + } 27 + 28 + /* negative return value -> some internal error 29 + * positive return value -> userns creation failed 30 + * 0 -> userns creation succeeded 31 + */ 32 + static int create_user_ns(void) 33 + { 34 + pid_t pid; 35 + 36 + pid = fork(); 37 + if (pid < 0) 38 + return -1; 39 + 40 + if (pid == 0) { 41 + if (unshare(CLONE_NEWUSER)) 42 + _exit(EXIT_FAILURE); 43 + _exit(EXIT_SUCCESS); 44 + } 45 + 46 + return wait_for_pid(pid); 47 + } 48 + 49 + static void test_userns_create_bpf(void) 50 + { 51 + __u32 cap_mask = 1ULL << CAP_SYS_ADMIN; 52 + __u64 old_caps = 0; 53 + 54 + cap_enable_effective(cap_mask, &old_caps); 55 + 56 + ASSERT_OK(create_user_ns(), "priv new user ns"); 57 + 58 + cap_disable_effective(cap_mask, &old_caps); 59 + 60 + ASSERT_EQ(create_user_ns(), EPERM, "unpriv new user ns"); 61 + 62 + if (cap_mask & old_caps) 63 + cap_enable_effective(cap_mask, NULL); 64 + } 65 + 66 + static void test_unpriv_userns_create_no_bpf(void) 67 + { 68 + __u32 cap_mask = 1ULL << CAP_SYS_ADMIN; 69 + __u64 old_caps = 0; 70 + 71 + cap_disable_effective(cap_mask, &old_caps); 72 + 73 + ASSERT_OK(create_user_ns(), "no-bpf unpriv new user ns"); 74 + 75 + if (cap_mask & old_caps) 76 + cap_enable_effective(cap_mask, NULL); 77 + } 78 + 79 + void test_deny_namespace(void) 80 + { 81 + struct test_deny_namespace *skel = NULL; 82 + int err; 83 + 84 + if (test__start_subtest("unpriv_userns_create_no_bpf")) 85 + test_unpriv_userns_create_no_bpf(); 86 + 87 + skel = test_deny_namespace__open_and_load(); 88 + if (!ASSERT_OK_PTR(skel, "skel load")) 89 + goto close_prog; 90 + 91 + err = test_deny_namespace__attach(skel); 92 + if (!ASSERT_OK(err, "attach")) 93 + goto close_prog; 94 + 95 + if (test__start_subtest("userns_create_bpf")) 96 + test_userns_create_bpf(); 97 + 98 + test_deny_namespace__detach(skel); 99 + 100 + close_prog: 101 + test_deny_namespace__destroy(skel); 102 + }
+33
tools/testing/selftests/bpf/progs/test_deny_namespace.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <linux/bpf.h> 3 + #include <bpf/bpf_helpers.h> 4 + #include <bpf/bpf_tracing.h> 5 + #include <errno.h> 6 + #include <linux/capability.h> 7 + 8 + struct kernel_cap_struct { 9 + __u32 cap[_LINUX_CAPABILITY_U32S_3]; 10 + } __attribute__((preserve_access_index)); 11 + 12 + struct cred { 13 + struct kernel_cap_struct cap_effective; 14 + } __attribute__((preserve_access_index)); 15 + 16 + char _license[] SEC("license") = "GPL"; 17 + 18 + SEC("lsm.s/userns_create") 19 + int BPF_PROG(test_userns_create, const struct cred *cred, int ret) 20 + { 21 + struct kernel_cap_struct caps = cred->cap_effective; 22 + int cap_index = CAP_TO_INDEX(CAP_SYS_ADMIN); 23 + __u32 cap_mask = CAP_TO_MASK(CAP_SYS_ADMIN); 24 + 25 + if (ret) 26 + return 0; 27 + 28 + ret = -EPERM; 29 + if (caps.cap[cap_index] & cap_mask) 30 + return 0; 31 + 32 + return -EPERM; 33 + }