linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v6 0/5] bpf: Add bpf_verify_pkcs7_signature() helper
@ 2022-06-28 12:27 Roberto Sassu
  2022-06-28 12:27 ` [PATCH v6 1/5] bpf: Export bpf_dynptr_get_size() Roberto Sassu
                   ` (4 more replies)
  0 siblings, 5 replies; 11+ messages in thread
From: Roberto Sassu @ 2022-06-28 12:27 UTC (permalink / raw)
  To: ast, daniel, andrii, kpsingh, john.fastabend, songliubraving,
	kafai, yhs, dhowells
  Cc: keyrings, bpf, netdev, linux-kselftest, linux-security-module,
	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. More
helpers will be introduced later, as necessary.

The job of bpf_verify_pkcs7_signature() is to retrieve the trusted keyring 
from function parameters, and to perform signature verification by calling
verify_pkcs7_signature().

Data and signature can be provided to the new helper with two dynamic
pointers, to reduce the number of parameters. The keyring can be provided
by its serial, or by its special ID defined in verification.h, if the
serial is zero (not a valid value). bpf_verify_pkcs7_signature() also
accepts key lookup-specific flags, passed to lookup_user_key() when the
helper searches the keyring by its serial.

While passing the keyring serial to bpf_verify_pkcs7_signature() provides
great flexibility, it seems suboptimal in terms of security guarantees, as
even if the eBPF program is assumed to be trusted, that serial might come
from untrusted user space not choosing one that the system administrator
approves to enforce a mandatory policy. The same goal could be instead more
easily achieved by setting a hardcoded keyring ID in the signed eBPF
program, to be passed to bpf_verify_pkcs7_signature().

bpf_verify_pkcs7_signature() 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.

A test was added to check the ability of bpf_verify_pkcs7_signature() to
verify PKCS#7 signatures from the session keyring, a newly-created keyring,
and from the primary and secondary keyring (taking the tcp_bic.ko kernel
module for the verification). The test does not fail if that kernel module
is not found (needs support from the CI).

A consideration was made on whether bpf_verify_pkcs7_signature() should be
a simple wrapper, doing as little as possible, or whether it could have
more complex logic. Having a simple and flexible wrapper requires two
additional helpers, bpf_lookup_user_key() and bpf_key_put(), to search and
acquire a key reference, pass that key to the wrapper, and release the
reference. More care is also required on the eBPF verifier side, to ensure
that an eBPF program always releases an acquired reference.

While that gives eBPF developers the greatest flexibility to use the
helpers as necessary, it does not match the security of the solution of
retrieving the key and using it within the same function, as for example in
security/keys/keyctl.c. The risk is that an eBPF program requests a key for
a purpose, and then uses the key in a different way with one of the
available key-related helpers (to be added in the future).

struct key is not like a file descriptor, carrying permissions requested
during an open, that can be revalidated at the time a read or write is
performed. It is more close to a struct inode, the function using the key
cannot know reliably which permission was requested at lookup time.

For that reason, the key lookup and usage cannot be separated, as the
kernel will guarantee (also to other MAC mechanisms) that once a key has
been requested with a specific purpose, it will be used accordingly, beyond
the control of eBFP programs.

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 fixes the helper prototype regular
expression to accept unsigned as type prefix. Patch 4 introduces the
bpf_verify_pkcs7_signature() helper and patch 5 adds the corresponding
test.

Changelog

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 (5):
  bpf: Export bpf_dynptr_get_size()
  KEYS: Move KEY_LOOKUP_ to include/linux/key.h
  scripts: Handle unsigned type prefix in bpf_doc.py
  bpf: Add bpf_verify_pkcs7_signature() helper
  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                      |  24 ++
 kernel/bpf/bpf_lsm.c                          |  63 +++
 kernel/bpf/helpers.c                          |   2 +-
 scripts/bpf_doc.py                            |   2 +-
 security/keys/internal.h                      |   2 -
 tools/include/uapi/linux/bpf.h                |  24 ++
 tools/testing/selftests/bpf/Makefile          |  14 +-
 tools/testing/selftests/bpf/config            |   2 +
 .../bpf/prog_tests/verify_pkcs7_sig.c         | 359 ++++++++++++++++++
 .../bpf/progs/test_verify_pkcs7_sig.c         |  79 ++++
 .../testing/selftests/bpf/verify_sig_setup.sh | 104 +++++
 13 files changed, 672 insertions(+), 7 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

-- 
2.25.1


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

* [PATCH v6 1/5] bpf: Export bpf_dynptr_get_size()
  2022-06-28 12:27 [PATCH v6 0/5] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
@ 2022-06-28 12:27 ` Roberto Sassu
  2022-06-28 12:27 ` [PATCH v6 2/5] KEYS: Move KEY_LOOKUP_ to include/linux/key.h Roberto Sassu
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 11+ messages in thread
From: Roberto Sassu @ 2022-06-28 12:27 UTC (permalink / raw)
  To: ast, daniel, andrii, kpsingh, john.fastabend, songliubraving,
	kafai, yhs, dhowells
  Cc: keyrings, bpf, netdev, linux-kselftest, linux-security-module,
	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 d05e1495a06e..3ec0f167e9d7 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -2521,5 +2521,6 @@ 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);
 
 #endif /* _LINUX_BPF_H */
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] 11+ messages in thread

