Skip to content

Commit c404011

Browse files
committed
selftests/landlock: Test UNIX sockets with any address formats
JIRA: https://issues.redhat.com/browse/RHEL-94688 Expand abstract UNIX socket restriction tests by examining different scenarios for UNIX sockets with pathname or unnamed address formats connection with scoped domain. The various_address_sockets tests ensure that UNIX sockets bound to a filesystem pathname and unnamed sockets created by socketpair can still connect to a socket outside of their scoped domain, meaning that even if the domain is scoped with LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, the socket can connect to a socket outside the scoped domain. Signed-off-by: Tahera Fahimi <fahimitahera@gmail.com> Link: https://lore.kernel.org/r/a9e8016aaa5846252623b158c8f1ce0d666944f4.1725494372.git.fahimitahera@gmail.com [mic: Remove useless clang-format tags, fix unlink/rmdir calls, drop capabilities, rename variables, remove useless mknod/unlink calls, clean up fixture, test write/read on sockets, test sendto() on datagram sockets, close sockets as soon as possible] Co-developed-by: Mickaël Salaün <mic@digikod.net> Signed-off-by: Mickaël Salaün <mic@digikod.net> (cherry picked from commit 4f9a5b5) Signed-off-by: Ryan Sullivan <rysulliv@redhat.com>
1 parent db94112 commit c404011

File tree

3 files changed

+252
-1
lines changed

3 files changed

+252
-1
lines changed

tools/testing/selftests/landlock/common.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
#include "../kselftest_harness.h"
2323

24+
#define TMP_DIR "tmp"
25+
2426
#ifndef __maybe_unused
2527
#define __maybe_unused __attribute__((__unused__))
2628
#endif

tools/testing/selftests/landlock/fs_test.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ int open_tree(int dfd, const char *filename, unsigned int flags)
5959
#define RENAME_EXCHANGE (1 << 1)
6060
#endif
6161

62-
#define TMP_DIR "tmp"
6362
#define BINARY_PATH "./true"
6463

6564
/* Paths (sibling number and depth) */

tools/testing/selftests/landlock/scoped_abstract_unix_test.c

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,4 +621,254 @@ TEST_F(outside_socket, socket_with_different_domain)
621621
_metadata->exit_code = KSFT_FAIL;
622622
}
623623

