Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
35static 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
41static 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
61static 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
71FIXTURE(access)
72{
73 int memfd, pipefd;
74 int pipe_fds[2], socket_fds[2];
75};
76
77FIXTURE_VARIANT(access)
78{
79 const bool mount_exec;
80 const bool file_exec;
81};
82
83/* clang-format off */
84FIXTURE_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 */
91FIXTURE_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 */
98FIXTURE_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 */
105FIXTURE_VARIANT_ADD(access, mount_noexec_file_noexec) {
106 /* clang-format on */
107 .mount_exec = false,
108 .file_exec = false,
109};
110
111static const char binary_path[] = "./false";
112static const char workdir_path[] = "./test-mount";
113static const char reg_file_path[] = "./test-mount/regular_file";
114static const char dir_path[] = "./test-mount/directory";
115static const char block_dev_path[] = "./test-mount/block_device";
116static const char char_dev_path[] = "./test-mount/character_device";
117static const char fifo_path[] = "./test-mount/fifo";
118
119FIXTURE_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
173FIXTURE_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
180static 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
203static 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
217static 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
247static 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. */
267TEST_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
277TEST_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. */
290TEST_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
300TEST_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
312TEST_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 */
323FIXTURE(secbits) {};
324/* clang-format on */
325
326FIXTURE_VARIANT(secbits)
327{
328 const bool is_privileged;
329 const int error;
330};
331
332/* clang-format off */
333FIXTURE_VARIANT_ADD(secbits, priv) {
334 /* clang-format on */
335 .is_privileged = true,
336 .error = 0,
337};
338
339/* clang-format off */
340FIXTURE_VARIANT_ADD(secbits, unpriv) {
341 /* clang-format on */
342 .is_privileged = false,
343 .error = EPERM,
344};
345
346FIXTURE_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
356FIXTURE_TEARDOWN(secbits)
357{
358}
359
360TEST_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
375TEST_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
395TEST_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
413TEST_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
429TEST_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
447TEST_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
463TEST_HARNESS_MAIN