Provide three keyctl functions that permit userspace to make use of the new key type ops for accessing and driving asymmetric kpp keys. (*) Query an asymmetric kpp key. long keyctl(KEYCTL_KPP_QUERY, key_serial_t key, struct keyctl_kpp_query *res); Get information about an asymmetric kpp key. The information is returned in the keyctl_kpp_query struct: __u32 supported_ops; A bit mask of flags indicating which ops are supported. This is constructed from a bitwise-OR of: KEYCTL_SUPPORTS_{GEN_PUBKEY, COMPUTE_SS} __u32 max_size; The maximum size in bytes of the key. __spare must be set to 0. This is intended for future use to hand over one or more passphrases needed to unlock a key. If successful, 0 is returned. If the key is not an asymmetric kpp key, EOPNOTSUPP is returned. (*) Generate the public key or compute the shared secret using an asymmetric kpp key. long keyctl(KEYCTL_KPP_GEN_PUBKEY, const struct keyctl_kpp_params *params, void *out); long keyctl(KEYCTL_KPP_GEN_PUBKEY, const struct keyctl_kpp_params *params, const void *in, void *out); The parameter block pointed by params contains a number of integer values: __s32 key_id; __u32 in_len; __u32 out_len; For a given operation, the in and out buffers are used as follows: Operation ID in,in_len out,out_len ======================= =================== ======================== KEYCTL_KPP_GEN_PUBKEY - Corresponding public key KEYCTL_KPP_COMPUTE_SS Pair's public key Shared Secret The __spare space in the parameter block must be set to 0. This is intended, amongst other things, to allow the passing of passphrases required to unlock a key. If successful, the public key generation and the shared secret computation will return the amount of data written into the output buffer. Signed-off-by: Tudor Ambarus <tudor.ambarus@xxxxxxxxxxxxx> --- Documentation/security/keys/core.rst | 59 ++++++++++ include/uapi/linux/keyctl.h | 16 +++ security/keys/Makefile | 1 + security/keys/compat.c | 10 ++ security/keys/internal.h | 28 +++++ security/keys/keyctl.c | 13 +++ security/keys/keyctl_kpp.c | 205 +++++++++++++++++++++++++++++++++++ 7 files changed, 332 insertions(+) create mode 100644 security/keys/keyctl_kpp.c diff --git a/Documentation/security/keys/core.rst b/Documentation/security/keys/core.rst index 9b69a1f..31b9501 100644 --- a/Documentation/security/keys/core.rst +++ b/Documentation/security/keys/core.rst @@ -994,6 +994,65 @@ The keyctl syscall functions are: If successful, encrypt, decrypt and sign all return the amount of data written into the output buffer. Verification returns 0 on success. + * Query an asymmetric kpp key:: + + long keyctl(KEYCTL_KPP_QUERY, + key_serial_t key, struct keyctl_kpp_query *res); + + Get information about an asymmetric kpp key. The information is + returned in the keyctl_kpp_query struct:: + + struct keyctl_kpp_query { + __u32 supported_ops; /* Which ops are supported */ + __u32 max_size; /* Maximum size of the output buffer in bytes */ + __u32 __spare[10]; + }; + + ``__u32 supported_ops;`` is a bit mask of flags indicating which ops are + supported. This is constructed from a bitwise-OR of:: + + KEYCTL_SUPPORTS_{GEN_PUBKEY, COMPUTE_SS} + + ``__spare`` must be set to 0. This is intended for future use to hand + over one or more passphrases needed to unlock a key. + + If successful, 0 is returned. If the key is not an asymmetric kpp key, + EOPNOTSUPP is returned. + + * Generate the public key or compute the shared secret using an asymmetric + kpp key:: + + long keyctl(KEYCTL_KPP_GEN_PUBKEY, + const struct keyctl_kpp_params *params, + void *out); + + long keyctl(KEYCTL_KPP_GEN_PUBKEY, + const struct keyctl_kpp_params *params, + const void *in, void *out); + + The parameter block pointed by params contains a number of integer + values:: + __s32 key_id; + __u32 in_len; + __u32 out_len; + + ``key_id`` is the key tp be used and ``in_len`` and ``out_len`` are the + lengths in bytes of the input and output buffers. + + For a given operation, the in and out buffers are used as follows:: + + Operation ID in,in_len out,out_len + ======================= =================== ======================== + KEYCTL_KPP_GEN_PUBKEY - Corresponding public key + KEYCTL_KPP_COMPUTE_SS Pair's public key Shared Secret + + The __spare space in the parameter block must be set to 0. This is + intended, amongst other things, to allow the passing of passphrases + required to unlock a key. + + If successful, the public key generation and the shared secret computation + will return the amount of data written into the output buffer. + Kernel Services =============== diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h index 98b79f8..dcee74c 100644 --- a/include/uapi/linux/keyctl.h +++ b/include/uapi/linux/keyctl.h @@ -67,6 +67,9 @@ #define KEYCTL_PKEY_DECRYPT 26 /* Decrypt a blob using a public key */ #define KEYCTL_PKEY_SIGN 27 /* Create a public key signature */ #define KEYCTL_PKEY_VERIFY 28 /* Verify a public key signature */ +#define KEYCTL_KPP_QUERY 30 /* Query KPP parameters */ +#define KEYCTL_KPP_GEN_PUBKEY 31 /* Generate public key */ +#define KEYCTL_KPP_COMPUTE_SS 32 /* Compute shared secret */ /* keyctl structures */ struct keyctl_dh_params { @@ -110,4 +113,17 @@ struct keyctl_pkey_params { #define KEYCTL_SUPPORTS_GEN_PUBKEY 0x0f #define KEYCTL_SUPPORTS_COMPUTE_SS 0x10 +struct keyctl_kpp_query { + __u32 supported_ops; /* Which ops are supported */ + __u32 max_size; /* Maximum size of the output buffer in bytes */ + __u32 __spare[10]; +}; + +struct keyctl_kpp_params { + __s32 key_id; /* Serial no. of private key to use */ + __u32 in_len; /* Input data size */ + __u32 out_len; /* Output buffer size */ + __u32 __spare[7]; +}; + #endif /* _LINUX_KEYCTL_H */ diff --git a/security/keys/Makefile b/security/keys/Makefile index 9cef540..eb05d3c 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_SYSCTL) += sysctl.o obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += keyctl_pkey.o +obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += keyctl_kpp.o # # Key types diff --git a/security/keys/compat.c b/security/keys/compat.c index 9482df6..0909715 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -159,6 +159,16 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option, return keyctl_pkey_verify(compat_ptr(arg2), compat_ptr(arg3), compat_ptr(arg4), compat_ptr(arg5)); + case KEYCTL_KPP_QUERY: + return keyctl_kpp_query(arg2, compat_ptr(arg5)); + + case KEYCTL_KPP_GEN_PUBKEY: + return keyctl_kpp_gen_pubkey(compat_ptr(arg2), compat_ptr(arg5); + + case KEYCTL_KPP_COMPUTE_SS: + return keyctl_kpp_compute_ss(compat_ptr(arg2), compat_ptr(arg4), + compat_ptr(arg5)); + default: return -EOPNOTSUPP; } diff --git a/security/keys/internal.h b/security/keys/internal.h index 74cb0ff..bacfda5 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -311,6 +311,14 @@ extern long keyctl_pkey_e_d_s(int, const struct keyctl_pkey_params __user *, const char __user *, const void __user *, void __user *); + +extern long keyctl_kpp_query(key_serial_t, struct keyctl_kpp_query __user *); + +extern long keyctl_kpp_gen_pubkey(const struct keyctl_kpp_params __user *, + void __user *); + +extern long keyctl_kpp_compute_ss(const struct keyctl_kpp_params __user *, + const void __user *, void __user *); #else static inline long keyctl_pkey_query(key_serial_t id, const char __user *_info, @@ -335,6 +343,26 @@ static inline long keyctl_pkey_e_d_s(int op, { return -EOPNOTSUPP; } + +static inline long keyctl_kpp_query(key_serial_t id, + struct keyctl_kpp_query __user *_res); +{ + return -EOPNOTSUPP; +} + +static inline long keyctl_kpp_gen_pubkey( + const struct keyctl_kpp_params __user *params, + void __user *_out); +{ + return -EOPNOTSUPP; +} + +static inline long keyctl_kpp_compute_ss( + const struct keyctl_kpp_params __user *params, + const void __user *in, void __user *_out); +{ + return -EOPNOTSUPP; +} #endif /* diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 614b147bc..a9a47e6 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1775,6 +1775,19 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, (const void __user *)arg4, (const void __user *)arg5); + case KEYCTL_KPP_QUERY: + return keyctl_kpp_query((key_serial_t)arg2, + (struct keyctl_kpp_query *)arg5); + case KEYCTL_KPP_GEN_PUBKEY: + return keyctl_kpp_gen_pubkey( + (const struct keyctl_kpp_params __user *)arg2, + (void __user *)arg5); + case KEYCTL_KPP_COMPUTE_SS: + return keyctl_kpp_compute_ss( + (const struct keyctl_kpp_params __user *)arg2, + (const void __user *)arg4, + (void __user *)arg5); + default: return -EOPNOTSUPP; } diff --git a/security/keys/keyctl_kpp.c b/security/keys/keyctl_kpp.c new file mode 100644 index 0000000..8b4f77a --- /dev/null +++ b/security/keys/keyctl_kpp.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/err.h> +#include <linux/key.h> +#include <linux/keyctl.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include "internal.h" + +enum kernel_kpp_operation { + kernel_kpp_gen_pubkey, + kernel_kpp_compute_ss, +}; + +static void keyctl_kpp_params_free(struct kernel_kpp_params *params) +{ + key_put(params->key); +} + +/* + * Interpret parameters. Callers must always call the free function + * on params, even if an error is returned. + */ +static int keyctl_kpp_params_get(key_serial_t id, + struct kernel_kpp_params *params) +{ + key_ref_t key_ref; + + key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH); + if (IS_ERR(key_ref)) + return PTR_ERR(key_ref); + params->key = key_ref_to_ptr(key_ref); + + if (!params->key->type->asym_kpp_query) + return -EOPNOTSUPP; + + return 0; +} + +/* + * Get parameters from userspace. Callers must always call the free function + * on params, even if an error is returned. + */ +static int keyctl_kpp_params_get_2( + const struct keyctl_kpp_params __user *_params, + int operation, + struct kernel_kpp_params *params) +{ + struct keyctl_kpp_params uparams; + struct kernel_kpp_query res; + int ret; + + if (copy_from_user(&uparams, _params, sizeof(uparams)) != 0) + return -EFAULT; + + ret = keyctl_kpp_params_get(uparams.key_id, params); + if (ret < 0) + return ret; + + ret = params->key->type->asym_kpp_query(params, &res); + if (ret < 0) + return ret; + + switch (operation) { + case kernel_kpp_gen_pubkey: + if (uparams.out_len > res.max_size) + return -EINVAL; + break; + case kernel_kpp_compute_ss: + if (uparams.in_len > res.max_size || + uparams.out_len > res.max_size) + return -EINVAL; + params->in_len = uparams.in_len; + break; + default: + return -EINVAL; + } + params->out_len = uparams.out_len; + + return 0; +} + +/* + * Query information about an asymmetric key. + */ +long keyctl_kpp_query(key_serial_t id, struct keyctl_kpp_query __user *_res) +{ + struct kernel_kpp_params params; + struct kernel_kpp_query res; + long ret; + + memset(¶ms, 0, sizeof(params)); + + ret = keyctl_kpp_params_get(id, ¶ms); + if (ret < 0) + goto free_params; + + ret = params.key->type->asym_kpp_query(¶ms, &res); + if (ret < 0) + goto free_params; + + if (copy_to_user(_res, &res, sizeof(res)) || + clear_user(_res->__spare, sizeof(_res->__spare))) + ret = -EFAULT; + +free_params: + keyctl_kpp_params_free(¶ms); + return ret; +} + +/* + * Generate public key. + * + * If successful, the amount of data written into the output buffer is + * returned. + */ +long keyctl_kpp_gen_pubkey(const struct keyctl_kpp_params __user *_params, + void __user *_out) +{ + struct kernel_kpp_params params; + void *out; + long ret; + + memset(¶ms, 0, sizeof(params)); + + ret = keyctl_kpp_params_get_2(_params, kernel_kpp_gen_pubkey, ¶ms); + if (ret < 0) + goto free_params; + + if (!params.key->type->asym_kpp_gen_pubkey) { + ret = -EOPNOTSUPP; + goto free_params; + } + + out = kmalloc(params.out_len, GFP_KERNEL); + if (!out) { + ret = -ENOMEM; + goto free_params; + } + + ret = params.key->type->asym_kpp_gen_pubkey(¶ms, out); + if (ret < 0) + goto free_all; + + if (copy_to_user(_out, out, ret) != 0) + ret = -EFAULT; + +free_all: + kfree(out); +free_params: + keyctl_kpp_params_free(¶ms); + return ret; +} + +/* + * Compute shared secret. + * + * If successful, the amount of data written into the output buffer is + * returned. + */ +long keyctl_kpp_compute_ss(const struct keyctl_kpp_params __user *_params, + const void __user *_in, + void __user *_out) +{ + struct kernel_kpp_params params; + void *in, *out; + long ret; + + memset(¶ms, 0, sizeof(params)); + + ret = keyctl_kpp_params_get_2(_params, kernel_kpp_compute_ss, ¶ms); + if (ret < 0) + goto free_params; + + if (!params.key->type->asym_kpp_compute_ss) { + ret = -EOPNOTSUPP; + goto free_params; + } + + in = memdup_user(_in, params.in_len); + if (IS_ERR(in)) { + ret = PTR_ERR(in); + goto free_params; + } + + out = kmalloc(params.out_len, GFP_KERNEL); + if (!out) { + ret = -ENOMEM; + goto free_in; + } + + ret = params.key->type->asym_kpp_compute_ss(¶ms, in, out); + if (ret < 0) + goto free_all; + + if (copy_to_user(_out, out, ret) != 0) + ret = -EFAULT; + +free_all: + kfree(out); +free_in: + kfree(in); +free_params: + keyctl_kpp_params_free(¶ms); + return ret; +} -- 2.9.4