[PATCH v10 11/13] selftests/landlock: Add 10 new test suites dedicated to network
Mickaël Salaün
mic at digikod.net
Wed Apr 26 19:10:08 UTC 2023
On 21/04/2023 12:02, Konstantin Meskhidze (A) wrote:
>
>
> 4/16/2023 7:13 PM, Mickaël Salaün пишет:
>> First batch of the tests review:
>>
>> On 23/03/2023 09:52, Konstantin Meskhidze wrote:
>>> These test suites try to check edge cases for TCP sockets
>>> bind() and connect() actions.
>>>
>>> socket:
>>> * bind: Tests with non-landlocked/landlocked ipv4 and ipv6 sockets.
>>> * connect: Tests with non-landlocked/landlocked ipv4 and ipv6 sockets.
>>> * bind_afunspec: Tests with non-landlocked/landlocked restrictions
>>> for bind action with AF_UNSPEC socket family.
>>> * connect_afunspec: Tests with non-landlocked/landlocked restrictions
>>> for connect action with AF_UNSPEC socket family.
>>> * ruleset_overlap: Tests with overlapping rules for one port.
>>> * ruleset_expanding: Tests with expanding rulesets in which rules are
>>> gradually added one by one, restricting sockets' connections.
>>> * inval: Tests with invalid user space supplied data:
>>> - out of range ruleset attribute;
>>> - unhandled allowed access;
>>> - zero port value;
>>> - zero access value;
>>> - legitimate access values;
>>> * bind_connect_inval_addrlen: Tests with invalid address length.
>>> * inval_port_format: Tests with wrong port format for ipv4/ipv6 sockets
>>> and with port values more than U16_MAX.
>>>
>>> layout1:
>>> * with_net: Tests with network bind() socket action within
>>> filesystem directory access test.
>>>
>>> Test coverage for security/landlock is 94.5% of 945 lines according
>>> to gcc/gcov-11.
>>>
>>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze at huawei.com>
>>> ---
>>>
>>> Changes since v9:
>>> * Fixes mixing code declaration and code.
>>> * Refactors FIXTURE_TEARDOWN() with clang-format.
>>> * Replaces struct _fixture_variant_socket with
>>> FIXTURE_VARIANT(socket).
>>
>> I was pretty sure clang-format and checkpatch.pl were agree with
>> FIXTURE_VARIANT(), but that was not the case. You'll need to get back to
>> struct _fixture_variant_socket to pass both these checks, and also the
>> "/* struct _fixture_variant_socket */" comments.
>>
> Ok. I will refator this part. Thanks.
>>
>>> * Deletes useless condition if (variant->is_sandboxed)
>>> in multiple locations.
>>> * Deletes zero_size argument in bind_variant() and
>>> connect_variant().
>>> * Adds tests for port values exceeding U16_MAX.
>>>
>>> Changes since v8:
>>> * Adds is_sandboxed const for FIXTURE_VARIANT(socket).
>>> * Refactors AF_UNSPEC tests.
>>> * Adds address length checking tests.
>>> * Convert ports in all tests to __be16.
>>> * Adds invalid port values tests.
>>> * Minor fixes.
>>>
>>> Changes since v7:
>>> * Squashes all selftest commits.
>>> * Adds fs test with network bind() socket action.
>>> * Minor fixes.
>>>
>>> ---
>>> tools/testing/selftests/landlock/config | 4 +
>>> tools/testing/selftests/landlock/fs_test.c | 64 +
>>> tools/testing/selftests/landlock/net_test.c | 1176 +++++++++++++++++++
>>> 3 files changed, 1244 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/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
>>> index b762b5419a89..9dfbef276e4e 100644
>>> --- a/tools/testing/selftests/landlock/fs_test.c
>>> +++ b/tools/testing/selftests/landlock/fs_test.c
>>> @@ -8,8 +8,10 @@
>>> */
>>>
>>> #define _GNU_SOURCE
>>> +#include <arpa/inet.h>
>>> #include <fcntl.h>
>>> #include <linux/landlock.h>
>>> +#include <netinet/in.h>
>>> #include <sched.h>
>>> #include <stdio.h>
>>> #include <string.h>
>>> @@ -17,6 +19,7 @@
>>> #include <sys/mount.h>
>>> #include <sys/prctl.h>
>>> #include <sys/sendfile.h>
>>> +#include <sys/socket.h>
>>> #include <sys/stat.h>
>>> #include <sys/sysmacros.h>
>>> #include <unistd.h>
>>> @@ -4413,4 +4416,65 @@ TEST_F_FORK(layout2_overlay, same_content_different_file)
>>> }
>>> }
>>>
>>> +#define IP_ADDRESS "127.0.0.1"
>>> +
>>> +TEST_F_FORK(layout1, with_net)
>>> +{
>>> + const struct rule rules[] = {
>>> + {
>>> + .path = dir_s1d2,
>>> + .access = ACCESS_RO,
>>> + },
>>> + {},
>>> + };
>>> + int sockfd;
>>> + int sock_port = 15000;
>>> + struct sockaddr_in addr4;
>>> +
>>> + struct landlock_ruleset_attr ruleset_attr_net = {
>>> + .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
>>> + LANDLOCK_ACCESS_NET_CONNECT_TCP,
>>> + };
>>> + struct landlock_net_service_attr net_service = {
>>> + .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
>>> +
>>> + .port = sock_port,
>>> + };
>>> +
>>> + addr4.sin_family = AF_INET;
>>> + addr4.sin_port = htons(sock_port);
>>> + addr4.sin_addr.s_addr = inet_addr(IP_ADDRESS);
>>> + memset(&addr4.sin_zero, '\0', 8);
>>> +
>>> + /* Creates ruleset for network access. */
>>> + const int ruleset_fd_net = landlock_create_ruleset(
>>> + &ruleset_attr_net, sizeof(ruleset_attr_net), 0);
>>> + ASSERT_LE(0, ruleset_fd_net);
>>> +
>>> + /* Adds a network rule. */
>>> + ASSERT_EQ(0,
>>> + landlock_add_rule(ruleset_fd_net, LANDLOCK_RULE_NET_SERVICE,
>>> + &net_service, 0));
>>> +
>>> + enforce_ruleset(_metadata, ruleset_fd_net);
>>> + ASSERT_EQ(0, close(ruleset_fd_net));
>>> +
>>> + const int ruleset_fd = create_ruleset(_metadata, ACCESS_RW, rules);
>>> + ASSERT_LE(0, ruleset_fd);
>>> + enforce_ruleset(_metadata, ruleset_fd);
>>> + ASSERT_EQ(0, close(ruleset_fd));
>>> +
>>> + /* Tests on a directory with the network rule loaded. */
>>> + ASSERT_EQ(0, test_open(dir_s1d2, O_RDONLY));
>>> + ASSERT_EQ(0, test_open(file1_s1d2, O_RDONLY));
>>> +
>>> + sockfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
>>> + ASSERT_LE(0, sockfd);
>>> + /* Binds a socket to port 15000. */
>>> + ASSERT_EQ(0, bind(sockfd, &addr4, sizeof(addr4)));
>>> +
>>> + /* Closes bounded socket. */
>>> + ASSERT_EQ(0, close(sockfd));
>>> +}
>>> +
>>> TEST_HARNESS_MAIN
>>> diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
>>> new file mode 100644
>>> index 000000000000..d15a93c5b2c3
>>> --- /dev/null
>>> +++ b/tools/testing/selftests/landlock/net_test.c
>>> @@ -0,0 +1,1176 @@
>>> +// 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 <linux/in.h>
>>> +#include <sched.h>
>>> +#include <stdint.h>
>>> +#include <string.h>
>>> +#include <sys/prctl.h>
>>> +#include <sys/socket.h>
>>> +
>>> +#include "common.h"
>>> +
>>> +#define MAX_SOCKET_NUM 10
>>
>> You can define all other constants with either "const short" or const
>> char ...[]" instead of "#define" (and use lower case).
>>
> Thanks for the tip.
>>
>>> +
>>> +#define SOCK_PORT_START 3470
>>> +#define SOCK_PORT_ADD 10
>>> +
>>> +#define IP_ADDRESS_IPV4 "127.0.0.1"
>>
>> const char loopback_ipv4[] = "127.0.0.1";
>>
> Ok.
>>
>>> +#define IP_ADDRESS_IPV6 "::1"
>>> +#define SOCK_PORT 15000
>>> +
>>> +/* Number pending connections queue to be hold. */
>>> +#define BACKLOG 10
>>> +
>>> +const struct sockaddr addr_unspec = { .sa_family = AF_UNSPEC };
>>
>> There is no need for this variable to be global.
>
> Ok. Thanks.
>>
>>
>>> +
>>> +/* Invalid attribute, out of landlock network access range. */
>>> +#define LANDLOCK_INVAL_ATTR 7
>>> +
>>> +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;
>>> + const bool is_sandboxed;
>>> +};
>>> +
>>> +/* clang-format off */
>>> +FIXTURE_VARIANT_ADD(socket, ipv4) {
>>> + /* clang-format on */
>>> + .is_ipv4 = true,
>>> + .is_sandboxed = false,
>>> +};
>>> +
>>> +/* clang-format off */
>>> +FIXTURE_VARIANT_ADD(socket, ipv4_sandboxed) {
>>> + /* clang-format on */
>>> + .is_ipv4 = true,
>>> + .is_sandboxed = true,
>>> +};
>>> +
>>> +/* clang-format off */
>>> +FIXTURE_VARIANT_ADD(socket, ipv6) {
>>> + /* clang-format on */
>>> + .is_ipv4 = false,
>>> + .is_sandboxed = false,
>>> +};
>>> +
>>> +/* clang-format off */
>>> +FIXTURE_VARIANT_ADD(socket, ipv6_sandboxed) {
>>> + /* clang-format on */
>>> + .is_ipv4 = false,
>>> + .is_sandboxed = true,
>>> +};
>>> +
>>> +static int create_socket_variant(const FIXTURE_VARIANT(socket) * const variant,
>>> + const int type)
>>
>> socket_variant() would be more consistent with other names.
>
> Sorry. What do mean ".. other names" ???
I meant with other *_variant() helpers. You can rename
create_socket_variant() to socket_variant() (i.e. original function name
+ _variant).
>> [...]
>>
>>> +
>>> + /* Closes the connection*/
>>> + ASSERT_EQ(0, close(sockfd));
>>> +
>>> + addr4.sin_family = AF_INET;
>>> + addr4.sin_port = htons(UINT16_MAX);
>>> + addr4.sin_addr.s_addr = htonl(INADDR_ANY);
>>> + memset(&addr4.sin_zero, '\0', 8);
>>> +
>>> + /* Creates a socket. */
>>> + sockfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
>>
>> Why not create_socket_variant()? Same question for all direct socket()
>> calls.
>
> I thought it would be easier to add such specific tests than changing
> create_socket_variant(), cause its needs to add more variabless in
> FIXTURE_VARIANT(socket) and makes tests' logic more tricky.
Hmm, running all variants for that would indeed not be useful. However,
you can remove the addr* fields from the FIXTURE(socket_standalone) struct.
Because there is no teardown, you should be able to replace all
TEST_F_FORK() with TEST_F().
BTW, the socket's `self->port` field should be an `unsigned short` type.
bind_afunspec doesn't need any fixture but only the `is_sandboxed`
variant, so you can use TEST_F(port, bind) instead, and declare a `port`
fixture with only a self->port data. This should also apply to
TEST_F(port, inval).
To be consistent, you can also rename the `socket` fixture into `inet`
because it defines a set of IP (address) properties.
>>
>>
>>> + ASSERT_LE(0, sockfd);
>>> + /* Allows to reuse of local address. */
>>> + ASSERT_EQ(0, setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &one,
>>> + sizeof(one)));
>>> +
>>> + /* Binds the socket to UINT16_MAX. */
>>> + ret = bind(sockfd, &addr4, sizeof(addr4));
>>> + ASSERT_EQ(0, ret);
>>> +
>>> + /* Closes the connection*/
>>> + ASSERT_EQ(0, close(sockfd));
>>> +}
A line break here would be nice.
>>> +TEST_HARNESS_MAIN
>>> --
>>> 2.25.1
>>>
>> .
More information about the Linux-security-module-archive
mailing list