1 /* SPDX-License-Identifier: GPL-2.0 */ 2 /* 3 * Landlock test helpers 4 * 5 * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> 6 * Copyright © 2019-2020 ANSSI 7 * Copyright © 2021 Microsoft Corporation 8 */ 9 10 #include <errno.h> 11 #include <linux/landlock.h> 12 #include <sys/capability.h> 13 #include <sys/socket.h> 14 #include <sys/syscall.h> 15 #include <sys/types.h> 16 #include <sys/wait.h> 17 #include <unistd.h> 18 19 #include "../kselftest_harness.h" 20 21 #ifndef __maybe_unused 22 #define __maybe_unused __attribute__((__unused__)) 23 #endif 24 25 /* 26 * TEST_F_FORK() is useful when a test drop privileges but the corresponding 27 * FIXTURE_TEARDOWN() requires them (e.g. to remove files from a directory 28 * where write actions are denied). For convenience, FIXTURE_TEARDOWN() is 29 * also called when the test failed, but not when FIXTURE_SETUP() failed. For 30 * this to be possible, we must not call abort() but instead exit smoothly 31 * (hence the step print). 32 */ 33 /* clang-format off */ 34 #define TEST_F_FORK(fixture_name, test_name) \ 35 static void fixture_name##_##test_name##_child( \ 36 struct __test_metadata *_metadata, \ 37 FIXTURE_DATA(fixture_name) *self, \ 38 const FIXTURE_VARIANT(fixture_name) *variant); \ 39 TEST_F(fixture_name, test_name) \ 40 { \ 41 int status; \ 42 const pid_t child = fork(); \ 43 if (child < 0) \ 44 abort(); \ 45 if (child == 0) { \ 46 _metadata->no_print = 1; \ 47 fixture_name##_##test_name##_child(_metadata, self, variant); \ 48 if (_metadata->skip) \ 49 _exit(255); \ 50 if (_metadata->passed) \ 51 _exit(0); \ 52 _exit(_metadata->step); \ 53 } \ 54 if (child != waitpid(child, &status, 0)) \ 55 abort(); \ 56 if (WIFSIGNALED(status) || !WIFEXITED(status)) { \ 57 _metadata->passed = 0; \ 58 _metadata->step = 1; \ 59 return; \ 60 } \ 61 switch (WEXITSTATUS(status)) { \ 62 case 0: \ 63 _metadata->passed = 1; \ 64 break; \ 65 case 255: \ 66 _metadata->passed = 1; \ 67 _metadata->skip = 1; \ 68 break; \ 69 default: \ 70 _metadata->passed = 0; \ 71 _metadata->step = WEXITSTATUS(status); \ 72 break; \ 73 } \ 74 } \ 75 static void fixture_name##_##test_name##_child( \ 76 struct __test_metadata __attribute__((unused)) *_metadata, \ 77 FIXTURE_DATA(fixture_name) __attribute__((unused)) *self, \ 78 const FIXTURE_VARIANT(fixture_name) \ 79 __attribute__((unused)) *variant) 80 /* clang-format on */ 81 82 #ifndef landlock_create_ruleset 83 static inline int 84 landlock_create_ruleset(const struct landlock_ruleset_attr *const attr, 85 const size_t size, const __u32 flags) 86 { 87 return syscall(__NR_landlock_create_ruleset, attr, size, flags); 88 } 89 #endif 90 91 #ifndef landlock_add_rule 92 static inline int landlock_add_rule(const int ruleset_fd, 93 const enum landlock_rule_type rule_type, 94 const void *const rule_attr, 95 const __u32 flags) 96 { 97 return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr, 98 flags); 99 } 100 #endif 101 102 #ifndef landlock_restrict_self 103 static inline int landlock_restrict_self(const int ruleset_fd, 104 const __u32 flags) 105 { 106 return syscall(__NR_landlock_restrict_self, ruleset_fd, flags); 107 } 108 #endif 109 110 static void _init_caps(struct __test_metadata *const _metadata, bool drop_all) 111 { 112 cap_t cap_p; 113 /* Only these three capabilities are useful for the tests. */ 114 const cap_value_t caps[] = { 115 /* clang-format off */ 116 CAP_DAC_OVERRIDE, 117 CAP_MKNOD, 118 CAP_SYS_ADMIN, 119 CAP_SYS_CHROOT, 120 CAP_NET_BIND_SERVICE, 121 /* clang-format on */ 122 }; 123 124 cap_p = cap_get_proc(); 125 EXPECT_NE(NULL, cap_p) 126 { 127 TH_LOG("Failed to cap_get_proc: %s", strerror(errno)); 128 } 129 EXPECT_NE(-1, cap_clear(cap_p)) 130 { 131 TH_LOG("Failed to cap_clear: %s", strerror(errno)); 132 } 133 if (!drop_all) { 134 EXPECT_NE(-1, cap_set_flag(cap_p, CAP_PERMITTED, 135 ARRAY_SIZE(caps), caps, CAP_SET)) 136 { 137 TH_LOG("Failed to cap_set_flag: %s", strerror(errno)); 138 } 139 } 140 EXPECT_NE(-1, cap_set_proc(cap_p)) 141 { 142 TH_LOG("Failed to cap_set_proc: %s", strerror(errno)); 143 } 144 EXPECT_NE(-1, cap_free(cap_p)) 145 { 146 TH_LOG("Failed to cap_free: %s", strerror(errno)); 147 } 148 } 149 150 /* We cannot put such helpers in a library because of kselftest_harness.h . */ 151 static void __maybe_unused disable_caps(struct __test_metadata *const _metadata) 152 { 153 _init_caps(_metadata, false); 154 } 155 156 static void __maybe_unused drop_caps(struct __test_metadata *const _metadata) 157 { 158 _init_caps(_metadata, true); 159 } 160 161 static void _effective_cap(struct __test_metadata *const _metadata, 162 const cap_value_t caps, const cap_flag_value_t value) 163 { 164 cap_t cap_p; 165 166 cap_p = cap_get_proc(); 167 EXPECT_NE(NULL, cap_p) 168 { 169 TH_LOG("Failed to cap_get_proc: %s", strerror(errno)); 170 } 171 EXPECT_NE(-1, cap_set_flag(cap_p, CAP_EFFECTIVE, 1, &caps, value)) 172 { 173 TH_LOG("Failed to cap_set_flag: %s", strerror(errno)); 174 } 175 EXPECT_NE(-1, cap_set_proc(cap_p)) 176 { 177 TH_LOG("Failed to cap_set_proc: %s", strerror(errno)); 178 } 179 EXPECT_NE(-1, cap_free(cap_p)) 180 { 181 TH_LOG("Failed to cap_free: %s", strerror(errno)); 182 } 183 } 184 185 static void __maybe_unused set_cap(struct __test_metadata *const _metadata, 186 const cap_value_t caps) 187 { 188 _effective_cap(_metadata, caps, CAP_SET); 189 } 190 191 static void __maybe_unused clear_cap(struct __test_metadata *const _metadata, 192 const cap_value_t caps) 193 { 194 _effective_cap(_metadata, caps, CAP_CLEAR); 195 } 196 197 /* Receives an FD from a UNIX socket. Returns the received FD, or -errno. */ 198 static int __maybe_unused recv_fd(int usock) 199 { 200 int fd_rx; 201 union { 202 /* Aligned ancillary data buffer. */ 203 char buf[CMSG_SPACE(sizeof(fd_rx))]; 204 struct cmsghdr _align; 205 } cmsg_rx = {}; 206 char data = '\0'; 207 struct iovec io = { 208 .iov_base = &data, 209 .iov_len = sizeof(data), 210 }; 211 struct msghdr msg = { 212 .msg_iov = &io, 213 .msg_iovlen = 1, 214 .msg_control = &cmsg_rx.buf, 215 .msg_controllen = sizeof(cmsg_rx.buf), 216 }; 217 struct cmsghdr *cmsg; 218 int res; 219 220 res = recvmsg(usock, &msg, MSG_CMSG_CLOEXEC); 221 if (res < 0) 222 return -errno; 223 224 cmsg = CMSG_FIRSTHDR(&msg); 225 if (cmsg->cmsg_len != CMSG_LEN(sizeof(fd_rx))) 226 return -EIO; 227 228 memcpy(&fd_rx, CMSG_DATA(cmsg), sizeof(fd_rx)); 229 return fd_rx; 230 } 231 232 /* Sends an FD on a UNIX socket. Returns 0 on success or -errno. */ 233 static int __maybe_unused send_fd(int usock, int fd_tx) 234 { 235 union { 236 /* Aligned ancillary data buffer. */ 237 char buf[CMSG_SPACE(sizeof(fd_tx))]; 238 struct cmsghdr _align; 239 } cmsg_tx = {}; 240 char data_tx = '.'; 241 struct iovec io = { 242 .iov_base = &data_tx, 243 .iov_len = sizeof(data_tx), 244 }; 245 struct msghdr msg = { 246 .msg_iov = &io, 247 .msg_iovlen = 1, 248 .msg_control = &cmsg_tx.buf, 249 .msg_controllen = sizeof(cmsg_tx.buf), 250 }; 251 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 252 253 cmsg->cmsg_len = CMSG_LEN(sizeof(fd_tx)); 254 cmsg->cmsg_level = SOL_SOCKET; 255 cmsg->cmsg_type = SCM_RIGHTS; 256 memcpy(CMSG_DATA(cmsg), &fd_tx, sizeof(fd_tx)); 257 258 if (sendmsg(usock, &msg, 0) < 0) 259 return -errno; 260 return 0; 261 } 262 263 static void __maybe_unused 264 enforce_ruleset(struct __test_metadata *const _metadata, const int ruleset_fd) 265 { 266 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 267 ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)) 268 { 269 TH_LOG("Failed to enforce ruleset: %s", strerror(errno)); 270 } 271 } 272