linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v7 0/7] bpf: Add bpf_verify_pkcs7_signature() helper
@ 2022-07-12 18:41 Roberto Sassu
  2022-07-12 18:41 ` [PATCH v7 1/7] bpf: Export bpf_dynptr_get_size() Roberto Sassu
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Roberto Sassu @ 2022-07-12 18:41 UTC (permalink / raw)
  To: ast, daniel, john.fastabend, andrii, martin.lau, song, yhs,
	kpsingh, dhowells, jarkko, shuah
  Cc: bpf, keyrings, linux-security-module, linux-kselftest, netdev,
	linux-kernel, Roberto Sassu

One of the desirable features in security is the ability to restrict import
of data to a given system based on data authenticity. If data import can be
restricted, it would be possible to enforce a system-wide policy based on
the signing keys the system owner trusts.

This feature is widely used in the kernel. For example, if the restriction
is enabled, kernel modules can be plugged in only if they are signed with a
key whose public part is in the primary or secondary keyring.

For eBPF, it can be useful as well. For example, it might be useful to
authenticate data an eBPF program makes security decisions on.

After a discussion in the eBPF mailing list, it was decided that the stated
goal should be accomplished by introducing a new helper:
bpf_verify_pkcs7_signature(), dedicated to verify PKCS#7 signatures.

Other than the data and the signature, the helper also receives two
parameters for the keyring, which can be provided as alternatives: one is a
key pointer returned by the new bpf_lookup_user_key() helper, called with a
key serial possibly decided by the user; another is a pre-determined ID
among values defined in include/linux/verification.h.

While the first keyring-related parameter provides great flexibility, it
seems suboptimal in terms of security guarantees, as even if the eBPF
program is assumed to be trusted, the serial used to obtain the key pointer
might come from untrusted user space not choosing one that the system
administrator approves to enforce a mandatory policy.

The second keyring-related parameter instead provides much stronger
guarantees, especially if the pre-determined ID is not passed by user space
but is hardcoded in the eBPF program, and that program is signed. In this
case, bpf_verify_pkcs7_signature() will always perform signature
verification with a key that the system administrator approves, i.e. the
primary, secondary or platform keyring.

bpf_lookup_user_key() comes with the corresponding release helper
bpf_key_put(), to decrement the reference count of the key found with the
former helper. The eBPF verifier has been enhanced to ensure that the
release helper is always called whenever the acquire helper is called, or
otherwise refuses to load the program.

bpf_lookup_user_key() also accepts lookup-specific flags KEY_LOOKUP_CREATE
and KEY_LOOKUP_PARTIAL. Although these are most likely not useful for the
bpf_verify_pkcs7_signature(), newly defined flags could be.

bpf_lookup_user_key() does not request a particular permission to
lookup_user_key(), as it cannot determine it by itself. Also, it should not
get it from the user, as the user could pass an arbitrary value and use the
key for a different purpose. Instead, bpf_lookup_user_key() requests
KEY_DEFER_PERM_CHECK, and defers the permission check to the helper that
actually uses the key, in this patch set to bpf_verify_pkcs7_signature().

Since key_task_permission() is called by the PKCS#7 code during signature
verification, the only additional function bpf_verify_pkcs7_signature() has
to call is key_validate(). With that, the permission check can be
considered complete and equivalent, as it was done by bpf_lookup_user_key()
with the appropriate permission (in this case KEY_NEED_SEARCH).

All helpers can be called only from sleepable programs, because of memory
allocation (with lookup flag KEY_LOOKUP_CREATE) and crypto operations. For
example, the lsm.s/bpf attach point is suitable,
fexit/array_map_update_elem is not.

The correctness of implementation of the new helpers and of their usage is
checked with the introduced tests.

The patch set is organized as follows.

Patch 1 exports bpf_dynptr_get_size(), to obtain the real size of data
carried by a dynamic pointer. Patch 2 makes available for new eBPF helpers
some key-related definitions. Patch 3 introduces the bpf_lookup_user_key()
and bpf_key_put() helpers. Patch 4 introduces the
bpf_verify_pkcs7_signature(). Finally, patches 5-7 introduce the tests.

Changelog

v6:
 - Switch back to key lookup helpers + signature verification (until v5),
   and defer permission check from bpf_lookup_user_key() to
   bpf_verify_pkcs7_signature()
 - Add additional key lookup test to illustrate the usage of the
   KEY_LOOKUP_CREATE flag and validate the flags (suggested by Daniel)
 - Make description of flags of bpf_lookup_user_key() more user-friendly
   (suggested by Daniel)
 - Fix validation of flags parameter in bpf_lookup_user_key() (reported by
   Daniel)
 - Rename bpf_verify_pkcs7_signature() keyring-related parameters to
   user_keyring and system_keyring to make their purpose more clear
 - Accept keyring-related parameters of bpf_verify_pkcs7_signature() as
   alternatives (suggested by KP)
 - Replace unsigned long type with u64 in helper declaration (suggested by
   Daniel)
 - Extend the bpf_verify_pkcs7_signature() test by calling the helper
   without data, by ensuring that the helper enforces the keyring-related
   parameters as alternatives, by ensuring that the helper rejects
   inaccessible and expired keyrings, and by checking all system keyrings
 - Move bpf_lookup_user_key() and bpf_key_put() usage tests to
   ref_tracking.c (suggested by John)
 - Call bpf_lookup_user_key() and bpf_key_put() only in sleepable programs

v5:
 - Move KEY_LOOKUP_ to include/linux/key.h
   for validation of bpf_verify_pkcs7_signature() parameter
 - Remove bpf_lookup_user_key() and bpf_key_put() helpers, and the
   corresponding tests
 - Replace struct key parameter of bpf_verify_pkcs7_signature() with the
   keyring serial and lookup flags
 - Call lookup_user_key() and key_put() in bpf_verify_pkcs7_signature()
   code, to ensure that the retrieved key is used according to the
   permission requested at lookup time
 - Clarified keyring precedence in the description of
   bpf_verify_pkcs7_signature() (suggested by John)
 - Remove newline in the second argument of ASSERT_
 - Fix helper prototype regular expression in bpf_doc.py

v4:
 - Remove bpf_request_key_by_id(), don't return an invalid pointer that
   other helpers can use
 - Pass the keyring ID (without ULONG_MAX, suggested by Alexei) to
   bpf_verify_pkcs7_signature()
 - Introduce bpf_lookup_user_key() and bpf_key_put() helpers (suggested by
   Alexei)
 - Add lookup_key_norelease test, to ensure that the verifier blocks eBPF
   programs which don't decrement the key reference count
 - Parse raw PKCS#7 signature instead of module-style signature in the
   verify_pkcs7_signature test (suggested by Alexei)
 - Parse kernel module in user space and pass raw PKCS#7 signature to the
   eBPF program for signature verification

v3:
 - Rename bpf_verify_signature() back to bpf_verify_pkcs7_signature() to
   avoid managing different parameters for each signature verification
   function in one helper (suggested by Daniel)
 - Use dynamic pointers and export bpf_dynptr_get_size() (suggested by
   Alexei)
 - Introduce bpf_request_key_by_id() to give more flexibility to the caller
   of bpf_verify_pkcs7_signature() to retrieve the appropriate keyring
   (suggested by Alexei)
 - Fix test by reordering the gcc command line, always compile sign-file
 - Improve helper support check mechanism in the test

v2:
 - Rename bpf_verify_pkcs7_signature() to a more generic
   bpf_verify_signature() and pass the signature type (suggested by KP)
 - Move the helper and prototype declaration under #ifdef so that user
   space can probe for support for the helper (suggested by Daniel)
 - Describe better the keyring types (suggested by Daniel)
 - Include linux/bpf.h instead of vmlinux.h to avoid implicit or
   redeclaration
 - Make the test selfcontained (suggested by Alexei)

v1:
 - Don't define new map flag but introduce simple wrapper of
   verify_pkcs7_signature() (suggested by Alexei and KP)

Roberto Sassu (7):
  bpf: Export bpf_dynptr_get_size()
  KEYS: Move KEY_LOOKUP_ to include/linux/key.h
  bpf: Add bpf_lookup_user_key() and bpf_key_put() helpers
  bpf: Add bpf_verify_pkcs7_signature() helper
  selftests: Add verifier tests for bpf_lookup_user_key() and
    bpf_key_put()
  selftests/bpf: Add additional test for bpf_lookup_user_key()
  selftests/bpf: Add test for bpf_verify_pkcs7_signature() helper

 include/linux/bpf.h                           |   1 +
 include/linux/key.h                           |   3 +
 include/uapi/linux/bpf.h                      |  47 ++
 kernel/bpf/bpf_lsm.c                          | 116 +++++
 kernel/bpf/helpers.c                          |   2 +-
 kernel/bpf/verifier.c                         |   6 +-
 scripts/bpf_doc.py                            |   2 +
 security/keys/internal.h                      |   2 -
 tools/include/uapi/linux/bpf.h                |  47 ++
 tools/testing/selftests/bpf/Makefile          |  14 +-
 tools/testing/selftests/bpf/config            |   2 +
 .../bpf/prog_tests/lookup_user_key.c          |  94 ++++
 .../bpf/prog_tests/verify_pkcs7_sig.c         | 410 ++++++++++++++++++
 .../bpf/progs/test_lookup_user_key.c          |  35 ++
 .../bpf/progs/test_verify_pkcs7_sig.c         |  90 ++++
 tools/testing/selftests/bpf/test_verifier.c   |   3 +-
 .../selftests/bpf/verifier/ref_tracking.c     |  66 +++
 .../testing/selftests/bpf/verify_sig_setup.sh | 104 +++++
 18 files changed, 1035 insertions(+), 9 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/lookup_user_key.c
 create mode 100644 tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_lookup_user_key.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c
 create mode 100755 tools/testing/selftests/bpf/verify_sig_setup.sh

-- 
2.25.1


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v7 1/7] bpf: Export bpf_dynptr_get_size()
  2022-07-12 18:41 [PATCH v7 0/7] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
@ 2022-07-12 18:41 ` Roberto Sassu
  2022-07-12 18:41 ` [PATCH v7 2/7] KEYS: Move KEY_LOOKUP_ to include/linux/key.h Roberto Sassu
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Roberto Sassu @ 2022-07-12 18:41 UTC (permalink / raw)
  To: ast, daniel, john.fastabend, andrii, martin.lau, song, yhs,
	kpsingh, dhowells, jarkko, shuah
  Cc: bpf, keyrings, linux-security-module, linux-kselftest, netdev,
	linux-kernel, Roberto Sassu, Joanne Koong

Export bpf_dynptr_get_size(), so that kernel code dealing with eBPF dynamic
pointers can obtain the real size of data carried by this data structure.

Reviewed-by: Joanne Koong <joannelkoong@gmail.com>
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 include/linux/bpf.h  | 1 +
 kernel/bpf/helpers.c | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 2b21f2a3452f..5f5cd968b6ad 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -2545,6 +2545,7 @@ void bpf_dynptr_init(struct bpf_dynptr_kern *ptr, void *data,
 		     enum bpf_dynptr_type type, u32 offset, u32 size);
 void bpf_dynptr_set_null(struct bpf_dynptr_kern *ptr);
 int bpf_dynptr_check_size(u32 size);
+u32 bpf_dynptr_get_size(struct bpf_dynptr_kern *ptr);
 
 #ifdef CONFIG_BPF_LSM
 void bpf_cgroup_atype_get(u32 attach_btf_id, int cgroup_atype);
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index a1c84d256f83..3f5ff8dbd3cb 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -1430,7 +1430,7 @@ static void bpf_dynptr_set_type(struct bpf_dynptr_kern *ptr, enum bpf_dynptr_typ
 	ptr->size |= type << DYNPTR_TYPE_SHIFT;
 }
 
-static u32 bpf_dynptr_get_size(struct bpf_dynptr_kern *ptr)
+u32 bpf_dynptr_get_size(struct bpf_dynptr_kern *ptr)
 {
 	return ptr->size & DYNPTR_SIZE_MASK;
 }
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v7 2/7] KEYS: Move KEY_LOOKUP_ to include/linux/key.h
  2022-07-12 18:41 [PATCH v7 0/7] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
  2022-07-12 18:41 ` [PATCH v7 1/7] bpf: Export bpf_dynptr_get_size() Roberto Sassu