* [PATCH v6 2/5] KEYS: Move KEY_LOOKUP_ to include/linux/key.h
  2022-06-28 12:27 [PATCH v6 0/5] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
  2022-06-28 12:27 ` [PATCH v6 1/5] bpf: Export bpf_dynptr_get_size() Roberto Sassu
@ 2022-06-28 12:27 ` Roberto Sassu
  2022-06-28 12:27 ` [PATCH v6 3/5] scripts: Handle unsigned type prefix in bpf_doc.py Roberto Sassu
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 11+ messages in thread
From: Roberto Sassu @ 2022-06-28 12:27 UTC (permalink / raw)
  To: ast, daniel, andrii, kpsingh, john.fastabend, songliubraving,
	kafai, yhs, dhowells
  Cc: keyrings, bpf, netdev, linux-kselftest, linux-security-module,
	linux-kernel, Roberto Sassu

In preparation for the patch that introduces the
bpf_verify_pkcs7_signature() 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] 11+ messages in thread

* [PATCH v6 3/5] scripts: Handle unsigned type prefix in bpf_doc.py
  2022-06-28 12:27 [PATCH v6 0/5] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
  2022-06-28 12:27 ` [PATCH v6 1/5] bpf: Export bpf_dynptr_get_size() Roberto Sassu
  2022-06-28 12:27 ` [PATCH v6 2/5] KEYS: Move KEY_LOOKUP_ to include/linux/key.h Roberto Sassu
@ 2022-06-28 12:27 ` Roberto Sassu
  2022-06-28 12:27 ` [PATCH v6 4/5] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
  2022-06-28 12:27 ` [PATCH v6 5/5] selftests/bpf: Add test for " Roberto Sassu
  4 siblings, 0 replies; 11+ messages in thread
From: Roberto Sassu @ 2022-06-28 12:27 UTC (permalink / raw)
  To: ast, daniel, andrii, kpsingh, john.fastabend, songliubraving,
	kafai, yhs, dhowells
  Cc: keyrings, bpf, netdev, linux-kselftest, linux-security-module,
	linux-kernel, Roberto Sassu

While unsigned long is an accepted parameter type, the regular expression
validating helper prototypes does not correctly take into account types
composed by multiple words.

The regular expression:

((const )?(struct )?(\w+|\.\.\.)( \**\w+)?)

