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 CAP_DAC_OVERRIDE, 116 CAP_MKNOD, 117 CAP_SYS_ADMIN, 118 CAP_SYS_CHROOT, 119 }; 120 121 cap_p = cap_get_proc(); 122 EXPECT_NE(NULL, cap_p) 123 { 124 TH_LOG("Failed to cap_get_proc: %s", strerror(errno)); 125 } 126 EXPECT_NE(-1, cap_clear(cap_p)) 127 { 128 TH_LOG("Failed to cap_clear: %s", strerror(errno)); 129 } 130 if (!drop_all) { 131 EXPECT_NE(-1, cap_set_flag(cap_p, CAP_PERMITTED, 132 ARRAY_SIZE(caps), caps, CAP_SET)) 133 { 134 TH_LOG("Failed to cap_set_flag: %s", strerror(errno)); 135 } 136 } 137 EXPECT_NE(-1, cap_set_proc(cap_p)) 138 { 139 TH_LOG("Failed to cap_set_proc: %s", strerror(errno)); 140 } 141 EXPECT_NE(-1, cap_free(cap_p)) 142 { 143 TH_LOG("Failed to cap_free: %s", strerror(errno)); 144 } 145 } 146 147 /* We cannot put such helpers in a library because of kselftest_harness.h . */ 148 static void __maybe_unused disable_caps(struct __test_metadata *const _metadata) 149 { 150 _init_caps(_metadata, false); 151 } 152 153 static void __maybe_unused drop_caps(struct __test_metadata *const _metadata) 154 { 155 _init_caps(_metadata, true); 156 } 157 158 static void _effective_cap(struct __test_metadata *const _metadata, 159 const cap_value_t caps, const cap_flag_value_t value) 160 { 161 cap_t cap_p; 162 163 cap_p = cap_get_proc(); 164 EXPECT_NE(NULL, cap_p) 165 { 166 TH_LOG("Failed to cap_get_proc: %s", strerror(errno)); 167 } 168 EXPECT_NE(-1, cap_set_flag(cap_p, CAP_EFFECTIVE, 1, &caps, value)) 169 { 170 TH_LOG("Failed to cap_set_flag: %s", strerror(errno)); 171 } 172 EXPECT_NE(-1, cap_set_proc(cap_p)) 173 { 174 TH_LOG("Failed to cap_set_proc: %s", strerror(errno)); 175 } 176 EXPECT_NE(-1, cap_free(cap_p)) 177 { 178 TH_LOG("Failed to cap_free: %s", strerror(errno)); 179 } 180 } 181 182 static void __maybe_unused set_cap(struct __test_metadata *const _metadata, 183 const cap_value_t caps) 184 { 185 _effective_cap(_metadata, caps, CAP_SET); 186 } 187 188 static void __maybe_unused clear_cap(struct __test_metadata *const _metadata, 189 const cap_value_t caps) 190 { 191 _effective_cap(_metadata, caps, CAP_CLEAR); 192 } 193 194 /* Receives an FD from a UNIX socket. Returns the received FD, or -errno. */ 195 static int __maybe_unused recv_fd(int usock) 196 { 197 int fd_rx; 198 union { 199 /* Aligned ancillary data buffer. */ 200 char buf[CMSG_SPACE(sizeof(fd_rx))]; 201 struct cmsghdr _align; 202 } cmsg_rx = {}; 203 char data = '\0'; 204 struct iovec io = { 205 .iov_base = &data, 206 .iov_len = sizeof(data), 207 }; 208 struct msghdr msg = { 209 .msg_iov = &io, 210 .msg_iovlen = 1, 211 .msg_control = &cmsg_rx.buf, 212 .msg_controllen = sizeof(cmsg_rx.buf), 213 }; 214 struct cmsghdr *cmsg; 215 int res; 216 217 res = recvmsg(usock, &msg, MSG_CMSG_CLOEXEC); 218 if (res < 0) 219 return -errno; 220 221 cmsg = CMSG_FIRSTHDR(&msg); 222 if (cmsg->cmsg_len != CMSG_LEN(sizeof(fd_rx))) 223 return -EIO; 224 225 memcpy(&fd_rx, CMSG_DATA(cmsg), sizeof(fd_rx)); 226 return fd_rx; 227 } 228 229 /* Sends an FD on a UNIX socket. Returns 0 on success or -errno. */ 230 static int __maybe_unused send_fd(int usock, int fd_tx) 231 { 232 union { 233 /* Aligned ancillary data buffer. */ 234 char buf[CMSG_SPACE(sizeof(fd_tx))]; 235 struct cmsghdr _align; 236 } cmsg_tx = {}; 237 char data_tx = '.'; 238 struct iovec io = { 239 .iov_base = &data_tx, 240 .iov_len = sizeof(data_tx), 241 }; 242 struct msghdr msg = { 243 .msg_iov = &io, 244 .msg_iovlen = 1, 245 .msg_control = &cmsg_tx.buf, 246 .msg_controllen = sizeof(cmsg_tx.buf), 247 }; 248 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 249 250 cmsg->cmsg_len = CMSG_LEN(sizeof(fd_tx)); 251 cmsg->cmsg_level = SOL_SOCKET; 252 cmsg->cmsg_type = SCM_RIGHTS; 253 memcpy(CMSG_DATA(cmsg), &fd_tx, sizeof(fd_tx)); 254 255 if (sendmsg(usock, &msg, 0) < 0) 256 return -errno; 257 return 0; 258 } 259