diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-05-01 18:50:44 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-05-01 18:50:44 -0700 | 
| commit | 17ae69aba89dbfa2139b7f8024b757ab3cc42f59 (patch) | |
| tree | d0d13c06cf9bb3024563036a9ba5213b06b454e0 /samples | |
| parent | e6f0bf09f0669b3c2cd77fa906830123279a0a21 (diff) | |
| parent | 3532b0b4352ce79400b0aa68414f1a0fc422b920 (diff) | |
| download | linux-17ae69aba89dbfa2139b7f8024b757ab3cc42f59.tar.bz2 | |
Merge tag 'landlock_v34' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull Landlock LSM from James Morris:
 "Add Landlock, a new LSM from Mickaël Salaün.
  Briefly, Landlock provides for unprivileged application sandboxing.
  From Mickaël's cover letter:
    "The goal of Landlock is to enable to restrict ambient rights (e.g.
     global filesystem access) for a set of processes. Because Landlock
     is a stackable LSM [1], it makes possible to create safe security
     sandboxes as new security layers in addition to the existing
     system-wide access-controls. This kind of sandbox is expected to
     help mitigate the security impact of bugs or unexpected/malicious
     behaviors in user-space applications. Landlock empowers any
     process, including unprivileged ones, to securely restrict
     themselves.
     Landlock is inspired by seccomp-bpf but instead of filtering
     syscalls and their raw arguments, a Landlock rule can restrict the
     use of kernel objects like file hierarchies, according to the
     kernel semantic. Landlock also takes inspiration from other OS
     sandbox mechanisms: XNU Sandbox, FreeBSD Capsicum or OpenBSD
     Pledge/Unveil.
     In this current form, Landlock misses some access-control features.
     This enables to minimize this patch series and ease review. This
     series still addresses multiple use cases, especially with the
     combined use of seccomp-bpf: applications with built-in sandboxing,
     init systems, security sandbox tools and security-oriented APIs [2]"
  The cover letter and v34 posting is here:
      https://lore.kernel.org/linux-security-module/20210422154123.13086-1-mic@digikod.net/
  See also:
      https://landlock.io/
  This code has had extensive design discussion and review over several
  years"
Link: https://lore.kernel.org/lkml/50db058a-7dde-441b-a7f9-f6837fe8b69f@schaufler-ca.com/ [1]
Link: https://lore.kernel.org/lkml/f646e1c7-33cf-333f-070c-0a40ad0468cd@digikod.net/ [2]
* tag 'landlock_v34' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security:
  landlock: Enable user space to infer supported features
  landlock: Add user and kernel documentation
  samples/landlock: Add a sandbox manager example
  selftests/landlock: Add user space tests
  landlock: Add syscall implementations
  arch: Wire up Landlock syscalls
  fs,security: Add sb_delete hook
  landlock: Support filesystem access-control
  LSM: Infrastructure management of the superblock
  landlock: Add ptrace restrictions
  landlock: Set up the security framework and manage credentials
  landlock: Add ruleset and domain management
  landlock: Add object management