accepts only const and struct as prefix before the type. The following part
of the regular expression expects a word with [a-zA-Z0-9_] characters
(without space), so it would get just unsigned. Parsing words with \w+ in
greedy mode makes the regular expression work even if the type is composed
by two words, but not always. It wouldn't have been the case in possessive
mode \w++ (don't give back characters to match the regular expression).

Simply adding unsigned as possible prefix is not correct, as the struct
unsigned combination is not legal. Make instead struct and unsigned as
alternatives, so that the following new combinations are legal:

unsigned type
struct type
const unsigned type

and not:

struct unsigned type

The regular expression is a preliminary check. The type, other than being
legal, must be also present in the known_types array.

Don't mention the change in the regular expression description, as it is
assumed that type implies also multiple words types.

At this point, don't switch from greedy to possessive mode (\w+ -> \w++) to
avoid partial parsing of the type of helper parameters, as this
functionality has only been added recently in Python 3.11.

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 scripts/bpf_doc.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/bpf_doc.py b/scripts/bpf_doc.py
index a0ec321469bd..25e79d811487 100755
--- a/scripts/bpf_doc.py
+++ b/scripts/bpf_doc.py
@@ -124,7 +124,7 @@ class HeaderParser(object):
         #   - Same as above, with "const" and/or "struct" in front of type
         #   - "..." (undefined number of arguments, for bpf_trace_printk())
         # There is at least one term ("void"), and at most five arguments.
-        p = re.compile(' \* ?((.+) \**\w+\((((const )?(struct )?(\w+|\.\.\.)( \**\w+)?)(, )?){1,5}\))$')
+        p = re.compile(' \* ?((.+) \**\w+\((((const )?((struct )|(unsigned )?)(\w+|\.\.\.)( \**\w+)?)(, )?){1,5}\))$')
         capture = p.match(self.line)
         if not capture:
             raise NoHelperFound
-- 
2.25.1


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

* [PATCH v6 4/5] bpf: Add bpf_verify_pkcs7_signature() helper
  2022-06-28 12:27 [PATCH v6 0/5] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
                   ` (2 preceding siblings ...)
  2022-06-28 12:27 ` [PATCH v6 3/5] scripts: Handle unsigned type prefix in bpf_doc.py Roberto Sassu
@ 2022-06-28 12:27 ` Roberto Sassu
  2022-07-06 16:03   ` Daniel Borkmann
  2022-06-28 12:27 ` [PATCH v6 5/5] selftests/bpf: Add test for " Roberto Sassu
  4 siblings, 1 reply; 11+ messages in thread
From: Roberto Sassu @ 2022-06-28 12:27 UTC (permalink / raw)
  To: ast, daniel, andrii, kpsingh, john.fastabend, songliubraving,
	kafai, yhs, dhowells
  Cc: keyrings, bpf, netdev, linux-kselftest, linux-security-module,
	linux-kernel, Roberto Sassu, kernel test robot

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).

The caller should also provide a trusted keyring serial, together with key
lookup-specific flags, to determine which keys can be used for signature
verification. Alternatively, the caller could specify zero as serial value
(not valid, serials must be positive), and provide instead a special
keyring ID.

Key lookup flags are defined in include/linux/key.h and can be: 1, to
request that special keyrings be created if referred to directly; 2 to
permit partially constructed keys to be found.

Special IDs are defined in include/linux/verification.h and can be: 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).

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
Reported-by: kernel test robot <lkp@intel.com> (cast warning)
---
 include/uapi/linux/bpf.h       | 24 +++++++++++++
 kernel/bpf/bpf_lsm.c           | 63 ++++++++++++++++++++++++++++++++++
 tools/include/uapi/linux/bpf.h | 24 +++++++++++++
 3 files changed, 111 insertions(+)

diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index e81362891596..b4f5ad863281 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -5325,6 +5325,29 @@ union bpf_attr {
  *		**-EACCES** if the SYN cookie is not valid.
  *
  *		**-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin.
+ *
+ * long bpf_verify_pkcs7_signature(struct bpf_dynptr *data_ptr, struct bpf_dynptr *sig_ptr, u32 trusted_keyring_serial, unsigned long lookup_flags, unsigned long trusted_keyring_id)
+ *	Description
+ *		Verify the PKCS#7 signature *sig_ptr* against the supplied
+ *		*data_ptr* with keys in a keyring with serial
+ *		*trusted_keyring_serial*, searched with *lookup_flags*, if the
+ *		parameter value is positive, or alternatively in a keyring with
+ *		special ID *trusted_keyring_id* if *trusted_keyring_serial* is
+ *		zero.
+ *
+ *		*lookup_flags* are defined in include/linux/key.h and can be: 1,
+ *		to request that special keyrings be created if referred to
+ *		directly; 2 to permit partially constructed keys to be found.
+ *
+ *		Special IDs are defined in include/linux/verification.h and can
+ *		be: 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),			\
@@ -5535,6 +5558,7 @@ union bpf_attr {
 	FN(tcp_raw_gen_syncookie_ipv6),	\
 	FN(tcp_raw_check_syncookie_ipv4),	\
 	FN(tcp_raw_check_syncookie_ipv6),	\
+	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 c1351df9f7ee..401bda01ad84 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -16,6 +16,8 @@
 #include <linux/bpf_local_storage.h>
 #include <linux/btf_ids.h>
 #include <linux/ima.h>
+#include <linux/verification.h>
+#include <linux/key.h>
 
 /* For every LSM hook that allows attachment of BPF programs, declare a nop
  * function where a BPF program can be attached.
@@ -132,6 +134,62 @@ static const struct bpf_func_proto bpf_get_attach_cookie_proto = {
 	.arg1_type	= ARG_PTR_TO_CTX,
 };
 
+#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
+BPF_CALL_5(bpf_verify_pkcs7_signature, struct bpf_dynptr_kern *, data_ptr,
+	   struct bpf_dynptr_kern *, sig_ptr, u32, trusted_keyring_serial,
+	   unsigned long, lookup_flags, unsigned long, trusted_keyring_id)
+{
+	key_ref_t trusted_keyring_ref;
+	struct key *trusted_keyring;
+	int ret;
+
+	/* Keep in sync with defs in include/linux/key.h. */
+	if (lookup_flags > KEY_LOOKUP_PARTIAL)
+		return -EINVAL;
+
+	/* Keep in sync with defs in include/linux/verification.h. */
+	if (trusted_keyring_id > (unsigned long)VERIFY_USE_PLATFORM_KEYRING)
+		return -EINVAL;
+
+	if (trusted_keyring_serial) {
+		trusted_keyring_ref = lookup_user_key(trusted_keyring_serial,
+						      lookup_flags,
+						      KEY_NEED_SEARCH);
+		if (IS_ERR(trusted_keyring_ref))
+			return PTR_ERR(trusted_keyring_ref);
+
+		trusted_keyring = key_ref_to_ptr(trusted_keyring_ref);
+		goto verify;
+	}
+
+	trusted_keyring = (struct key *)trusted_keyring_id;
+verify:
+	ret = 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);
+	if (trusted_keyring_serial)
+		key_put(trusted_keyring);
+
+	return ret;
+}
+
+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_ANYTHING,
+	.arg4_type	= ARG_ANYTHING,
+	.arg5_type	= ARG_ANYTHING,
+	.allowed	= bpf_ima_inode_hash_allowed,
+};
+#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
+
 static const struct bpf_func_proto *
 bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 {
@@ -158,6 +216,11 @@ bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 		return prog->aux->sleepable ? &bpf_ima_file_hash_proto : NULL;
 	case BPF_FUNC_get_attach_cookie:
 		return bpf_prog_has_trampoline(prog) ? &bpf_get_attach_cookie_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 */
 	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 e81362891596..b4f5ad863281 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -5325,6 +5325,29 @@ union bpf_attr {
  *		**-EACCES** if the SYN cookie is not valid.
  *
  *		**-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin.
+ *
+ * long bpf_verify_pkcs7_signature(struct bpf_dynptr *data_ptr, struct bpf_dynptr *sig_ptr, u32 trusted_keyring_serial, unsigned long lookup_flags, unsigned long trusted_keyring_id)
+ *	Description
+ *		Verify the PKCS#7 signature *sig_ptr* against the supplied
+ *		*data_ptr* with keys in a keyring with serial
+ *		*trusted_keyring_serial*, searched with *lookup_flags*, if the
+ *		parameter value is positive, or alternatively in a keyring with
+ *		special ID *trusted_keyring_id* if *trusted_keyring_serial* is
+ *		zero.
+ *
+ *		*lookup_flags* are defined in include/linux/key.h and can be: 1,
+ *		to request that special keyrings be created if referred to
+ *		directly; 2 to permit partially constructed keys to be found.
+ *
+ *		Special IDs are defined in include/linux/verification.h and can
+ *		be: 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),			\
@@ -5535,6 +5558,7 @@ union bpf_attr {
 	FN(tcp_raw_gen_syncookie_ipv6),	\
 	FN(tcp_raw_check_syncookie_ipv4),	\
 	FN(tcp_raw_check_syncookie_ipv6),	\
+	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] 11+ messages in thread

* [PATCH v6 5/5] selftests/bpf: Add test for bpf_verify_pkcs7_signature() helper
  2022-06-28 12:27 [PATCH v6 0/5] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
                   ` (3 preceding siblings ...)
  2022-06-28 12:27 ` [PATCH v6 4/5] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
@ 2022-06-28 12:27 ` Roberto Sassu
  2022-06-28 12:32   ` Roberto Sassu
  4 siblings, 1 reply; 11+ messages in thread
From: Roberto Sassu @ 2022-06-28 12:27 UTC (permalink / raw)
  To: ast, daniel, andrii, kpsingh, john.fastabend, songliubraving,
	kafai, yhs, dhowells
  Cc: keyrings, bpf, netdev, linux-kselftest, linux-security-module,
	linux-kernel, Roberto Sassu

Ensure that signature verification is performed successfully from an eBPF
program, with the new bpf_verify_pkcs7_signature() helper.

Generate a testing signature key and compile sign-file from scripts/, so
that the test is selfcontained. Also, search for the tcb_bic.ko kernel
module, parse it in user space to extract the raw PKCS#7 signature and send
it to the eBPF program for signature verification. If tcb_bic.ko is not
found, the test does not fail.

The additional verification of a kernel module is necessary to test lookup
of a keyring with a special ID (primary, secondary or platform) since, if
kernel modules are signed, the public key is also added at kernel build
time to one of those keyrings.

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         | 359 ++++++++++++++++++
 .../bpf/progs/test_verify_pkcs7_sig.c         |  79 ++++
 .../testing/selftests/bpf/verify_sig_setup.sh | 104 +++++
 5 files changed, 555 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 4fbd88a8ed9e..cad3607e9a6f 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 \
 	xdpxceiver 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
@@ -512,7 +519,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..85552dc48333
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c
@@ -0,0 +1,359 @@
+// 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
+
+/* 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;
+
+	ret = populate_data_item_str(tmp_dir, &data);
+	if (!ASSERT_OK(ret, "populate_data_item_str"))
+		goto close_prog;
+
+	skel->bss->monitored_pid = getpid();
+	skel->bss->trusted_keyring_serial = 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"))
+		goto close_prog;
+
+	/* Search the verification key in the testing keyring. */
+	skel->bss->trusted_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"))
+		goto close_prog;
+
+	/* Corrupt 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;
+
+	if (data.data_len) {
+		skel->bss->trusted_keyring_serial = 0;
+		skel->bss->trusted_keyring_id = 0;
+
+		ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data,
+					  BPF_ANY);
+		if (!ASSERT_OK(ret, "bpf_map_update_elem"))
+			goto close_prog;
+
+		skel->bss->trusted_keyring_id = 1;
+
+		ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data,
+					  BPF_ANY);
+		if (!ASSERT_OK(ret, "bpf_map_update_elem"))
+			goto close_prog;
+
+		/* Verification key is not in the platform keyring. */
+		skel->bss->trusted_keyring_id = 2;
+
+		ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data,
+					  BPF_ANY);
+		ASSERT_LT(ret, 0, "bpf_map_update_elem");
+	}
+
+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..e9937f1ec541
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c
@@ -0,0 +1,79 @@
+// 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 trusted_keyring_serial;
+unsigned long trusted_keyring_id;
+
+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;
+	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);
+
+	return bpf_verify_pkcs7_signature(&data_ptr, &sig_ptr,
+					  trusted_keyring_serial, 0,
+					  trusted_keyring_id);
+}
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] 11+ messages in thread

* RE: [PATCH v6 5/5] selftests/bpf: Add test for bpf_verify_pkcs7_signature() helper
  2022-06-28 12:27 ` [PATCH v6 5/5] selftests/bpf: Add test for " Roberto Sassu
@ 2022-06-28 12:32   ` Roberto Sassu
  0 siblings, 0 replies; 11+ messages in thread
From: Roberto Sassu @ 2022-06-28 12:32 UTC (permalink / raw)
  To: ast, daniel, andrii, kpsingh, john.fastabend, songliubraving,
	kafai, yhs, dhowells
  Cc: keyrings, bpf, netdev, linux-kselftest, linux-security-module,
	linux-kernel

> From: Roberto Sassu
> Sent: Tuesday, June 28, 2022 2:28 PM
> Ensure that signature verification is performed successfully from an eBPF
> program, with the new bpf_verify_pkcs7_signature() helper.
> 
> Generate a testing signature key and compile sign-file from scripts/, so
> that the test is selfcontained. Also, search for the tcb_bic.ko kernel
> module, parse it in user space to extract the raw PKCS#7 signature and send
> it to the eBPF program for signature verification. If tcb_bic.ko is not
> found, the test does not fail.

Ops, tcp_bic.ko.

Roberto

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

* Re: [PATCH v6 4/5] bpf: Add bpf_verify_pkcs7_signature() helper
  2022-06-28 12:27 ` [PATCH v6 4/5] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
@ 2022-07-06 16:03   ` Daniel Borkmann
  2022-07-06 23:49     ` KP Singh
  0 siblings, 1 reply; 11+ messages in thread
From: Daniel Borkmann @ 2022-07-06 16:03 UTC (permalink / raw)
  To: Roberto Sassu, ast, andrii, kpsingh, john.fastabend,
	songliubraving, kafai, yhs, dhowells
  Cc: keyrings, bpf, netdev, linux-kselftest, linux-security-module,
	linux-kernel

On 6/28/22 2:27 PM, Roberto Sassu wrote:
> 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).
> 
> The caller should also provide a trusted keyring serial, together with key
> lookup-specific flags, to determine which keys can be used for signature
> verification. Alternatively, the caller could specify zero as serial value
> (not valid, serials must be positive), and provide instead a special
> keyring ID.
> 
> Key lookup flags are defined in include/linux/key.h and can be: 1, to
> request that special keyrings be created if referred to directly; 2 to
> permit partially constructed keys to be found.
> 
> Special IDs are defined in include/linux/verification.h and can be: 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).
> 
> Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> Reported-by: kernel test robot <lkp@intel.com> (cast warning)

