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(¶ms, 0, sizeof(params)); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci ret = keyctl_pkey_params_get(id, _info, ¶ms); 1808c2ecf20Sopenharmony_ci if (ret < 0) 1818c2ecf20Sopenharmony_ci goto error; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci ret = params.key->type->asym_query(¶ms, &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(¶ms); 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, ¶ms); 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(¶ms, 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(¶ms); 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 ¶ms); 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(¶ms, in, in2); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci kfree(in2); 3248c2ecf20Sopenharmony_cierror_in: 3258c2ecf20Sopenharmony_ci kfree(in); 3268c2ecf20Sopenharmony_cierror_params: 3278c2ecf20Sopenharmony_ci keyctl_pkey_params_free(¶ms); 3288c2ecf20Sopenharmony_ci return ret; 3298c2ecf20Sopenharmony_ci} 330