mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 02:17:36 -04:00
Merge tag 'landlock-7.2-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux
Pull landlock fix from Mickaël Salaün: "This fixes TCP Fast Open support, specific test environments, and doc warnings" * tag 'landlock-7.2-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux: selftests/landlock: Skip scoped_signal subtest with MSG_OOB if not available selftests/landlock: Fix screwed up pointers in the scoped_signal_test landlock: Update formatting landlock: Fix kernel-doc for the nested quiet layer flag selftests/landlock: Add test for TCP fast open landlock: Fix TCP Fast Open connection bypass
This commit is contained in:
@@ -351,6 +351,14 @@ static int hook_socket_sendmsg(struct socket *const sock,
|
||||
access_mask_t access_request;
|
||||
int ret = 0;
|
||||
|
||||
if ((msg->msg_flags & MSG_FASTOPEN) && address && sk_is_tcp(sock->sk)) {
|
||||
ret = current_check_access_socket(
|
||||
sock, address, addrlen, LANDLOCK_ACCESS_NET_CONNECT_TCP,
|
||||
true);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (sk_is_udp(sock->sk))
|
||||
access_request = LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP;
|
||||
else
|
||||
|
||||
@@ -35,8 +35,8 @@ struct landlock_layer {
|
||||
*/
|
||||
struct {
|
||||
/**
|
||||
* @quiet: Suppresses denial logs for the object covered by this
|
||||
* rule in this domain. For filesystem rules, this inherits
|
||||
* @flags.quiet: Suppresses denial logs for the object covered by
|
||||
* this rule in this domain. For filesystem rules, this inherits
|
||||
* down the file hierarchy.
|
||||
*/
|
||||
u8 quiet : 1;
|
||||
|
||||
@@ -95,8 +95,7 @@ static int hook_ptrace_access_check(struct task_struct *const child,
|
||||
if (!parent_subject)
|
||||
return 0;
|
||||
|
||||
scoped_guard(rcu)
|
||||
{
|
||||
scoped_guard(rcu) {
|
||||
const struct landlock_ruleset *const child_dom =
|
||||
landlock_get_task_domain(child);
|
||||
err = domain_ptrace(parent_subject->domain, child_dom);
|
||||
@@ -370,8 +369,7 @@ static int hook_task_kill(struct task_struct *const p,
|
||||
if (!subject)
|
||||
return 0;
|
||||
|
||||
scoped_guard(rcu)
|
||||
{
|
||||
scoped_guard(rcu) {
|
||||
is_scoped = domain_is_scoped(subject->domain,
|
||||
landlock_get_task_domain(p),
|
||||
signal_scope.scope);
|
||||
@@ -422,8 +420,7 @@ static int hook_file_send_sigiotask(struct task_struct *tsk,
|
||||
if (task_tgid(tsk) == landlock_file(fown->file)->fown_tg)
|
||||
return 0;
|
||||
|
||||
scoped_guard(rcu)
|
||||
{
|
||||
scoped_guard(rcu) {
|
||||
is_scoped = domain_is_scoped(subject->domain,
|
||||
landlock_get_task_domain(tsk),
|
||||
signal_scope.scope);
|
||||
|
||||
@@ -1281,6 +1281,103 @@ TEST_F(protocol, connect_unspec)
|
||||
EXPECT_EQ(0, close(bind_fd));
|
||||
}
|
||||
|
||||
TEST_F(protocol, tcp_fastopen)
|
||||
{
|
||||
const bool restricted = variant->sandbox == TCP_SANDBOX &&
|
||||
variant->prot.type == SOCK_STREAM &&
|
||||
(variant->prot.protocol == IPPROTO_TCP ||
|
||||
variant->prot.protocol == IPPROTO_IP) &&
|
||||
(variant->prot.domain == AF_INET ||
|
||||
variant->prot.domain == AF_INET6);
|
||||
const struct landlock_ruleset_attr ruleset_attr = {
|
||||
.handled_access_net = LANDLOCK_ACCESS_NET_CONNECT_TCP,
|
||||
};
|
||||
int bind_fd, client_fd, status;
|
||||
char buf;
|
||||
pid_t child;
|
||||
|
||||
bind_fd = socket_variant(&self->srv0);
|
||||
ASSERT_LE(0, bind_fd);
|
||||
EXPECT_EQ(0, bind_variant(bind_fd, &self->srv0));
|
||||
if (self->srv0.protocol.type == SOCK_STREAM)
|
||||
EXPECT_EQ(0, listen(bind_fd, backlog));
|
||||
|
||||
child = fork();
|
||||
ASSERT_LE(0, child);
|
||||
if (child == 0) {
|
||||
int connect_fd, ret;
|
||||
|
||||
/* Closes listening socket for the child. */
|
||||
EXPECT_EQ(0, close(bind_fd));
|
||||
|
||||
connect_fd = socket_variant(&self->srv0);
|
||||
ASSERT_LE(0, connect_fd);
|
||||
|
||||
if (variant->sandbox == TCP_SANDBOX) {
|
||||
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));
|
||||
}
|
||||
|
||||
/* Fast Open with no address. */
|
||||
ret = sendto_variant(connect_fd, NULL, NULL, 0, MSG_FASTOPEN);
|
||||
if (self->srv0.protocol.domain == AF_UNIX) {
|
||||
EXPECT_EQ(-ENOTCONN, ret);
|
||||
} else if (self->srv0.protocol.type == SOCK_DGRAM) {
|
||||
EXPECT_EQ(-EDESTADDRREQ, ret);
|
||||
} else {
|
||||
EXPECT_EQ(-EINVAL, ret);
|
||||
}
|
||||
|
||||
/* Fast Open to a denied address. */
|
||||
ret = sendto_variant(connect_fd, &self->srv0, "A", 1,
|
||||
MSG_FASTOPEN);
|
||||
if (restricted) {
|
||||
EXPECT_EQ(-EACCES, ret);
|
||||
} else if (self->srv0.protocol.domain == AF_UNIX &&
|
||||
self->srv0.protocol.type == SOCK_STREAM) {
|
||||
EXPECT_EQ(-EOPNOTSUPP, ret);
|
||||
} else {
|
||||
EXPECT_EQ(0, ret);
|
||||
}
|
||||
|
||||
EXPECT_EQ(0, close(connect_fd));
|
||||
_exit(_metadata->exit_code);
|
||||
return;
|
||||
}
|
||||
|
||||
client_fd = bind_fd;
|
||||
if (!restricted && self->srv0.protocol.type == SOCK_STREAM &&
|
||||
self->srv0.protocol.domain != AF_UNIX) {
|
||||
client_fd = accept(bind_fd, NULL, 0);
|
||||
ASSERT_LE(0, client_fd);
|
||||
}
|
||||
|
||||
if (restricted) {
|
||||
EXPECT_EQ(-1, read(client_fd, &buf, 1));
|
||||
EXPECT_EQ(ENOTCONN, errno);
|
||||
} else if (self->srv0.protocol.domain == AF_UNIX &&
|
||||
self->srv0.protocol.type == SOCK_STREAM) {
|
||||
EXPECT_EQ(-1, read(client_fd, &buf, 1));
|
||||
EXPECT_EQ(EINVAL, errno);
|
||||
} else {
|
||||
EXPECT_EQ(1, read(client_fd, &buf, 1));
|
||||
EXPECT_EQ('A', buf);
|
||||
}
|
||||
|
||||
EXPECT_EQ(child, waitpid(child, &status, 0));
|
||||
EXPECT_EQ(1, WIFEXITED(status));
|
||||
EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
|
||||
|
||||
if (client_fd != bind_fd)
|
||||
EXPECT_LE(0, close(client_fd));
|
||||
|
||||
EXPECT_EQ(0, close(bind_fd));
|
||||
}
|
||||
|
||||
TEST_F(protocol, sendmsg_stream)
|
||||
{
|
||||
int srv0_fd, tmp_fd, client_fd, res;
|
||||
|
||||
@@ -249,12 +249,12 @@ TEST_F(scoped_domains, check_access_signal)
|
||||
_metadata->exit_code = KSFT_FAIL;
|
||||
}
|
||||
|
||||
enum thread_return {
|
||||
THREAD_INVALID = 0,
|
||||
THREAD_SUCCESS = 1,
|
||||
THREAD_ERROR = 2,
|
||||
THREAD_TEST_FAILED = 3,
|
||||
};
|
||||
/* clang-format off */
|
||||
#define THREAD_INVALID ((void *)0)
|
||||
#define THREAD_SUCCESS ((void *)1)
|
||||
#define THREAD_ERROR ((void *)2)
|
||||
#define THREAD_TEST_FAILED ((void *)3)
|
||||
/* clang-format on */
|
||||
|
||||
static void *thread_sync(void *arg)
|
||||
{
|
||||
@@ -262,15 +262,15 @@ static void *thread_sync(void *arg)
|
||||
char buf;
|
||||
|
||||
if (read(pipe_read, &buf, 1) != 1)
|
||||
return (void *)THREAD_ERROR;
|
||||
return THREAD_ERROR;
|
||||
|
||||
return (void *)THREAD_SUCCESS;
|
||||
return THREAD_SUCCESS;
|
||||
}
|
||||
|
||||
TEST(signal_scoping_thread_before)
|
||||
{
|
||||
pthread_t no_sandbox_thread;
|
||||
enum thread_return ret = THREAD_INVALID;
|
||||
void *ret = THREAD_INVALID;
|
||||
int thread_pipe[2];
|
||||
|
||||
drop_caps(_metadata);
|
||||
@@ -285,7 +285,7 @@ TEST(signal_scoping_thread_before)
|
||||
EXPECT_EQ(0, pthread_kill(no_sandbox_thread, 0));
|
||||
EXPECT_EQ(1, write(thread_pipe[1], ".", 1));
|
||||
|
||||
EXPECT_EQ(0, pthread_join(no_sandbox_thread, (void **)&ret));
|
||||
EXPECT_EQ(0, pthread_join(no_sandbox_thread, &ret));
|
||||
EXPECT_EQ(THREAD_SUCCESS, ret);
|
||||
|
||||
EXPECT_EQ(0, close(thread_pipe[0]));
|
||||
@@ -295,7 +295,7 @@ TEST(signal_scoping_thread_before)
|
||||
TEST(signal_scoping_thread_after)
|
||||
{
|
||||
pthread_t scoped_thread;
|
||||
enum thread_return ret = THREAD_INVALID;
|
||||
void *ret = THREAD_INVALID;
|
||||
int thread_pipe[2];
|
||||
|
||||
drop_caps(_metadata);
|
||||
@@ -310,7 +310,7 @@ TEST(signal_scoping_thread_after)
|
||||
EXPECT_EQ(0, pthread_kill(scoped_thread, 0));
|
||||
EXPECT_EQ(1, write(thread_pipe[1], ".", 1));
|
||||
|
||||
EXPECT_EQ(0, pthread_join(scoped_thread, (void **)&ret));
|
||||
EXPECT_EQ(0, pthread_join(scoped_thread, &ret));
|
||||
EXPECT_EQ(THREAD_SUCCESS, ret);
|
||||
|
||||
EXPECT_EQ(0, close(thread_pipe[0]));
|
||||
@@ -327,20 +327,20 @@ void *thread_setuid(void *ptr)
|
||||
char buf;
|
||||
|
||||
if (read(arg->pipe_read, &buf, 1) != 1)
|
||||
return (void *)THREAD_ERROR;
|
||||
return THREAD_ERROR;
|
||||
|
||||
/* libc's setuid() should update all thread's credentials. */
|
||||
if (getuid() != arg->new_uid)
|
||||
return (void *)THREAD_TEST_FAILED;
|
||||
return THREAD_TEST_FAILED;
|
||||
|
||||
return (void *)THREAD_SUCCESS;
|
||||
return THREAD_SUCCESS;
|
||||
}
|
||||
|
||||
TEST(signal_scoping_thread_setuid)
|
||||
{
|
||||
struct thread_setuid_args arg;
|
||||
pthread_t no_sandbox_thread;
|
||||
enum thread_return ret = THREAD_INVALID;
|
||||
void *ret = THREAD_INVALID;
|
||||
int pipe_parent[2];
|
||||
int prev_uid;
|
||||
|
||||
@@ -367,7 +367,7 @@ TEST(signal_scoping_thread_setuid)
|
||||
EXPECT_EQ(arg.new_uid, getuid());
|
||||
EXPECT_EQ(1, write(pipe_parent[1], ".", 1));
|
||||
|
||||
EXPECT_EQ(0, pthread_join(no_sandbox_thread, (void **)&ret));
|
||||
EXPECT_EQ(0, pthread_join(no_sandbox_thread, &ret));
|
||||
EXPECT_EQ(THREAD_SUCCESS, ret);
|
||||
|
||||
clear_cap(_metadata, CAP_SETUID);
|
||||
@@ -400,6 +400,24 @@ static int setup_signal_handler(int signal)
|
||||
return sigaction(SIGURG, &sa, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* MSG_OOB might be disabled in the kernel via the CONFIG_AF_UNIX_OOB
|
||||
* switch, so this function can be used for probing for its availability.
|
||||
*/
|
||||
static bool has_af_unix_oob(void)
|
||||
{
|
||||
bool available = false;
|
||||
int sp[2];
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0) {
|
||||
available = (send(sp[0], ".", 1, MSG_OOB) == 1);
|
||||
close(sp[0]);
|
||||
close(sp[1]);
|
||||
}
|
||||
|
||||
return available;
|
||||
}
|
||||
|
||||
/* clang-format off */
|
||||
FIXTURE(fown) {};
|
||||
/* clang-format on */
|
||||
@@ -462,6 +480,9 @@ TEST_F(fown, sigurg_socket)
|
||||
int pipe_parent[2], pipe_child[2];
|
||||
pid_t child;
|
||||
|
||||
if (!has_af_unix_oob())
|
||||
SKIP(return, "CONFIG_AF_UNIX_OOB / MSG_OOB not available");
|
||||
|
||||
memset(&server_address, 0, sizeof(server_address));
|
||||
set_unix_address(&server_address, 0);
|
||||
|
||||
@@ -667,20 +688,20 @@ static void *thread_setown_scoped(void *arg)
|
||||
ruleset_fd =
|
||||
landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
|
||||
if (ruleset_fd < 0)
|
||||
return (void *)THREAD_ERROR;
|
||||
return THREAD_ERROR;
|
||||
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) ||
|
||||
landlock_restrict_self(ruleset_fd, 0)) {
|
||||
close(ruleset_fd);
|
||||
return (void *)THREAD_ERROR;
|
||||
return THREAD_ERROR;
|
||||
}
|
||||
close(ruleset_fd);
|
||||
|
||||
/* Makes this process group own the SIGIO source. */
|
||||
if (fcntl(fd, F_SETSIG, SIGURG) || fcntl(fd, F_SETOWN, -getpgrp()) ||
|
||||
fcntl(fd, F_SETFL, O_ASYNC))
|
||||
return (void *)THREAD_ERROR;
|
||||
return THREAD_ERROR;
|
||||
|
||||
return (void *)THREAD_SUCCESS;
|
||||
return THREAD_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -702,7 +723,7 @@ TEST(sigio_to_pgid_self)
|
||||
{
|
||||
int trigger[2];
|
||||
pthread_t thread;
|
||||
enum thread_return ret = THREAD_INVALID;
|
||||
void *ret = THREAD_INVALID;
|
||||
int i;
|
||||
|
||||
drop_caps(_metadata);
|
||||
@@ -722,7 +743,7 @@ TEST(sigio_to_pgid_self)
|
||||
*/
|
||||
ASSERT_EQ(0, pthread_create(&thread, NULL, thread_setown_scoped,
|
||||
&trigger[0]));
|
||||
ASSERT_EQ(0, pthread_join(thread, (void **)&ret));
|
||||
ASSERT_EQ(0, pthread_join(thread, &ret));
|
||||
ASSERT_EQ(THREAD_SUCCESS, ret);
|
||||
|
||||
/* Fans SIGURG out to the process group. */
|
||||
|
||||
Reference in New Issue
Block a user