@ 2022-07-12 18:41 ` Roberto Sassu
  2022-07-12 18:41 ` [PATCH v7 3/7] bpf: Add bpf_lookup_user_key() and bpf_key_put() helpers Roberto Sassu
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Roberto Sassu @ 2022-07-12 18:41 UTC (permalink / raw)
  To: ast, daniel, john.fastabend, andrii, martin.lau, song, yhs,
	kpsingh, dhowells, jarkko, shuah
  Cc: bpf, keyrings, linux-security-module, linux-kselftest, netdev,
	linux-kernel, Roberto Sassu

In preparation for the patch that introduces the bpf_lookup_user_key() eBPF
helper, move KEY_LOOKUP_ definitions to include/linux/key.h, to be able to
validate the helper parameters.

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 include/linux/key.h      | 3 +++
 security/keys/internal.h | 2 --
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/include/linux/key.h b/include/linux/key.h
index 7febc4881363..a297e075038c 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -88,6 +88,9 @@ enum key_need_perm {
 	KEY_DEFER_PERM_CHECK,	/* Special: permission check is deferred */
 };
 
+#define KEY_LOOKUP_CREATE	0x01
+#define KEY_LOOKUP_PARTIAL	0x02
+
 struct seq_file;
 struct user_struct;
 struct signal_struct;
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 9b9cf3b6fcbb..3c1e7122076b 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -165,8 +165,6 @@ extern struct key *request_key_and_link(struct key_type *type,
 
 extern bool lookup_user_key_possessed(const struct key *key,
 				      const struct key_match_data *match_data);
-#define KEY_LOOKUP_CREATE	0x01
-#define KEY_LOOKUP_PARTIAL	0x02
 
 extern long join_session_keyring(const char *name);
 extern void key_change_session_keyring(struct callback_head *twork);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v7 3/7] bpf: Add bpf_lookup_user_key() and bpf_key_put() helpers
  2022-07-12 18:41 [PATCH v7 0/7] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
  2022-07-12 18:41 ` [PATCH v7 1/7] bpf: Export bpf_dynptr_get_size() Roberto Sassu
  2022-07-12 18:41 ` [PATCH v7 2/7] KEYS: Move KEY_LOOKUP_ to include/linux/key.h Roberto Sassu
@ 2022-07-12 18:41 ` Roberto Sassu
  2022-07-12 18:41 ` [PATCH v7 4/7] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Roberto Sassu @ 2022-07-12 18:41 UTC (permalink / raw)
  To: ast, daniel, john.fastabend, andrii, martin.lau, song, yhs,
	kpsingh, dhowells, jarkko, shuah
  Cc: bpf, keyrings, linux-security-module, linux-kselftest, netdev,
	linux-kernel, Roberto Sassu

Add the bpf_lookup_user_key() and bpf_key_put() helpers, to respectively
search a key with a given serial and flags, and release the reference count
of the found key.

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 include/uapi/linux/bpf.h       | 25 ++++++++++++++++
 kernel/bpf/bpf_lsm.c           | 53 ++++++++++++++++++++++++++++++++++
 kernel/bpf/verifier.c          |  6 ++--
 scripts/bpf_doc.py             |  2 ++
 tools/include/uapi/linux/bpf.h | 25 ++++++++++++++++
 5 files changed, 109 insertions(+), 2 deletions(-)

diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 379e68fb866f..a3f6506ddeba 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -5327,6 +5327,29 @@ union bpf_attr {
  *		**-EACCES** if the SYN cookie is not valid.
  *
  *		**-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin.
+ *
+ * struct key *bpf_lookup_user_key(u32 serial, u64 flags)
+ *	Description
+ *		Search a key with a given *serial* and the provided *flags*. The
+ *		returned key, if found, has the reference count incremented by
+ *		one, and must be passed to bpf_key_put() when done with it.
+ *		Permission checks are deferred to the time the key is used by
+ *		one of the available key-specific helpers.
+ *
+ *		Set *flags* with 1 to attempt creating a requested special
+ *		keyring (e.g. session keyring), if it doesn't yet exist. Set
+ *		*flags* to 2 to lookup a key without waiting for the key
+ *		construction, and to retrieve uninstantiated keys (keys without
+ *		data attached to them).
+ *	Return
+ *		A key pointer if the key is found, a NULL pointer otherwise.
+ *
+ * void bpf_key_put(struct key *key)
+ *	Description
+ *		Decrement the reference count of *key* obtained with the
+ *		bpf_lookup_user_key() helper.
+ *	Return
+ *		0
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -5537,6 +5560,8 @@ union bpf_attr {
 	FN(tcp_raw_gen_syncookie_ipv6),	\
 	FN(tcp_raw_check_syncookie_ipv4),	\
 	FN(tcp_raw_check_syncookie_ipv6),	\
+	FN(lookup_user_key),		\
+	FN(key_put),			\
 	/* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index d469b7f3deef..0a95e2137d65 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -184,6 +184,51 @@ static const struct bpf_func_proto bpf_get_attach_cookie_proto = {
 	.arg1_type	= ARG_PTR_TO_CTX,
 };
 
+#ifdef CONFIG_KEYS
+BTF_ID_LIST_SINGLE(btf_key_ids, struct, key)
+
+BPF_CALL_2(bpf_lookup_user_key, u32, serial, u64, flags)
+{
+	key_ref_t key_ref;
+
+	/* Keep in sync with include/linux/key.h. */
+	if (flags > (KEY_LOOKUP_PARTIAL << 1) - 1)
+		return (unsigned long)NULL;
+
+	/* Permission check is deferred until actual helper using the key. */
+	key_ref = lookup_user_key(serial, flags, KEY_DEFER_PERM_CHECK);
+	if (IS_ERR(key_ref))
+		return (unsigned long)NULL;
+
+	return (unsigned long)key_ref_to_ptr(key_ref);
+}
+
+static const struct bpf_func_proto bpf_lookup_user_key_proto = {
+	.func		= bpf_lookup_user_key,
+	.gpl_only	= false,
+	.ret_type	= RET_PTR_TO_BTF_ID_OR_NULL,
+	.ret_btf_id	= &btf_key_ids[0],
+	.arg1_type	= ARG_ANYTHING,
+	.arg2_type	= ARG_ANYTHING,
+	.allowed	= bpf_ima_inode_hash_allowed,
+};
+
+BPF_CALL_1(bpf_key_put, struct key *, key)
+{
+	key_put(key);
+	return 0;
+}
+
+static const struct bpf_func_proto bpf_key_put_proto = {
+	.func		= bpf_key_put,
+	.gpl_only	= false,
+	.ret_type	= RET_VOID,
+	.arg1_type	= ARG_PTR_TO_BTF_ID | OBJ_RELEASE,
+	.arg1_btf_id	= &btf_key_ids[0],
+	.allowed	= bpf_ima_inode_hash_allowed,
+};
+#endif /* CONFIG_KEYS */
+
 static const struct bpf_func_proto *
 bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 {
@@ -239,6 +284,14 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 					prog->aux->attach_btf_id))
 			return &bpf_unlocked_sk_getsockopt_proto;
 		return NULL;
