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