nit: Given this a new feature not a fix to existing code, there is no need to
      add the above reported-by from kbuild bot.

> ---
>   include/uapi/linux/bpf.h       | 24 +++++++++++++
>   kernel/bpf/bpf_lsm.c           | 63 ++++++++++++++++++++++++++++++++++
>   tools/include/uapi/linux/bpf.h | 24 +++++++++++++
>   3 files changed, 111 insertions(+)
> 
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index e81362891596..b4f5ad863281 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -5325,6 +5325,29 @@ union bpf_attr {
>    *		**-EACCES** if the SYN cookie is not valid.
>    *
>    *		**-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin.
> + *
> + * long bpf_verify_pkcs7_signature(struct bpf_dynptr *data_ptr, struct bpf_dynptr *sig_ptr, u32 trusted_keyring_serial, unsigned long lookup_flags, unsigned long trusted_keyring_id)

nit: for the args instead of ulong, just do u64

> + *	Description
> + *		Verify the PKCS#7 signature *sig_ptr* against the supplied
> + *		*data_ptr* with keys in a keyring with serial
> + *		*trusted_keyring_serial*, searched with *lookup_flags*, if the
> + *		parameter value is positive, or alternatively in a keyring with
> + *		special ID *trusted_keyring_id* if *trusted_keyring_serial* is
> + *		zero.
> + *
> + *		*lookup_flags* are defined in include/linux/key.h and can be: 1,
> + *		to request that special keyrings be created if referred to
> + *		directly; 2 to permit partially constructed keys to be found.
> + *
> + *		Special IDs are defined in include/linux/verification.h and can
> + *		be: 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),			\
> @@ -5535,6 +5558,7 @@ union bpf_attr {
>   	FN(tcp_raw_gen_syncookie_ipv6),	\
>   	FN(tcp_raw_check_syncookie_ipv4),	\
>   	FN(tcp_raw_check_syncookie_ipv6),	\
> +	FN(verify_pkcs7_signature),	\

(Needs rebase)

>   	/* */
>   
>   /* 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 c1351df9f7ee..401bda01ad84 100644
> --- a/kernel/bpf/bpf_lsm.c
> +++ b/kernel/bpf/bpf_lsm.c
> @@ -16,6 +16,8 @@
>   #include <linux/bpf_local_storage.h>
>   #include <linux/btf_ids.h>
>   #include <linux/ima.h>
> +#include <linux/verification.h>
> +#include <linux/key.h>
>   
>   /* For every LSM hook that allows attachment of BPF programs, declare a nop
>    * function where a BPF program can be attached.
> @@ -132,6 +134,62 @@ static const struct bpf_func_proto bpf_get_attach_cookie_proto = {
>   	.arg1_type	= ARG_PTR_TO_CTX,
>   };
>   
> +#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
> +BPF_CALL_5(bpf_verify_pkcs7_signature, struct bpf_dynptr_kern *, data_ptr,
> +	   struct bpf_dynptr_kern *, sig_ptr, u32, trusted_keyring_serial,
> +	   unsigned long, lookup_flags, unsigned long, trusted_keyring_id)
> +{
> +	key_ref_t trusted_keyring_ref;
> +	struct key *trusted_keyring;
> +	int ret;
> +
> +	/* Keep in sync with defs in include/linux/key.h. */
> +	if (lookup_flags > KEY_LOOKUP_PARTIAL)
> +		return -EINVAL;

iiuc, the KEY_LOOKUP_* is a mask, so you could also combine the two, e.g.
KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL. I haven't seen you mentioning anything
specific on why it is not allowed. What's the rationale, if it's intentional
if should probably be documented?

At minimum I also think the helper description needs to be improved for people
to understand enough w/o reading through the kernel source, e.g. wrt lookup_flags
since I haven't seen it in your selftests either ... when does a user need to
use the given flags.

nit: when both trusted_keyring_serial and trusted_keyring_id are passed to the
helper, then this should be rejected as invalid argument? (Kind of feels a bit
like we're cramming two things in one helper.. KP, thoughts? :))

> +	/* Keep in sync with defs in include/linux/verification.h. */
> +	if (trusted_keyring_id > (unsigned long)VERIFY_USE_PLATFORM_KEYRING)
> +		return -EINVAL;
> +
> +	if (trusted_keyring_serial) {
> +		trusted_keyring_ref = lookup_user_key(trusted_keyring_serial,
> +						      lookup_flags,
> +						      KEY_NEED_SEARCH);
> +		if (IS_ERR(trusted_keyring_ref))
> +			return PTR_ERR(trusted_keyring_ref);
> +
> +		trusted_keyring = key_ref_to_ptr(trusted_keyring_ref);
> +		goto verify;
> +	}
> +
> +	trusted_keyring = (struct key *)trusted_keyring_id;
> +verify:
> +	ret = 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);
> +	if (trusted_keyring_serial)
> +		key_put(trusted_keyring);
> +
> +	return ret;
> +}
> +
> +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_ANYTHING,
> +	.arg4_type	= ARG_ANYTHING,
> +	.arg5_type	= ARG_ANYTHING,
> +	.allowed	= bpf_ima_inode_hash_allowed,
> +};
> +#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
> +

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

