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