staticvoid scope_signal_handler(int sig, siginfo_t *info, void *ucontext)
{ if (sig == SIGTRAP || sig == SIGURG || sig == SIGHUP || sig == SIGTSTP)
is_signaled = 1;
}
/* * In this test, a child process sends a signal to parent before and * after getting scoped.
*/
TEST_F(scoping_signals, send_sig_to_parent)
{ int pipe_parent[2]; int status;
pid_t child;
pid_t parent = getpid(); struct sigaction action = {
.sa_sigaction = scope_signal_handler,
.sa_flags = SA_SIGINFO,
/* The process should not have already been signaled. */
EXPECT_EQ(0, is_signaled);
child = fork();
ASSERT_LE(0, child); if (child == 0) { char buf_child; int err;
EXPECT_EQ(0, close(pipe_parent[1]));
/* * The child process can send signal to parent when * domain is not scoped.
*/
err = kill(parent, variant->sig);
ASSERT_EQ(0, err);
ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
EXPECT_EQ(0, close(pipe_parent[0]));
/* * The child process cannot send signal to the parent * anymore.
*/
err = kill(parent, variant->sig);
ASSERT_EQ(-1, err);
ASSERT_EQ(EPERM, errno);
/* * No matter of the domain, a process should be able to * send a signal to itself.
*/
ASSERT_EQ(0, is_signaled);
ASSERT_EQ(0, raise(variant->sig));
ASSERT_EQ(1, is_signaled);
/* Waits for a first signal to be received, without race condition. */ while (!is_signaled && !usleep(1))
;
ASSERT_EQ(1, is_signaled);
ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
EXPECT_EQ(0, close(pipe_parent[1]));
is_signaled = 0;
/* * This test ensures that a scoped process cannot send signal out of * scoped domain.
*/
TEST_F(scoped_domains, check_access_signal)
{
pid_t child;
pid_t parent = getpid(); int status; bool can_signal_child, can_signal_parent; int pipe_parent[2], pipe_child[2]; char buf_parent; int err;
/* Waits for the parent to send signals. */
ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
EXPECT_EQ(0, close(pipe_parent[0]));
err = kill(parent, 0); if (can_signal_parent) {
ASSERT_EQ(0, err);
} else {
ASSERT_EQ(-1, err);
ASSERT_EQ(EPERM, errno);
} /* * No matter of the domain, a process should be able to * send a signal to itself.
*/
ASSERT_EQ(0, raise(0));
/* Capabilities must be set before creating a new thread. */
set_cap(_metadata, CAP_SETUID);
ASSERT_EQ(0, pthread_create(&no_sandbox_thread, NULL, thread_setuid,
&arg));
/* Enforces restriction after creating the thread. */
create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
/* clang-format off */
FIXTURE_VARIANT_ADD(fown, no_sandbox) { /* clang-format on */
.sandbox_setown = SANDBOX_NONE,
};
/* clang-format off */
FIXTURE_VARIANT_ADD(fown, sandbox_before_fork) { /* clang-format on */
.sandbox_setown = SANDBOX_BEFORE_FORK,
};
/* clang-format off */
FIXTURE_VARIANT_ADD(fown, sandbox_before_setown) { /* clang-format on */
.sandbox_setown = SANDBOX_BEFORE_SETOWN,
};
/* clang-format off */
FIXTURE_VARIANT_ADD(fown, sandbox_after_setown) { /* clang-format on */
.sandbox_setown = SANDBOX_AFTER_SETOWN,
};
FIXTURE_SETUP(fown)
{
drop_caps(_metadata);
}
FIXTURE_TEARDOWN(fown)
{
}
/* * Sending an out of bound message will trigger the SIGURG signal * through file_send_sigiotask.
*/
TEST_F(fown, sigurg_socket)
{ int server_socket, recv_socket; struct service_fixture server_address; char buffer_parent; int status; int pipe_parent[2], pipe_child[2];
pid_t child;
/* Waits for the parent to listen. */
ASSERT_EQ(1, read(pipe_parent[0], &buffer_child, 1));
ASSERT_EQ(0, connect(client_socket, &server_address.unix_addr,
server_address.unix_addr_len));
/* * Waits for the parent to accept the connection, sandbox * itself, and call fcntl(2).
*/
ASSERT_EQ(1, read(pipe_parent[0], &buffer_child, 1)); /* May signal itself. */
ASSERT_EQ(1, send(client_socket, ".", 1, MSG_OOB));
EXPECT_EQ(0, close(client_socket));
ASSERT_EQ(1, write(pipe_child[1], ".", 1));
EXPECT_EQ(0, close(pipe_child[1]));
/* Waits for the message to be received. */
ASSERT_EQ(1, read(pipe_parent[0], &buffer_child, 1));
EXPECT_EQ(0, close(pipe_parent[0]));
if (variant->sandbox_setown == SANDBOX_BEFORE_SETOWN) {
ASSERT_EQ(0, signal_received);
} else { /* * A signal is only received if fcntl(F_SETOWN) was * called before any sandboxing or if the signal * receiver is in the same domain.
*/
ASSERT_EQ(1, signal_received);
}
_exit(_metadata->exit_code); return;
}
EXPECT_EQ(0, close(pipe_parent[0]));
EXPECT_EQ(0, close(pipe_child[1]));
if (variant->sandbox_setown == SANDBOX_BEFORE_SETOWN)
create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
/* * Sets the child to receive SIGURG for MSG_OOB. This uncommon use is * a valid attack scenario which also simplifies this test.
*/
ASSERT_EQ(0, fcntl(recv_socket, F_SETOWN, child));
if (variant->sandbox_setown == SANDBOX_AFTER_SETOWN)
create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
/* Waits for the child to send MSG_OOB. */
ASSERT_EQ(1, read(pipe_child[0], &buffer_parent, 1));
EXPECT_EQ(0, close(pipe_child[0]));
ASSERT_EQ(1, recv(recv_socket, &buffer_parent, 1, MSG_OOB));
EXPECT_EQ(0, close(recv_socket));
EXPECT_EQ(0, close(server_socket));
ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
EXPECT_EQ(0, close(pipe_parent[1]));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.