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 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 */ 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 */ 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 */ 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: 13800d60fd3SDavid Howells case KEYCTL_PKEY_DECRYPT: 13900d60fd3SDavid Howells if (uparams.in_len > info.max_enc_size || 14000d60fd3SDavid Howells uparams.out_len > info.max_dec_size) 14100d60fd3SDavid Howells return -EINVAL; 14200d60fd3SDavid Howells break; 14300d60fd3SDavid Howells case KEYCTL_PKEY_SIGN: 14400d60fd3SDavid Howells case KEYCTL_PKEY_VERIFY: 14500d60fd3SDavid Howells if (uparams.in_len > info.max_sig_size || 14600d60fd3SDavid Howells uparams.out_len > info.max_data_size) 14700d60fd3SDavid Howells return -EINVAL; 14800d60fd3SDavid Howells break; 14900d60fd3SDavid Howells default: 15000d60fd3SDavid Howells BUG(); 15100d60fd3SDavid Howells } 15200d60fd3SDavid Howells 15300d60fd3SDavid Howells params->in_len = uparams.in_len; 15400d60fd3SDavid Howells params->out_len = uparams.out_len; 15500d60fd3SDavid Howells return 0; 15600d60fd3SDavid Howells } 15700d60fd3SDavid Howells 15800d60fd3SDavid Howells /* 15900d60fd3SDavid Howells * Query information about an asymmetric key. 16000d60fd3SDavid Howells */ 16100d60fd3SDavid Howells long keyctl_pkey_query(key_serial_t id, 16200d60fd3SDavid Howells const char __user *_info, 16300d60fd3SDavid Howells struct keyctl_pkey_query __user *_res) 16400d60fd3SDavid Howells { 16500d60fd3SDavid Howells struct kernel_pkey_params params; 16600d60fd3SDavid Howells struct kernel_pkey_query res; 16700d60fd3SDavid Howells long ret; 16800d60fd3SDavid Howells 16900d60fd3SDavid Howells memset(¶ms, 0, sizeof(params)); 17000d60fd3SDavid Howells 17100d60fd3SDavid Howells ret = keyctl_pkey_params_get(id, _info, ¶ms); 17200d60fd3SDavid Howells if (ret < 0) 17300d60fd3SDavid Howells goto error; 17400d60fd3SDavid Howells 17500d60fd3SDavid Howells ret = params.key->type->asym_query(¶ms, &res); 17600d60fd3SDavid Howells if (ret < 0) 17700d60fd3SDavid Howells goto error; 17800d60fd3SDavid Howells 17900d60fd3SDavid Howells ret = -EFAULT; 18000d60fd3SDavid Howells if (copy_to_user(_res, &res, sizeof(res)) == 0 && 18100d60fd3SDavid Howells clear_user(_res->__spare, sizeof(_res->__spare)) == 0) 18200d60fd3SDavid Howells ret = 0; 18300d60fd3SDavid Howells 18400d60fd3SDavid Howells error: 18500d60fd3SDavid Howells keyctl_pkey_params_free(¶ms); 18600d60fd3SDavid Howells return ret; 18700d60fd3SDavid Howells } 18800d60fd3SDavid Howells 18900d60fd3SDavid Howells /* 19000d60fd3SDavid Howells * Encrypt/decrypt/sign 19100d60fd3SDavid Howells * 19200d60fd3SDavid Howells * Encrypt data, decrypt data or sign data using a public key. 19300d60fd3SDavid Howells * 19400d60fd3SDavid Howells * _info is a string of supplementary information in key=val format. For 19500d60fd3SDavid Howells * instance, it might contain: 19600d60fd3SDavid Howells * 19700d60fd3SDavid Howells * "enc=pkcs1 hash=sha256" 19800d60fd3SDavid Howells * 19900d60fd3SDavid Howells * where enc= specifies the encoding and hash= selects the OID to go in that 20000d60fd3SDavid Howells * particular encoding if required. If enc= isn't supplied, it's assumed that 20100d60fd3SDavid Howells * the caller is supplying raw values. 20200d60fd3SDavid Howells * 20300d60fd3SDavid Howells * If successful, the amount of data written into the output buffer is 20400d60fd3SDavid Howells * returned. 20500d60fd3SDavid Howells */ 20600d60fd3SDavid Howells long keyctl_pkey_e_d_s(int op, 20700d60fd3SDavid Howells const struct keyctl_pkey_params __user *_params, 20800d60fd3SDavid Howells const char __user *_info, 20900d60fd3SDavid Howells const void __user *_in, 21000d60fd3SDavid Howells void __user *_out) 21100d60fd3SDavid Howells { 21200d60fd3SDavid Howells struct kernel_pkey_params params; 21300d60fd3SDavid Howells void *in, *out; 21400d60fd3SDavid Howells long ret; 21500d60fd3SDavid Howells 21600d60fd3SDavid Howells ret = keyctl_pkey_params_get_2(_params, _info, op, ¶ms); 21700d60fd3SDavid Howells if (ret < 0) 21800d60fd3SDavid Howells goto error_params; 21900d60fd3SDavid Howells 22000d60fd3SDavid Howells ret = -EOPNOTSUPP; 22100d60fd3SDavid Howells if (!params.key->type->asym_eds_op) 22200d60fd3SDavid Howells goto error_params; 22300d60fd3SDavid Howells 22400d60fd3SDavid Howells switch (op) { 22500d60fd3SDavid Howells case KEYCTL_PKEY_ENCRYPT: 22600d60fd3SDavid Howells params.op = kernel_pkey_encrypt; 22700d60fd3SDavid Howells break; 22800d60fd3SDavid Howells case KEYCTL_PKEY_DECRYPT: 22900d60fd3SDavid Howells params.op = kernel_pkey_decrypt; 23000d60fd3SDavid Howells break; 23100d60fd3SDavid Howells case KEYCTL_PKEY_SIGN: 23200d60fd3SDavid Howells params.op = kernel_pkey_sign; 23300d60fd3SDavid Howells break; 23400d60fd3SDavid Howells default: 23500d60fd3SDavid Howells BUG(); 23600d60fd3SDavid Howells } 23700d60fd3SDavid Howells 23800d60fd3SDavid Howells in = memdup_user(_in, params.in_len); 23900d60fd3SDavid Howells if (IS_ERR(in)) { 24000d60fd3SDavid Howells ret = PTR_ERR(in); 24100d60fd3SDavid Howells goto error_params; 24200d60fd3SDavid Howells } 24300d60fd3SDavid Howells 24400d60fd3SDavid Howells ret = -ENOMEM; 24500d60fd3SDavid Howells out = kmalloc(params.out_len, GFP_KERNEL); 24600d60fd3SDavid Howells if (!out) 24700d60fd3SDavid Howells goto error_in; 24800d60fd3SDavid Howells 24900d60fd3SDavid Howells ret = params.key->type->asym_eds_op(¶ms, in, out); 25000d60fd3SDavid Howells if (ret < 0) 25100d60fd3SDavid Howells goto error_out; 25200d60fd3SDavid Howells 25300d60fd3SDavid Howells if (copy_to_user(_out, out, ret) != 0) 25400d60fd3SDavid Howells ret = -EFAULT; 25500d60fd3SDavid Howells 25600d60fd3SDavid Howells error_out: 25700d60fd3SDavid Howells kfree(out); 25800d60fd3SDavid Howells error_in: 25900d60fd3SDavid Howells kfree(in); 26000d60fd3SDavid Howells error_params: 26100d60fd3SDavid Howells keyctl_pkey_params_free(¶ms); 26200d60fd3SDavid Howells return ret; 26300d60fd3SDavid Howells } 26400d60fd3SDavid Howells 26500d60fd3SDavid Howells /* 26600d60fd3SDavid Howells * Verify a signature. 26700d60fd3SDavid Howells * 26800d60fd3SDavid Howells * Verify a public key signature using the given key, or if not given, search 26900d60fd3SDavid Howells * for a matching key. 27000d60fd3SDavid Howells * 27100d60fd3SDavid Howells * _info is a string of supplementary information in key=val format. For 27200d60fd3SDavid Howells * instance, it might contain: 27300d60fd3SDavid Howells * 27400d60fd3SDavid Howells * "enc=pkcs1 hash=sha256" 27500d60fd3SDavid Howells * 27600d60fd3SDavid Howells * where enc= specifies the signature blob encoding and hash= selects the OID 27700d60fd3SDavid Howells * to go in that particular encoding. If enc= isn't supplied, it's assumed 27800d60fd3SDavid Howells * that the caller is supplying raw values. 27900d60fd3SDavid Howells * 28000d60fd3SDavid Howells * If successful, 0 is returned. 28100d60fd3SDavid Howells */ 28200d60fd3SDavid Howells long keyctl_pkey_verify(const struct keyctl_pkey_params __user *_params, 28300d60fd3SDavid Howells const char __user *_info, 28400d60fd3SDavid Howells const void __user *_in, 28500d60fd3SDavid Howells const void __user *_in2) 28600d60fd3SDavid Howells { 28700d60fd3SDavid Howells struct kernel_pkey_params params; 28800d60fd3SDavid Howells void *in, *in2; 28900d60fd3SDavid Howells long ret; 29000d60fd3SDavid Howells 29100d60fd3SDavid Howells ret = keyctl_pkey_params_get_2(_params, _info, KEYCTL_PKEY_VERIFY, 29200d60fd3SDavid Howells ¶ms); 29300d60fd3SDavid Howells if (ret < 0) 29400d60fd3SDavid Howells goto error_params; 29500d60fd3SDavid Howells 29600d60fd3SDavid Howells ret = -EOPNOTSUPP; 29700d60fd3SDavid Howells if (!params.key->type->asym_verify_signature) 29800d60fd3SDavid Howells goto error_params; 29900d60fd3SDavid Howells 30000d60fd3SDavid Howells in = memdup_user(_in, params.in_len); 30100d60fd3SDavid Howells if (IS_ERR(in)) { 30200d60fd3SDavid Howells ret = PTR_ERR(in); 30300d60fd3SDavid Howells goto error_params; 30400d60fd3SDavid Howells } 30500d60fd3SDavid Howells 30600d60fd3SDavid Howells in2 = memdup_user(_in2, params.in2_len); 30700d60fd3SDavid Howells if (IS_ERR(in2)) { 30800d60fd3SDavid Howells ret = PTR_ERR(in2); 30900d60fd3SDavid Howells goto error_in; 31000d60fd3SDavid Howells } 31100d60fd3SDavid Howells 31200d60fd3SDavid Howells params.op = kernel_pkey_verify; 31300d60fd3SDavid Howells ret = params.key->type->asym_verify_signature(¶ms, in, in2); 31400d60fd3SDavid Howells 31500d60fd3SDavid Howells kfree(in2); 31600d60fd3SDavid Howells error_in: 31700d60fd3SDavid Howells kfree(in); 31800d60fd3SDavid Howells error_params: 31900d60fd3SDavid Howells keyctl_pkey_params_free(¶ms); 32000d60fd3SDavid Howells return ret; 32100d60fd3SDavid Howells } 322