forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Acked-by: Martin KaFai Lau <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
- Loading branch information
Showing
2 changed files
with
154 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
#include <test_progs.h> | ||
#include "bind_perm.skel.h" | ||
|
||
#include <sys/types.h> | ||
#include <sys/socket.h> | ||
#include <sys/capability.h> | ||
|
||
static int duration; | ||
|
||
void try_bind(int family, int port, int expected_errno) | ||
{ | ||
struct sockaddr_storage addr = {}; | ||
struct sockaddr_in6 *sin6; | ||
struct sockaddr_in *sin; | ||
int fd = -1; | ||
|
||
fd = socket(family, SOCK_STREAM, 0); | ||
if (CHECK(fd < 0, "fd", "errno %d", errno)) | ||
goto close_socket; | ||
|
||
if (family == AF_INET) { | ||
sin = (struct sockaddr_in *)&addr; | ||
sin->sin_family = family; | ||
sin->sin_port = htons(port); | ||
} else { | ||
sin6 = (struct sockaddr_in6 *)&addr; | ||
sin6->sin6_family = family; | ||
sin6->sin6_port = htons(port); | ||
} | ||
|
||
errno = 0; | ||
bind(fd, (struct sockaddr *)&addr, sizeof(addr)); | ||
ASSERT_EQ(errno, expected_errno, "bind"); | ||
|
||
close_socket: | ||
if (fd >= 0) | ||
close(fd); | ||
} | ||
|
||
bool cap_net_bind_service(cap_flag_value_t flag) | ||
{ | ||
const cap_value_t cap_net_bind_service = CAP_NET_BIND_SERVICE; | ||
cap_flag_value_t original_value; | ||
bool was_effective = false; | ||
cap_t caps; | ||
|
||
caps = cap_get_proc(); | ||
if (CHECK(!caps, "cap_get_proc", "errno %d", errno)) | ||
goto free_caps; | ||
|
||
if (CHECK(cap_get_flag(caps, CAP_NET_BIND_SERVICE, CAP_EFFECTIVE, | ||
&original_value), | ||
"cap_get_flag", "errno %d", errno)) | ||
goto free_caps; | ||
|
||
was_effective = (original_value == CAP_SET); | ||
|
||
if (CHECK(cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap_net_bind_service, | ||
flag), | ||
"cap_set_flag", "errno %d", errno)) | ||
goto free_caps; | ||
|
||
if (CHECK(cap_set_proc(caps), "cap_set_proc", "errno %d", errno)) | ||
goto free_caps; | ||
|
||
free_caps: | ||
CHECK(cap_free(caps), "cap_free", "errno %d", errno); | ||
return was_effective; | ||
} | ||
|
||
void test_bind_perm(void) | ||
{ | ||
bool cap_was_effective; | ||
struct bind_perm *skel; | ||
int cgroup_fd; | ||
|
||
cgroup_fd = test__join_cgroup("/bind_perm"); | ||
if (CHECK(cgroup_fd < 0, "cg-join", "errno %d", errno)) | ||
return; | ||
|
||
skel = bind_perm__open_and_load(); | ||
if (!ASSERT_OK_PTR(skel, "skel")) | ||
goto close_cgroup_fd; | ||
|
||
skel->links.bind_v4_prog = bpf_program__attach_cgroup(skel->progs.bind_v4_prog, cgroup_fd); | ||
if (!ASSERT_OK_PTR(skel, "bind_v4_prog")) | ||
goto close_skeleton; | ||
|
||
skel->links.bind_v6_prog = bpf_program__attach_cgroup(skel->progs.bind_v6_prog, cgroup_fd); | ||
if (!ASSERT_OK_PTR(skel, "bind_v6_prog")) | ||
goto close_skeleton; | ||
|
||
cap_was_effective = cap_net_bind_service(CAP_CLEAR); | ||
|
||
try_bind(AF_INET, 110, EACCES); | ||
try_bind(AF_INET6, 110, EACCES); | ||
|
||
try_bind(AF_INET, 111, 0); | ||
try_bind(AF_INET6, 111, 0); | ||
|
||
if (cap_was_effective) | ||
cap_net_bind_service(CAP_SET); | ||
|
||
close_skeleton: | ||
bind_perm__destroy(skel); | ||
close_cgroup_fd: | ||
close(cgroup_fd); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
|
||
#include <linux/stddef.h> | ||
#include <linux/bpf.h> | ||
#include <sys/types.h> | ||
#include <sys/socket.h> | ||
#include <bpf/bpf_helpers.h> | ||
#include <bpf/bpf_endian.h> | ||
|
||
static __always_inline int bind_prog(struct bpf_sock_addr *ctx, int family) | ||
{ | ||
struct bpf_sock *sk; | ||
|
||
sk = ctx->sk; | ||
if (!sk) | ||
return 0; | ||
|
||
if (sk->family != family) | ||
return 0; | ||
|
||
if (ctx->type != SOCK_STREAM) | ||
return 0; | ||
|
||
/* Return 1 OR'ed with the first bit set to indicate | ||
* that CAP_NET_BIND_SERVICE should be bypassed. | ||
*/ | ||
if (ctx->user_port == bpf_htons(111)) | ||
return (1 | 2); | ||
|
||
return 1; | ||
} | ||
|
||
SEC("cgroup/bind4") | ||
int bind_v4_prog(struct bpf_sock_addr *ctx) | ||
{ | ||
return bind_prog(ctx, AF_INET); | ||
} | ||
|
||
SEC("cgroup/bind6") | ||
int bind_v6_prog(struct bpf_sock_addr *ctx) | ||
{ | ||
return bind_prog(ctx, AF_INET6); | ||
} | ||
|
||
char _license[] SEC("license") = "GPL"; |