|
22 | 22 | #include <sys/ioctl.h> |
23 | 23 | #include <sys/mount.h> |
24 | 24 | #include <sys/prctl.h> |
| 25 | +#include <sys/resource.h> |
25 | 26 | #include <sys/sendfile.h> |
26 | 27 | #include <sys/socket.h> |
27 | 28 | #include <sys/stat.h> |
@@ -4928,6 +4929,148 @@ TEST_F(scoped_domains, unix_seqpacket_connect_to_child_full) |
4928 | 4929 | #undef USE_SENDTO |
4929 | 4930 | #undef ENFORCE_ALL |
4930 | 4931 |
|
| 4932 | +static void read_core_pattern(struct __test_metadata *const _metadata, |
| 4933 | + char *buf, size_t buf_size) |
| 4934 | +{ |
| 4935 | + int fd; |
| 4936 | + ssize_t ret; |
| 4937 | + |
| 4938 | + fd = open("/proc/sys/kernel/core_pattern", O_RDONLY | O_CLOEXEC); |
| 4939 | + ASSERT_LE(0, fd); |
| 4940 | + |
| 4941 | + ret = read(fd, buf, buf_size - 1); |
| 4942 | + ASSERT_LE(0, ret); |
| 4943 | + EXPECT_EQ(0, close(fd)); |
| 4944 | + |
| 4945 | + buf[ret] = '\0'; |
| 4946 | +} |
| 4947 | + |
| 4948 | +static void set_core_pattern(struct __test_metadata *const _metadata, |
| 4949 | + const char *pattern) |
| 4950 | +{ |
| 4951 | + int fd; |
| 4952 | + size_t len = strlen(pattern); |
| 4953 | + |
| 4954 | + /* |
| 4955 | + * Writing to /proc/sys/kernel/core_pattern requires EUID 0 because |
| 4956 | + * sysctl_perm() checks that, ignoring capabilities like |
| 4957 | + * CAP_SYS_ADMIN or CAP_DAC_OVERRIDE. |
| 4958 | + * |
| 4959 | + * Switching EUID clears the dumpable flag, which must be restored |
| 4960 | + * afterwards to allow coredumps. |
| 4961 | + */ |
| 4962 | + set_cap(_metadata, CAP_SETUID); |
| 4963 | + ASSERT_EQ(0, seteuid(0)); |
| 4964 | + clear_cap(_metadata, CAP_SETUID); |
| 4965 | + |
| 4966 | + fd = open("/proc/sys/kernel/core_pattern", O_WRONLY | O_CLOEXEC); |
| 4967 | + ASSERT_LE(0, fd) |
| 4968 | + { |
| 4969 | + TH_LOG("Failed to open core_pattern for writing: %s", |
| 4970 | + strerror(errno)); |
| 4971 | + } |
| 4972 | + |
| 4973 | + ASSERT_EQ(len, write(fd, pattern, len)); |
| 4974 | + EXPECT_EQ(0, close(fd)); |
| 4975 | + |
| 4976 | + set_cap(_metadata, CAP_SETUID); |
| 4977 | + ASSERT_EQ(0, seteuid(getuid())); |
| 4978 | + clear_cap(_metadata, CAP_SETUID); |
| 4979 | + |
| 4980 | + /* Restore dumpable flag cleared by seteuid(). */ |
| 4981 | + ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 1, 0, 0, 0)); |
| 4982 | +} |
| 4983 | + |
| 4984 | +FIXTURE(coredump) |
| 4985 | +{ |
| 4986 | + char original_core_pattern[256]; |
| 4987 | +}; |
| 4988 | + |
| 4989 | +FIXTURE_SETUP(coredump) |
| 4990 | +{ |
| 4991 | + disable_caps(_metadata); |
| 4992 | + read_core_pattern(_metadata, self->original_core_pattern, |
| 4993 | + sizeof(self->original_core_pattern)); |
| 4994 | +} |
| 4995 | + |
| 4996 | +FIXTURE_TEARDOWN_PARENT(coredump) |
| 4997 | +{ |
| 4998 | + set_core_pattern(_metadata, self->original_core_pattern); |
| 4999 | +} |
| 5000 | + |
| 5001 | +/* |
| 5002 | + * Test that even when a process is restricted with |
| 5003 | + * LANDLOCK_ACCESS_FS_RESOLVE_UNIX, the kernel can still initiate a connection |
| 5004 | + * to the coredump socket on the processes' behalf. |
| 5005 | + */ |
| 5006 | +TEST_F_FORK(coredump, socket_not_restricted) |
| 5007 | +{ |
| 5008 | + static const char core_pattern[] = "@/tmp/landlock_coredump_test.sock"; |
| 5009 | + const char *const sock_path = core_pattern + 1; |
| 5010 | + int srv_fd, conn_fd, status; |
| 5011 | + pid_t child_pid; |
| 5012 | + struct ucred cred; |
| 5013 | + socklen_t cred_len = sizeof(cred); |
| 5014 | + char buf[4096]; |
| 5015 | + |
| 5016 | + /* Set up the coredump server socket. */ |
| 5017 | + unlink(sock_path); |
| 5018 | + srv_fd = set_up_named_unix_server(_metadata, SOCK_STREAM, sock_path); |
| 5019 | + |
| 5020 | + /* Point coredumps at our socket. */ |
| 5021 | + set_core_pattern(_metadata, core_pattern); |
| 5022 | + |
| 5023 | + /* Restrict LANDLOCK_ACCESS_FS_RESOLVE_UNIX. */ |
| 5024 | + drop_access_rights(_metadata, |
| 5025 | + &(struct landlock_ruleset_attr){ |
| 5026 | + .handled_access_fs = |
| 5027 | + LANDLOCK_ACCESS_FS_RESOLVE_UNIX, |
| 5028 | + }); |
| 5029 | + |
| 5030 | + /* Fork a child that crashes. */ |
| 5031 | + child_pid = fork(); |
| 5032 | + ASSERT_LE(0, child_pid); |
| 5033 | + if (child_pid == 0) { |
| 5034 | + struct rlimit rl = { |
| 5035 | + .rlim_cur = RLIM_INFINITY, |
| 5036 | + .rlim_max = RLIM_INFINITY, |
| 5037 | + }; |
| 5038 | + |
| 5039 | + ASSERT_EQ(0, setrlimit(RLIMIT_CORE, &rl)); |
| 5040 | + |
| 5041 | + /* Crash on purpose. */ |
| 5042 | + kill(getpid(), SIGSEGV); |
| 5043 | + _exit(1); |
| 5044 | + } |
| 5045 | + |
| 5046 | + /* |
| 5047 | + * Accept the coredump connection. If Landlock incorrectly denies the |
| 5048 | + * kernel's coredump connect, accept() will block forever, so the test |
| 5049 | + * would time out. |
| 5050 | + */ |
| 5051 | + conn_fd = accept(srv_fd, NULL, NULL); |
| 5052 | + ASSERT_LE(0, conn_fd); |
| 5053 | + |
| 5054 | + /* Check that the connection came from the crashing child. */ |
| 5055 | + ASSERT_EQ(0, getsockopt(conn_fd, SOL_SOCKET, SO_PEERCRED, &cred, |
| 5056 | + &cred_len)); |
| 5057 | + EXPECT_EQ(child_pid, cred.pid); |
| 5058 | + |
| 5059 | + /* Drain the coredump data so the kernel can finish. */ |
| 5060 | + while (read(conn_fd, buf, sizeof(buf)) > 0) |
| 5061 | + ; |
| 5062 | + |
| 5063 | + EXPECT_EQ(0, close(conn_fd)); |
| 5064 | + |
| 5065 | + /* Wait for the child and verify it coredumped. */ |
| 5066 | + ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); |
| 5067 | + ASSERT_TRUE(WIFSIGNALED(status)); |
| 5068 | + ASSERT_TRUE(WCOREDUMP(status)); |
| 5069 | + |
| 5070 | + EXPECT_EQ(0, close(srv_fd)); |
| 5071 | + EXPECT_EQ(0, unlink(sock_path)); |
| 5072 | +} |
| 5073 | + |
4931 | 5074 | /* clang-format off */ |
4932 | 5075 | FIXTURE(layout1_bind) {}; |
4933 | 5076 | /* clang-format on */ |
|
0 commit comments