KEYS: Provide keyctls to drive the new key type ops for asymmetric keys [ver #2]

Provide five keyctl functions that permit userspace to make use of the new
key type ops for accessing and driving asymmetric keys.

 (*) Query an asymmetric key.

	long keyctl(KEYCTL_PKEY_QUERY,
		    key_serial_t key, unsigned long reserved,
		    struct keyctl_pkey_query *info);

     Get information about an asymmetric key.  The information is returned
     in the keyctl_pkey_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_{ENCRYPT,DECRYPT,SIGN,VERIFY}

	__u32	key_size;

     The size in bits of the key.

	__u16	max_data_size;
	__u16	max_sig_size;
	__u16	max_enc_size;
	__u16	max_dec_size;

     The maximum sizes in bytes of a blob of data to be signed, a signature
     blob, a blob to be encrypted and a blob to be decrypted.

     reserved must be set to 0.  This is intended for future use to hand
     over one or more passphrases needed unlock a key.

     If successful, 0 is returned.  If the key is not an asymmetric key,
     EOPNOTSUPP is returned.

 (*) Encrypt, decrypt, sign or verify a blob using an asymmetric key.

	long keyctl(KEYCTL_PKEY_ENCRYPT,
		    const struct keyctl_pkey_params *params,
		    const char *info,
		    const void *in,
		    void *out);

	long keyctl(KEYCTL_PKEY_DECRYPT,
		    const struct keyctl_pkey_params *params,
		    const char *info,
		    const void *in,
		    void *out);

	long keyctl(KEYCTL_PKEY_SIGN,
		    const struct keyctl_pkey_params *params,
		    const char *info,
		    const void *in,
		    void *out);

	long keyctl(KEYCTL_PKEY_VERIFY,
		    const struct keyctl_pkey_params *params,
		    const char *info,
		    const void *in,
		    const void *in2);

     Use an asymmetric key to perform a public-key cryptographic operation
     a blob of data.

     The parameter block pointed to by params contains a number of integer
     values:

	__s32		key_id;
	__u32		in_len;
	__u32		out_len;
	__u32		in2_len;

     For a given operation, the in and out buffers are used as follows:

	Operation ID		in,in_len	out,out_len	in2,in2_len
	=======================	===============	===============	===========
	KEYCTL_PKEY_ENCRYPT	Raw data	Encrypted data	-
	KEYCTL_PKEY_DECRYPT	Encrypted data	Raw data	-
	KEYCTL_PKEY_SIGN	Raw data	Signature	-
	KEYCTL_PKEY_VERIFY	Raw data	-		Signature

     info is a string of key=value pairs that supply supplementary
     information.

     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, encrypt, decrypt and sign all return the amount of data
     written into the output buffer.  Verification returns 0 on success.

Signed-off-by: David Howells <dhowells@redhat.com>
Tested-by: Marcel Holtmann <marcel@holtmann.org>
Reviewed-by: Marcel Holtmann <marcel@holtmann.org>
Reviewed-by: Denis Kenzior <denkenz@gmail.com>
Tested-by: Denis Kenzior <denkenz@gmail.com>
Signed-off-by: James Morris <james.morris@microsoft.com>
This commit is contained in:
David Howells 2018-10-09 17:46:59 +01:00 committed by James Morris
parent 70025f84e5
commit 00d60fd3b9
7 changed files with 541 additions and 0 deletions

View File

@ -859,6 +859,7 @@ The keyctl syscall functions are:
and either the buffer length or the OtherInfo length exceeds the and either the buffer length or the OtherInfo length exceeds the
allowed length. allowed length.
* Restrict keyring linkage:: * Restrict keyring linkage::
long keyctl(KEYCTL_RESTRICT_KEYRING, key_serial_t keyring, long keyctl(KEYCTL_RESTRICT_KEYRING, key_serial_t keyring,
@ -890,6 +891,116 @@ The keyctl syscall functions are:
applicable to the asymmetric key type. applicable to the asymmetric key type.
* Query an asymmetric key::
long keyctl(KEYCTL_PKEY_QUERY,
key_serial_t key_id, unsigned long reserved,
struct keyctl_pkey_query *info);
Get information about an asymmetric key. The information is returned in
the keyctl_pkey_query struct::
__u32 supported_ops;
__u32 key_size;
__u16 max_data_size;
__u16 max_sig_size;
__u16 max_enc_size;
__u16 max_dec_size;
__u32 __spare[10];
``supported_ops`` contains a bit mask of flags indicating which ops are
supported. This is constructed from a bitwise-OR of::
KEYCTL_SUPPORTS_{ENCRYPT,DECRYPT,SIGN,VERIFY}
``key_size`` indicated the size of the key in bits.
``max_*_size`` indicate the maximum sizes in bytes of a blob of data to be
signed, a signature blob, a blob to be encrypted and a blob to be
decrypted.
``__spare[]`` must be set to 0. This is intended for future use to hand
over one or more passphrases needed unlock a key.
If successful, 0 is returned. If the key is not an asymmetric key,
EOPNOTSUPP is returned.
* Encrypt, decrypt, sign or verify a blob using an asymmetric key::
long keyctl(KEYCTL_PKEY_ENCRYPT,
const struct keyctl_pkey_params *params,
const char *info,
const void *in,
void *out);
long keyctl(KEYCTL_PKEY_DECRYPT,
const struct keyctl_pkey_params *params,
const char *info,
const void *in,
void *out);
long keyctl(KEYCTL_PKEY_SIGN,
const struct keyctl_pkey_params *params,
const char *info,
const void *in,
void *out);
long keyctl(KEYCTL_PKEY_VERIFY,
const struct keyctl_pkey_params *params,
const char *info,
const void *in,
const void *in2);
Use an asymmetric key to perform a public-key cryptographic operation a
blob of data. For encryption and verification, the asymmetric key may
only need the public parts to be available, but for decryption and signing
the private parts are required also.
The parameter block pointed to by params contains a number of integer
values::
__s32 key_id;
__u32 in_len;
__u32 out_len;
__u32 in2_len;
``key_id`` is the ID of the asymmetric key to be used. ``in_len`` and
``in2_len`` indicate the amount of data in the in and in2 buffers and
``out_len`` indicates the size of the out buffer as appropriate for the
above operations.
For a given operation, the in and out buffers are used as follows::
Operation ID in,in_len out,out_len in2,in2_len
======================= =============== =============== ===============
KEYCTL_PKEY_ENCRYPT Raw data Encrypted data -
KEYCTL_PKEY_DECRYPT Encrypted data Raw data -
KEYCTL_PKEY_SIGN Raw data Signature -
KEYCTL_PKEY_VERIFY Raw data - Signature
``info`` is a string of key=value pairs that supply supplementary
information. These include:
``enc=<encoding>`` The encoding of the encrypted/signature blob. This
can be "pkcs1" for RSASSA-PKCS1-v1.5 or
RSAES-PKCS1-v1.5; "pss" for "RSASSA-PSS"; "oaep" for
"RSAES-OAEP". If omitted or is "raw", the raw output
of the encryption function is specified.
``hash=<algo>`` If the data buffer contains the output of a hash
function and the encoding includes some indication of
which hash function was used, the hash function can be
specified with this, eg. "hash=sha256".
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, encrypt, decrypt and sign all return the amount of data
written into the output buffer. Verification returns 0 on success.
Kernel Services Kernel Services
=============== ===============

View File

@ -61,6 +61,11 @@
#define KEYCTL_INVALIDATE 21 /* invalidate a key */ #define KEYCTL_INVALIDATE 21 /* invalidate a key */
#define KEYCTL_GET_PERSISTENT 22 /* get a user's persistent keyring */ #define KEYCTL_GET_PERSISTENT 22 /* get a user's persistent keyring */
#define KEYCTL_DH_COMPUTE 23 /* Compute Diffie-Hellman values */ #define KEYCTL_DH_COMPUTE 23 /* Compute Diffie-Hellman values */
#define KEYCTL_PKEY_QUERY 24 /* Query public key parameters */
#define KEYCTL_PKEY_ENCRYPT 25 /* Encrypt a blob using a public key */
#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_RESTRICT_KEYRING 29 /* Restrict keys allowed to link to a keyring */ #define KEYCTL_RESTRICT_KEYRING 29 /* Restrict keys allowed to link to a keyring */
/* keyctl structures */ /* keyctl structures */
@ -87,4 +92,24 @@ struct keyctl_kdf_params {
#define KEYCTL_SUPPORTS_SIGN 0x04 #define KEYCTL_SUPPORTS_SIGN 0x04
#define KEYCTL_SUPPORTS_VERIFY 0x08 #define KEYCTL_SUPPORTS_VERIFY 0x08
struct keyctl_pkey_query {
__u32 supported_ops; /* Which ops are supported */
__u32 key_size; /* Size of the key in bits */
__u16 max_data_size; /* Maximum size of raw data to sign in bytes */
__u16 max_sig_size; /* Maximum size of signature in bytes */
__u16 max_enc_size; /* Maximum size of encrypted blob in bytes */
__u16 max_dec_size; /* Maximum size of decrypted blob in bytes */
__u32 __spare[10];
};
struct keyctl_pkey_params {
__s32 key_id; /* Serial no. of public key to use */
__u32 in_len; /* Input data size */
union {
__u32 out_len; /* Output buffer size (encrypt/decrypt/sign) */
__u32 in2_len; /* 2nd input data size (verify) */
};
__u32 __spare[7];
};
#endif /* _LINUX_KEYCTL_H */ #endif /* _LINUX_KEYCTL_H */

View File

@ -22,6 +22,7 @@ obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_SYSCTL) += sysctl.o obj-$(CONFIG_SYSCTL) += sysctl.o
obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o
obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o
obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += keyctl_pkey.o
# #
# Key types # Key types

View File

@ -141,6 +141,24 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option,
return keyctl_restrict_keyring(arg2, compat_ptr(arg3), return keyctl_restrict_keyring(arg2, compat_ptr(arg3),
compat_ptr(arg4)); compat_ptr(arg4));
case KEYCTL_PKEY_QUERY:
if (arg3 != 0)
return -EINVAL;
return keyctl_pkey_query(arg2,
compat_ptr(arg4),
compat_ptr(arg5));
case KEYCTL_PKEY_ENCRYPT:
case KEYCTL_PKEY_DECRYPT:
case KEYCTL_PKEY_SIGN:
return keyctl_pkey_e_d_s(option,
compat_ptr(arg2), compat_ptr(arg3),
compat_ptr(arg4), compat_ptr(arg5));
case KEYCTL_PKEY_VERIFY:
return keyctl_pkey_verify(compat_ptr(arg2), compat_ptr(arg3),
compat_ptr(arg4), compat_ptr(arg5));
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }

View File

@ -298,6 +298,45 @@ static inline long compat_keyctl_dh_compute(
#endif #endif
#endif #endif
#ifdef CONFIG_ASYMMETRIC_KEY_TYPE
extern long keyctl_pkey_query(key_serial_t,
const char __user *,
struct keyctl_pkey_query __user *);
extern long keyctl_pkey_verify(const struct keyctl_pkey_params __user *,
const char __user *,
const void __user *, const void __user *);
extern long keyctl_pkey_e_d_s(int,
const struct keyctl_pkey_params __user *,
const char __user *,
const void __user *, void __user *);
#else
static inline long keyctl_pkey_query(key_serial_t id,
const char __user *_info,
struct keyctl_pkey_query __user *_res)
{
return -EOPNOTSUPP;
}
static inline long keyctl_pkey_verify(const struct keyctl_pkey_params __user *params,
const char __user *_info,
const void __user *_in,
const void __user *_in2)
{
return -EOPNOTSUPP;
}
static inline long keyctl_pkey_e_d_s(int op,
const struct keyctl_pkey_params __user *params,
const char __user *_info,
const void __user *_in,
void __user *_out)
{
return -EOPNOTSUPP;
}
#endif
/* /*
* Debugging key validation * Debugging key validation
*/ */

View File

@ -1747,6 +1747,30 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
(const char __user *) arg3, (const char __user *) arg3,
(const char __user *) arg4); (const char __user *) arg4);
case KEYCTL_PKEY_QUERY:
if (arg3 != 0)
return -EINVAL;
return keyctl_pkey_query((key_serial_t)arg2,
(const char __user *)arg4,
(struct keyctl_pkey_query *)arg5);
case KEYCTL_PKEY_ENCRYPT:
case KEYCTL_PKEY_DECRYPT:
case KEYCTL_PKEY_SIGN:
return keyctl_pkey_e_d_s(
option,
(const struct keyctl_pkey_params __user *)arg2,
(const char __user *)arg3,
(const void __user *)arg4,
(void __user *)arg5);
case KEYCTL_PKEY_VERIFY:
return keyctl_pkey_verify(
(const struct keyctl_pkey_params __user *)arg2,
(const char __user *)arg3,
(const void __user *)arg4,
(const void __user *)arg5);
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }

