selftests/landlock: Add tests for UDP send

Add tests specific to UDP sendmsg() in the protocol_* variants to ensure
behaviour is consistent across AF_INET, AF_INET6 and AF_UNIX.

Signed-off-by: Matthieu Buffet <matthieu@buffet.re>
Link: https://patch.msgid.link/20260611162107.49278-5-matthieu@buffet.re
[mic: Fix comment formatting, rebase]
Signed-off-by: Mickaël Salaün <mic@digikod.net>
This commit is contained in:
Matthieu Buffet 2026-06-11 18:21:04 +02:00 committed by Mickaël Salaün
parent 42afa5c467
commit cf393de90f
No known key found for this signature in database
GPG Key ID: E5E3D0E88C82F6D2

View File

@ -289,9 +289,163 @@ static int connect_variant(const int sock_fd,
return connect_variant_addrlen(sock_fd, srv, get_addrlen(srv, false));
}
static int sendto_variant_addrlen(const int sock_fd,
const struct service_fixture *const srv,
const socklen_t addrlen, void *buf,
size_t len, size_t flags)
{
const struct sockaddr *dst = NULL;
ssize_t ret;
/*
* We never want our processes to be killed by SIGPIPE: we check return
* codes and errno, so that we have actual error messages.
*/
flags |= MSG_NOSIGNAL;
if (srv != NULL) {
switch (srv->protocol.domain) {
case AF_UNSPEC:
case AF_INET:
dst = (const struct sockaddr *)&srv->ipv4_addr;
break;
case AF_INET6:
dst = (const struct sockaddr *)&srv->ipv6_addr;
break;
case AF_UNIX:
dst = (const struct sockaddr *)&srv->unix_addr;
break;
default:
errno = EAFNOSUPPORT;
return -errno;
}
}
ret = sendto(sock_fd, buf, len, flags, dst, addrlen);
if (ret < 0)
return -errno;
/* errno is not set in cases of partial writes. */
if (ret != len)
return -EINTR;
return 0;
}
static int sendto_variant(const int sock_fd,
const struct service_fixture *const srv, void *buf,
size_t len, size_t flags)
{
socklen_t addrlen = 0;
if (srv != NULL)
addrlen = get_addrlen(srv, false);
return sendto_variant_addrlen(sock_fd, srv, addrlen, buf, len, flags);
}
static int test_sendmsg(struct __test_metadata *const _metadata,
const struct protocol_variant *prot, int client_fd,
int server_fd, const struct service_fixture *srv,
bool bind_denied, bool send_denied)
{
int ret;
socklen_t opt_len;
int sock_type;
int addr_family;
struct sockaddr_storage peer_addr = { 0 };
bool has_remote_port;
bool needs_autobind;
char read_buf[1] = { 0 };
/*
* Prepare the test by inspecting the socket type and whether it has a
* local/remote address set (all of which determine the expected
* outcomes).
*/
opt_len = sizeof(sock_type);
ASSERT_EQ(0, getsockopt(client_fd, SOL_SOCKET, SO_TYPE, &sock_type,
&opt_len));
opt_len = sizeof(addr_family);
ASSERT_EQ(0, getsockopt(client_fd, SOL_SOCKET, SO_DOMAIN, &addr_family,
&opt_len));
opt_len = sizeof(peer_addr);
has_remote_port = (getpeername(client_fd, (struct sockaddr *)&peer_addr,
&opt_len) == 0);
needs_autobind = (addr_family == AF_INET || addr_family == AF_INET6) &&
get_binded_port(client_fd, prot) == 0;
/* First, check error code with truncated explicit address. */
if (srv != NULL) {
ret = sendto_variant_addrlen(
client_fd, srv, get_addrlen(srv, true) - 1, "A", 1, 0);
if (sock_type == SOCK_STREAM && !has_remote_port) {
EXPECT_EQ(-EPIPE, ret)
{
return -1;
}
} else if (bind_denied && needs_autobind) {
EXPECT_EQ(-EACCES, ret)
{
return -1;
}
} else {
EXPECT_EQ(-EINVAL, ret)
{
return -1;
}
}
}
/* With or without explicit destination address (srv can be NULL). */
ret = sendto_variant(client_fd, srv, "B", 1, 0);
if (sock_type == SOCK_STREAM && !has_remote_port) {
EXPECT_EQ(-EPIPE, ret)
{
return -1;
}
} else if ((send_denied && srv != NULL) ||
(bind_denied && needs_autobind)) {
ASSERT_EQ(-EACCES, ret)
{
return -1;
}
} else if (srv == NULL && !has_remote_port) {
if (addr_family == AF_UNIX) {
ASSERT_EQ(-ENOTCONN, ret)
{
return -1;
}
} else if (sock_type == SOCK_STREAM) {
ASSERT_EQ(-EPIPE, ret)
{
return -1;
}
} else {
ASSERT_EQ(-EDESTADDRREQ, ret)
{
return -1;
}
}
} else {
ASSERT_EQ(0, ret);
ASSERT_EQ(1, recv(server_fd, read_buf, 1, 0));
ASSERT_EQ(read_buf[0], 'B')
{
return -1;
}
}
return 0;
}
FIXTURE(protocol)
{
struct service_fixture srv0, srv1, srv2, unspec_any0, unspec_srv0;
struct service_fixture srv0, srv1, srv2;
struct service_fixture unspec_any0, unspec_srv0, unspec_srv1;
};
FIXTURE_VARIANT(protocol)
@ -313,6 +467,7 @@ FIXTURE_SETUP(protocol)
ASSERT_EQ(0, set_service(&self->srv2, variant->prot, 2));
ASSERT_EQ(0, set_service(&self->unspec_srv0, prot_unspec, 0));
ASSERT_EQ(0, set_service(&self->unspec_srv1, prot_unspec, 1));
ASSERT_EQ(0, set_service(&self->unspec_any0, prot_unspec, 0));
self->unspec_any0.ipv4_addr.sin_addr.s_addr = htonl(INADDR_ANY);
@ -1126,6 +1281,441 @@ TEST_F(protocol, connect_unspec)
EXPECT_EQ(0, close(bind_fd));
}
TEST_F(protocol, sendmsg_stream)
{
int srv0_fd, tmp_fd, client_fd, res;
char read_buf[1] = { 0 };
/*
* Simple test for stream sockets: just deny all connect()/
* send(explicit addr)/bind(), and make sure we don't interfere with any
* operation.
*/
if (variant->prot.type != SOCK_STREAM)
return;
if (variant->sandbox == UDP_SANDBOX) {
const struct landlock_ruleset_attr ruleset_attr = {
.handled_access_net =
LANDLOCK_ACCESS_NET_BIND_UDP |
LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP,
};
const int ruleset_fd = landlock_create_ruleset(
&ruleset_attr, sizeof(ruleset_attr), 0);
ASSERT_LE(0, ruleset_fd);
enforce_ruleset(_metadata, ruleset_fd);
EXPECT_EQ(0, close(ruleset_fd));
}
ASSERT_LE(0, client_fd = socket_variant(&self->srv0));
ASSERT_LE(0, srv0_fd = socket_variant(&self->srv0));
ASSERT_EQ(0, bind_variant(srv0_fd, &self->srv0));
ASSERT_EQ(0, listen(srv0_fd, backlog));
/* Send on a non-connected socket. */
res = sendto_variant(client_fd, NULL, "A", 1, 0);
if (variant->prot.domain == AF_UNIX) {
EXPECT_EQ(-ENOTCONN, res);
} else {
EXPECT_EQ(-EPIPE, res);
}
/* Send to a truncated (invalid) address on a non-connected socket. */
res = sendto_variant_addrlen(client_fd, &self->srv0,
get_addrlen(&self->srv0, true) - 1, "B", 1,
0);
if (variant->prot.domain == AF_UNIX) {
EXPECT_EQ(-EOPNOTSUPP, res);
} else {
EXPECT_EQ(-EPIPE, res);
}
/* Connect. */
ASSERT_EQ(0, connect_variant(client_fd, &self->srv0));
tmp_fd = accept(srv0_fd, NULL, 0);
ASSERT_LE(0, tmp_fd);
EXPECT_EQ(0, close(srv0_fd));
srv0_fd = tmp_fd;
/* Send without an explicit address. */
EXPECT_EQ(0, sendto_variant(client_fd, NULL, "C", 1, 0));
EXPECT_EQ(1, recv(srv0_fd, read_buf, 1, 0))
{
TH_LOG("recv() failed: %s", strerror(errno));
}
EXPECT_EQ(read_buf[0], 'C');
/* Send to a truncated (invalid) address. */
res = sendto_variant_addrlen(client_fd, &self->srv0,
get_addrlen(&self->srv0, true) - 1, "D", 1,
0);
if (variant->prot.domain == AF_UNIX) {
EXPECT_EQ(-EISCONN, res);
} else {
ASSERT_EQ(0, res);
EXPECT_EQ(1, recv(srv0_fd, read_buf, 1, 0))
{
TH_LOG("recv() failed: %s", strerror(errno));
}
EXPECT_EQ(read_buf[0], 'D');
}
/* Send to a valid but different address. */
res = sendto_variant(client_fd, &self->srv1, "E", 1, 0);
if (variant->prot.domain == AF_UNIX) {
EXPECT_EQ(-EISCONN, res);
} else {
ASSERT_EQ(0, res);
EXPECT_EQ(1, recv(srv0_fd, read_buf, 1, 0))
{
TH_LOG("recv() failed: %s", strerror(errno));
}
EXPECT_EQ(read_buf[0], 'E');
}
EXPECT_EQ(0, close(client_fd));
}
TEST_F(protocol, sendmsg_dgram)
{
const bool restricted = is_restricted(&variant->prot, variant->sandbox);
int srv0_fd, srv1_fd, client_fd, child, status, res;
if (variant->prot.type != SOCK_DGRAM)
return;
/* Prepare server on port #0 to be allowed. */
ASSERT_LE(0, srv0_fd = socket_variant(&self->srv0));
ASSERT_EQ(0, bind_variant(srv0_fd, &self->srv0));
/* And another server on port #1 to be denied. */
ASSERT_LE(0, srv1_fd = socket_variant(&self->srv1));
ASSERT_EQ(0, bind_variant(srv1_fd, &self->srv1));
/*
* Check that sockets connected before restrictions are not impacted in
* any way.
*/
child = fork();
ASSERT_LE(0, child);
if (child == 0) {
ASSERT_LE(0, client_fd = socket_variant(&self->srv0));
ASSERT_EQ(0, connect_variant(client_fd, &self->srv0));
if (variant->sandbox == UDP_SANDBOX) {
/* Deny all connect()/send(explicit addr)/bind(). */
const struct landlock_ruleset_attr ruleset_attr = {
.handled_access_net =
LANDLOCK_ACCESS_NET_BIND_UDP |
LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP,
};
const int ruleset_fd = landlock_create_ruleset(
&ruleset_attr, sizeof(ruleset_attr), 0);
ASSERT_LE(0, ruleset_fd);
enforce_ruleset(_metadata, ruleset_fd);
EXPECT_EQ(0, close(ruleset_fd));
}
EXPECT_EQ(0,
test_sendmsg(_metadata, &variant->prot, client_fd,
srv0_fd, NULL, restricted, restricted));
EXPECT_EQ(0, test_sendmsg(_metadata, &variant->prot, client_fd,
srv0_fd, &self->srv0, restricted,
restricted));
EXPECT_EQ(0, test_sendmsg(_metadata, &variant->prot, client_fd,
srv1_fd, &self->srv1, restricted,
restricted));
EXPECT_EQ(0, close(client_fd));
_exit(_metadata->exit_code);
}
EXPECT_EQ(child, waitpid(child, &status, 0));
EXPECT_EQ(1, WIFEXITED(status));
EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
/*
* Restrict connect/send, but not bind(). Then try sending with no
* destination (and no remote peer set), an allowed destination, then a
* denied destination.
*/
child = fork();
ASSERT_LE(0, child);
if (child == 0) {
if (variant->sandbox == UDP_SANDBOX) {
const struct landlock_ruleset_attr ruleset_attr = {
.handled_access_net =
LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP,
};
const struct landlock_net_port_attr send_p0 = {
.allowed_access =
LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP,
.port = self->srv0.port,
};
const int ruleset_fd = landlock_create_ruleset(
&ruleset_attr, sizeof(ruleset_attr), 0);
ASSERT_LE(0, ruleset_fd);
ASSERT_EQ(0, landlock_add_rule(ruleset_fd,
LANDLOCK_RULE_NET_PORT,
&send_p0, 0));
enforce_ruleset(_metadata, ruleset_fd);
EXPECT_EQ(0, close(ruleset_fd));
}
ASSERT_LE(0, client_fd = socket_variant(&self->srv0));
EXPECT_EQ(0, test_sendmsg(_metadata, &variant->prot, client_fd,
-1, NULL, false, false));
EXPECT_EQ(0, test_sendmsg(_metadata, &variant->prot, client_fd,
srv0_fd, &self->srv0, false, false));
EXPECT_EQ(0, test_sendmsg(_metadata, &variant->prot, client_fd,
srv1_fd, &self->srv1, false,
restricted));
EXPECT_EQ(0, close(client_fd));
_exit(_metadata->exit_code);
return;
}
EXPECT_EQ(child, waitpid(child, &status, 0));
EXPECT_EQ(1, WIFEXITED(status));
EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
/*
* Rest of this test is just for autobind enforcement, which only exists
* in IP sockets.
*/
if (variant->prot.domain != AF_INET && variant->prot.domain != AF_INET6)
return;
/* Restrict bind() to explicit calls with an arbitrary (non-0) port. */
child = fork();
ASSERT_LE(0, child);
if (child == 0) {
const uint16_t allowed_src_port = 42424;
struct service_fixture allowed_src;
allowed_src = self->srv0;
set_port(&allowed_src, allowed_src_port);
if (variant->sandbox == UDP_SANDBOX) {
const struct landlock_ruleset_attr ruleset_attr = {
.handled_access_net =
LANDLOCK_ACCESS_NET_BIND_UDP,
};
const struct landlock_net_port_attr rule = {
.allowed_access = LANDLOCK_ACCESS_NET_BIND_UDP,
.port = allowed_src_port,
};
const int ruleset_fd = landlock_create_ruleset(
&ruleset_attr, sizeof(ruleset_attr), 0);
ASSERT_LE(0, ruleset_fd);
ASSERT_EQ(0, landlock_add_rule(ruleset_fd,
LANDLOCK_RULE_NET_PORT,
&rule, 0));
enforce_ruleset(_metadata, ruleset_fd);
EXPECT_EQ(0, close(ruleset_fd));
}
ASSERT_LE(0, client_fd = socket_variant(&self->srv0));
/* Check that implicit bind(0) in sendmsg() is denied. */
EXPECT_EQ(0, test_sendmsg(_metadata, &variant->prot, client_fd,
srv0_fd, &self->srv0, restricted,
false));
/* Same thing for autobind in connect(). */
res = connect_variant(client_fd, &self->srv0);
if (restricted) {
EXPECT_EQ(-EACCES, res);
} else {
EXPECT_EQ(0, res);
}
EXPECT_EQ(0, close(client_fd));
/* Make sendmsg() work by explicitly binding to the only allowed port. */
ASSERT_LE(0, client_fd = socket_variant(&self->srv0));
EXPECT_EQ(0, bind_variant(client_fd, &allowed_src));
EXPECT_EQ(0, test_sendmsg(_metadata, &variant->prot, client_fd,
srv0_fd, &self->srv0, restricted,
false));
EXPECT_EQ(0, close(client_fd));
/* Make connect() work by explicitly binding to the only allowed port. */
ASSERT_LE(0, client_fd = socket_variant(&self->srv0));
EXPECT_EQ(0, bind_variant(client_fd, &allowed_src));
EXPECT_EQ(0, connect_variant(client_fd, &self->srv0));
EXPECT_EQ(0, close(client_fd));
_exit(_metadata->exit_code);
return;
}
EXPECT_EQ(child, waitpid(child, &status, 0));
EXPECT_EQ(1, WIFEXITED(status));
EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
/*
* Check that %LANDLOCK_ACCESS_NET_BIND_UDP on port 0 allows implicit
* autobinds.
*/
child = fork();
ASSERT_LE(0, child);
if (child == 0) {
if (variant->sandbox == UDP_SANDBOX) {
const struct landlock_ruleset_attr ruleset_attr = {
.handled_access_net =
LANDLOCK_ACCESS_NET_BIND_UDP,
};
const struct landlock_net_port_attr rule = {
.allowed_access = LANDLOCK_ACCESS_NET_BIND_UDP,
.port = 0,
};
const int ruleset_fd = landlock_create_ruleset(
&ruleset_attr, sizeof(ruleset_attr), 0);
ASSERT_LE(0, ruleset_fd);
ASSERT_EQ(0, landlock_add_rule(ruleset_fd,
LANDLOCK_RULE_NET_PORT,
&rule, 0));
enforce_ruleset(_metadata, ruleset_fd);
EXPECT_EQ(0, close(ruleset_fd));
}
ASSERT_LE(0, client_fd = socket_variant(&self->srv0));
EXPECT_EQ(0, test_sendmsg(_metadata, &variant->prot, client_fd,
srv0_fd, &self->srv0, false, false));
EXPECT_EQ(0, close(client_fd));
_exit(_metadata->exit_code);
}
EXPECT_EQ(child, waitpid(child, &status, 0));
EXPECT_EQ(1, WIFEXITED(status));
EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
}
TEST_F(protocol, sendmsg_unspec)
{
const bool restricted = is_restricted(&variant->prot, variant->sandbox);
int client_fd, srv0_fd, srv1_fd, res;
char read_buf[1] = { 0 };
/*
* We already test for the absence of influence on sendmsg for other
* socket types and other address families, there's no point in adapting
* this test for stream sockets too.
*/
if (variant->prot.type != SOCK_DGRAM)
return;
/* Prepare client of the right family. */
ASSERT_LE(0, client_fd = socket_variant(&self->srv0));
/* Prepare server on port #0 to be allowed. */
ASSERT_LE(0, srv0_fd = socket_variant(&self->srv0));
ASSERT_EQ(0, bind_variant(srv0_fd, &self->srv0));
/* And another server on port #1 to be denied. */
ASSERT_LE(0, srv1_fd = socket_variant(&self->srv1));
ASSERT_EQ(0, bind_variant(srv1_fd, &self->srv1));
if (variant->sandbox == UDP_SANDBOX) {
const struct landlock_ruleset_attr ruleset_attr = {
.handled_access_net =
LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP,
};
const struct landlock_net_port_attr rule = {
.allowed_access = LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP,
.port = self->srv0.port,
};
const int ruleset_fd = landlock_create_ruleset(
&ruleset_attr, sizeof(ruleset_attr), 0);
ASSERT_LE(0, ruleset_fd);
ASSERT_EQ(0,
landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT,
&rule, 0));
enforce_ruleset(_metadata, ruleset_fd);
EXPECT_EQ(0, close(ruleset_fd));
}
/* Explicit AF_UNSPEC address but truncated. */
EXPECT_EQ(-EINVAL, sendto_variant_addrlen(
client_fd, &self->unspec_srv0,
get_addrlen(&self->unspec_srv0, true) - 1,
"A", 1, 0));
/*
* Explicit AF_UNSPEC address, should be treated as AF_INET by IPv4
* sockets (and thus map to srv0, allowed), but be denied by IPv6
* sockets.
*/
res = sendto_variant(client_fd, &self->unspec_srv0, "B", 1, 0);
if (variant->prot.domain == AF_INET6) {
if (restricted) {
/* Always denied on IPv6 socket. */
EXPECT_EQ(-EACCES, res);
} else {
/* IPv6 sockets treat AF_UNSPEC as a NULL address. */
EXPECT_EQ(-EDESTADDRREQ, res);
}
} else if (variant->prot.domain == AF_INET) {
ASSERT_EQ(0, res);
EXPECT_EQ(1, read(srv0_fd, read_buf, 1))
{
TH_LOG("read() failed: %s", strerror(errno));
}
EXPECT_EQ(read_buf[0], 'B');
} else {
/* Unix sockets don't accept AF_UNSPEC. */
EXPECT_EQ(-EINVAL, res);
}
/*
* Explicit AF_UNSPEC address, should be treated as AF_INET on IPv4
* sockets (and thus map to srv1, denied), and be denied on IPv6 sockets
* as always.
*/
res = sendto_variant(client_fd, &self->unspec_srv1, "C", 1, 0);
if (variant->prot.domain == AF_INET6) {
if (restricted) {
/* Always denied on IPv6 socket. */
EXPECT_EQ(-EACCES, res);
} else {
/* IPv6 sockets treat AF_UNSPEC as a NULL address. */
EXPECT_EQ(-EDESTADDRREQ, res);
}
} else if (variant->prot.domain == AF_INET) {
if (restricted) {
/* Sending to srv1 is not allowed, only srv0. */
EXPECT_EQ(-EACCES, res);
} else {
ASSERT_EQ(0, res);
EXPECT_EQ(1, read(srv1_fd, read_buf, 1))
{
TH_LOG("read() failed: %s", strerror(errno));
}
EXPECT_EQ(read_buf[0], 'C');
}
} else {
/* Unix sockets don't accept AF_UNSPEC. */
EXPECT_EQ(-EINVAL, res);
}
ASSERT_EQ(0, connect_variant(client_fd, &self->srv0));
/* Minimal explicit AF_UNSPEC address (just the sa_family_t field) */
res = sendto_variant_addrlen(client_fd, &self->unspec_srv0,
get_addrlen(&self->unspec_srv0, true), "D",
1, 0);
if (variant->prot.domain == AF_INET6) {
if (restricted) {
/* AF_UNSPEC is always denied in IPv6. */
EXPECT_EQ(-EACCES, res);
} else {
/*
* IPv6 sockets treat AF_UNSPEC as a NULL address,
* falling back to the connected address.
*/
ASSERT_EQ(0, res);
EXPECT_EQ(1, read(srv0_fd, read_buf, 1));
EXPECT_EQ(read_buf[0], 'D');
}
} else {
/*
* IPv4 socket will expect a struct sockaddr_in, our address is
* considered truncated. And Unix sockets don't accept
* AF_UNSPEC at all.
*/
EXPECT_EQ(-EINVAL, res);
}
}
FIXTURE(ipv4)
{
struct service_fixture srv0, srv1;
@ -2187,6 +2777,7 @@ FIXTURE(audit)
{
struct service_fixture srv0;
struct service_fixture srv1;
struct service_fixture unspec_srv0;
struct audit_filter audit_filter;
int audit_fd;
};
@ -2239,8 +2830,13 @@ FIXTURE_VARIANT_ADD(audit, ipv6_udp) {
FIXTURE_SETUP(audit)
{
struct protocol_variant prot_unspec = variant->prot;
prot_unspec.domain = AF_UNSPEC;
ASSERT_EQ(0, set_service(&self->srv0, variant->prot, 0));
ASSERT_EQ(0, set_service(&self->srv1, variant->prot, 1));
ASSERT_EQ(0, set_service(&self->unspec_srv0, prot_unspec, 0));
setup_loopback(_metadata);
@ -2419,4 +3015,59 @@ TEST_F(audit, connect_bound)
EXPECT_EQ(0, close(sock_fd));
}
TEST_F(audit, sendmsg)
{
const struct landlock_ruleset_attr ruleset_attr = {
.handled_access_net = LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP |
LANDLOCK_ACCESS_NET_BIND_UDP,
};
const struct landlock_net_port_attr rule = {
.allowed_access = LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP,
.port = self->srv1.port,
};
struct audit_records records;
int ruleset_fd;
int sock_fd;
/* Sendmsg on stream sockets is never denied. */
if (variant->prot.type != SOCK_DGRAM)
return;
ruleset_fd =
landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
ASSERT_LE(0, ruleset_fd);
ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT,
&rule, 0));
enforce_ruleset(_metadata, ruleset_fd);
EXPECT_EQ(0, close(ruleset_fd));
sock_fd = socket_variant(&self->srv0);
ASSERT_LE(0, sock_fd);
EXPECT_EQ(-EACCES, sendto_variant(sock_fd, &self->srv0, "A", 1, 0));
EXPECT_EQ(0, matches_auditlog(self->audit_fd, "net\\.connect_send_udp",
"daddr", variant->addr, "dest"));
EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
EXPECT_EQ(0, records.access);
EXPECT_EQ(1, records.domain);
/* Check that autobind generates a denied bind event. */
EXPECT_EQ(-EACCES, sendto_variant(sock_fd, &self->srv1, "A", 1, 0));
EXPECT_EQ(0, matches_auditlog(self->audit_fd, "net\\.bind_udp", NULL,
NULL, NULL));
EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
EXPECT_EQ(0, records.access);
EXPECT_EQ(0, records.domain);
EXPECT_EQ(-EACCES,
sendto_variant(sock_fd, &self->unspec_srv0, "B", 1, 0));
EXPECT_EQ(0, matches_auditlog(self->audit_fd, "net\\.connect_send_udp",
"daddr", NULL, "dest"));
EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
EXPECT_EQ(0, records.access);
EXPECT_EQ(0, records.domain);
EXPECT_EQ(0, close(sock_fd));
}
TEST_HARNESS_MAIN