* Re: [PATCH v6 4/5] bpf: Add bpf_verify_pkcs7_signature() helper
  2022-07-06 16:03   ` Daniel Borkmann
@ 2022-07-06 23:49     ` KP Singh
  2022-07-07 11:00       ` Roberto Sassu
  0 siblings, 1 reply; 11+ messages in thread
From: KP Singh @ 2022-07-06 23:49 UTC (permalink / raw)
  To: Daniel Borkmann
  Cc: Roberto Sassu, ast, andrii, john.fastabend, songliubraving,
	kafai, yhs, dhowells, keyrings, bpf, netdev, linux-kselftest,
	linux-security-module, linux-kernel

On Wed, Jul 6, 2022 at 6:04 PM Daniel Borkmann <daniel@iogearbox.net> wrote:
>
> On 6/28/22 2:27 PM, Roberto Sassu wrote:
> > 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).
> >
> > The caller should also provide a trusted keyring serial, together with key
> > lookup-specific flags, to determine which keys can be used for signature
> > verification. Alternatively, the caller could specify zero as serial value
> > (not valid, serials must be positive), and provide instead a special
> > keyring ID.
> >
> > Key lookup flags are defined in include/linux/key.h and can be: 1, to
> > request that special keyrings be created if referred to directly; 2 to
> > permit partially constructed keys to be found.
> >
> > Special IDs are defined in include/linux/verification.h and can be: 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).
> >
> > Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> > Reported-by: kernel test robot <lkp@intel.com> (cast warning)
>
> nit: Given this a new feature not a fix to existing code, there is no need to
>       add the above reported-by from kbuild bot.
>
> > ---
> >   include/uapi/linux/bpf.h       | 24 +++++++++++++
> >   kernel/bpf/bpf_lsm.c           | 63 ++++++++++++++++++++++++++++++++++
> >   tools/include/uapi/linux/bpf.h | 24 +++++++++++++
> >   3 files changed, 111 insertions(+)
> >
> > diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> > index e81362891596..b4f5ad863281 100644
> > --- a/include/uapi/linux/bpf.h
> > +++ b/include/uapi/linux/bpf.h
> > @@ -5325,6 +5325,29 @@ union bpf_attr {
> >    *          **-EACCES** if the SYN cookie is not valid.
> >    *
> >    *          **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin.
> > + *
> > + * long bpf_verify_pkcs7_signature(struct bpf_dynptr *data_ptr, struct bpf_dynptr *sig_ptr, u32 trusted_keyring_serial, unsigned long lookup_flags, unsigned long trusted_keyring_id)
>
> nit: for the args instead of ulong, just do u64
>
> > + *   Description
> > + *           Verify the PKCS#7 signature *sig_ptr* against the supplied
> > + *           *data_ptr* with keys in a keyring with serial
> > + *           *trusted_keyring_serial*, searched with *lookup_flags*, if the
> > + *           parameter value is positive, or alternatively in a keyring with
> > + *           special ID *trusted_keyring_id* if *trusted_keyring_serial* is
> > + *           zero.
> > + *
> > + *           *lookup_flags* are defined in include/linux/key.h and can be: 1,
> > + *           to request that special keyrings be created if referred to
> > + *           directly; 2 to permit partially constructed keys to be found.
> > + *
> > + *           Special IDs are defined in include/linux/verification.h and can
> > + *           be: 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),                     \
> > @@ -5535,6 +5558,7 @@ union bpf_attr {
> >       FN(tcp_raw_gen_syncookie_ipv6), \
> >       FN(tcp_raw_check_syncookie_ipv4),       \
> >       FN(tcp_raw_check_syncookie_ipv6),       \
> > +     FN(verify_pkcs7_signature),     \
>
> (Needs rebase)
>
> >       /* */
> >
> >   /* 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 c1351df9f7ee..401bda01ad84 100644
> > --- a/kernel/bpf/bpf_lsm.c
> > +++ b/kernel/bpf/bpf_lsm.c
> > @@ -16,6 +16,8 @@
> >   #include <linux/bpf_local_storage.h>
> >   #include <linux/btf_ids.h>
> >   #include <linux/ima.h>
> > +#include <linux/verification.h>
> > +#include <linux/key.h>
> >
> >   /* For every LSM hook that allows attachment of BPF programs, declare a nop
> >    * function where a BPF program can be attached.
> > @@ -132,6 +134,62 @@ static const struct bpf_func_proto bpf_get_attach_cookie_proto = {
> >       .arg1_type      = ARG_PTR_TO_CTX,
> >   };
> >
> > +#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
> > +BPF_CALL_5(bpf_verify_pkcs7_signature, struct bpf_dynptr_kern *, data_ptr,
> > +        struct bpf_dynptr_kern *, sig_ptr, u32, trusted_keyring_serial,
> > +        unsigned long, lookup_flags, unsigned long, trusted_keyring_id)
> > +{
> > +     key_ref_t trusted_keyring_ref;
> > +     struct key *trusted_keyring;
> > +     int ret;
> > +
> > +     /* Keep in sync with defs in include/linux/key.h. */
> > +     if (lookup_flags > KEY_LOOKUP_PARTIAL)
> > +             return -EINVAL;
>
> iiuc, the KEY_LOOKUP_* is a mask, so you could also combine the two, e.g.
> KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL. I haven't seen you mentioning anything
> specific on why it is not allowed. What's the rationale, if it's intentional
> if should probably be documented?

I think this was a part of the digilim threat model (only allow
limited lookup operations),
but this seems to be conflating the policy into the implementation of
the helper.

Roberto, can this not be implemented in digilim as a BPF LSM check
that attaches to the key_permission LSM hook?

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/lsm_hooks.h#n1158

>
> At minimum I also think the helper description needs to be improved for people
> to understand enough w/o reading through the kernel source, e.g. wrt lookup_flags
> since I haven't seen it in your selftests either ... when does a user need to
> use the given flags.
>
> nit: when both trusted_keyring_serial and trusted_keyring_id are passed to the
> helper, then this should be rejected as invalid argument? (Kind of feels a bit
> like we're cramming two things in one helper.. KP, thoughts? :))

EINVAL when both are passed seems reasonable. The signature (pun?) of the
does seem to get bloated, but I am not sure if it's worth adding two
helpers here.

>
> > +     /* Keep in sync with defs in include/linux/verification.h. */
> > +     if (trusted_keyring_id > (unsigned long)VERIFY_USE_PLATFORM_KEYRING)
> > +             return -EINVAL;
> > +
> > +     if (trusted_keyring_serial) {
> > +             trusted_keyring_ref = lookup_user_key(trusted_keyring_serial,
> > +                                                   lookup_flags,
> > +                                                   KEY_NEED_SEARCH);
> > +             if (IS_ERR(trusted_keyring_ref))
> > +                     return PTR_ERR(trusted_keyring_ref);
> > +
> > +             trusted_keyring = key_ref_to_ptr(trusted_keyring_ref);
> > +             goto verify;
> > +     }
> > +
> > +     trusted_keyring = (struct key *)trusted_keyring_id;
> > +verify:
> > +     ret = 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);
> > +     if (trusted_keyring_serial)
> > +             key_put(trusted_keyring);
> > +
> > +     return ret;
> > +}
> > +
> > +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_ANYTHING,
> > +     .arg4_type      = ARG_ANYTHING,
> > +     .arg5_type      = ARG_ANYTHING,
> > +     .allowed        = bpf_ima_inode_hash_allowed,
> > +};
> > +#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
> > +

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

* RE: [PATCH v6 4/5] bpf: Add bpf_verify_pkcs7_signature() helper
  2022-07-06 23:49     ` KP Singh
