xref: /openbmc/linux/security/keys/keyctl_pkey.c (revision fe8df448)
1b4d0d230SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
200d60fd3SDavid Howells /* Public-key operation keyctls
300d60fd3SDavid Howells  *
400d60fd3SDavid Howells  * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
500d60fd3SDavid Howells  * Written by David Howells (dhowells@redhat.com)
600d60fd3SDavid Howells  */
700d60fd3SDavid Howells 
800d60fd3SDavid Howells #include <linux/slab.h>
900d60fd3SDavid Howells #include <linux/err.h>
1000d60fd3SDavid Howells #include <linux/key.h>
1100d60fd3SDavid Howells #include <linux/keyctl.h>
1200d60fd3SDavid Howells #include <linux/parser.h>
1300d60fd3SDavid Howells #include <linux/uaccess.h>
1400d60fd3SDavid Howells #include <keys/user-type.h>
1500d60fd3SDavid Howells #include "internal.h"
1600d60fd3SDavid Howells 
keyctl_pkey_params_free(struct kernel_pkey_params * params)1700d60fd3SDavid Howells static void keyctl_pkey_params_free(struct kernel_pkey_params *params)
1800d60fd3SDavid Howells {
1900d60fd3SDavid Howells 	kfree(params->info);
2000d60fd3SDavid Howells 	key_put(params->key);
2100d60fd3SDavid Howells }
2200d60fd3SDavid Howells 
2300d60fd3SDavid Howells enum {
2494c13f66SLinus Torvalds 	Opt_err,
2500d60fd3SDavid Howells 	Opt_enc,		/* "enc=<encoding>" eg. "enc=oaep" */
2600d60fd3SDavid Howells 	Opt_hash,		/* "hash=<digest-name>" eg. "hash=sha1" */
2700d60fd3SDavid Howells };
2800d60fd3SDavid Howells 
2900d60fd3SDavid Howells static const match_table_t param_keys = {
3000d60fd3SDavid Howells 	{ Opt_enc,	"enc=%s" },
3100d60fd3SDavid Howells 	{ Opt_hash,	"hash=%s" },
3200d60fd3SDavid Howells 	{ Opt_err,	NULL }
3300d60fd3SDavid Howells };
3400d60fd3SDavid Howells 
3500d60fd3SDavid Howells /*
3600d60fd3SDavid Howells  * Parse the information string which consists of key=val pairs.
3700d60fd3SDavid Howells  */
keyctl_pkey_params_parse(struct kernel_pkey_params * params)3800d60fd3SDavid Howells static int keyctl_pkey_params_parse(struct kernel_pkey_params *params)
3900d60fd3SDavid Howells {
4000d60fd3SDavid Howells 	unsigned long token_mask = 0;
4100d60fd3SDavid Howells 	substring_t args[MAX_OPT_ARGS];
4200d60fd3SDavid Howells 	char *c = params->info, *p, *q;
4300d60fd3SDavid Howells 	int token;
4400d60fd3SDavid Howells 
4500d60fd3SDavid Howells 	while ((p = strsep(&c, " \t"))) {
4600d60fd3SDavid Howells 		if (*p == '\0' || *p == ' ' || *p == '\t')
4700d60fd3SDavid Howells 			continue;
4800d60fd3SDavid Howells 		token = match_token(p, param_keys, args);
4957b0e314SEric Biggers 		if (token == Opt_err)
5057b0e314SEric Biggers 			return -EINVAL;
5100d60fd3SDavid Howells 		if (__test_and_set_bit(token, &token_mask))
5200d60fd3SDavid Howells 			return -EINVAL;
5300d60fd3SDavid Howells 		q = args[0].from;
5400d60fd3SDavid Howells 		if (!q[0])
5500d60fd3SDavid Howells 			return -EINVAL;
5600d60fd3SDavid Howells 
5700d60fd3SDavid Howells 		switch (token) {
5800d60fd3SDavid Howells 		case Opt_enc:
5900d60fd3SDavid Howells 			params->encoding = q;
6000d60fd3SDavid Howells 			break;
6100d60fd3SDavid Howells 
6200d60fd3SDavid Howells 		case Opt_hash:
6300d60fd3SDavid Howells 			params->hash_algo = q;
6400d60fd3SDavid Howells 			break;
6500d60fd3SDavid Howells 
6600d60fd3SDavid Howells 		default:
6700d60fd3SDavid Howells 			return -EINVAL;
6800d60fd3SDavid Howells 		}
6900d60fd3SDavid Howells 	}
7000d60fd3SDavid Howells 
7100d60fd3SDavid Howells 	return 0;
7200d60fd3SDavid Howells }
7300d60fd3SDavid Howells 
7400d60fd3SDavid Howells /*
7500d60fd3SDavid Howells  * Interpret parameters.  Callers must always call the free function
7600d60fd3SDavid Howells  * on params, even if an error is returned.
7700d60fd3SDavid Howells  */
keyctl_pkey_params_get(key_serial_t id,const char __user * _info,struct kernel_pkey_params * params)7800d60fd3SDavid Howells static int keyctl_pkey_params_get(key_serial_t id,
7900d60fd3SDavid Howells 				  const char __user *_info,
8000d60fd3SDavid Howells 				  struct kernel_pkey_params *params)
8100d60fd3SDavid Howells {
8200d60fd3SDavid Howells 	key_ref_t key_ref;
8300d60fd3SDavid Howells 	void *p;
8400d60fd3SDavid Howells 	int ret;
8500d60fd3SDavid Howells 
8600d60fd3SDavid Howells 	memset(params, 0, sizeof(*params));
8700d60fd3SDavid Howells 	params->encoding = "raw";
8800d60fd3SDavid Howells 
8900d60fd3SDavid Howells 	p = strndup_user(_info, PAGE_SIZE);
9000d60fd3SDavid Howells 	if (IS_ERR(p))
9100d60fd3SDavid Howells 		return PTR_ERR(p);
9200d60fd3SDavid Howells 	params->info = p;
9300d60fd3SDavid Howells 
9400d60fd3SDavid Howells 	ret = keyctl_pkey_params_parse(params);
9500d60fd3SDavid Howells 	if (ret < 0)
9600d60fd3SDavid Howells 		return ret;
9700d60fd3SDavid Howells 
9800d60fd3SDavid Howells 	key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH);
9900d60fd3SDavid Howells 	if (IS_ERR(key_ref))
10000d60fd3SDavid Howells 		return PTR_ERR(key_ref);
10100d60fd3SDavid Howells 	params->key = key_ref_to_ptr(key_ref);
10200d60fd3SDavid Howells 
10300d60fd3SDavid Howells 	if (!params->key->type->asym_query)
10400d60fd3SDavid Howells 		return -EOPNOTSUPP;
10500d60fd3SDavid Howells 
10600d60fd3SDavid Howells 	return 0;
10700d60fd3SDavid Howells }
10800d60fd3SDavid Howells 
10900d60fd3SDavid Howells /*
11000d60fd3SDavid Howells  * Get parameters from userspace.  Callers must always call the free function
11100d60fd3SDavid Howells  * on params, even if an error is returned.
11200d60fd3SDavid Howells  */
keyctl_pkey_params_get_2(const struct keyctl_pkey_params __user * _params,const char __user * _info,int op,struct kernel_pkey_params * params)11300d60fd3SDavid Howells static int keyctl_pkey_params_get_2(const struct keyctl_pkey_params __user *_params,
11400d60fd3SDavid Howells 				    const char __user *_info,
11500d60fd3SDavid Howells 				    int op,
11600d60fd3SDavid Howells 				    struct kernel_pkey_params *params)
11700d60fd3SDavid Howells {
11800d60fd3SDavid Howells 	struct keyctl_pkey_params uparams;
11900d60fd3SDavid Howells 	struct kernel_pkey_query info;
12000d60fd3SDavid Howells 	int ret;
12100d60fd3SDavid Howells 
12200d60fd3SDavid Howells 	memset(params, 0, sizeof(*params));
12300d60fd3SDavid Howells 	params->encoding = "raw";
12400d60fd3SDavid Howells 
12500d60fd3SDavid Howells 	if (copy_from_user(&uparams, _params, sizeof(uparams)) != 0)
12600d60fd3SDavid Howells 		return -EFAULT;
12700d60fd3SDavid Howells 
12800d60fd3SDavid Howells 	ret = keyctl_pkey_params_get(uparams.key_id, _info, params);
12900d60fd3SDavid Howells 	if (ret < 0)
13000d60fd3SDavid Howells 		return ret;
13100d60fd3SDavid Howells 
13200d60fd3SDavid Howells 	ret = params->key->type->asym_query(params, &info);
13300d60fd3SDavid Howells 	if (ret < 0)
13400d60fd3SDavid Howells 		return ret;
13500d60fd3SDavid Howells 
13600d60fd3SDavid Howells 	switch (op) {
13700d60fd3SDavid Howells 	case KEYCTL_PKEY_ENCRYPT:
138*fe8df448SEric Biggers 		if (uparams.in_len  > info.max_dec_size ||
139*fe8df448SEric Biggers 		    uparams.out_len > info.max_enc_size)
140*fe8df448SEric Biggers 			return -EINVAL;
141*fe8df448SEric Biggers 		break;
14200d60fd3SDavid Howells 	case KEYCTL_PKEY_DECRYPT:
14300d60fd3SDavid Howells 		if (uparams.in_len  > info.max_enc_size ||
14400d60fd3SDavid Howells 		    uparams.out_len > info.max_dec_size)
14500d60fd3SDavid Howells 			return -EINVAL;
14600d60fd3SDavid Howells 		break;
14700d60fd3SDavid Howells 	case KEYCTL_PKEY_SIGN:
148*fe8df448SEric Biggers 		if (uparams.in_len  > info.max_data_size ||
149*fe8df448SEric Biggers 		    uparams.out_len > info.max_sig_size)
150*fe8df448SEric Biggers 			return -EINVAL;
151*fe8df448SEric Biggers 		break;
15200d60fd3SDavid Howells 	case KEYCTL_PKEY_VERIFY:
153*fe8df448SEric Biggers 		if (uparams.in_len  > info.max_data_size ||
154*fe8df448SEric Biggers 		    uparams.in2_len > info.max_sig_size)
15500d60fd3SDavid Howells 			return -EINVAL;
15600d60fd3SDavid Howells 		break;
15700d60fd3SDavid Howells 	default:
15800d60fd3SDavid Howells 		BUG();
15900d60fd3SDavid Howells 	}
16000d60fd3SDavid Howells 
16100d60fd3SDavid Howells 	params->in_len  = uparams.in_len;
162*fe8df448SEric Biggers 	params->out_len = uparams.out_len; /* Note: same as in2_len */
16300d60fd3SDavid Howells 	return 0;
16400d60fd3SDavid Howells }
16500d60fd3SDavid Howells 
16600d60fd3SDavid Howells /*
16700d60fd3SDavid Howells  * Query information about an asymmetric key.
16800d60fd3SDavid Howells  */
keyctl_pkey_query(key_serial_t id,const char __user * _info,struct keyctl_pkey_query __user * _res)16900d60fd3SDavid Howells long keyctl_pkey_query(key_serial_t id,
17000d60fd3SDavid Howells 		       const char __user *_info,
17100d60fd3SDavid Howells 		       struct keyctl_pkey_query __user *_res)
17200d60fd3SDavid Howells {
17300d60fd3SDavid Howells 	struct kernel_pkey_params params;
17400d60fd3SDavid Howells 	struct kernel_pkey_query res;
17500d60fd3SDavid Howells 	long ret;
17600d60fd3SDavid Howells 
17700d60fd3SDavid Howells 	ret = keyctl_pkey_params_get(id, _info, &params);
17800d60fd3SDavid Howells 	if (ret < 0)
17900d60fd3SDavid Howells 		goto error;
18000d60fd3SDavid Howells 
18100d60fd3SDavid Howells 	ret = params.key->type->asym_query(&params, &res);
18200d60fd3SDavid Howells 	if (ret < 0)
18300d60fd3SDavid Howells 		goto error;
18400d60fd3SDavid Howells 
18500d60fd3SDavid Howells 	ret = -EFAULT;
18600d60fd3SDavid Howells 	if (copy_to_user(_res, &res, sizeof(res)) == 0 &&
18700d60fd3SDavid Howells 	    clear_user(_res->__spare, sizeof(_res->__spare)) == 0)
18800d60fd3SDavid Howells 		ret = 0;
18900d60fd3SDavid Howells 
19000d60fd3SDavid Howells error:
19100d60fd3SDavid Howells 	keyctl_pkey_params_free(&params);
19200d60fd3SDavid Howells 	return ret;
19300d60fd3SDavid Howells }
19400d60fd3SDavid Howells 
19500d60fd3SDavid Howells /*
19600d60fd3SDavid Howells  * Encrypt/decrypt/sign
19700d60fd3SDavid Howells  *
19800d60fd3SDavid Howells  * Encrypt data, decrypt data or sign data using a public key.
19900d60fd3SDavid Howells  *
20000d60fd3SDavid Howells  * _info is a string of supplementary information in key=val format.  For
20100d60fd3SDavid Howells  * instance, it might contain:
20200d60fd3SDavid Howells  *
20300d60fd3SDavid Howells  *	"enc=pkcs1 hash=sha256"
20400d60fd3SDavid Howells  *
20500d60fd3SDavid Howells  * where enc= specifies the encoding and hash= selects the OID to go in that
20600d60fd3SDavid Howells  * particular encoding if required.  If enc= isn't supplied, it's assumed that
20700d60fd3SDavid Howells  * the caller is supplying raw values.
20800d60fd3SDavid Howells  *
20900d60fd3SDavid Howells  * If successful, the amount of data written into the output buffer is
21000d60fd3SDavid Howells  * returned.
21100d60fd3SDavid Howells  */
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)21200d60fd3SDavid Howells long keyctl_pkey_e_d_s(int op,
21300d60fd3SDavid Howells 		       const struct keyctl_pkey_params __user *_params,
21400d60fd3SDavid Howells 		       const char __user *_info,
21500d60fd3SDavid Howells 		       const void __user *_in,
21600d60fd3SDavid Howells 		       void __user *_out)
21700d60fd3SDavid Howells {
21800d60fd3SDavid Howells 	struct kernel_pkey_params params;
21900d60fd3SDavid Howells 	void *in, *out;
22000d60fd3SDavid Howells 	long ret;
22100d60fd3SDavid Howells 
22200d60fd3SDavid Howells 	ret = keyctl_pkey_params_get_2(_params, _info, op, &params);
22300d60fd3SDavid Howells 	if (ret < 0)
22400d60fd3SDavid Howells 		goto error_params;
22500d60fd3SDavid Howells 
22600d60fd3SDavid Howells 	ret = -EOPNOTSUPP;
22700d60fd3SDavid Howells 	if (!params.key->type->asym_eds_op)
22800d60fd3SDavid Howells 		goto error_params;
22900d60fd3SDavid Howells 
23000d60fd3SDavid Howells 	switch (op) {
23100d60fd3SDavid Howells 	case KEYCTL_PKEY_ENCRYPT:
23200d60fd3SDavid Howells 		params.op = kernel_pkey_encrypt;
23300d60fd3SDavid Howells 		break;
23400d60fd3SDavid Howells 	case KEYCTL_PKEY_DECRYPT:
23500d60fd3SDavid Howells 		params.op = kernel_pkey_decrypt;
23600d60fd3SDavid Howells 		break;
23700d60fd3SDavid Howells 	case KEYCTL_PKEY_SIGN:
23800d60fd3SDavid Howells 		params.op = kernel_pkey_sign;
23900d60fd3SDavid Howells 		break;
24000d60fd3SDavid Howells 	default:
24100d60fd3SDavid Howells 		BUG();
24200d60fd3SDavid Howells 	}
24300d60fd3SDavid Howells 
24400d60fd3SDavid Howells 	in = memdup_user(_in, params.in_len);
24500d60fd3SDavid Howells 	if (IS_ERR(in)) {
24600d60fd3SDavid Howells 		ret = PTR_ERR(in);
24700d60fd3SDavid Howells 		goto error_params;
24800d60fd3SDavid Howells 	}
24900d60fd3SDavid Howells 
25000d60fd3SDavid Howells 	ret = -ENOMEM;
25100d60fd3SDavid Howells 	out = kmalloc(params.out_len, GFP_KERNEL);
25200d60fd3SDavid Howells 	if (!out)
25300d60fd3SDavid Howells 		goto error_in;
25400d60fd3SDavid Howells 
25500d60fd3SDavid Howells 	ret = params.key->type->asym_eds_op(&params, in, out);
25600d60fd3SDavid Howells 	if (ret < 0)
25700d60fd3SDavid Howells 		goto error_out;
25800d60fd3SDavid Howells 
25900d60fd3SDavid Howells 	if (copy_to_user(_out, out, ret) != 0)
26000d60fd3SDavid Howells 		ret = -EFAULT;
26100d60fd3SDavid Howells 
26200d60fd3SDavid Howells error_out:
26300d60fd3SDavid Howells 	kfree(out);
26400d60fd3SDavid Howells error_in:
26500d60fd3SDavid Howells 	kfree(in);
26600d60fd3SDavid Howells error_params:
26700d60fd3SDavid Howells 	keyctl_pkey_params_free(&params);
26800d60fd3SDavid Howells 	return ret;
26900d60fd3SDavid Howells }
27000d60fd3SDavid Howells 
27100d60fd3SDavid Howells /*
27200d60fd3SDavid Howells  * Verify a signature.
27300d60fd3SDavid Howells  *
27400d60fd3SDavid Howells  * Verify a public key signature using the given key, or if not given, search
27500d60fd3SDavid Howells  * for a matching key.
27600d60fd3SDavid Howells  *
27700d60fd3SDavid Howells  * _info is a string of supplementary information in key=val format.  For
27800d60fd3SDavid Howells  * instance, it might contain:
27900d60fd3SDavid Howells  *
28000d60fd3SDavid Howells  *	"enc=pkcs1 hash=sha256"
28100d60fd3SDavid Howells  *
28200d60fd3SDavid Howells  * where enc= specifies the signature blob encoding and hash= selects the OID
28300d60fd3SDavid Howells  * to go in that particular encoding.  If enc= isn't supplied, it's assumed
28400d60fd3SDavid Howells  * that the caller is supplying raw values.
28500d60fd3SDavid Howells  *
28600d60fd3SDavid Howells  * If successful, 0 is returned.
28700d60fd3SDavid Howells  */
keyctl_pkey_verify(const struct keyctl_pkey_params __user * _params,const char __user * _info,const void __user * _in,const void __user * _in2)28800d60fd3SDavid Howells long keyctl_pkey_verify(const struct keyctl_pkey_params __user *_params,
28900d60fd3SDavid Howells 			const char __user *_info,
29000d60fd3SDavid Howells 			const void __user *_in,
29100d60fd3SDavid Howells 			const void __user *_in2)
29200d60fd3SDavid Howells {
29300d60fd3SDavid Howells 	struct kernel_pkey_params params;
29400d60fd3SDavid Howells 	void *in, *in2;
29500d60fd3SDavid Howells 	long ret;
29600d60fd3SDavid Howells 
29700d60fd3SDavid Howells 	ret = keyctl_pkey_params_get_2(_params, _info, KEYCTL_PKEY_VERIFY,
29800d60fd3SDavid Howells 				       &params);
29900d60fd3SDavid Howells 	if (ret < 0)
30000d60fd3SDavid Howells 		goto error_params;
30100d60fd3SDavid Howells 
30200d60fd3SDavid Howells 	ret = -EOPNOTSUPP;
30300d60fd3SDavid Howells 	if (!params.key->type->asym_verify_signature)
30400d60fd3SDavid Howells 		goto error_params;
30500d60fd3SDavid Howells 
30600d60fd3SDavid Howells 	in = memdup_user(_in, params.in_len);
30700d60fd3SDavid Howells 	if (IS_ERR(in)) {
30800d60fd3SDavid Howells 		ret = PTR_ERR(in);
30900d60fd3SDavid Howells 		goto error_params;
31000d60fd3SDavid Howells 	}
31100d60fd3SDavid Howells 
31200d60fd3SDavid Howells 	in2 = memdup_user(_in2, params.in2_len);
31300d60fd3SDavid Howells 	if (IS_ERR(in2)) {
31400d60fd3SDavid Howells 		ret = PTR_ERR(in2);
31500d60fd3SDavid Howells 		goto error_in;
31600d60fd3SDavid Howells 	}
31700d60fd3SDavid Howells 
31800d60fd3SDavid Howells 	params.op = kernel_pkey_verify;
31900d60fd3SDavid Howells 	ret = params.key->type->asym_verify_signature(&params, in, in2);
32000d60fd3SDavid Howells 
32100d60fd3SDavid Howells 	kfree(in2);
32200d60fd3SDavid Howells error_in:
32300d60fd3SDavid Howells 	kfree(in);
32400d60fd3SDavid Howells error_params:
32500d60fd3SDavid Howells 	keyctl_pkey_params_free(&params);
32600d60fd3SDavid Howells 	return ret;
32700d60fd3SDavid Howells }
328