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