@ 2022-07-07 11:00       ` Roberto Sassu
  2022-07-08 12:12         ` Roberto Sassu
  0 siblings, 1 reply; 11+ messages in thread
From: Roberto Sassu @ 2022-07-07 11:00 UTC (permalink / raw)
  To: KP Singh, Daniel Borkmann
  Cc: ast, andrii, john.fastabend, songliubraving, kafai, yhs,
	dhowells, keyrings, bpf, netdev, linux-kselftest,
	linux-security-module, linux-kernel

> From: KP Singh [mailto:kpsingh@kernel.org]
> Sent: Thursday, July 7, 2022 1:49 AM
> On Wed, Jul 6, 2022 at 6:04 PM Daniel Borkmann <daniel@iogearbox.net>
> wrote:
> >
> > On 6/28/22 2:27 PM, Roberto Sassu wrote:
> > > 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).
> > >
> > > The caller should also provide a trusted keyring serial, together with key
> > > lookup-specific flags, to determine which keys can be used for signature
> > > verification. Alternatively, the caller could specify zero as serial value
> > > (not valid, serials must be positive), and provide instead a special
> > > keyring ID.
> > >
> > > Key lookup flags are defined in include/linux/key.h and can be: 1, to
> > > request that special keyrings be created if referred to directly; 2 to
> > > permit partially constructed keys to be found.
> > >
> > > Special IDs are defined in include/linux/verification.h and can be: 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).
> > >
> > > Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> > > Reported-by: kernel test robot <lkp@intel.com> (cast warning)
> >
> > nit: Given this a new feature not a fix to existing code, there is no need to
> >       add the above reported-by from kbuild bot.

