Message ID | 20220829170401.834298-12-konstantin.meskhidze@huawei.com (mailing list archive) |
---|---|
State | Not Applicable |
Headers | show |
Series | Network support for Landlock | expand |
Context | Check | Description |
---|---|---|
netdev/tree_selection | success | Not a local patch, async |
On 29/08/2022 19:03, Konstantin Meskhidze wrote: > Adds selftests for bind() socket action. > The first is with no landlock restrictions: > - bind without restrictions for ip4; > - bind without restrictions for ip6; There is no "ip4" nor "ip6" but "IPv4" and "IPv6" (everywhere). > The second ones is with mixed landlock rules: > - bind with restrictions for ip4; > - bind with restrictions for ip6; > > Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com> > --- > > Changes since v6: > * None. > > Changes since v5: > * Splits commit. > * Adds local address 127.0.0.1. > * Adds FIXTURE_VARIANT and FIXTURE_VARIANT_ADD > helpers to support both ip4 and ip6 family tests and > shorten the code. > * Adds create_socket_variant() and bind_variant() helpers. > * Gets rid of reuse_addr variable in create_socket_variant. > * Formats code with clang-format-14. > > Changes since v4: > * Adds port[MAX_SOCKET_NUM], struct sockaddr_in addr4 > and struct sockaddr_in addr6 in FIXTURE. > * Refactors FIXTURE_SETUP: > - initializing self->port, self->addr4 and self->addr6. > - adding network namespace. > * Refactors code with self->port, self->addr4 and > self->addr6 variables. > * Adds selftests for IP6 family: > - bind_no_restrictions_ip6. > - bind_with_restrictions_ip6. > * Refactors selftests/landlock/config > * Moves enforce_ruleset() into common.h > > Changes since v3: > * Split commit. > * Add helper create_socket. > * Add FIXTURE_SETUP. > > --- > tools/testing/selftests/landlock/config | 4 + > tools/testing/selftests/landlock/net_test.c | 180 ++++++++++++++++++++ > 2 files changed, 184 insertions(+) > create mode 100644 tools/testing/selftests/landlock/net_test.c > > diff --git a/tools/testing/selftests/landlock/config b/tools/testing/selftests/landlock/config > index 0f0a65287bac..71f7e9a8a64c 100644 > --- a/tools/testing/selftests/landlock/config > +++ b/tools/testing/selftests/landlock/config > @@ -1,3 +1,7 @@ > +CONFIG_INET=y > +CONFIG_IPV6=y > +CONFIG_NET=y > +CONFIG_NET_NS=y > CONFIG_OVERLAY_FS=y > CONFIG_SECURITY_LANDLOCK=y > CONFIG_SECURITY_PATH=y > diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c > new file mode 100644 > index 000000000000..79c71fa37ddb > --- /dev/null > +++ b/tools/testing/selftests/landlock/net_test.c > @@ -0,0 +1,180 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Landlock tests - Network > + * > + * Copyright (C) 2022 Huawei Tech. Co., Ltd. > + */ > + > +#define _GNU_SOURCE > +#include <arpa/inet.h> > +#include <errno.h> > +#include <fcntl.h> > +#include <linux/landlock.h> > +#include <netinet/in.h> > +#include <sched.h> > +#include <string.h> > +#include <sys/prctl.h> > +#include <sys/socket.h> > +#include <sys/types.h> > + > +#include "common.h" > + > +#define MAX_SOCKET_NUM 10 > + > +#define SOCK_PORT_START 3470 > +#define SOCK_PORT_ADD 10 > + > +#define IP_ADDRESS "127.0.0.1" > + > +FIXTURE(socket) > +{ > + uint port[MAX_SOCKET_NUM]; > + struct sockaddr_in addr4[MAX_SOCKET_NUM]; > + struct sockaddr_in6 addr6[MAX_SOCKET_NUM]; > +}; > + /* struct _fixture_variant_socket */ > +FIXTURE_VARIANT(socket) > +{ > + const bool is_ipv4; > +}; > + > +/* clang-format off */ > +FIXTURE_VARIANT_ADD(socket, ipv4) { > + /* clang-format on */ > + .is_ipv4 = true, > +}; > + > +/* clang-format off */ > +FIXTURE_VARIANT_ADD(socket, ipv6) { > + /* clang-format on */ > + .is_ipv4 = false, > +}; > + > +static int create_socket_variant(const FIXTURE_VARIANT(socket) *const variant, To avoid scripts/checkpatch.pl errors [1] (and to follow a consistent formatting thanks to clang-format), please use the generated struct type for FIXTURE_VARIANT and FIXTURE_DATA instead (everywhere), and add a comment before the declaration (as for FIXTURE_VARIANT(socket) just above): FIXTURE_VARIANT(socket) => struct _fixture_variant_socket FIXTURE_DATA(socket) => struct _test_data_socket [1] https://lore.kernel.org/all/b1cfb8d4-ad54-9cc1-3d9d-e690c81da016@digikod.net/ > + const int type) > +{ > + if (variant->is_ipv4) > + return socket(AF_INET, type | SOCK_CLOEXEC, 0); > + else > + return socket(AF_INET6, type | SOCK_CLOEXEC, 0); > +} > + > +static int bind_variant(const FIXTURE_VARIANT(socket) *const variant, > + const int sockfd, > + const FIXTURE_DATA(socket) *const self, > + const size_t index) > +{ > + if (variant->is_ipv4) > + return bind(sockfd, &self->addr4[index], > + sizeof(self->addr4[index])); > + else > + return bind(sockfd, &self->addr6[index], > + sizeof(self->addr6[index])); > +} > + > +FIXTURE_SETUP(socket) > +{ > + int i; line break > + /* Creates IP4 socket addresses. */ > + for (i = 0; i < MAX_SOCKET_NUM; i++) { > + self->port[i] = SOCK_PORT_START + SOCK_PORT_ADD * i; > + self->addr4[i].sin_family = AF_INET; > + self->addr4[i].sin_port = htons(self->port[i]); > + self->addr4[i].sin_addr.s_addr = inet_addr(IP_ADDRESS); > + memset(&(self->addr4[i].sin_zero), '\0', 8); > + } > + > + /* Creates IP6 socket addresses. */ > + for (i = 0; i < MAX_SOCKET_NUM; i++) { > + self->port[i] = SOCK_PORT_START + SOCK_PORT_ADD * i; > + self->addr6[i].sin6_family = AF_INET6; > + self->addr6[i].sin6_port = htons(self->port[i]); > + inet_pton(AF_INET6, IP_ADDRESS, &(self->addr6[i].sin6_addr)); > + } > + > + set_cap(_metadata, CAP_SYS_ADMIN); > + ASSERT_EQ(0, unshare(CLONE_NEWNET)); > + ASSERT_EQ(0, system("ip link set dev lo up")); > + clear_cap(_metadata, CAP_SYS_ADMIN); > +} > + > +FIXTURE_TEARDOWN(socket) > +{ > +} > + > +TEST_F(socket, bind_no_restrictions) Please use TEST_F_FORK() everywhere. > +{ > + int sockfd; > + > + sockfd = create_socket_variant(variant, SOCK_STREAM); > + ASSERT_LE(0, sockfd); > + > + /* Binds a socket to port[0]. */ > + ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0)); > + > + ASSERT_EQ(0, close(sockfd)); > +} > + > +TEST_F(socket, bind_with_restrictions) > +{ > + int sockfd; > + > + struct landlock_ruleset_attr ruleset_attr = { > + .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | > + LANDLOCK_ACCESS_NET_CONNECT_TCP, > + }; > + struct landlock_net_service_attr net_service_1 = { > + .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP | > + LANDLOCK_ACCESS_NET_CONNECT_TCP, > + .port = self->port[0], > + }; > + struct landlock_net_service_attr net_service_2 = { > + .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP, > + .port = self->port[1], > + }; > + struct landlock_net_service_attr net_service_3 = { > + .allowed_access = 0, > + .port = self->port[2], > + }; > + > + const int ruleset_fd = > + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); > + ASSERT_LE(0, ruleset_fd); > + > + /* Allows connect and bind operations to the port[0] socket. */ > + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE, > + &net_service_1, 0)); > + /* Allows connect and deny bind operations to the port[1] socket. */ > + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE, > + &net_service_2, 0)); > + /* Empty allowed_access (i.e. deny rules) are ignored in network actions Only comment lines ending with "*/" should start with "/*", otherwise you need to add a line break after "/*" (everywhere). > + * for port[2] socket. > + */ > + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE, > + &net_service_3, 0)); > + ASSERT_EQ(ENOMSG, errno); > + > + /* Enforces the ruleset. */ > + enforce_ruleset(_metadata, ruleset_fd); > + > + sockfd = create_socket_variant(variant, SOCK_STREAM); > + ASSERT_LE(0, sockfd); > + /* Binds a socket to port[0]. */ > + ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0)); > + > + /* Close bounded socket. */ Closes > + ASSERT_EQ(0, close(sockfd)); > + > + sockfd = create_socket_variant(variant, SOCK_STREAM); > + ASSERT_LE(0, sockfd); > + /* Binds a socket to port[1]. */ > + ASSERT_EQ(-1, bind_variant(variant, sockfd, self, 1)); > + ASSERT_EQ(EACCES, errno); > + > + sockfd = create_socket_variant(variant, SOCK_STREAM); > + ASSERT_LE(0, sockfd); > + /* Binds a socket to port[2]. */ > + ASSERT_EQ(-1, bind_variant(variant, sockfd, self, 2)); > + ASSERT_EQ(EACCES, errno); > +} > +TEST_HARNESS_MAIN > -- > 2.25.1 >
9/6/2022 11:09 AM, Mickaël Salaün пишет: > > On 29/08/2022 19:03, Konstantin Meskhidze wrote: >> Adds selftests for bind() socket action. >> The first is with no landlock restrictions: >> - bind without restrictions for ip4; >> - bind without restrictions for ip6; > > There is no "ip4" nor "ip6" but "IPv4" and "IPv6" (everywhere). > Ok. I will fix it. > >> The second ones is with mixed landlock rules: >> - bind with restrictions for ip4; >> - bind with restrictions for ip6; >> >> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com> >> --- >> >> Changes since v6: >> * None. >> >> Changes since v5: >> * Splits commit. >> * Adds local address 127.0.0.1. >> * Adds FIXTURE_VARIANT and FIXTURE_VARIANT_ADD >> helpers to support both ip4 and ip6 family tests and >> shorten the code. >> * Adds create_socket_variant() and bind_variant() helpers. >> * Gets rid of reuse_addr variable in create_socket_variant. >> * Formats code with clang-format-14. >> >> Changes since v4: >> * Adds port[MAX_SOCKET_NUM], struct sockaddr_in addr4 >> and struct sockaddr_in addr6 in FIXTURE. >> * Refactors FIXTURE_SETUP: >> - initializing self->port, self->addr4 and self->addr6. >> - adding network namespace. >> * Refactors code with self->port, self->addr4 and >> self->addr6 variables. >> * Adds selftests for IP6 family: >> - bind_no_restrictions_ip6. >> - bind_with_restrictions_ip6. >> * Refactors selftests/landlock/config >> * Moves enforce_ruleset() into common.h >> >> Changes since v3: >> * Split commit. >> * Add helper create_socket. >> * Add FIXTURE_SETUP. >> >> --- >> tools/testing/selftests/landlock/config | 4 + >> tools/testing/selftests/landlock/net_test.c | 180 ++++++++++++++++++++ >> 2 files changed, 184 insertions(+) >> create mode 100644 tools/testing/selftests/landlock/net_test.c >> >> diff --git a/tools/testing/selftests/landlock/config b/tools/testing/selftests/landlock/config >> index 0f0a65287bac..71f7e9a8a64c 100644 >> --- a/tools/testing/selftests/landlock/config >> +++ b/tools/testing/selftests/landlock/config >> @@ -1,3 +1,7 @@ >> +CONFIG_INET=y >> +CONFIG_IPV6=y >> +CONFIG_NET=y >> +CONFIG_NET_NS=y >> CONFIG_OVERLAY_FS=y >> CONFIG_SECURITY_LANDLOCK=y >> CONFIG_SECURITY_PATH=y >> diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c >> new file mode 100644 >> index 000000000000..79c71fa37ddb >> --- /dev/null >> +++ b/tools/testing/selftests/landlock/net_test.c >> @@ -0,0 +1,180 @@ >> +// SPDX-License-Identifier: GPL-2.0-only >> +/* >> + * Landlock tests - Network >> + * >> + * Copyright (C) 2022 Huawei Tech. Co., Ltd. >> + */ >> + >> +#define _GNU_SOURCE >> +#include <arpa/inet.h> >> +#include <errno.h> >> +#include <fcntl.h> >> +#include <linux/landlock.h> >> +#include <netinet/in.h> >> +#include <sched.h> >> +#include <string.h> >> +#include <sys/prctl.h> >> +#include <sys/socket.h> >> +#include <sys/types.h> >> + >> +#include "common.h" >> + >> +#define MAX_SOCKET_NUM 10 >> + >> +#define SOCK_PORT_START 3470 >> +#define SOCK_PORT_ADD 10 >> + >> +#define IP_ADDRESS "127.0.0.1" >> + >> +FIXTURE(socket) >> +{ >> + uint port[MAX_SOCKET_NUM]; >> + struct sockaddr_in addr4[MAX_SOCKET_NUM]; >> + struct sockaddr_in6 addr6[MAX_SOCKET_NUM]; >> +}; >> + > > /* struct _fixture_variant_socket */ > >> +FIXTURE_VARIANT(socket) >> +{ >> + const bool is_ipv4; >> +}; >> + >> +/* clang-format off */ >> +FIXTURE_VARIANT_ADD(socket, ipv4) { >> + /* clang-format on */ >> + .is_ipv4 = true, >> +}; >> + >> +/* clang-format off */ >> +FIXTURE_VARIANT_ADD(socket, ipv6) { >> + /* clang-format on */ >> + .is_ipv4 = false, >> +}; >> + >> +static int create_socket_variant(const FIXTURE_VARIANT(socket) *const variant, > > To avoid scripts/checkpatch.pl errors [1] (and to follow a consistent > formatting thanks to clang-format), please use the generated struct type > for FIXTURE_VARIANT and FIXTURE_DATA instead (everywhere), and add a > comment before the declaration (as for FIXTURE_VARIANT(socket) just above): > > FIXTURE_VARIANT(socket) => struct _fixture_variant_socket > FIXTURE_DATA(socket) => struct _test_data_socket > > > [1] > https://lore.kernel.org/all/b1cfb8d4-ad54-9cc1-3d9d-e690c81da016@digikod.net/ > Thanks for the tip here. I will fix it. > >> + const int type) >> +{ >> + if (variant->is_ipv4) >> + return socket(AF_INET, type | SOCK_CLOEXEC, 0); >> + else >> + return socket(AF_INET6, type | SOCK_CLOEXEC, 0); >> +} >> + >> +static int bind_variant(const FIXTURE_VARIANT(socket) *const variant, >> + const int sockfd, >> + const FIXTURE_DATA(socket) *const self, >> + const size_t index) >> +{ >> + if (variant->is_ipv4) >> + return bind(sockfd, &self->addr4[index], >> + sizeof(self->addr4[index])); >> + else >> + return bind(sockfd, &self->addr6[index], >> + sizeof(self->addr6[index])); >> +} >> + >> +FIXTURE_SETUP(socket) >> +{ >> + int i; > > line break Got it. > > >> + /* Creates IP4 socket addresses. */ >> + for (i = 0; i < MAX_SOCKET_NUM; i++) { >> + self->port[i] = SOCK_PORT_START + SOCK_PORT_ADD * i; >> + self->addr4[i].sin_family = AF_INET; >> + self->addr4[i].sin_port = htons(self->port[i]); >> + self->addr4[i].sin_addr.s_addr = inet_addr(IP_ADDRESS); >> + memset(&(self->addr4[i].sin_zero), '\0', 8); >> + } >> + >> + /* Creates IP6 socket addresses. */ >> + for (i = 0; i < MAX_SOCKET_NUM; i++) { >> + self->port[i] = SOCK_PORT_START + SOCK_PORT_ADD * i; >> + self->addr6[i].sin6_family = AF_INET6; >> + self->addr6[i].sin6_port = htons(self->port[i]); >> + inet_pton(AF_INET6, IP_ADDRESS, &(self->addr6[i].sin6_addr)); >> + } >> + >> + set_cap(_metadata, CAP_SYS_ADMIN); >> + ASSERT_EQ(0, unshare(CLONE_NEWNET)); >> + ASSERT_EQ(0, system("ip link set dev lo up")); >> + clear_cap(_metadata, CAP_SYS_ADMIN); >> +} >> + >> +FIXTURE_TEARDOWN(socket) >> +{ >> +} >> + >> +TEST_F(socket, bind_no_restrictions) > > Please use TEST_F_FORK() everywhere. > Got it. > >> +{ >> + int sockfd; >> + >> + sockfd = create_socket_variant(variant, SOCK_STREAM); >> + ASSERT_LE(0, sockfd); >> + >> + /* Binds a socket to port[0]. */ >> + ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0)); >> + >> + ASSERT_EQ(0, close(sockfd)); >> +} >> + >> +TEST_F(socket, bind_with_restrictions) >> +{ >> + int sockfd; >> + >> + struct landlock_ruleset_attr ruleset_attr = { >> + .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | >> + LANDLOCK_ACCESS_NET_CONNECT_TCP, >> + }; >> + struct landlock_net_service_attr net_service_1 = { >> + .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP | >> + LANDLOCK_ACCESS_NET_CONNECT_TCP, >> + .port = self->port[0], >> + }; >> + struct landlock_net_service_attr net_service_2 = { >> + .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP, >> + .port = self->port[1], >> + }; >> + struct landlock_net_service_attr net_service_3 = { >> + .allowed_access = 0, >> + .port = self->port[2], >> + }; >> + >> + const int ruleset_fd = >> + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); >> + ASSERT_LE(0, ruleset_fd); >> + >> + /* Allows connect and bind operations to the port[0] socket. */ >> + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE, >> + &net_service_1, 0)); >> + /* Allows connect and deny bind operations to the port[1] socket. */ >> + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE, >> + &net_service_2, 0)); >> + /* Empty allowed_access (i.e. deny rules) are ignored in network actions > > Only comment lines ending with "*/" should start with "/*", otherwise > you need to add a line break after "/*" (everywhere). > Ok. Thanks. > >> + * for port[2] socket. >> + */ >> + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE, >> + &net_service_3, 0)); >> + ASSERT_EQ(ENOMSG, errno); >> + >> + /* Enforces the ruleset. */ >> + enforce_ruleset(_metadata, ruleset_fd); >> + >> + sockfd = create_socket_variant(variant, SOCK_STREAM); >> + ASSERT_LE(0, sockfd); >> + /* Binds a socket to port[0]. */ >> + ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0)); >> + >> + /* Close bounded socket. */ > > Closes Yep. Thanks. > >> + ASSERT_EQ(0, close(sockfd)); >> + >> + sockfd = create_socket_variant(variant, SOCK_STREAM); >> + ASSERT_LE(0, sockfd); >> + /* Binds a socket to port[1]. */ >> + ASSERT_EQ(-1, bind_variant(variant, sockfd, self, 1)); >> + ASSERT_EQ(EACCES, errno); >> + >> + sockfd = create_socket_variant(variant, SOCK_STREAM); >> + ASSERT_LE(0, sockfd); >> + /* Binds a socket to port[2]. */ >> + ASSERT_EQ(-1, bind_variant(variant, sockfd, self, 2)); >> + ASSERT_EQ(EACCES, errno); >> +} >> +TEST_HARNESS_MAIN >> -- >> 2.25.1 >> > .
diff --git a/tools/testing/selftests/landlock/config b/tools/testing/selftests/landlock/config index 0f0a65287bac..71f7e9a8a64c 100644 --- a/tools/testing/selftests/landlock/config +++ b/tools/testing/selftests/landlock/config @@ -1,3 +1,7 @@ +CONFIG_INET=y +CONFIG_IPV6=y +CONFIG_NET=y +CONFIG_NET_NS=y CONFIG_OVERLAY_FS=y CONFIG_SECURITY_LANDLOCK=y CONFIG_SECURITY_PATH=y diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c new file mode 100644 index 000000000000..79c71fa37ddb --- /dev/null +++ b/tools/testing/selftests/landlock/net_test.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Landlock tests - Network + * + * Copyright (C) 2022 Huawei Tech. Co., Ltd. + */ + +#define _GNU_SOURCE +#include <arpa/inet.h> +#include <errno.h> +#include <fcntl.h> +#include <linux/landlock.h> +#include <netinet/in.h> +#include <sched.h> +#include <string.h> +#include <sys/prctl.h> +#include <sys/socket.h> +#include <sys/types.h> + +#include "common.h" + +#define MAX_SOCKET_NUM 10 + +#define SOCK_PORT_START 3470 +#define SOCK_PORT_ADD 10 + +#define IP_ADDRESS "127.0.0.1" + +FIXTURE(socket) +{ + uint port[MAX_SOCKET_NUM]; + struct sockaddr_in addr4[MAX_SOCKET_NUM]; + struct sockaddr_in6 addr6[MAX_SOCKET_NUM]; +}; + +FIXTURE_VARIANT(socket) +{ + const bool is_ipv4; +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(socket, ipv4) { + /* clang-format on */ + .is_ipv4 = true, +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(socket, ipv6) { + /* clang-format on */ + .is_ipv4 = false, +}; + +static int create_socket_variant(const FIXTURE_VARIANT(socket) *const variant, + const int type) +{ + if (variant->is_ipv4) + return socket(AF_INET, type | SOCK_CLOEXEC, 0); + else + return socket(AF_INET6, type | SOCK_CLOEXEC, 0); +} + +static int bind_variant(const FIXTURE_VARIANT(socket) *const variant, + const int sockfd, + const FIXTURE_DATA(socket) *const self, + const size_t index) +{ + if (variant->is_ipv4) + return bind(sockfd, &self->addr4[index], + sizeof(self->addr4[index])); + else + return bind(sockfd, &self->addr6[index], + sizeof(self->addr6[index])); +} + +FIXTURE_SETUP(socket) +{ + int i; + /* Creates IP4 socket addresses. */ + for (i = 0; i < MAX_SOCKET_NUM; i++) { + self->port[i] = SOCK_PORT_START + SOCK_PORT_ADD * i; + self->addr4[i].sin_family = AF_INET; + self->addr4[i].sin_port = htons(self->port[i]); + self->addr4[i].sin_addr.s_addr = inet_addr(IP_ADDRESS); + memset(&(self->addr4[i].sin_zero), '\0', 8); + } + + /* Creates IP6 socket addresses. */ + for (i = 0; i < MAX_SOCKET_NUM; i++) { + self->port[i] = SOCK_PORT_START + SOCK_PORT_ADD * i; + self->addr6[i].sin6_family = AF_INET6; + self->addr6[i].sin6_port = htons(self->port[i]); + inet_pton(AF_INET6, IP_ADDRESS, &(self->addr6[i].sin6_addr)); + } + + set_cap(_metadata, CAP_SYS_ADMIN); + ASSERT_EQ(0, unshare(CLONE_NEWNET)); + ASSERT_EQ(0, system("ip link set dev lo up")); + clear_cap(_metadata, CAP_SYS_ADMIN); +} + +FIXTURE_TEARDOWN(socket) +{ +} + +TEST_F(socket, bind_no_restrictions) +{ + int sockfd; + + sockfd = create_socket_variant(variant, SOCK_STREAM); + ASSERT_LE(0, sockfd); + + /* Binds a socket to port[0]. */ + ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0)); + + ASSERT_EQ(0, close(sockfd)); +} + +TEST_F(socket, bind_with_restrictions) +{ + int sockfd; + + struct landlock_ruleset_attr ruleset_attr = { + .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP | + LANDLOCK_ACCESS_NET_CONNECT_TCP, + }; + struct landlock_net_service_attr net_service_1 = { + .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP | + LANDLOCK_ACCESS_NET_CONNECT_TCP, + .port = self->port[0], + }; + struct landlock_net_service_attr net_service_2 = { + .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP, + .port = self->port[1], + }; + struct landlock_net_service_attr net_service_3 = { + .allowed_access = 0, + .port = self->port[2], + }; + + const int ruleset_fd = + landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); + ASSERT_LE(0, ruleset_fd); + + /* Allows connect and bind operations to the port[0] socket. */ + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE, + &net_service_1, 0)); + /* Allows connect and deny bind operations to the port[1] socket. */ + ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE, + &net_service_2, 0)); + /* Empty allowed_access (i.e. deny rules) are ignored in network actions + * for port[2] socket. + */ + ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE, + &net_service_3, 0)); + ASSERT_EQ(ENOMSG, errno); + + /* Enforces the ruleset. */ + enforce_ruleset(_metadata, ruleset_fd); + + sockfd = create_socket_variant(variant, SOCK_STREAM); + ASSERT_LE(0, sockfd); + /* Binds a socket to port[0]. */ + ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0)); + + /* Close bounded socket. */ + ASSERT_EQ(0, close(sockfd)); + + sockfd = create_socket_variant(variant, SOCK_STREAM); + ASSERT_LE(0, sockfd); + /* Binds a socket to port[1]. */ + ASSERT_EQ(-1, bind_variant(variant, sockfd, self, 1)); + ASSERT_EQ(EACCES, errno); + + sockfd = create_socket_variant(variant, SOCK_STREAM); + ASSERT_LE(0, sockfd); + /* Binds a socket to port[2]. */ + ASSERT_EQ(-1, bind_variant(variant, sockfd, self, 2)); + ASSERT_EQ(EACCES, errno); +} +TEST_HARNESS_MAIN
Adds selftests for bind() socket action. The first is with no landlock restrictions: - bind without restrictions for ip4; - bind without restrictions for ip6; The second ones is with mixed landlock rules: - bind with restrictions for ip4; - bind with restrictions for ip6; Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com> --- Changes since v6: * None. Changes since v5: * Splits commit. * Adds local address 127.0.0.1. * Adds FIXTURE_VARIANT and FIXTURE_VARIANT_ADD helpers to support both ip4 and ip6 family tests and shorten the code. * Adds create_socket_variant() and bind_variant() helpers. * Gets rid of reuse_addr variable in create_socket_variant. * Formats code with clang-format-14. Changes since v4: * Adds port[MAX_SOCKET_NUM], struct sockaddr_in addr4 and struct sockaddr_in addr6 in FIXTURE. * Refactors FIXTURE_SETUP: - initializing self->port, self->addr4 and self->addr6. - adding network namespace. * Refactors code with self->port, self->addr4 and self->addr6 variables. * Adds selftests for IP6 family: - bind_no_restrictions_ip6. - bind_with_restrictions_ip6. * Refactors selftests/landlock/config * Moves enforce_ruleset() into common.h Changes since v3: * Split commit. * Add helper create_socket. * Add FIXTURE_SETUP. --- tools/testing/selftests/landlock/config | 4 + tools/testing/selftests/landlock/net_test.c | 180 ++++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 tools/testing/selftests/landlock/net_test.c -- 2.25.1