+#ifdef CONFIG_KEYS
+	case BPF_FUNC_lookup_user_key:
+		return prog->aux->sleepable ?
+		       &bpf_lookup_user_key_proto : NULL;
+	case BPF_FUNC_key_put:
+		return prog->aux->sleepable ?
+		       &bpf_key_put_proto : NULL;
+#endif /* CONFIG_KEYS */
 	default:
 		return tracing_prog_func_proto(func_id, prog);
 	}
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 328cfab3af60..d9f52fbfb660 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -483,7 +483,8 @@ static bool may_be_acquire_function(enum bpf_func_id func_id)
 		func_id == BPF_FUNC_sk_lookup_udp ||
 		func_id == BPF_FUNC_skc_lookup_tcp ||
 		func_id == BPF_FUNC_map_lookup_elem ||
-	        func_id == BPF_FUNC_ringbuf_reserve;
+		func_id == BPF_FUNC_ringbuf_reserve ||
+		func_id == BPF_FUNC_lookup_user_key;
 }
 
 static bool is_acquire_function(enum bpf_func_id func_id,
@@ -495,7 +496,8 @@ static bool is_acquire_function(enum bpf_func_id func_id,
 	    func_id == BPF_FUNC_sk_lookup_udp ||
 	    func_id == BPF_FUNC_skc_lookup_tcp ||
 	    func_id == BPF_FUNC_ringbuf_reserve ||
-	    func_id == BPF_FUNC_kptr_xchg)
+	    func_id == BPF_FUNC_kptr_xchg ||
+	    func_id == BPF_FUNC_lookup_user_key)
 		return true;
 
 	if (func_id == BPF_FUNC_map_lookup_elem &&
diff --git a/scripts/bpf_doc.py b/scripts/bpf_doc.py
index a0ec321469bd..3d5a7ad6f493 100755
--- a/scripts/bpf_doc.py
+++ b/scripts/bpf_doc.py
@@ -637,6 +637,7 @@ class PrinterHelpers(Printer):
             'struct bpf_dynptr',
             'struct iphdr',
             'struct ipv6hdr',
+            'struct key',
     ]
     known_types = {
             '...',
@@ -690,6 +691,7 @@ class PrinterHelpers(Printer):
             'struct bpf_dynptr',
             'struct iphdr',
             'struct ipv6hdr',
+            'struct key',
     }
     mapped_types = {
             'u8': '__u8',
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 379e68fb866f..a3f6506ddeba 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -5327,6 +5327,29 @@ union bpf_attr {
  *		**-EACCES** if the SYN cookie is not valid.
  *
  *		**-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin.
+ *
+ * struct key *bpf_lookup_user_key(u32 serial, u64 flags)
+ *	Description
+ *		Search a key with a given *serial* and the provided *flags*. The
+ *		returned key, if found, has the reference count incremented by
+ *		one, and must be passed to bpf_key_put() when done with it.
+ *		Permission checks are deferred to the time the key is used by
+ *		one of the available key-specific helpers.
+ *
+ *		Set *flags* with 1 to attempt creating a requested special
+ *		keyring (e.g. session keyring), if it doesn't yet exist. Set
+ *		*flags* to 2 to lookup a key without waiting for the key
+ *		construction, and to retrieve uninstantiated keys (keys without
+ *		data attached to them).
+ *	Return
+ *		A key pointer if the key is found, a NULL pointer otherwise.
+ *
+ * void bpf_key_put(struct key *key)
+ *	Description
+ *		Decrement the reference count of *key* obtained with the
+ *		bpf_lookup_user_key() helper.
+ *	Return
+ *		0
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -5537,6 +5560,8 @@ union bpf_attr {
 	FN(tcp_raw_gen_syncookie_ipv6),	\
 	FN(tcp_raw_check_syncookie_ipv4),	\
 	FN(tcp_raw_check_syncookie_ipv6),	\
+	FN(lookup_user_key),		\
+	FN(key_put),			\
 	/* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v7 4/7] bpf: Add bpf_verify_pkcs7_signature() helper
  2022-07-12 18:41 [PATCH v7 0/7] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
                   ` (2 preceding siblings ...)
  2022-07-12 18:41 ` [PATCH v7 3/7] bpf: Add bpf_lookup_user_key() and bpf_key_put() helpers Roberto Sassu
@ 2022-07-12 18:41 ` Roberto Sassu
  2022-07-12 18:41 ` [PATCH v7 5/7] selftests: Add verifier tests for bpf_lookup_user_key() and bpf_key_put() Roberto Sassu
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Roberto Sassu @ 2022-07-12 18:41 UTC (permalink / raw)
  To: ast, daniel, john.fastabend, andrii, martin.lau, song, yhs,
	kpsingh, dhowells, jarkko, shuah
  Cc: bpf, keyrings, linux-security-module, linux-kselftest, netdev,
	linux-kernel, Roberto Sassu

Add the bpf_verify_pkcs7_signature() helper, to give eBPF security modules
the ability to check the validity of a signature against supplied data, by
using user-provided or system-provided keys as trust anchor.

The new helper makes it possible to enforce mandatory policies, as eBPF
programs might be allowed to make security decisions only based on data
sources the system administrator approves.

The caller should provide both the data to be verified and the signature as
eBPF dynamic pointers (to minimize the number of parameters) and,
alternatively, a keyring obtained from bpf_lookup_user_key(), or a
pre-determined keyring ID with values defined in
include/linux/verification.h.

The two keyring parameters have to be provided separately: the
pre-determined IDs exist only in the context of verify_pkcs7_signature().
They should not be passed to the bpf_lookup_user_key() helper, or to a new
helper doing type casting to a struct key (like: ((struct key *)2UL) in
include/linux/verification.h), as otherwise, each helper accepting a struct
key would have to check if it is a valid pointer or not.

Finally, bpf_verify_pkcs7_signature() completes the permission check
deferred by bpf_lookup_user_key(), by calling key_validate().
key_task_permission() is already called by the PKCS#7 code.

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 include/uapi/linux/bpf.h       | 22 ++++++++++++
 kernel/bpf/bpf_lsm.c           | 63 ++++++++++++++++++++++++++++++++++
 tools/include/uapi/linux/bpf.h | 22 ++++++++++++
 3 files changed, 107 insertions(+)

diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index a3f6506ddeba..9037ff1414c2 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -5350,6 +5350,27 @@ union bpf_attr {
  *		bpf_lookup_user_key() helper.
  *	Return
  *		0
+ *
+ * long bpf_verify_pkcs7_signature(struct bpf_dynptr *data_ptr, struct bpf_dynptr *sig_ptr, struct key *user_keyring, u64 system_keyring)
+ *	Description
+ *		Verify the PKCS#7 signature *sig_ptr* against the supplied
+ *		*data_ptr* alternatively with keys in *user_keyring* or
+ *		*system_keyring*. Either one of the two must be provided.
+ *		Respectively, NULL or UINT64_MAX must be passed to signal to the
+ *		helper that the parameter is not used.
+ *
+ *		*user_keyring* is a key pointer obtained from
+ *		bpf_lookup_user_key(), while *system_keyring* is a
+ *		pre-determined ID with values defined in
+ *		include/linux/verification.h: 0 for the primary keyring
+ *		(immutable keyring of system keys); 1 for both the primary and
+ *		secondary keyring (where keys can be added only if they are
+ *		vouched for by existing keys in those keyrings); 2 for the
+ *		platform keyring (primarily used by the integrity subsystem to
+ *		verify a kexec'ed kerned image and, possibly, the initramfs
+ *		signature).
+ *	Return
+ *		0 on success, a negative value on error.
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -5562,6 +5583,7 @@ union bpf_attr {
 	FN(tcp_raw_check_syncookie_ipv6),	\
 	FN(lookup_user_key),		\
 	FN(key_put),			\
+	FN(verify_pkcs7_signature),	\
 	/* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 0a95e2137d65..6d8a40b8b1e0 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -17,6 +17,7 @@
 #include <linux/btf_ids.h>
 #include <linux/ima.h>
 #include <linux/bpf-cgroup.h>
+#include <linux/verification.h>
 
 /* For every LSM hook that allows attachment of BPF programs, declare a nop
  * function where a BPF program can be attached.
@@ -227,6 +228,63 @@ static const struct bpf_func_proto bpf_key_put_proto = {
 	.arg1_btf_id	= &btf_key_ids[0],
 	.allowed	= bpf_ima_inode_hash_allowed,
 };
+
+#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
+BPF_CALL_4(bpf_verify_pkcs7_signature, struct bpf_dynptr_kern *, data_ptr,
+	   struct bpf_dynptr_kern *, sig_ptr, struct key *, user_keyring,
+	   u64, system_keyring)
+{
+	struct key *trusted_keyring;
+	int ret;
+
+	/* Either user_keyring or system_keyring must be specified. */
+	if ((user_keyring && system_keyring != U64_MAX) ||
+	    (!user_keyring && system_keyring == U64_MAX))
+		return -EINVAL;
+
+	if (user_keyring) {
+		/*
+		 * Do the permission check deferred in bpf_lookup_user_key().
+		 *
+		 * A call to key_task_permission() here would be redundant, as
+		 * it is already done by keyring_search() called by
+		 * find_asymmetric_key().
+		 */
+		ret = key_validate(user_keyring);
+		if (ret < 0)
+			return ret;
+
+		trusted_keyring = user_keyring;
+		goto verify;
+	}
+
+	/* Keep in sync with defs in include/linux/verification.h. */
+	if (system_keyring > (unsigned long)VERIFY_USE_PLATFORM_KEYRING)
+		return -EINVAL;
+
+	trusted_keyring = (struct key *)(unsigned long)system_keyring;
+verify:
+	return verify_pkcs7_signature(data_ptr->data,
+				      bpf_dynptr_get_size(data_ptr),
+				      sig_ptr->data,
+				      bpf_dynptr_get_size(sig_ptr),
+				      trusted_keyring,
+				      VERIFYING_UNSPECIFIED_SIGNATURE, NULL,
+				      NULL);
+}
+
+static const struct bpf_func_proto bpf_verify_pkcs7_signature_proto = {
+	.func		= bpf_verify_pkcs7_signature,
+	.gpl_only	= false,
+	.ret_type	= RET_INTEGER,
+	.arg1_type	= ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_LOCAL,
+	.arg2_type	= ARG_PTR_TO_DYNPTR | DYNPTR_TYPE_LOCAL,
+	.arg3_type	= ARG_PTR_TO_BTF_ID_OR_NULL,
+	.arg3_btf_id	= &btf_key_ids[0],
+	.arg4_type	= ARG_ANYTHING,
+	.allowed	= bpf_ima_inode_hash_allowed,
+};
+#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
 #endif /* CONFIG_KEYS */
 
 static const struct bpf_func_proto *
@@ -291,6 +349,11 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 	case BPF_FUNC_key_put:
 		return prog->aux->sleepable ?
 		       &bpf_key_put_proto : NULL;
+#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
+	case BPF_FUNC_verify_pkcs7_signature:
+		return prog->aux->sleepable ?
+		       &bpf_verify_pkcs7_signature_proto : NULL;
+#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
 #endif /* CONFIG_KEYS */
 	default:
 		return tracing_prog_func_proto(func_id, prog);
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index a3f6506ddeba..9037ff1414c2 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -5350,6 +5350,27 @@ union bpf_attr {
  *		bpf_lookup_user_key() helper.
  *	Return
  *		0
+ *
+ * long bpf_verify_pkcs7_signature(struct bpf_dynptr *data_ptr, struct bpf_dynptr *sig_ptr, struct key *user_keyring, u64 system_keyring)
+ *	Description
+ *		Verify the PKCS#7 signature *sig_ptr* against the supplied
+ *		*data_ptr* alternatively with keys in *user_keyring* or
+ *		*system_keyring*. Either one of the two must be provided.
+ *		Respectively, NULL or UINT64_MAX must be passed to signal to the
+ *		helper that the parameter is not used.
+ *
+ *		*user_keyring* is a key pointer obtained from
+ *		bpf_lookup_user_key(), while *system_keyring* is a
+ *		pre-determined ID with values defined in
+ *		include/linux/verification.h: 0 for the primary keyring
+ *		(immutable keyring of system keys); 1 for both the primary and
+ *		secondary keyring (where keys can be added only if they are
+ *		vouched for by existing keys in those keyrings); 2 for the
+ *		platform keyring (primarily used by the integrity subsystem to
+ *		verify a kexec'ed kerned image and, possibly, the initramfs
+ *		signature).
+ *	Return
+ *		0 on success, a negative value on error.
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -5562,6 +5583,7 @@ union bpf_attr {
 	FN(tcp_raw_check_syncookie_ipv6),	\
 	FN(lookup_user_key),		\
 	FN(key_put),			\
+	FN(verify_pkcs7_signature),	\
 	/* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v7 5/7] selftests: Add verifier tests for bpf_lookup_user_key() and bpf_key_put()
  2022-07-12 18:41 [PATCH v7 0/7] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
                   ` (3 preceding siblings ...)
  2022-07-12 18:41 ` [PATCH v7 4/7] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
@ 2022-07-12 18:41 ` Roberto Sassu
  2022-07-12 18:41 ` [PATCH v7 6/7] selftests/bpf: Add additional test for bpf_lookup_user_key() Roberto Sassu
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Roberto Sassu @ 2022-07-12 18:41 UTC (permalink / raw)
  To: ast, daniel, john.fastabend, andrii, martin.lau, song, yhs,
	kpsingh, dhowells, jarkko, shuah
  Cc: bpf, keyrings, linux-security-module, linux-kselftest, netdev,
	linux-kernel, Roberto Sassu

Add verifier tests for bpf_lookup_user_key() and bpf_key_put(), to ensure
that acquired key references can be released, that a non-NULL pointer is
passed to bpf_key_put(), and that key references are not leaked.

Also, slightly modify test_verifier.c, to find the BTF ID of the attach
point for the LSM program type (currently, it is done only for TRACING).

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 tools/testing/selftests/bpf/test_verifier.c   |  3 +-
 .../selftests/bpf/verifier/ref_tracking.c     | 66 +++++++++++++++++++
 2 files changed, 68 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c
index f9d553fbf68a..2dbcbf363c18 100644
--- a/tools/testing/selftests/bpf/test_verifier.c
+++ b/tools/testing/selftests/bpf/test_verifier.c
@@ -1498,7 +1498,8 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
 		opts.log_level = DEFAULT_LIBBPF_LOG_LEVEL;
 	opts.prog_flags = pflags;
 
-	if (prog_type == BPF_PROG_TYPE_TRACING && test->kfunc) {
+	if ((prog_type == BPF_PROG_TYPE_TRACING ||
+	     prog_type == BPF_PROG_TYPE_LSM) && test->kfunc) {
 		int attach_btf_id;
 
 		attach_btf_id = libbpf_find_vmlinux_btf_id(test->kfunc,
diff --git a/tools/testing/selftests/bpf/verifier/ref_tracking.c b/tools/testing/selftests/bpf/verifier/ref_tracking.c
index 57a83d763ec1..36e0b9b342de 100644
--- a/tools/testing/selftests/bpf/verifier/ref_tracking.c
+++ b/tools/testing/selftests/bpf/verifier/ref_tracking.c
@@ -84,6 +84,72 @@
 	.errstr = "Unreleased reference",
 	.result = REJECT,
 },
+{
+	"reference tracking: release key reference",
+	.insns = {
+	BPF_MOV64_IMM(BPF_REG_1, -3),
+	BPF_MOV64_IMM(BPF_REG_2, 0),
+	BPF_EMIT_CALL(BPF_FUNC_lookup_user_key),
+	BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
+	BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+	BPF_EMIT_CALL(BPF_FUNC_key_put),
+	BPF_MOV64_IMM(BPF_REG_0, 0),
+	BPF_EXIT_INSN(),
+	},
+	.prog_type = BPF_PROG_TYPE_LSM,
+	.kfunc = "bpf",
+	.expected_attach_type = BPF_LSM_MAC,
+	.flags = BPF_F_SLEEPABLE,
+	.result = ACCEPT,
+},
+{
+	"reference tracking: release key reference without check",
+	.insns = {
+	BPF_MOV64_IMM(BPF_REG_1, -3),
+	BPF_MOV64_IMM(BPF_REG_2, 0),
+	BPF_EMIT_CALL(BPF_FUNC_lookup_user_key),
+	BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+	BPF_EMIT_CALL(BPF_FUNC_key_put),
+	BPF_MOV64_IMM(BPF_REG_0, 0),
+	BPF_EXIT_INSN(),
+	},
+	.prog_type = BPF_PROG_TYPE_LSM,
+	.kfunc = "bpf",
+	.expected_attach_type = BPF_LSM_MAC,
+	.flags = BPF_F_SLEEPABLE,
+	.errstr = "type=ptr_or_null_ expected=ptr_",
+	.result = REJECT,
+},
+{
+	"reference tracking: release reference with NULL key pointer",
+	.insns = {
+	BPF_MOV64_IMM(BPF_REG_1, 0),
+	BPF_EMIT_CALL(BPF_FUNC_key_put),
+	BPF_MOV64_IMM(BPF_REG_0, 0),
+	BPF_EXIT_INSN(),
+	},
+	.prog_type = BPF_PROG_TYPE_LSM,
+	.kfunc = "bpf",
+	.expected_attach_type = BPF_LSM_MAC,
+	.flags = BPF_F_SLEEPABLE,
+	.errstr = "type=scalar expected=ptr_",
+	.result = REJECT,
+},
+{
+	"reference tracking: leak potential reference to key",
+	.insns = {
+	BPF_MOV64_IMM(BPF_REG_1, -3),
+	BPF_MOV64_IMM(BPF_REG_2, 0),
+	BPF_EMIT_CALL(BPF_FUNC_lookup_user_key),
+	BPF_EXIT_INSN(),
+	},
+	.prog_type = BPF_PROG_TYPE_LSM,
+	.kfunc = "bpf",
+	.expected_attach_type = BPF_LSM_MAC,
+	.flags = BPF_F_SLEEPABLE,
+	.errstr = "Unreleased reference",
+	.result = REJECT,
+},
 {
 	"reference tracking: release reference without check",
 	.insns = {
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v7 6/7] selftests/bpf: Add additional test for bpf_lookup_user_key()
  2022-07-12 18:41 [PATCH v7 0/7] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
                   ` (4 preceding siblings ...)
  2022-07-12 18:41 ` [PATCH v7 5/7] selftests: Add verifier tests for bpf_lookup_user_key() and bpf_key_put() Roberto Sassu
@ 2022-07-12 18:41 ` Roberto Sassu
  2022-07-12 18:41 ` [PATCH v7 7/7] selftests/bpf: Add test for bpf_verify_pkcs7_signature() helper Roberto Sassu
  2022-07-13  0:53 ` [PATCH v7 0/7] bpf: Add " Alexei Starovoitov
  7 siblings, 0 replies; 9+ messages in thread
From: Roberto Sassu @ 2022-07-12 18:41 UTC (permalink / raw)
  To: ast, daniel, john.fastabend, andrii, martin.lau, song, yhs,
	kpsingh, dhowells, jarkko, shuah
  Cc: bpf, keyrings, linux-security-module, linux-kselftest, netdev,
	linux-kernel, Roberto Sassu

Add an additional test to ensure that bpf_lookup_user_key() creates a
referenced special keyring when the KEY_LOOKUP_CREATE flag is passed to
this function.

Also ensure that the helper rejects invalid flags.

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 .../bpf/prog_tests/lookup_user_key.c          | 94 +++++++++++++++++++
 .../bpf/progs/test_lookup_user_key.c          | 35 +++++++
 2 files changed, 129 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/lookup_user_key.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_lookup_user_key.c

diff --git a/tools/testing/selftests/bpf/prog_tests/lookup_user_key.c b/tools/testing/selftests/bpf/prog_tests/lookup_user_key.c
new file mode 100644
index 000000000000..6487699d30f6
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/lookup_user_key.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ */
+
+#include <linux/keyctl.h>
+#include <test_progs.h>
+
+#include "test_lookup_user_key.skel.h"
+
+#define LOG_BUF_SIZE 16384
+
+#define KEY_LOOKUP_CREATE	0x01
+#define KEY_LOOKUP_PARTIAL	0x02
+
+void test_lookup_user_key(void)
+{
+	char *buf = NULL;
+	struct test_lookup_user_key *skel = NULL;
+	u32 next_id;
+	int ret;
+
+	LIBBPF_OPTS(bpf_object_open_opts, opts);
+
+	buf = malloc(LOG_BUF_SIZE);
+	if (!ASSERT_OK_PTR(buf, "malloc"))
+		goto close_prog;
+
+	opts.kernel_log_buf = buf;
+	opts.kernel_log_size = LOG_BUF_SIZE;
+	opts.kernel_log_level = 1;
+
+	skel = test_lookup_user_key__open_opts(&opts);
+	if (!ASSERT_OK_PTR(skel, "test_lookup_user_key__open_opts"))
+		goto close_prog;
+
+	ret = test_lookup_user_key__load(skel);
+
+	if (ret < 0 && strstr(buf, "unknown func bpf_lookup_user_key")) {
+		printf("%s:SKIP:bpf_lookup_user_key() helper not supported\n",
+		       __func__);
+		test__skip();
+		goto close_prog;
+	}
+
+	if (!ASSERT_OK(ret, "test_lookup_user_key__load"))
+		goto close_prog;
+
+	ret = test_lookup_user_key__attach(skel);
+	if (!ASSERT_OK(ret, "test_lookup_user_key__attach"))
+		goto close_prog;
+
+	skel->bss->monitored_pid = getpid();
+	skel->bss->key_serial = KEY_SPEC_THREAD_KEYRING;
+
+	/* The thread-specific keyring does not exist, this test fails. */
+	skel->bss->flags = 0;
+
+	ret = bpf_prog_get_next_id(0, &next_id);
+	if (!ASSERT_LT(ret, 0, "bpf_prog_get_next_id"))
+		goto close_prog;
+
+	/* Force creation of the thread-specific keyring, this test succeeds. */
+	skel->bss->flags = KEY_LOOKUP_CREATE;
+
+	ret = bpf_prog_get_next_id(0, &next_id);
+	if (!ASSERT_OK(ret, "bpf_prog_get_next_id"))
+		goto close_prog;
+
+	/* Pass both lookup flags for parameter validation. */
+	skel->bss->flags = KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL;
+
+	ret = bpf_prog_get_next_id(0, &next_id);
+	if (!ASSERT_OK(ret, "bpf_prog_get_next_id"))
+		goto close_prog;
+
+	/* Pass invalid flags. */
+	skel->bss->flags = UINT64_MAX;
+
+	ret = bpf_prog_get_next_id(0, &next_id);
+	ASSERT_LT(ret, 0, "bpf_prog_get_next_id");
+
+close_prog:
+	free(buf);
+
+	if (!skel)
+		return;
+
+	skel->bss->monitored_pid = 0;
+	test_lookup_user_key__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_lookup_user_key.c b/tools/testing/selftests/bpf/progs/test_lookup_user_key.c
new file mode 100644
index 000000000000..5b63753ba163
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_lookup_user_key.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ */
+
+#include <errno.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+char _license[] SEC("license") = "GPL";
+
+__u32 monitored_pid;
+__u32 key_serial;
+__u64 flags;
+
+SEC("lsm.s/bpf")
+int BPF_PROG(bpf, int cmd, union bpf_attr *attr, unsigned int size)
+{
+	struct key *key;
+	__u32 pid;
+
+	pid = bpf_get_current_pid_tgid() >> 32;
+	if (pid != monitored_pid)
+		return 0;
+
+	key = bpf_lookup_user_key(key_serial, flags);
+	if (key)
+		bpf_key_put(key);
+
+	return (key) ? 0 : -ENOENT;
+}
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v7 7/7] selftests/bpf: Add test for bpf_verify_pkcs7_signature() helper
  2022-07-12 18:41 [PATCH v7 0/7] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
                   ` (5 preceding siblings ...)
  2022-07-12 18:41 ` [PATCH v7 6/7] selftests/bpf: Add additional test for bpf_lookup_user_key() Roberto Sassu
@ 2022-07-12 18:41 ` Roberto Sassu
  2022-07-13  0:53 ` [PATCH v7 0/7] bpf: Add " Alexei Starovoitov
  7 siblings, 0 replies; 9+ messages in thread
From: Roberto Sassu @ 2022-07-12 18:41 UTC (permalink / raw)
  To: ast, daniel, john.fastabend, andrii, martin.lau, song, yhs,
	kpsingh, dhowells, jarkko, shuah
  Cc: bpf, keyrings, linux-security-module, linux-kselftest, netdev,
	linux-kernel, Roberto Sassu

Perform several tests to ensure the correct implementation of the
bpf_verify_pkcs7_signature() helper.

Do the tests with data signed with a generated testing key (by using
sign-file from scripts/) and with the tcp_bic.ko kernel module if it is
found in the system. The test does not fail if tcp_bic.ko is not found.

First, ensure that bpf_verify_pkcs7_signature() rejects invalid parameters.

Then, perform a successful signature verification with the session keyring
and a new one created for testing.

Then, ensure that permission and validation checks are done properly on the
keyring provided to bpf_verify_pkcs7_signature(), despite those checks were
deferred at the time the keyring was retrieved with bpf_lookup_user_key().
The tests expect to encounter an error if the Search permission is removed
from the keyring, or the keyring is expired.

Finally, perform a successful and unsuccessful signature verification with
the keyrings with pre-determined IDs (the last test fails because the key
is not in the platform keyring).

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 tools/testing/selftests/bpf/Makefile          |  14 +-
 tools/testing/selftests/bpf/config            |   2 +
 .../bpf/prog_tests/verify_pkcs7_sig.c         | 410 ++++++++++++++++++
 .../bpf/progs/test_verify_pkcs7_sig.c         |  90 ++++
 .../testing/selftests/bpf/verify_sig_setup.sh | 104 +++++
 5 files changed, 617 insertions(+), 3 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c
 create mode 100755 tools/testing/selftests/bpf/verify_sig_setup.sh

diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 8d59ec7f4c2d..5ae079e276b3 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -14,6 +14,7 @@ BPFTOOLDIR := $(TOOLSDIR)/bpf/bpftool
 APIDIR := $(TOOLSINCDIR)/uapi
 GENDIR := $(abspath ../../../../include/generated)
 GENHDR := $(GENDIR)/autoconf.h
+HOSTPKG_CONFIG := pkg-config
 
 ifneq ($(wildcard $(GENHDR)),)
   GENFLAGS := -DHAVE_GENHDR
@@ -75,7 +76,7 @@ TEST_PROGS := test_kmod.sh \
 	test_xsk.sh
 
 TEST_PROGS_EXTENDED := with_addr.sh \
-	with_tunnels.sh ima_setup.sh \
+	with_tunnels.sh ima_setup.sh verify_sig_setup.sh \
 	test_xdp_vlan.sh test_bpftool.py
 
 # Compile but not part of 'make run_tests'
@@ -84,7 +85,7 @@ TEST_GEN_PROGS_EXTENDED = test_sock_addr test_skb_cgroup_id_user \
 	test_lirc_mode2_user xdping test_cpp runqslower bench bpf_testmod.ko \
 	xskxceiver xdp_redirect_multi xdp_synproxy
 
-TEST_CUSTOM_PROGS = $(OUTPUT)/urandom_read
+TEST_CUSTOM_PROGS = $(OUTPUT)/urandom_read $(OUTPUT)/sign-file
 
 # Emit succinct information message describing current building step
 # $1 - generic step name (e.g., CC, LINK, etc);
@@ -189,6 +190,12 @@ $(OUTPUT)/urandom_read: urandom_read.c urandom_read_aux.c $(OUTPUT)/liburandom_r
 		     -fuse-ld=$(LLD) -Wl,-znoseparate-code		       \
 		     -Wl,-rpath=. -Wl,--build-id=sha1 -o $@
 
+$(OUTPUT)/sign-file: ../../../../scripts/sign-file.c
+	$(call msg,SIGN-FILE,,$@)
+	$(Q)$(CC) $(shell $(HOSTPKG_CONFIG)--cflags libcrypto 2> /dev/null) \
+		  $< -o $@ \
+		  $(shell $(HOSTPKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
+
 $(OUTPUT)/bpf_testmod.ko: $(VMLINUX_BTF) $(wildcard bpf_testmod/Makefile bpf_testmod/*.[ch])
 	$(call msg,MOD,,$@)
 	$(Q)$(RM) bpf_testmod/bpf_testmod.ko # force re-compilation
@@ -514,7 +521,8 @@ TRUNNER_EXTRA_SOURCES := test_progs.c cgroup_helpers.c trace_helpers.c	\
 TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read $(OUTPUT)/bpf_testmod.ko	\
 		       $(OUTPUT)/liburandom_read.so			\
 		       $(OUTPUT)/xdp_synproxy				\
-		       ima_setup.sh					\
+		       $(OUTPUT)/sign-file				\
+		       ima_setup.sh verify_sig_setup.sh			\
 		       $(wildcard progs/btf_dump_test_case_*.c)
 TRUNNER_BPF_BUILD_RULE := CLANG_BPF_BUILD_RULE
 TRUNNER_BPF_CFLAGS := $(BPF_CFLAGS) $(CLANG_CFLAGS) -DENABLE_ATOMICS_TESTS
diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config
index c05904d631ec..76b65acd897e 100644
--- a/tools/testing/selftests/bpf/config
+++ b/tools/testing/selftests/bpf/config
@@ -63,3 +63,5 @@ CONFIG_NETFILTER_XT_MATCH_STATE=y
 CONFIG_IP_NF_FILTER=y
 CONFIG_IP_NF_TARGET_SYNPROXY=y
 CONFIG_IP_NF_RAW=y
+CONFIG_MODULE_SIG=y
+CONFIG_KEYS=y
diff --git a/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c b/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c
new file mode 100644
index 000000000000..05f7580e34ec
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c
@@ -0,0 +1,410 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <endian.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <linux/keyctl.h>
+#include <test_progs.h>
+
+#include "test_verify_pkcs7_sig.skel.h"
+
+#define MAX_DATA_SIZE (1024 * 1024)
+#define MAX_SIG_SIZE 1024
+#define LOG_BUF_SIZE 16384
+
+#define VERIFY_USE_SECONDARY_KEYRING (1UL)
+#define VERIFY_USE_PLATFORM_KEYRING  (2UL)
+
+/* In stripped ARM and x86-64 modules, ~ is surprisingly rare. */
+#define MODULE_SIG_STRING "~Module signature appended~\n"
+
+/*
+ * Module signature information block.
+ *
+ * The constituents of the signature section are, in order:
+ *
+ *	- Signer's name
+ *	- Key identifier
+ *	- Signature data
+ *	- Information block
+ */
+struct module_signature {
+	u8	algo;		/* Public-key crypto algorithm [0] */
+	u8	hash;		/* Digest algorithm [0] */
+	u8	id_type;	/* Key identifier type [PKEY_ID_PKCS7] */
+	u8	signer_len;	/* Length of signer's name [0] */
+	u8	key_id_len;	/* Length of key identifier [0] */
+	u8	__pad[3];
+	__be32	sig_len;	/* Length of signature data */
+};
+
+struct data {
+	u8 data[MAX_DATA_SIZE];
+	u32 data_len;
+	u8 sig[MAX_SIG_SIZE];
+	u32 sig_len;
+};
+
+static int _run_setup_process(const char *setup_dir, const char *cmd)
+{
+	int child_pid, child_status;
+
+	child_pid = fork();
+	if (child_pid == 0) {
+		execlp("./verify_sig_setup.sh", "./verify_sig_setup.sh", cmd,
+		       setup_dir, NULL);
+		exit(errno);
+
+	} else if (child_pid > 0) {
+		waitpid(child_pid, &child_status, 0);
+		return WEXITSTATUS(child_status);
+	}
+
+	return -EINVAL;
+}
+
+static int populate_data_item_str(const char *tmp_dir, struct data *data_item)
+{
+	struct stat st;
+	char data_template[] = "/tmp/dataXXXXXX";
+	char path[PATH_MAX];
+	int ret, fd, child_status, child_pid;
+
+	data_item->data_len = 4;
+	memcpy(data_item->data, "test", data_item->data_len);
+
+	fd = mkstemp(data_template);
+	if (fd == -1)
+		return -errno;
+
+	ret = write(fd, data_item->data, data_item->data_len);
+
+	close(fd);
+
+	if (ret != data_item->data_len) {
+		ret = -EIO;
+		goto out;
+	}
+
+	child_pid = fork();
+
+	if (child_pid == -1) {
+		ret = -errno;
+		goto out;
+	}
+
+	if (child_pid == 0) {
+		snprintf(path, sizeof(path), "%s/signing_key.pem", tmp_dir);
+
+		return execlp("./sign-file", "./sign-file", "-d", "sha256",
+			      path, path, data_template, NULL);
+	}
+
+	waitpid(child_pid, &child_status, 0);
+
+	ret = WEXITSTATUS(child_status);
+	if (ret)
+		goto out;
+
+	snprintf(path, sizeof(path), "%s.p7s", data_template);
+
+	ret = stat(path, &st);
+	if (ret == -1) {
+		ret = -errno;
+		goto out;
+	}
+
+	if (st.st_size > sizeof(data_item->sig)) {
+		ret = -EINVAL;
+		goto out_sig;
+	}
+
+	data_item->sig_len = st.st_size;
+
+	fd = open(path, O_RDONLY);
+	if (fd == -1) {
+		ret = -errno;
+		goto out_sig;
+	}
+
+	ret = read(fd, data_item->sig, data_item->sig_len);
+
+	close(fd);
+
+	if (ret != data_item->sig_len) {
+		ret = -EIO;
+		goto out_sig;
+	}
+
+	ret = 0;
+out_sig:
+	unlink(path);
+out:
+	unlink(data_template);
+	return ret;
+}
+
+static int populate_data_item_mod(struct data *data_item)
+{
+	char mod_path[PATH_MAX], *mod_path_ptr;
+	struct stat st;
+	void *mod;
+	FILE *fp;
+	struct module_signature ms;
+	int ret, fd, modlen, marker_len, sig_len;
+
+	data_item->data_len = 0;
+
+	if (stat("/lib/modules", &st) == -1)
+		return 0;
+
+	/* Requires CONFIG_TCP_CONG_BIC=m. */
+	fp = popen("find /lib/modules/$(uname -r) -name tcp_bic.ko", "r");
+	if (!fp)
+		return 0;
+
+	mod_path_ptr = fgets(mod_path, sizeof(mod_path), fp);
+	pclose(fp);
+
+	if (!mod_path_ptr)
+		return 0;
+
+	mod_path_ptr = strchr(mod_path, '\n');
+	if (!mod_path_ptr)
+		return 0;
+
+	*mod_path_ptr = '\0';
+
+	if (stat(mod_path, &st) == -1)
+		return 0;
+
+	modlen = st.st_size;
+	marker_len = sizeof(MODULE_SIG_STRING) - 1;
+
+	fd = open(mod_path, O_RDONLY);
+	if (fd == -1)
+		return -errno;
+
+	mod = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+
+	close(fd);
+
+	if (mod == MAP_FAILED)
+		return -errno;
+
+	if (strncmp(mod + modlen - marker_len, MODULE_SIG_STRING, marker_len)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	modlen -= marker_len;
+
+	memcpy(&ms, mod + (modlen - sizeof(ms)), sizeof(ms));
+
+	sig_len = __be32_to_cpu(ms.sig_len);
+	modlen -= sig_len + sizeof(ms);
+
+	if (modlen > sizeof(data_item->data)) {
+		ret = -E2BIG;
+		goto out;
+	}
+
+	memcpy(data_item->data, mod, modlen);
+	data_item->data_len = modlen;
+
+	if (sig_len > sizeof(data_item->sig)) {
+		ret = -E2BIG;
+		goto out;
+	}
+
+	memcpy(data_item->sig, mod + modlen, sig_len);
+	data_item->sig_len = sig_len;
+	ret = 0;
+out:
+	munmap(mod, st.st_size);
+	return ret;
+}
+
+void test_verify_pkcs7_sig(void)
+{
+	char tmp_dir_template[] = "/tmp/verify_sigXXXXXX";
+	char *tmp_dir;
+	char *buf = NULL;
+	struct test_verify_pkcs7_sig *skel = NULL;
+	struct bpf_map *map;
+	struct data data;
+	int ret, zero = 0;
+
+	LIBBPF_OPTS(bpf_object_open_opts, opts);
+
+	/* Trigger creation of session keyring. */
+	syscall(__NR_request_key, "keyring", "_uid.0", NULL,
+		KEY_SPEC_SESSION_KEYRING);
+
+	tmp_dir = mkdtemp(tmp_dir_template);
+	if (!ASSERT_OK_PTR(tmp_dir, "mkdtemp"))
+		return;
+
+	ret = _run_setup_process(tmp_dir, "setup");
+	if (!ASSERT_OK(ret, "_run_setup_process"))
+		goto close_prog;
+
+	buf = malloc(LOG_BUF_SIZE);
+	if (!ASSERT_OK_PTR(buf, "malloc"))
+		goto close_prog;
+
+	opts.kernel_log_buf = buf;
+	opts.kernel_log_size = LOG_BUF_SIZE;
+	opts.kernel_log_level = 1;
+
+	skel = test_verify_pkcs7_sig__open_opts(&opts);
+	if (!ASSERT_OK_PTR(skel, "test_verify_pkcs7_sig__open_opts"))
+		goto close_prog;
+
+	ret = test_verify_pkcs7_sig__load(skel);
+
+	if (ret < 0 && strstr(buf, "unknown func bpf_verify_pkcs7_signature")) {
+		printf(
+		  "%s:SKIP:bpf_verify_pkcs7_signature() helper not supported\n",
+		  __func__);
+		test__skip();
+		goto close_prog;
+	}
+
+	if (!ASSERT_OK(ret, "test_verify_pkcs7_sig__load"))
+		goto close_prog;
+
+	ret = test_verify_pkcs7_sig__attach(skel);
+	if (!ASSERT_OK(ret, "test_verify_pkcs7_sig__attach"))
+		goto close_prog;
+
+	map = bpf_object__find_map_by_name(skel->obj, "data_input");
+	if (!ASSERT_OK_PTR(map, "data_input not found"))
+		goto close_prog;
+
+	skel->bss->monitored_pid = getpid();
+
+	/* Test incorrect parameters. */
+	skel->bss->user_keyring_serial = 0;
+	skel->bss->system_keyring = UINT64_MAX;
+
+	ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data, BPF_ANY);
+	if (!ASSERT_LT(ret, 0, "bpf_map_update_elem data_input"))
+		goto close_prog;
+
+	skel->bss->user_keyring_serial = KEY_SPEC_SESSION_KEYRING;
+	skel->bss->system_keyring = 0;
+
+	ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data, BPF_ANY);
+	if (!ASSERT_LT(ret, 0, "bpf_map_update_elem data_input"))
+		goto close_prog;
+
+	/* Test without data and signature. */
+	skel->bss->user_keyring_serial = KEY_SPEC_SESSION_KEYRING;
+	skel->bss->system_keyring = UINT64_MAX;
+
+	ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data, BPF_ANY);
+	if (!ASSERT_LT(ret, 0, "bpf_map_update_elem data_input"))
+		goto close_prog;
+
+	/* Test successful signature verification with session keyring. */
+	ret = populate_data_item_str(tmp_dir, &data);
+	if (!ASSERT_OK(ret, "populate_data_item_str"))
+		goto close_prog;
+
+	ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data, BPF_ANY);
+	if (!ASSERT_OK(ret, "bpf_map_update_elem data_input"))
+		goto close_prog;
+
+	/* Test successful signature verification with testing keyring. */
+	skel->bss->user_keyring_serial = syscall(__NR_request_key, "keyring",
+						 "ebpf_testing_keyring", NULL,
+						 KEY_SPEC_SESSION_KEYRING);
+
+	ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data, BPF_ANY);
+	if (!ASSERT_OK(ret, "bpf_map_update_elem data_input"))
+		goto close_prog;
+
+	/*
+	 * Ensure key_task_permission() is called and rejects the keyring
+	 * (no Search permission).
+	 */
+	syscall(__NR_keyctl, KEYCTL_SETPERM, skel->bss->user_keyring_serial,
+		0x37373737);
+
+	ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data, BPF_ANY);
+	if (!ASSERT_LT(ret, 0, "bpf_map_update_elem data_input"))
+		goto close_prog;
+
+	syscall(__NR_keyctl, KEYCTL_SETPERM, skel->bss->user_keyring_serial,
+		0x3f3f3f3f);
+
+	/*
+	 * Ensure key_validate() is called and rejects the keyring (key expired)
+	 */
+	syscall(__NR_keyctl, KEYCTL_SET_TIMEOUT,
+		skel->bss->user_keyring_serial, 1);
+	sleep(1);
+
+	ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data, BPF_ANY);
+	if (!ASSERT_LT(ret, 0, "bpf_map_update_elem data_input"))
+		goto close_prog;
+
+	skel->bss->user_keyring_serial = KEY_SPEC_SESSION_KEYRING;
+
+	/* Test with corrupted data (signature verification should fail). */
+	data.data[0] = 'a';
+	ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data, BPF_ANY);
+	if (!ASSERT_LT(ret, 0, "bpf_map_update_elem data_input"))
+		goto close_prog;
+
+	ret = populate_data_item_mod(&data);
+	if (!ASSERT_OK(ret, "populate_data_item_mod"))
+		goto close_prog;
+
+	/* Test signature verification with system keyrings. */
+	if (data.data_len) {
+		skel->bss->user_keyring_serial = 0;
+		skel->bss->system_keyring = 0;
+
+		ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data,
+					  BPF_ANY);
+		if (!ASSERT_OK(ret, "bpf_map_update_elem data_input"))
+			goto close_prog;
+
+		skel->bss->system_keyring = VERIFY_USE_SECONDARY_KEYRING;
+
+		ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data,
+					  BPF_ANY);
+		if (!ASSERT_OK(ret, "bpf_map_update_elem data_input"))
+			goto close_prog;
+
+		skel->bss->system_keyring = VERIFY_USE_PLATFORM_KEYRING;
+
+		ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data,
+					  BPF_ANY);
+		ASSERT_LT(ret, 0, "bpf_map_update_elem data_input");
+	}
+
+close_prog:
+	_run_setup_process(tmp_dir, "cleanup");
+	free(buf);
+
+	if (!skel)
+		return;
+
+	skel->bss->monitored_pid = 0;
+	test_verify_pkcs7_sig__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c b/tools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c
new file mode 100644
index 000000000000..421de5ed3fcb
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+#define MAX_DATA_SIZE (1024 * 1024)
+#define MAX_SIG_SIZE 1024
+
+typedef __u8 u8;
+typedef __u16 u16;
+typedef __u32 u32;
+typedef __u64 u64;
+
+u32 monitored_pid;
+u32 user_keyring_serial;
+u64 system_keyring;
+
+struct data {
+	u8 data[MAX_DATA_SIZE];
+	u32 data_len;
+	u8 sig[MAX_SIG_SIZE];
+	u32 sig_len;
+};
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, 1);
+	__type(key, __u32);
+	__type(value, struct data);
+} data_input SEC(".maps");
+
+char _license[] SEC("license") = "GPL";
+
+SEC("lsm.s/bpf")
+int BPF_PROG(bpf, int cmd, union bpf_attr *attr, unsigned int size)
+{
+	struct bpf_dynptr data_ptr, sig_ptr;
+	struct data *data_val;
+	struct key *user_keyring = NULL;
+	u32 pid;
+	u64 value;
+	int ret, zero = 0;
+
+	pid = bpf_get_current_pid_tgid() >> 32;
+	if (pid != monitored_pid)
+		return 0;
+
+	data_val = bpf_map_lookup_elem(&data_input, &zero);
+	if (!data_val)
+		return 0;
+
+	bpf_probe_read(&value, sizeof(value), &attr->value);
+
+	bpf_copy_from_user(data_val, sizeof(struct data),
+			   (void *)(unsigned long)value);
+
+	if (data_val->data_len > sizeof(data_val->data))
+		return -EINVAL;
+
+	bpf_dynptr_from_mem(data_val->data, data_val->data_len, 0, &data_ptr);
+
+	if (data_val->sig_len > sizeof(data_val->sig))
+		return -EINVAL;
+
+	bpf_dynptr_from_mem(data_val->sig, data_val->sig_len, 0, &sig_ptr);
+
+	if (user_keyring_serial) {
+		user_keyring = bpf_lookup_user_key(user_keyring_serial, 0);
+		if (!user_keyring)
+			return -ENOENT;
+	}
+
+	ret = bpf_verify_pkcs7_signature(&data_ptr, &sig_ptr, user_keyring,
+					 system_keyring);
+
+	if (user_keyring)
+		bpf_key_put(user_keyring);
+
+	return ret;
+}
diff --git a/tools/testing/selftests/bpf/verify_sig_setup.sh b/tools/testing/selftests/bpf/verify_sig_setup.sh
new file mode 100755
index 000000000000..ba08922b4a27
--- /dev/null
+++ b/tools/testing/selftests/bpf/verify_sig_setup.sh
@@ -0,0 +1,104 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+set -e
+set -u
+set -o pipefail
+
+VERBOSE="${SELFTESTS_VERBOSE:=0}"
+LOG_FILE="$(mktemp /tmp/verify_sig_setup.log.XXXXXX)"
+
+x509_genkey_content="\
+[ req ]
+default_bits = 2048
+distinguished_name = req_distinguished_name
+prompt = no
+string_mask = utf8only
+x509_extensions = myexts
+
+[ req_distinguished_name ]
+CN = eBPF Signature Verification Testing Key
+
+[ myexts ]
+basicConstraints=critical,CA:FALSE
+keyUsage=digitalSignature
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid
+"
+
+usage()
+{
+	echo "Usage: $0 <setup|cleanup <existing_tmp_dir>"
+	exit 1
+}
+
+setup()
+{
+	local tmp_dir="$1"
+
+	echo "${x509_genkey_content}" > ${tmp_dir}/x509.genkey
+
+	openssl req -new -nodes -utf8 -sha256 -days 36500 \
+			-batch -x509 -config ${tmp_dir}/x509.genkey \
+			-outform PEM -out ${tmp_dir}/signing_key.pem \
+			-keyout ${tmp_dir}/signing_key.pem 2>&1
+
+	openssl x509 -in ${tmp_dir}/signing_key.pem -out \
+		${tmp_dir}/signing_key.der -outform der
+
+	key_id=$(cat ${tmp_dir}/signing_key.der | keyctl padd asymmetric ebpf_testing_key @s)
+
+	keyring_id=$(keyctl newring ebpf_testing_keyring @s)
+	keyctl link $key_id $keyring_id
+}
+
+cleanup() {
+	local tmp_dir="$1"
+
+	keyctl unlink $(keyctl search @s asymmetric ebpf_testing_key) @s
+	keyctl unlink $(keyctl search @s keyring ebpf_testing_keyring) @s
+	rm -rf ${tmp_dir}
+}
+
+catch()
+{
+	local exit_code="$1"
+	local log_file="$2"
+
+	if [[ "${exit_code}" -ne 0 ]]; then
+		cat "${log_file}" >&3
+	fi
+
+	rm -f "${log_file}"
+	exit ${exit_code}
+}
+
+main()
+{
+	[[ $# -ne 2 ]] && usage
+
+	local action="$1"
+	local tmp_dir="$2"
+
+	[[ ! -d "${tmp_dir}" ]] && echo "Directory ${tmp_dir} doesn't exist" && exit 1
+
+	if [[ "${action}" == "setup" ]]; then
+		setup "${tmp_dir}"
+	elif [[ "${action}" == "cleanup" ]]; then
+		cleanup "${tmp_dir}"
+	else
+		echo "Unknown action: ${action}"
+		exit 1
+	fi
+}
+
+trap 'catch "$?" "${LOG_FILE}"' EXIT
+
+if [[ "${VERBOSE}" -eq 0 ]]; then
+	# Save the stderr to 3 so that we can output back to
+	# it incase of an error.
+	exec 3>&2 1>"${LOG_FILE}" 2>&1
+fi
+
+main "$@"
+rm -f "${LOG_FILE}"
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v7 0/7] bpf: Add bpf_verify_pkcs7_signature() helper
  2022-07-12 18:41 [PATCH v7 0/7] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
                   ` (6 preceding siblings ...)
  2022-07-12 18:41 ` [PATCH v7 7/7] selftests/bpf: Add test for bpf_verify_pkcs7_signature() helper Roberto Sassu
@ 2022-07-13  0:53 ` Alexei Starovoitov
  7 siblings, 0 replies; 9+ messages in thread
From: Alexei Starovoitov @ 2022-07-13  0:53 UTC (permalink / raw)
  To: Roberto Sassu
  Cc: ast, daniel, john.fastabend, andrii, martin.lau, song, yhs,
	kpsingh, dhowells, jarkko, shuah, bpf, keyrings,
	linux-security-module, linux-kselftest, netdev, linux-kernel

On Tue, Jul 12, 2022 at 08:41:21PM +0200, Roberto Sassu wrote:
> One of the desirable features in security is the ability to restrict import
> of data to a given system based on data authenticity. If data import can be
> restricted, it would be possible to enforce a system-wide policy based on
> the signing keys the system owner trusts.
> 
> This feature is widely used in the kernel. For example, if the restriction
> is enabled, kernel modules can be plugged in only if they are signed with a
> key whose public part is in the primary or secondary keyring.
> 
> For eBPF, it can be useful as well. For example, it might be useful to
> authenticate data an eBPF program makes security decisions on.
> 
> After a discussion in the eBPF mailing list, it was decided that the stated
> goal should be accomplished by introducing a new helper:
> bpf_verify_pkcs7_signature(), dedicated to verify PKCS#7 signatures.
> 
> Other than the data and the signature, the helper also receives two
> parameters for the keyring, which can be provided as alternatives: one is a
> key pointer returned by the new bpf_lookup_user_key() helper, called with a
> key serial possibly decided by the user; another is a pre-determined ID
> among values defined in include/linux/verification.h.
> 
> While the first keyring-related parameter provides great flexibility, it
> seems suboptimal in terms of security guarantees, as even if the eBPF
> program is assumed to be trusted, the serial used to obtain the key pointer
> might come from untrusted user space not choosing one that the system
> administrator approves to enforce a mandatory policy.
> 
> The second keyring-related parameter instead provides much stronger
> guarantees, especially if the pre-determined ID is not passed by user space
> but is hardcoded in the eBPF program, and that program is signed. In this
> case, bpf_verify_pkcs7_signature() will always perform signature
> verification with a key that the system administrator approves, i.e. the
> primary, secondary or platform keyring.
> 
> bpf_lookup_user_key() comes with the corresponding release helper
> bpf_key_put(), to decrement the reference count of the key found with the
> former helper. The eBPF verifier has been enhanced to ensure that the
> release helper is always called whenever the acquire helper is called, or
> otherwise refuses to load the program.
> 
> bpf_lookup_user_key() also accepts lookup-specific flags KEY_LOOKUP_CREATE
> and KEY_LOOKUP_PARTIAL. Although these are most likely not useful for the
> bpf_verify_pkcs7_signature(), newly defined flags could be.
> 
> bpf_lookup_user_key() does not request a particular permission to
> lookup_user_key(), as it cannot determine it by itself. Also, it should not
> get it from the user, as the user could pass an arbitrary value and use the
> key for a different purpose. Instead, bpf_lookup_user_key() requests
> KEY_DEFER_PERM_CHECK, and defers the permission check to the helper that
> actually uses the key, in this patch set to bpf_verify_pkcs7_signature().
> 
> Since key_task_permission() is called by the PKCS#7 code during signature
> verification, the only additional function bpf_verify_pkcs7_signature() has
> to call is key_validate(). With that, the permission check can be
> considered complete and equivalent, as it was done by bpf_lookup_user_key()
> with the appropriate permission (in this case KEY_NEED_SEARCH).
> 
> All helpers can be called only from sleepable programs, because of memory
> allocation (with lookup flag KEY_LOOKUP_CREATE) and crypto operations. For
> example, the lsm.s/bpf attach point is suitable,
> fexit/array_map_update_elem is not.
> 
> The correctness of implementation of the new helpers and of their usage is
> checked with the introduced tests.
> 
> The patch set is organized as follows.
> 
> Patch 1 exports bpf_dynptr_get_size(), to obtain the real size of data
> carried by a dynamic pointer. Patch 2 makes available for new eBPF helpers
> some key-related definitions. Patch 3 introduces the bpf_lookup_user_key()
> and bpf_key_put() helpers. Patch 4 introduces the
> bpf_verify_pkcs7_signature(). Finally, patches 5-7 introduce the tests.
> 
> Changelog
> 
> v6:
>  - Switch back to key lookup helpers + signature verification (until v5),
>    and defer permission check from bpf_lookup_user_key() to
>    bpf_verify_pkcs7_signature()
>  - Add additional key lookup test to illustrate the usage of the
>    KEY_LOOKUP_CREATE flag and validate the flags (suggested by Daniel)
>  - Make description of flags of bpf_lookup_user_key() more user-friendly
>    (suggested by Daniel)
>  - Fix validation of flags parameter in bpf_lookup_user_key() (reported by
>    Daniel)
>  - Rename bpf_verify_pkcs7_signature() keyring-related parameters to
>    user_keyring and system_keyring to make their purpose more clear
>  - Accept keyring-related parameters of bpf_verify_pkcs7_signature() as
>    alternatives (suggested by KP)
>  - Replace unsigned long type with u64 in helper declaration (suggested by
>    Daniel)
>  - Extend the bpf_verify_pkcs7_signature() test by calling the helper
>    without data, by ensuring that the helper enforces the keyring-related
>    parameters as alternatives, by ensuring that the helper rejects
>    inaccessible and expired keyrings, and by checking all system keyrings
>  - Move bpf_lookup_user_key() and bpf_key_put() usage tests to
>    ref_tracking.c (suggested by John)
>  - Call bpf_lookup_user_key() and bpf_key_put() only in sleepable programs

Judging by amount of back and forth in api design and still outstanding
questions whether it's something that will work long term we probably
should not be baking these helpers into uapi.
Let's extend verifier support for ARG_PTR_TO_DYNPTR in kfunc and make
them all as kfuncs.
This way we can change them later.

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2022-07-13  0:54 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-07-12 18:41 [PATCH v7 0/7] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
2022-07-12 18:41 ` [PATCH v7 1/7] bpf: Export bpf_dynptr_get_size() Roberto Sassu
2022-07-12 18:41 ` [PATCH v7 2/7] KEYS: Move KEY_LOOKUP_ to include/linux/key.h Roberto Sassu
2022-07-12 18:41 ` [PATCH v7 3/7] bpf: Add bpf_lookup_user_key() and bpf_key_put() helpers Roberto Sassu
2022-07-12 18:41 ` [PATCH v7 4/7] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
2022-07-12 18:41 ` [PATCH v7 5/7] selftests: Add verifier tests for bpf_lookup_user_key() and bpf_key_put() Roberto Sassu
2022-07-12 18:41 ` [PATCH v7 6/7] selftests/bpf: Add additional test for bpf_lookup_user_key() Roberto Sassu
2022-07-12 18:41 ` [PATCH v7 7/7] selftests/bpf: Add test for bpf_verify_pkcs7_signature() helper Roberto Sassu
2022-07-13  0:53 ` [PATCH v7 0/7] bpf: Add " Alexei Starovoitov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).