Ok.

> > > ---
> > >   include/uapi/linux/bpf.h       | 24 +++++++++++++
> > >   kernel/bpf/bpf_lsm.c           | 63 ++++++++++++++++++++++++++++++++++
> > >   tools/include/uapi/linux/bpf.h | 24 +++++++++++++
> > >   3 files changed, 111 insertions(+)
> > >
> > > diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> > > index e81362891596..b4f5ad863281 100644
> > > --- a/include/uapi/linux/bpf.h
> > > +++ b/include/uapi/linux/bpf.h
> > > @@ -5325,6 +5325,29 @@ union bpf_attr {
> > >    *          **-EACCES** if the SYN cookie is not valid.
> > >    *
> > >    *          **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin.
> > > + *
> > > + * long bpf_verify_pkcs7_signature(struct bpf_dynptr *data_ptr, struct
> bpf_dynptr *sig_ptr, u32 trusted_keyring_serial, unsigned long lookup_flags,
> unsigned long trusted_keyring_id)
> >
> > nit: for the args instead of ulong, just do u64

Ok.

> > > + *   Description
> > > + *           Verify the PKCS#7 signature *sig_ptr* against the supplied
> > > + *           *data_ptr* with keys in a keyring with serial
> > > + *           *trusted_keyring_serial*, searched with *lookup_flags*, if the
> > > + *           parameter value is positive, or alternatively in a keyring with
> > > + *           special ID *trusted_keyring_id* if *trusted_keyring_serial* is
> > > + *           zero.
> > > + *
> > > + *           *lookup_flags* are defined in include/linux/key.h and can be: 1,
> > > + *           to request that special keyrings be created if referred to
> > > + *           directly; 2 to permit partially constructed keys to be found.
> > > + *
> > > + *           Special IDs are defined in include/linux/verification.h and can
> > > + *           be: 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),                     \
> > > @@ -5535,6 +5558,7 @@ union bpf_attr {
> > >       FN(tcp_raw_gen_syncookie_ipv6), \
> > >       FN(tcp_raw_check_syncookie_ipv4),       \
> > >       FN(tcp_raw_check_syncookie_ipv6),       \
> > > +     FN(verify_pkcs7_signature),     \
> >
> > (Needs rebase)
> >
> > >       /* */
> > >
> > >   /* 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 c1351df9f7ee..401bda01ad84 100644
> > > --- a/kernel/bpf/bpf_lsm.c
> > > +++ b/kernel/bpf/bpf_lsm.c
> > > @@ -16,6 +16,8 @@
> > >   #include <linux/bpf_local_storage.h>
> > >   #include <linux/btf_ids.h>
> > >   #include <linux/ima.h>
> > > +#include <linux/verification.h>
> > > +#include <linux/key.h>
> > >
> > >   /* For every LSM hook that allows attachment of BPF programs, declare a
> nop
> > >    * function where a BPF program can be attached.
> > > @@ -132,6 +134,62 @@ static const struct bpf_func_proto
> bpf_get_attach_cookie_proto = {
> > >       .arg1_type      = ARG_PTR_TO_CTX,
> > >   };
> > >
> > > +#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
> > > +BPF_CALL_5(bpf_verify_pkcs7_signature, struct bpf_dynptr_kern *,
> data_ptr,
> > > +        struct bpf_dynptr_kern *, sig_ptr, u32, trusted_keyring_serial,
> > > +        unsigned long, lookup_flags, unsigned long, trusted_keyring_id)
> > > +{
> > > +     key_ref_t trusted_keyring_ref;
> > > +     struct key *trusted_keyring;
> > > +     int ret;
> > > +
> > > +     /* Keep in sync with defs in include/linux/key.h. */
> > > +     if (lookup_flags > KEY_LOOKUP_PARTIAL)
> > > +             return -EINVAL;
> >
> > iiuc, the KEY_LOOKUP_* is a mask, so you could also combine the two, e.g.
> > KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL. I haven't seen you
> mentioning anything
> > specific on why it is not allowed. What's the rationale, if it's intentional
> > if should probably be documented?

It was a mistake. Will fix it.

> I think this was a part of the digilim threat model (only allow
> limited lookup operations),
> but this seems to be conflating the policy into the implementation of
> the helper.

Uhm, yes, but these flags should not affect that requirement.

As long as I can select the primary or secondary keyring reliably
with the pre-determined IDs, that should be fine.

> Roberto, can this not be implemented in digilim as a BPF LSM check
> that attaches to the key_permission LSM hook?
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/li
> nux/lsm_hooks.h#n1158

The pre-determined IDs handled by verify_pkcs7_signature() are
more effective in selecting the proper key.

> > At minimum I also think the helper description needs to be improved for
> people
> > to understand enough w/o reading through the kernel source, e.g. wrt
> lookup_flags
> > since I haven't seen it in your selftests either ... when does a user need to
> > use the given flags.
> >
> > nit: when both trusted_keyring_serial and trusted_keyring_id are passed to the
> > helper, then this should be rejected as invalid argument? (Kind of feels a bit
> > like we're cramming two things in one helper.. KP, thoughts? :))
> 
> EINVAL when both are passed seems reasonable. The signature (pun?) of the
> does seem to get bloated, but I am not sure if it's worth adding two
> helpers here.

Ok for EINVAL. Should I change the trusted_keyring_id type to signed,
and use -1 when it should not be specified?

Thanks

Roberto

> > > +     /* Keep in sync with defs in include/linux/verification.h. */
> > > +     if (trusted_keyring_id > (unsigned
> long)VERIFY_USE_PLATFORM_KEYRING)
> > > +             return -EINVAL;
> > > +
> > > +     if (trusted_keyring_serial) {
> > > +             trusted_keyring_ref = lookup_user_key(trusted_keyring_serial,
> > > +                                                   lookup_flags,
> > > +                                                   KEY_NEED_SEARCH);
> > > +             if (IS_ERR(trusted_keyring_ref))
> > > +                     return PTR_ERR(trusted_keyring_ref);
> > > +
> > > +             trusted_keyring = key_ref_to_ptr(trusted_keyring_ref);
> > > +             goto verify;
> > > +     }
> > > +
> > > +     trusted_keyring = (struct key *)trusted_keyring_id;
> > > +verify:
> > > +     ret = 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);
> > > +     if (trusted_keyring_serial)
> > > +             key_put(trusted_keyring);
> > > +
> > > +     return ret;
> > > +}
> > > +
> > > +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_ANYTHING,
> > > +     .arg4_type      = ARG_ANYTHING,
> > > +     .arg5_type      = ARG_ANYTHING,
> > > +     .allowed        = bpf_ima_inode_hash_allowed,
> > > +};
> > > +#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
> > > +

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

