mirror of
				https://github.com/linuxkit/linuxkit.git
				synced 2025-10-31 09:09:57 +00:00 
			
		
		
		
	landlock: Backport patches from the sixth series
Backport from Linux v4.11-rc3-812-gc6bf33827b7d to Linux 4.9.20: https://github.com/landlock-lsm/linux/commits/landlock-v6-linux-v4.9.20 Do not include documentation nor tests. See built documentation here: https://landlock-lsm.github.io/linux-doc/landlock-v6/security/landlock/index.html Signed-off-by: Mickaël Salaün <mic@digikod.net> Link: https://lkml.kernel.org/r/20170328234650.19695-1-mic@digikod.net
This commit is contained in:
		| @@ -0,0 +1,38 @@ | |||||||
|  | From 9a94a681dffa7b2115806405ab573ba41052ad04 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= <mic@digikod.net> | ||||||
|  | Date: Tue, 7 Feb 2017 21:56:05 +0100 | ||||||
|  | Subject: [PATCH 01/12] tools lib bpf: Add missing header to the library | ||||||
|  | MIME-Version: 1.0 | ||||||
|  | Content-Type: text/plain; charset=UTF-8 | ||||||
|  | Content-Transfer-Encoding: 8bit | ||||||
|  |  | ||||||
|  | Include stddef.h to define size_t. | ||||||
|  |  | ||||||
|  | Signed-off-by: Mickaël Salaün <mic@digikod.net> | ||||||
|  | Acked-by: Wang Nan <wangnan0@huawei.com> | ||||||
|  | Cc: Alexei Starovoitov <ast@fb.com> | ||||||
|  | Cc: Daniel Borkmann <daniel@iogearbox.net> | ||||||
|  | Cc: David S. Miller <davem@davemloft.net> | ||||||
|  | Cc: Joe Stringer <joe@ovn.org> | ||||||
|  | Link: http://lkml.kernel.org/r/20170207205609.8035-2-mic@digikod.net | ||||||
|  | Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> | ||||||
|  | (cherry picked from commit 7a5980f9c0066d085319415ec15ee51f165111f5) | ||||||
|  | --- | ||||||
|  |  tools/lib/bpf/bpf.h | 1 + | ||||||
|  |  1 file changed, 1 insertion(+) | ||||||
|  |  | ||||||
|  | diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h | ||||||
|  | index e8ba54087497..eb584e639500 100644 | ||||||
|  | --- a/tools/lib/bpf/bpf.h | ||||||
|  | +++ b/tools/lib/bpf/bpf.h | ||||||
|  | @@ -22,6 +22,7 @@ | ||||||
|  |  #define __BPF_BPF_H | ||||||
|  |   | ||||||
|  |  #include <linux/bpf.h> | ||||||
|  | +#include <stddef.h> | ||||||
|  |   | ||||||
|  |  int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, | ||||||
|  |  		   int max_entries); | ||||||
|  | --  | ||||||
|  | 2.11.0 | ||||||
|  |  | ||||||
| @@ -0,0 +1,39 @@ | |||||||
|  | From 7dbf58fc4e42a55cc2d37130362080f7ecf8ca1a Mon Sep 17 00:00:00 2001 | ||||||
|  | From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= <mic@digikod.net> | ||||||
|  | Date: Wed, 8 Feb 2017 21:27:44 +0100 | ||||||
|  | Subject: [PATCH 02/12] samples/bpf: Add missing header | ||||||
|  | MIME-Version: 1.0 | ||||||
|  | Content-Type: text/plain; charset=UTF-8 | ||||||
|  | Content-Transfer-Encoding: 8bit | ||||||
|  |  | ||||||
|  | Include unistd.h to define __NR_getuid and __NR_getsid. | ||||||
|  |  | ||||||
|  | Signed-off-by: Mickaël Salaün <mic@digikod.net> | ||||||
|  | Acked-by: Joe Stringer <joe@ovn.org> | ||||||
|  | Acked-by: Wang Nan <wangnan0@huawei.com> | ||||||
|  | Cc: Alexei Starovoitov <ast@fb.com> | ||||||
|  | Cc: Daniel Borkmann <daniel@iogearbox.net> | ||||||
|  | Cc: David S. Miller <davem@davemloft.net> | ||||||
|  | Cc: netdev@vger.kernel.org | ||||||
|  | Link: http://lkml.kernel.org/r/20170208202744.16274-4-mic@digikod.net | ||||||
|  | Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> | ||||||
|  | (cherry picked from commit af392a8f5399e831cb502ff210dacef8b38ca513) | ||||||
|  | --- | ||||||
|  |  samples/bpf/tracex5_kern.c | 1 + | ||||||
|  |  1 file changed, 1 insertion(+) | ||||||
|  |  | ||||||
|  | diff --git a/samples/bpf/tracex5_kern.c b/samples/bpf/tracex5_kern.c | ||||||
|  | index fd12d7154d42..7e4cf74553ff 100644 | ||||||
|  | --- a/samples/bpf/tracex5_kern.c | ||||||
|  | +++ b/samples/bpf/tracex5_kern.c | ||||||
|  | @@ -8,6 +8,7 @@ | ||||||
|  |  #include <linux/version.h> | ||||||
|  |  #include <uapi/linux/bpf.h> | ||||||
|  |  #include <uapi/linux/seccomp.h> | ||||||
|  | +#include <uapi/linux/unistd.h> | ||||||
|  |  #include "bpf_helpers.h" | ||||||
|  |   | ||||||
|  |  #define PROG(F) SEC("kprobe/"__stringify(F)) int bpf_func_##F | ||||||
|  | --  | ||||||
|  | 2.11.0 | ||||||
|  |  | ||||||
| @@ -0,0 +1,40 @@ | |||||||
|  | From 7e0ee6e01fbafac371e50052eddd59af9b053825 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= <mic@digikod.net> | ||||||
|  | Date: Wed, 8 Feb 2017 21:27:42 +0100 | ||||||
|  | Subject: [PATCH 03/12] samples/bpf: Ignore already processed ELF sections | ||||||
|  | MIME-Version: 1.0 | ||||||
|  | Content-Type: text/plain; charset=UTF-8 | ||||||
|  | Content-Transfer-Encoding: 8bit | ||||||
|  |  | ||||||
|  | Add a missing check for the map fixup loop. | ||||||
|  |  | ||||||
|  | Signed-off-by: Mickaël Salaün <mic@digikod.net> | ||||||
|  | Acked-by: Joe Stringer <joe@ovn.org> | ||||||
|  | Acked-by: Wang Nan <wangnan0@huawei.com> | ||||||
|  | Cc: Alexei Starovoitov <ast@fb.com> | ||||||
|  | Cc: Daniel Borkmann <daniel@iogearbox.net> | ||||||
|  | Cc: David S. Miller <davem@davemloft.net> | ||||||
|  | Cc: netdev@vger.kernel.org | ||||||
|  | Link: http://lkml.kernel.org/r/20170208202744.16274-2-mic@digikod.net | ||||||
|  | Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> | ||||||
|  | (cherry picked from commit 16ad1329002f905c643a438ddcfb0a180787850a) | ||||||
|  | --- | ||||||
|  |  samples/bpf/bpf_load.c | 2 ++ | ||||||
|  |  1 file changed, 2 insertions(+) | ||||||
|  |  | ||||||
|  | diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c | ||||||
|  | index 97913e109b14..4b86bd3c7c6b 100644 | ||||||
|  | --- a/samples/bpf/bpf_load.c | ||||||
|  | +++ b/samples/bpf/bpf_load.c | ||||||
|  | @@ -307,6 +307,8 @@ int load_bpf_file(char *path) | ||||||
|  |   | ||||||
|  |  	/* load programs that need map fixup (relocations) */ | ||||||
|  |  	for (i = 1; i < ehdr.e_shnum; i++) { | ||||||
|  | +		if (processed_sec[i]) | ||||||
|  | +			continue; | ||||||
|  |   | ||||||
|  |  		if (get_sec(elf, i, &ehdr, &shname, &shdr, &data)) | ||||||
|  |  			continue; | ||||||
|  | --  | ||||||
|  | 2.11.0 | ||||||
|  |  | ||||||
| @@ -0,0 +1,44 @@ | |||||||
|  | From 3dd0d725f5e83a53ea2d4cbb3fe0856ce2b836cf Mon Sep 17 00:00:00 2001 | ||||||
|  | From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= <mic@digikod.net> | ||||||
|  | Date: Wed, 8 Feb 2017 21:27:43 +0100 | ||||||
|  | Subject: [PATCH 04/12] samples/bpf: Reset global variables | ||||||
|  | MIME-Version: 1.0 | ||||||
|  | Content-Type: text/plain; charset=UTF-8 | ||||||
|  | Content-Transfer-Encoding: 8bit | ||||||
|  |  | ||||||
|  | Before loading a new ELF, clean previous kernel version, license and | ||||||
|  | processed sections. | ||||||
|  |  | ||||||
|  | Signed-off-by: Mickaël Salaün <mic@digikod.net> | ||||||
|  | Acked-by: Joe Stringer <joe@ovn.org> | ||||||
|  | Acked-by: Wang Nan <wangnan0@huawei.com> | ||||||
|  | Cc: Alexei Starovoitov <ast@fb.com> | ||||||
|  | Cc: Daniel Borkmann <daniel@iogearbox.net> | ||||||
|  | Cc: David S. Miller <davem@davemloft.net> | ||||||
|  | Cc: netdev@vger.kernel.org | ||||||
|  | Link: http://lkml.kernel.org/r/20170208202744.16274-3-mic@digikod.net | ||||||
|  | Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> | ||||||
|  | (cherry picked from commit a734fb5d60067a73dd7099a58756847c07f9cd68) | ||||||
|  | --- | ||||||
|  |  samples/bpf/bpf_load.c | 5 +++++ | ||||||
|  |  1 file changed, 5 insertions(+) | ||||||
|  |  | ||||||
|  | diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c | ||||||
|  | index 4b86bd3c7c6b..765a6e45b92d 100644 | ||||||
|  | --- a/samples/bpf/bpf_load.c | ||||||
|  | +++ b/samples/bpf/bpf_load.c | ||||||
|  | @@ -256,6 +256,11 @@ int load_bpf_file(char *path) | ||||||
|  |  	Elf_Data *data, *data_prog, *symbols = NULL; | ||||||
|  |  	char *shname, *shname_prog; | ||||||
|  |   | ||||||
|  | +	/* reset global variables */ | ||||||
|  | +	kern_version = 0; | ||||||
|  | +	memset(license, 0, sizeof(license)); | ||||||
|  | +	memset(processed_sec, 0, sizeof(processed_sec)); | ||||||
|  | + | ||||||
|  |  	if (elf_version(EV_CURRENT) == EV_NONE) | ||||||
|  |  		return 1; | ||||||
|  |   | ||||||
|  | --  | ||||||
|  | 2.11.0 | ||||||
|  |  | ||||||
| @@ -0,0 +1,640 @@ | |||||||
|  | From 0ecb458868b0902aa40d583a24819b97998c5555 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= <mic@digikod.net> | ||||||
|  | Date: Wed, 29 Mar 2017 01:30:33 +0200 | ||||||
|  | Subject: [PATCH 05/12] bpf: Add eBPF program subtype and is_valid_subtype() | ||||||
|  |  verifier | ||||||
|  | MIME-Version: 1.0 | ||||||
|  | Content-Type: text/plain; charset=UTF-8 | ||||||
|  | Content-Transfer-Encoding: 8bit | ||||||
|  |  | ||||||
|  | The goal of the program subtype is to be able to have different static | ||||||
|  | fine-grained verifications for a unique program type. | ||||||
|  |  | ||||||
|  | The struct bpf_verifier_ops gets a new optional function: | ||||||
|  | is_valid_subtype(). This new verifier is called at the beginning of the | ||||||
|  | eBPF program verification to check if the (optional) program subtype is | ||||||
|  | valid. | ||||||
|  |  | ||||||
|  | For now, only Landlock eBPF programs are using a program subtype (see | ||||||
|  | next commit) but this could be used by other program types in the future. | ||||||
|  |  | ||||||
|  | Changes since v5: | ||||||
|  | * use a prog_subtype pointer and make it future-proof | ||||||
|  | * add subtype test | ||||||
|  | * constify bpf_load_program()'s subtype argument | ||||||
|  | * cleanup subtype initialization | ||||||
|  | * rebase | ||||||
|  |  | ||||||
|  | Changes since v4: | ||||||
|  | * replace the "status" field with "version" (more generic) | ||||||
|  | * replace the "access" field with "ability" (less confusing) | ||||||
|  |  | ||||||
|  | Changes since v3: | ||||||
|  | * remove the "origin" field | ||||||
|  | * add an "option" field | ||||||
|  | * cleanup comments | ||||||
|  |  | ||||||
|  | Signed-off-by: Mickaël Salaün <mic@digikod.net> | ||||||
|  | Cc: Alexei Starovoitov <ast@kernel.org> | ||||||
|  | Cc: Arnaldo Carvalho de Melo <acme@kernel.org> | ||||||
|  | Cc: Daniel Borkmann <daniel@iogearbox.net> | ||||||
|  | Cc: David S. Miller <davem@davemloft.net> | ||||||
|  | Link: https://lkml.kernel.org/r/20160827205559.GA43880@ast-mbp.thefacebook.com | ||||||
|  | (cherry picked from commit 173f32497bc24bc1e3379a0050e0ca603fb2922d) | ||||||
|  | --- | ||||||
|  |  include/linux/bpf.h            |  7 +++- | ||||||
|  |  include/linux/filter.h         |  2 + | ||||||
|  |  include/uapi/linux/bpf.h       | 11 +++++ | ||||||
|  |  kernel/bpf/syscall.c           | 92 ++++++++++++++++++++++++++++-------------- | ||||||
|  |  kernel/bpf/verifier.c          | 14 ++++++- | ||||||
|  |  kernel/trace/bpf_trace.c       | 15 ++++--- | ||||||
|  |  net/core/filter.c              | 22 ++++++---- | ||||||
|  |  samples/bpf/bpf_load.c         |  3 +- | ||||||
|  |  samples/bpf/fds_example.c      |  2 +- | ||||||
|  |  samples/bpf/libbpf.c           |  7 +++- | ||||||
|  |  samples/bpf/libbpf.h           |  3 +- | ||||||
|  |  samples/bpf/sock_example.c     |  2 +- | ||||||
|  |  samples/bpf/test_verifier.c    |  2 +- | ||||||
|  |  tools/include/uapi/linux/bpf.h | 11 +++++ | ||||||
|  |  tools/lib/bpf/bpf.c            |  5 ++- | ||||||
|  |  tools/lib/bpf/bpf.h            |  2 +- | ||||||
|  |  tools/lib/bpf/libbpf.c         |  4 +- | ||||||
|  |  tools/perf/tests/bpf.c         |  2 +- | ||||||
|  |  18 files changed, 147 insertions(+), 59 deletions(-) | ||||||
|  |  | ||||||
|  | diff --git a/include/linux/bpf.h b/include/linux/bpf.h | ||||||
|  | index c201017b5730..d4b9ca479f79 100644 | ||||||
|  | --- a/include/linux/bpf.h | ||||||
|  | +++ b/include/linux/bpf.h | ||||||
|  | @@ -152,18 +152,21 @@ struct bpf_prog; | ||||||
|  |   | ||||||
|  |  struct bpf_verifier_ops { | ||||||
|  |  	/* return eBPF function prototype for verification */ | ||||||
|  | -	const struct bpf_func_proto *(*get_func_proto)(enum bpf_func_id func_id); | ||||||
|  | +	const struct bpf_func_proto *(*get_func_proto)(enum bpf_func_id func_id, | ||||||
|  | +				      union bpf_prog_subtype *prog_subtype); | ||||||
|  |   | ||||||
|  |  	/* return true if 'size' wide access at offset 'off' within bpf_context | ||||||
|  |  	 * with 'type' (read or write) is allowed | ||||||
|  |  	 */ | ||||||
|  |  	bool (*is_valid_access)(int off, int size, enum bpf_access_type type, | ||||||
|  | -				enum bpf_reg_type *reg_type); | ||||||
|  | +				enum bpf_reg_type *reg_type, | ||||||
|  | +				union bpf_prog_subtype *prog_subtype); | ||||||
|  |  	int (*gen_prologue)(struct bpf_insn *insn, bool direct_write, | ||||||
|  |  			    const struct bpf_prog *prog); | ||||||
|  |  	u32 (*convert_ctx_access)(enum bpf_access_type type, int dst_reg, | ||||||
|  |  				  int src_reg, int ctx_off, | ||||||
|  |  				  struct bpf_insn *insn, struct bpf_prog *prog); | ||||||
|  | +	bool (*is_valid_subtype)(union bpf_prog_subtype *prog_subtype); | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  struct bpf_prog_type_list { | ||||||
|  | diff --git a/include/linux/filter.h b/include/linux/filter.h | ||||||
|  | index 1f09c521adfe..782a271bf54e 100644 | ||||||
|  | --- a/include/linux/filter.h | ||||||
|  | +++ b/include/linux/filter.h | ||||||
|  | @@ -406,6 +406,8 @@ struct bpf_prog { | ||||||
|  |  	kmemcheck_bitfield_end(meta); | ||||||
|  |  	u32			len;		/* Number of filter blocks */ | ||||||
|  |  	enum bpf_prog_type	type;		/* Type of BPF program */ | ||||||
|  | +	u8			has_subtype; | ||||||
|  | +	union bpf_prog_subtype	subtype;	/* Fine-grained verifications */ | ||||||
|  |  	struct bpf_prog_aux	*aux;		/* Auxiliary fields */ | ||||||
|  |  	struct sock_fprog_kern	*orig_prog;	/* Original BPF program */ | ||||||
|  |  	unsigned int		(*bpf_func)(const struct sk_buff *skb, | ||||||
|  | diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h | ||||||
|  | index f09c70b97eca..a203fbcb0b2d 100644 | ||||||
|  | --- a/include/uapi/linux/bpf.h | ||||||
|  | +++ b/include/uapi/linux/bpf.h | ||||||
|  | @@ -107,6 +107,15 @@ enum bpf_prog_type { | ||||||
|  |   | ||||||
|  |  #define BPF_F_NO_PREALLOC	(1U << 0) | ||||||
|  |   | ||||||
|  | +union bpf_prog_subtype { | ||||||
|  | +	struct { | ||||||
|  | +		__u32		version; /* cf. documentation */ | ||||||
|  | +		__u32		event; /* enum landlock_subtype_event */ | ||||||
|  | +		__aligned_u64	ability; /* LANDLOCK_SUBTYPE_ABILITY_* */ | ||||||
|  | +		__aligned_u64	option; /* LANDLOCK_SUBTYPE_OPTION_* */ | ||||||
|  | +	} landlock_rule; | ||||||
|  | +} __attribute__((aligned(8))); | ||||||
|  | + | ||||||
|  |  union bpf_attr { | ||||||
|  |  	struct { /* anonymous struct used by BPF_MAP_CREATE command */ | ||||||
|  |  		__u32	map_type;	/* one of enum bpf_map_type */ | ||||||
|  | @@ -135,6 +144,8 @@ union bpf_attr { | ||||||
|  |  		__u32		log_size;	/* size of user buffer */ | ||||||
|  |  		__aligned_u64	log_buf;	/* user supplied buffer */ | ||||||
|  |  		__u32		kern_version;	/* checked when prog_type=kprobe */ | ||||||
|  | +		__aligned_u64	prog_subtype;	/* bpf_prog_subtype address */ | ||||||
|  | +		__u32		prog_subtype_size; | ||||||
|  |  	}; | ||||||
|  |   | ||||||
|  |  	struct { /* anonymous struct used by BPF_OBJ_* commands */ | ||||||
|  | diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c | ||||||
|  | index 237f3d6a7ddc..17bbd1af517f 100644 | ||||||
|  | --- a/kernel/bpf/syscall.c | ||||||
|  | +++ b/kernel/bpf/syscall.c | ||||||
|  | @@ -580,7 +580,8 @@ static void fixup_bpf_calls(struct bpf_prog *prog) | ||||||
|  |  				continue; | ||||||
|  |  			} | ||||||
|  |   | ||||||
|  | -			fn = prog->aux->ops->get_func_proto(insn->imm); | ||||||
|  | +			fn = prog->aux->ops->get_func_proto(insn->imm, | ||||||
|  | +							    &prog->subtype); | ||||||
|  |  			/* all functions that have prototype and verifier allowed | ||||||
|  |  			 * programs to call them, must be real in-kernel functions | ||||||
|  |  			 */ | ||||||
|  | @@ -717,8 +718,44 @@ struct bpf_prog *bpf_prog_get_type(u32 ufd, enum bpf_prog_type type) | ||||||
|  |  } | ||||||
|  |  EXPORT_SYMBOL_GPL(bpf_prog_get_type); | ||||||
|  |   | ||||||
|  | +static int check_user_buf(void __user *uptr, unsigned int size_req, | ||||||
|  | +			  unsigned int size_max) | ||||||
|  | +{ | ||||||
|  | +	if (!access_ok(VERIFY_READ, uptr, 1)) | ||||||
|  | +		return -EFAULT; | ||||||
|  | + | ||||||
|  | +	if (size_req > PAGE_SIZE)	/* silly large */ | ||||||
|  | +		return -E2BIG; | ||||||
|  | + | ||||||
|  | +	/* If we're handed a bigger struct than we know of, | ||||||
|  | +	 * ensure all the unknown bits are 0 - i.e. new | ||||||
|  | +	 * user-space does not rely on any kernel feature | ||||||
|  | +	 * extensions we dont know about yet. | ||||||
|  | +	 */ | ||||||
|  | +	if (size_req > size_max) { | ||||||
|  | +		unsigned char __user *addr; | ||||||
|  | +		unsigned char __user *end; | ||||||
|  | +		unsigned char val; | ||||||
|  | +		int err; | ||||||
|  | + | ||||||
|  | +		addr = uptr + size_max; | ||||||
|  | +		end  = uptr + size_req; | ||||||
|  | + | ||||||
|  | +		for (; addr < end; addr++) { | ||||||
|  | +			err = get_user(val, addr); | ||||||
|  | +			if (err) | ||||||
|  | +				return err; | ||||||
|  | +			if (val) | ||||||
|  | +				return -E2BIG; | ||||||
|  | +		} | ||||||
|  | +		return size_max; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	return size_req; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  |  /* last field in 'union bpf_attr' used by this command */ | ||||||
|  | -#define	BPF_PROG_LOAD_LAST_FIELD kern_version | ||||||
|  | +#define	BPF_PROG_LOAD_LAST_FIELD prog_subtype_size | ||||||
|  |   | ||||||
|  |  static int bpf_prog_load(union bpf_attr *attr) | ||||||
|  |  { | ||||||
|  | @@ -777,6 +814,26 @@ static int bpf_prog_load(union bpf_attr *attr) | ||||||
|  |  	if (err < 0) | ||||||
|  |  		goto free_prog; | ||||||
|  |   | ||||||
|  | +	/* copy eBPF program subtype from user space */ | ||||||
|  | +	if (attr->prog_subtype) { | ||||||
|  | +		__u32 size; | ||||||
|  | + | ||||||
|  | +		size = check_user_buf((void __user *)attr->prog_subtype, | ||||||
|  | +				      attr->prog_subtype_size, | ||||||
|  | +				      sizeof(prog->subtype)); | ||||||
|  | +		if (size < 0) { | ||||||
|  | +			err = size; | ||||||
|  | +			goto free_prog; | ||||||
|  | +		} | ||||||
|  | +		/* prog->subtype is __GFP_ZERO */ | ||||||
|  | +		if (copy_from_user(&prog->subtype, | ||||||
|  | +				   u64_to_user_ptr(attr->prog_subtype), size) | ||||||
|  | +				   != 0) | ||||||
|  | +			return -EFAULT; | ||||||
|  | +		prog->has_subtype = 1; | ||||||
|  | +	} else if (attr->prog_subtype_size != 0) | ||||||
|  | +		return -EINVAL; | ||||||
|  | + | ||||||
|  |  	/* run eBPF verifier */ | ||||||
|  |  	err = bpf_check(&prog, attr); | ||||||
|  |  	if (err < 0) | ||||||
|  | @@ -832,34 +889,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz | ||||||
|  |  	if (!capable(CAP_SYS_ADMIN) && sysctl_unprivileged_bpf_disabled) | ||||||
|  |  		return -EPERM; | ||||||
|  |   | ||||||
|  | -	if (!access_ok(VERIFY_READ, uattr, 1)) | ||||||
|  | -		return -EFAULT; | ||||||
|  | - | ||||||
|  | -	if (size > PAGE_SIZE)	/* silly large */ | ||||||
|  | -		return -E2BIG; | ||||||
|  | - | ||||||
|  | -	/* If we're handed a bigger struct than we know of, | ||||||
|  | -	 * ensure all the unknown bits are 0 - i.e. new | ||||||
|  | -	 * user-space does not rely on any kernel feature | ||||||
|  | -	 * extensions we dont know about yet. | ||||||
|  | -	 */ | ||||||
|  | -	if (size > sizeof(attr)) { | ||||||
|  | -		unsigned char __user *addr; | ||||||
|  | -		unsigned char __user *end; | ||||||
|  | -		unsigned char val; | ||||||
|  | - | ||||||
|  | -		addr = (void __user *)uattr + sizeof(attr); | ||||||
|  | -		end  = (void __user *)uattr + size; | ||||||
|  | - | ||||||
|  | -		for (; addr < end; addr++) { | ||||||
|  | -			err = get_user(val, addr); | ||||||
|  | -			if (err) | ||||||
|  | -				return err; | ||||||
|  | -			if (val) | ||||||
|  | -				return -E2BIG; | ||||||
|  | -		} | ||||||
|  | -		size = sizeof(attr); | ||||||
|  | -	} | ||||||
|  | +	size = check_user_buf((void __user *)uattr, size, sizeof(attr)); | ||||||
|  | +	if (size < 0) | ||||||
|  | +		return size; | ||||||
|  |   | ||||||
|  |  	/* copy attributes from user space, may be less than sizeof(bpf_attr) */ | ||||||
|  |  	if (copy_from_user(&attr, uattr, size) != 0) | ||||||
|  | diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c | ||||||
|  | index 85d1c9423ccb..f5f082de2f7f 100644 | ||||||
|  | --- a/kernel/bpf/verifier.c | ||||||
|  | +++ b/kernel/bpf/verifier.c | ||||||
|  | @@ -660,7 +660,8 @@ static int check_ctx_access(struct bpf_verifier_env *env, int off, int size, | ||||||
|  |  		return 0; | ||||||
|  |   | ||||||
|  |  	if (env->prog->aux->ops->is_valid_access && | ||||||
|  | -	    env->prog->aux->ops->is_valid_access(off, size, t, reg_type)) { | ||||||
|  | +	    env->prog->aux->ops->is_valid_access(off, size, t, reg_type, | ||||||
|  | +						 &env->prog->subtype)) { | ||||||
|  |  		/* remember the offset of last byte accessed in ctx */ | ||||||
|  |  		if (env->prog->aux->max_ctx_offset < off + size) | ||||||
|  |  			env->prog->aux->max_ctx_offset = off + size; | ||||||
|  | @@ -1182,7 +1183,8 @@ static int check_call(struct bpf_verifier_env *env, int func_id) | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  |  	if (env->prog->aux->ops->get_func_proto) | ||||||
|  | -		fn = env->prog->aux->ops->get_func_proto(func_id); | ||||||
|  | +		fn = env->prog->aux->ops->get_func_proto(func_id, | ||||||
|  | +							 &env->prog->subtype); | ||||||
|  |   | ||||||
|  |  	if (!fn) { | ||||||
|  |  		verbose("unknown func %d\n", func_id); | ||||||
|  | @@ -3116,6 +3118,14 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) | ||||||
|  |  	if ((*prog)->len <= 0 || (*prog)->len > BPF_MAXINSNS) | ||||||
|  |  		return -E2BIG; | ||||||
|  |   | ||||||
|  | +	if ((*prog)->aux->ops->is_valid_subtype) { | ||||||
|  | +		if (!(*prog)->aux->ops->is_valid_subtype(&(*prog)->subtype)) | ||||||
|  | +			return -EINVAL; | ||||||
|  | +	} else if ((*prog)->has_subtype) { | ||||||
|  | +		/* do not accept a subtype if the program does not handle it */ | ||||||
|  | +		return -EINVAL; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  |  	/* 'struct bpf_verifier_env' can be global, but since it's not small, | ||||||
|  |  	 * allocate/free it every time bpf_check() is called | ||||||
|  |  	 */ | ||||||
|  | diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c | ||||||
|  | index 5dcb99281259..653695fbc520 100644 | ||||||
|  | --- a/kernel/trace/bpf_trace.c | ||||||
|  | +++ b/kernel/trace/bpf_trace.c | ||||||
|  | @@ -435,7 +435,8 @@ static const struct bpf_func_proto *tracing_func_proto(enum bpf_func_id func_id) | ||||||
|  |  	} | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func_id) | ||||||
|  | +static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func_id, | ||||||
|  | +		union bpf_prog_subtype *prog_subtype) | ||||||
|  |  { | ||||||
|  |  	switch (func_id) { | ||||||
|  |  	case BPF_FUNC_perf_event_output: | ||||||
|  | @@ -449,7 +450,8 @@ static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func | ||||||
|  |   | ||||||
|  |  /* bpf+kprobe programs can access fields of 'struct pt_regs' */ | ||||||
|  |  static bool kprobe_prog_is_valid_access(int off, int size, enum bpf_access_type type, | ||||||
|  | -					enum bpf_reg_type *reg_type) | ||||||
|  | +					enum bpf_reg_type *reg_type, | ||||||
|  | +					union bpf_prog_subtype *prog_subtype) | ||||||
|  |  { | ||||||
|  |  	if (off < 0 || off >= sizeof(struct pt_regs)) | ||||||
|  |  		return false; | ||||||
|  | @@ -517,7 +519,8 @@ static const struct bpf_func_proto bpf_get_stackid_proto_tp = { | ||||||
|  |  	.arg3_type	= ARG_ANYTHING, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | -static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id) | ||||||
|  | +static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id, | ||||||
|  | +		union bpf_prog_subtype *prog_subtype) | ||||||
|  |  { | ||||||
|  |  	switch (func_id) { | ||||||
|  |  	case BPF_FUNC_perf_event_output: | ||||||
|  | @@ -530,7 +533,8 @@ static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id) | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static bool tp_prog_is_valid_access(int off, int size, enum bpf_access_type type, | ||||||
|  | -				    enum bpf_reg_type *reg_type) | ||||||
|  | +				    enum bpf_reg_type *reg_type, | ||||||
|  | +				    union bpf_prog_subtype *prog_subtype) | ||||||
|  |  { | ||||||
|  |  	if (off < sizeof(void *) || off >= PERF_MAX_TRACE_SIZE) | ||||||
|  |  		return false; | ||||||
|  | @@ -552,7 +556,8 @@ static struct bpf_prog_type_list tracepoint_tl = { | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  static bool pe_prog_is_valid_access(int off, int size, enum bpf_access_type type, | ||||||
|  | -				    enum bpf_reg_type *reg_type) | ||||||
|  | +				    enum bpf_reg_type *reg_type, | ||||||
|  | +				    union bpf_prog_subtype *prog_subtype) | ||||||
|  |  { | ||||||
|  |  	if (off < 0 || off >= sizeof(struct bpf_perf_event_data)) | ||||||
|  |  		return false; | ||||||
|  | diff --git a/net/core/filter.c b/net/core/filter.c | ||||||
|  | index b391209838ef..36a773c846b9 100644 | ||||||
|  | --- a/net/core/filter.c | ||||||
|  | +++ b/net/core/filter.c | ||||||
|  | @@ -2531,7 +2531,8 @@ static const struct bpf_func_proto bpf_xdp_event_output_proto = { | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  static const struct bpf_func_proto * | ||||||
|  | -sk_filter_func_proto(enum bpf_func_id func_id) | ||||||
|  | +sk_filter_func_proto(enum bpf_func_id func_id, | ||||||
|  | +		    union bpf_prog_subtype *prog_subtype) | ||||||
|  |  { | ||||||
|  |  	switch (func_id) { | ||||||
|  |  	case BPF_FUNC_map_lookup_elem: | ||||||
|  | @@ -2557,7 +2558,8 @@ sk_filter_func_proto(enum bpf_func_id func_id) | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static const struct bpf_func_proto * | ||||||
|  | -tc_cls_act_func_proto(enum bpf_func_id func_id) | ||||||
|  | +tc_cls_act_func_proto(enum bpf_func_id func_id, | ||||||
|  | +		      union bpf_prog_subtype *prog_subtype) | ||||||
|  |  { | ||||||
|  |  	switch (func_id) { | ||||||
|  |  	case BPF_FUNC_skb_store_bytes: | ||||||
|  | @@ -2611,12 +2613,13 @@ tc_cls_act_func_proto(enum bpf_func_id func_id) | ||||||
|  |  	case BPF_FUNC_skb_under_cgroup: | ||||||
|  |  		return &bpf_skb_under_cgroup_proto; | ||||||
|  |  	default: | ||||||
|  | -		return sk_filter_func_proto(func_id); | ||||||
|  | +		return sk_filter_func_proto(func_id, prog_subtype); | ||||||
|  |  	} | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  static const struct bpf_func_proto * | ||||||
|  | -xdp_func_proto(enum bpf_func_id func_id) | ||||||
|  | +xdp_func_proto(enum bpf_func_id func_id, | ||||||
|  | +	       union bpf_prog_subtype *prog_subtype) | ||||||
|  |  { | ||||||
|  |  	switch (func_id) { | ||||||
|  |  	case BPF_FUNC_perf_event_output: | ||||||
|  | @@ -2624,7 +2627,7 @@ xdp_func_proto(enum bpf_func_id func_id) | ||||||
|  |  	case BPF_FUNC_get_smp_processor_id: | ||||||
|  |  		return &bpf_get_smp_processor_id_proto; | ||||||
|  |  	default: | ||||||
|  | -		return sk_filter_func_proto(func_id); | ||||||
|  | +		return sk_filter_func_proto(func_id, prog_subtype); | ||||||
|  |  	} | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | @@ -2643,7 +2646,8 @@ static bool __is_valid_access(int off, int size, enum bpf_access_type type) | ||||||
|  |   | ||||||
|  |  static bool sk_filter_is_valid_access(int off, int size, | ||||||
|  |  				      enum bpf_access_type type, | ||||||
|  | -				      enum bpf_reg_type *reg_type) | ||||||
|  | +				      enum bpf_reg_type *reg_type, | ||||||
|  | +				      union bpf_prog_subtype *prog_subtype) | ||||||
|  |  { | ||||||
|  |  	switch (off) { | ||||||
|  |  	case offsetof(struct __sk_buff, tc_classid): | ||||||
|  | @@ -2706,7 +2710,8 @@ static int tc_cls_act_prologue(struct bpf_insn *insn_buf, bool direct_write, | ||||||
|  |   | ||||||
|  |  static bool tc_cls_act_is_valid_access(int off, int size, | ||||||
|  |  				       enum bpf_access_type type, | ||||||
|  | -				       enum bpf_reg_type *reg_type) | ||||||
|  | +				       enum bpf_reg_type *reg_type, | ||||||
|  | +				       union bpf_prog_subtype *prog_subtype) | ||||||
|  |  { | ||||||
|  |  	if (type == BPF_WRITE) { | ||||||
|  |  		switch (off) { | ||||||
|  | @@ -2749,7 +2754,8 @@ static bool __is_valid_xdp_access(int off, int size, | ||||||
|  |   | ||||||
|  |  static bool xdp_is_valid_access(int off, int size, | ||||||
|  |  				enum bpf_access_type type, | ||||||
|  | -				enum bpf_reg_type *reg_type) | ||||||
|  | +				enum bpf_reg_type *reg_type, | ||||||
|  | +				union bpf_prog_subtype *prog_subtype) | ||||||
|  |  { | ||||||
|  |  	if (type == BPF_WRITE) | ||||||
|  |  		return false; | ||||||
|  | diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c | ||||||
|  | index 765a6e45b92d..40cf828a37c7 100644 | ||||||
|  | --- a/samples/bpf/bpf_load.c | ||||||
|  | +++ b/samples/bpf/bpf_load.c | ||||||
|  | @@ -56,6 +56,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size) | ||||||
|  |  	char buf[256]; | ||||||
|  |  	int fd, efd, err, id; | ||||||
|  |  	struct perf_event_attr attr = {}; | ||||||
|  | +	union bpf_prog_subtype *st = NULL; | ||||||
|  |   | ||||||
|  |  	attr.type = PERF_TYPE_TRACEPOINT; | ||||||
|  |  	attr.sample_type = PERF_SAMPLE_RAW; | ||||||
|  | @@ -77,7 +78,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size) | ||||||
|  |  		return -1; | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | -	fd = bpf_prog_load(prog_type, prog, size, license, kern_version); | ||||||
|  | +	fd = bpf_prog_load(prog_type, prog, size, license, kern_version, st); | ||||||
|  |  	if (fd < 0) { | ||||||
|  |  		printf("bpf_prog_load() err=%d\n%s", errno, bpf_log_buf); | ||||||
|  |  		return -1; | ||||||
|  | diff --git a/samples/bpf/fds_example.c b/samples/bpf/fds_example.c | ||||||
|  | index 625e797be6ef..df38b68f3586 100644 | ||||||
|  | --- a/samples/bpf/fds_example.c | ||||||
|  | +++ b/samples/bpf/fds_example.c | ||||||
|  | @@ -59,7 +59,7 @@ static int bpf_prog_create(const char *object) | ||||||
|  |  		return prog_fd[0]; | ||||||
|  |  	} else { | ||||||
|  |  		return bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, | ||||||
|  | -				     insns, sizeof(insns), "GPL", 0); | ||||||
|  | +				     insns, sizeof(insns), "GPL", 0, NULL); | ||||||
|  |  	} | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | diff --git a/samples/bpf/libbpf.c b/samples/bpf/libbpf.c | ||||||
|  | index 9969e35550c3..b5a4275d13a2 100644 | ||||||
|  | --- a/samples/bpf/libbpf.c | ||||||
|  | +++ b/samples/bpf/libbpf.c | ||||||
|  | @@ -13,7 +13,7 @@ | ||||||
|  |  #include <arpa/inet.h> | ||||||
|  |  #include "libbpf.h" | ||||||
|  |   | ||||||
|  | -static __u64 ptr_to_u64(void *ptr) | ||||||
|  | +static __u64 ptr_to_u64(const void *ptr) | ||||||
|  |  { | ||||||
|  |  	return (__u64) (unsigned long) ptr; | ||||||
|  |  } | ||||||
|  | @@ -82,7 +82,8 @@ char bpf_log_buf[LOG_BUF_SIZE]; | ||||||
|  |   | ||||||
|  |  int bpf_prog_load(enum bpf_prog_type prog_type, | ||||||
|  |  		  const struct bpf_insn *insns, int prog_len, | ||||||
|  | -		  const char *license, int kern_version) | ||||||
|  | +		  const char *license, int kern_version, | ||||||
|  | +		  const union bpf_prog_subtype *subtype) | ||||||
|  |  { | ||||||
|  |  	union bpf_attr attr = { | ||||||
|  |  		.prog_type = prog_type, | ||||||
|  | @@ -92,6 +93,8 @@ int bpf_prog_load(enum bpf_prog_type prog_type, | ||||||
|  |  		.log_buf = ptr_to_u64(bpf_log_buf), | ||||||
|  |  		.log_size = LOG_BUF_SIZE, | ||||||
|  |  		.log_level = 1, | ||||||
|  | +		.prog_subtype = ptr_to_u64(subtype), | ||||||
|  | +		.prog_subtype_size = subtype ? sizeof(*subtype) : 0, | ||||||
|  |  	}; | ||||||
|  |   | ||||||
|  |  	/* assign one field outside of struct init to make sure any | ||||||
|  | diff --git a/samples/bpf/libbpf.h b/samples/bpf/libbpf.h | ||||||
|  | index ac6edb61b64a..56a86b847544 100644 | ||||||
|  | --- a/samples/bpf/libbpf.h | ||||||
|  | +++ b/samples/bpf/libbpf.h | ||||||
|  | @@ -13,7 +13,8 @@ int bpf_get_next_key(int fd, void *key, void *next_key); | ||||||
|  |   | ||||||
|  |  int bpf_prog_load(enum bpf_prog_type prog_type, | ||||||
|  |  		  const struct bpf_insn *insns, int insn_len, | ||||||
|  | -		  const char *license, int kern_version); | ||||||
|  | +		  const char *license, int kern_version, | ||||||
|  | +		  const union bpf_prog_subtype *subtype); | ||||||
|  |   | ||||||
|  |  int bpf_obj_pin(int fd, const char *pathname); | ||||||
|  |  int bpf_obj_get(const char *pathname); | ||||||
|  | diff --git a/samples/bpf/sock_example.c b/samples/bpf/sock_example.c | ||||||
|  | index 28b60baa9fa8..521f918ab34d 100644 | ||||||
|  | --- a/samples/bpf/sock_example.c | ||||||
|  | +++ b/samples/bpf/sock_example.c | ||||||
|  | @@ -56,7 +56,7 @@ static int test_sock(void) | ||||||
|  |  	}; | ||||||
|  |   | ||||||
|  |  	prog_fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, prog, sizeof(prog), | ||||||
|  | -				"GPL", 0); | ||||||
|  | +				"GPL", 0, NULL); | ||||||
|  |  	if (prog_fd < 0) { | ||||||
|  |  		printf("failed to load prog '%s'\n", strerror(errno)); | ||||||
|  |  		goto cleanup; | ||||||
|  | diff --git a/samples/bpf/test_verifier.c b/samples/bpf/test_verifier.c | ||||||
|  | index 369ffaad3799..7a965da8ed2d 100644 | ||||||
|  | --- a/samples/bpf/test_verifier.c | ||||||
|  | +++ b/samples/bpf/test_verifier.c | ||||||
|  | @@ -2468,7 +2468,7 @@ static int test(void) | ||||||
|  |   | ||||||
|  |  		prog_fd = bpf_prog_load(prog_type ?: BPF_PROG_TYPE_SOCKET_FILTER, | ||||||
|  |  					prog, prog_len * sizeof(struct bpf_insn), | ||||||
|  | -					"GPL", 0); | ||||||
|  | +					"GPL", 0, NULL); | ||||||
|  |   | ||||||
|  |  		if (unpriv && tests[i].result_unpriv != UNDEF) | ||||||
|  |  			expected_result = tests[i].result_unpriv; | ||||||
|  | diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h | ||||||
|  | index 9e5fc168c8a3..aae3b82a673c 100644 | ||||||
|  | --- a/tools/include/uapi/linux/bpf.h | ||||||
|  | +++ b/tools/include/uapi/linux/bpf.h | ||||||
|  | @@ -106,6 +106,15 @@ enum bpf_prog_type { | ||||||
|  |   | ||||||
|  |  #define BPF_F_NO_PREALLOC	(1U << 0) | ||||||
|  |   | ||||||
|  | +union bpf_prog_subtype { | ||||||
|  | +	struct { | ||||||
|  | +		__u32		version; /* cf. documentation */ | ||||||
|  | +		__u32		event; /* enum landlock_subtype_event */ | ||||||
|  | +		__aligned_u64	ability; /* LANDLOCK_SUBTYPE_ABILITY_* */ | ||||||
|  | +		__aligned_u64	option; /* LANDLOCK_SUBTYPE_OPTION_* */ | ||||||
|  | +	} landlock_rule; | ||||||
|  | +} __attribute__((aligned(8))); | ||||||
|  | + | ||||||
|  |  union bpf_attr { | ||||||
|  |  	struct { /* anonymous struct used by BPF_MAP_CREATE command */ | ||||||
|  |  		__u32	map_type;	/* one of enum bpf_map_type */ | ||||||
|  | @@ -134,6 +143,8 @@ union bpf_attr { | ||||||
|  |  		__u32		log_size;	/* size of user buffer */ | ||||||
|  |  		__aligned_u64	log_buf;	/* user supplied buffer */ | ||||||
|  |  		__u32		kern_version;	/* checked when prog_type=kprobe */ | ||||||
|  | +		__aligned_u64	prog_subtype;	/* bpf_prog_subtype address */ | ||||||
|  | +		__u32		prog_subtype_size; | ||||||
|  |  	}; | ||||||
|  |   | ||||||
|  |  	struct { /* anonymous struct used by BPF_OBJ_* commands */ | ||||||
|  | diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c | ||||||
|  | index 4212ed62235b..57258decb4bd 100644 | ||||||
|  | --- a/tools/lib/bpf/bpf.c | ||||||
|  | +++ b/tools/lib/bpf/bpf.c | ||||||
|  | @@ -70,7 +70,8 @@ int bpf_create_map(enum bpf_map_type map_type, int key_size, | ||||||
|  |   | ||||||
|  |  int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns, | ||||||
|  |  		     size_t insns_cnt, char *license, | ||||||
|  | -		     u32 kern_version, char *log_buf, size_t log_buf_sz) | ||||||
|  | +		     u32 kern_version, char *log_buf, size_t log_buf_sz, | ||||||
|  | +		     const union bpf_prog_subtype *subtype) | ||||||
|  |  { | ||||||
|  |  	int fd; | ||||||
|  |  	union bpf_attr attr; | ||||||
|  | @@ -84,6 +85,8 @@ int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns, | ||||||
|  |  	attr.log_size = 0; | ||||||
|  |  	attr.log_level = 0; | ||||||
|  |  	attr.kern_version = kern_version; | ||||||
|  | +	attr.prog_subtype = ptr_to_u64(subtype); | ||||||
|  | +	attr.prog_subtype_size = subtype ? sizeof(*subtype) : 0; | ||||||
|  |   | ||||||
|  |  	fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); | ||||||
|  |  	if (fd >= 0 || !log_buf || !log_buf_sz) | ||||||
|  | diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h | ||||||
|  | index eb584e639500..8a772c817b87 100644 | ||||||
|  | --- a/tools/lib/bpf/bpf.h | ||||||
|  | +++ b/tools/lib/bpf/bpf.h | ||||||
|  | @@ -32,7 +32,7 @@ int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, | ||||||
|  |  int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns, | ||||||
|  |  		     size_t insns_cnt, char *license, | ||||||
|  |  		     u32 kern_version, char *log_buf, | ||||||
|  | -		     size_t log_buf_sz); | ||||||
|  | +		     size_t log_buf_sz, const union bpf_prog_subtype *subtype); | ||||||
|  |   | ||||||
|  |  int bpf_map_update_elem(int fd, void *key, void *value, | ||||||
|  |  			u64 flags); | ||||||
|  | diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c | ||||||
|  | index b699aea9a025..ea8c03a12c16 100644 | ||||||
|  | --- a/tools/lib/bpf/libbpf.c | ||||||
|  | +++ b/tools/lib/bpf/libbpf.c | ||||||
|  | @@ -916,7 +916,7 @@ load_program(enum bpf_prog_type type, struct bpf_insn *insns, | ||||||
|  |  		pr_warning("Alloc log buffer for bpf loader error, continue without log\n"); | ||||||
|  |   | ||||||
|  |  	ret = bpf_load_program(type, insns, insns_cnt, license, | ||||||
|  | -			       kern_version, log_buf, BPF_LOG_BUF_SIZE); | ||||||
|  | +			       kern_version, log_buf, BPF_LOG_BUF_SIZE, NULL); | ||||||
|  |   | ||||||
|  |  	if (ret >= 0) { | ||||||
|  |  		*pfd = ret; | ||||||
|  | @@ -943,7 +943,7 @@ load_program(enum bpf_prog_type type, struct bpf_insn *insns, | ||||||
|  |   | ||||||
|  |  			fd = bpf_load_program(BPF_PROG_TYPE_KPROBE, insns, | ||||||
|  |  					      insns_cnt, license, kern_version, | ||||||
|  | -					      NULL, 0); | ||||||
|  | +					      NULL, 0, NULL); | ||||||
|  |  			if (fd >= 0) { | ||||||
|  |  				close(fd); | ||||||
|  |  				ret = -LIBBPF_ERRNO__PROGTYPE; | ||||||
|  | diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c | ||||||
|  | index 2673e86ed50f..285b872b46a2 100644 | ||||||
|  | --- a/tools/perf/tests/bpf.c | ||||||
|  | +++ b/tools/perf/tests/bpf.c | ||||||
|  | @@ -266,7 +266,7 @@ static int check_env(void) | ||||||
|  |   | ||||||
|  |  	err = bpf_load_program(BPF_PROG_TYPE_KPROBE, insns, | ||||||
|  |  			       sizeof(insns) / sizeof(insns[0]), | ||||||
|  | -			       license, kver_int, NULL, 0); | ||||||
|  | +			       license, kver_int, NULL, 0, NULL); | ||||||
|  |  	if (err < 0) { | ||||||
|  |  		pr_err("Missing basic BPF support, skip this test: %s\n", | ||||||
|  |  		       strerror(errno)); | ||||||
|  | --  | ||||||
|  | 2.11.0 | ||||||
|  |  | ||||||
| @@ -0,0 +1,566 @@ | |||||||
|  | From 7136b7f6c5f451e4c1fd0db5beb69994a2b9a9f7 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= <mic@digikod.net> | ||||||
|  | Date: Wed, 29 Mar 2017 01:30:33 +0200 | ||||||
|  | Subject: [PATCH 06/12] bpf,landlock: Define an eBPF program type for Landlock | ||||||
|  | MIME-Version: 1.0 | ||||||
|  | Content-Type: text/plain; charset=UTF-8 | ||||||
|  | Content-Transfer-Encoding: 8bit | ||||||
|  |  | ||||||
|  | Add a new type of eBPF program used by Landlock rules. | ||||||
|  |  | ||||||
|  | This new BPF program type will be registered with the Landlock LSM | ||||||
|  | initialization. | ||||||
|  |  | ||||||
|  | Add an initial Landlock Kconfig. | ||||||
|  |  | ||||||
|  | Changes since v5: | ||||||
|  | * rename file hooks.c to init.c | ||||||
|  | * fix spelling | ||||||
|  |  | ||||||
|  | Changes since v4: | ||||||
|  | * merge a minimal (not enabled) LSM code and Kconfig in this commit | ||||||
|  |  | ||||||
|  | Changes since v3: | ||||||
|  | * split commit | ||||||
|  | * revamp the landlock_context: | ||||||
|  |   * add arch, syscall_nr and syscall_cmd (ioctl, fcntl…) to be able to | ||||||
|  |     cross-check action with the event type | ||||||
|  |   * replace args array with dedicated fields to ease the addition of new | ||||||
|  |     fields | ||||||
|  |  | ||||||
|  | Signed-off-by: Mickaël Salaün <mic@digikod.net> | ||||||
|  | Cc: Alexei Starovoitov <ast@kernel.org> | ||||||
|  | Cc: Andy Lutomirski <luto@amacapital.net> | ||||||
|  | Cc: Daniel Borkmann <daniel@iogearbox.net> | ||||||
|  | Cc: David S. Miller <davem@davemloft.net> | ||||||
|  | Cc: James Morris <james.l.morris@oracle.com> | ||||||
|  | Cc: Kees Cook <keescook@chromium.org> | ||||||
|  | Cc: Serge E. Hallyn <serge@hallyn.com> | ||||||
|  | (cherry picked from commit f2265894fff03038ec0a81dbcf68ee8d1bf7c33d) | ||||||
|  | --- | ||||||
|  |  include/linux/landlock.h       |  23 ++++++++ | ||||||
|  |  include/uapi/linux/bpf.h       | 110 ++++++++++++++++++++++++++++++++++++ | ||||||
|  |  security/Kconfig               |   1 + | ||||||
|  |  security/Makefile              |   2 + | ||||||
|  |  security/landlock/Kconfig      |  18 ++++++ | ||||||
|  |  security/landlock/Makefile     |   3 + | ||||||
|  |  security/landlock/common.h     |  25 +++++++++ | ||||||
|  |  security/landlock/init.c       | 123 +++++++++++++++++++++++++++++++++++++++++ | ||||||
|  |  tools/include/uapi/linux/bpf.h | 111 +++++++++++++++++++++++++++++++++++++ | ||||||
|  |  9 files changed, 416 insertions(+) | ||||||
|  |  create mode 100644 include/linux/landlock.h | ||||||
|  |  create mode 100644 security/landlock/Kconfig | ||||||
|  |  create mode 100644 security/landlock/Makefile | ||||||
|  |  create mode 100644 security/landlock/common.h | ||||||
|  |  create mode 100644 security/landlock/init.c | ||||||
|  |  | ||||||
|  | diff --git a/include/linux/landlock.h b/include/linux/landlock.h | ||||||
|  | new file mode 100644 | ||||||
|  | index 000000000000..53013dc374fe | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/include/linux/landlock.h | ||||||
|  | @@ -0,0 +1,23 @@ | ||||||
|  | +/* | ||||||
|  | + * Landlock LSM - public kernel headers | ||||||
|  | + * | ||||||
|  | + * Copyright © 2017 Mickaël Salaün <mic@digikod.net> | ||||||
|  | + * | ||||||
|  | + * This program is free software; you can redistribute it and/or modify | ||||||
|  | + * it under the terms of the GNU General Public License version 2, as | ||||||
|  | + * published by the Free Software Foundation. | ||||||
|  | + */ | ||||||
|  | + | ||||||
|  | +#ifndef _LINUX_LANDLOCK_H | ||||||
|  | +#define _LINUX_LANDLOCK_H | ||||||
|  | +#ifdef CONFIG_SECURITY_LANDLOCK | ||||||
|  | + | ||||||
|  | +/* | ||||||
|  | + * This is not intended for the UAPI headers. Each userland software should use | ||||||
|  | + * a static minimal version for the required features as explained in the | ||||||
|  | + * documentation. | ||||||
|  | + */ | ||||||
|  | +#define LANDLOCK_VERSION 1 | ||||||
|  | + | ||||||
|  | +#endif /* CONFIG_SECURITY_LANDLOCK */ | ||||||
|  | +#endif /* _LINUX_LANDLOCK_H */ | ||||||
|  | diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h | ||||||
|  | index a203fbcb0b2d..f190be4f609f 100644 | ||||||
|  | --- a/include/uapi/linux/bpf.h | ||||||
|  | +++ b/include/uapi/linux/bpf.h | ||||||
|  | @@ -96,6 +96,12 @@ enum bpf_prog_type { | ||||||
|  |  	BPF_PROG_TYPE_TRACEPOINT, | ||||||
|  |  	BPF_PROG_TYPE_XDP, | ||||||
|  |  	BPF_PROG_TYPE_PERF_EVENT, | ||||||
|  | +	BPF_PROG_TYPE_CGROUP_SKB, | ||||||
|  | +	BPF_PROG_TYPE_CGROUP_SOCK, | ||||||
|  | +	BPF_PROG_TYPE_LWT_IN, | ||||||
|  | +	BPF_PROG_TYPE_LWT_OUT, | ||||||
|  | +	BPF_PROG_TYPE_LWT_XMIT, | ||||||
|  | +	BPF_PROG_TYPE_LANDLOCK, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  #define BPF_PSEUDO_MAP_FD	1 | ||||||
|  | @@ -532,4 +538,108 @@ struct xdp_md { | ||||||
|  |  	__u32 data_end; | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | +/** | ||||||
|  | + * enum landlock_subtype_event - event occurring when an action is performed on | ||||||
|  | + * a particular kernel object | ||||||
|  | + * | ||||||
|  | + * An event is a policy decision point which exposes the same context type | ||||||
|  | + * (especially the same arg[0-9] field types) for each rule execution. | ||||||
|  | + * | ||||||
|  | + * @LANDLOCK_SUBTYPE_EVENT_UNSPEC: invalid value | ||||||
|  | + * @LANDLOCK_SUBTYPE_EVENT_FS: generic filesystem event | ||||||
|  | + */ | ||||||
|  | +enum landlock_subtype_event { | ||||||
|  | +	LANDLOCK_SUBTYPE_EVENT_UNSPEC, | ||||||
|  | +	LANDLOCK_SUBTYPE_EVENT_FS, | ||||||
|  | +}; | ||||||
|  | +#define _LANDLOCK_SUBTYPE_EVENT_LAST LANDLOCK_SUBTYPE_EVENT_FS | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * DOC: landlock_subtype_access | ||||||
|  | + * | ||||||
|  | + * eBPF context and functions allowed for a rule | ||||||
|  | + * | ||||||
|  | + * - LANDLOCK_SUBTYPE_ABILITY_WRITE: allows to directly send notification to | ||||||
|  | + *   userland (e.g. through a map), which may leaks sensitive information | ||||||
|  | + * - LANDLOCK_SUBTYPE_ABILITY_DEBUG: allows to do debug actions (e.g. writing | ||||||
|  | + *   logs), which may be dangerous and should only be used for rule testing | ||||||
|  | + */ | ||||||
|  | +#define LANDLOCK_SUBTYPE_ABILITY_WRITE		(1ULL << 0) | ||||||
|  | +#define LANDLOCK_SUBTYPE_ABILITY_DEBUG		(1ULL << 1) | ||||||
|  | +#define _LANDLOCK_SUBTYPE_ABILITY_NB		2 | ||||||
|  | +#define _LANDLOCK_SUBTYPE_ABILITY_MASK		((1ULL << _LANDLOCK_SUBTYPE_ABILITY_NB) - 1) | ||||||
|  | + | ||||||
|  | +/* | ||||||
|  | + * Future options for a Landlock rule (e.g. run even if a previous rule denied | ||||||
|  | + * an action). | ||||||
|  | + */ | ||||||
|  | +#define _LANDLOCK_SUBTYPE_OPTION_NB		0 | ||||||
|  | +#define _LANDLOCK_SUBTYPE_OPTION_MASK		((1ULL << _LANDLOCK_SUBTYPE_OPTION_NB) - 1) | ||||||
|  | + | ||||||
|  | +/* | ||||||
|  | + * Status visible in the @status field of a context (e.g. already called in | ||||||
|  | + * this syscall session, with same args...). | ||||||
|  | + * | ||||||
|  | + * The @status field exposed to a rule shall depend on the rule version. | ||||||
|  | + */ | ||||||
|  | +#define _LANDLOCK_SUBTYPE_STATUS_NB		0 | ||||||
|  | +#define _LANDLOCK_SUBTYPE_STATUS_MASK		((1ULL << _LANDLOCK_SUBTYPE_STATUS_NB) - 1) | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * DOC: landlock_action_fs | ||||||
|  | + * | ||||||
|  | + * - %LANDLOCK_ACTION_FS_EXEC: execute a file or walk through a directory | ||||||
|  | + * - %LANDLOCK_ACTION_FS_WRITE: modify a file or a directory view (which | ||||||
|  | + *   include mount actions) | ||||||
|  | + * - %LANDLOCK_ACTION_FS_READ: read a file or a directory | ||||||
|  | + * - %LANDLOCK_ACTION_FS_NEW: create a file or a directory | ||||||
|  | + * - %LANDLOCK_ACTION_FS_GET: open or receive a file | ||||||
|  | + * - %LANDLOCK_ACTION_FS_REMOVE: unlink a file or remove a directory | ||||||
|  | + * | ||||||
|  | + * Each of the following actions are specific to syscall multiplexers. They | ||||||
|  | + * fill the syscall_cmd field from &struct landlock_context with their custom | ||||||
|  | + * command. | ||||||
|  | + * | ||||||
|  | + * - %LANDLOCK_ACTION_FS_IOCTL: ioctl command | ||||||
|  | + * - %LANDLOCK_ACTION_FS_LOCK: flock or fcntl lock command | ||||||
|  | + * - %LANDLOCK_ACTION_FS_FCNTL: fcntl command | ||||||
|  | + */ | ||||||
|  | +#define LANDLOCK_ACTION_FS_EXEC			(1ULL << 0) | ||||||
|  | +#define LANDLOCK_ACTION_FS_WRITE		(1ULL << 1) | ||||||
|  | +#define LANDLOCK_ACTION_FS_READ			(1ULL << 2) | ||||||
|  | +#define LANDLOCK_ACTION_FS_NEW			(1ULL << 3) | ||||||
|  | +#define LANDLOCK_ACTION_FS_GET			(1ULL << 4) | ||||||
|  | +#define LANDLOCK_ACTION_FS_REMOVE		(1ULL << 5) | ||||||
|  | +#define LANDLOCK_ACTION_FS_IOCTL		(1ULL << 6) | ||||||
|  | +#define LANDLOCK_ACTION_FS_LOCK			(1ULL << 7) | ||||||
|  | +#define LANDLOCK_ACTION_FS_FCNTL		(1ULL << 8) | ||||||
|  | +#define _LANDLOCK_ACTION_FS_NB			9 | ||||||
|  | +#define _LANDLOCK_ACTION_FS_MASK		((1ULL << _LANDLOCK_ACTION_FS_NB) - 1) | ||||||
|  | + | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * struct landlock_context - context accessible to a Landlock rule | ||||||
|  | + * | ||||||
|  | + * @status: bitfield for future use (LANDLOCK_SUBTYPE_STATUS_*) | ||||||
|  | + * @arch: indicates system call convention as an AUDIT_ARCH_* value | ||||||
|  | + *        as defined in <linux/audit.h> | ||||||
|  | + * @syscall_nr: the system call number called by the current process (may be | ||||||
|  | + *              useful to debug: find out from which syscall this request came | ||||||
|  | + *              from) | ||||||
|  | + * @syscall_cmd: contains the command used by a multiplexer syscall (e.g. | ||||||
|  | + *               ioctl, fcntl, flock) | ||||||
|  | + * @event: event type (&enum landlock_subtype_event) | ||||||
|  | + * @arg1: event's first optional argument | ||||||
|  | + * @arg2: event's second optional argument | ||||||
|  | + */ | ||||||
|  | +struct landlock_context { | ||||||
|  | +	__u64 status; | ||||||
|  | +	__u32 arch; | ||||||
|  | +	__u32 syscall_nr; | ||||||
|  | +	__u32 syscall_cmd; | ||||||
|  | +	__u32 event; | ||||||
|  | +	__u64 arg1; | ||||||
|  | +	__u64 arg2; | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  |  #endif /* _UAPI__LINUX_BPF_H__ */ | ||||||
|  | diff --git a/security/Kconfig b/security/Kconfig | ||||||
|  | index 118f4549404e..c63194c561c5 100644 | ||||||
|  | --- a/security/Kconfig | ||||||
|  | +++ b/security/Kconfig | ||||||
|  | @@ -164,6 +164,7 @@ source security/tomoyo/Kconfig | ||||||
|  |  source security/apparmor/Kconfig | ||||||
|  |  source security/loadpin/Kconfig | ||||||
|  |  source security/yama/Kconfig | ||||||
|  | +source security/landlock/Kconfig | ||||||
|  |   | ||||||
|  |  source security/integrity/Kconfig | ||||||
|  |   | ||||||
|  | diff --git a/security/Makefile b/security/Makefile | ||||||
|  | index f2d71cdb8e19..3fdc2f19dc48 100644 | ||||||
|  | --- a/security/Makefile | ||||||
|  | +++ b/security/Makefile | ||||||
|  | @@ -9,6 +9,7 @@ subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo | ||||||
|  |  subdir-$(CONFIG_SECURITY_APPARMOR)	+= apparmor | ||||||
|  |  subdir-$(CONFIG_SECURITY_YAMA)		+= yama | ||||||
|  |  subdir-$(CONFIG_SECURITY_LOADPIN)	+= loadpin | ||||||
|  | +subdir-$(CONFIG_SECURITY_LANDLOCK)		+= landlock | ||||||
|  |   | ||||||
|  |  # always enable default capabilities | ||||||
|  |  obj-y					+= commoncap.o | ||||||
|  | @@ -24,6 +25,7 @@ obj-$(CONFIG_SECURITY_TOMOYO)		+= tomoyo/ | ||||||
|  |  obj-$(CONFIG_SECURITY_APPARMOR)		+= apparmor/ | ||||||
|  |  obj-$(CONFIG_SECURITY_YAMA)		+= yama/ | ||||||
|  |  obj-$(CONFIG_SECURITY_LOADPIN)		+= loadpin/ | ||||||
|  | +obj-$(CONFIG_SECURITY_LANDLOCK)	+= landlock/ | ||||||
|  |  obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o | ||||||
|  |   | ||||||
|  |  # Object integrity file lists | ||||||
|  | diff --git a/security/landlock/Kconfig b/security/landlock/Kconfig | ||||||
|  | new file mode 100644 | ||||||
|  | index 000000000000..aa5808e116f1 | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/security/landlock/Kconfig | ||||||
|  | @@ -0,0 +1,18 @@ | ||||||
|  | +config SECURITY_LANDLOCK | ||||||
|  | +	bool "Landlock sandbox support" | ||||||
|  | +	depends on SECURITY | ||||||
|  | +	depends on BPF_SYSCALL | ||||||
|  | +	depends on SECCOMP_FILTER | ||||||
|  | +	default y | ||||||
|  | +	help | ||||||
|  | +	  Landlock is a stackable LSM which allows to load a security policy to | ||||||
|  | +	  restrict processes (i.e. create a sandbox). The policy is a list of | ||||||
|  | +	  stacked eBPF programs, called rules, dedicated to restrict access to | ||||||
|  | +	  a type of kernel object (e.g. file). | ||||||
|  | + | ||||||
|  | +	  You need to enable seccomp filter to apply a security policy to a | ||||||
|  | +	  process hierarchy (e.g. application with built-in sandboxing). | ||||||
|  | + | ||||||
|  | +	  See Documentation/security/landlock/ for further information. | ||||||
|  | + | ||||||
|  | +	  If you are unsure how to answer this question, answer Y. | ||||||
|  | diff --git a/security/landlock/Makefile b/security/landlock/Makefile | ||||||
|  | new file mode 100644 | ||||||
|  | index 000000000000..7205f9a7a2ee | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/security/landlock/Makefile | ||||||
|  | @@ -0,0 +1,3 @@ | ||||||
|  | +obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o | ||||||
|  | + | ||||||
|  | +landlock-y := init.o | ||||||
|  | diff --git a/security/landlock/common.h b/security/landlock/common.h | ||||||
|  | new file mode 100644 | ||||||
|  | index 000000000000..a2483405349f | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/security/landlock/common.h | ||||||
|  | @@ -0,0 +1,25 @@ | ||||||
|  | +/* | ||||||
|  | + * Landlock LSM - private headers | ||||||
|  | + * | ||||||
|  | + * Copyright © 2017 Mickaël Salaün <mic@digikod.net> | ||||||
|  | + * | ||||||
|  | + * This program is free software; you can redistribute it and/or modify | ||||||
|  | + * it under the terms of the GNU General Public License version 2, as | ||||||
|  | + * published by the Free Software Foundation. | ||||||
|  | + */ | ||||||
|  | + | ||||||
|  | +#ifndef _SECURITY_LANDLOCK_COMMON_H | ||||||
|  | +#define _SECURITY_LANDLOCK_COMMON_H | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * get_index - get an index for the rules of struct landlock_events | ||||||
|  | + * | ||||||
|  | + * @event: a Landlock event type | ||||||
|  | + */ | ||||||
|  | +static inline int get_index(enum landlock_subtype_event event) | ||||||
|  | +{ | ||||||
|  | +	/* event ID > 0 for loaded programs */ | ||||||
|  | +	return event - 1; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +#endif /* _SECURITY_LANDLOCK_COMMON_H */ | ||||||
|  | diff --git a/security/landlock/init.c b/security/landlock/init.c | ||||||
|  | new file mode 100644 | ||||||
|  | index 000000000000..0a97026f1c07 | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/security/landlock/init.c | ||||||
|  | @@ -0,0 +1,123 @@ | ||||||
|  | +/* | ||||||
|  | + * Landlock LSM - init | ||||||
|  | + * | ||||||
|  | + * Copyright © 2017 Mickaël Salaün <mic@digikod.net> | ||||||
|  | + * | ||||||
|  | + * This program is free software; you can redistribute it and/or modify | ||||||
|  | + * it under the terms of the GNU General Public License version 2, as | ||||||
|  | + * published by the Free Software Foundation. | ||||||
|  | + */ | ||||||
|  | + | ||||||
|  | +#include <linux/bpf.h> /* enum bpf_access_type */ | ||||||
|  | +#include <linux/capability.h> /* capable */ | ||||||
|  | +#include <linux/landlock.h> /* LANDLOCK_VERSION */ | ||||||
|  | + | ||||||
|  | + | ||||||
|  | +static inline bool bpf_landlock_is_valid_access(int off, int size, | ||||||
|  | +		enum bpf_access_type type, enum bpf_reg_type *reg_type, | ||||||
|  | +		union bpf_prog_subtype *prog_subtype) | ||||||
|  | +{ | ||||||
|  | +	if (WARN_ON(!prog_subtype)) | ||||||
|  | +		return false; | ||||||
|  | + | ||||||
|  | +	switch (prog_subtype->landlock_rule.event) { | ||||||
|  | +	case LANDLOCK_SUBTYPE_EVENT_FS: | ||||||
|  | +	case LANDLOCK_SUBTYPE_EVENT_UNSPEC: | ||||||
|  | +	default: | ||||||
|  | +		return false; | ||||||
|  | +	} | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static inline bool bpf_landlock_is_valid_subtype( | ||||||
|  | +		union bpf_prog_subtype *prog_subtype) | ||||||
|  | +{ | ||||||
|  | +	if (WARN_ON(!prog_subtype)) | ||||||
|  | +		return false; | ||||||
|  | + | ||||||
|  | +	switch (prog_subtype->landlock_rule.event) { | ||||||
|  | +	case LANDLOCK_SUBTYPE_EVENT_FS: | ||||||
|  | +		break; | ||||||
|  | +	case LANDLOCK_SUBTYPE_EVENT_UNSPEC: | ||||||
|  | +	default: | ||||||
|  | +		return false; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	if (!prog_subtype->landlock_rule.version || | ||||||
|  | +			prog_subtype->landlock_rule.version > LANDLOCK_VERSION) | ||||||
|  | +		return false; | ||||||
|  | +	if (!prog_subtype->landlock_rule.event || | ||||||
|  | +			prog_subtype->landlock_rule.event > _LANDLOCK_SUBTYPE_EVENT_LAST) | ||||||
|  | +		return false; | ||||||
|  | +	if (prog_subtype->landlock_rule.ability & ~_LANDLOCK_SUBTYPE_ABILITY_MASK) | ||||||
|  | +		return false; | ||||||
|  | +	if (prog_subtype->landlock_rule.option & ~_LANDLOCK_SUBTYPE_OPTION_MASK) | ||||||
|  | +		return false; | ||||||
|  | + | ||||||
|  | +	/* check ability flags */ | ||||||
|  | +	if (prog_subtype->landlock_rule.ability & LANDLOCK_SUBTYPE_ABILITY_WRITE && | ||||||
|  | +			!capable(CAP_SYS_ADMIN)) | ||||||
|  | +		return false; | ||||||
|  | +	if (prog_subtype->landlock_rule.ability & LANDLOCK_SUBTYPE_ABILITY_DEBUG && | ||||||
|  | +			!capable(CAP_SYS_ADMIN)) | ||||||
|  | +		return false; | ||||||
|  | + | ||||||
|  | +	return true; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static inline const struct bpf_func_proto *bpf_landlock_func_proto( | ||||||
|  | +		enum bpf_func_id func_id, union bpf_prog_subtype *prog_subtype) | ||||||
|  | +{ | ||||||
|  | +	bool event_fs = (prog_subtype->landlock_rule.event == | ||||||
|  | +			LANDLOCK_SUBTYPE_EVENT_FS); | ||||||
|  | +	bool ability_write = !!(prog_subtype->landlock_rule.ability & | ||||||
|  | +			LANDLOCK_SUBTYPE_ABILITY_WRITE); | ||||||
|  | +	bool ability_debug = !!(prog_subtype->landlock_rule.ability & | ||||||
|  | +			LANDLOCK_SUBTYPE_ABILITY_DEBUG); | ||||||
|  | + | ||||||
|  | +	switch (func_id) { | ||||||
|  | +	case BPF_FUNC_map_lookup_elem: | ||||||
|  | +		return &bpf_map_lookup_elem_proto; | ||||||
|  | + | ||||||
|  | +	/* ability_write */ | ||||||
|  | +	case BPF_FUNC_map_delete_elem: | ||||||
|  | +		if (ability_write) | ||||||
|  | +			return &bpf_map_delete_elem_proto; | ||||||
|  | +		return NULL; | ||||||
|  | +	case BPF_FUNC_map_update_elem: | ||||||
|  | +		if (ability_write) | ||||||
|  | +			return &bpf_map_update_elem_proto; | ||||||
|  | +		return NULL; | ||||||
|  | + | ||||||
|  | +	/* ability_debug */ | ||||||
|  | +	case BPF_FUNC_get_current_comm: | ||||||
|  | +		if (ability_debug) | ||||||
|  | +			return &bpf_get_current_comm_proto; | ||||||
|  | +		return NULL; | ||||||
|  | +	case BPF_FUNC_get_current_pid_tgid: | ||||||
|  | +		if (ability_debug) | ||||||
|  | +			return &bpf_get_current_pid_tgid_proto; | ||||||
|  | +		return NULL; | ||||||
|  | +	case BPF_FUNC_get_current_uid_gid: | ||||||
|  | +		if (ability_debug) | ||||||
|  | +			return &bpf_get_current_uid_gid_proto; | ||||||
|  | +		return NULL; | ||||||
|  | +	case BPF_FUNC_trace_printk: | ||||||
|  | +		if (ability_debug) | ||||||
|  | +			return bpf_get_trace_printk_proto(); | ||||||
|  | +		return NULL; | ||||||
|  | + | ||||||
|  | +	default: | ||||||
|  | +		return NULL; | ||||||
|  | +	} | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static const struct bpf_verifier_ops bpf_landlock_ops = { | ||||||
|  | +	.get_func_proto	= bpf_landlock_func_proto, | ||||||
|  | +	.is_valid_access = bpf_landlock_is_valid_access, | ||||||
|  | +	.is_valid_subtype = bpf_landlock_is_valid_subtype, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +static struct bpf_prog_type_list bpf_landlock_type __ro_after_init = { | ||||||
|  | +	.ops = &bpf_landlock_ops, | ||||||
|  | +	.type = BPF_PROG_TYPE_LANDLOCK, | ||||||
|  | +}; | ||||||
|  | diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h | ||||||
|  | index aae3b82a673c..0167f61cb3ba 100644 | ||||||
|  | --- a/tools/include/uapi/linux/bpf.h | ||||||
|  | +++ b/tools/include/uapi/linux/bpf.h | ||||||
|  | @@ -95,6 +95,13 @@ enum bpf_prog_type { | ||||||
|  |  	BPF_PROG_TYPE_SCHED_ACT, | ||||||
|  |  	BPF_PROG_TYPE_TRACEPOINT, | ||||||
|  |  	BPF_PROG_TYPE_XDP, | ||||||
|  | +	BPF_PROG_TYPE_PERF_EVENT, | ||||||
|  | +	BPF_PROG_TYPE_CGROUP_SKB, | ||||||
|  | +	BPF_PROG_TYPE_CGROUP_SOCK, | ||||||
|  | +	BPF_PROG_TYPE_LWT_IN, | ||||||
|  | +	BPF_PROG_TYPE_LWT_OUT, | ||||||
|  | +	BPF_PROG_TYPE_LWT_XMIT, | ||||||
|  | +	BPF_PROG_TYPE_LANDLOCK, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  #define BPF_PSEUDO_MAP_FD	1 | ||||||
|  | @@ -481,4 +488,108 @@ struct xdp_md { | ||||||
|  |  	__u32 data_end; | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | +/** | ||||||
|  | + * enum landlock_subtype_event - event occurring when an action is performed on | ||||||
|  | + * a particular kernel object | ||||||
|  | + * | ||||||
|  | + * An event is a policy decision point which exposes the same context type | ||||||
|  | + * (especially the same arg[0-9] field types) for each rule execution. | ||||||
|  | + * | ||||||
|  | + * @LANDLOCK_SUBTYPE_EVENT_UNSPEC: invalid value | ||||||
|  | + * @LANDLOCK_SUBTYPE_EVENT_FS: generic filesystem event | ||||||
|  | + */ | ||||||
|  | +enum landlock_subtype_event { | ||||||
|  | +	LANDLOCK_SUBTYPE_EVENT_UNSPEC, | ||||||
|  | +	LANDLOCK_SUBTYPE_EVENT_FS, | ||||||
|  | +}; | ||||||
|  | +#define _LANDLOCK_SUBTYPE_EVENT_LAST LANDLOCK_SUBTYPE_EVENT_FS | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * DOC: landlock_subtype_access | ||||||
|  | + * | ||||||
|  | + * eBPF context and functions allowed for a rule | ||||||
|  | + * | ||||||
|  | + * - LANDLOCK_SUBTYPE_ABILITY_WRITE: allows to directly send notification to | ||||||
|  | + *   userland (e.g. through a map), which may leaks sensitive information | ||||||
|  | + * - LANDLOCK_SUBTYPE_ABILITY_DEBUG: allows to do debug actions (e.g. writing | ||||||
|  | + *   logs), which may be dangerous and should only be used for rule testing | ||||||
|  | + */ | ||||||
|  | +#define LANDLOCK_SUBTYPE_ABILITY_WRITE		(1ULL << 0) | ||||||
|  | +#define LANDLOCK_SUBTYPE_ABILITY_DEBUG		(1ULL << 1) | ||||||
|  | +#define _LANDLOCK_SUBTYPE_ABILITY_NB		2 | ||||||
|  | +#define _LANDLOCK_SUBTYPE_ABILITY_MASK		((1ULL << _LANDLOCK_SUBTYPE_ABILITY_NB) - 1) | ||||||
|  | + | ||||||
|  | +/* | ||||||
|  | + * Future options for a Landlock rule (e.g. run even if a previous rule denied | ||||||
|  | + * an action). | ||||||
|  | + */ | ||||||
|  | +#define _LANDLOCK_SUBTYPE_OPTION_NB		0 | ||||||
|  | +#define _LANDLOCK_SUBTYPE_OPTION_MASK		((1ULL << _LANDLOCK_SUBTYPE_OPTION_NB) - 1) | ||||||
|  | + | ||||||
|  | +/* | ||||||
|  | + * Status visible in the @status field of a context (e.g. already called in | ||||||
|  | + * this syscall session, with same args...). | ||||||
|  | + * | ||||||
|  | + * The @status field exposed to a rule shall depend on the rule version. | ||||||
|  | + */ | ||||||
|  | +#define _LANDLOCK_SUBTYPE_STATUS_NB		0 | ||||||
|  | +#define _LANDLOCK_SUBTYPE_STATUS_MASK		((1ULL << _LANDLOCK_SUBTYPE_STATUS_NB) - 1) | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * DOC: landlock_action_fs | ||||||
|  | + * | ||||||
|  | + * - %LANDLOCK_ACTION_FS_EXEC: execute a file or walk through a directory | ||||||
|  | + * - %LANDLOCK_ACTION_FS_WRITE: modify a file or a directory view (which | ||||||
|  | + *   include mount actions) | ||||||
|  | + * - %LANDLOCK_ACTION_FS_READ: read a file or a directory | ||||||
|  | + * - %LANDLOCK_ACTION_FS_NEW: create a file or a directory | ||||||
|  | + * - %LANDLOCK_ACTION_FS_GET: open or receive a file | ||||||
|  | + * - %LANDLOCK_ACTION_FS_REMOVE: unlink a file or remove a directory | ||||||
|  | + * | ||||||
|  | + * Each of the following actions are specific to syscall multiplexers. They | ||||||
|  | + * fill the syscall_cmd field from &struct landlock_context with their custom | ||||||
|  | + * command. | ||||||
|  | + * | ||||||
|  | + * - %LANDLOCK_ACTION_FS_IOCTL: ioctl command | ||||||
|  | + * - %LANDLOCK_ACTION_FS_LOCK: flock or fcntl lock command | ||||||
|  | + * - %LANDLOCK_ACTION_FS_FCNTL: fcntl command | ||||||
|  | + */ | ||||||
|  | +#define LANDLOCK_ACTION_FS_EXEC			(1ULL << 0) | ||||||
|  | +#define LANDLOCK_ACTION_FS_WRITE		(1ULL << 1) | ||||||
|  | +#define LANDLOCK_ACTION_FS_READ			(1ULL << 2) | ||||||
|  | +#define LANDLOCK_ACTION_FS_NEW			(1ULL << 3) | ||||||
|  | +#define LANDLOCK_ACTION_FS_GET			(1ULL << 4) | ||||||
|  | +#define LANDLOCK_ACTION_FS_REMOVE		(1ULL << 5) | ||||||
|  | +#define LANDLOCK_ACTION_FS_IOCTL		(1ULL << 6) | ||||||
|  | +#define LANDLOCK_ACTION_FS_LOCK			(1ULL << 7) | ||||||
|  | +#define LANDLOCK_ACTION_FS_FCNTL		(1ULL << 8) | ||||||
|  | +#define _LANDLOCK_ACTION_FS_NB			9 | ||||||
|  | +#define _LANDLOCK_ACTION_FS_MASK		((1ULL << _LANDLOCK_ACTION_FS_NB) - 1) | ||||||
|  | + | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * struct landlock_context - context accessible to a Landlock rule | ||||||
|  | + * | ||||||
|  | + * @status: bitfield for future use (LANDLOCK_SUBTYPE_STATUS_*) | ||||||
|  | + * @arch: indicates system call convention as an AUDIT_ARCH_* value | ||||||
|  | + *        as defined in <linux/audit.h> | ||||||
|  | + * @syscall_nr: the system call number called by the current process (may be | ||||||
|  | + *              useful to debug: find out from which syscall this request came | ||||||
|  | + *              from) | ||||||
|  | + * @syscall_cmd: contains the command used by a multiplexer syscall (e.g. | ||||||
|  | + *               ioctl, fcntl, flock) | ||||||
|  | + * @event: event type (&enum landlock_subtype_event) | ||||||
|  | + * @arg1: event's first optional argument | ||||||
|  | + * @arg2: event's second optional argument | ||||||
|  | + */ | ||||||
|  | +struct landlock_context { | ||||||
|  | +	__u64 status; | ||||||
|  | +	__u32 arch; | ||||||
|  | +	__u32 syscall_nr; | ||||||
|  | +	__u32 syscall_cmd; | ||||||
|  | +	__u32 event; | ||||||
|  | +	__u64 arg1; | ||||||
|  | +	__u64 arg2; | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  |  #endif /* _UAPI__LINUX_BPF_H__ */ | ||||||
|  | --  | ||||||
|  | 2.11.0 | ||||||
|  |  | ||||||
| @@ -0,0 +1,377 @@ | |||||||
|  | From ca8ae2066f8852a118e0885ab22d22e603001481 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= <mic@digikod.net> | ||||||
|  | Date: Wed, 29 Mar 2017 01:30:33 +0200 | ||||||
|  | Subject: [PATCH 07/12] bpf: Define handle_fs and add a new helper | ||||||
|  |  bpf_handle_fs_get_mode() | ||||||
|  | MIME-Version: 1.0 | ||||||
|  | Content-Type: text/plain; charset=UTF-8 | ||||||
|  | Content-Transfer-Encoding: 8bit | ||||||
|  |  | ||||||
|  | Add an eBPF function bpf_handle_fs_get_mode(handle_fs) to get the mode | ||||||
|  | of a an abstract object wrapping either a file, a dentry, a path, or an | ||||||
|  | inode. | ||||||
|  |  | ||||||
|  | Changes since v5: | ||||||
|  | * cosmetic fixes and rebase | ||||||
|  |  | ||||||
|  | Changes since v4: | ||||||
|  | * use a file abstraction (handle) to wrap inode, dentry, path and file | ||||||
|  |   structs | ||||||
|  | * remove bpf_landlock_cmp_fs_beneath() | ||||||
|  | * rename the BPF helper and move it to kernel/bpf/ | ||||||
|  | * tighten helpers accessible by a Landlock rule | ||||||
|  |  | ||||||
|  | Changes since v3: | ||||||
|  | * remove bpf_landlock_cmp_fs_prop() (suggested by Alexie Starovoitov) | ||||||
|  | * add hooks dealing with struct inode and struct path pointers: | ||||||
|  |   inode_permission and inode_getattr | ||||||
|  | * add abstraction over eBPF helper arguments thanks to wrapping structs | ||||||
|  | * add bpf_landlock_get_fs_mode() helper to check file type and mode | ||||||
|  | * merge WARN_ON() (suggested by Kees Cook) | ||||||
|  | * fix and update bpf_helpers.h | ||||||
|  | * use BPF_CALL_* for eBPF helpers (suggested by Alexie Starovoitov) | ||||||
|  | * make handle arraymap safe (RCU) and remove buggy synchronize_rcu() | ||||||
|  | * factor out the arraymay walk | ||||||
|  | * use size_t to index array (suggested by Jann Horn) | ||||||
|  |  | ||||||
|  | Changes since v2: | ||||||
|  | * add MNT_INTERNAL check to only add file handle from user-visible FS | ||||||
|  |   (e.g. no anonymous inode) | ||||||
|  | * replace struct file* with struct path* in map_landlock_handle | ||||||
|  | * add BPF protos | ||||||
|  | * fix bpf_landlock_cmp_fs_prop_with_struct_file() | ||||||
|  |  | ||||||
|  | Signed-off-by: Mickaël Salaün <mic@digikod.net> | ||||||
|  | Cc: Alexei Starovoitov <ast@kernel.org> | ||||||
|  | Cc: Andy Lutomirski <luto@amacapital.net> | ||||||
|  | Cc: Daniel Borkmann <daniel@iogearbox.net> | ||||||
|  | Cc: David S. Miller <davem@davemloft.net> | ||||||
|  | Cc: James Morris <james.l.morris@oracle.com> | ||||||
|  | Cc: Kees Cook <keescook@chromium.org> | ||||||
|  | Cc: Serge E. Hallyn <serge@hallyn.com> | ||||||
|  | Cc: Jann Horn <jann@thejh.net> | ||||||
|  | (cherry picked from commit 7cb1d72a1cca9442bc0b9c3eeff621b9d1709296) | ||||||
|  | --- | ||||||
|  |  include/linux/bpf.h            | 33 +++++++++++++++++++++ | ||||||
|  |  include/uapi/linux/bpf.h       | 17 +++++++++++ | ||||||
|  |  kernel/bpf/Makefile            |  2 +- | ||||||
|  |  kernel/bpf/helpers_fs.c        | 52 ++++++++++++++++++++++++++++++++ | ||||||
|  |  kernel/bpf/verifier.c          |  6 ++++ | ||||||
|  |  samples/bpf/bpf_helpers.h      |  2 ++ | ||||||
|  |  security/landlock/init.c       |  6 ++++ | ||||||
|  |  tools/include/uapi/linux/bpf.h | 67 ++++++++++++++++++++++++++++++++++++++++++ | ||||||
|  |  8 files changed, 184 insertions(+), 1 deletion(-) | ||||||
|  |  create mode 100644 kernel/bpf/helpers_fs.c | ||||||
|  |  | ||||||
|  | diff --git a/include/linux/bpf.h b/include/linux/bpf.h | ||||||
|  | index d4b9ca479f79..d66843a2aafb 100644 | ||||||
|  | --- a/include/linux/bpf.h | ||||||
|  | +++ b/include/linux/bpf.h | ||||||
|  | @@ -13,6 +13,11 @@ | ||||||
|  |  #include <linux/percpu.h> | ||||||
|  |  #include <linux/err.h> | ||||||
|  |   | ||||||
|  | +/* FS helpers */ | ||||||
|  | +#include <linux/dcache.h> /* struct dentry */ | ||||||
|  | +#include <linux/fs.h> /* struct file, struct inode */ | ||||||
|  | +#include <linux/path.h> /* struct path */ | ||||||
|  | + | ||||||
|  |  struct perf_event; | ||||||
|  |  struct bpf_map; | ||||||
|  |   | ||||||
|  | @@ -80,6 +85,8 @@ enum bpf_arg_type { | ||||||
|  |   | ||||||
|  |  	ARG_PTR_TO_CTX,		/* pointer to context */ | ||||||
|  |  	ARG_ANYTHING,		/* any (initialized) argument is ok */ | ||||||
|  | + | ||||||
|  | +	ARG_CONST_PTR_TO_HANDLE_FS,	/* pointer to an abstract FS struct */ | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  /* type of values returned from helper functions */ | ||||||
|  | @@ -146,6 +153,9 @@ enum bpf_reg_type { | ||||||
|  |  	 * map element. | ||||||
|  |  	 */ | ||||||
|  |  	PTR_TO_MAP_VALUE_ADJ, | ||||||
|  | + | ||||||
|  | +	/* FS helpers */ | ||||||
|  | +	CONST_PTR_TO_HANDLE_FS, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  struct bpf_prog; | ||||||
|  | @@ -215,6 +225,26 @@ struct bpf_event_entry { | ||||||
|  |  	struct rcu_head rcu; | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | +/* FS helpers */ | ||||||
|  | +enum bpf_handle_fs_type { | ||||||
|  | +	BPF_HANDLE_FS_TYPE_NONE, | ||||||
|  | +	BPF_HANDLE_FS_TYPE_FILE, | ||||||
|  | +	BPF_HANDLE_FS_TYPE_INODE, | ||||||
|  | +	BPF_HANDLE_FS_TYPE_PATH, | ||||||
|  | +	BPF_HANDLE_FS_TYPE_DENTRY, | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +struct bpf_handle_fs { | ||||||
|  | +	enum bpf_handle_fs_type type; | ||||||
|  | +	union { | ||||||
|  | +		struct file *file; | ||||||
|  | +		struct inode *inode; | ||||||
|  | +		const struct path *path; | ||||||
|  | +		struct dentry *dentry; | ||||||
|  | +	}; | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | + | ||||||
|  |  u64 bpf_tail_call(u64 ctx, u64 r2, u64 index, u64 r4, u64 r5); | ||||||
|  |  u64 bpf_get_stackid(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); | ||||||
|  |   | ||||||
|  | @@ -331,6 +361,9 @@ extern const struct bpf_func_proto bpf_skb_vlan_push_proto; | ||||||
|  |  extern const struct bpf_func_proto bpf_skb_vlan_pop_proto; | ||||||
|  |  extern const struct bpf_func_proto bpf_get_stackid_proto; | ||||||
|  |   | ||||||
|  | +/* FS helpers */ | ||||||
|  | +extern const struct bpf_func_proto bpf_handle_fs_get_mode_proto; | ||||||
|  | + | ||||||
|  |  /* Shared helpers among cBPF and eBPF. */ | ||||||
|  |  void bpf_user_rnd_init_once(void); | ||||||
|  |  u64 bpf_user_rnd_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); | ||||||
|  | diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h | ||||||
|  | index f190be4f609f..6aebf7144e93 100644 | ||||||
|  | --- a/include/uapi/linux/bpf.h | ||||||
|  | +++ b/include/uapi/linux/bpf.h | ||||||
|  | @@ -443,6 +443,23 @@ enum bpf_func_id { | ||||||
|  |  	 */ | ||||||
|  |  	BPF_FUNC_set_hash_invalid, | ||||||
|  |   | ||||||
|  | +	BPF_FUNC_get_numa_node_id, | ||||||
|  | +	BPF_FUNC_skb_change_head, | ||||||
|  | +	BPF_FUNC_xdp_adjust_head, | ||||||
|  | +	BPF_FUNC_probe_read_str, | ||||||
|  | +	BPF_FUNC_get_socket_cookie, | ||||||
|  | +	BPF_FUNC_get_socket_uid, | ||||||
|  | + | ||||||
|  | +	/** | ||||||
|  | +	* s64 bpf_handle_fs_get_mode(handle_fs) | ||||||
|  | +	*     Get the mode of a struct bpf_handle_fs | ||||||
|  | +	*     fs: struct bpf_handle_fs address | ||||||
|  | +	*     Return: | ||||||
|  | +	*       >= 0 file mode | ||||||
|  | +	*       < 0 error | ||||||
|  | +	*/ | ||||||
|  | +	BPF_FUNC_handle_fs_get_mode, | ||||||
|  | + | ||||||
|  |  	__BPF_FUNC_MAX_ID, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile | ||||||
|  | index eed911d091da..8fffb30ac7a1 100644 | ||||||
|  | --- a/kernel/bpf/Makefile | ||||||
|  | +++ b/kernel/bpf/Makefile | ||||||
|  | @@ -1,6 +1,6 @@ | ||||||
|  |  obj-y := core.o | ||||||
|  |   | ||||||
|  | -obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o | ||||||
|  | +obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o helpers_fs.o | ||||||
|  |  obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o | ||||||
|  |  ifeq ($(CONFIG_PERF_EVENTS),y) | ||||||
|  |  obj-$(CONFIG_BPF_SYSCALL) += stackmap.o | ||||||
|  | diff --git a/kernel/bpf/helpers_fs.c b/kernel/bpf/helpers_fs.c | ||||||
|  | new file mode 100644 | ||||||
|  | index 000000000000..d524d382adeb | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/kernel/bpf/helpers_fs.c | ||||||
|  | @@ -0,0 +1,52 @@ | ||||||
|  | +/* | ||||||
|  | + * BPF filesystem helpers | ||||||
|  | + * | ||||||
|  | + * Copyright © 2017 Mickaël Salaün <mic@digikod.net> | ||||||
|  | + * | ||||||
|  | + * This program is free software; you can redistribute it and/or modify | ||||||
|  | + * it under the terms of the GNU General Public License version 2, as | ||||||
|  | + * published by the Free Software Foundation. | ||||||
|  | + */ | ||||||
|  | + | ||||||
|  | +#include <linux/bpf.h> /* struct bpf_handle_fs */ | ||||||
|  | +#include <linux/errno.h> | ||||||
|  | +#include <linux/filter.h> /* BPF_CALL*() */ | ||||||
|  | + | ||||||
|  | +BPF_CALL_1(bpf_handle_fs_get_mode, struct bpf_handle_fs *, handle_fs) | ||||||
|  | +{ | ||||||
|  | +	if (WARN_ON(!handle_fs)) | ||||||
|  | +		return -EFAULT; | ||||||
|  | +	if (!handle_fs->file) { | ||||||
|  | +		/* file can be null for anonymous mmap */ | ||||||
|  | +		WARN_ON(handle_fs->type != BPF_HANDLE_FS_TYPE_FILE); | ||||||
|  | +		return -ENOENT; | ||||||
|  | +	} | ||||||
|  | +	switch (handle_fs->type) { | ||||||
|  | +	case BPF_HANDLE_FS_TYPE_FILE: | ||||||
|  | +		if (WARN_ON(!handle_fs->file->f_inode)) | ||||||
|  | +			return -ENOENT; | ||||||
|  | +		return handle_fs->file->f_inode->i_mode; | ||||||
|  | +	case BPF_HANDLE_FS_TYPE_INODE: | ||||||
|  | +		return handle_fs->inode->i_mode; | ||||||
|  | +	case BPF_HANDLE_FS_TYPE_PATH: | ||||||
|  | +		if (WARN_ON(!handle_fs->path->dentry || | ||||||
|  | +				!handle_fs->path->dentry->d_inode)) | ||||||
|  | +			return -ENOENT; | ||||||
|  | +		return handle_fs->path->dentry->d_inode->i_mode; | ||||||
|  | +	case BPF_HANDLE_FS_TYPE_DENTRY: | ||||||
|  | +		if (WARN_ON(!handle_fs->dentry->d_inode)) | ||||||
|  | +			return -ENOENT; | ||||||
|  | +		return handle_fs->dentry->d_inode->i_mode; | ||||||
|  | +	case BPF_HANDLE_FS_TYPE_NONE: | ||||||
|  | +	default: | ||||||
|  | +		WARN_ON(1); | ||||||
|  | +		return -EFAULT; | ||||||
|  | +	} | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +const struct bpf_func_proto bpf_handle_fs_get_mode_proto = { | ||||||
|  | +	.func		= bpf_handle_fs_get_mode, | ||||||
|  | +	.gpl_only	= true, | ||||||
|  | +	.ret_type	= RET_INTEGER, | ||||||
|  | +	.arg1_type	= ARG_CONST_PTR_TO_HANDLE_FS, | ||||||
|  | +}; | ||||||
|  | diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c | ||||||
|  | index f5f082de2f7f..7cecf5099207 100644 | ||||||
|  | --- a/kernel/bpf/verifier.c | ||||||
|  | +++ b/kernel/bpf/verifier.c | ||||||
|  | @@ -188,6 +188,7 @@ static const char * const reg_type_str[] = { | ||||||
|  |  	[CONST_IMM]		= "imm", | ||||||
|  |  	[PTR_TO_PACKET]		= "pkt", | ||||||
|  |  	[PTR_TO_PACKET_END]	= "pkt_end", | ||||||
|  | +	[CONST_PTR_TO_HANDLE_FS] = "handle_fs", | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  static void print_verifier_state(struct bpf_verifier_state *state) | ||||||
|  | @@ -520,6 +521,7 @@ static bool is_spillable_regtype(enum bpf_reg_type type) | ||||||
|  |  	case PTR_TO_PACKET_END: | ||||||
|  |  	case FRAME_PTR: | ||||||
|  |  	case CONST_PTR_TO_MAP: | ||||||
|  | +	case CONST_PTR_TO_HANDLE_FS: | ||||||
|  |  		return true; | ||||||
|  |  	default: | ||||||
|  |  		return false; | ||||||
|  | @@ -981,6 +983,10 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, | ||||||
|  |  		expected_type = PTR_TO_CTX; | ||||||
|  |  		if (type != expected_type) | ||||||
|  |  			goto err_type; | ||||||
|  | +	} else if (arg_type == ARG_CONST_PTR_TO_HANDLE_FS) { | ||||||
|  | +		expected_type = CONST_PTR_TO_HANDLE_FS; | ||||||
|  | +		if (type != expected_type) | ||||||
|  | +			goto err_type; | ||||||
|  |  	} else if (arg_type == ARG_PTR_TO_STACK || | ||||||
|  |  		   arg_type == ARG_PTR_TO_RAW_STACK) { | ||||||
|  |  		expected_type = PTR_TO_STACK; | ||||||
|  | diff --git a/samples/bpf/bpf_helpers.h b/samples/bpf/bpf_helpers.h | ||||||
|  | index dadd5161bd91..d962a5d76725 100644 | ||||||
|  | --- a/samples/bpf/bpf_helpers.h | ||||||
|  | +++ b/samples/bpf/bpf_helpers.h | ||||||
|  | @@ -57,6 +57,8 @@ static int (*bpf_skb_set_tunnel_opt)(void *ctx, void *md, int size) = | ||||||
|  |  	(void *) BPF_FUNC_skb_set_tunnel_opt; | ||||||
|  |  static unsigned long long (*bpf_get_prandom_u32)(void) = | ||||||
|  |  	(void *) BPF_FUNC_get_prandom_u32; | ||||||
|  | +static long long (*bpf_handle_fs_get_mode)(void *handle_fs) = | ||||||
|  | +	(void *) BPF_FUNC_handle_fs_get_mode; | ||||||
|  |   | ||||||
|  |  /* llvm builtin functions that eBPF C program may use to | ||||||
|  |   * emit BPF_LD_ABS and BPF_LD_IND instructions | ||||||
|  | diff --git a/security/landlock/init.c b/security/landlock/init.c | ||||||
|  | index 0a97026f1c07..914895d08320 100644 | ||||||
|  | --- a/security/landlock/init.c | ||||||
|  | +++ b/security/landlock/init.c | ||||||
|  | @@ -78,6 +78,12 @@ static inline const struct bpf_func_proto *bpf_landlock_func_proto( | ||||||
|  |  	case BPF_FUNC_map_lookup_elem: | ||||||
|  |  		return &bpf_map_lookup_elem_proto; | ||||||
|  |   | ||||||
|  | +	/* event_fs */ | ||||||
|  | +	case BPF_FUNC_handle_fs_get_mode: | ||||||
|  | +		if (event_fs) | ||||||
|  | +			return &bpf_handle_fs_get_mode_proto; | ||||||
|  | +		return NULL; | ||||||
|  | + | ||||||
|  |  	/* ability_write */ | ||||||
|  |  	case BPF_FUNC_map_delete_elem: | ||||||
|  |  		if (ability_write) | ||||||
|  | diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h | ||||||
|  | index 0167f61cb3ba..6aebf7144e93 100644 | ||||||
|  | --- a/tools/include/uapi/linux/bpf.h | ||||||
|  | +++ b/tools/include/uapi/linux/bpf.h | ||||||
|  | @@ -393,6 +393,73 @@ enum bpf_func_id { | ||||||
|  |  	 */ | ||||||
|  |  	BPF_FUNC_probe_write_user, | ||||||
|  |   | ||||||
|  | +	/** | ||||||
|  | +	 * bpf_current_task_under_cgroup(map, index) - Check cgroup2 membership of current task | ||||||
|  | +	 * @map: pointer to bpf_map in BPF_MAP_TYPE_CGROUP_ARRAY type | ||||||
|  | +	 * @index: index of the cgroup in the bpf_map | ||||||
|  | +	 * Return: | ||||||
|  | +	 *   == 0 current failed the cgroup2 descendant test | ||||||
|  | +	 *   == 1 current succeeded the cgroup2 descendant test | ||||||
|  | +	 *    < 0 error | ||||||
|  | +	 */ | ||||||
|  | +	BPF_FUNC_current_task_under_cgroup, | ||||||
|  | + | ||||||
|  | +	/** | ||||||
|  | +	 * bpf_skb_change_tail(skb, len, flags) | ||||||
|  | +	 * The helper will resize the skb to the given new size, | ||||||
|  | +	 * to be used f.e. with control messages. | ||||||
|  | +	 * @skb: pointer to skb | ||||||
|  | +	 * @len: new skb length | ||||||
|  | +	 * @flags: reserved | ||||||
|  | +	 * Return: 0 on success or negative error | ||||||
|  | +	 */ | ||||||
|  | +	BPF_FUNC_skb_change_tail, | ||||||
|  | + | ||||||
|  | +	/** | ||||||
|  | +	 * bpf_skb_pull_data(skb, len) | ||||||
|  | +	 * The helper will pull in non-linear data in case the | ||||||
|  | +	 * skb is non-linear and not all of len are part of the | ||||||
|  | +	 * linear section. Only needed for read/write with direct | ||||||
|  | +	 * packet access. | ||||||
|  | +	 * @skb: pointer to skb | ||||||
|  | +	 * @len: len to make read/writeable | ||||||
|  | +	 * Return: 0 on success or negative error | ||||||
|  | +	 */ | ||||||
|  | +	BPF_FUNC_skb_pull_data, | ||||||
|  | + | ||||||
|  | +	/** | ||||||
|  | +	 * bpf_csum_update(skb, csum) | ||||||
|  | +	 * Adds csum into skb->csum in case of CHECKSUM_COMPLETE. | ||||||
|  | +	 * @skb: pointer to skb | ||||||
|  | +	 * @csum: csum to add | ||||||
|  | +	 * Return: csum on success or negative error | ||||||
|  | +	 */ | ||||||
|  | +	BPF_FUNC_csum_update, | ||||||
|  | + | ||||||
|  | +	/** | ||||||
|  | +	 * bpf_set_hash_invalid(skb) | ||||||
|  | +	 * Invalidate current skb>hash. | ||||||
|  | +	 * @skb: pointer to skb | ||||||
|  | +	 */ | ||||||
|  | +	BPF_FUNC_set_hash_invalid, | ||||||
|  | + | ||||||
|  | +	BPF_FUNC_get_numa_node_id, | ||||||
|  | +	BPF_FUNC_skb_change_head, | ||||||
|  | +	BPF_FUNC_xdp_adjust_head, | ||||||
|  | +	BPF_FUNC_probe_read_str, | ||||||
|  | +	BPF_FUNC_get_socket_cookie, | ||||||
|  | +	BPF_FUNC_get_socket_uid, | ||||||
|  | + | ||||||
|  | +	/** | ||||||
|  | +	* s64 bpf_handle_fs_get_mode(handle_fs) | ||||||
|  | +	*     Get the mode of a struct bpf_handle_fs | ||||||
|  | +	*     fs: struct bpf_handle_fs address | ||||||
|  | +	*     Return: | ||||||
|  | +	*       >= 0 file mode | ||||||
|  | +	*       < 0 error | ||||||
|  | +	*/ | ||||||
|  | +	BPF_FUNC_handle_fs_get_mode, | ||||||
|  | + | ||||||
|  |  	__BPF_FUNC_MAX_ID, | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  | --  | ||||||
|  | 2.11.0 | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,143 @@ | |||||||
|  | From 1512eca4f0a5b3d1a98ed9940cae462bf71cc956 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= <mic@digikod.net> | ||||||
|  | Date: Wed, 29 Mar 2017 01:30:33 +0200 | ||||||
|  | Subject: [PATCH 09/12] seccomp: Split put_seccomp_filter() with put_seccomp() | ||||||
|  | MIME-Version: 1.0 | ||||||
|  | Content-Type: text/plain; charset=UTF-8 | ||||||
|  | Content-Transfer-Encoding: 8bit | ||||||
|  |  | ||||||
|  | The semantic is unchanged. This will be useful for the Landlock | ||||||
|  | integration with seccomp (next commit). | ||||||
|  |  | ||||||
|  | Signed-off-by: Mickaël Salaün <mic@digikod.net> | ||||||
|  | Cc: Kees Cook <keescook@chromium.org> | ||||||
|  | Cc: Andy Lutomirski <luto@amacapital.net> | ||||||
|  | Cc: Will Drewry <wad@chromium.org> | ||||||
|  | (cherry picked from commit 2f707c41fd744e5c2beb382dafe3b2dc658c26d4) | ||||||
|  | --- | ||||||
|  |  include/linux/seccomp.h   |  4 ++-- | ||||||
|  |  kernel/fork.c             |  2 +- | ||||||
|  |  kernel/seccomp.c          | 18 +++++++++++++----- | ||||||
|  |  security/landlock/hooks.c |  4 +--- | ||||||
|  |  security/landlock/init.c  |  2 +- | ||||||
|  |  5 files changed, 18 insertions(+), 12 deletions(-) | ||||||
|  |  | ||||||
|  | diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h | ||||||
|  | index ecc296c137cd..e25aee2cdfc0 100644 | ||||||
|  | --- a/include/linux/seccomp.h | ||||||
|  | +++ b/include/linux/seccomp.h | ||||||
|  | @@ -77,10 +77,10 @@ static inline int seccomp_mode(struct seccomp *s) | ||||||
|  |  #endif /* CONFIG_SECCOMP */ | ||||||
|  |   | ||||||
|  |  #ifdef CONFIG_SECCOMP_FILTER | ||||||
|  | -extern void put_seccomp_filter(struct task_struct *tsk); | ||||||
|  | +extern void put_seccomp(struct task_struct *tsk); | ||||||
|  |  extern void get_seccomp_filter(struct task_struct *tsk); | ||||||
|  |  #else  /* CONFIG_SECCOMP_FILTER */ | ||||||
|  | -static inline void put_seccomp_filter(struct task_struct *tsk) | ||||||
|  | +static inline void put_seccomp(struct task_struct *tsk) | ||||||
|  |  { | ||||||
|  |  	return; | ||||||
|  |  } | ||||||
|  | diff --git a/kernel/fork.c b/kernel/fork.c | ||||||
|  | index ba8a01564985..48996df6eb5e 100644 | ||||||
|  | --- a/kernel/fork.c | ||||||
|  | +++ b/kernel/fork.c | ||||||
|  | @@ -352,7 +352,7 @@ void free_task(struct task_struct *tsk) | ||||||
|  |  #endif | ||||||
|  |  	rt_mutex_debug_task_free(tsk); | ||||||
|  |  	ftrace_graph_exit_task(tsk); | ||||||
|  | -	put_seccomp_filter(tsk); | ||||||
|  | +	put_seccomp(tsk); | ||||||
|  |  	arch_release_task_struct(tsk); | ||||||
|  |  	free_task_struct(tsk); | ||||||
|  |  } | ||||||
|  | diff --git a/kernel/seccomp.c b/kernel/seccomp.c | ||||||
|  | index 0db7c8a2afe2..e741a82eab4d 100644 | ||||||
|  | --- a/kernel/seccomp.c | ||||||
|  | +++ b/kernel/seccomp.c | ||||||
|  | @@ -63,6 +63,8 @@ struct seccomp_filter { | ||||||
|  |  /* Limit any path through the tree to 256KB worth of instructions. */ | ||||||
|  |  #define MAX_INSNS_PER_PATH ((1 << 18) / sizeof(struct sock_filter)) | ||||||
|  |   | ||||||
|  | +static void put_seccomp_filter(struct seccomp_filter *filter); | ||||||
|  | + | ||||||
|  |  /* | ||||||
|  |   * Endianness is explicitly ignored and left for BPF program authors to manage | ||||||
|  |   * as per the specific architecture. | ||||||
|  | @@ -313,7 +315,7 @@ static inline void seccomp_sync_threads(void) | ||||||
|  |  		 * current's path will hold a reference.  (This also | ||||||
|  |  		 * allows a put before the assignment.) | ||||||
|  |  		 */ | ||||||
|  | -		put_seccomp_filter(thread); | ||||||
|  | +		put_seccomp_filter(thread->seccomp.filter); | ||||||
|  |  		smp_store_release(&thread->seccomp.filter, | ||||||
|  |  				  caller->seccomp.filter); | ||||||
|  |   | ||||||
|  | @@ -475,10 +477,11 @@ static inline void seccomp_filter_free(struct seccomp_filter *filter) | ||||||
|  |  	} | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | -/* put_seccomp_filter - decrements the ref count of tsk->seccomp.filter */ | ||||||
|  | -void put_seccomp_filter(struct task_struct *tsk) | ||||||
|  | +/* put_seccomp_filter - decrements the ref count of a filter */ | ||||||
|  | +static void put_seccomp_filter(struct seccomp_filter *filter) | ||||||
|  |  { | ||||||
|  | -	struct seccomp_filter *orig = tsk->seccomp.filter; | ||||||
|  | +	struct seccomp_filter *orig = filter; | ||||||
|  | + | ||||||
|  |  	/* Clean up single-reference branches iteratively. */ | ||||||
|  |  	while (orig && atomic_dec_and_test(&orig->usage)) { | ||||||
|  |  		struct seccomp_filter *freeme = orig; | ||||||
|  | @@ -487,6 +490,11 @@ void put_seccomp_filter(struct task_struct *tsk) | ||||||
|  |  	} | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +void put_seccomp(struct task_struct *tsk) | ||||||
|  | +{ | ||||||
|  | +	put_seccomp_filter(tsk->seccomp.filter); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  |  /** | ||||||
|  |   * seccomp_send_sigsys - signals the task to allow in-process syscall emulation | ||||||
|  |   * @syscall: syscall number to send to userland | ||||||
|  | @@ -898,7 +906,7 @@ long seccomp_get_filter(struct task_struct *task, unsigned long filter_off, | ||||||
|  |  	if (copy_to_user(data, fprog->filter, bpf_classic_proglen(fprog))) | ||||||
|  |  		ret = -EFAULT; | ||||||
|  |   | ||||||
|  | -	put_seccomp_filter(task); | ||||||
|  | +	put_seccomp_filter(task->seccomp.filter); | ||||||
|  |  	return ret; | ||||||
|  |   | ||||||
|  |  out: | ||||||
|  | diff --git a/security/landlock/hooks.c b/security/landlock/hooks.c | ||||||
|  | index eaee8162ff70..cbad4b66ca13 100644 | ||||||
|  | --- a/security/landlock/hooks.c | ||||||
|  | +++ b/security/landlock/hooks.c | ||||||
|  | @@ -27,10 +27,8 @@ __init void landlock_register_hooks(struct security_hook_list *hooks, int count) | ||||||
|  |  { | ||||||
|  |  	int i; | ||||||
|  |   | ||||||
|  | -	for (i = 0; i < count; i++) { | ||||||
|  | -		hooks[i].lsm = "landlock"; | ||||||
|  | +	for (i = 0; i < count; i++) | ||||||
|  |  		list_add_tail_rcu(&hooks[i].list, hooks[i].head); | ||||||
|  | -	} | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  bool landlock_is_valid_access(int off, int size, enum bpf_access_type type, | ||||||
|  | diff --git a/security/landlock/init.c b/security/landlock/init.c | ||||||
|  | index 1c2750e12dfa..909c51c3fa32 100644 | ||||||
|  | --- a/security/landlock/init.c | ||||||
|  | +++ b/security/landlock/init.c | ||||||
|  | @@ -137,6 +137,6 @@ void __init landlock_add_hooks(void) | ||||||
|  |  { | ||||||
|  |  	pr_info("landlock: Version %u", LANDLOCK_VERSION); | ||||||
|  |  	landlock_add_hooks_fs(); | ||||||
|  | -	security_add_hooks(NULL, 0, "landlock"); | ||||||
|  | +	security_add_hooks(NULL, 0); | ||||||
|  |  	bpf_register_prog_type(&bpf_landlock_type); | ||||||
|  |  } | ||||||
|  | --  | ||||||
|  | 2.11.0 | ||||||
|  |  | ||||||
| @@ -0,0 +1,599 @@ | |||||||
|  | From a53bef1072081d7ff33c58293a019cfab91111a7 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= <mic@digikod.net> | ||||||
|  | Date: Wed, 29 Mar 2017 01:30:33 +0200 | ||||||
|  | Subject: [PATCH 10/12] seccomp,landlock: Handle Landlock events per process | ||||||
|  |  hierarchy | ||||||
|  | MIME-Version: 1.0 | ||||||
|  | Content-Type: text/plain; charset=UTF-8 | ||||||
|  | Content-Transfer-Encoding: 8bit | ||||||
|  |  | ||||||
|  | The seccomp(2) syscall can be used by a task to apply a Landlock rule to | ||||||
|  | itself. As a seccomp filter, a Landlock rule is enforced for the current | ||||||
|  | task and all its future children. A rule is immutable and a task can | ||||||
|  | only add new restricting rules to itself, forming a chain of rules. | ||||||
|  |  | ||||||
|  | A Landlock rule is tied to a Landlock event. If the use of a kernel | ||||||
|  | object is allowed by the other Linux security mechanisms (e.g. DAC, | ||||||
|  | capabilities, other LSM), then a Landlock event related to this kind of | ||||||
|  | object is triggered. The chain of rules for this event is then | ||||||
|  | evaluated. Each rule return a 32-bit value which can deny the use of a | ||||||
|  | kernel object with a non-zero value. If every rules of the chain return | ||||||
|  | zero, then the use of the object is allowed. | ||||||
|  |  | ||||||
|  | Changes since v5: | ||||||
|  | * remove struct landlock_node and use a similar inheritance mechanisme | ||||||
|  |   as seccomp-bpf (requested by Andy Lutomirski) | ||||||
|  | * rename SECCOMP_ADD_LANDLOCK_RULE to SECCOMP_APPEND_LANDLOCK_RULE | ||||||
|  | * rename file manager.c to providers.c | ||||||
|  | * add comments | ||||||
|  | * typo and cosmetic fixes | ||||||
|  |  | ||||||
|  | Changes since v4: | ||||||
|  | * merge manager and seccomp patches | ||||||
|  | * return -EFAULT in seccomp(2) when user_bpf_fd is null to easely check | ||||||
|  |   if Landlock is supported | ||||||
|  | * only allow a process with the global CAP_SYS_ADMIN to use Landlock | ||||||
|  |   (will be lifted in the future) | ||||||
|  | * add an early check to exit as soon as possible if the current process | ||||||
|  |   does not have Landlock rules | ||||||
|  |  | ||||||
|  | Changes since v3: | ||||||
|  | * remove the hard link with seccomp (suggested by Andy Lutomirski and | ||||||
|  |   Kees Cook): | ||||||
|  |   * remove the cookie which could imply multiple evaluation of Landlock | ||||||
|  |     rules | ||||||
|  |   * remove the origin field in struct landlock_data | ||||||
|  | * remove documentation fix (merged upstream) | ||||||
|  | * rename the new seccomp command to SECCOMP_ADD_LANDLOCK_RULE | ||||||
|  | * internal renaming | ||||||
|  | * split commit | ||||||
|  | * new design to be able to inherit on the fly the parent rules | ||||||
|  |  | ||||||
|  | Changes since v2: | ||||||
|  | * Landlock programs can now be run without seccomp filter but for any | ||||||
|  |   syscall (from the process) or interruption | ||||||
|  | * move Landlock related functions and structs into security/landlock/* | ||||||
|  |   (to manage cgroups as well) | ||||||
|  | * fix seccomp filter handling: run Landlock programs for each of their | ||||||
|  |   legitimate seccomp filter | ||||||
|  | * properly clean up all seccomp results | ||||||
|  | * cosmetic changes to ease the understanding | ||||||
|  | * fix some ifdef | ||||||
|  |  | ||||||
|  | Signed-off-by: Mickaël Salaün <mic@digikod.net> | ||||||
|  | Cc: Alexei Starovoitov <ast@kernel.org> | ||||||
|  | Cc: Andrew Morton <akpm@linux-foundation.org> | ||||||
|  | Cc: Andy Lutomirski <luto@amacapital.net> | ||||||
|  | Cc: James Morris <james.l.morris@oracle.com> | ||||||
|  | Cc: Kees Cook <keescook@chromium.org> | ||||||
|  | Cc: Serge E. Hallyn <serge@hallyn.com> | ||||||
|  | Cc: Will Drewry <wad@chromium.org> | ||||||
|  | Link: https://lkml.kernel.org/r/c10a503d-5e35-7785-2f3d-25ed8dd63fab@digikod.net | ||||||
|  | (cherry picked from commit 47986b417d07970983d82579fd9def4844f8ed78) | ||||||
|  | --- | ||||||
|  |  include/linux/landlock.h      |  36 +++++++ | ||||||
|  |  include/linux/seccomp.h       |   8 ++ | ||||||
|  |  include/uapi/linux/seccomp.h  |   1 + | ||||||
|  |  kernel/fork.c                 |  14 ++- | ||||||
|  |  kernel/seccomp.c              |   8 ++ | ||||||
|  |  security/landlock/Makefile    |   2 +- | ||||||
|  |  security/landlock/hooks.c     |  37 +++++++ | ||||||
|  |  security/landlock/hooks.h     |   5 + | ||||||
|  |  security/landlock/init.c      |   3 +- | ||||||
|  |  security/landlock/providers.c | 232 ++++++++++++++++++++++++++++++++++++++++++ | ||||||
|  |  10 files changed, 342 insertions(+), 4 deletions(-) | ||||||
|  |  create mode 100644 security/landlock/providers.c | ||||||
|  |  | ||||||
|  | diff --git a/include/linux/landlock.h b/include/linux/landlock.h | ||||||
|  | index 53013dc374fe..c40ee78e86e0 100644 | ||||||
|  | --- a/include/linux/landlock.h | ||||||
|  | +++ b/include/linux/landlock.h | ||||||
|  | @@ -12,6 +12,9 @@ | ||||||
|  |  #define _LINUX_LANDLOCK_H | ||||||
|  |  #ifdef CONFIG_SECURITY_LANDLOCK | ||||||
|  |   | ||||||
|  | +#include <linux/bpf.h>	/* _LANDLOCK_SUBTYPE_EVENT_LAST */ | ||||||
|  | +#include <linux/types.h> /* atomic_t */ | ||||||
|  | + | ||||||
|  |  /* | ||||||
|  |   * This is not intended for the UAPI headers. Each userland software should use | ||||||
|  |   * a static minimal version for the required features as explained in the | ||||||
|  | @@ -19,5 +22,38 @@ | ||||||
|  |   */ | ||||||
|  |  #define LANDLOCK_VERSION 1 | ||||||
|  |   | ||||||
|  | +struct landlock_rule { | ||||||
|  | +	atomic_t usage; | ||||||
|  | +	struct landlock_rule *prev; | ||||||
|  | +	struct bpf_prog *prog; | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * struct landlock_events - Landlock event rules enforced on a thread | ||||||
|  | + * | ||||||
|  | + * This is used for low performance impact when forking a process. Instead of | ||||||
|  | + * copying the full array and incrementing the usage of each entries, only | ||||||
|  | + * create a pointer to &struct landlock_events and increments its usage. When | ||||||
|  | + * appending a new rule, if &struct landlock_events is shared with other tasks, | ||||||
|  | + * then duplicate it and append the rule to this new &struct landlock_events. | ||||||
|  | + * | ||||||
|  | + * @usage: reference count to manage the object lifetime. When a thread need to | ||||||
|  | + *         add Landlock rules and if @usage is greater than 1, then the thread | ||||||
|  | + *         must duplicate &struct landlock_events to not change the children's | ||||||
|  | + *         rules as well. | ||||||
|  | + * @rules: array of non-NULL &struct landlock_rule pointers | ||||||
|  | + */ | ||||||
|  | +struct landlock_events { | ||||||
|  | +	atomic_t usage; | ||||||
|  | +	struct landlock_rule *rules[_LANDLOCK_SUBTYPE_EVENT_LAST]; | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +void put_landlock_events(struct landlock_events *events); | ||||||
|  | + | ||||||
|  | +#ifdef CONFIG_SECCOMP_FILTER | ||||||
|  | +int landlock_seccomp_append_prog(unsigned int flags, | ||||||
|  | +		const char __user *user_bpf_fd); | ||||||
|  | +#endif /* CONFIG_SECCOMP_FILTER */ | ||||||
|  | + | ||||||
|  |  #endif /* CONFIG_SECURITY_LANDLOCK */ | ||||||
|  |  #endif /* _LINUX_LANDLOCK_H */ | ||||||
|  | diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h | ||||||
|  | index e25aee2cdfc0..9a38de3c0e72 100644 | ||||||
|  | --- a/include/linux/seccomp.h | ||||||
|  | +++ b/include/linux/seccomp.h | ||||||
|  | @@ -10,6 +10,10 @@ | ||||||
|  |  #include <linux/thread_info.h> | ||||||
|  |  #include <asm/seccomp.h> | ||||||
|  |   | ||||||
|  | +#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK) | ||||||
|  | +struct landlock_events; | ||||||
|  | +#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */ | ||||||
|  | + | ||||||
|  |  struct seccomp_filter; | ||||||
|  |  /** | ||||||
|  |   * struct seccomp - the state of a seccomp'ed process | ||||||
|  | @@ -18,6 +22,7 @@ struct seccomp_filter; | ||||||
|  |   *         system calls available to a process. | ||||||
|  |   * @filter: must always point to a valid seccomp-filter or NULL as it is | ||||||
|  |   *          accessed without locking during system call entry. | ||||||
|  | + * @landlock_events: contains an array of Landlock rules. | ||||||
|  |   * | ||||||
|  |   *          @filter must only be accessed from the context of current as there | ||||||
|  |   *          is no read locking. | ||||||
|  | @@ -25,6 +30,9 @@ struct seccomp_filter; | ||||||
|  |  struct seccomp { | ||||||
|  |  	int mode; | ||||||
|  |  	struct seccomp_filter *filter; | ||||||
|  | +#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK) | ||||||
|  | +	struct landlock_events *landlock_events; | ||||||
|  | +#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */ | ||||||
|  |  }; | ||||||
|  |   | ||||||
|  |  #ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER | ||||||
|  | diff --git a/include/uapi/linux/seccomp.h b/include/uapi/linux/seccomp.h | ||||||
|  | index 0f238a43ff1e..74891cf60ca6 100644 | ||||||
|  | --- a/include/uapi/linux/seccomp.h | ||||||
|  | +++ b/include/uapi/linux/seccomp.h | ||||||
|  | @@ -13,6 +13,7 @@ | ||||||
|  |  /* Valid operations for seccomp syscall. */ | ||||||
|  |  #define SECCOMP_SET_MODE_STRICT	0 | ||||||
|  |  #define SECCOMP_SET_MODE_FILTER	1 | ||||||
|  | +#define SECCOMP_APPEND_LANDLOCK_RULE	2 | ||||||
|  |   | ||||||
|  |  /* Valid flags for SECCOMP_SET_MODE_FILTER */ | ||||||
|  |  #define SECCOMP_FILTER_FLAG_TSYNC	1 | ||||||
|  | diff --git a/kernel/fork.c b/kernel/fork.c | ||||||
|  | index 48996df6eb5e..8c7e289d0727 100644 | ||||||
|  | --- a/kernel/fork.c | ||||||
|  | +++ b/kernel/fork.c | ||||||
|  | @@ -37,6 +37,7 @@ | ||||||
|  |  #include <linux/security.h> | ||||||
|  |  #include <linux/hugetlb.h> | ||||||
|  |  #include <linux/seccomp.h> | ||||||
|  | +#include <linux/landlock.h> | ||||||
|  |  #include <linux/swap.h> | ||||||
|  |  #include <linux/syscalls.h> | ||||||
|  |  #include <linux/jiffies.h> | ||||||
|  | @@ -513,7 +514,10 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node) | ||||||
|  |  	 * the usage counts on the error path calling free_task. | ||||||
|  |  	 */ | ||||||
|  |  	tsk->seccomp.filter = NULL; | ||||||
|  | -#endif | ||||||
|  | +#ifdef CONFIG_SECURITY_LANDLOCK | ||||||
|  | +	tsk->seccomp.landlock_events = NULL; | ||||||
|  | +#endif /* CONFIG_SECURITY_LANDLOCK */ | ||||||
|  | +#endif /* CONFIG_SECCOMP */ | ||||||
|  |   | ||||||
|  |  	setup_thread_stack(tsk, orig); | ||||||
|  |  	clear_user_return_notifier(tsk); | ||||||
|  | @@ -1384,7 +1388,13 @@ static void copy_seccomp(struct task_struct *p) | ||||||
|  |   | ||||||
|  |  	/* Ref-count the new filter user, and assign it. */ | ||||||
|  |  	get_seccomp_filter(current); | ||||||
|  | -	p->seccomp = current->seccomp; | ||||||
|  | +	p->seccomp.mode = current->seccomp.mode; | ||||||
|  | +	p->seccomp.filter = current->seccomp.filter; | ||||||
|  | +#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK) | ||||||
|  | +	p->seccomp.landlock_events = current->seccomp.landlock_events; | ||||||
|  | +	if (p->seccomp.landlock_events) | ||||||
|  | +		atomic_inc(&p->seccomp.landlock_events->usage); | ||||||
|  | +#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */ | ||||||
|  |   | ||||||
|  |  	/* | ||||||
|  |  	 * Explicitly enable no_new_privs here in case it got set | ||||||
|  | diff --git a/kernel/seccomp.c b/kernel/seccomp.c | ||||||
|  | index e741a82eab4d..72b1cc4ce63b 100644 | ||||||
|  | --- a/kernel/seccomp.c | ||||||
|  | +++ b/kernel/seccomp.c | ||||||
|  | @@ -32,6 +32,7 @@ | ||||||
|  |  #include <linux/security.h> | ||||||
|  |  #include <linux/tracehook.h> | ||||||
|  |  #include <linux/uaccess.h> | ||||||
|  | +#include <linux/landlock.h> | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  |   * struct seccomp_filter - container for seccomp BPF programs | ||||||
|  | @@ -493,6 +494,9 @@ static void put_seccomp_filter(struct seccomp_filter *filter) | ||||||
|  |  void put_seccomp(struct task_struct *tsk) | ||||||
|  |  { | ||||||
|  |  	put_seccomp_filter(tsk->seccomp.filter); | ||||||
|  | +#ifdef CONFIG_SECURITY_LANDLOCK | ||||||
|  | +	put_landlock_events(tsk->seccomp.landlock_events); | ||||||
|  | +#endif /* CONFIG_SECURITY_LANDLOCK */ | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  /** | ||||||
|  | @@ -797,6 +801,10 @@ static long do_seccomp(unsigned int op, unsigned int flags, | ||||||
|  |  		return seccomp_set_mode_strict(); | ||||||
|  |  	case SECCOMP_SET_MODE_FILTER: | ||||||
|  |  		return seccomp_set_mode_filter(flags, uargs); | ||||||
|  | +#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_SECURITY_LANDLOCK) | ||||||
|  | +	case SECCOMP_APPEND_LANDLOCK_RULE: | ||||||
|  | +		return landlock_seccomp_append_prog(flags, uargs); | ||||||
|  | +#endif /* CONFIG_SECCOMP_FILTER && CONFIG_SECURITY_LANDLOCK */ | ||||||
|  |  	default: | ||||||
|  |  		return -EINVAL; | ||||||
|  |  	} | ||||||
|  | diff --git a/security/landlock/Makefile b/security/landlock/Makefile | ||||||
|  | index c0db504a6335..da8ba8b5183e 100644 | ||||||
|  | --- a/security/landlock/Makefile | ||||||
|  | +++ b/security/landlock/Makefile | ||||||
|  | @@ -2,4 +2,4 @@ ccflags-$(CONFIG_SECURITY_LANDLOCK) += -Werror=unused-function | ||||||
|  |   | ||||||
|  |  obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o | ||||||
|  |   | ||||||
|  | -landlock-y := init.o hooks.o hooks_fs.o | ||||||
|  | +landlock-y := init.o providers.o hooks.o hooks_fs.o | ||||||
|  | diff --git a/security/landlock/hooks.c b/security/landlock/hooks.c | ||||||
|  | index cbad4b66ca13..194cd4307b01 100644 | ||||||
|  | --- a/security/landlock/hooks.c | ||||||
|  | +++ b/security/landlock/hooks.c | ||||||
|  | @@ -93,6 +93,38 @@ bool landlock_is_valid_access(int off, int size, enum bpf_access_type type, | ||||||
|  |  	return true; | ||||||
|  |  } | ||||||
|  |   | ||||||
|  | +/** | ||||||
|  | + * landlock_event_deny - run Landlock rules tied to an event | ||||||
|  | + * | ||||||
|  | + * @event_idx: event index in the rules array | ||||||
|  | + * @ctx: non-NULL eBPF context | ||||||
|  | + * @events: Landlock events pointer | ||||||
|  | + * | ||||||
|  | + * Return true if at least one rule deny the event. | ||||||
|  | + */ | ||||||
|  | +static bool landlock_event_deny(u32 event_idx, const struct landlock_context *ctx, | ||||||
|  | +		struct landlock_events *events) | ||||||
|  | +{ | ||||||
|  | +	struct landlock_rule *rule; | ||||||
|  | + | ||||||
|  | +	if (!events) | ||||||
|  | +		return false; | ||||||
|  | + | ||||||
|  | +	for (rule = events->rules[event_idx]; rule; rule = rule->prev) { | ||||||
|  | +		u32 ret; | ||||||
|  | + | ||||||
|  | +		if (WARN_ON(!rule->prog)) | ||||||
|  | +			continue; | ||||||
|  | +		rcu_read_lock(); | ||||||
|  | +		ret = BPF_PROG_RUN(rule->prog, (void *)ctx); | ||||||
|  | +		rcu_read_unlock(); | ||||||
|  | +		/* deny access if a program returns a value different than 0 */ | ||||||
|  | +		if (ret) | ||||||
|  | +			return true; | ||||||
|  | +	} | ||||||
|  | +	return false; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  |  int landlock_decide(enum landlock_subtype_event event, | ||||||
|  |  		__u64 ctx_values[CTX_ARG_NB], u32 cmd, const char *hook) | ||||||
|  |  { | ||||||
|  | @@ -109,5 +141,10 @@ int landlock_decide(enum landlock_subtype_event event, | ||||||
|  |  		.arg2 = ctx_values[1], | ||||||
|  |  	}; | ||||||
|  |   | ||||||
|  | +#ifdef CONFIG_SECCOMP_FILTER | ||||||
|  | +	deny = landlock_event_deny(event_idx, &ctx, | ||||||
|  | +			current->seccomp.landlock_events); | ||||||
|  | +#endif /* CONFIG_SECCOMP_FILTER */ | ||||||
|  | + | ||||||
|  |  	return deny ? -EPERM : 0; | ||||||
|  |  } | ||||||
|  | diff --git a/security/landlock/hooks.h b/security/landlock/hooks.h | ||||||
|  | index 2e180f6ed86b..dd0486a4c284 100644 | ||||||
|  | --- a/security/landlock/hooks.h | ||||||
|  | +++ b/security/landlock/hooks.h | ||||||
|  | @@ -12,6 +12,7 @@ | ||||||
|  |  #include <linux/bpf.h> /* enum bpf_access_type */ | ||||||
|  |  #include <linux/lsm_hooks.h> | ||||||
|  |  #include <linux/sched.h> /* struct task_struct */ | ||||||
|  | +#include <linux/seccomp.h> | ||||||
|  |   | ||||||
|  |  /* separators */ | ||||||
|  |  #define SEP_COMMA() , | ||||||
|  | @@ -163,7 +164,11 @@ WRAP_TYPE_RAW_C; | ||||||
|  |   | ||||||
|  |  static inline bool landlocked(const struct task_struct *task) | ||||||
|  |  { | ||||||
|  | +#ifdef CONFIG_SECCOMP_FILTER | ||||||
|  | +	return !!(task->seccomp.landlock_events); | ||||||
|  | +#else | ||||||
|  |  	return false; | ||||||
|  | +#endif /* CONFIG_SECCOMP_FILTER */ | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  __init void landlock_register_hooks(struct security_hook_list *hooks, int count); | ||||||
|  | diff --git a/security/landlock/init.c b/security/landlock/init.c | ||||||
|  | index 909c51c3fa32..9ea7963dcf4c 100644 | ||||||
|  | --- a/security/landlock/init.c | ||||||
|  | +++ b/security/landlock/init.c | ||||||
|  | @@ -135,7 +135,8 @@ static struct bpf_prog_type_list bpf_landlock_type __ro_after_init = { | ||||||
|  |   | ||||||
|  |  void __init landlock_add_hooks(void) | ||||||
|  |  { | ||||||
|  | -	pr_info("landlock: Version %u", LANDLOCK_VERSION); | ||||||
|  | +	pr_info("landlock: Version %u, ready to sandbox with %s\n", | ||||||
|  | +			LANDLOCK_VERSION, "seccomp"); | ||||||
|  |  	landlock_add_hooks_fs(); | ||||||
|  |  	security_add_hooks(NULL, 0); | ||||||
|  |  	bpf_register_prog_type(&bpf_landlock_type); | ||||||
|  | diff --git a/security/landlock/providers.c b/security/landlock/providers.c | ||||||
|  | new file mode 100644 | ||||||
|  | index 000000000000..6d867a39c947 | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/security/landlock/providers.c | ||||||
|  | @@ -0,0 +1,232 @@ | ||||||
|  | +/* | ||||||
|  | + * Landlock LSM - seccomp provider | ||||||
|  | + * | ||||||
|  | + * Copyright © 2017 Mickaël Salaün <mic@digikod.net> | ||||||
|  | + * | ||||||
|  | + * This program is free software; you can redistribute it and/or modify | ||||||
|  | + * it under the terms of the GNU General Public License version 2, as | ||||||
|  | + * published by the Free Software Foundation. | ||||||
|  | + */ | ||||||
|  | + | ||||||
|  | +#include <asm/page.h> /* PAGE_SIZE */ | ||||||
|  | +#include <linux/atomic.h> /* atomic_*(), smp_store_release() */ | ||||||
|  | +#include <linux/bpf.h> /* bpf_prog_put() */ | ||||||
|  | +#include <linux/filter.h> /* struct bpf_prog */ | ||||||
|  | +#include <linux/kernel.h> /* round_up() */ | ||||||
|  | +#include <linux/landlock.h> | ||||||
|  | +#include <linux/sched.h> /* current_cred(), task_no_new_privs() */ | ||||||
|  | +#include <linux/security.h> /* security_capable_noaudit() */ | ||||||
|  | +#include <linux/slab.h> /* alloc(), kfree() */ | ||||||
|  | +#include <linux/types.h> /* atomic_t */ | ||||||
|  | +#include <linux/uaccess.h> /* copy_from_user() */ | ||||||
|  | + | ||||||
|  | +#include "common.h" | ||||||
|  | + | ||||||
|  | +static void put_landlock_rule(struct landlock_rule *rule) | ||||||
|  | +{ | ||||||
|  | +	struct landlock_rule *orig = rule; | ||||||
|  | + | ||||||
|  | +	/* clean up single-reference branches iteratively */ | ||||||
|  | +	while (orig && atomic_dec_and_test(&orig->usage)) { | ||||||
|  | +		struct landlock_rule *freeme = orig; | ||||||
|  | + | ||||||
|  | +		bpf_prog_put(orig->prog); | ||||||
|  | +		orig = orig->prev; | ||||||
|  | +		kfree(freeme); | ||||||
|  | +	} | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +void put_landlock_events(struct landlock_events *events) | ||||||
|  | +{ | ||||||
|  | +	if (events && atomic_dec_and_test(&events->usage)) { | ||||||
|  | +		size_t i; | ||||||
|  | + | ||||||
|  | +		for (i = 0; i < ARRAY_SIZE(events->rules); i++) | ||||||
|  | +			/* XXX: Do we need to use lockless_dereference() here? */ | ||||||
|  | +			put_landlock_rule(events->rules[i]); | ||||||
|  | +		kfree(events); | ||||||
|  | +	} | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static struct landlock_events *new_landlock_events(void) | ||||||
|  | +{ | ||||||
|  | +	struct landlock_events *ret; | ||||||
|  | + | ||||||
|  | +	/* array filled with NULL values */ | ||||||
|  | +	ret = kzalloc(sizeof(*ret), GFP_KERNEL); | ||||||
|  | +	if (!ret) | ||||||
|  | +		return ERR_PTR(-ENOMEM); | ||||||
|  | +	atomic_set(&ret->usage, 1); | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static void add_landlock_rule(struct landlock_events *events, | ||||||
|  | +		struct landlock_rule *rule) | ||||||
|  | +{ | ||||||
|  | +	/* subtype.landlock_rule.event > 0 for loaded programs */ | ||||||
|  | +	u32 event_idx = get_index(rule->prog->subtype.landlock_rule.event); | ||||||
|  | + | ||||||
|  | +	rule->prev = events->rules[event_idx]; | ||||||
|  | +	WARN_ON(atomic_read(&rule->usage)); | ||||||
|  | +	atomic_set(&rule->usage, 1); | ||||||
|  | +	/* do not increment the previous rule usage */ | ||||||
|  | +	smp_store_release(&events->rules[event_idx], rule); | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +/* limit Landlock events to 256KB */ | ||||||
|  | +#define LANDLOCK_EVENTS_MAX_PAGES (1 << 6) | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * landlock_append_prog - attach a Landlock rule to @current_events | ||||||
|  | + * | ||||||
|  | + * @current_events: landlock_events pointer, must be locked (if needed) to | ||||||
|  | + *                  prevent a concurrent put/free. This pointer must not be | ||||||
|  | + *                  freed after the call. | ||||||
|  | + * @prog: non-NULL Landlock rule to append to @current_events. @prog will be | ||||||
|  | + *        owned by landlock_append_prog() and freed if an error happened. | ||||||
|  | + * | ||||||
|  | + * Return @current_events or a new pointer when OK. Return a pointer error | ||||||
|  | + * otherwise. | ||||||
|  | + */ | ||||||
|  | +static struct landlock_events *landlock_append_prog( | ||||||
|  | +		struct landlock_events *current_events, struct bpf_prog *prog) | ||||||
|  | +{ | ||||||
|  | +	struct landlock_events *new_events = current_events; | ||||||
|  | +	unsigned long pages; | ||||||
|  | +	struct landlock_rule *rule; | ||||||
|  | +	u32 event_idx; | ||||||
|  | + | ||||||
|  | +	if (prog->type != BPF_PROG_TYPE_LANDLOCK) { | ||||||
|  | +		new_events = ERR_PTR(-EINVAL); | ||||||
|  | +		goto put_prog; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	/* validate memory size allocation */ | ||||||
|  | +	pages = prog->pages; | ||||||
|  | +	if (current_events) { | ||||||
|  | +		size_t i; | ||||||
|  | + | ||||||
|  | +		for (i = 0; i < ARRAY_SIZE(current_events->rules); i++) { | ||||||
|  | +			struct landlock_rule *walker_r; | ||||||
|  | + | ||||||
|  | +			for (walker_r = current_events->rules[i]; walker_r; | ||||||
|  | +					walker_r = walker_r->prev) | ||||||
|  | +				pages += walker_r->prog->pages; | ||||||
|  | +		} | ||||||
|  | +		/* count a struct landlock_events if we need to allocate one */ | ||||||
|  | +		if (atomic_read(¤t_events->usage) != 1) | ||||||
|  | +			pages += round_up(sizeof(*current_events), PAGE_SIZE) / | ||||||
|  | +				PAGE_SIZE; | ||||||
|  | +	} | ||||||
|  | +	if (pages > LANDLOCK_EVENTS_MAX_PAGES) { | ||||||
|  | +		new_events = ERR_PTR(-E2BIG); | ||||||
|  | +		goto put_prog; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	rule = kzalloc(sizeof(*rule), GFP_KERNEL); | ||||||
|  | +	if (!rule) { | ||||||
|  | +		new_events = ERR_PTR(-ENOMEM); | ||||||
|  | +		goto put_prog; | ||||||
|  | +	} | ||||||
|  | +	rule->prog = prog; | ||||||
|  | + | ||||||
|  | +	/* subtype.landlock_rule.event > 0 for loaded programs */ | ||||||
|  | +	event_idx = get_index(rule->prog->subtype.landlock_rule.event); | ||||||
|  | + | ||||||
|  | +	if (!new_events) { | ||||||
|  | +		/* | ||||||
|  | +		 * If there is no Landlock events used by the current task, | ||||||
|  | +		 * then create a new one. | ||||||
|  | +		 */ | ||||||
|  | +		new_events = new_landlock_events(); | ||||||
|  | +		if (IS_ERR(new_events)) | ||||||
|  | +			goto put_rule; | ||||||
|  | +	} else if (atomic_read(¤t_events->usage) > 1) { | ||||||
|  | +		/* | ||||||
|  | +		 * If the current task is not the sole user of its Landlock | ||||||
|  | +		 * events, then duplicate them. | ||||||
|  | +		 */ | ||||||
|  | +		size_t i; | ||||||
|  | + | ||||||
|  | +		new_events = new_landlock_events(); | ||||||
|  | +		if (IS_ERR(new_events)) | ||||||
|  | +			goto put_rule; | ||||||
|  | +		for (i = 0; i < ARRAY_SIZE(new_events->rules); i++) { | ||||||
|  | +			new_events->rules[i] = | ||||||
|  | +				lockless_dereference(current_events->rules[i]); | ||||||
|  | +			if (new_events->rules[i]) | ||||||
|  | +				atomic_inc(&new_events->rules[i]->usage); | ||||||
|  | +		} | ||||||
|  | + | ||||||
|  | +		/* | ||||||
|  | +		 * Landlock events from the current task will not be freed here | ||||||
|  | +		 * because the usage is strictly greater than 1. It is only | ||||||
|  | +		 * prevented to be freed by another subject thanks to the | ||||||
|  | +		 * caller of landlock_append_prog() which should be locked if | ||||||
|  | +		 * needed. | ||||||
|  | +		 */ | ||||||
|  | +		put_landlock_events(current_events); | ||||||
|  | +	} | ||||||
|  | +	add_landlock_rule(new_events, rule); | ||||||
|  | +	return new_events; | ||||||
|  | + | ||||||
|  | +put_prog: | ||||||
|  | +	bpf_prog_put(prog); | ||||||
|  | +	return new_events; | ||||||
|  | + | ||||||
|  | +put_rule: | ||||||
|  | +	put_landlock_rule(rule); | ||||||
|  | +	return new_events; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * landlock_seccomp_append_prog - attach a Landlock rule to the current process | ||||||
|  | + * | ||||||
|  | + * current->seccomp.landlock_events is lazily allocated. When a process fork, | ||||||
|  | + * only a pointer is copied. When a new event is added by a process, if there | ||||||
|  | + * is other references to this process' landlock_events, then a new allocation | ||||||
|  | + * is made to contain an array pointing to Landlock rule lists. This design | ||||||
|  | + * enable low-performance impact and is memory efficient while keeping the | ||||||
|  | + * property of append-only rules. | ||||||
|  | + * | ||||||
|  | + * @flags: not used for now, but could be used for TSYNC | ||||||
|  | + * @user_bpf_fd: file descriptor pointing to a loaded Landlock rule | ||||||
|  | + */ | ||||||
|  | +#ifdef CONFIG_SECCOMP_FILTER | ||||||
|  | +int landlock_seccomp_append_prog(unsigned int flags, | ||||||
|  | +		const char __user *user_bpf_fd) | ||||||
|  | +{ | ||||||
|  | +	struct landlock_events *new_events; | ||||||
|  | +	struct bpf_prog *prog; | ||||||
|  | +	int bpf_fd; | ||||||
|  | + | ||||||
|  | +	/* force no_new_privs to limit privilege escalation */ | ||||||
|  | +	if (!task_no_new_privs(current)) | ||||||
|  | +		return -EPERM; | ||||||
|  | +	/* will be removed in the future to allow unprivileged tasks */ | ||||||
|  | +	if (!capable(CAP_SYS_ADMIN)) | ||||||
|  | +		return -EPERM; | ||||||
|  | +	/* enable to check if Landlock is supported with early EFAULT */ | ||||||
|  | +	if (!user_bpf_fd) | ||||||
|  | +		return -EFAULT; | ||||||
|  | +	if (flags) | ||||||
|  | +		return -EINVAL; | ||||||
|  | +	if (copy_from_user(&bpf_fd, user_bpf_fd, sizeof(bpf_fd))) | ||||||
|  | +		return -EFAULT; | ||||||
|  | +	prog = bpf_prog_get(bpf_fd); | ||||||
|  | +	if (IS_ERR(prog)) | ||||||
|  | +		return PTR_ERR(prog); | ||||||
|  | + | ||||||
|  | +	/* | ||||||
|  | +	 * We don't need to lock anything for the current process hierarchy, | ||||||
|  | +	 * everything is guarded by the atomic counters. | ||||||
|  | +	 */ | ||||||
|  | +	new_events = landlock_append_prog(current->seccomp.landlock_events, | ||||||
|  | +			prog); | ||||||
|  | +	/* @prog is managed/freed by landlock_append_prog() */ | ||||||
|  | +	if (IS_ERR(new_events)) | ||||||
|  | +		return PTR_ERR(new_events); | ||||||
|  | +	current->seccomp.landlock_events = new_events; | ||||||
|  | +	return 0; | ||||||
|  | +} | ||||||
|  | +#endif /* CONFIG_SECCOMP_FILTER */ | ||||||
|  | --  | ||||||
|  | 2.11.0 | ||||||
|  |  | ||||||
| @@ -0,0 +1,216 @@ | |||||||
|  | From 2a43bc167fa63c0d02bed4b5826bdfc1fd719714 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= <mic@digikod.net> | ||||||
|  | Date: Wed, 29 Mar 2017 01:30:33 +0200 | ||||||
|  | Subject: [PATCH 11/12] landlock: Add ptrace restrictions | ||||||
|  | MIME-Version: 1.0 | ||||||
|  | Content-Type: text/plain; charset=UTF-8 | ||||||
|  | Content-Transfer-Encoding: 8bit | ||||||
|  |  | ||||||
|  | A landlocked process has less privileges than a non-landlocked process | ||||||
|  | and must then be subject to additional restrictions when manipulating | ||||||
|  | processes. To be allowed to use ptrace(2) and related syscalls on a | ||||||
|  | target process, a landlocked process must have a subset of the target | ||||||
|  | process' rules. | ||||||
|  |  | ||||||
|  | New in v6 | ||||||
|  |  | ||||||
|  | Signed-off-by: Mickaël Salaün <mic@digikod.net> | ||||||
|  | Cc: Alexei Starovoitov <ast@kernel.org> | ||||||
|  | Cc: Andy Lutomirski <luto@amacapital.net> | ||||||
|  | Cc: Daniel Borkmann <daniel@iogearbox.net> | ||||||
|  | Cc: David S. Miller <davem@davemloft.net> | ||||||
|  | Cc: James Morris <james.l.morris@oracle.com> | ||||||
|  | Cc: Kees Cook <keescook@chromium.org> | ||||||
|  | Cc: Serge E. Hallyn <serge@hallyn.com> | ||||||
|  | (cherry picked from commit 9f9bb82c0f1a9694263a0a1d6833f3049f0abec2) | ||||||
|  | --- | ||||||
|  |  security/landlock/Makefile       |   2 +- | ||||||
|  |  security/landlock/hooks_ptrace.c | 126 +++++++++++++++++++++++++++++++++++++++ | ||||||
|  |  security/landlock/hooks_ptrace.h |  11 ++++ | ||||||
|  |  security/landlock/init.c         |   2 + | ||||||
|  |  4 files changed, 140 insertions(+), 1 deletion(-) | ||||||
|  |  create mode 100644 security/landlock/hooks_ptrace.c | ||||||
|  |  create mode 100644 security/landlock/hooks_ptrace.h | ||||||
|  |  | ||||||
|  | diff --git a/security/landlock/Makefile b/security/landlock/Makefile | ||||||
|  | index da8ba8b5183e..099a56ca4842 100644 | ||||||
|  | --- a/security/landlock/Makefile | ||||||
|  | +++ b/security/landlock/Makefile | ||||||
|  | @@ -2,4 +2,4 @@ ccflags-$(CONFIG_SECURITY_LANDLOCK) += -Werror=unused-function | ||||||
|  |   | ||||||
|  |  obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o | ||||||
|  |   | ||||||
|  | -landlock-y := init.o providers.o hooks.o hooks_fs.o | ||||||
|  | +landlock-y := init.o providers.o hooks.o hooks_fs.o hooks_ptrace.o | ||||||
|  | diff --git a/security/landlock/hooks_ptrace.c b/security/landlock/hooks_ptrace.c | ||||||
|  | new file mode 100644 | ||||||
|  | index 000000000000..8ab53baba9ad | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/security/landlock/hooks_ptrace.c | ||||||
|  | @@ -0,0 +1,126 @@ | ||||||
|  | +/* | ||||||
|  | + * Landlock LSM - ptrace hooks | ||||||
|  | + * | ||||||
|  | + * Copyright © 2017 Mickaël Salaün <mic@digikod.net> | ||||||
|  | + * | ||||||
|  | + * This program is free software; you can redistribute it and/or modify | ||||||
|  | + * it under the terms of the GNU General Public License version 2, as | ||||||
|  | + * published by the Free Software Foundation. | ||||||
|  | + */ | ||||||
|  | + | ||||||
|  | +#include <asm/current.h> | ||||||
|  | +#include <linux/kernel.h> /* ARRAY_SIZE */ | ||||||
|  | +#include <linux/landlock.h> /* struct landlock_events */ | ||||||
|  | +#include <linux/lsm_hooks.h> | ||||||
|  | +#include <linux/sched.h> /* struct task_struct */ | ||||||
|  | +#include <linux/seccomp.h> | ||||||
|  | + | ||||||
|  | +#include "hooks.h" /* landlocked() */ | ||||||
|  | + | ||||||
|  | +#include "hooks_ptrace.h" | ||||||
|  | + | ||||||
|  | + | ||||||
|  | +static bool landlock_events_are_subset(const struct landlock_events *parent, | ||||||
|  | +		const struct landlock_events *child) | ||||||
|  | +{ | ||||||
|  | +	size_t i; | ||||||
|  | + | ||||||
|  | +	if (!parent || !child) | ||||||
|  | +		return false; | ||||||
|  | +	if (parent == child) | ||||||
|  | +		return true; | ||||||
|  | + | ||||||
|  | +	for (i = 0; i < ARRAY_SIZE(child->rules); i++) { | ||||||
|  | +		struct landlock_rule *walker; | ||||||
|  | +		bool found_parent = false; | ||||||
|  | + | ||||||
|  | +		if (!parent->rules[i]) | ||||||
|  | +			continue; | ||||||
|  | +		for (walker = child->rules[i]; walker; walker = walker->prev) { | ||||||
|  | +			if (walker == parent->rules[i]) { | ||||||
|  | +				found_parent = true; | ||||||
|  | +				break; | ||||||
|  | +			} | ||||||
|  | +		} | ||||||
|  | +		if (!found_parent) | ||||||
|  | +			return false; | ||||||
|  | +	} | ||||||
|  | +	return true; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static bool landlock_task_has_subset_events(const struct task_struct *parent, | ||||||
|  | +		const struct task_struct *child) | ||||||
|  | +{ | ||||||
|  | +#ifdef CONFIG_SECCOMP_FILTER | ||||||
|  | +	if (landlock_events_are_subset(parent->seccomp.landlock_events, | ||||||
|  | +				child->seccomp.landlock_events)) | ||||||
|  | +		/* must be ANDed with other providers (i.e. cgroup) */ | ||||||
|  | +		return true; | ||||||
|  | +#endif /* CONFIG_SECCOMP_FILTER */ | ||||||
|  | +	return false; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * landlock_ptrace_access_check - determine whether the current process may | ||||||
|  | + *				  access another | ||||||
|  | + * | ||||||
|  | + * @child: the process to be accessed | ||||||
|  | + * @mode: the mode of attachment | ||||||
|  | + * | ||||||
|  | + * If the current task has Landlock rules, then the child must have at least | ||||||
|  | + * the same rules.  Else denied. | ||||||
|  | + * | ||||||
|  | + * Determine whether a process may access another, returning 0 if permission | ||||||
|  | + * granted, -errno if denied. | ||||||
|  | + */ | ||||||
|  | +static int landlock_ptrace_access_check(struct task_struct *child, | ||||||
|  | +		unsigned int mode) | ||||||
|  | +{ | ||||||
|  | +	if (!landlocked(current)) | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	if (!landlocked(child)) | ||||||
|  | +		return -EPERM; | ||||||
|  | + | ||||||
|  | +	if (landlock_task_has_subset_events(current, child)) | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	return -EPERM; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +/** | ||||||
|  | + * landlock_ptrace_traceme - determine whether another process may trace the | ||||||
|  | + *			     current one | ||||||
|  | + * | ||||||
|  | + * @parent: the task proposed to be the tracer | ||||||
|  | + * | ||||||
|  | + * If the parent has Landlock rules, then the current task must have the same | ||||||
|  | + * or more rules. | ||||||
|  | + * Else denied. | ||||||
|  | + * | ||||||
|  | + * Determine whether the nominated task is permitted to trace the current | ||||||
|  | + * process, returning 0 if permission is granted, -errno if denied. | ||||||
|  | + */ | ||||||
|  | +static int landlock_ptrace_traceme(struct task_struct *parent) | ||||||
|  | +{ | ||||||
|  | +	if (!landlocked(parent)) | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	if (!landlocked(current)) | ||||||
|  | +		return -EPERM; | ||||||
|  | + | ||||||
|  | +	if (landlock_task_has_subset_events(parent, current)) | ||||||
|  | +		return 0; | ||||||
|  | + | ||||||
|  | +	return -EPERM; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +static struct security_hook_list landlock_hooks[] = { | ||||||
|  | +	LSM_HOOK_INIT(ptrace_access_check, landlock_ptrace_access_check), | ||||||
|  | +	LSM_HOOK_INIT(ptrace_traceme, landlock_ptrace_traceme), | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +__init void landlock_add_hooks_ptrace(void) | ||||||
|  | +{ | ||||||
|  | +	landlock_register_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks)); | ||||||
|  | +} | ||||||
|  | diff --git a/security/landlock/hooks_ptrace.h b/security/landlock/hooks_ptrace.h | ||||||
|  | new file mode 100644 | ||||||
|  | index 000000000000..15b1f3479e0e | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/security/landlock/hooks_ptrace.h | ||||||
|  | @@ -0,0 +1,11 @@ | ||||||
|  | +/* | ||||||
|  | + * Landlock LSM - ptrace hooks | ||||||
|  | + * | ||||||
|  | + * Copyright © 2017 Mickaël Salaün <mic@digikod.net> | ||||||
|  | + * | ||||||
|  | + * This program is free software; you can redistribute it and/or modify | ||||||
|  | + * it under the terms of the GNU General Public License version 2, as | ||||||
|  | + * published by the Free Software Foundation. | ||||||
|  | + */ | ||||||
|  | + | ||||||
|  | +__init void landlock_add_hooks_ptrace(void); | ||||||
|  | diff --git a/security/landlock/init.c b/security/landlock/init.c | ||||||
|  | index 9ea7963dcf4c..74f0e17a92f6 100644 | ||||||
|  | --- a/security/landlock/init.c | ||||||
|  | +++ b/security/landlock/init.c | ||||||
|  | @@ -14,6 +14,7 @@ | ||||||
|  |  #include <linux/lsm_hooks.h> | ||||||
|  |   | ||||||
|  |  #include "hooks_fs.h" | ||||||
|  | +#include "hooks_ptrace.h" | ||||||
|  |   | ||||||
|  |   | ||||||
|  |  static inline bool bpf_landlock_is_valid_access(int off, int size, | ||||||
|  | @@ -137,6 +138,7 @@ void __init landlock_add_hooks(void) | ||||||
|  |  { | ||||||
|  |  	pr_info("landlock: Version %u, ready to sandbox with %s\n", | ||||||
|  |  			LANDLOCK_VERSION, "seccomp"); | ||||||
|  | +	landlock_add_hooks_ptrace(); | ||||||
|  |  	landlock_add_hooks_fs(); | ||||||
|  |  	security_add_hooks(NULL, 0); | ||||||
|  |  	bpf_register_prog_type(&bpf_landlock_type); | ||||||
|  | --  | ||||||
|  | 2.11.0 | ||||||
|  |  | ||||||
| @@ -0,0 +1,355 @@ | |||||||
|  | From 9fca752f05909f12edd32f619ec40d38a6c2b305 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= <mic@digikod.net> | ||||||
|  | Date: Wed, 29 Mar 2017 01:30:33 +0200 | ||||||
|  | Subject: [PATCH 12/12] bpf: Add a Landlock sandbox example | ||||||
|  | MIME-Version: 1.0 | ||||||
|  | Content-Type: text/plain; charset=UTF-8 | ||||||
|  | Content-Transfer-Encoding: 8bit | ||||||
|  |  | ||||||
|  | Add a basic sandbox tool to create a process isolated from some part of | ||||||
|  | the system. This sandbox create a read-only environment. It is only | ||||||
|  | allowed to write to a character device such as a TTY: | ||||||
|  |  | ||||||
|  |   # :> X | ||||||
|  |   # echo $? | ||||||
|  |   0 | ||||||
|  |   # ./samples/bpf/landlock1 /bin/sh -i | ||||||
|  |   Launching a new sandboxed process. | ||||||
|  |   # :> Y | ||||||
|  |   cannot create Y: Operation not permitted | ||||||
|  |  | ||||||
|  | Changes since v5: | ||||||
|  | * cosmetic fixes | ||||||
|  | * rebase | ||||||
|  |  | ||||||
|  | Changes since v4: | ||||||
|  | * write Landlock rule in C and compiled it with LLVM | ||||||
|  | * remove cgroup handling | ||||||
|  | * remove path handling: only handle a read-only environment | ||||||
|  | * remove errno return codes | ||||||
|  |  | ||||||
|  | Changes since v3: | ||||||
|  | * remove seccomp and origin field: completely free from seccomp programs | ||||||
|  | * handle more FS-related hooks | ||||||
|  | * handle inode hooks and directory traversal | ||||||
|  | * add faked but consistent view thanks to ENOENT | ||||||
|  | * add /lib64 in the example | ||||||
|  | * fix spelling | ||||||
|  | * rename some types and definitions (e.g. SECCOMP_ADD_LANDLOCK_RULE) | ||||||
|  |  | ||||||
|  | Changes since v2: | ||||||
|  | * use BPF_PROG_ATTACH for cgroup handling | ||||||
|  |  | ||||||
|  | Signed-off-by: Mickaël Salaün <mic@digikod.net> | ||||||
|  | Cc: Alexei Starovoitov <ast@kernel.org> | ||||||
|  | Cc: Andy Lutomirski <luto@amacapital.net> | ||||||
|  | Cc: Daniel Borkmann <daniel@iogearbox.net> | ||||||
|  | Cc: David S. Miller <davem@davemloft.net> | ||||||
|  | Cc: James Morris <james.l.morris@oracle.com> | ||||||
|  | Cc: Kees Cook <keescook@chromium.org> | ||||||
|  | Cc: Serge E. Hallyn <serge@hallyn.com> | ||||||
|  | (cherry picked from commit 9c5c745d4c0640a96f30f072cb835c21e7bd3ca6) | ||||||
|  | --- | ||||||
|  |  samples/bpf/Makefile         |   4 ++ | ||||||
|  |  samples/bpf/bpf_load.c       |  30 +++++++++++-- | ||||||
|  |  samples/bpf/landlock1_kern.c |  46 +++++++++++++++++++ | ||||||
|  |  samples/bpf/landlock1_user.c | 102 +++++++++++++++++++++++++++++++++++++++++++ | ||||||
|  |  4 files changed, 178 insertions(+), 4 deletions(-) | ||||||
|  |  create mode 100644 samples/bpf/landlock1_kern.c | ||||||
|  |  create mode 100644 samples/bpf/landlock1_user.c | ||||||
|  |  | ||||||
|  | diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile | ||||||
|  | index 72c58675973e..c9ce3b2e7a7e 100644 | ||||||
|  | --- a/samples/bpf/Makefile | ||||||
|  | +++ b/samples/bpf/Makefile | ||||||
|  | @@ -28,6 +28,7 @@ hostprogs-y += test_current_task_under_cgroup | ||||||
|  |  hostprogs-y += trace_event | ||||||
|  |  hostprogs-y += sampleip | ||||||
|  |  hostprogs-y += tc_l2_redirect | ||||||
|  | +hostprogs-y += landlock1 | ||||||
|  |   | ||||||
|  |  test_verifier-objs := test_verifier.o libbpf.o | ||||||
|  |  test_maps-objs := test_maps.o libbpf.o | ||||||
|  | @@ -58,6 +59,7 @@ test_current_task_under_cgroup-objs := bpf_load.o libbpf.o \ | ||||||
|  |  trace_event-objs := bpf_load.o libbpf.o trace_event_user.o | ||||||
|  |  sampleip-objs := bpf_load.o libbpf.o sampleip_user.o | ||||||
|  |  tc_l2_redirect-objs := bpf_load.o libbpf.o tc_l2_redirect_user.o | ||||||
|  | +landlock1-objs := bpf_load.o libbpf.o landlock1_user.o | ||||||
|  |   | ||||||
|  |  # Tell kbuild to always build the programs | ||||||
|  |  always := $(hostprogs-y) | ||||||
|  | @@ -88,6 +90,7 @@ always += xdp2_kern.o | ||||||
|  |  always += test_current_task_under_cgroup_kern.o | ||||||
|  |  always += trace_event_kern.o | ||||||
|  |  always += sampleip_kern.o | ||||||
|  | +always += landlock1_kern.o | ||||||
|  |   | ||||||
|  |  HOSTCFLAGS += -I$(objtree)/usr/include | ||||||
|  |   | ||||||
|  | @@ -115,6 +118,7 @@ HOSTLOADLIBES_test_current_task_under_cgroup += -lelf | ||||||
|  |  HOSTLOADLIBES_trace_event += -lelf | ||||||
|  |  HOSTLOADLIBES_sampleip += -lelf | ||||||
|  |  HOSTLOADLIBES_tc_l2_redirect += -l elf | ||||||
|  | +HOSTLOADLIBES_landlock1 += -lelf | ||||||
|  |   | ||||||
|  |  # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline: | ||||||
|  |  #  make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang | ||||||
|  | diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c | ||||||
|  | index 40cf828a37c7..1a461afb1d82 100644 | ||||||
|  | --- a/samples/bpf/bpf_load.c | ||||||
|  | +++ b/samples/bpf/bpf_load.c | ||||||
|  | @@ -25,6 +25,8 @@ | ||||||
|  |   | ||||||
|  |  static char license[128]; | ||||||
|  |  static int kern_version; | ||||||
|  | +static union bpf_prog_subtype subtype = {}; | ||||||
|  | +static bool has_subtype; | ||||||
|  |  static bool processed_sec[128]; | ||||||
|  |  int map_fd[MAX_MAPS]; | ||||||
|  |  int prog_fd[MAX_PROGS]; | ||||||
|  | @@ -52,6 +54,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size) | ||||||
|  |  	bool is_tracepoint = strncmp(event, "tracepoint/", 11) == 0; | ||||||
|  |  	bool is_xdp = strncmp(event, "xdp", 3) == 0; | ||||||
|  |  	bool is_perf_event = strncmp(event, "perf_event", 10) == 0; | ||||||
|  | +	bool is_landlock = strncmp(event, "landlock", 8) == 0; | ||||||
|  |  	enum bpf_prog_type prog_type; | ||||||
|  |  	char buf[256]; | ||||||
|  |  	int fd, efd, err, id; | ||||||
|  | @@ -73,6 +76,13 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size) | ||||||
|  |  		prog_type = BPF_PROG_TYPE_XDP; | ||||||
|  |  	} else if (is_perf_event) { | ||||||
|  |  		prog_type = BPF_PROG_TYPE_PERF_EVENT; | ||||||
|  | +	} else if (is_landlock) { | ||||||
|  | +		prog_type = BPF_PROG_TYPE_LANDLOCK; | ||||||
|  | +		if (!has_subtype) { | ||||||
|  | +			printf("No subtype\n"); | ||||||
|  | +			return -1; | ||||||
|  | +		} | ||||||
|  | +		st = &subtype; | ||||||
|  |  	} else { | ||||||
|  |  		printf("Unknown event '%s'\n", event); | ||||||
|  |  		return -1; | ||||||
|  | @@ -86,7 +96,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size) | ||||||
|  |   | ||||||
|  |  	prog_fd[prog_cnt++] = fd; | ||||||
|  |   | ||||||
|  | -	if (is_xdp || is_perf_event) | ||||||
|  | +	if (is_xdp || is_perf_event || is_landlock) | ||||||
|  |  		return 0; | ||||||
|  |   | ||||||
|  |  	if (is_socket) { | ||||||
|  | @@ -261,6 +271,7 @@ int load_bpf_file(char *path) | ||||||
|  |  	kern_version = 0; | ||||||
|  |  	memset(license, 0, sizeof(license)); | ||||||
|  |  	memset(processed_sec, 0, sizeof(processed_sec)); | ||||||
|  | +	has_subtype = false; | ||||||
|  |   | ||||||
|  |  	if (elf_version(EV_CURRENT) == EV_NONE) | ||||||
|  |  		return 1; | ||||||
|  | @@ -306,6 +317,16 @@ int load_bpf_file(char *path) | ||||||
|  |  			processed_sec[i] = true; | ||||||
|  |  			if (load_maps(data->d_buf, data->d_size)) | ||||||
|  |  				return 1; | ||||||
|  | +		} else if (strcmp(shname, "subtype") == 0) { | ||||||
|  | +			processed_sec[i] = true; | ||||||
|  | +			if (data->d_size != sizeof(union bpf_prog_subtype)) { | ||||||
|  | +				printf("invalid size of subtype section %zd\n", | ||||||
|  | +				       data->d_size); | ||||||
|  | +				return 1; | ||||||
|  | +			} | ||||||
|  | +			memcpy(&subtype, data->d_buf, | ||||||
|  | +			       sizeof(union bpf_prog_subtype)); | ||||||
|  | +			has_subtype = true; | ||||||
|  |  		} else if (shdr.sh_type == SHT_SYMTAB) { | ||||||
|  |  			symbols = data; | ||||||
|  |  		} | ||||||
|  | @@ -338,14 +359,14 @@ int load_bpf_file(char *path) | ||||||
|  |  			    memcmp(shname_prog, "tracepoint/", 11) == 0 || | ||||||
|  |  			    memcmp(shname_prog, "xdp", 3) == 0 || | ||||||
|  |  			    memcmp(shname_prog, "perf_event", 10) == 0 || | ||||||
|  | -			    memcmp(shname_prog, "socket", 6) == 0) | ||||||
|  | +			    memcmp(shname_prog, "socket", 6) == 0 || | ||||||
|  | +			    memcmp(shname_prog, "landlock", 8) == 0) | ||||||
|  |  				load_and_attach(shname_prog, insns, data_prog->d_size); | ||||||
|  |  		} | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  |  	/* load programs that don't use maps */ | ||||||
|  |  	for (i = 1; i < ehdr.e_shnum; i++) { | ||||||
|  | - | ||||||
|  |  		if (processed_sec[i]) | ||||||
|  |  			continue; | ||||||
|  |   | ||||||
|  | @@ -357,7 +378,8 @@ int load_bpf_file(char *path) | ||||||
|  |  		    memcmp(shname, "tracepoint/", 11) == 0 || | ||||||
|  |  		    memcmp(shname, "xdp", 3) == 0 || | ||||||
|  |  		    memcmp(shname, "perf_event", 10) == 0 || | ||||||
|  | -		    memcmp(shname, "socket", 6) == 0) | ||||||
|  | +		    memcmp(shname, "socket", 6) == 0 || | ||||||
|  | +		    memcmp(shname, "landlock", 8) == 0) | ||||||
|  |  			load_and_attach(shname, data->d_buf, data->d_size); | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  | diff --git a/samples/bpf/landlock1_kern.c b/samples/bpf/landlock1_kern.c | ||||||
|  | new file mode 100644 | ||||||
|  | index 000000000000..b8a9b0ca84c9 | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/samples/bpf/landlock1_kern.c | ||||||
|  | @@ -0,0 +1,46 @@ | ||||||
|  | +/* | ||||||
|  | + * Landlock rule - partial read-only filesystem | ||||||
|  | + * | ||||||
|  | + * Copyright © 2017 Mickaël Salaün <mic@digikod.net> | ||||||
|  | + * | ||||||
|  | + * This program is free software; you can redistribute it and/or modify | ||||||
|  | + * it under the terms of the GNU General Public License version 2, as | ||||||
|  | + * published by the Free Software Foundation. | ||||||
|  | + */ | ||||||
|  | + | ||||||
|  | +#define KBUILD_MODNAME "foo" | ||||||
|  | +#include <uapi/linux/bpf.h> | ||||||
|  | +#include <uapi/linux/stat.h> /* S_ISCHR() */ | ||||||
|  | +#include "bpf_helpers.h" | ||||||
|  | + | ||||||
|  | +SEC("landlock1") | ||||||
|  | +static int landlock_fs_prog1(struct landlock_context *ctx) | ||||||
|  | +{ | ||||||
|  | +	char fmt_error[] = "landlock1: error: get_mode:%lld\n"; | ||||||
|  | +	char fmt_name[] = "landlock1: syscall:%d\n"; | ||||||
|  | +	long long ret; | ||||||
|  | + | ||||||
|  | +	if (!(ctx->arg2 & LANDLOCK_ACTION_FS_WRITE)) | ||||||
|  | +		return 0; | ||||||
|  | +	ret = bpf_handle_fs_get_mode((void *)ctx->arg1); | ||||||
|  | +	if (ret < 0) { | ||||||
|  | +		bpf_trace_printk(fmt_error, sizeof(fmt_error), ret); | ||||||
|  | +		return 1; | ||||||
|  | +	} | ||||||
|  | +	if (S_ISCHR(ret)) | ||||||
|  | +		return 0; | ||||||
|  | +	bpf_trace_printk(fmt_name, sizeof(fmt_name), ctx->syscall_nr); | ||||||
|  | +	return 1; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +SEC("subtype") | ||||||
|  | +static union bpf_prog_subtype _subtype = { | ||||||
|  | +	.landlock_rule = { | ||||||
|  | +		.version = 1, | ||||||
|  | +		.event = LANDLOCK_SUBTYPE_EVENT_FS, | ||||||
|  | +		.ability = LANDLOCK_SUBTYPE_ABILITY_DEBUG, | ||||||
|  | +	} | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +SEC("license") | ||||||
|  | +static const char _license[] = "GPL"; | ||||||
|  | diff --git a/samples/bpf/landlock1_user.c b/samples/bpf/landlock1_user.c | ||||||
|  | new file mode 100644 | ||||||
|  | index 000000000000..6f79eb0ee6db | ||||||
|  | --- /dev/null | ||||||
|  | +++ b/samples/bpf/landlock1_user.c | ||||||
|  | @@ -0,0 +1,102 @@ | ||||||
|  | +/* | ||||||
|  | + * Landlock sandbox - partial read-only filesystem | ||||||
|  | + * | ||||||
|  | + * Copyright © 2017 Mickaël Salaün <mic@digikod.net> | ||||||
|  | + * | ||||||
|  | + * This program is free software; you can redistribute it and/or modify | ||||||
|  | + * it under the terms of the GNU General Public License version 2, as | ||||||
|  | + * published by the Free Software Foundation. | ||||||
|  | + */ | ||||||
|  | + | ||||||
|  | +#include "bpf_load.h" | ||||||
|  | +#include "libbpf.h" | ||||||
|  | + | ||||||
|  | +#define _GNU_SOURCE | ||||||
|  | +#include <errno.h> | ||||||
|  | +#include <fcntl.h> /* open() */ | ||||||
|  | +#include <linux/bpf.h> | ||||||
|  | +#include <linux/filter.h> | ||||||
|  | +#include <linux/prctl.h> | ||||||
|  | +#include <linux/seccomp.h> | ||||||
|  | +#include <stddef.h> | ||||||
|  | +#include <stdio.h> | ||||||
|  | +#include <stdlib.h> | ||||||
|  | +#include <string.h> | ||||||
|  | +#include <sys/prctl.h> | ||||||
|  | +#include <sys/syscall.h> | ||||||
|  | +#include <unistd.h> | ||||||
|  | + | ||||||
|  | +#ifndef seccomp | ||||||
|  | +static int seccomp(unsigned int op, unsigned int flags, void *args) | ||||||
|  | +{ | ||||||
|  | +	errno = 0; | ||||||
|  | +	return syscall(__NR_seccomp, op, flags, args); | ||||||
|  | +} | ||||||
|  | +#endif | ||||||
|  | + | ||||||
|  | +#define ARRAY_SIZE(a)	(sizeof(a) / sizeof(a[0])) | ||||||
|  | +#define MAX_ERRNO	4095 | ||||||
|  | + | ||||||
|  | + | ||||||
|  | +struct landlock_rule { | ||||||
|  | +	enum landlock_subtype_event event; | ||||||
|  | +	struct bpf_insn *bpf; | ||||||
|  | +	size_t size; | ||||||
|  | +}; | ||||||
|  | + | ||||||
|  | +static int apply_sandbox(int prog_fd) | ||||||
|  | +{ | ||||||
|  | +	int ret = 0; | ||||||
|  | + | ||||||
|  | +	/* set up the test sandbox */ | ||||||
|  | +	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { | ||||||
|  | +		perror("prctl(no_new_priv)"); | ||||||
|  | +		return 1; | ||||||
|  | +	} | ||||||
|  | +	if (seccomp(SECCOMP_APPEND_LANDLOCK_RULE, 0, &prog_fd)) { | ||||||
|  | +		perror("seccomp(set_hook)"); | ||||||
|  | +		ret = 1; | ||||||
|  | +	} | ||||||
|  | +	close(prog_fd); | ||||||
|  | + | ||||||
|  | +	return ret; | ||||||
|  | +} | ||||||
|  | + | ||||||
|  | +int main(int argc, char * const argv[], char * const *envp) | ||||||
|  | +{ | ||||||
|  | +	char filename[256]; | ||||||
|  | +	char *cmd_path; | ||||||
|  | +	char * const *cmd_argv; | ||||||
|  | + | ||||||
|  | +	if (argc < 2) { | ||||||
|  | +		fprintf(stderr, "usage: %s <cmd> [args]...\n\n", argv[0]); | ||||||
|  | +		fprintf(stderr, "Launch a command in a read-only environment " | ||||||
|  | +				"(except for character devices).\n"); | ||||||
|  | +		fprintf(stderr, "Display debug with: " | ||||||
|  | +				"cat /sys/kernel/debug/tracing/trace_pipe &\n"); | ||||||
|  | +		return 1; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); | ||||||
|  | +	if (load_bpf_file(filename)) { | ||||||
|  | +		printf("%s", bpf_log_buf); | ||||||
|  | +		return 1; | ||||||
|  | +	} | ||||||
|  | +	if (!prog_fd[0]) { | ||||||
|  | +		if (errno) { | ||||||
|  | +			printf("load_bpf_file: %s\n", strerror(errno)); | ||||||
|  | +		} else { | ||||||
|  | +			printf("load_bpf_file: Error\n"); | ||||||
|  | +		} | ||||||
|  | +		return 1; | ||||||
|  | +	} | ||||||
|  | + | ||||||
|  | +	if (apply_sandbox(prog_fd[0])) | ||||||
|  | +		return 1; | ||||||
|  | +	cmd_path = argv[1]; | ||||||
|  | +	cmd_argv = argv + 1; | ||||||
|  | +	fprintf(stderr, "Launching a new sandboxed process.\n"); | ||||||
|  | +	execve(cmd_path, cmd_argv, envp); | ||||||
|  | +	perror("execve"); | ||||||
|  | +	return 1; | ||||||
|  | +} | ||||||
|  | --  | ||||||
|  | 2.11.0 | ||||||
|  |  | ||||||
		Reference in New Issue
	
	Block a user