cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sandboxer.c (7188B)


      1// SPDX-License-Identifier: BSD-3-Clause
      2/*
      3 * Simple Landlock sandbox manager able to launch a process restricted by a
      4 * user-defined filesystem access control policy.
      5 *
      6 * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net>
      7 * Copyright © 2020 ANSSI
      8 */
      9
     10#define _GNU_SOURCE
     11#include <errno.h>
     12#include <fcntl.h>
     13#include <linux/landlock.h>
     14#include <linux/prctl.h>
     15#include <stddef.h>
     16#include <stdio.h>
     17#include <stdlib.h>
     18#include <string.h>
     19#include <sys/prctl.h>
     20#include <sys/stat.h>
     21#include <sys/syscall.h>
     22#include <unistd.h>
     23
     24#ifndef landlock_create_ruleset
     25static inline int
     26landlock_create_ruleset(const struct landlock_ruleset_attr *const attr,
     27			const size_t size, const __u32 flags)
     28{
     29	return syscall(__NR_landlock_create_ruleset, attr, size, flags);
     30}
     31#endif
     32
     33#ifndef landlock_add_rule
     34static inline int landlock_add_rule(const int ruleset_fd,
     35				    const enum landlock_rule_type rule_type,
     36				    const void *const rule_attr,
     37				    const __u32 flags)
     38{
     39	return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, rule_attr,
     40		       flags);
     41}
     42#endif
     43
     44#ifndef landlock_restrict_self
     45static inline int landlock_restrict_self(const int ruleset_fd,
     46					 const __u32 flags)
     47{
     48	return syscall(__NR_landlock_restrict_self, ruleset_fd, flags);
     49}
     50#endif
     51
     52#define ENV_FS_RO_NAME "LL_FS_RO"
     53#define ENV_FS_RW_NAME "LL_FS_RW"
     54#define ENV_PATH_TOKEN ":"
     55
     56static int parse_path(char *env_path, const char ***const path_list)
     57{
     58	int i, num_paths = 0;
     59
     60	if (env_path) {
     61		num_paths++;
     62		for (i = 0; env_path[i]; i++) {
     63			if (env_path[i] == ENV_PATH_TOKEN[0])
     64				num_paths++;
     65		}
     66	}
     67	*path_list = malloc(num_paths * sizeof(**path_list));
     68	for (i = 0; i < num_paths; i++)
     69		(*path_list)[i] = strsep(&env_path, ENV_PATH_TOKEN);
     70
     71	return num_paths;
     72}
     73
     74/* clang-format off */
     75
     76#define ACCESS_FILE ( \
     77	LANDLOCK_ACCESS_FS_EXECUTE | \
     78	LANDLOCK_ACCESS_FS_WRITE_FILE | \
     79	LANDLOCK_ACCESS_FS_READ_FILE)
     80
     81/* clang-format on */
     82
     83static int populate_ruleset(const char *const env_var, const int ruleset_fd,
     84			    const __u64 allowed_access)
     85{
     86	int num_paths, i, ret = 1;
     87	char *env_path_name;
     88	const char **path_list = NULL;
     89	struct landlock_path_beneath_attr path_beneath = {
     90		.parent_fd = -1,
     91	};
     92
     93	env_path_name = getenv(env_var);
     94	if (!env_path_name) {
     95		/* Prevents users to forget a setting. */
     96		fprintf(stderr, "Missing environment variable %s\n", env_var);
     97		return 1;
     98	}
     99	env_path_name = strdup(env_path_name);
    100	unsetenv(env_var);
    101	num_paths = parse_path(env_path_name, &path_list);
    102	if (num_paths == 1 && path_list[0][0] == '\0') {
    103		/*
    104		 * Allows to not use all possible restrictions (e.g. use
    105		 * LL_FS_RO without LL_FS_RW).
    106		 */
    107		ret = 0;
    108		goto out_free_name;
    109	}
    110
    111	for (i = 0; i < num_paths; i++) {
    112		struct stat statbuf;
    113
    114		path_beneath.parent_fd = open(path_list[i], O_PATH | O_CLOEXEC);
    115		if (path_beneath.parent_fd < 0) {
    116			fprintf(stderr, "Failed to open \"%s\": %s\n",
    117				path_list[i], strerror(errno));
    118			goto out_free_name;
    119		}
    120		if (fstat(path_beneath.parent_fd, &statbuf)) {
    121			close(path_beneath.parent_fd);
    122			goto out_free_name;
    123		}
    124		path_beneath.allowed_access = allowed_access;
    125		if (!S_ISDIR(statbuf.st_mode))
    126			path_beneath.allowed_access &= ACCESS_FILE;
    127		if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
    128				      &path_beneath, 0)) {
    129			fprintf(stderr,
    130				"Failed to update the ruleset with \"%s\": %s\n",
    131				path_list[i], strerror(errno));
    132			close(path_beneath.parent_fd);
    133			goto out_free_name;
    134		}
    135		close(path_beneath.parent_fd);
    136	}
    137	ret = 0;
    138
    139out_free_name:
    140	free(path_list);
    141	free(env_path_name);
    142	return ret;
    143}
    144
    145/* clang-format off */
    146
    147#define ACCESS_FS_ROUGHLY_READ ( \
    148	LANDLOCK_ACCESS_FS_EXECUTE | \
    149	LANDLOCK_ACCESS_FS_READ_FILE | \
    150	LANDLOCK_ACCESS_FS_READ_DIR)
    151
    152#define ACCESS_FS_ROUGHLY_WRITE ( \
    153	LANDLOCK_ACCESS_FS_WRITE_FILE | \
    154	LANDLOCK_ACCESS_FS_REMOVE_DIR | \
    155	LANDLOCK_ACCESS_FS_REMOVE_FILE | \
    156	LANDLOCK_ACCESS_FS_MAKE_CHAR | \
    157	LANDLOCK_ACCESS_FS_MAKE_DIR | \
    158	LANDLOCK_ACCESS_FS_MAKE_REG | \
    159	LANDLOCK_ACCESS_FS_MAKE_SOCK | \
    160	LANDLOCK_ACCESS_FS_MAKE_FIFO | \
    161	LANDLOCK_ACCESS_FS_MAKE_BLOCK | \
    162	LANDLOCK_ACCESS_FS_MAKE_SYM | \
    163	LANDLOCK_ACCESS_FS_REFER)
    164
    165#define ACCESS_ABI_2 ( \
    166	LANDLOCK_ACCESS_FS_REFER)
    167
    168/* clang-format on */
    169
    170int main(const int argc, char *const argv[], char *const *const envp)
    171{
    172	const char *cmd_path;
    173	char *const *cmd_argv;
    174	int ruleset_fd, abi;
    175	__u64 access_fs_ro = ACCESS_FS_ROUGHLY_READ,
    176	      access_fs_rw = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE;
    177	struct landlock_ruleset_attr ruleset_attr = {
    178		.handled_access_fs = access_fs_rw,
    179	};
    180
    181	if (argc < 2) {
    182		fprintf(stderr,
    183			"usage: %s=\"...\" %s=\"...\" %s <cmd> [args]...\n\n",
    184			ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]);
    185		fprintf(stderr,
    186			"Launch a command in a restricted environment.\n\n");
    187		fprintf(stderr, "Environment variables containing paths, "
    188				"each separated by a colon:\n");
    189		fprintf(stderr,
    190			"* %s: list of paths allowed to be used in a read-only way.\n",
    191			ENV_FS_RO_NAME);
    192		fprintf(stderr,
    193			"* %s: list of paths allowed to be used in a read-write way.\n",
    194			ENV_FS_RW_NAME);
    195		fprintf(stderr,
    196			"\nexample:\n"
    197			"%s=\"/bin:/lib:/usr:/proc:/etc:/dev/urandom\" "
    198			"%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" "
    199			"%s bash -i\n",
    200			ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]);
    201		return 1;
    202	}
    203
    204	abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
    205	if (abi < 0) {
    206		const int err = errno;
    207
    208		perror("Failed to check Landlock compatibility");
    209		switch (err) {
    210		case ENOSYS:
    211			fprintf(stderr,
    212				"Hint: Landlock is not supported by the current kernel. "
    213				"To support it, build the kernel with "
    214				"CONFIG_SECURITY_LANDLOCK=y and prepend "
    215				"\"landlock,\" to the content of CONFIG_LSM.\n");
    216			break;
    217		case EOPNOTSUPP:
    218			fprintf(stderr,
    219				"Hint: Landlock is currently disabled. "
    220				"It can be enabled in the kernel configuration by "
    221				"prepending \"landlock,\" to the content of CONFIG_LSM, "
    222				"or at boot time by setting the same content to the "
    223				"\"lsm\" kernel parameter.\n");
    224			break;
    225		}
    226		return 1;
    227	}
    228	/* Best-effort security. */
    229	if (abi < 2) {
    230		ruleset_attr.handled_access_fs &= ~ACCESS_ABI_2;
    231		access_fs_ro &= ~ACCESS_ABI_2;
    232		access_fs_rw &= ~ACCESS_ABI_2;
    233	}
    234
    235	ruleset_fd =
    236		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
    237	if (ruleset_fd < 0) {
    238		perror("Failed to create a ruleset");
    239		return 1;
    240	}
    241	if (populate_ruleset(ENV_FS_RO_NAME, ruleset_fd, access_fs_ro)) {
    242		goto err_close_ruleset;
    243	}
    244	if (populate_ruleset(ENV_FS_RW_NAME, ruleset_fd, access_fs_rw)) {
    245		goto err_close_ruleset;
    246	}
    247	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
    248		perror("Failed to restrict privileges");
    249		goto err_close_ruleset;
    250	}
    251	if (landlock_restrict_self(ruleset_fd, 0)) {
    252		perror("Failed to enforce ruleset");
    253		goto err_close_ruleset;
    254	}
    255	close(ruleset_fd);
    256
    257	cmd_path = argv[1];
    258	cmd_argv = argv + 1;
    259	execvpe(cmd_path, cmd_argv, envp);
    260	fprintf(stderr, "Failed to execute \"%s\": %s\n", cmd_path,
    261		strerror(errno));
    262	fprintf(stderr, "Hint: access to the binary, the interpreter or "
    263			"shared libraries may be denied.\n");
    264	return 1;
    265
    266err_close_ruleset:
    267	close(ruleset_fd);
    268	return 1;
    269}