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