18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* Public-key operation keyctls
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include <linux/err.h>
108c2ecf20Sopenharmony_ci#include <linux/key.h>
118c2ecf20Sopenharmony_ci#include <linux/keyctl.h>
128c2ecf20Sopenharmony_ci#include <linux/parser.h>
138c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
148c2ecf20Sopenharmony_ci#include <keys/user-type.h>
158c2ecf20Sopenharmony_ci#include "internal.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic void keyctl_pkey_params_free(struct kernel_pkey_params *params)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	kfree(params->info);
208c2ecf20Sopenharmony_ci	key_put(params->key);
218c2ecf20Sopenharmony_ci}
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cienum {
248c2ecf20Sopenharmony_ci	Opt_err,
258c2ecf20Sopenharmony_ci	Opt_enc,		/* "enc=<encoding>" eg. "enc=oaep" */
268c2ecf20Sopenharmony_ci	Opt_hash,		/* "hash=<digest-name>" eg. "hash=sha1" */
278c2ecf20Sopenharmony_ci};
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic const match_table_t param_keys = {
308c2ecf20Sopenharmony_ci	{ Opt_enc,	"enc=%s" },
318c2ecf20Sopenharmony_ci	{ Opt_hash,	"hash=%s" },
328c2ecf20Sopenharmony_ci	{ Opt_err,	NULL }
338c2ecf20Sopenharmony_ci};
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/*
368c2ecf20Sopenharmony_ci * Parse the information string which consists of key=val pairs.
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_cistatic int keyctl_pkey_params_parse(struct kernel_pkey_params *params)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	unsigned long token_mask = 0;
418c2ecf20Sopenharmony_ci	substring_t args[MAX_OPT_ARGS];
428c2ecf20Sopenharmony_ci	char *c = params->info, *p, *q;
438c2ecf20Sopenharmony_ci	int token;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	while ((p = strsep(&c, " \t"))) {
468c2ecf20Sopenharmony_ci		if (*p == '\0' || *p == ' ' || *p == '\t')
478c2ecf20Sopenharmony_ci			continue;
488c2ecf20Sopenharmony_ci		token = match_token(p, param_keys, args);
498c2ecf20Sopenharmony_ci		if (token == Opt_err)
508c2ecf20Sopenharmony_ci			return -EINVAL;
518c2ecf20Sopenharmony_ci		if (__test_and_set_bit(token, &token_mask))
528c2ecf20Sopenharmony_ci			return -EINVAL;
538c2ecf20Sopenharmony_ci		q = args[0].from;
548c2ecf20Sopenharmony_ci		if (!q[0])
558c2ecf20Sopenharmony_ci			return -EINVAL;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci		switch (token) {
588c2ecf20Sopenharmony_ci		case Opt_enc:
598c2ecf20Sopenharmony_ci			params->encoding = q;
608c2ecf20Sopenharmony_ci			break;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci		case Opt_hash:
638c2ecf20Sopenharmony_ci			params->hash_algo = q;
648c2ecf20Sopenharmony_ci			break;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci		default:
678c2ecf20Sopenharmony_ci			return -EINVAL;
688c2ecf20Sopenharmony_ci		}
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	return 0;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/*
758c2ecf20Sopenharmony_ci * Interpret parameters.  Callers must always call the free function
768c2ecf20Sopenharmony_ci * on params, even if an error is returned.
778c2ecf20Sopenharmony_ci */
788c2ecf20Sopenharmony_cistatic int keyctl_pkey_params_get(key_serial_t id,
798c2ecf20Sopenharmony_ci				  const char __user *_info,
808c2ecf20Sopenharmony_ci				  struct kernel_pkey_params *params)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	key_ref_t key_ref;
838c2ecf20Sopenharmony_ci	void *p;
848c2ecf20Sopenharmony_ci	int ret;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	memset(params, 0, sizeof(*params));
878c2ecf20Sopenharmony_ci	params->encoding = "raw";
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	p = strndup_user(_info, PAGE_SIZE);
908c2ecf20Sopenharmony_ci	if (IS_ERR(p))
918c2ecf20Sopenharmony_ci		return PTR_ERR(p);
928c2ecf20Sopenharmony_ci	params->info = p;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	ret = keyctl_pkey_params_parse(params);
958c2ecf20Sopenharmony_ci	if (ret < 0)
968c2ecf20Sopenharmony_ci		return ret;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH);
998c2ecf20Sopenharmony_ci	if (IS_ERR(key_ref))
1008c2ecf20Sopenharmony_ci		return PTR_ERR(key_ref);
1018c2ecf20Sopenharmony_ci	params->key = key_ref_to_ptr(key_ref);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (!params->key->type->asym_query)
1048c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return 0;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/*
1108c2ecf20Sopenharmony_ci * Get parameters from userspace.  Callers must always call the free function
1118c2ecf20Sopenharmony_ci * on params, even if an error is returned.
1128c2ecf20Sopenharmony_ci */
1138c2ecf20Sopenharmony_cistatic int keyctl_pkey_params_get_2(const struct keyctl_pkey_params __user *_params,
1148c2ecf20Sopenharmony_ci				    const char __user *_info,
1158c2ecf20Sopenharmony_ci				    int op,
1168c2ecf20Sopenharmony_ci				    struct kernel_pkey_params *params)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	struct keyctl_pkey_params uparams;
1198c2ecf20Sopenharmony_ci	struct kernel_pkey_query info;
1208c2ecf20Sopenharmony_ci	int ret;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	memset(params, 0, sizeof(*params));
1238c2ecf20Sopenharmony_ci	params->encoding = "raw";
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (copy_from_user(&uparams, _params, sizeof(uparams)) != 0)
1268c2ecf20Sopenharmony_ci		return -EFAULT;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	ret = keyctl_pkey_params_get(uparams.key_id, _info, params);
1298c2ecf20Sopenharmony_ci	if (ret < 0)
1308c2ecf20Sopenharmony_ci		return ret;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	ret = params->key->type->asym_query(params, &info);
1338c2ecf20Sopenharmony_ci	if (ret < 0)
1348c2ecf20Sopenharmony_ci		return ret;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	switch (op) {
1378c2ecf20Sopenharmony_ci	case KEYCTL_PKEY_ENCRYPT:
1388c2ecf20Sopenharmony_ci		if (uparams.in_len  > info.max_dec_size ||
1398c2ecf20Sopenharmony_ci		    uparams.out_len > info.max_enc_size)
1408c2ecf20Sopenharmony_ci			return -EINVAL;
1418c2ecf20Sopenharmony_ci		break;
1428c2ecf20Sopenharmony_ci	case KEYCTL_PKEY_DECRYPT:
1438c2ecf20Sopenharmony_ci		if (uparams.in_len  > info.max_enc_size ||
1448c2ecf20Sopenharmony_ci		    uparams.out_len > info.max_dec_size)
1458c2ecf20Sopenharmony_ci			return -EINVAL;
1468c2ecf20Sopenharmony_ci		break;
1478c2ecf20Sopenharmony_ci	case KEYCTL_PKEY_SIGN:
1488c2ecf20Sopenharmony_ci		if (uparams.in_len  > info.max_data_size ||
1498c2ecf20Sopenharmony_ci		    uparams.out_len > info.max_sig_size)
1508c2ecf20Sopenharmony_ci			return -EINVAL;
1518c2ecf20Sopenharmony_ci		break;
1528c2ecf20Sopenharmony_ci	case KEYCTL_PKEY_VERIFY:
1538c2ecf20Sopenharmony_ci		if (uparams.in_len  > info.max_data_size ||
1548c2ecf20Sopenharmony_ci		    uparams.in2_len > info.max_sig_size)
1558c2ecf20Sopenharmony_ci			return -EINVAL;
1568c2ecf20Sopenharmony_ci		break;
1578c2ecf20Sopenharmony_ci	default:
1588c2ecf20Sopenharmony_ci		BUG();
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	params->in_len  = uparams.in_len;
1628c2ecf20Sopenharmony_ci	params->out_len = uparams.out_len; /* Note: same as in2_len */
1638c2ecf20Sopenharmony_ci	return 0;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci/*
1678c2ecf20Sopenharmony_ci * Query information about an asymmetric key.
1688c2ecf20Sopenharmony_ci */
1698c2ecf20Sopenharmony_cilong keyctl_pkey_query(key_serial_t id,
1708c2ecf20Sopenharmony_ci		       const char __user *_info,
1718c2ecf20Sopenharmony_ci		       struct keyctl_pkey_query __user *_res)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	struct kernel_pkey_params params;
1748c2ecf20Sopenharmony_ci	struct kernel_pkey_query res;
1758c2ecf20Sopenharmony_ci	long ret;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	memset(&params, 0, sizeof(params));
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	ret = keyctl_pkey_params_get(id, _info, &params);
1808c2ecf20Sopenharmony_ci	if (ret < 0)
1818c2ecf20Sopenharmony_ci		goto error;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	ret = params.key->type->asym_query(&params, &res);
1848c2ecf20Sopenharmony_ci	if (ret < 0)
1858c2ecf20Sopenharmony_ci		goto error;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	ret = -EFAULT;
1888c2ecf20Sopenharmony_ci	if (copy_to_user(_res, &res, sizeof(res)) == 0 &&
1898c2ecf20Sopenharmony_ci	    clear_user(_res->__spare, sizeof(_res->__spare)) == 0)
1908c2ecf20Sopenharmony_ci		ret = 0;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cierror:
1938c2ecf20Sopenharmony_ci	keyctl_pkey_params_free(&params);
1948c2ecf20Sopenharmony_ci	return ret;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci/*
1988c2ecf20Sopenharmony_ci * Encrypt/decrypt/sign
1998c2ecf20Sopenharmony_ci *
2008c2ecf20Sopenharmony_ci * Encrypt data, decrypt data or sign data using a public key.
2018c2ecf20Sopenharmony_ci *
2028c2ecf20Sopenharmony_ci * _info is a string of supplementary information in key=val format.  For
2038c2ecf20Sopenharmony_ci * instance, it might contain:
2048c2ecf20Sopenharmony_ci *
2058c2ecf20Sopenharmony_ci *	"enc=pkcs1 hash=sha256"
2068c2ecf20Sopenharmony_ci *
2078c2ecf20Sopenharmony_ci * where enc= specifies the encoding and hash= selects the OID to go in that
2088c2ecf20Sopenharmony_ci * particular encoding if required.  If enc= isn't supplied, it's assumed that
2098c2ecf20Sopenharmony_ci * the caller is supplying raw values.
2108c2ecf20Sopenharmony_ci *
2118c2ecf20Sopenharmony_ci * If successful, the amount of data written into the output buffer is
2128c2ecf20Sopenharmony_ci * returned.
2138c2ecf20Sopenharmony_ci */
2148c2ecf20Sopenharmony_cilong keyctl_pkey_e_d_s(int op,
2158c2ecf20Sopenharmony_ci		       const struct keyctl_pkey_params __user *_params,
2168c2ecf20Sopenharmony_ci		       const char __user *_info,
2178c2ecf20Sopenharmony_ci		       const void __user *_in,
2188c2ecf20Sopenharmony_ci		       void __user *_out)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	struct kernel_pkey_params params;
2218c2ecf20Sopenharmony_ci	void *in, *out;
2228c2ecf20Sopenharmony_ci	long ret;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	ret = keyctl_pkey_params_get_2(_params, _info, op, &params);
2258c2ecf20Sopenharmony_ci	if (ret < 0)
2268c2ecf20Sopenharmony_ci		goto error_params;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	ret = -EOPNOTSUPP;
2298c2ecf20Sopenharmony_ci	if (!params.key->type->asym_eds_op)
2308c2ecf20Sopenharmony_ci		goto error_params;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	switch (op) {
2338c2ecf20Sopenharmony_ci	case KEYCTL_PKEY_ENCRYPT:
2348c2ecf20Sopenharmony_ci		params.op = kernel_pkey_encrypt;
2358c2ecf20Sopenharmony_ci		break;
2368c2ecf20Sopenharmony_ci	case KEYCTL_PKEY_DECRYPT:
2378c2ecf20Sopenharmony_ci		params.op = kernel_pkey_decrypt;
2388c2ecf20Sopenharmony_ci		break;
2398c2ecf20Sopenharmony_ci	case KEYCTL_PKEY_SIGN:
2408c2ecf20Sopenharmony_ci		params.op = kernel_pkey_sign;
2418c2ecf20Sopenharmony_ci		break;
2428c2ecf20Sopenharmony_ci	default:
2438c2ecf20Sopenharmony_ci		BUG();
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	in = memdup_user(_in, params.in_len);
2478c2ecf20Sopenharmony_ci	if (IS_ERR(in)) {
2488c2ecf20Sopenharmony_ci		ret = PTR_ERR(in);
2498c2ecf20Sopenharmony_ci		goto error_params;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	ret = -ENOMEM;
2538c2ecf20Sopenharmony_ci	out = kmalloc(params.out_len, GFP_KERNEL);
2548c2ecf20Sopenharmony_ci	if (!out)
2558c2ecf20Sopenharmony_ci		goto error_in;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	ret = params.key->type->asym_eds_op(&params, in, out);
2588c2ecf20Sopenharmony_ci	if (ret < 0)
2598c2ecf20Sopenharmony_ci		goto error_out;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	if (copy_to_user(_out, out, ret) != 0)
2628c2ecf20Sopenharmony_ci		ret = -EFAULT;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cierror_out:
2658c2ecf20Sopenharmony_ci	kfree(out);
2668c2ecf20Sopenharmony_cierror_in:
2678c2ecf20Sopenharmony_ci	kfree(in);
2688c2ecf20Sopenharmony_cierror_params:
2698c2ecf20Sopenharmony_ci	keyctl_pkey_params_free(&params);
2708c2ecf20Sopenharmony_ci	return ret;
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci/*
2748c2ecf20Sopenharmony_ci * Verify a signature.
2758c2ecf20Sopenharmony_ci *
2768c2ecf20Sopenharmony_ci * Verify a public key signature using the given key, or if not given, search
2778c2ecf20Sopenharmony_ci * for a matching key.
2788c2ecf20Sopenharmony_ci *
2798c2ecf20Sopenharmony_ci * _info is a string of supplementary information in key=val format.  For
2808c2ecf20Sopenharmony_ci * instance, it might contain:
2818c2ecf20Sopenharmony_ci *
2828c2ecf20Sopenharmony_ci *	"enc=pkcs1 hash=sha256"
2838c2ecf20Sopenharmony_ci *
2848c2ecf20Sopenharmony_ci * where enc= specifies the signature blob encoding and hash= selects the OID
2858c2ecf20Sopenharmony_ci * to go in that particular encoding.  If enc= isn't supplied, it's assumed
2868c2ecf20Sopenharmony_ci * that the caller is supplying raw values.
2878c2ecf20Sopenharmony_ci *
2888c2ecf20Sopenharmony_ci * If successful, 0 is returned.
2898c2ecf20Sopenharmony_ci */
2908c2ecf20Sopenharmony_cilong keyctl_pkey_verify(const struct keyctl_pkey_params __user *_params,
2918c2ecf20Sopenharmony_ci			const char __user *_info,
2928c2ecf20Sopenharmony_ci			const void __user *_in,
2938c2ecf20Sopenharmony_ci			const void __user *_in2)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	struct kernel_pkey_params params;
2968c2ecf20Sopenharmony_ci	void *in, *in2;
2978c2ecf20Sopenharmony_ci	long ret;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	ret = keyctl_pkey_params_get_2(_params, _info, KEYCTL_PKEY_VERIFY,
3008c2ecf20Sopenharmony_ci				       &params);
3018c2ecf20Sopenharmony_ci	if (ret < 0)
3028c2ecf20Sopenharmony_ci		goto error_params;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	ret = -EOPNOTSUPP;
3058c2ecf20Sopenharmony_ci	if (!params.key->type->asym_verify_signature)
3068c2ecf20Sopenharmony_ci		goto error_params;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	in = memdup_user(_in, params.in_len);
3098c2ecf20Sopenharmony_ci	if (IS_ERR(in)) {
3108c2ecf20Sopenharmony_ci		ret = PTR_ERR(in);
3118c2ecf20Sopenharmony_ci		goto error_params;
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	in2 = memdup_user(_in2, params.in2_len);
3158c2ecf20Sopenharmony_ci	if (IS_ERR(in2)) {
3168c2ecf20Sopenharmony_ci		ret = PTR_ERR(in2);
3178c2ecf20Sopenharmony_ci		goto error_in;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	params.op = kernel_pkey_verify;
3218c2ecf20Sopenharmony_ci	ret = params.key->type->asym_verify_signature(&params, in, in2);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	kfree(in2);
3248c2ecf20Sopenharmony_cierror_in:
3258c2ecf20Sopenharmony_ci	kfree(in);
3268c2ecf20Sopenharmony_cierror_params:
3278c2ecf20Sopenharmony_ci	keyctl_pkey_params_free(&params);
3288c2ecf20Sopenharmony_ci	return ret;
3298c2ecf20Sopenharmony_ci}
330