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

selftests/bpf: Verify that rebinding to port < 1024 from BPF works

Return 3 to indicate that permission check for port 111
should be skipped.

Signed-off-by: Stanislav Fomichev <sdf@google.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Martin KaFai Lau <kafai@fb.com>
Link: https://lore.kernel.org/bpf/20210127193140.3170382-2-sdf@google.com

authored by

Stanislav Fomichev and committed by
Alexei Starovoitov
8259fdeb 77241217

+154
+109
tools/testing/selftests/bpf/prog_tests/bind_perm.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <test_progs.h> 3 + #include "bind_perm.skel.h" 4 + 5 + #include <sys/types.h> 6 + #include <sys/socket.h> 7 + #include <sys/capability.h> 8 + 9 + static int duration; 10 + 11 + void try_bind(int family, int port, int expected_errno) 12 + { 13 + struct sockaddr_storage addr = {}; 14 + struct sockaddr_in6 *sin6; 15 + struct sockaddr_in *sin; 16 + int fd = -1; 17 + 18 + fd = socket(family, SOCK_STREAM, 0); 19 + if (CHECK(fd < 0, "fd", "errno %d", errno)) 20 + goto close_socket; 21 + 22 + if (family == AF_INET) { 23 + sin = (struct sockaddr_in *)&addr; 24 + sin->sin_family = family; 25 + sin->sin_port = htons(port); 26 + } else { 27 + sin6 = (struct sockaddr_in6 *)&addr; 28 + sin6->sin6_family = family; 29 + sin6->sin6_port = htons(port); 30 + } 31 + 32 + errno = 0; 33 + bind(fd, (struct sockaddr *)&addr, sizeof(addr)); 34 + ASSERT_EQ(errno, expected_errno, "bind"); 35 + 36 + close_socket: 37 + if (fd >= 0) 38 + close(fd); 39 + } 40 + 41 + bool cap_net_bind_service(cap_flag_value_t flag) 42 + { 43 + const cap_value_t cap_net_bind_service = CAP_NET_BIND_SERVICE; 44 + cap_flag_value_t original_value; 45 + bool was_effective = false; 46 + cap_t caps; 47 + 48 + caps = cap_get_proc(); 49 + if (CHECK(!caps, "cap_get_proc", "errno %d", errno)) 50 + goto free_caps; 51 + 52 + if (CHECK(cap_get_flag(caps, CAP_NET_BIND_SERVICE, CAP_EFFECTIVE, 53 + &original_value), 54 + "cap_get_flag", "errno %d", errno)) 55 + goto free_caps; 56 + 57 + was_effective = (original_value == CAP_SET); 58 + 59 + if (CHECK(cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap_net_bind_service, 60 + flag), 61 + "cap_set_flag", "errno %d", errno)) 62 + goto free_caps; 63 + 64 + if (CHECK(cap_set_proc(caps), "cap_set_proc", "errno %d", errno)) 65 + goto free_caps; 66 + 67 + free_caps: 68 + CHECK(cap_free(caps), "cap_free", "errno %d", errno); 69 + return was_effective; 70 + } 71 + 72 + void test_bind_perm(void) 73 + { 74 + bool cap_was_effective; 75 + struct bind_perm *skel; 76 + int cgroup_fd; 77 + 78 + cgroup_fd = test__join_cgroup("/bind_perm"); 79 + if (CHECK(cgroup_fd < 0, "cg-join", "errno %d", errno)) 80 + return; 81 + 82 + skel = bind_perm__open_and_load(); 83 + if (!ASSERT_OK_PTR(skel, "skel")) 84 + goto close_cgroup_fd; 85 + 86 + skel->links.bind_v4_prog = bpf_program__attach_cgroup(skel->progs.bind_v4_prog, cgroup_fd); 87 + if (!ASSERT_OK_PTR(skel, "bind_v4_prog")) 88 + goto close_skeleton; 89 + 90 + skel->links.bind_v6_prog = bpf_program__attach_cgroup(skel->progs.bind_v6_prog, cgroup_fd); 91 + if (!ASSERT_OK_PTR(skel, "bind_v6_prog")) 92 + goto close_skeleton; 93 + 94 + cap_was_effective = cap_net_bind_service(CAP_CLEAR); 95 + 96 + try_bind(AF_INET, 110, EACCES); 97 + try_bind(AF_INET6, 110, EACCES); 98 + 99 + try_bind(AF_INET, 111, 0); 100 + try_bind(AF_INET6, 111, 0); 101 + 102 + if (cap_was_effective) 103 + cap_net_bind_service(CAP_SET); 104 + 105 + close_skeleton: 106 + bind_perm__destroy(skel); 107 + close_cgroup_fd: 108 + close(cgroup_fd); 109 + }
+45
tools/testing/selftests/bpf/progs/bind_perm.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #include <linux/stddef.h> 4 + #include <linux/bpf.h> 5 + #include <sys/types.h> 6 + #include <sys/socket.h> 7 + #include <bpf/bpf_helpers.h> 8 + #include <bpf/bpf_endian.h> 9 + 10 + static __always_inline int bind_prog(struct bpf_sock_addr *ctx, int family) 11 + { 12 + struct bpf_sock *sk; 13 + 14 + sk = ctx->sk; 15 + if (!sk) 16 + return 0; 17 + 18 + if (sk->family != family) 19 + return 0; 20 + 21 + if (ctx->type != SOCK_STREAM) 22 + return 0; 23 + 24 + /* Return 1 OR'ed with the first bit set to indicate 25 + * that CAP_NET_BIND_SERVICE should be bypassed. 26 + */ 27 + if (ctx->user_port == bpf_htons(111)) 28 + return (1 | 2); 29 + 30 + return 1; 31 + } 32 + 33 + SEC("cgroup/bind4") 34 + int bind_v4_prog(struct bpf_sock_addr *ctx) 35 + { 36 + return bind_prog(ctx, AF_INET); 37 + } 38 + 39 + SEC("cgroup/bind6") 40 + int bind_v6_prog(struct bpf_sock_addr *ctx) 41 + { 42 + return bind_prog(ctx, AF_INET6); 43 + } 44 + 45 + char _license[] SEC("license") = "GPL";