323
security/keys/keyctl_pkey.c Normal file
View File

@ -0,0 +1,323 @@
/* Public-key operation keyctls
*
* Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation; either version
* 2 of the Licence, or (at your option) any later version.
*/
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/key.h>
#include <linux/keyctl.h>
#include <linux/parser.h>
#include <linux/uaccess.h>
#include <keys/user-type.h>
#include "internal.h"
static void keyctl_pkey_params_free(struct kernel_pkey_params *params)
{
kfree(params->info);
key_put(params->key);
}
enum {
Opt_err = -1,
Opt_enc, /* "enc=<encoding>" eg. "enc=oaep" */
Opt_hash, /* "hash=<digest-name>" eg. "hash=sha1" */
};
static const match_table_t param_keys = {
{ Opt_enc, "enc=%s" },
{ Opt_hash, "hash=%s" },
{ Opt_err, NULL }
};
/*
* Parse the information string which consists of key=val pairs.
*/
static int keyctl_pkey_params_parse(struct kernel_pkey_params *params)
{
unsigned long token_mask = 0;
substring_t args[MAX_OPT_ARGS];
char *c = params->info, *p, *q;
int token;
while ((p = strsep(&c, " \t"))) {
if (*p == '\0' || *p == ' ' || *p == '\t')
continue;
token = match_token(p, param_keys, args);
if (__test_and_set_bit(token, &token_mask))
return -EINVAL;
q = args[0].from;
if (!q[0])
return -EINVAL;
switch (token) {
case Opt_enc:
params->encoding = q;
break;
case Opt_hash:
params->hash_algo = q;
break;
default:
return -EINVAL;
}
}
return 0;
}
/*
* Interpret parameters. Callers must always call the free function
* on params, even if an error is returned.
*/
static int keyctl_pkey_params_get(key_serial_t id,
const char __user *_info,
struct kernel_pkey_params *params)
{
key_ref_t key_ref;
void *p;
int ret;
memset(params, 0, sizeof(*params));
params->encoding = "raw";
p = strndup_user(_info, PAGE_SIZE);
if (IS_ERR(p))
return PTR_ERR(p);
params->info = p;
ret = keyctl_pkey_params_parse(params);
if (ret < 0)
return ret;
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_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_pkey_params_get_2(const struct keyctl_pkey_params __user *_params,
const char __user *_info,
int op,
struct kernel_pkey_params *params)
{
struct keyctl_pkey_params uparams;
struct kernel_pkey_query info;
int ret;
memset(params, 0, sizeof(*params));
params->encoding = "raw";
if (copy_from_user(&uparams, _params, sizeof(uparams)) != 0)
return -EFAULT;
ret = keyctl_pkey_params_get(uparams.key_id, _info, params);
if (ret < 0)
return ret;
ret = params->key->type->asym_query(params, &info);
if (ret < 0)
return ret;
switch (op) {
case KEYCTL_PKEY_ENCRYPT:
case KEYCTL_PKEY_DECRYPT:
if (uparams.in_len > info.max_enc_size ||
uparams.out_len > info.max_dec_size)
return -EINVAL;
break;
case KEYCTL_PKEY_SIGN:
case KEYCTL_PKEY_VERIFY:
if (uparams.in_len > info.max_sig_size ||
uparams.out_len > info.max_data_size)
return -EINVAL;
break;
default:
BUG();
}
params->in_len = uparams.in_len;
params->out_len = uparams.out_len;
return 0;
}
/*
* Query information about an asymmetric key.
*/
long keyctl_pkey_query(key_serial_t id,
const char __user *_info,
struct keyctl_pkey_query __user *_res)
{
struct kernel_pkey_params params;
struct kernel_pkey_query res;
long ret;
memset(&params, 0, sizeof(params));
ret = keyctl_pkey_params_get(id, _info, &params);
if (ret < 0)
goto error;
ret = params.key->type->asym_query(&params, &res);
if (ret < 0)
goto error;
ret = -EFAULT;
if (copy_to_user(_res, &res, sizeof(res)) == 0 &&
clear_user(_res->__spare, sizeof(_res->__spare)) == 0)
ret = 0;
error:
keyctl_pkey_params_free(&params);
return ret;
}
/*
* Encrypt/decrypt/sign
*
* Encrypt data, decrypt data or sign data using a public key.
*
* _info is a string of supplementary information in key=val format. For
* instance, it might contain:
*
* "enc=pkcs1 hash=sha256"
*
* where enc= specifies the encoding and hash= selects the OID to go in that
* particular encoding if required. If enc= isn't supplied, it's assumed that
* the caller is supplying raw values.
*
* If successful, the amount of data written into the output buffer is
* returned.
*/
long keyctl_pkey_e_d_s(int op,
const struct keyctl_pkey_params __user *_params,
const char __user *_info,
const void __user *_in,
void __user *_out)
{
struct kernel_pkey_params params;
void *in, *out;
long ret;
ret = keyctl_pkey_params_get_2(_params, _info, op, &params);
if (ret < 0)
goto error_params;
ret = -EOPNOTSUPP;
if (!params.key->type->asym_eds_op)
goto error_params;
switch (op) {
case KEYCTL_PKEY_ENCRYPT:
params.op = kernel_pkey_encrypt;
break;
case KEYCTL_PKEY_DECRYPT:
params.op = kernel_pkey_decrypt;
break;
case KEYCTL_PKEY_SIGN:
params.op = kernel_pkey_sign;
break;
default:
BUG();
}
in = memdup_user(_in, params.in_len);
if (IS_ERR(in)) {
ret = PTR_ERR(in);
goto error_params;
}
ret = -ENOMEM;
out = kmalloc(params.out_len, GFP_KERNEL);
if (!out)
goto error_in;
ret = params.key->type->asym_eds_op(&params, in, out);
if (ret < 0)
goto error_out;
if (copy_to_user(_out, out, ret) != 0)
ret = -EFAULT;
error_out:
kfree(out);
error_in:
kfree(in);
error_params:
keyctl_pkey_params_free(&params);
return ret;
}
/*
* Verify a signature.
*
* Verify a public key signature using the given key, or if not given, search
* for a matching key.
*
* _info is a string of supplementary information in key=val format. For
* instance, it might contain:
*
* "enc=pkcs1 hash=sha256"
*
* where enc= specifies the signature blob encoding and hash= selects the OID
* to go in that particular encoding. If enc= isn't supplied, it's assumed
* that the caller is supplying raw values.
*
* If successful, 0 is returned.
*/
long keyctl_pkey_verify(const struct keyctl_pkey_params __user *_params,
const char __user *_info,
const void __user *_in,
const void __user *_in2)
{
struct kernel_pkey_params params;
void *in, *in2;
long ret;
ret = keyctl_pkey_params_get_2(_params, _info, KEYCTL_PKEY_VERIFY,
&params);
if (ret < 0)
goto error_params;
ret = -EOPNOTSUPP;
if (!params.key->type->asym_verify_signature)
goto error_params;
in = memdup_user(_in, params.in_len);
if (IS_ERR(in)) {
ret = PTR_ERR(in);
goto error_params;
}
in2 = memdup_user(_in2, params.in2_len);
if (IS_ERR(in2)) {
ret = PTR_ERR(in2);
goto error_in;
}
params.op = kernel_pkey_verify;
ret = params.key->type->asym_verify_signature(&params, in, in2);
kfree(in2);
error_in:
kfree(in);
error_params:
keyctl_pkey_params_free(&params);
return ret;
}