Merge tag 'landlock-6.19-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux
Pull landlock fixes from Mickaël Salaün: "This fixes TCP handling, tests, documentation, non-audit elided code, and minor cosmetic changes" * tag 'landlock-6.19-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux: landlock: Clarify documentation for the IOCTL access right selftests/landlock: Properly close a file descriptor landlock: Improve the comment for domain_is_scoped selftests/landlock: Use scoped_base_variants.h for ptrace_test selftests/landlock: Fix missing semicolon selftests/landlock: Fix typo in fs_test landlock: Optimize stack usage when !CONFIG_AUDIT landlock: Fix spelling landlock: Clean up hook_ptrace_access_check() landlock: Improve erratum documentation landlock: Remove useless include landlock: Fix wrong type usage selftests/landlock: NULL-terminate unix pathname addresses selftests/landlock: Remove invalid unix socket bind() selftests/landlock: Add missing connect(minimal AF_UNSPEC) test selftests/landlock: Fix TCP bind(AF_UNSPEC) test case landlock: Fix TCP handling of short AF_UNSPEC addresses landlock: Fix formatting
This commit is contained in:
@@ -216,6 +216,23 @@ struct landlock_net_port_attr {
|
||||
* :manpage:`ftruncate(2)`, :manpage:`creat(2)`, or :manpage:`open(2)` with
|
||||
* ``O_TRUNC``. This access right is available since the third version of the
|
||||
* Landlock ABI.
|
||||
* - %LANDLOCK_ACCESS_FS_IOCTL_DEV: Invoke :manpage:`ioctl(2)` commands on an opened
|
||||
* character or block device.
|
||||
*
|
||||
* This access right applies to all `ioctl(2)` commands implemented by device
|
||||
* drivers. However, the following common IOCTL commands continue to be
|
||||
* invokable independent of the %LANDLOCK_ACCESS_FS_IOCTL_DEV right:
|
||||
*
|
||||
* * IOCTL commands targeting file descriptors (``FIOCLEX``, ``FIONCLEX``),
|
||||
* * IOCTL commands targeting file descriptions (``FIONBIO``, ``FIOASYNC``),
|
||||
* * IOCTL commands targeting file systems (``FIFREEZE``, ``FITHAW``,
|
||||
* ``FIGETBSZ``, ``FS_IOC_GETFSUUID``, ``FS_IOC_GETFSSYSFSPATH``)
|
||||
* * Some IOCTL commands which do not make sense when used with devices, but
|
||||
* whose implementations are safe and return the right error codes
|
||||
* (``FS_IOC_FIEMAP``, ``FICLONE``, ``FICLONERANGE``, ``FIDEDUPERANGE``)
|
||||
*
|
||||
* This access right is available since the fifth version of the Landlock
|
||||
* ABI.
|
||||
*
|
||||
* Whether an opened file can be truncated with :manpage:`ftruncate(2)` or used
|
||||
* with `ioctl(2)` is determined during :manpage:`open(2)`, in the same way as
|
||||
@@ -275,26 +292,6 @@ struct landlock_net_port_attr {
|
||||
* If multiple requirements are not met, the ``EACCES`` error code takes
|
||||
* precedence over ``EXDEV``.
|
||||
*
|
||||
* The following access right applies both to files and directories:
|
||||
*
|
||||
* - %LANDLOCK_ACCESS_FS_IOCTL_DEV: Invoke :manpage:`ioctl(2)` commands on an opened
|
||||
* character or block device.
|
||||
*
|
||||
* This access right applies to all `ioctl(2)` commands implemented by device
|
||||
* drivers. However, the following common IOCTL commands continue to be
|
||||
* invokable independent of the %LANDLOCK_ACCESS_FS_IOCTL_DEV right:
|
||||
*
|
||||
* * IOCTL commands targeting file descriptors (``FIOCLEX``, ``FIONCLEX``),
|
||||
* * IOCTL commands targeting file descriptions (``FIONBIO``, ``FIOASYNC``),
|
||||
* * IOCTL commands targeting file systems (``FIFREEZE``, ``FITHAW``,
|
||||
* ``FIGETBSZ``, ``FS_IOC_GETFSUUID``, ``FS_IOC_GETFSSYSFSPATH``)
|
||||
* * Some IOCTL commands which do not make sense when used with devices, but
|
||||
* whose implementations are safe and return the right error codes
|
||||
* (``FS_IOC_FIEMAP``, ``FICLONE``, ``FICLONERANGE``, ``FIDEDUPERANGE``)
|
||||
*
|
||||
* This access right is available since the fifth version of the Landlock
|
||||
* ABI.
|
||||
*
|
||||
* .. warning::
|
||||
*
|
||||
* It is currently not possible to restrict some file-related actions
|
||||
|
||||
@@ -191,7 +191,7 @@ static size_t get_denied_layer(const struct landlock_ruleset *const domain,
|
||||
long youngest_layer = -1;
|
||||
|
||||
for_each_set_bit(access_bit, &access_req, layer_masks_size) {
|
||||
const access_mask_t mask = (*layer_masks)[access_bit];
|
||||
const layer_mask_t mask = (*layer_masks)[access_bit];
|
||||
long layer;
|
||||
|
||||
if (!mask)
|
||||
|
||||
@@ -97,7 +97,7 @@ struct landlock_hierarchy {
|
||||
*/
|
||||
atomic64_t num_denials;
|
||||
/**
|
||||
* @id: Landlock domain ID, sets once at domain creation time.
|
||||
* @id: Landlock domain ID, set once at domain creation time.
|
||||
*/
|
||||
u64 id;
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* This fix addresses an issue where signal scoping was overly restrictive,
|
||||
* preventing sandboxed threads from signaling other threads within the same
|
||||
* process if they belonged to different domains. Because threads are not
|
||||
* security boundaries, user space might assume that any thread within the same
|
||||
* security boundaries, user space might assume that all threads within the same
|
||||
* process can send signals between themselves (see :manpage:`nptl(7)` and
|
||||
* :manpage:`libpsx(3)`). Consistent with :manpage:`ptrace(2)` behavior, direct
|
||||
* interaction between threads of the same process should always be allowed.
|
||||
|
||||
@@ -939,7 +939,12 @@ jump_up:
|
||||
}
|
||||
path_put(&walker_path);
|
||||
|
||||
if (!allowed_parent1) {
|
||||
/*
|
||||
* Check CONFIG_AUDIT to enable elision of log_request_parent* and
|
||||
* associated caller's stack variables thanks to dead code elimination.
|
||||
*/
|
||||
#ifdef CONFIG_AUDIT
|
||||
if (!allowed_parent1 && log_request_parent1) {
|
||||
log_request_parent1->type = LANDLOCK_REQUEST_FS_ACCESS;
|
||||
log_request_parent1->audit.type = LSM_AUDIT_DATA_PATH;
|
||||
log_request_parent1->audit.u.path = *path;
|
||||
@@ -949,7 +954,7 @@ jump_up:
|
||||
ARRAY_SIZE(*layer_masks_parent1);
|
||||
}
|
||||
|
||||
if (!allowed_parent2) {
|
||||
if (!allowed_parent2 && log_request_parent2) {
|
||||
log_request_parent2->type = LANDLOCK_REQUEST_FS_ACCESS;
|
||||
log_request_parent2->audit.type = LSM_AUDIT_DATA_PATH;
|
||||
log_request_parent2->audit.u.path = *path;
|
||||
@@ -958,6 +963,8 @@ jump_up:
|
||||
log_request_parent2->layer_masks_size =
|
||||
ARRAY_SIZE(*layer_masks_parent2);
|
||||
}
|
||||
#endif /* CONFIG_AUDIT */
|
||||
|
||||
return allowed_parent1 && allowed_parent2;
|
||||
}
|
||||
|
||||
@@ -1314,7 +1321,8 @@ static void hook_sb_delete(struct super_block *const sb)
|
||||
* second call to iput() for the same Landlock object. Also
|
||||
* checks I_NEW because such inode cannot be tied to an object.
|
||||
*/
|
||||
if (inode_state_read(inode) & (I_FREEING | I_WILL_FREE | I_NEW)) {
|
||||
if (inode_state_read(inode) &
|
||||
(I_FREEING | I_WILL_FREE | I_NEW)) {
|
||||
spin_unlock(&inode->i_lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -71,6 +71,61 @@ static int current_check_access_socket(struct socket *const sock,
|
||||
|
||||
switch (address->sa_family) {
|
||||
case AF_UNSPEC:
|
||||
if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP) {
|
||||
/*
|
||||
* Connecting to an address with AF_UNSPEC dissolves
|
||||
* the TCP association, which have the same effect as
|
||||
* closing the connection while retaining the socket
|
||||
* object (i.e., the file descriptor). As for dropping
|
||||
* privileges, closing connections is always allowed.
|
||||
*
|
||||
* For a TCP access control system, this request is
|
||||
* legitimate. Let the network stack handle potential
|
||||
* inconsistencies and return -EINVAL if needed.
|
||||
*/
|
||||
return 0;
|
||||
} else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP) {
|
||||
/*
|
||||
* Binding to an AF_UNSPEC address is treated
|
||||
* differently by IPv4 and IPv6 sockets. The socket's
|
||||
* family may change under our feet due to
|
||||
* setsockopt(IPV6_ADDRFORM), but that's ok: we either
|
||||
* reject entirely or require
|
||||
* %LANDLOCK_ACCESS_NET_BIND_TCP for the given port, so
|
||||
* it cannot be used to bypass the policy.
|
||||
*
|
||||
* IPv4 sockets map AF_UNSPEC to AF_INET for
|
||||
* retrocompatibility for bind accesses, only if the
|
||||
* address is INADDR_ANY (cf. __inet_bind). IPv6
|
||||
* sockets always reject it.
|
||||
*
|
||||
* Checking the address is required to not wrongfully
|
||||
* return -EACCES instead of -EAFNOSUPPORT or -EINVAL.
|
||||
* We could return 0 and let the network stack handle
|
||||
* these checks, but it is safer to return a proper
|
||||
* error and test consistency thanks to kselftest.
|
||||
*/
|
||||
if (sock->sk->__sk_common.skc_family == AF_INET) {
|
||||
const struct sockaddr_in *const sockaddr =
|
||||
(struct sockaddr_in *)address;
|
||||
|
||||
if (addrlen < sizeof(struct sockaddr_in))
|
||||
return -EINVAL;
|
||||
|
||||
if (sockaddr->sin_addr.s_addr !=
|
||||
htonl(INADDR_ANY))
|
||||
return -EAFNOSUPPORT;
|
||||
} else {
|
||||
if (addrlen < SIN6_LEN_RFC2133)
|
||||
return -EINVAL;
|
||||
else
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
} else {
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
/* Only for bind(AF_UNSPEC+INADDR_ANY) on IPv4 socket. */
|
||||
fallthrough;
|
||||
case AF_INET: {
|
||||
const struct sockaddr_in *addr4;
|
||||
|
||||
@@ -119,57 +174,18 @@ static int current_check_access_socket(struct socket *const sock,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Specific AF_UNSPEC handling. */
|
||||
if (address->sa_family == AF_UNSPEC) {
|
||||
/*
|
||||
* Connecting to an address with AF_UNSPEC dissolves the TCP
|
||||
* association, which have the same effect as closing the
|
||||
* connection while retaining the socket object (i.e., the file
|
||||
* descriptor). As for dropping privileges, closing
|
||||
* connections is always allowed.
|
||||
*
|
||||
* For a TCP access control system, this request is legitimate.
|
||||
* Let the network stack handle potential inconsistencies and
|
||||
* return -EINVAL if needed.
|
||||
*/
|
||||
if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* For compatibility reason, accept AF_UNSPEC for bind
|
||||
* accesses (mapped to AF_INET) only if the address is
|
||||
* INADDR_ANY (cf. __inet_bind). Checking the address is
|
||||
* required to not wrongfully return -EACCES instead of
|
||||
* -EAFNOSUPPORT.
|
||||
*
|
||||
* We could return 0 and let the network stack handle these
|
||||
* checks, but it is safer to return a proper error and test
|
||||
* consistency thanks to kselftest.
|
||||
*/
|
||||
if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP) {
|
||||
/* addrlen has already been checked for AF_UNSPEC. */
|
||||
const struct sockaddr_in *const sockaddr =
|
||||
(struct sockaddr_in *)address;
|
||||
|
||||
if (sock->sk->__sk_common.skc_family != AF_INET)
|
||||
return -EINVAL;
|
||||
|
||||
if (sockaddr->sin_addr.s_addr != htonl(INADDR_ANY))
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Checks sa_family consistency to not wrongfully return
|
||||
* -EACCES instead of -EINVAL. Valid sa_family changes are
|
||||
* only (from AF_INET or AF_INET6) to AF_UNSPEC.
|
||||
*
|
||||
* We could return 0 and let the network stack handle this
|
||||
* check, but it is safer to return a proper error and test
|
||||
* consistency thanks to kselftest.
|
||||
*/
|
||||
if (address->sa_family != sock->sk->__sk_common.skc_family)
|
||||
return -EINVAL;
|
||||
}
|
||||
/*
|
||||
* Checks sa_family consistency to not wrongfully return
|
||||
* -EACCES instead of -EINVAL. Valid sa_family changes are
|
||||
* only (from AF_INET or AF_INET6) to AF_UNSPEC.
|
||||
*
|
||||
* We could return 0 and let the network stack handle this
|
||||
* check, but it is safer to return a proper error and test
|
||||
* consistency thanks to kselftest.
|
||||
*/
|
||||
if (address->sa_family != sock->sk->__sk_common.skc_family &&
|
||||
address->sa_family != AF_UNSPEC)
|
||||
return -EINVAL;
|
||||
|
||||
id.key.data = (__force uintptr_t)port;
|
||||
BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data));
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "access.h"
|
||||
#include "audit.h"
|
||||
#include "domain.h"
|
||||
#include "limits.h"
|
||||
#include "object.h"
|
||||
|
||||
@@ -86,7 +86,6 @@ static int hook_ptrace_access_check(struct task_struct *const child,
|
||||
const unsigned int mode)
|
||||
{
|
||||
const struct landlock_cred_security *parent_subject;
|
||||
const struct landlock_ruleset *child_dom;
|
||||
int err;
|
||||
|
||||
/* Quick return for non-landlocked tasks. */
|
||||
@@ -96,7 +95,8 @@ static int hook_ptrace_access_check(struct task_struct *const child,
|
||||
|
||||
scoped_guard(rcu)
|
||||
{
|
||||
child_dom = landlock_get_task_domain(child);
|
||||
const struct landlock_ruleset *const child_dom =
|
||||
landlock_get_task_domain(child);
|
||||
err = domain_ptrace(parent_subject->domain, child_dom);
|
||||
}
|
||||
|
||||
@@ -166,15 +166,15 @@ static int hook_ptrace_traceme(struct task_struct *const parent)
|
||||
}
|
||||
|
||||
/**
|
||||
* domain_is_scoped - Checks if the client domain is scoped in the same
|
||||
* domain as the server.
|
||||
* domain_is_scoped - Check if an interaction from a client/sender to a
|
||||
* server/receiver should be restricted based on scope controls.
|
||||
*
|
||||
* @client: IPC sender domain.
|
||||
* @server: IPC receiver domain.
|
||||
* @scope: The scope restriction criteria.
|
||||
*
|
||||
* Returns: True if the @client domain is scoped to access the @server,
|
||||
* unless the @server is also scoped in the same domain as @client.
|
||||
* Returns: True if @server is in a different domain from @client, and @client
|
||||
* is scoped to access @server (i.e. access should be denied).
|
||||
*/
|
||||
static bool domain_is_scoped(const struct landlock_ruleset *const client,
|
||||
const struct landlock_ruleset *const server,
|
||||
|
||||
@@ -237,6 +237,7 @@ struct service_fixture {
|
||||
struct sockaddr_un unix_addr;
|
||||
socklen_t unix_addr_len;
|
||||
};
|
||||
struct sockaddr_storage _largest;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -4362,22 +4362,24 @@ TEST_F_FORK(layout1, named_unix_domain_socket_ioctl)
|
||||
{
|
||||
const char *const path = file1_s1d1;
|
||||
int srv_fd, cli_fd, ruleset_fd;
|
||||
socklen_t size;
|
||||
struct sockaddr_un srv_un, cli_un;
|
||||
struct sockaddr_un srv_un = {
|
||||
.sun_family = AF_UNIX,
|
||||
};
|
||||
struct sockaddr_un cli_un = {
|
||||
.sun_family = AF_UNIX,
|
||||
};
|
||||
const struct landlock_ruleset_attr attr = {
|
||||
.handled_access_fs = LANDLOCK_ACCESS_FS_IOCTL_DEV,
|
||||
};
|
||||
|
||||
/* Sets up a server */
|
||||
srv_un.sun_family = AF_UNIX;
|
||||
strncpy(srv_un.sun_path, path, sizeof(srv_un.sun_path));
|
||||
|
||||
ASSERT_EQ(0, unlink(path));
|
||||
srv_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
ASSERT_LE(0, srv_fd);
|
||||
|
||||
size = offsetof(struct sockaddr_un, sun_path) + strlen(srv_un.sun_path);
|
||||
ASSERT_EQ(0, bind(srv_fd, (struct sockaddr *)&srv_un, size));
|
||||
strncpy(srv_un.sun_path, path, sizeof(srv_un.sun_path));
|
||||
ASSERT_EQ(0, bind(srv_fd, (struct sockaddr *)&srv_un, sizeof(srv_un)));
|
||||
|
||||
ASSERT_EQ(0, listen(srv_fd, 10 /* qlen */));
|
||||
|
||||
/* Enables Landlock. */
|
||||
@@ -4387,24 +4389,18 @@ TEST_F_FORK(layout1, named_unix_domain_socket_ioctl)
|
||||
ASSERT_EQ(0, close(ruleset_fd));
|
||||
|
||||
/* Sets up a client connection to it */
|
||||
cli_un.sun_family = AF_UNIX;
|
||||
cli_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
ASSERT_LE(0, cli_fd);
|
||||
|
||||
size = offsetof(struct sockaddr_un, sun_path) + strlen(cli_un.sun_path);
|
||||
ASSERT_EQ(0, bind(cli_fd, (struct sockaddr *)&cli_un, size));
|
||||
|
||||
bzero(&cli_un, sizeof(cli_un));
|
||||
cli_un.sun_family = AF_UNIX;
|
||||
strncpy(cli_un.sun_path, path, sizeof(cli_un.sun_path));
|
||||
size = offsetof(struct sockaddr_un, sun_path) + strlen(cli_un.sun_path);
|
||||
|
||||
ASSERT_EQ(0, connect(cli_fd, (struct sockaddr *)&cli_un, size));
|
||||
ASSERT_EQ(0,
|
||||
connect(cli_fd, (struct sockaddr *)&cli_un, sizeof(cli_un)));
|
||||
|
||||
/* FIONREAD and other IOCTLs should not be forbidden. */
|
||||
EXPECT_EQ(0, test_fionread_ioctl(cli_fd));
|
||||
|
||||
ASSERT_EQ(0, close(cli_fd));
|
||||
EXPECT_EQ(0, close(cli_fd));
|
||||
EXPECT_EQ(0, close(srv_fd));
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
@@ -7074,8 +7070,8 @@ static int matches_log_fs_extra(struct __test_metadata *const _metadata,
|
||||
return -E2BIG;
|
||||
|
||||
/*
|
||||
* It is assume that absolute_path does not contain control characters nor
|
||||
* spaces, see audit_string_contains_control().
|
||||
* It is assumed that absolute_path does not contain control
|
||||
* characters nor spaces, see audit_string_contains_control().
|
||||
*/
|
||||
absolute_path = realpath(path, NULL);
|
||||
if (!absolute_path)
|
||||
|
||||
@@ -121,6 +121,10 @@ static socklen_t get_addrlen(const struct service_fixture *const srv,
|
||||
{
|
||||
switch (srv->protocol.domain) {
|
||||
case AF_UNSPEC:
|
||||
if (minimal)
|
||||
return sizeof(sa_family_t);
|
||||
return sizeof(struct sockaddr_storage);
|
||||
|
||||
case AF_INET:
|
||||
return sizeof(srv->ipv4_addr);
|
||||
|
||||
@@ -758,6 +762,11 @@ TEST_F(protocol, bind_unspec)
|
||||
bind_fd = socket_variant(&self->srv0);
|
||||
ASSERT_LE(0, bind_fd);
|
||||
|
||||
/* Tries to bind with too small addrlen. */
|
||||
EXPECT_EQ(-EINVAL, bind_variant_addrlen(
|
||||
bind_fd, &self->unspec_any0,
|
||||
get_addrlen(&self->unspec_any0, true) - 1));
|
||||
|
||||
/* Allowed bind on AF_UNSPEC/INADDR_ANY. */
|
||||
ret = bind_variant(bind_fd, &self->unspec_any0);
|
||||
if (variant->prot.domain == AF_INET) {
|
||||
@@ -766,6 +775,8 @@ TEST_F(protocol, bind_unspec)
|
||||
TH_LOG("Failed to bind to unspec/any socket: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
} else if (variant->prot.domain == AF_INET6) {
|
||||
EXPECT_EQ(-EAFNOSUPPORT, ret);
|
||||
} else {
|
||||
EXPECT_EQ(-EINVAL, ret);
|
||||
}
|
||||
@@ -792,6 +803,8 @@ TEST_F(protocol, bind_unspec)
|
||||
} else {
|
||||
EXPECT_EQ(0, ret);
|
||||
}
|
||||
} else if (variant->prot.domain == AF_INET6) {
|
||||
EXPECT_EQ(-EAFNOSUPPORT, ret);
|
||||
} else {
|
||||
EXPECT_EQ(-EINVAL, ret);
|
||||
}
|
||||
@@ -801,7 +814,8 @@ TEST_F(protocol, bind_unspec)
|
||||
bind_fd = socket_variant(&self->srv0);
|
||||
ASSERT_LE(0, bind_fd);
|
||||
ret = bind_variant(bind_fd, &self->unspec_srv0);
|
||||
if (variant->prot.domain == AF_INET) {
|
||||
if (variant->prot.domain == AF_INET ||
|
||||
variant->prot.domain == AF_INET6) {
|
||||
EXPECT_EQ(-EAFNOSUPPORT, ret);
|
||||
} else {
|
||||
EXPECT_EQ(-EINVAL, ret)
|
||||
@@ -892,7 +906,19 @@ TEST_F(protocol, connect_unspec)
|
||||
EXPECT_EQ(0, close(ruleset_fd));
|
||||
}
|
||||
|
||||
ret = connect_variant(connect_fd, &self->unspec_any0);
|
||||
/* Try to re-disconnect with a truncated address struct. */
|
||||
EXPECT_EQ(-EINVAL,
|
||||
connect_variant_addrlen(
|
||||
connect_fd, &self->unspec_any0,
|
||||
get_addrlen(&self->unspec_any0, true) - 1));
|
||||
|
||||
/*
|
||||
* Re-disconnect, with a minimal sockaddr struct (just a
|
||||
* bare af_family=AF_UNSPEC field).
|
||||
*/
|
||||
ret = connect_variant_addrlen(connect_fd, &self->unspec_any0,
|
||||
get_addrlen(&self->unspec_any0,
|
||||
true));
|
||||
if (self->srv0.protocol.domain == AF_UNIX &&
|
||||
self->srv0.protocol.type == SOCK_STREAM) {
|
||||
EXPECT_EQ(-EINVAL, ret);
|
||||
|
||||
@@ -86,16 +86,9 @@ static int get_yama_ptrace_scope(void)
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
FIXTURE(hierarchy) {};
|
||||
FIXTURE(scoped_domains) {};
|
||||
/* clang-format on */
|
||||
|
||||
FIXTURE_VARIANT(hierarchy)
|
||||
{
|
||||
const bool domain_both;
|
||||
const bool domain_parent;
|
||||
const bool domain_child;
|
||||
};
|
||||
|
||||
/*
|
||||
* Test multiple tracing combinations between a parent process P1 and a child
|
||||
* process P2.
|
||||
@@ -104,155 +97,18 @@ FIXTURE_VARIANT(hierarchy)
|
||||
* restriction is enforced in addition to any Landlock check, which means that
|
||||
* all P2 requests to trace P1 would be denied.
|
||||
*/
|
||||
#include "scoped_base_variants.h"
|
||||
|
||||
/*
|
||||
* No domain
|
||||
*
|
||||
* P1-. P1 -> P2 : allow
|
||||
* \ P2 -> P1 : allow
|
||||
* 'P2
|
||||
*/
|
||||
/* clang-format off */
|
||||
FIXTURE_VARIANT_ADD(hierarchy, allow_without_domain) {
|
||||
/* clang-format on */
|
||||
.domain_both = false,
|
||||
.domain_parent = false,
|
||||
.domain_child = false,
|
||||
};
|
||||
|
||||
/*
|
||||
* Child domain
|
||||
*
|
||||
* P1--. P1 -> P2 : allow
|
||||
* \ P2 -> P1 : deny
|
||||
* .'-----.
|
||||
* | P2 |
|
||||
* '------'
|
||||
*/
|
||||
/* clang-format off */
|
||||
FIXTURE_VARIANT_ADD(hierarchy, allow_with_one_domain) {
|
||||
/* clang-format on */
|
||||
.domain_both = false,
|
||||
.domain_parent = false,
|
||||
.domain_child = true,
|
||||
};
|
||||
|
||||
/*
|
||||
* Parent domain
|
||||
* .------.
|
||||
* | P1 --. P1 -> P2 : deny
|
||||
* '------' \ P2 -> P1 : allow
|
||||
* '
|
||||
* P2
|
||||
*/
|
||||
/* clang-format off */
|
||||
FIXTURE_VARIANT_ADD(hierarchy, deny_with_parent_domain) {
|
||||
/* clang-format on */
|
||||
.domain_both = false,
|
||||
.domain_parent = true,
|
||||
.domain_child = false,
|
||||
};
|
||||
|
||||
/*
|
||||
* Parent + child domain (siblings)
|
||||
* .------.
|
||||
* | P1 ---. P1 -> P2 : deny
|
||||
* '------' \ P2 -> P1 : deny
|
||||
* .---'--.
|
||||
* | P2 |
|
||||
* '------'
|
||||
*/
|
||||
/* clang-format off */
|
||||
FIXTURE_VARIANT_ADD(hierarchy, deny_with_sibling_domain) {
|
||||
/* clang-format on */
|
||||
.domain_both = false,
|
||||
.domain_parent = true,
|
||||
.domain_child = true,
|
||||
};
|
||||
|
||||
/*
|
||||
* Same domain (inherited)
|
||||
* .-------------.
|
||||
* | P1----. | P1 -> P2 : allow
|
||||
* | \ | P2 -> P1 : allow
|
||||
* | ' |
|
||||
* | P2 |
|
||||
* '-------------'
|
||||
*/
|
||||
/* clang-format off */
|
||||
FIXTURE_VARIANT_ADD(hierarchy, allow_sibling_domain) {
|
||||
/* clang-format on */
|
||||
.domain_both = true,
|
||||
.domain_parent = false,
|
||||
.domain_child = false,
|
||||
};
|
||||
|
||||
/*
|
||||
* Inherited + child domain
|
||||
* .-----------------.
|
||||
* | P1----. | P1 -> P2 : allow
|
||||
* | \ | P2 -> P1 : deny
|
||||
* | .-'----. |
|
||||
* | | P2 | |
|
||||
* | '------' |
|
||||
* '-----------------'
|
||||
*/
|
||||
/* clang-format off */
|
||||
FIXTURE_VARIANT_ADD(hierarchy, allow_with_nested_domain) {
|
||||
/* clang-format on */
|
||||
.domain_both = true,
|
||||
.domain_parent = false,
|
||||
.domain_child = true,
|
||||
};
|
||||
|
||||
/*
|
||||
* Inherited + parent domain
|
||||
* .-----------------.
|
||||
* |.------. | P1 -> P2 : deny
|
||||
* || P1 ----. | P2 -> P1 : allow
|
||||
* |'------' \ |
|
||||
* | ' |
|
||||
* | P2 |
|
||||
* '-----------------'
|
||||
*/
|
||||
/* clang-format off */
|
||||
FIXTURE_VARIANT_ADD(hierarchy, deny_with_nested_and_parent_domain) {
|
||||
/* clang-format on */
|
||||
.domain_both = true,
|
||||
.domain_parent = true,
|
||||
.domain_child = false,
|
||||
};
|
||||
|
||||
/*
|
||||
* Inherited + parent and child domain (siblings)
|
||||
* .-----------------.
|
||||
* | .------. | P1 -> P2 : deny
|
||||
* | | P1 . | P2 -> P1 : deny
|
||||
* | '------'\ |
|
||||
* | \ |
|
||||
* | .--'---. |
|
||||
* | | P2 | |
|
||||
* | '------' |
|
||||
* '-----------------'
|
||||
*/
|
||||
/* clang-format off */
|
||||
FIXTURE_VARIANT_ADD(hierarchy, deny_with_forked_domain) {
|
||||
/* clang-format on */
|
||||
.domain_both = true,
|
||||
.domain_parent = true,
|
||||
.domain_child = true,
|
||||
};
|
||||
|
||||
FIXTURE_SETUP(hierarchy)
|
||||
FIXTURE_SETUP(scoped_domains)
|
||||
{
|
||||
}
|
||||
|
||||
FIXTURE_TEARDOWN(hierarchy)
|
||||
FIXTURE_TEARDOWN(scoped_domains)
|
||||
{
|
||||
}
|
||||
|
||||
/* Test PTRACE_TRACEME and PTRACE_ATTACH for parent and child. */
|
||||
TEST_F(hierarchy, trace)
|
||||
TEST_F(scoped_domains, trace)
|
||||
{
|
||||
pid_t child, parent;
|
||||
int status, err_proc_read;
|
||||
|
||||
@@ -543,7 +543,7 @@ TEST_F(scoped_vs_unscoped, unix_scoping)
|
||||
|
||||
ASSERT_EQ(1, write(pipe_child[1], ".", 1));
|
||||
ASSERT_EQ(grand_child, waitpid(grand_child, &status, 0));
|
||||
EXPECT_EQ(0, close(stream_server_child))
|
||||
EXPECT_EQ(0, close(stream_server_child));
|
||||
EXPECT_EQ(0, close(dgram_server_child));
|
||||
return;
|
||||
}
|
||||
@@ -779,7 +779,6 @@ FIXTURE_TEARDOWN(various_address_sockets)
|
||||
|
||||
TEST_F(various_address_sockets, scoped_pathname_sockets)
|
||||
{
|
||||
socklen_t size_stream, size_dgram;
|
||||
pid_t child;
|
||||
int status;
|
||||
char buf_child, buf_parent;
|
||||
@@ -798,12 +797,8 @@ TEST_F(various_address_sockets, scoped_pathname_sockets)
|
||||
/* Pathname address. */
|
||||
snprintf(stream_pathname_addr.sun_path,
|
||||
sizeof(stream_pathname_addr.sun_path), "%s", stream_path);
|
||||
size_stream = offsetof(struct sockaddr_un, sun_path) +
|
||||
strlen(stream_pathname_addr.sun_path);
|
||||
snprintf(dgram_pathname_addr.sun_path,
|
||||
sizeof(dgram_pathname_addr.sun_path), "%s", dgram_path);
|
||||
size_dgram = offsetof(struct sockaddr_un, sun_path) +
|
||||
strlen(dgram_pathname_addr.sun_path);
|
||||
|
||||
/* Abstract address. */
|
||||
memset(&stream_abstract_addr, 0, sizeof(stream_abstract_addr));
|
||||
@@ -841,8 +836,9 @@ TEST_F(various_address_sockets, scoped_pathname_sockets)
|
||||
/* Connects with pathname sockets. */
|
||||
stream_pathname_socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
ASSERT_LE(0, stream_pathname_socket);
|
||||
ASSERT_EQ(0, connect(stream_pathname_socket,
|
||||
&stream_pathname_addr, size_stream));
|
||||
ASSERT_EQ(0,
|
||||
connect(stream_pathname_socket, &stream_pathname_addr,
|
||||
sizeof(stream_pathname_addr)));
|
||||
ASSERT_EQ(1, write(stream_pathname_socket, "b", 1));
|
||||
EXPECT_EQ(0, close(stream_pathname_socket));
|
||||
|
||||
@@ -850,12 +846,13 @@ TEST_F(various_address_sockets, scoped_pathname_sockets)
|
||||
dgram_pathname_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
ASSERT_LE(0, dgram_pathname_socket);
|
||||
err = sendto(dgram_pathname_socket, "c", 1, 0,
|
||||
&dgram_pathname_addr, size_dgram);
|
||||
&dgram_pathname_addr, sizeof(dgram_pathname_addr));
|
||||
EXPECT_EQ(1, err);
|
||||
|
||||
/* Sends with connection. */
|
||||
ASSERT_EQ(0, connect(dgram_pathname_socket,
|
||||
&dgram_pathname_addr, size_dgram));
|
||||
ASSERT_EQ(0,
|
||||
connect(dgram_pathname_socket, &dgram_pathname_addr,
|
||||
sizeof(dgram_pathname_addr)));
|
||||
ASSERT_EQ(1, write(dgram_pathname_socket, "d", 1));
|
||||
EXPECT_EQ(0, close(dgram_pathname_socket));
|
||||
|
||||
@@ -910,13 +907,13 @@ TEST_F(various_address_sockets, scoped_pathname_sockets)
|
||||
stream_pathname_socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
ASSERT_LE(0, stream_pathname_socket);
|
||||
ASSERT_EQ(0, bind(stream_pathname_socket, &stream_pathname_addr,
|
||||
size_stream));
|
||||
sizeof(stream_pathname_addr)));
|
||||
ASSERT_EQ(0, listen(stream_pathname_socket, backlog));
|
||||
|
||||
dgram_pathname_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
ASSERT_LE(0, dgram_pathname_socket);
|
||||
ASSERT_EQ(0, bind(dgram_pathname_socket, &dgram_pathname_addr,
|
||||
size_dgram));
|
||||
sizeof(dgram_pathname_addr)));
|
||||
|
||||
/* Sets up abstract servers. */
|
||||
stream_abstract_socket = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Landlock scoped_domains variants
|
||||
* Landlock scoped_domains test variant definition.
|
||||
*
|
||||
* See the hierarchy variants from ptrace_test.c
|
||||
* This file defines a fixture variant "scoped_domains" that has all
|
||||
* permutations of parent/child process being in separate or shared
|
||||
* Landlock domain, or not being in a Landlock domain at all.
|
||||
*
|
||||
* Scoped access tests can include this file to avoid repeating these
|
||||
* combinations.
|
||||
*
|
||||
* Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
|
||||
* Copyright © 2019-2020 ANSSI
|
||||
|
||||
Reference in New Issue
Block a user