1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Test execveat(2) with AT_EXECVE_CHECK, and prctl(2) with 4 * SECBIT_EXEC_RESTRICT_FILE, SECBIT_EXEC_DENY_INTERACTIVE, and their locked 5 * counterparts. 6 * 7 * Copyright © 2018-2020 ANSSI 8 * Copyright © 2024 Microsoft Corporation 9 * 10 * Author: Mickaël Salaün <mic@digikod.net> 11 */ 12 13 #include <asm-generic/unistd.h> 14 #include <errno.h> 15 #include <fcntl.h> 16 #include <linux/prctl.h> 17 #include <linux/securebits.h> 18 #include <stdio.h> 19 #include <stdlib.h> 20 #include <sys/capability.h> 21 #include <sys/mount.h> 22 #include <sys/prctl.h> 23 #include <sys/socket.h> 24 #include <sys/stat.h> 25 #include <sys/sysmacros.h> 26 #include <unistd.h> 27 28 /* Defines AT_EXECVE_CHECK without type conflicts. */ 29 #define _ASM_GENERIC_FCNTL_H 30 #include <linux/fcntl.h> 31 32 #include "../kselftest_harness.h" 33 34 static void drop_privileges(struct __test_metadata *const _metadata) 35 { 36 const unsigned int noroot = SECBIT_NOROOT | SECBIT_NOROOT_LOCKED; 37 cap_t cap_p; 38 39 if ((cap_get_secbits() & noroot) != noroot) 40 EXPECT_EQ(0, cap_set_secbits(noroot)); 41 42 cap_p = cap_get_proc(); 43 EXPECT_NE(NULL, cap_p); 44 EXPECT_NE(-1, cap_clear(cap_p)); 45 46 /* 47 * Drops everything, especially CAP_SETPCAP, CAP_DAC_OVERRIDE, and 48 * CAP_DAC_READ_SEARCH. 49 */ 50 EXPECT_NE(-1, cap_set_proc(cap_p)); 51 EXPECT_NE(-1, cap_free(cap_p)); 52 } 53 54 static int test_secbits_set(const unsigned int secbits) 55 { 56 int err; 57 58 err = prctl(PR_SET_SECUREBITS, secbits); 59 if (err) 60 return errno; 61 return 0; 62 } 63 64 FIXTURE(access) 65 { 66 int memfd, pipefd; 67 int pipe_fds[2], socket_fds[2]; 68 }; 69 70 FIXTURE_VARIANT(access) 71 { 72 const bool mount_exec; 73 const bool file_exec; 74 }; 75 76 /* clang-format off */ 77 FIXTURE_VARIANT_ADD(access, mount_exec_file_exec) { 78 /* clang-format on */ 79 .mount_exec = true, 80 .file_exec = true, 81 }; 82 83 /* clang-format off */ 84 FIXTURE_VARIANT_ADD(access, mount_exec_file_noexec) { 85 /* clang-format on */ 86 .mount_exec = true, 87 .file_exec = false, 88 }; 89 90 /* clang-format off */ 91 FIXTURE_VARIANT_ADD(access, mount_noexec_file_exec) { 92 /* clang-format on */ 93 .mount_exec = false, 94 .file_exec = true, 95 }; 96 97 /* clang-format off */ 98 FIXTURE_VARIANT_ADD(access, mount_noexec_file_noexec) { 99 /* clang-format on */ 100 .mount_exec = false, 101 .file_exec = false, 102 }; 103 104 static const char binary_path[] = "./false"; 105 static const char workdir_path[] = "./test-mount"; 106 static const char reg_file_path[] = "./test-mount/regular_file"; 107 static const char dir_path[] = "./test-mount/directory"; 108 static const char block_dev_path[] = "./test-mount/block_device"; 109 static const char char_dev_path[] = "./test-mount/character_device"; 110 static const char fifo_path[] = "./test-mount/fifo"; 111 112 FIXTURE_SETUP(access) 113 { 114 int procfd_path_size; 115 static const char path_template[] = "/proc/self/fd/%d"; 116 char procfd_path[sizeof(path_template) + 10]; 117 118 /* Makes sure we are not already restricted nor locked. */ 119 EXPECT_EQ(0, test_secbits_set(0)); 120 121 /* 122 * Cleans previous workspace if any error previously happened (don't 123 * check errors). 124 */ 125 umount(workdir_path); 126 rmdir(workdir_path); 127 128 /* Creates a clean mount point. */ 129 ASSERT_EQ(0, mkdir(workdir_path, 00700)); 130 ASSERT_EQ(0, mount("test", workdir_path, "tmpfs", 131 MS_MGC_VAL | (variant->mount_exec ? 0 : MS_NOEXEC), 132 "mode=0700,size=9m")); 133 134 /* Creates a regular file. */ 135 ASSERT_EQ(0, mknod(reg_file_path, 136 S_IFREG | (variant->file_exec ? 0700 : 0600), 0)); 137 /* Creates a directory. */ 138 ASSERT_EQ(0, mkdir(dir_path, variant->file_exec ? 0700 : 0600)); 139 /* Creates a character device: /dev/null. */ 140 ASSERT_EQ(0, mknod(char_dev_path, S_IFCHR | 0400, makedev(1, 3))); 141 /* Creates a block device: /dev/loop0 */ 142 ASSERT_EQ(0, mknod(block_dev_path, S_IFBLK | 0400, makedev(7, 0))); 143 /* Creates a fifo. */ 144 ASSERT_EQ(0, mknod(fifo_path, S_IFIFO | 0600, 0)); 145 146 /* Creates a regular file without user mount point. */ 147 self->memfd = memfd_create("test-exec-probe", MFD_CLOEXEC); 148 ASSERT_LE(0, self->memfd); 149 /* Sets mode, which must be ignored by the exec check. */ 150 ASSERT_EQ(0, fchmod(self->memfd, variant->file_exec ? 0700 : 0600)); 151 152 /* Creates a pipefs file descriptor. */ 153 ASSERT_EQ(0, pipe(self->pipe_fds)); 154 procfd_path_size = snprintf(procfd_path, sizeof(procfd_path), 155 path_template, self->pipe_fds[0]); 156 ASSERT_LT(procfd_path_size, sizeof(procfd_path)); 157 self->pipefd = open(procfd_path, O_RDWR | O_CLOEXEC); 158 ASSERT_LE(0, self->pipefd); 159 ASSERT_EQ(0, fchmod(self->pipefd, variant->file_exec ? 0700 : 0600)); 160 161 /* Creates a socket file descriptor. */ 162 ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0, 163 self->socket_fds)); 164 } 165 166 FIXTURE_TEARDOWN_PARENT(access) 167 { 168 /* There is no need to unlink the test files. */ 169 EXPECT_EQ(0, umount(workdir_path)); 170 EXPECT_EQ(0, rmdir(workdir_path)); 171 } 172 173 static void fill_exec_fd(struct __test_metadata *_metadata, const int fd_out) 174 { 175 char buf[1024]; 176 size_t len; 177 int fd_in; 178 179 fd_in = open(binary_path, O_CLOEXEC | O_RDONLY); 180 ASSERT_LE(0, fd_in); 181 /* Cannot use copy_file_range(2) because of EXDEV. */ 182 len = read(fd_in, buf, sizeof(buf)); 183 EXPECT_LE(0, len); 184 while (len > 0) { 185 EXPECT_EQ(len, write(fd_out, buf, len)) 186 { 187 TH_LOG("Failed to write: %s (%d)", strerror(errno), 188 errno); 189 } 190 len = read(fd_in, buf, sizeof(buf)); 191 EXPECT_LE(0, len); 192 } 193 EXPECT_EQ(0, close(fd_in)); 194 } 195 196 static void fill_exec_path(struct __test_metadata *_metadata, 197 const char *const path) 198 { 199 int fd_out; 200 201 fd_out = open(path, O_CLOEXEC | O_WRONLY); 202 ASSERT_LE(0, fd_out) 203 { 204 TH_LOG("Failed to open %s: %s", path, strerror(errno)); 205 } 206 fill_exec_fd(_metadata, fd_out); 207 EXPECT_EQ(0, close(fd_out)); 208 } 209 210 static void test_exec_fd(struct __test_metadata *_metadata, const int fd, 211 const int err_code) 212 { 213 char *const argv[] = { "", NULL }; 214 int access_ret, access_errno; 215 216 /* 217 * If we really execute fd, filled with the "false" binary, the current 218 * thread will exits with an error, which will be interpreted by the 219 * test framework as an error. With AT_EXECVE_CHECK, we only check a 220 * potential successful execution. 221 */ 222 access_ret = 223 execveat(fd, "", argv, NULL, AT_EMPTY_PATH | AT_EXECVE_CHECK); 224 access_errno = errno; 225 if (err_code) { 226 EXPECT_EQ(-1, access_ret); 227 EXPECT_EQ(err_code, access_errno) 228 { 229 TH_LOG("Wrong error for execveat(2): %s (%d)", 230 strerror(access_errno), errno); 231 } 232 } else { 233 EXPECT_EQ(0, access_ret) 234 { 235 TH_LOG("Access denied: %s", strerror(access_errno)); 236 } 237 } 238 } 239 240 static void test_exec_path(struct __test_metadata *_metadata, 241 const char *const path, const int err_code) 242 { 243 int flags = O_CLOEXEC; 244 int fd; 245 246 /* Do not block on pipes. */ 247 if (path == fifo_path) 248 flags |= O_NONBLOCK; 249 250 fd = open(path, flags | O_RDONLY); 251 ASSERT_LE(0, fd) 252 { 253 TH_LOG("Failed to open %s: %s", path, strerror(errno)); 254 } 255 test_exec_fd(_metadata, fd, err_code); 256 EXPECT_EQ(0, close(fd)); 257 } 258 259 /* Tests that we don't get ENOEXEC. */ 260 TEST_F(access, regular_file_empty) 261 { 262 const int exec = variant->mount_exec && variant->file_exec; 263 264 test_exec_path(_metadata, reg_file_path, exec ? 0 : EACCES); 265 266 drop_privileges(_metadata); 267 test_exec_path(_metadata, reg_file_path, exec ? 0 : EACCES); 268 } 269 270 TEST_F(access, regular_file_elf) 271 { 272 const int exec = variant->mount_exec && variant->file_exec; 273 274 fill_exec_path(_metadata, reg_file_path); 275 276 test_exec_path(_metadata, reg_file_path, exec ? 0 : EACCES); 277 278 drop_privileges(_metadata); 279 test_exec_path(_metadata, reg_file_path, exec ? 0 : EACCES); 280 } 281 282 /* Tests that we don't get ENOEXEC. */ 283 TEST_F(access, memfd_empty) 284 { 285 const int exec = variant->file_exec; 286 287 test_exec_fd(_metadata, self->memfd, exec ? 0 : EACCES); 288 289 drop_privileges(_metadata); 290 test_exec_fd(_metadata, self->memfd, exec ? 0 : EACCES); 291 } 292 293 TEST_F(access, memfd_elf) 294 { 295 const int exec = variant->file_exec; 296 297 fill_exec_fd(_metadata, self->memfd); 298 299 test_exec_fd(_metadata, self->memfd, exec ? 0 : EACCES); 300 301 drop_privileges(_metadata); 302 test_exec_fd(_metadata, self->memfd, exec ? 0 : EACCES); 303 } 304 305 TEST_F(access, non_regular_files) 306 { 307 test_exec_path(_metadata, dir_path, EACCES); 308 test_exec_path(_metadata, block_dev_path, EACCES); 309 test_exec_path(_metadata, char_dev_path, EACCES); 310 test_exec_path(_metadata, fifo_path, EACCES); 311 test_exec_fd(_metadata, self->socket_fds[0], EACCES); 312 test_exec_fd(_metadata, self->pipefd, EACCES); 313 } 314 315 /* clang-format off */ 316 FIXTURE(secbits) {}; 317 /* clang-format on */ 318 319 FIXTURE_VARIANT(secbits) 320 { 321 const bool is_privileged; 322 const int error; 323 }; 324 325 /* clang-format off */ 326 FIXTURE_VARIANT_ADD(secbits, priv) { 327 /* clang-format on */ 328 .is_privileged = true, 329 .error = 0, 330 }; 331 332 /* clang-format off */ 333 FIXTURE_VARIANT_ADD(secbits, unpriv) { 334 /* clang-format on */ 335 .is_privileged = false, 336 .error = EPERM, 337 }; 338 339 FIXTURE_SETUP(secbits) 340 { 341 /* Makes sure no exec bits are set. */ 342 EXPECT_EQ(0, test_secbits_set(0)); 343 EXPECT_EQ(0, prctl(PR_GET_SECUREBITS)); 344 345 if (!variant->is_privileged) 346 drop_privileges(_metadata); 347 } 348 349 FIXTURE_TEARDOWN(secbits) 350 { 351 } 352 353 TEST_F(secbits, legacy) 354 { 355 EXPECT_EQ(variant->error, test_secbits_set(0)); 356 } 357 358 #define CHILD(...) \ 359 do { \ 360 pid_t child = vfork(); \ 361 EXPECT_LE(0, child); \ 362 if (child == 0) { \ 363 __VA_ARGS__; \ 364 _exit(0); \ 365 } \ 366 } while (0) 367 368 TEST_F(secbits, exec) 369 { 370 unsigned int secbits = prctl(PR_GET_SECUREBITS); 371 372 secbits |= SECBIT_EXEC_RESTRICT_FILE; 373 EXPECT_EQ(0, test_secbits_set(secbits)); 374 EXPECT_EQ(secbits, prctl(PR_GET_SECUREBITS)); 375 CHILD(EXPECT_EQ(secbits, prctl(PR_GET_SECUREBITS))); 376 377 secbits |= SECBIT_EXEC_DENY_INTERACTIVE; 378 EXPECT_EQ(0, test_secbits_set(secbits)); 379 EXPECT_EQ(secbits, prctl(PR_GET_SECUREBITS)); 380 CHILD(EXPECT_EQ(secbits, prctl(PR_GET_SECUREBITS))); 381 382 secbits &= ~(SECBIT_EXEC_RESTRICT_FILE | SECBIT_EXEC_DENY_INTERACTIVE); 383 EXPECT_EQ(0, test_secbits_set(secbits)); 384 EXPECT_EQ(secbits, prctl(PR_GET_SECUREBITS)); 385 CHILD(EXPECT_EQ(secbits, prctl(PR_GET_SECUREBITS))); 386 } 387 388 TEST_F(secbits, check_locked_set) 389 { 390 unsigned int secbits = prctl(PR_GET_SECUREBITS); 391 392 secbits |= SECBIT_EXEC_RESTRICT_FILE; 393 EXPECT_EQ(0, test_secbits_set(secbits)); 394 secbits |= SECBIT_EXEC_RESTRICT_FILE_LOCKED; 395 EXPECT_EQ(0, test_secbits_set(secbits)); 396 397 /* Checks lock set but unchanged. */ 398 EXPECT_EQ(variant->error, test_secbits_set(secbits)); 399 CHILD(EXPECT_EQ(variant->error, test_secbits_set(secbits))); 400 401 secbits &= ~SECBIT_EXEC_RESTRICT_FILE; 402 EXPECT_EQ(EPERM, test_secbits_set(0)); 403 CHILD(EXPECT_EQ(EPERM, test_secbits_set(0))); 404 } 405 406 TEST_F(secbits, check_locked_unset) 407 { 408 unsigned int secbits = prctl(PR_GET_SECUREBITS); 409 410 secbits |= SECBIT_EXEC_RESTRICT_FILE_LOCKED; 411 EXPECT_EQ(0, test_secbits_set(secbits)); 412 413 /* Checks lock unset but unchanged. */ 414 EXPECT_EQ(variant->error, test_secbits_set(secbits)); 415 CHILD(EXPECT_EQ(variant->error, test_secbits_set(secbits))); 416 417 secbits &= ~SECBIT_EXEC_RESTRICT_FILE; 418 EXPECT_EQ(EPERM, test_secbits_set(0)); 419 CHILD(EXPECT_EQ(EPERM, test_secbits_set(0))); 420 } 421 422 TEST_F(secbits, restrict_locked_set) 423 { 424 unsigned int secbits = prctl(PR_GET_SECUREBITS); 425 426 secbits |= SECBIT_EXEC_DENY_INTERACTIVE; 427 EXPECT_EQ(0, test_secbits_set(secbits)); 428 secbits |= SECBIT_EXEC_DENY_INTERACTIVE_LOCKED; 429 EXPECT_EQ(0, test_secbits_set(secbits)); 430 431 /* Checks lock set but unchanged. */ 432 EXPECT_EQ(variant->error, test_secbits_set(secbits)); 433 CHILD(EXPECT_EQ(variant->error, test_secbits_set(secbits))); 434 435 secbits &= ~SECBIT_EXEC_DENY_INTERACTIVE; 436 EXPECT_EQ(EPERM, test_secbits_set(0)); 437 CHILD(EXPECT_EQ(EPERM, test_secbits_set(0))); 438 } 439 440 TEST_F(secbits, restrict_locked_unset) 441 { 442 unsigned int secbits = prctl(PR_GET_SECUREBITS); 443 444 secbits |= SECBIT_EXEC_DENY_INTERACTIVE_LOCKED; 445 EXPECT_EQ(0, test_secbits_set(secbits)); 446 447 /* Checks lock unset but unchanged. */ 448 EXPECT_EQ(variant->error, test_secbits_set(secbits)); 449 CHILD(EXPECT_EQ(variant->error, test_secbits_set(secbits))); 450 451 secbits &= ~SECBIT_EXEC_DENY_INTERACTIVE; 452 EXPECT_EQ(EPERM, test_secbits_set(0)); 453 CHILD(EXPECT_EQ(EPERM, test_secbits_set(0))); 454 } 455 456 TEST_HARNESS_MAIN 457