Diffstat (limited to 'samples')
| -rw-r--r-- | samples/Kconfig | 7 | ||||
| -rw-r--r-- | samples/Makefile | 1 | ||||
| -rw-r--r-- | samples/landlock/.gitignore | 1 | ||||
| -rw-r--r-- | samples/landlock/Makefile | 13 | ||||
| -rw-r--r-- | samples/landlock/sandboxer.c | 238 | 
5 files changed, 260 insertions, 0 deletions
diff --git a/samples/Kconfig b/samples/Kconfig index e76cdfc50e25..b5a1a7aa7e23 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -124,6 +124,13 @@ config SAMPLE_HIDRAW  	bool "hidraw sample"  	depends on CC_CAN_LINK && HEADERS_INSTALL +config SAMPLE_LANDLOCK +	bool "Landlock example" +	depends on CC_CAN_LINK && HEADERS_INSTALL +	help +	  Build a simple Landlock sandbox manager able to start a process +	  restricted by a user-defined filesystem access control policy. +  config SAMPLE_PIDFD  	bool "pidfd sample"  	depends on CC_CAN_LINK && HEADERS_INSTALL diff --git a/samples/Makefile b/samples/Makefile index c3392a595e4b..087e0988ccc5 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_SAMPLE_KDB)		+= kdb/  obj-$(CONFIG_SAMPLE_KFIFO)		+= kfifo/  obj-$(CONFIG_SAMPLE_KOBJECT)		+= kobject/  obj-$(CONFIG_SAMPLE_KPROBES)		+= kprobes/ +subdir-$(CONFIG_SAMPLE_LANDLOCK)	+= landlock  obj-$(CONFIG_SAMPLE_LIVEPATCH)		+= livepatch/  subdir-$(CONFIG_SAMPLE_PIDFD)		+= pidfd  obj-$(CONFIG_SAMPLE_QMI_CLIENT)		+= qmi/ diff --git a/samples/landlock/.gitignore b/samples/landlock/.gitignore new file mode 100644 index 000000000000..f43668b2d318 --- /dev/null +++ b/samples/landlock/.gitignore @@ -0,0 +1 @@ +/sandboxer diff --git a/samples/landlock/Makefile b/samples/landlock/Makefile new file mode 100644 index 000000000000..5d601e51c2eb --- /dev/null +++ b/samples/landlock/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: BSD-3-Clause + +userprogs-always-y := sandboxer + +userccflags += -I usr/include + +.PHONY: all clean + +all: +	$(MAKE) -C ../.. samples/landlock/ + +clean: +	$(MAKE) -C ../.. M=samples/landlock/ clean diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c new file mode 100644 index 000000000000..7a15910d2171 --- /dev/null +++ b/samples/landlock/sandboxer.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Simple Landlock sandbox manager able to launch a process restricted by a + * user-defined filesystem access control policy. + * + * Copyright © 2017-2020 Mickaël Salaün <mic@digikod.net> + * Copyright © 2020 ANSSI + */ + +#define _GNU_SOURCE +#include <errno.h> +#include <fcntl.h> +#include <linux/landlock.h> +#include <linux/prctl.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/prctl.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <unistd.h> + +#ifndef landlock_create_ruleset +static inline int landlock_create_ruleset( +		const struct landlock_ruleset_attr *const attr, +		const size_t size, const __u32 flags) +{ +	return syscall(__NR_landlock_create_ruleset, attr, size, flags); +} +#endif + +#ifndef landlock_add_rule +static inline int landlock_add_rule(const int ruleset_fd, +		const enum landlock_rule_type rule_type, +		const void *const rule_attr, const __u32 flags) +{ +	return syscall(__NR_landlock_add_rule, ruleset_fd, rule_type, +			rule_attr, flags); +} +#endif + +#ifndef landlock_restrict_self +static inline int landlock_restrict_self(const int ruleset_fd, +		const __u32 flags) +{ +	return syscall(__NR_landlock_restrict_self, ruleset_fd, flags); +} +#endif + +#define ENV_FS_RO_NAME "LL_FS_RO" +#define ENV_FS_RW_NAME "LL_FS_RW" +#define ENV_PATH_TOKEN ":" + +static int parse_path(char *env_path, const char ***const path_list) +{ +	int i, num_paths = 0; + +	if (env_path) { +		num_paths++; +		for (i = 0; env_path[i]; i++) { +			if (env_path[i] == ENV_PATH_TOKEN[0]) +				num_paths++; +		} +	} +	*path_list = malloc(num_paths * sizeof(**path_list)); +	for (i = 0; i < num_paths; i++) +		(*path_list)[i] = strsep(&env_path, ENV_PATH_TOKEN); + +	return num_paths; +} + +#define ACCESS_FILE ( \ +	LANDLOCK_ACCESS_FS_EXECUTE | \ +	LANDLOCK_ACCESS_FS_WRITE_FILE | \ +	LANDLOCK_ACCESS_FS_READ_FILE) + +static int populate_ruleset( +		const char *const env_var, const int ruleset_fd, +		const __u64 allowed_access) +{ +	int num_paths, i, ret = 1; +	char *env_path_name; +	const char **path_list = NULL; +	struct landlock_path_beneath_attr path_beneath = { +		.parent_fd = -1, +	}; + +	env_path_name = getenv(env_var); +	if (!env_path_name) { +		/* Prevents users to forget a setting. */ +		fprintf(stderr, "Missing environment variable %s\n", env_var); +		return 1; +	} +	env_path_name = strdup(env_path_name); +	unsetenv(env_var); +	num_paths = parse_path(env_path_name, &path_list); +	if (num_paths == 1 && path_list[0][0] == '\0') { +		/* +		 * Allows to not use all possible restrictions (e.g. use +		 * LL_FS_RO without LL_FS_RW). +		 */ +		ret = 0; +		goto out_free_name; +	} + +	for (i = 0; i < num_paths; i++) { +		struct stat statbuf; + +		path_beneath.parent_fd = open(path_list[i], O_PATH | +				O_CLOEXEC); +		if (path_beneath.parent_fd < 0) { +			fprintf(stderr, "Failed to open \"%s\": %s\n", +					path_list[i], +					strerror(errno)); +			goto out_free_name; +		} +		if (fstat(path_beneath.parent_fd, &statbuf)) { +			close(path_beneath.parent_fd); +			goto out_free_name; +		} +		path_beneath.allowed_access = allowed_access; +		if (!S_ISDIR(statbuf.st_mode)) +			path_beneath.allowed_access &= ACCESS_FILE; +		if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, +					&path_beneath, 0)) { +			fprintf(stderr, "Failed to update the ruleset with \"%s\": %s\n", +					path_list[i], strerror(errno)); +			close(path_beneath.parent_fd); +			goto out_free_name; +		} +		close(path_beneath.parent_fd); +	} +	ret = 0; + +out_free_name: +	free(env_path_name); +	return ret; +} + +#define ACCESS_FS_ROUGHLY_READ ( \ +	LANDLOCK_ACCESS_FS_EXECUTE | \ +	LANDLOCK_ACCESS_FS_READ_FILE | \ +	LANDLOCK_ACCESS_FS_READ_DIR) + +#define ACCESS_FS_ROUGHLY_WRITE ( \ +	LANDLOCK_ACCESS_FS_WRITE_FILE | \ +	LANDLOCK_ACCESS_FS_REMOVE_DIR | \ +	LANDLOCK_ACCESS_FS_REMOVE_FILE | \ +	LANDLOCK_ACCESS_FS_MAKE_CHAR | \ +	LANDLOCK_ACCESS_FS_MAKE_DIR | \ +	LANDLOCK_ACCESS_FS_MAKE_REG | \ +	LANDLOCK_ACCESS_FS_MAKE_SOCK | \ +	LANDLOCK_ACCESS_FS_MAKE_FIFO | \ +	LANDLOCK_ACCESS_FS_MAKE_BLOCK | \ +	LANDLOCK_ACCESS_FS_MAKE_SYM) + +int main(const int argc, char *const argv[], char *const *const envp) +{ +	const char *cmd_path; +	char *const *cmd_argv; +	int ruleset_fd; +	struct landlock_ruleset_attr ruleset_attr = { +		.handled_access_fs = ACCESS_FS_ROUGHLY_READ | +			ACCESS_FS_ROUGHLY_WRITE, +	}; + +	if (argc < 2) { +		fprintf(stderr, "usage: %s=\"...\" %s=\"...\" %s <cmd> [args]...\n\n", +				ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]); +		fprintf(stderr, "Launch a command in a restricted environment.\n\n"); +		fprintf(stderr, "Environment variables containing paths, " +				"each separated by a colon:\n"); +		fprintf(stderr, "* %s: list of paths allowed to be used in a read-only way.\n", +				ENV_FS_RO_NAME); +		fprintf(stderr, "* %s: list of paths allowed to be used in a read-write way.\n", +				ENV_FS_RW_NAME); +		fprintf(stderr, "\nexample:\n" +				"%s=\"/bin:/lib:/usr:/proc:/etc:/dev/urandom\" " +				"%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" " +				"%s bash -i\n", +				ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]); +		return 1; +	} + +	ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0); +	if (ruleset_fd < 0) { +		const int err = errno; + +		perror("Failed to create a ruleset"); +		switch (err) { +		case ENOSYS: +			fprintf(stderr, "Hint: Landlock is not supported by the current kernel. " +					"To support it, build the kernel with " +					"CONFIG_SECURITY_LANDLOCK=y and prepend " +					"\"landlock,\" to the content of CONFIG_LSM.\n"); +			break; +		case EOPNOTSUPP: +			fprintf(stderr, "Hint: Landlock is currently disabled. " +					"It can be enabled in the kernel configuration by " +					"prepending \"landlock,\" to the content of CONFIG_LSM, " +					"or at boot time by setting the same content to the " +					"\"lsm\" kernel parameter.\n"); +			break; +		} +		return 1; +	} +	if (populate_ruleset(ENV_FS_RO_NAME, ruleset_fd, +				ACCESS_FS_ROUGHLY_READ)) { +		goto err_close_ruleset; +	} +	if (populate_ruleset(ENV_FS_RW_NAME, ruleset_fd, +				ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE)) { +		goto err_close_ruleset; +	} +	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { +		perror("Failed to restrict privileges"); +		goto err_close_ruleset; +	} +	if (landlock_restrict_self(ruleset_fd, 0)) { +		perror("Failed to enforce ruleset"); +		goto err_close_ruleset; +	} +	close(ruleset_fd); + +	cmd_path = argv[1]; +	cmd_argv = argv + 1; +	execvpe(cmd_path, cmd_argv, envp); +	fprintf(stderr, "Failed to execute \"%s\": %s\n", cmd_path, +			strerror(errno)); +	fprintf(stderr, "Hint: access to the binary, the interpreter or " +			"shared libraries may be denied.\n"); +	return 1; + +err_close_ruleset: +	close(ruleset_fd); +	return 1; +}  |