624+
static const char stream_path[] = TMP_DIR "/stream.sock";
625+
static const char dgram_path[] = TMP_DIR "/dgram.sock";
626+
627+
/* clang-format off */
628+
FIXTURE(various_address_sockets) {};
629+
/* clang-format on */
630+
631+
FIXTURE_VARIANT(various_address_sockets)
632+
{
633+
const int domain;
634+
};
635+
636+
/* clang-format off */
637+
FIXTURE_VARIANT_ADD(various_address_sockets, pathname_socket_scoped_domain) {
638+
/* clang-format on */
639+
.domain = SCOPE_SANDBOX,
640+
};
641+
642+
/* clang-format off */
643+
FIXTURE_VARIANT_ADD(various_address_sockets, pathname_socket_other_domain) {
644+
/* clang-format on */
645+
.domain = OTHER_SANDBOX,
646+
};
647+
648+
/* clang-format off */
649+
FIXTURE_VARIANT_ADD(various_address_sockets, pathname_socket_no_domain) {
650+
/* clang-format on */
651+
.domain = NO_SANDBOX,
652+
};
653+
654+
FIXTURE_SETUP(various_address_sockets)
655+
{
656+
drop_caps(_metadata);
657+
658+
umask(0077);
659+
ASSERT_EQ(0, mkdir(TMP_DIR, 0700));
660+
}
661+
662+
FIXTURE_TEARDOWN(various_address_sockets)
663+
{
664+
EXPECT_EQ(0, unlink(stream_path));
665+
EXPECT_EQ(0, unlink(dgram_path));
666+
EXPECT_EQ(0, rmdir(TMP_DIR));
667+
}
668+
669+
TEST_F(various_address_sockets, scoped_pathname_sockets)
670+
{
671+
socklen_t size_stream, size_dgram;
672+
pid_t child;
673+
int status;
674+
char buf_child, buf_parent;
675+
int pipe_parent[2];
676+
int unnamed_sockets[2];
677+
int stream_pathname_socket, dgram_pathname_socket,
678+
stream_abstract_socket, dgram_abstract_socket, data_socket;
679+
struct service_fixture stream_abstract_addr, dgram_abstract_addr;
680+
struct sockaddr_un stream_pathname_addr = {
681+
.sun_family = AF_UNIX,
682+
};
683+
struct sockaddr_un dgram_pathname_addr = {
684+
.sun_family = AF_UNIX,
685+
};
686+
687+
/* Pathname address. */
688+
snprintf(stream_pathname_addr.sun_path,
689+
sizeof(stream_pathname_addr.sun_path), "%s", stream_path);
690+
size_stream = offsetof(struct sockaddr_un, sun_path) +
691+
strlen(stream_pathname_addr.sun_path);
692+
snprintf(dgram_pathname_addr.sun_path,
693+
sizeof(dgram_pathname_addr.sun_path), "%s", dgram_path);
694+
size_dgram = offsetof(struct sockaddr_un, sun_path) +
695+
strlen(dgram_pathname_addr.sun_path);
696+
697+
/* Abstract address. */
698+
memset(&stream_abstract_addr, 0, sizeof(stream_abstract_addr));
699+
set_unix_address(&stream_abstract_addr, 0);
700+
memset(&dgram_abstract_addr, 0, sizeof(dgram_abstract_addr));
701+
set_unix_address(&dgram_abstract_addr, 1);
702+
703+
/* Unnamed address for datagram socket. */
704+
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_DGRAM, 0, unnamed_sockets));
705+
706+
ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
707+
708+
child = fork();
709+
ASSERT_LE(0, child);
710+
if (child == 0) {
711+
int err;
712+
713+
EXPECT_EQ(0, close(pipe_parent[1]));
714+
EXPECT_EQ(0, close(unnamed_sockets[1]));
715+
716+
if (variant->domain == SCOPE_SANDBOX)
717+
create_scoped_domain(
718+
_metadata, LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET);
719+
else if (variant->domain == OTHER_SANDBOX)
720+
create_fs_domain(_metadata);
721+
722+
/* Waits for parent to listen. */
723+
ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
724+
EXPECT_EQ(0, close(pipe_parent[0]));
725+
726+
/* Checks that we can send data through a datagram socket. */
727+
ASSERT_EQ(1, write(unnamed_sockets[0], "a", 1));
728+
EXPECT_EQ(0, close(unnamed_sockets[0]));
729+
730+
/* Connects with pathname sockets. */
731+
stream_pathname_socket = socket(AF_UNIX, SOCK_STREAM, 0);
732+
ASSERT_LE(0, stream_pathname_socket);
733+
ASSERT_EQ(0, connect(stream_pathname_socket,
734+
&stream_pathname_addr, size_stream));
735+
ASSERT_EQ(1, write(stream_pathname_socket, "b", 1));
736+
EXPECT_EQ(0, close(stream_pathname_socket));
737+
738+
/* Sends without connection. */
739+
dgram_pathname_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
740+
ASSERT_LE(0, dgram_pathname_socket);
741+
err = sendto(dgram_pathname_socket, "c", 1, 0,
742+
&dgram_pathname_addr, size_dgram);
743+
EXPECT_EQ(1, err);
744+
745+
/* Sends with connection. */
746+
ASSERT_EQ(0, connect(dgram_pathname_socket,
747+
&dgram_pathname_addr, size_dgram));
748+
ASSERT_EQ(1, write(dgram_pathname_socket, "d", 1));
749+
EXPECT_EQ(0, close(dgram_pathname_socket));
750+
751+
/* Connects with abstract sockets. */
752+
stream_abstract_socket = socket(AF_UNIX, SOCK_STREAM, 0);
753+
ASSERT_LE(0, stream_abstract_socket);
754+
err = connect(stream_abstract_socket,
755+
&stream_abstract_addr.unix_addr,
756+
stream_abstract_addr.unix_addr_len);
757+
if (variant->domain == SCOPE_SANDBOX) {
758+
EXPECT_EQ(-1, err);
759+
EXPECT_EQ(EPERM, errno);
760+
} else {
761+
EXPECT_EQ(0, err);
762+
ASSERT_EQ(1, write(stream_abstract_socket, "e", 1));
763+
}
764+
EXPECT_EQ(0, close(stream_abstract_socket));
765+
766+
/* Sends without connection. */
767+
dgram_abstract_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
768+
ASSERT_LE(0, dgram_abstract_socket);
769+
err = sendto(dgram_abstract_socket, "f", 1, 0,
770+
&dgram_abstract_addr.unix_addr,
771+
dgram_abstract_addr.unix_addr_len);
772+
if (variant->domain == SCOPE_SANDBOX) {
773+
EXPECT_EQ(-1, err);
774+
EXPECT_EQ(EPERM, errno);
775+
} else {
776+
EXPECT_EQ(1, err);
777+
}
778+
779+
/* Sends with connection. */
780+
err = connect(dgram_abstract_socket,
781+
&dgram_abstract_addr.unix_addr,
782+
dgram_abstract_addr.unix_addr_len);
783+
if (variant->domain == SCOPE_SANDBOX) {
784+
EXPECT_EQ(-1, err);
785+
EXPECT_EQ(EPERM, errno);
786+
} else {
787+
EXPECT_EQ(0, err);
788+
ASSERT_EQ(1, write(dgram_abstract_socket, "g", 1));
789+
}
790+
EXPECT_EQ(0, close(dgram_abstract_socket));
791+
792+
_exit(_metadata->exit_code);
793+
return;
794+
}
795+
EXPECT_EQ(0, close(pipe_parent[0]));
796+
EXPECT_EQ(0, close(unnamed_sockets[0]));
797+
798+
/* Sets up pathname servers. */
799+
stream_pathname_socket = socket(AF_UNIX, SOCK_STREAM, 0);
800+
ASSERT_LE(0, stream_pathname_socket);
801+
ASSERT_EQ(0, bind(stream_pathname_socket, &stream_pathname_addr,
802+
size_stream));
803+
ASSERT_EQ(0, listen(stream_pathname_socket, backlog));
804+
805+
dgram_pathname_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
806+
ASSERT_LE(0, dgram_pathname_socket);
807+
ASSERT_EQ(0, bind(dgram_pathname_socket, &dgram_pathname_addr,
808+
size_dgram));
809+
810+
/* Sets up abstract servers. */
811+
stream_abstract_socket = socket(AF_UNIX, SOCK_STREAM, 0);
812+
ASSERT_LE(0, stream_abstract_socket);
813+
ASSERT_EQ(0,
814+
bind(stream_abstract_socket, &stream_abstract_addr.unix_addr,
815+
stream_abstract_addr.unix_addr_len));
816+
817+
dgram_abstract_socket = socket(AF_UNIX, SOCK_DGRAM, 0);
818+
ASSERT_LE(0, dgram_abstract_socket);
819+
ASSERT_EQ(0, bind(dgram_abstract_socket, &dgram_abstract_addr.unix_addr,
820+
dgram_abstract_addr.unix_addr_len));
821+
ASSERT_EQ(0, listen(stream_abstract_socket, backlog));
822+
823+
ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
824+
EXPECT_EQ(0, close(pipe_parent[1]));
825+
826+
/* Reads from unnamed socket. */
827+
ASSERT_EQ(1, read(unnamed_sockets[1], &buf_parent, sizeof(buf_parent)));
828+
ASSERT_EQ('a', buf_parent);
829+
EXPECT_LE(0, close(unnamed_sockets[1]));
830+
831+
/* Reads from pathname sockets. */
832+
data_socket = accept(stream_pathname_socket, NULL, NULL);
833+
ASSERT_LE(0, data_socket);
834+
ASSERT_EQ(1, read(data_socket, &buf_parent, sizeof(buf_parent)));
835+
ASSERT_EQ('b', buf_parent);
836+
EXPECT_EQ(0, close(data_socket));
837+
EXPECT_EQ(0, close(stream_pathname_socket));
838+
839+
ASSERT_EQ(1,
840+
read(dgram_pathname_socket, &buf_parent, sizeof(buf_parent)));
841+
ASSERT_EQ('c', buf_parent);
842+
ASSERT_EQ(1,
843+
read(dgram_pathname_socket, &buf_parent, sizeof(buf_parent)));
844+
ASSERT_EQ('d', buf_parent);
845+
EXPECT_EQ(0, close(dgram_pathname_socket));
846+
847+
if (variant->domain != SCOPE_SANDBOX) {
848+
/* Reads from abstract sockets if allowed to send. */
849+
data_socket = accept(stream_abstract_socket, NULL, NULL);
850+
ASSERT_LE(0, data_socket);
851+
ASSERT_EQ(1,
852+
read(data_socket, &buf_parent, sizeof(buf_parent)));
853+
ASSERT_EQ('e', buf_parent);
854+
EXPECT_EQ(0, close(data_socket));
855+
856+
ASSERT_EQ(1, read(dgram_abstract_socket, &buf_parent,
857+
sizeof(buf_parent)));
858+
ASSERT_EQ('f', buf_parent);
859+
ASSERT_EQ(1, read(dgram_abstract_socket, &buf_parent,
860+
sizeof(buf_parent)));
861+
ASSERT_EQ('g', buf_parent);
862+
}
863+
864+
/* Waits for all abstract socket tests. */
865+
ASSERT_EQ(child, waitpid(child, &status, 0));
866+
EXPECT_EQ(0, close(stream_abstract_socket));
867+
EXPECT_EQ(0, close(dgram_abstract_socket));
868+
869+
if (WIFSIGNALED(status) || !WIFEXITED(status) ||
870+
WEXITSTATUS(status) != EXIT_SUCCESS)
871+
_metadata->exit_code = KSFT_FAIL;
872+
}
873+
624874
TEST_HARNESS_MAIN

0 commit comments

Comments
 (0)