* RE: [PATCH v6 4/5] bpf: Add bpf_verify_pkcs7_signature() helper
  2022-07-07 11:00       ` Roberto Sassu
@ 2022-07-08 12:12         ` Roberto Sassu
  0 siblings, 0 replies; 11+ messages in thread
From: Roberto Sassu @ 2022-07-08 12:12 UTC (permalink / raw)
  To: dhowells
  Cc: ast, andrii, john.fastabend, songliubraving, kafai, yhs,
	keyrings, bpf, netdev, linux-kselftest, linux-security-module,
	linux-kernel, KP Singh, Daniel Borkmann

> From: Roberto Sassu [mailto:roberto.sassu@huawei.com]
> Sent: Thursday, July 7, 2022 1:01 PM
> > From: KP Singh [mailto:kpsingh@kernel.org]
> > Sent: Thursday, July 7, 2022 1:49 AM
> > On Wed, Jul 6, 2022 at 6:04 PM Daniel Borkmann <daniel@iogearbox.net>
> > wrote:

[...]

> > > nit: when both trusted_keyring_serial and trusted_keyring_id are passed to
> the
> > > helper, then this should be rejected as invalid argument? (Kind of feels a bit
> > > like we're cramming two things in one helper.. KP, thoughts? :))
> >
> > EINVAL when both are passed seems reasonable. The signature (pun?) of the
> > does seem to get bloated, but I am not sure if it's worth adding two
> > helpers here.
> 
> Ok for EINVAL. Should I change the trusted_keyring_id type to signed,
> and use -1 when it should not be specified?

I still have the impression that a helper for lookup_user_key() is the
preferred solution, despite the access control concern.

David, may ask if this is the correct way to use the key subsystem
API when permission check is deferred? key_permission() is currently
not defined outside the key subsystem.

The first three functions will be exposed by the kernel to eBPF programs
and can be called at any time. bpf_<key_helper> is a generic helper
dealing with a key.

BPF_CALL_2(bpf_lookup_user_key, u32, serial, unsigned long, flags)
{
...
        key_ref = lookup_user_key(serial, flags, KEY_DEFER_PERM_CHECK);
...
        return (unsigned long)key_ref_to_ptr(key_ref);
}

BPF_CALL_X(bpf_<key_helper>, struct key, *key, ...)
{
        ret = key_read_state(key);
...
        ret = key_validate(key);
...
        ret = key_permission(key_ref, <key helper-specific permission>);
...
}

BPF_CALL_1(bpf_key_put, struct key *, key)
{
        key_put(key);
        return 0;
}

An eBPF program would do for example:

SEC("lsm.s/bpf")
int BPF_PROG(bpf, int cmd, union bpf_attr *attr, unsigned int size)
{
        struct key *key;
...
        key = bpf_lookup_user_key(serial, flags);
...
        ret = bpf_key_helper(key, ...);
...
        key_put(key);
}

Thanks

Roberto

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

end of thread, other threads:[~2022-07-08 12:12 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-28 12:27 [PATCH v6 0/5] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
2022-06-28 12:27 ` [PATCH v6 1/5] bpf: Export bpf_dynptr_get_size() Roberto Sassu
2022-06-28 12:27 ` [PATCH v6 2/5] KEYS: Move KEY_LOOKUP_ to include/linux/key.h Roberto Sassu
2022-06-28 12:27 ` [PATCH v6 3/5] scripts: Handle unsigned type prefix in bpf_doc.py Roberto Sassu
2022-06-28 12:27 ` [PATCH v6 4/5] bpf: Add bpf_verify_pkcs7_signature() helper Roberto Sassu
2022-07-06 16:03   ` Daniel Borkmann
2022-07-06 23:49     ` KP Singh
2022-07-07 11:00       ` Roberto Sassu
2022-07-08 12:12         ` Roberto Sassu
2022-06-28 12:27 ` [PATCH v6 5/5] selftests/bpf: Add test for " Roberto Sassu
2022-06-28 12:32   ` Roberto Sassu

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).