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 <arpa/inet.h> 11 #include <errno.h> 12 #include <linux/nsfs.h> 13 #include <linux/securebits.h> 14 #include <sys/capability.h> 15 #include <sys/prctl.h> 16 #include <sys/socket.h> 17 #include <sys/stat.h> 18 #include <sys/un.h> 19 #include <sys/wait.h> 20 #include <unistd.h> 21 22 #include "kselftest_harness.h" 23 #include "wrappers.h" 24 25 #define TMP_DIR "tmp" 26 27 /* TEST_F_FORK() should not be used for new tests. */ 28 #define TEST_F_FORK(fixture_name, test_name) TEST_F(fixture_name, test_name) 29 30 #define LANDLOCK_MAX_NUM_LAYERS 16 31 32 static const char bin_sandbox_and_launch[] = "./sandbox-and-launch"; 33 static const char bin_wait_pipe[] = "./wait-pipe"; 34 static const char bin_wait_pipe_sandbox[] = "./wait-pipe-sandbox"; 35 36 static void _init_caps(struct __test_metadata *const _metadata, bool drop_all) 37 { 38 cap_t cap_p; 39 /* Only these three capabilities are useful for the tests. */ 40 const cap_value_t caps[] = { 41 /* clang-format off */ 42 CAP_AUDIT_CONTROL, 43 CAP_DAC_OVERRIDE, 44 CAP_MKNOD, 45 CAP_NET_ADMIN, 46 CAP_NET_BIND_SERVICE, 47 CAP_SETUID, 48 CAP_SYS_ADMIN, 49 CAP_SYS_CHROOT, 50 /* clang-format on */ 51 }; 52 const unsigned int noroot = SECBIT_NOROOT | SECBIT_NOROOT_LOCKED; 53 54 if ((cap_get_secbits() & noroot) != noroot) 55 EXPECT_EQ(0, cap_set_secbits(noroot)); 56 57 cap_p = cap_get_proc(); 58 EXPECT_NE(NULL, cap_p); 59 EXPECT_NE(-1, cap_clear(cap_p)); 60 if (!drop_all) { 61 EXPECT_NE(-1, cap_set_flag(cap_p, CAP_PERMITTED, 62 ARRAY_SIZE(caps), caps, CAP_SET)); 63 } 64 65 /* Automatically resets ambient capabilities. */ 66 EXPECT_NE(-1, cap_set_proc(cap_p)) 67 { 68 TH_LOG("Failed to set capabilities: %s", strerror(errno)); 69 } 70 EXPECT_NE(-1, cap_free(cap_p)); 71 72 /* Quickly checks that ambient capabilities are cleared. */ 73 EXPECT_NE(-1, cap_get_ambient(caps[0])); 74 } 75 76 /* We cannot put such helpers in a library because of kselftest_harness.h . */ 77 static void __maybe_unused disable_caps(struct __test_metadata *const _metadata) 78 { 79 _init_caps(_metadata, false); 80 } 81 82 static void __maybe_unused drop_caps(struct __test_metadata *const _metadata) 83 { 84 _init_caps(_metadata, true); 85 } 86 87 static void _change_cap(struct __test_metadata *const _metadata, 88 const cap_flag_t flag, const cap_value_t cap, 89 const cap_flag_value_t value) 90 { 91 cap_t cap_p; 92 93 cap_p = cap_get_proc(); 94 EXPECT_NE(NULL, cap_p); 95 EXPECT_NE(-1, cap_set_flag(cap_p, flag, 1, &cap, value)); 96 EXPECT_NE(-1, cap_set_proc(cap_p)) 97 { 98 TH_LOG("Failed to set capability %d: %s", cap, strerror(errno)); 99 } 100 EXPECT_NE(-1, cap_free(cap_p)); 101 } 102 103 static void __maybe_unused set_cap(struct __test_metadata *const _metadata, 104 const cap_value_t cap) 105 { 106 _change_cap(_metadata, CAP_EFFECTIVE, cap, CAP_SET); 107 } 108 109 static void __maybe_unused clear_cap(struct __test_metadata *const _metadata, 110 const cap_value_t cap) 111 { 112 _change_cap(_metadata, CAP_EFFECTIVE, cap, CAP_CLEAR); 113 } 114 115 static void __maybe_unused 116 set_ambient_cap(struct __test_metadata *const _metadata, const cap_value_t cap) 117 { 118 _change_cap(_metadata, CAP_INHERITABLE, cap, CAP_SET); 119 120 EXPECT_NE(-1, cap_set_ambient(cap, CAP_SET)) 121 { 122 TH_LOG("Failed to set ambient capability %d: %s", cap, 123 strerror(errno)); 124 } 125 } 126 127 static void __maybe_unused clear_ambient_cap( 128 struct __test_metadata *const _metadata, const cap_value_t cap) 129 { 130 EXPECT_EQ(1, cap_get_ambient(cap)); 131 _change_cap(_metadata, CAP_INHERITABLE, cap, CAP_CLEAR); 132 EXPECT_EQ(0, cap_get_ambient(cap)); 133 } 134 135 /* Returns true if the current process is in the initial user namespace. */ 136 static bool __maybe_unused is_in_init_user_ns(void) 137 { 138 struct stat user_ns; 139 140 if (stat("/proc/self/ns/user", &user_ns)) 141 return false; 142 143 return user_ns.st_ino == USER_NS_INIT_INO; 144 } 145 146 /* Receives an FD from a UNIX socket. Returns the received FD, or -errno. */ 147 static int __maybe_unused recv_fd(int usock) 148 { 149 int fd_rx; 150 union { 151 /* Aligned ancillary data buffer. */ 152 char buf[CMSG_SPACE(sizeof(fd_rx))]; 153 struct cmsghdr _align; 154 } cmsg_rx = {}; 155 char data = '\0'; 156 struct iovec io = { 157 .iov_base = &data, 158 .iov_len = sizeof(data), 159 }; 160 struct msghdr msg = { 161 .msg_iov = &io, 162 .msg_iovlen = 1, 163 .msg_control = &cmsg_rx.buf, 164 .msg_controllen = sizeof(cmsg_rx.buf), 165 }; 166 struct cmsghdr *cmsg; 167 int res; 168 169 res = recvmsg(usock, &msg, MSG_CMSG_CLOEXEC); 170 if (res < 0) 171 return -errno; 172 173 cmsg = CMSG_FIRSTHDR(&msg); 174 if (cmsg->cmsg_len != CMSG_LEN(sizeof(fd_rx))) 175 return -EIO; 176 177 memcpy(&fd_rx, CMSG_DATA(cmsg), sizeof(fd_rx)); 178 return fd_rx; 179 } 180 181 /* Sends an FD on a UNIX socket. Returns 0 on success or -errno. */ 182 static int __maybe_unused send_fd(int usock, int fd_tx) 183 { 184 union { 185 /* Aligned ancillary data buffer. */ 186 char buf[CMSG_SPACE(sizeof(fd_tx))]; 187 struct cmsghdr _align; 188 } cmsg_tx = {}; 189 char data_tx = '.'; 190 struct iovec io = { 191 .iov_base = &data_tx, 192 .iov_len = sizeof(data_tx), 193 }; 194 struct msghdr msg = { 195 .msg_iov = &io, 196 .msg_iovlen = 1, 197 .msg_control = &cmsg_tx.buf, 198 .msg_controllen = sizeof(cmsg_tx.buf), 199 }; 200 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); 201 202 cmsg->cmsg_len = CMSG_LEN(sizeof(fd_tx)); 203 cmsg->cmsg_level = SOL_SOCKET; 204 cmsg->cmsg_type = SCM_RIGHTS; 205 memcpy(CMSG_DATA(cmsg), &fd_tx, sizeof(fd_tx)); 206 207 if (sendmsg(usock, &msg, 0) < 0) 208 return -errno; 209 return 0; 210 } 211 212 static void __maybe_unused 213 enforce_ruleset(struct __test_metadata *const _metadata, const int ruleset_fd) 214 { 215 ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)); 216 ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)) 217 { 218 TH_LOG("Failed to enforce ruleset: %s", strerror(errno)); 219 } 220 } 221 222 static void __maybe_unused 223 drop_access_rights(struct __test_metadata *const _metadata, 224 const struct landlock_ruleset_attr *const ruleset_attr) 225 { 226 int ruleset_fd; 227 228 ruleset_fd = 229 landlock_create_ruleset(ruleset_attr, sizeof(*ruleset_attr), 0); 230 EXPECT_LE(0, ruleset_fd) 231 { 232 TH_LOG("Failed to create a ruleset: %s", strerror(errno)); 233 } 234 enforce_ruleset(_metadata, ruleset_fd); 235 EXPECT_EQ(0, close(ruleset_fd)); 236 } 237 238 struct protocol_variant { 239 int domain; 240 int type; 241 int protocol; 242 }; 243 244 struct service_fixture { 245 struct protocol_variant protocol; 246 /* port is also stored in ipv4_addr.sin_port or ipv6_addr.sin6_port */ 247 unsigned short port; 248 union { 249 struct sockaddr_in ipv4_addr; 250 struct sockaddr_in6 ipv6_addr; 251 struct { 252 struct sockaddr_un unix_addr; 253 socklen_t unix_addr_len; 254 }; 255 struct sockaddr_storage _largest; 256 }; 257 }; 258 259 static void __maybe_unused set_unix_address(struct service_fixture *const srv, 260 const unsigned short index) 261 { 262 srv->unix_addr.sun_family = AF_UNIX; 263 sprintf(srv->unix_addr.sun_path, 264 "_selftests-landlock-abstract-unix-tid%d-index%d", sys_gettid(), 265 index); 266 srv->unix_addr_len = SUN_LEN(&srv->unix_addr); 267 srv->unix_addr.sun_path[0] = '\0'; 268 } 269 270 /** 271 * regex_escape - Escape BRE metacharacters in a string 272 * 273 * @src: Source string to escape. 274 * @dst: Destination buffer for the escaped string. 275 * @dst_size: Size of the destination buffer. 276 * 277 * Escapes characters that have special meaning in POSIX Basic Regular 278 * Expressions: $ * . [ \ ] ^ 279 * 280 * Returns a pointer to the NUL terminator in @dst (cursor-style API for 281 * chaining), or (char *)-ENOMEM if the buffer is too small. 282 */ 283 static __maybe_unused char *regex_escape(const char *const src, char *dst, 284 size_t dst_size) 285 { 286 char *d = dst; 287 288 for (const char *s = src; *s; s++) { 289 switch (*s) { 290 case '$': 291 case '*': 292 case '.': 293 case '[': 294 case '\\': 295 case ']': 296 case '^': 297 if (d >= dst + dst_size - 2) 298 return (char *)-ENOMEM; 299 300 *d++ = '\\'; 301 *d++ = *s; 302 break; 303 default: 304 if (d >= dst + dst_size - 1) 305 return (char *)-ENOMEM; 306 307 *d++ = *s; 308 } 309 } 310 if (d >= dst + dst_size - 1) 311 return (char *)-ENOMEM; 312 313 *d = '\0'; 314 return d; 315 } 316