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 <[email protected]>
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 
sys_execveat(int dirfd,const char * pathname,char * const argv[],char * const envp[],int flags)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 
drop_privileges(struct __test_metadata * const _metadata)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 
test_secbits_set(const unsigned int secbits)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 
FIXTURE(access)71 FIXTURE(access)
72 {
73 	int memfd, pipefd;
74 	int pipe_fds[2], socket_fds[2];
75 };
76 
FIXTURE_VARIANT(access)77 FIXTURE_VARIANT(access)
78 {
79 	const bool mount_exec;
80 	const bool file_exec;
81 };
82 
83 /* clang-format off */
FIXTURE_VARIANT_ADD(access,mount_exec_file_exec)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 */
FIXTURE_VARIANT_ADD(access,mount_exec_file_noexec)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 */
FIXTURE_VARIANT_ADD(access,mount_noexec_file_exec)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 */
FIXTURE_VARIANT_ADD(access,mount_noexec_file_noexec)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 
FIXTURE_SETUP(access)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 
FIXTURE_TEARDOWN_PARENT(access)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 
fill_exec_fd(struct __test_metadata * _metadata,const int fd_out)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 
fill_exec_path(struct __test_metadata * _metadata,const char * const path)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 
test_exec_fd(struct __test_metadata * _metadata,const int fd,const int err_code)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 
test_exec_path(struct __test_metadata * _metadata,const char * const path,const int err_code)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. */
TEST_F(access,regular_file_empty)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 
TEST_F(access,regular_file_elf)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. */
TEST_F(access,memfd_empty)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 
TEST_F(access,memfd_elf)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 
TEST_F(access,non_regular_files)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 */
FIXTURE(secbits)323 FIXTURE(secbits) {};
324 /* clang-format on */
325 
FIXTURE_VARIANT(secbits)326 FIXTURE_VARIANT(secbits)
327 {
328 	const bool is_privileged;
329 	const int error;
330 };
331 
332 /* clang-format off */
FIXTURE_VARIANT_ADD(secbits,priv)333 FIXTURE_VARIANT_ADD(secbits, priv) {
334 	/* clang-format on */
335 	.is_privileged = true,
336 	.error = 0,
337 };
338 
339 /* clang-format off */
FIXTURE_VARIANT_ADD(secbits,unpriv)340 FIXTURE_VARIANT_ADD(secbits, unpriv) {
341 	/* clang-format on */
342 	.is_privileged = false,
343 	.error = EPERM,
344 };
345 
FIXTURE_SETUP(secbits)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 
FIXTURE_TEARDOWN(secbits)356 FIXTURE_TEARDOWN(secbits)
357 {
358 }
359 
TEST_F(secbits,legacy)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 
TEST_F(secbits,exec)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 
TEST_F(secbits,check_locked_set)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 
TEST_F(secbits,check_locked_unset)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 
TEST_F(secbits,restrict_locked_set)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 
TEST_F(secbits,restrict_locked_unset)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