From: "Mickaël Salaün" <mic@digikod.net>
To: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>,
Eric Dumazet <edumazet@google.com>,
Vlad Yasevich <vyasevich@gmail.com>,
Neil Horman <nhorman@tuxdriver.com>,
"David S. Miller" <davem@davemloft.net>
Cc: gnoack@google.com, willemdebruijn.kernel@gmail.com,
Paul Moore <paul@paul-moore.com>,
Alexey Kodanev <alexey.kodanev@oracle.com>,
linux-security-module@vger.kernel.org, netdev@vger.kernel.org,
netfilter-devel@vger.kernel.org, yusongping@huawei.com,
artem.kuzin@huawei.com, konstantin.meskhidze@huawei.com,
Matthieu Buffet <matthieu@buffet.re>
Subject: Re: [RFC PATCH v1 2/2] selftests/landlock: Test non-TCP INET connection-based protocols
Date: Fri, 4 Oct 2024 12:14:14 +0200 [thread overview]
Message-ID: <20241004.Hohpheipieh2@digikod.net> (raw)
In-Reply-To: <b58680ca-81b2-7222-7287-0ac7f4227c3c@huawei-partners.com>
Eric, Vlad, Neil, and David, there might be a bug in the SCTP
implementation:
Paul, Alexey, there is a bug in the SELinux hooks for SCTP:
On Fri, Oct 04, 2024 at 12:22:42AM +0300, Mikhail Ivanov wrote:
>
>
> On 10/3/2024 8:45 PM, Mickaël Salaün wrote:
> > On Thu, Oct 03, 2024 at 10:39:32PM +0800, Mikhail Ivanov wrote:
> > > Extend protocol fixture with test suits for MPTCP, SCTP and SMC protocols.
> > > Add all options required by this protocols in config.
> >
> > Great coverage! It's nice to check against SCTP and MPTCP, but as you
> > were wondering, I think you can remove the SMC protocol to simplify
> > tests. MPTCP seems to work similarly as TCP wrt AF_UNSPEC, so it might
> > be worth keeping it, and we might want to control these protocols too
> > one day.
>
> Thanks! I'll remove SMC then.
>
> >
> > >
> > > Extend protocol_variant structure with protocol field (Cf. socket(2)).
> > >
> > > Refactor is_restricted() helper and add few helpers to check struct
> > > protocol_variant on specific protocols.
> >
> > >
> > > Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> > > ---
> > > tools/testing/selftests/landlock/common.h | 1 +
> > > tools/testing/selftests/landlock/config | 5 +
> > > tools/testing/selftests/landlock/net_test.c | 212 ++++++++++++++++++--
> > > 3 files changed, 198 insertions(+), 20 deletions(-)
> > >
> > > diff --git a/tools/testing/selftests/landlock/common.h b/tools/testing/selftests/landlock/common.h
> > > index 61056fa074bb..40a2def50b83 100644
> > > --- a/tools/testing/selftests/landlock/common.h
> > > +++ b/tools/testing/selftests/landlock/common.h
> > > @@ -234,6 +234,7 @@ enforce_ruleset(struct __test_metadata *const _metadata, const int ruleset_fd)
> > > struct protocol_variant {
> > > int domain;
> > > int type;
> > > + int protocol;
> > > };
> > > struct service_fixture {
> > > diff --git a/tools/testing/selftests/landlock/config b/tools/testing/selftests/landlock/config
> > > index 29af19c4e9f9..73b01d7d0881 100644
> > > --- a/tools/testing/selftests/landlock/config
> > > +++ b/tools/testing/selftests/landlock/config
> > > @@ -1,8 +1,12 @@
> > > CONFIG_CGROUPS=y
> > > CONFIG_CGROUP_SCHED=y
> > > CONFIG_INET=y
> > > +CONFIG_INFINIBAND=y
> >
> > Without SMC this infiniband should not be required.
>
> yeap
>
> >
> > > +CONFIG_IP_SCTP=y
> > > CONFIG_IPV6=y
> > > CONFIG_KEYS=y
> > > +CONFIG_MPTCP=y
> > > +CONFIG_MPTCP_IPV6=y
> > > CONFIG_NET=y
> > > CONFIG_NET_NS=y
> > > CONFIG_OVERLAY_FS=y
> > > @@ -10,6 +14,7 @@ CONFIG_PROC_FS=y
> > > CONFIG_SECURITY=y
> > > CONFIG_SECURITY_LANDLOCK=y
> > > CONFIG_SHMEM=y
> > > +CONFIG_SMC=y
> > > CONFIG_SYSFS=y
> > > CONFIG_TMPFS=y
> > > CONFIG_TMPFS_XATTR=y
> > > diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
> > > index 4e0aeb53b225..dbe77d436281 100644
> > > --- a/tools/testing/selftests/landlock/net_test.c
> > > +++ b/tools/testing/selftests/landlock/net_test.c
> > > @@ -36,6 +36,17 @@ enum sandbox_type {
> > > TCP_SANDBOX,
> > > };
> > > +/* Checks if IPPROTO_SMC is present for compatibility reasons. */
> > > +#if !defined(__alpha__) && defined(IPPROTO_SMC)
> > > +#define SMC_SUPPORTED 1
> > > +#else
> > > +#define SMC_SUPPORTED 0
> > > +#endif
> > > +
> > > +#ifndef IPPROTO_SMC
> > > +#define IPPROTO_SMC 256
> > > +#endif
> > > +
> > > static int set_service(struct service_fixture *const srv,
> > > const struct protocol_variant prot,
> > > const unsigned short index)
> > > @@ -85,19 +96,37 @@ static void setup_loopback(struct __test_metadata *const _metadata)
> > > clear_ambient_cap(_metadata, CAP_NET_ADMIN);
> > > }
> > > +static bool prot_is_inet_stream(const struct protocol_variant *const prot)
> > > +{
> > > + return (prot->domain == AF_INET || prot->domain == AF_INET6) &&
> > > + prot->type == SOCK_STREAM;
> > > +}
> > > +
> > > +static bool prot_is_tcp(const struct protocol_variant *const prot)
> > > +{
> > > + return prot_is_inet_stream(prot) &&
> > > + (prot->protocol == IPPROTO_TCP || prot->protocol == IPPROTO_IP);
> >
> > Why do we need to check against IPPROTO_IP?
>
> IPPROTO_IP = 0 and can be used as an alias for IPPROTO_TCP (=6) in
> socket(2) (also for IPPROTO_UDP(=17), Cf. inet_create).
>
> Since we create TCP sockets in a common way here (with protocol = 0),
> checking against IPPROTO_TCP is not necessary, but I decided to leave it
> for completeness.
Sound good, but we should then also add variants with IPPROTO_TCP for
sandboxed and not-sandboxed tests:
/* clang-format off */
FIXTURE_VARIANT_ADD(protocol, no_sandbox_with_ipv4_tcp1) {
/* clang-format on */
.sandbox = NO_SANDBOX,
.prot = {
.domain = AF_INET,
.type = SOCK_STREAM,
/* IPPROTO_IP == 0 */
.protocol = IPPROTO_IP,
},
};
/* clang-format off */
FIXTURE_VARIANT_ADD(protocol, no_sandbox_with_ipv4_tcp2) {
/* clang-format on */
.sandbox = NO_SANDBOX,
.prot = {
.domain = AF_INET,
.type = SOCK_STREAM,
.protocol = IPPROTO_TCP,
},
};
>
> >
> > > +}
> > > +
> > > +static bool prot_is_sctp(const struct protocol_variant *const prot)
> > > +{
> > > + return prot_is_inet_stream(prot) && prot->protocol == IPPROTO_SCTP;
> > > +}
> > > +
> > > +static bool prot_is_smc(const struct protocol_variant *const prot)
> > > +{
> > > + return prot_is_inet_stream(prot) && prot->protocol == IPPROTO_SMC;
> > > +}
> > > +
> > > +static bool prot_is_unix_stream(const struct protocol_variant *const prot)
> > > +{
> > > + return prot->domain == AF_UNIX && prot->type == SOCK_STREAM;
> > > +}
> > > +
> > > static bool is_restricted(const struct protocol_variant *const prot,
> > > const enum sandbox_type sandbox)
> > > {
> > > - switch (prot->domain) {
> > > - case AF_INET:
> > > - case AF_INET6:
> > > - switch (prot->type) {
> > > - case SOCK_STREAM:
> > > - return sandbox == TCP_SANDBOX;
> > > - }
> > > - break;
> > > - }
> > > - return false;
> > > + return prot_is_tcp(prot) && sandbox == TCP_SANDBOX;
> > > }
> > > static int socket_variant(const struct service_fixture *const srv)
> > > @@ -105,7 +134,7 @@ static int socket_variant(const struct service_fixture *const srv)
> > > int ret;
> > > ret = socket(srv->protocol.domain, srv->protocol.type | SOCK_CLOEXEC,
> > > - 0);
> > > + srv->protocol.protocol);
> > > if (ret < 0)
> > > return -errno;
> > > return ret;
> > > @@ -124,7 +153,7 @@ static socklen_t get_addrlen(const struct service_fixture *const srv,
> > > return sizeof(srv->ipv4_addr);
> > > case AF_INET6:
> > > - if (minimal)
> > > + if (minimal && !prot_is_sctp(&srv->protocol))
> >
> > Why SCTP requires this exception?
>
> SCTP implementation (possibly incorrectly) checks that address length is
> at least sizeof(struct sockaddr_in6) (Cf. sctp_sockaddr_af() for bind(2)
> and in sctp_connect() for connect(2)).
sctp_sockaddr_af() checks for len < SIN6_LEN_RFC2133, but also for
len < af->sockaddr_len, which refers to sctp_af_inet6.sockaddr_len =
sizeof(struct sockaddr_in6).
I think this is a bug in the SCTP implementation and it would be a fix
of 81e98370293a ("sctp: sctp_sockaddr_af must check minimal addr length
for AF_INET6"), which fixes all versions of Linux.
>
> >
> > > return SIN6_LEN_RFC2133;
> > > return sizeof(srv->ipv6_addr);
> > > @@ -271,6 +300,11 @@ FIXTURE_SETUP(protocol)
> > > .type = SOCK_STREAM,
> > > };
> > > +#if !SMC_SUPPORTED
> > > + if (prot_is_smc(&variant->prot))
> > > + SKIP(return, "SMC protocol is not supported.");
> > > +#endif
> > > +
> > > disable_caps(_metadata);
> > > ASSERT_EQ(0, set_service(&self->srv0, variant->prot, 0));
> > > @@ -299,6 +333,39 @@ FIXTURE_VARIANT_ADD(protocol, no_sandbox_with_ipv4_tcp) {
> > > },
> > > };
> > > +/* clang-format off */
> > > +FIXTURE_VARIANT_ADD(protocol, no_sandbox_with_ipv4_mptcp) {
> > > + /* clang-format on */
> > > + .sandbox = NO_SANDBOX,
> > > + .prot = {
> > > + .domain = AF_INET,
> > > + .type = SOCK_STREAM,
> > > + .protocol = IPPROTO_MPTCP,
> > > + },
> > > +};
> > > +
> > > +/* clang-format off */
> > > +FIXTURE_VARIANT_ADD(protocol, no_sandbox_with_ipv4_sctp) {
> > > + /* clang-format on */
> > > + .sandbox = NO_SANDBOX,
> > > + .prot = {
> > > + .domain = AF_INET,
> > > + .type = SOCK_STREAM,
> > > + .protocol = IPPROTO_SCTP,
> > > + },
> > > +};
> > > +
> > > +/* clang-format off */
> > > +FIXTURE_VARIANT_ADD(protocol, no_sandbox_with_ipv4_smc) {
> > > + /* clang-format on */
> > > + .sandbox = NO_SANDBOX,
> > > + .prot = {
> > > + .domain = AF_INET,
> > > + .type = SOCK_STREAM,
> > > + .protocol = IPPROTO_SMC,
> > > + },
> > > +};
> > > +
> > > /* clang-format off */
> > > FIXTURE_VARIANT_ADD(protocol, no_sandbox_with_ipv6_tcp) {
> > > /* clang-format on */
> > > @@ -309,6 +376,39 @@ FIXTURE_VARIANT_ADD(protocol, no_sandbox_with_ipv6_tcp) {
> > > },
> > > };
> > > +/* clang-format off */
> > > +FIXTURE_VARIANT_ADD(protocol, no_sandbox_with_ipv6_mptcp) {
> > > + /* clang-format on */
> > > + .sandbox = NO_SANDBOX,
> > > + .prot = {
> > > + .domain = AF_INET6,
> > > + .type = SOCK_STREAM,
> > > + .protocol = IPPROTO_MPTCP,
> > > + },
> > > +};
> > > +
> > > +/* clang-format off */
> > > +FIXTURE_VARIANT_ADD(protocol, no_sandbox_with_ipv6_sctp) {
> > > + /* clang-format on */
> > > + .sandbox = NO_SANDBOX,
> > > + .prot = {
> > > + .domain = AF_INET6,
> > > + .type = SOCK_STREAM,
> > > + .protocol = IPPROTO_SCTP,
> > > + },
> > > +};
> > > +
> > > +/* clang-format off */
> > > +FIXTURE_VARIANT_ADD(protocol, no_sandbox_with_ipv6_smc) {
> > > + /* clang-format on */
> > > + .sandbox = NO_SANDBOX,
> > > + .prot = {
> > > + .domain = AF_INET6,
> > > + .type = SOCK_STREAM,
> > > + .protocol = IPPROTO_SMC,
> > > + },
> > > +};
> > > +
> > > /* clang-format off */
> > > FIXTURE_VARIANT_ADD(protocol, no_sandbox_with_ipv4_udp) {
> > > /* clang-format on */
> > > @@ -359,6 +459,39 @@ FIXTURE_VARIANT_ADD(protocol, tcp_sandbox_with_ipv4_tcp) {
> > > },
> > > };
> > > +/* clang-format off */
> > > +FIXTURE_VARIANT_ADD(protocol, tcp_sandbox_with_ipv4_mptcp) {
> > > + /* clang-format on */
> > > + .sandbox = TCP_SANDBOX,
> > > + .prot = {
> > > + .domain = AF_INET,
> > > + .type = SOCK_STREAM,
> > > + .protocol = IPPROTO_MPTCP,
> > > + },
> > > +};
> > > +
> > > +/* clang-format off */
> > > +FIXTURE_VARIANT_ADD(protocol, tcp_sandbox_with_ipv4_sctp) {
> > > + /* clang-format on */
> > > + .sandbox = TCP_SANDBOX,
> > > + .prot = {
> > > + .domain = AF_INET,
> > > + .type = SOCK_STREAM,
> > > + .protocol = IPPROTO_SCTP,
> > > + },
> > > +};
> > > +
> > > +/* clang-format off */
> > > +FIXTURE_VARIANT_ADD(protocol, tcp_sandbox_with_ipv4_smc) {
> > > + /* clang-format on */
> > > + .sandbox = TCP_SANDBOX,
> > > + .prot = {
> > > + .domain = AF_INET,
> > > + .type = SOCK_STREAM,
> > > + .protocol = IPPROTO_SMC,
> > > + },
> > > +};
> > > +
> > > /* clang-format off */
> > > FIXTURE_VARIANT_ADD(protocol, tcp_sandbox_with_ipv6_tcp) {
> > > /* clang-format on */
> > > @@ -369,6 +502,39 @@ FIXTURE_VARIANT_ADD(protocol, tcp_sandbox_with_ipv6_tcp) {
> > > },
> > > };
> > > +/* clang-format off */
> > > +FIXTURE_VARIANT_ADD(protocol, tcp_sandbox_with_ipv6_mptcp) {
> > > + /* clang-format on */
> > > + .sandbox = TCP_SANDBOX,
> > > + .prot = {
> > > + .domain = AF_INET6,
> > > + .type = SOCK_STREAM,
> > > + .protocol = IPPROTO_MPTCP,
> > > + },
> > > +};
> > > +
> > > +/* clang-format off */
> > > +FIXTURE_VARIANT_ADD(protocol, tcp_sandbox_with_ipv6_sctp) {
> > > + /* clang-format on */
> > > + .sandbox = TCP_SANDBOX,
> > > + .prot = {
> > > + .domain = AF_INET6,
> > > + .type = SOCK_STREAM,
> > > + .protocol = IPPROTO_SCTP,
> > > + },
> > > +};
> > > +
> > > +/* clang-format off */
> > > +FIXTURE_VARIANT_ADD(protocol, tcp_sandbox_with_ipv6_smc) {
> > > + /* clang-format on */
> > > + .sandbox = TCP_SANDBOX,
> > > + .prot = {
> > > + .domain = AF_INET6,
> > > + .type = SOCK_STREAM,
> > > + .protocol = IPPROTO_SMC,
> > > + },
> > > +};
> > > +
> > > /* clang-format off */
> > > FIXTURE_VARIANT_ADD(protocol, tcp_sandbox_with_ipv4_udp) {
> > > /* clang-format on */
> > > @@ -663,7 +829,7 @@ TEST_F(protocol, bind_unspec)
> > > /* Allowed bind on AF_UNSPEC/INADDR_ANY. */
> > > ret = bind_variant(bind_fd, &self->unspec_any0);
> > > - if (variant->prot.domain == AF_INET) {
> > > + if (variant->prot.domain == AF_INET && !prot_is_sctp(&variant->prot)) {
> > > EXPECT_EQ(0, ret)
> > > {
> > > TH_LOG("Failed to bind to unspec/any socket: %s",
> > > @@ -689,7 +855,7 @@ TEST_F(protocol, bind_unspec)
> > > /* Denied bind on AF_UNSPEC/INADDR_ANY. */
> > > ret = bind_variant(bind_fd, &self->unspec_any0);
> > > - if (variant->prot.domain == AF_INET) {
> > > + if (variant->prot.domain == AF_INET && !prot_is_sctp(&variant->prot)) {
> >
> > It looks like we need the same exception for the next bind_variant()
> > call.
>
> I ran these tests with active selinux (and few other LSMs) (selinux is set
> by default for x86_64) and it seems that this check was passed
> correctly due to SCTP errno inconsistency in selinux_socket_bind().
>
> With selinux_socket_bind() disabled, bind_variant() returns -EINVAL as
> it should (Cf. sctp_do_bind).
>
> Such inconsistency happens because sksec->sclass security field can be
> initialized with SECCLASS_SOCKET (Cf. socket_type_to_security_class)
> in SCTP case, and selinux_socket_bind() provides following check:
>
> /* Note that SCTP services expect -EINVAL, others -EAFNOSUPPORT. */
> if (sksec->sclass == SECCLASS_SCTP_SOCKET)
> return -EINVAL;
> return -EAFNOSUPPORT;
>
> I'll possibly send a fix for this to selinux.
Yes please, and it would be handy to split this patch with the first
providing MPTCP coverage and the second SCTP coverage. This way I'll
quickly merge the MPTCP tests and wait for the SCTP fixes.
The SELinux issue might have been introduced with commit 0f8db8cc73df
("selinux: add AF_UNSPEC and INADDR_ANY checks to
selinux_socket_bind()").
>
> >
> > > if (is_restricted(&variant->prot, variant->sandbox)) {
> > > EXPECT_EQ(-EACCES, ret);
> > > } else {
> > > @@ -727,6 +893,10 @@ TEST_F(protocol, connect_unspec)
> > > int bind_fd, client_fd, status;
> > > pid_t child;
> > > + if (prot_is_smc(&variant->prot))
> > > + SKIP(return, "SMC does not properly handles disconnect "
> > > + "in the case of fallback to TCP");
> > > +
> > > /* Specific connection tests. */
> > > bind_fd = socket_variant(&self->srv0);
> > > ASSERT_LE(0, bind_fd);
> > > @@ -769,17 +939,18 @@ TEST_F(protocol, connect_unspec)
> > > /* Disconnects already connected socket, or set peer. */
> > > ret = connect_variant(connect_fd, &self->unspec_any0);
> > > - if (self->srv0.protocol.domain == AF_UNIX &&
> > > - self->srv0.protocol.type == SOCK_STREAM) {
> > > + if (prot_is_unix_stream(&variant->prot)) {
> > > EXPECT_EQ(-EINVAL, ret);
> > > + } else if (prot_is_sctp(&variant->prot)) {
> > > + EXPECT_EQ(-EOPNOTSUPP, ret);
> > > } else {
> > > EXPECT_EQ(0, ret);
> > > }
> > > /* Tries to reconnect, or set peer. */
> > > ret = connect_variant(connect_fd, &self->srv0);
> > > - if (self->srv0.protocol.domain == AF_UNIX &&
> > > - self->srv0.protocol.type == SOCK_STREAM) {
> > > + if (prot_is_unix_stream(&variant->prot) ||
> > > + prot_is_sctp(&variant->prot)) {
> > > EXPECT_EQ(-EISCONN, ret);
> > > } else {
> > > EXPECT_EQ(0, ret);
> > > @@ -796,9 +967,10 @@ TEST_F(protocol, connect_unspec)
> > > }
> > > ret = connect_variant(connect_fd, &self->unspec_any0);
> > > - if (self->srv0.protocol.domain == AF_UNIX &&
> > > - self->srv0.protocol.type == SOCK_STREAM) {
> > > + if (prot_is_unix_stream(&variant->prot)) {
> > > EXPECT_EQ(-EINVAL, ret);
> > > + } else if (prot_is_sctp(&variant->prot)) {
> > > + EXPECT_EQ(-EOPNOTSUPP, ret);
> > > } else {
> > > /* Always allowed to disconnect. */
> > > EXPECT_EQ(0, ret);
> > > --
> > > 2.34.1
> > >
> > >
>
prev parent reply other threads:[~2024-10-04 10:14 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-10-03 14:39 [RFC PATCH v1 0/2] Fix non-TCP sockets restriction Mikhail Ivanov
2024-10-03 14:39 ` [RFC PATCH v1 1/2] landlock: " Mikhail Ivanov
2024-10-03 15:57 ` Günther Noack
2024-10-03 17:45 ` Mickaël Salaün
2024-10-03 21:30 ` Mikhail Ivanov
2024-10-04 10:13 ` Mickaël Salaün
2024-10-04 18:16 ` Mikhail Ivanov
2024-10-05 15:49 ` Mickaël Salaün
2024-10-05 15:55 ` Mickaël Salaün
2024-10-07 11:06 ` Mikhail Ivanov
2024-10-07 11:58 ` Mikhail Ivanov
2024-10-07 13:35 ` Mickaël Salaün
2024-10-03 21:48 ` Mikhail Ivanov
2024-10-03 14:39 ` [RFC PATCH v1 2/2] selftests/landlock: Test non-TCP INET connection-based protocols Mikhail Ivanov
2024-10-03 15:59 ` Günther Noack
2024-10-03 17:45 ` Mickaël Salaün
2024-10-03 21:22 ` Mikhail Ivanov
2024-10-04 10:14 ` Mickaël Salaün [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20241004.Hohpheipieh2@digikod.net \
--to=mic@digikod.net \
--cc=alexey.kodanev@oracle.com \
--cc=artem.kuzin@huawei.com \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=gnoack@google.com \
--cc=ivanov.mikhail1@huawei-partners.com \
--cc=konstantin.meskhidze@huawei.com \
--cc=linux-security-module@vger.kernel.org \
--cc=matthieu@buffet.re \
--cc=netdev@vger.kernel.org \
--cc=netfilter-devel@vger.kernel.org \
--cc=nhorman@tuxdriver.com \
--cc=paul@paul-moore.com \
--cc=vyasevich@gmail.com \
--cc=willemdebruijn.kernel@gmail.com \
--cc=yusongping@huawei.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).