Message ID | 20220809134603.1769279-7-roberto.sassu@huawei.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | bpf: Add kfuncs for PKCS#7 signature verification | expand |
On 8/9/22 3:45 PM, Roberto Sassu wrote: > Add the bpf_lookup_user_key(), bpf_lookup_system_key() and bpf_key_put() > kfuncs, to respectively search a key with a given serial and flags, obtain > a key from a pre-determined ID defined in include/linux/verification.h, and > cleanup. > > Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com> > --- > include/linux/bpf.h | 6 ++ > kernel/trace/bpf_trace.c | 151 +++++++++++++++++++++++++++++++++++++++ > 2 files changed, 157 insertions(+) > > diff --git a/include/linux/bpf.h b/include/linux/bpf.h > index 0d56c23cc504..564b9e0b8c16 100644 > --- a/include/linux/bpf.h > +++ b/include/linux/bpf.h > @@ -2572,4 +2572,10 @@ static inline void bpf_cgroup_atype_get(u32 attach_btf_id, int cgroup_atype) {} > static inline void bpf_cgroup_atype_put(int cgroup_atype) {} > #endif /* CONFIG_BPF_LSM */ > > +#ifdef CONFIG_KEYS > +struct bpf_key { > + struct key *key; > + bool valid_ptr; > +}; > +#endif /* CONFIG_KEYS */ > #endif /* _LINUX_BPF_H */ > diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c > index 68e5cdd24cef..33ca4cfe6e26 100644 > --- a/kernel/trace/bpf_trace.c > +++ b/kernel/trace/bpf_trace.c > @@ -20,6 +20,7 @@ > #include <linux/fprobe.h> > #include <linux/bsearch.h> > #include <linux/sort.h> > +#include <linux/key.h> > > #include <net/bpf_sk_storage.h> > > @@ -1181,6 +1182,156 @@ static const struct bpf_func_proto bpf_get_func_arg_cnt_proto = { > .arg1_type = ARG_PTR_TO_CTX, > }; > > +#ifdef CONFIG_KEYS > +__diag_push(); > +__diag_ignore_all("-Wmissing-prototypes", > + "kfuncs which will be used in BPF programs"); > + > +/** > + * bpf_lookup_user_key - lookup a key by its serial > + * @serial: key serial > + * @flags: lookup-specific flags > + * > + * Search a key with a given *serial* and the provided *flags*. The > + * returned key, if found, has the reference count incremented by > + * one, and is stored in a bpf_key structure, returned to the caller. > + * The bpf_key structure must be passed to bpf_key_put() when done > + * with it, so that the key reference count is decremented and the > + * bpf_key structure is freed. > + * > + * Permission checks are deferred to the time the key is used by > + * one of the available key-specific kfuncs. > + * > + * Set *flags* with 1, to attempt creating a requested special > + * keyring (e.g. session keyring), if it doesn't yet exist. Set > + * *flags* with 2 to lookup a key without waiting for the key > + * construction, and to retrieve uninstantiated keys (keys without > + * data attached to them). > + * > + * Return: a bpf_key pointer with a valid key pointer if the key is found, a > + * NULL pointer otherwise. > + */ > +noinline __weak struct bpf_key *bpf_lookup_user_key(u32 serial, u64 flags) Why the need for noinline and the __weak here and below? (If indeed needed this should probably be explained in the commit desc.) > +{ > + key_ref_t key_ref; > + struct bpf_key *bkey; > + > + /* Keep in sync with include/linux/key.h. */ > + if (flags > (KEY_LOOKUP_PARTIAL << 1) - 1) Can't we just simplify and test flags & ~(KEY_LOOKUP_CREATE|KEY_LOOKUP_PARTIAL)? > + return NULL; > + > + /* Permission check is deferred until actual kfunc using the key. */ > + key_ref = lookup_user_key(serial, flags, KEY_DEFER_PERM_CHECK); > + if (IS_ERR(key_ref)) > + return NULL; > + > + bkey = kmalloc(sizeof(*bkey), GFP_KERNEL); > + if (!bkey) { > + key_put(key_ref_to_ptr(key_ref)); > + return bkey; nit: just return NULL probably cleaner > + } > + > + bkey->key = key_ref_to_ptr(key_ref); > + bkey->valid_ptr = true; nit: I'd probably rename s/valid_ptr/has_ref/. > + return bkey; > +} > + > +/** > + * bpf_lookup_system_key - lookup a key by a system-defined ID > + * @id: key ID > + * > + * Obtain a bpf_key structure with a key pointer set to the passed key ID. > + * The key pointer is marked as invalid, to prevent bpf_key_put() from > + * attempting to decrement the key reference count on that pointer. The key > + * pointer set in such way is currently understood only by > + * verify_pkcs7_signature(). > + * > + * Set *id* to one of the 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: a bpf_key pointer with an invalid key pointer set from the > + * pre-determined ID on success, a NULL pointer otherwise > + */ > +noinline __weak struct bpf_key *bpf_lookup_system_key(u64 id) > +{ > + struct bpf_key *bkey; > + > + /* Keep in sync with defs in include/linux/verification.h. */ > + if (id > (unsigned long)VERIFY_USE_PLATFORM_KEYRING) > + return NULL; > + > + bkey = kmalloc(sizeof(*bkey), GFP_KERNEL); nit: Can't this be GFP_ATOMIC? Then bpf_lookup_system_key doesn't need KF_SLEEPABLE attribute, fwiw. Overall, the bpf_lookup_{system,user}_key() look reasonable. > + if (!bkey) > + return bkey; > + > + bkey->key = (struct key *)(unsigned long)id; > + bkey->valid_ptr = false; > + > + return bkey; > +} > + > +/** > + * bpf_key_put - decrement key reference count if key is valid and free bpf_key > + * @bkey: bpf_key structure > + * > + * Decrement the reference count of the key inside *bkey*, if the pointer > + * is valid, and free *bkey*. > + */ > +noinline __weak void bpf_key_put(struct bpf_key *bkey) > +{ > + if (bkey->valid_ptr) > + key_put(bkey->key); > + > + kfree(bkey); > +} > + > +__diag_pop(); > + > +BTF_SET8_START(key_kfunc_set) > +BTF_ID_FLAGS(func, bpf_lookup_user_key, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE) > +BTF_ID_FLAGS(func, bpf_lookup_system_key, > + KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE) > +BTF_ID_FLAGS(func, bpf_key_put, KF_RELEASE) > +BTF_SET8_END(key_kfunc_set) > + > +static const struct btf_kfunc_id_set bpf_key_kfunc_set = { > + .owner = THIS_MODULE, > + .set = &key_kfunc_set, > +}; > +#endif /* CONFIG_KEYS */ > + > +const struct btf_kfunc_id_set *kfunc_sets[] = { > +#ifdef CONFIG_KEYS > + &bpf_key_kfunc_set, > +#endif /* CONFIG_KEYS */ > +};
> From: Daniel Borkmann [mailto:daniel@iogearbox.net] > Sent: Wednesday, August 10, 2022 12:53 AM > On 8/9/22 3:45 PM, Roberto Sassu wrote: > > Add the bpf_lookup_user_key(), bpf_lookup_system_key() and bpf_key_put() > > kfuncs, to respectively search a key with a given serial and flags, obtain > > a key from a pre-determined ID defined in include/linux/verification.h, and > > cleanup. > > > > Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com> > > --- > > include/linux/bpf.h | 6 ++ > > kernel/trace/bpf_trace.c | 151 > +++++++++++++++++++++++++++++++++++++++ > > 2 files changed, 157 insertions(+) > > > > diff --git a/include/linux/bpf.h b/include/linux/bpf.h > > index 0d56c23cc504..564b9e0b8c16 100644 > > --- a/include/linux/bpf.h > > +++ b/include/linux/bpf.h > > @@ -2572,4 +2572,10 @@ static inline void bpf_cgroup_atype_get(u32 > attach_btf_id, int cgroup_atype) {} > > static inline void bpf_cgroup_atype_put(int cgroup_atype) {} > > #endif /* CONFIG_BPF_LSM */ > > > > +#ifdef CONFIG_KEYS > > +struct bpf_key { > > + struct key *key; > > + bool valid_ptr; > > +}; > > +#endif /* CONFIG_KEYS */ > > #endif /* _LINUX_BPF_H */ > > diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c > > index 68e5cdd24cef..33ca4cfe6e26 100644 > > --- a/kernel/trace/bpf_trace.c > > +++ b/kernel/trace/bpf_trace.c > > @@ -20,6 +20,7 @@ > > #include <linux/fprobe.h> > > #include <linux/bsearch.h> > > #include <linux/sort.h> > > +#include <linux/key.h> > > > > #include <net/bpf_sk_storage.h> > > > > @@ -1181,6 +1182,156 @@ static const struct bpf_func_proto > bpf_get_func_arg_cnt_proto = { > > .arg1_type = ARG_PTR_TO_CTX, > > }; > > > > +#ifdef CONFIG_KEYS > > +__diag_push(); > > +__diag_ignore_all("-Wmissing-prototypes", > > + "kfuncs which will be used in BPF programs"); > > + > > +/** > > + * bpf_lookup_user_key - lookup a key by its serial > > + * @serial: key serial > > + * @flags: lookup-specific flags > > + * > > + * Search a key with a given *serial* and the provided *flags*. The > > + * returned key, if found, has the reference count incremented by > > + * one, and is stored in a bpf_key structure, returned to the caller. > > + * The bpf_key structure must be passed to bpf_key_put() when done > > + * with it, so that the key reference count is decremented and the > > + * bpf_key structure is freed. > > + * > > + * Permission checks are deferred to the time the key is used by > > + * one of the available key-specific kfuncs. > > + * > > + * Set *flags* with 1, to attempt creating a requested special > > + * keyring (e.g. session keyring), if it doesn't yet exist. Set > > + * *flags* with 2 to lookup a key without waiting for the key > > + * construction, and to retrieve uninstantiated keys (keys without > > + * data attached to them). > > + * > > + * Return: a bpf_key pointer with a valid key pointer if the key is found, a > > + * NULL pointer otherwise. > > + */ > > +noinline __weak struct bpf_key *bpf_lookup_user_key(u32 serial, u64 flags) > > Why the need for noinline and the __weak here and below? (If indeed needed > this > should probably be explained in the commit desc.) Oh, I took from v3 of KP's patch set. It is gone in v5. Will do the same as well. > > +{ > > + key_ref_t key_ref; > > + struct bpf_key *bkey; > > + > > + /* Keep in sync with include/linux/key.h. */ > > + if (flags > (KEY_LOOKUP_PARTIAL << 1) - 1) > > Can't we just simplify and test flags & > ~(KEY_LOOKUP_CREATE|KEY_LOOKUP_PARTIAL)? I thought as if we have many flags. > > + return NULL; > > + > > + /* Permission check is deferred until actual kfunc using the key. */ > > + key_ref = lookup_user_key(serial, flags, KEY_DEFER_PERM_CHECK); > > + if (IS_ERR(key_ref)) > > + return NULL; > > + > > + bkey = kmalloc(sizeof(*bkey), GFP_KERNEL); > > + if (!bkey) { > > + key_put(key_ref_to_ptr(key_ref)); > > + return bkey; > > nit: just return NULL probably cleaner Ok. > > + } > > + > > + bkey->key = key_ref_to_ptr(key_ref); > > + bkey->valid_ptr = true; > > nit: I'd probably rename s/valid_ptr/has_ref/. Ok. > > + return bkey; > > +} > > + > > +/** > > + * bpf_lookup_system_key - lookup a key by a system-defined ID > > + * @id: key ID > > + * > > + * Obtain a bpf_key structure with a key pointer set to the passed key ID. > > + * The key pointer is marked as invalid, to prevent bpf_key_put() from > > + * attempting to decrement the key reference count on that pointer. The key > > + * pointer set in such way is currently understood only by > > + * verify_pkcs7_signature(). > > + * > > + * Set *id* to one of the 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: a bpf_key pointer with an invalid key pointer set from the > > + * pre-determined ID on success, a NULL pointer otherwise > > + */ > > +noinline __weak struct bpf_key *bpf_lookup_system_key(u64 id) > > +{ > > + struct bpf_key *bkey; > > + > > + /* Keep in sync with defs in include/linux/verification.h. */ > > + if (id > (unsigned long)VERIFY_USE_PLATFORM_KEYRING) > > + return NULL; > > + > > + bkey = kmalloc(sizeof(*bkey), GFP_KERNEL); > > nit: Can't this be GFP_ATOMIC? Then bpf_lookup_system_key doesn't need > KF_SLEEPABLE > attribute, fwiw. Overall, the bpf_lookup_{system,user}_key() look reasonable. Ok, will do. Thanks Roberto > > + if (!bkey) > > + return bkey; > > + > > + bkey->key = (struct key *)(unsigned long)id; > > + bkey->valid_ptr = false; > > + > > + return bkey; > > +} > > + > > +/** > > + * bpf_key_put - decrement key reference count if key is valid and free > bpf_key > > + * @bkey: bpf_key structure > > + * > > + * Decrement the reference count of the key inside *bkey*, if the pointer > > + * is valid, and free *bkey*. > > + */ > > +noinline __weak void bpf_key_put(struct bpf_key *bkey) > > +{ > > + if (bkey->valid_ptr) > > + key_put(bkey->key); > > + > > + kfree(bkey); > > +} > > + > > +__diag_pop(); > > + > > +BTF_SET8_START(key_kfunc_set) > > +BTF_ID_FLAGS(func, bpf_lookup_user_key, KF_ACQUIRE | KF_RET_NULL | > KF_SLEEPABLE) > > +BTF_ID_FLAGS(func, bpf_lookup_system_key, > > + KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE) > > +BTF_ID_FLAGS(func, bpf_key_put, KF_RELEASE) > > +BTF_SET8_END(key_kfunc_set) > > + > > +static const struct btf_kfunc_id_set bpf_key_kfunc_set = { > > + .owner = THIS_MODULE, > > + .set = &key_kfunc_set, > > +}; > > +#endif /* CONFIG_KEYS */ > > + > > +const struct btf_kfunc_id_set *kfunc_sets[] = { > > +#ifdef CONFIG_KEYS > > + &bpf_key_kfunc_set, > > +#endif /* CONFIG_KEYS */ > > +};
[...] >>> +noinline __weak struct bpf_key *bpf_lookup_user_key(u32 serial, u64 flags) >> >> Why the need for noinline and the __weak here and below? (If indeed needed >> this >> should probably be explained in the commit desc.) > > Oh, I took from v3 of KP's patch set. It is gone in v5. Will do > the same as well. > >>> +{ >>> + key_ref_t key_ref; >>> + struct bpf_key *bkey; >>> + >>> + /* Keep in sync with include/linux/key.h. */ >>> + if (flags > (KEY_LOOKUP_PARTIAL << 1) - 1) >> >> Can't we just simplify and test flags & >> ~(KEY_LOOKUP_CREATE|KEY_LOOKUP_PARTIAL)? > > I thought as if we have many flags. I'd keep it simple for now, and if the actual need comes this can still be changed. >>> + return NULL; >>> + >>> + /* Permission check is deferred until actual kfunc using the key. */ >>> + key_ref = lookup_user_key(serial, flags, KEY_DEFER_PERM_CHECK); >>> + if (IS_ERR(key_ref)) >>> + return NULL; >>> + >>> + bkey = kmalloc(sizeof(*bkey), GFP_KERNEL); >>> + if (!bkey) { >>> + key_put(key_ref_to_ptr(key_ref)); >>> + return bkey; >> >> nit: just return NULL probably cleaner > > Ok.
diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 0d56c23cc504..564b9e0b8c16 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -2572,4 +2572,10 @@ static inline void bpf_cgroup_atype_get(u32 attach_btf_id, int cgroup_atype) {} static inline void bpf_cgroup_atype_put(int cgroup_atype) {} #endif /* CONFIG_BPF_LSM */ +#ifdef CONFIG_KEYS +struct bpf_key { + struct key *key; + bool valid_ptr; +}; +#endif /* CONFIG_KEYS */ #endif /* _LINUX_BPF_H */ diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 68e5cdd24cef..33ca4cfe6e26 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -20,6 +20,7 @@ #include <linux/fprobe.h> #include <linux/bsearch.h> #include <linux/sort.h> +#include <linux/key.h> #include <net/bpf_sk_storage.h> @@ -1181,6 +1182,156 @@ static const struct bpf_func_proto bpf_get_func_arg_cnt_proto = { .arg1_type = ARG_PTR_TO_CTX, }; +#ifdef CONFIG_KEYS +__diag_push(); +__diag_ignore_all("-Wmissing-prototypes", + "kfuncs which will be used in BPF programs"); + +/** + * bpf_lookup_user_key - lookup a key by its serial + * @serial: key serial + * @flags: lookup-specific flags + * + * Search a key with a given *serial* and the provided *flags*. The + * returned key, if found, has the reference count incremented by + * one, and is stored in a bpf_key structure, returned to the caller. + * The bpf_key structure must be passed to bpf_key_put() when done + * with it, so that the key reference count is decremented and the + * bpf_key structure is freed. + * + * Permission checks are deferred to the time the key is used by + * one of the available key-specific kfuncs. + * + * Set *flags* with 1, to attempt creating a requested special + * keyring (e.g. session keyring), if it doesn't yet exist. Set + * *flags* with 2 to lookup a key without waiting for the key + * construction, and to retrieve uninstantiated keys (keys without + * data attached to them). + * + * Return: a bpf_key pointer with a valid key pointer if the key is found, a + * NULL pointer otherwise. + */ +noinline __weak struct bpf_key *bpf_lookup_user_key(u32 serial, u64 flags) +{ + key_ref_t key_ref; + struct bpf_key *bkey; + + /* Keep in sync with include/linux/key.h. */ + if (flags > (KEY_LOOKUP_PARTIAL << 1) - 1) + return NULL; + + /* Permission check is deferred until actual kfunc using the key. */ + key_ref = lookup_user_key(serial, flags, KEY_DEFER_PERM_CHECK); + if (IS_ERR(key_ref)) + return NULL; + + bkey = kmalloc(sizeof(*bkey), GFP_KERNEL); + if (!bkey) { + key_put(key_ref_to_ptr(key_ref)); + return bkey; + } + + bkey->key = key_ref_to_ptr(key_ref); + bkey->valid_ptr = true; + + return bkey; +} + +/** + * bpf_lookup_system_key - lookup a key by a system-defined ID + * @id: key ID + * + * Obtain a bpf_key structure with a key pointer set to the passed key ID. + * The key pointer is marked as invalid, to prevent bpf_key_put() from + * attempting to decrement the key reference count on that pointer. The key + * pointer set in such way is currently understood only by + * verify_pkcs7_signature(). + * + * Set *id* to one of the 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: a bpf_key pointer with an invalid key pointer set from the + * pre-determined ID on success, a NULL pointer otherwise + */ +noinline __weak struct bpf_key *bpf_lookup_system_key(u64 id) +{ + struct bpf_key *bkey; + + /* Keep in sync with defs in include/linux/verification.h. */ + if (id > (unsigned long)VERIFY_USE_PLATFORM_KEYRING) + return NULL; + + bkey = kmalloc(sizeof(*bkey), GFP_KERNEL); + if (!bkey) + return bkey; + + bkey->key = (struct key *)(unsigned long)id; + bkey->valid_ptr = false; + + return bkey; +} + +/** + * bpf_key_put - decrement key reference count if key is valid and free bpf_key + * @bkey: bpf_key structure + * + * Decrement the reference count of the key inside *bkey*, if the pointer + * is valid, and free *bkey*. + */ +noinline __weak void bpf_key_put(struct bpf_key *bkey) +{ + if (bkey->valid_ptr) + key_put(bkey->key); + + kfree(bkey); +} + +__diag_pop(); + +BTF_SET8_START(key_kfunc_set) +BTF_ID_FLAGS(func, bpf_lookup_user_key, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE) +BTF_ID_FLAGS(func, bpf_lookup_system_key, + KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE) +BTF_ID_FLAGS(func, bpf_key_put, KF_RELEASE) +BTF_SET8_END(key_kfunc_set) + +static const struct btf_kfunc_id_set bpf_key_kfunc_set = { + .owner = THIS_MODULE, + .set = &key_kfunc_set, +}; +#endif /* CONFIG_KEYS */ + +const struct btf_kfunc_id_set *kfunc_sets[] = { +#ifdef CONFIG_KEYS + &bpf_key_kfunc_set, +#endif /* CONFIG_KEYS */ +}; + +static int __init bpf_kfuncs_init(void) +{ + int ret, i; + + for (i = 0; i < ARRAY_SIZE(kfunc_sets); i++) { + ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, + kfunc_sets[i]); + if (!ret) + continue; + + ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM, + kfunc_sets[i]); + if (ret < 0) + return ret; + } + + return 0; +} + +late_initcall(bpf_kfuncs_init); + static const struct bpf_func_proto * bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) {
Add the bpf_lookup_user_key(), bpf_lookup_system_key() and bpf_key_put() kfuncs, to respectively search a key with a given serial and flags, obtain a key from a pre-determined ID defined in include/linux/verification.h, and cleanup. Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com> --- include/linux/bpf.h | 6 ++ kernel/trace/bpf_trace.c | 151 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+)