brintos

brintos / linux-shallow public Read only

0
0
Text · 11.6 KiB · 1bc16fd Raw
404 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Landlock tests - Common user space base4 *5 * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>6 * Copyright © 2019-2020 ANSSI7 */8 9#define _GNU_SOURCE10#include <errno.h>11#include <fcntl.h>12#include <linux/keyctl.h>13#include <linux/landlock.h>14#include <string.h>15#include <sys/prctl.h>16#include <sys/socket.h>17#include <sys/types.h>18 19#include "common.h"20 21#ifndef O_PATH22#define O_PATH 01000000023#endif24 25TEST(inconsistent_attr)26{27	const long page_size = sysconf(_SC_PAGESIZE);28	char *const buf = malloc(page_size + 1);29	struct landlock_ruleset_attr *const ruleset_attr = (void *)buf;30 31	ASSERT_NE(NULL, buf);32 33	/* Checks copy_from_user(). */34	ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, 0, 0));35	/* The size if less than sizeof(struct landlock_attr_enforce). */36	ASSERT_EQ(EINVAL, errno);37	ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, 1, 0));38	ASSERT_EQ(EINVAL, errno);39	ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, 7, 0));40	ASSERT_EQ(EINVAL, errno);41 42	ASSERT_EQ(-1, landlock_create_ruleset(NULL, 1, 0));43	/* The size if less than sizeof(struct landlock_attr_enforce). */44	ASSERT_EQ(EFAULT, errno);45 46	ASSERT_EQ(-1, landlock_create_ruleset(47			      NULL, sizeof(struct landlock_ruleset_attr), 0));48	ASSERT_EQ(EFAULT, errno);49 50	ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, page_size + 1, 0));51	ASSERT_EQ(E2BIG, errno);52 53	/* Checks minimal valid attribute size. */54	ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, 8, 0));55	ASSERT_EQ(ENOMSG, errno);56	ASSERT_EQ(-1, landlock_create_ruleset(57			      ruleset_attr,58			      sizeof(struct landlock_ruleset_attr), 0));59	ASSERT_EQ(ENOMSG, errno);60	ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, page_size, 0));61	ASSERT_EQ(ENOMSG, errno);62 63	/* Checks non-zero value. */64	buf[page_size - 2] = '.';65	ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, page_size, 0));66	ASSERT_EQ(E2BIG, errno);67 68	ASSERT_EQ(-1, landlock_create_ruleset(ruleset_attr, page_size + 1, 0));69	ASSERT_EQ(E2BIG, errno);70 71	free(buf);72}73 74TEST(abi_version)75{76	const struct landlock_ruleset_attr ruleset_attr = {77		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,78	};79	ASSERT_EQ(6, landlock_create_ruleset(NULL, 0,80					     LANDLOCK_CREATE_RULESET_VERSION));81 82	ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,83					      LANDLOCK_CREATE_RULESET_VERSION));84	ASSERT_EQ(EINVAL, errno);85 86	ASSERT_EQ(-1, landlock_create_ruleset(NULL, sizeof(ruleset_attr),87					      LANDLOCK_CREATE_RULESET_VERSION));88	ASSERT_EQ(EINVAL, errno);89 90	ASSERT_EQ(-1,91		  landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr),92					  LANDLOCK_CREATE_RULESET_VERSION));93	ASSERT_EQ(EINVAL, errno);94 95	ASSERT_EQ(-1, landlock_create_ruleset(NULL, 0,96					      LANDLOCK_CREATE_RULESET_VERSION |97						      1 << 31));98	ASSERT_EQ(EINVAL, errno);99}100 101/* Tests ordering of syscall argument checks. */102TEST(create_ruleset_checks_ordering)103{104	const int last_flag = LANDLOCK_CREATE_RULESET_VERSION;105	const int invalid_flag = last_flag << 1;106	int ruleset_fd;107	const struct landlock_ruleset_attr ruleset_attr = {108		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,109	};110 111	/* Checks priority for invalid flags. */112	ASSERT_EQ(-1, landlock_create_ruleset(NULL, 0, invalid_flag));113	ASSERT_EQ(EINVAL, errno);114 115	ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0, invalid_flag));116	ASSERT_EQ(EINVAL, errno);117 118	ASSERT_EQ(-1, landlock_create_ruleset(NULL, sizeof(ruleset_attr),119					      invalid_flag));120	ASSERT_EQ(EINVAL, errno);121 122	ASSERT_EQ(-1,123		  landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr),124					  invalid_flag));125	ASSERT_EQ(EINVAL, errno);126 127	/* Checks too big ruleset_attr size. */128	ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, -1, 0));129	ASSERT_EQ(E2BIG, errno);130 131	/* Checks too small ruleset_attr size. */132	ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0, 0));133	ASSERT_EQ(EINVAL, errno);134	ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 1, 0));135	ASSERT_EQ(EINVAL, errno);136 137	/* Checks valid call. */138	ruleset_fd =139		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);140	ASSERT_LE(0, ruleset_fd);141	ASSERT_EQ(0, close(ruleset_fd));142}143 144/* Tests ordering of syscall argument checks. */145TEST(add_rule_checks_ordering)146{147	const struct landlock_ruleset_attr ruleset_attr = {148		.handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE,149	};150	struct landlock_path_beneath_attr path_beneath_attr = {151		.allowed_access = LANDLOCK_ACCESS_FS_EXECUTE,152		.parent_fd = -1,153	};154	const int ruleset_fd =155		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);156 157	ASSERT_LE(0, ruleset_fd);158 159	/* Checks invalid flags. */160	ASSERT_EQ(-1, landlock_add_rule(-1, 0, NULL, 1));161	ASSERT_EQ(EINVAL, errno);162 163	/* Checks invalid ruleset FD. */164	ASSERT_EQ(-1, landlock_add_rule(-1, 0, NULL, 0));165	ASSERT_EQ(EBADF, errno);166 167	/* Checks invalid rule type. */168	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, 0, NULL, 0));169	ASSERT_EQ(EINVAL, errno);170 171	/* Checks invalid rule attr. */172	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,173					NULL, 0));174	ASSERT_EQ(EFAULT, errno);175 176	/* Checks invalid path_beneath.parent_fd. */177	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,178					&path_beneath_attr, 0));179	ASSERT_EQ(EBADF, errno);180 181	/* Checks valid call. */182	path_beneath_attr.parent_fd =183		open("/tmp", O_PATH | O_NOFOLLOW | O_DIRECTORY | O_CLOEXEC);184	ASSERT_LE(0, path_beneath_attr.parent_fd);185	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,186				       &path_beneath_attr, 0));187	ASSERT_EQ(0, close(path_beneath_attr.parent_fd));188	ASSERT_EQ(0, close(ruleset_fd));189}190 191/* Tests ordering of syscall argument and permission checks. */192TEST(restrict_self_checks_ordering)193{194	const struct landlock_ruleset_attr ruleset_attr = {195		.handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE,196	};197	struct landlock_path_beneath_attr path_beneath_attr = {198		.allowed_access = LANDLOCK_ACCESS_FS_EXECUTE,199		.parent_fd = -1,200	};201	const int ruleset_fd =202		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);203 204	ASSERT_LE(0, ruleset_fd);205	path_beneath_attr.parent_fd =206		open("/tmp", O_PATH | O_NOFOLLOW | O_DIRECTORY | O_CLOEXEC);207	ASSERT_LE(0, path_beneath_attr.parent_fd);208	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,209				       &path_beneath_attr, 0));210	ASSERT_EQ(0, close(path_beneath_attr.parent_fd));211 212	/* Checks unprivileged enforcement without no_new_privs. */213	drop_caps(_metadata);214	ASSERT_EQ(-1, landlock_restrict_self(-1, -1));215	ASSERT_EQ(EPERM, errno);216	ASSERT_EQ(-1, landlock_restrict_self(-1, 0));217	ASSERT_EQ(EPERM, errno);218	ASSERT_EQ(-1, landlock_restrict_self(ruleset_fd, 0));219	ASSERT_EQ(EPERM, errno);220 221	ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));222 223	/* Checks invalid flags. */224	ASSERT_EQ(-1, landlock_restrict_self(-1, -1));225	ASSERT_EQ(EINVAL, errno);226 227	/* Checks invalid ruleset FD. */228	ASSERT_EQ(-1, landlock_restrict_self(-1, 0));229	ASSERT_EQ(EBADF, errno);230 231	/* Checks valid call. */232	ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0));233	ASSERT_EQ(0, close(ruleset_fd));234}235 236TEST(ruleset_fd_io)237{238	struct landlock_ruleset_attr ruleset_attr = {239		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,240	};241	int ruleset_fd;242	char buf;243 244	drop_caps(_metadata);245	ruleset_fd =246		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);247	ASSERT_LE(0, ruleset_fd);248 249	ASSERT_EQ(-1, write(ruleset_fd, ".", 1));250	ASSERT_EQ(EINVAL, errno);251	ASSERT_EQ(-1, read(ruleset_fd, &buf, 1));252	ASSERT_EQ(EINVAL, errno);253 254	ASSERT_EQ(0, close(ruleset_fd));255}256 257/* Tests enforcement of a ruleset FD transferred through a UNIX socket. */258TEST(ruleset_fd_transfer)259{260	struct landlock_ruleset_attr ruleset_attr = {261		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR,262	};263	struct landlock_path_beneath_attr path_beneath_attr = {264		.allowed_access = LANDLOCK_ACCESS_FS_READ_DIR,265	};266	int ruleset_fd_tx, dir_fd;267	int socket_fds[2];268	pid_t child;269	int status;270 271	drop_caps(_metadata);272 273	/* Creates a test ruleset with a simple rule. */274	ruleset_fd_tx =275		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);276	ASSERT_LE(0, ruleset_fd_tx);277	path_beneath_attr.parent_fd =278		open("/tmp", O_PATH | O_NOFOLLOW | O_DIRECTORY | O_CLOEXEC);279	ASSERT_LE(0, path_beneath_attr.parent_fd);280	ASSERT_EQ(0,281		  landlock_add_rule(ruleset_fd_tx, LANDLOCK_RULE_PATH_BENEATH,282				    &path_beneath_attr, 0));283	ASSERT_EQ(0, close(path_beneath_attr.parent_fd));284 285	/* Sends the ruleset FD over a socketpair and then close it. */286	ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0,287				socket_fds));288	ASSERT_EQ(0, send_fd(socket_fds[0], ruleset_fd_tx));289	ASSERT_EQ(0, close(socket_fds[0]));290	ASSERT_EQ(0, close(ruleset_fd_tx));291 292	child = fork();293	ASSERT_LE(0, child);294	if (child == 0) {295		const int ruleset_fd_rx = recv_fd(socket_fds[1]);296 297		ASSERT_LE(0, ruleset_fd_rx);298		ASSERT_EQ(0, close(socket_fds[1]));299 300		/* Enforces the received ruleset on the child. */301		ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));302		ASSERT_EQ(0, landlock_restrict_self(ruleset_fd_rx, 0));303		ASSERT_EQ(0, close(ruleset_fd_rx));304 305		/* Checks that the ruleset enforcement. */306		ASSERT_EQ(-1, open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC));307		ASSERT_EQ(EACCES, errno);308		dir_fd = open("/tmp", O_RDONLY | O_DIRECTORY | O_CLOEXEC);309		ASSERT_LE(0, dir_fd);310		ASSERT_EQ(0, close(dir_fd));311		_exit(_metadata->exit_code);312		return;313	}314 315	ASSERT_EQ(0, close(socket_fds[1]));316 317	/* Checks that the parent is unrestricted. */318	dir_fd = open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC);319	ASSERT_LE(0, dir_fd);320	ASSERT_EQ(0, close(dir_fd));321	dir_fd = open("/tmp", O_RDONLY | O_DIRECTORY | O_CLOEXEC);322	ASSERT_LE(0, dir_fd);323	ASSERT_EQ(0, close(dir_fd));324 325	ASSERT_EQ(child, waitpid(child, &status, 0));326	ASSERT_EQ(1, WIFEXITED(status));327	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));328}329 330TEST(cred_transfer)331{332	struct landlock_ruleset_attr ruleset_attr = {333		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_DIR,334	};335	int ruleset_fd, dir_fd;336	pid_t child;337	int status;338 339	drop_caps(_metadata);340 341	dir_fd = open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC);342	EXPECT_LE(0, dir_fd);343	EXPECT_EQ(0, close(dir_fd));344 345	/* Denies opening directories. */346	ruleset_fd =347		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);348	ASSERT_LE(0, ruleset_fd);349	EXPECT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));350	ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0));351	EXPECT_EQ(0, close(ruleset_fd));352 353	/* Checks ruleset enforcement. */354	EXPECT_EQ(-1, open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC));355	EXPECT_EQ(EACCES, errno);356 357	/* Needed for KEYCTL_SESSION_TO_PARENT permission checks */358	EXPECT_NE(-1, syscall(__NR_keyctl, KEYCTL_JOIN_SESSION_KEYRING, NULL, 0,359			      0, 0))360	{361		TH_LOG("Failed to join session keyring: %s", strerror(errno));362	}363 364	child = fork();365	ASSERT_LE(0, child);366	if (child == 0) {367		/* Checks ruleset enforcement. */368		EXPECT_EQ(-1, open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC));369		EXPECT_EQ(EACCES, errno);370 371		/*372		 * KEYCTL_SESSION_TO_PARENT is a no-op unless we have a373		 * different session keyring in the child, so make that happen.374		 */375		EXPECT_NE(-1, syscall(__NR_keyctl, KEYCTL_JOIN_SESSION_KEYRING,376				      NULL, 0, 0, 0));377 378		/*379		 * KEYCTL_SESSION_TO_PARENT installs credentials on the parent380		 * that never go through the cred_prepare hook, this path uses381		 * cred_transfer instead.382		 */383		EXPECT_EQ(0, syscall(__NR_keyctl, KEYCTL_SESSION_TO_PARENT, 0,384				     0, 0, 0));385 386		/* Re-checks ruleset enforcement. */387		EXPECT_EQ(-1, open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC));388		EXPECT_EQ(EACCES, errno);389 390		_exit(_metadata->exit_code);391		return;392	}393 394	EXPECT_EQ(child, waitpid(child, &status, 0));395	EXPECT_EQ(1, WIFEXITED(status));396	EXPECT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));397 398	/* Re-checks ruleset enforcement. */399	EXPECT_EQ(-1, open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC));400	EXPECT_EQ(EACCES, errno);401}402 403TEST_HARNESS_MAIN404