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, ¶ms);
17800d60fd3SDavid Howells if (ret < 0)
17900d60fd3SDavid Howells goto error;
18000d60fd3SDavid Howells
18100d60fd3SDavid Howells ret = params.key->type->asym_query(¶ms, &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(¶ms);
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, ¶ms);
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(¶ms, 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(¶ms);
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 ¶ms);
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(¶ms, 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(¶ms);
32600d60fd3SDavid Howells return ret;
32700d60fd3SDavid Howells }
328