123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /* Public-key operation keyctls
- *
- * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells ([email protected])
- */
- #include <linux/slab.h>
- #include <linux/err.h>
- #include <linux/key.h>
- #include <linux/keyctl.h>
- #include <linux/parser.h>
- #include <linux/uaccess.h>
- #include <keys/user-type.h>
- #include "internal.h"
- static void keyctl_pkey_params_free(struct kernel_pkey_params *params)
- {
- kfree(params->info);
- key_put(params->key);
- }
- enum {
- Opt_err,
- Opt_enc, /* "enc=<encoding>" eg. "enc=oaep" */
- Opt_hash, /* "hash=<digest-name>" eg. "hash=sha1" */
- };
- static const match_table_t param_keys = {
- { Opt_enc, "enc=%s" },
- { Opt_hash, "hash=%s" },
- { Opt_err, NULL }
- };
- /*
- * Parse the information string which consists of key=val pairs.
- */
- static int keyctl_pkey_params_parse(struct kernel_pkey_params *params)
- {
- unsigned long token_mask = 0;
- substring_t args[MAX_OPT_ARGS];
- char *c = params->info, *p, *q;
- int token;
- while ((p = strsep(&c, " \t"))) {
- if (*p == '\0' || *p == ' ' || *p == '\t')
- continue;
- token = match_token(p, param_keys, args);
- if (token == Opt_err)
- return -EINVAL;
- if (__test_and_set_bit(token, &token_mask))
- return -EINVAL;
- q = args[0].from;
- if (!q[0])
- return -EINVAL;
- switch (token) {
- case Opt_enc:
- params->encoding = q;
- break;
- case Opt_hash:
- params->hash_algo = q;
- break;
- default:
- return -EINVAL;
- }
- }
- return 0;
- }
- /*
- * Interpret parameters. Callers must always call the free function
- * on params, even if an error is returned.
- */
- static int keyctl_pkey_params_get(key_serial_t id,
- const char __user *_info,
- struct kernel_pkey_params *params)
- {
- key_ref_t key_ref;
- void *p;
- int ret;
- memset(params, 0, sizeof(*params));
- params->encoding = "raw";
- p = strndup_user(_info, PAGE_SIZE);
- if (IS_ERR(p))
- return PTR_ERR(p);
- params->info = p;
- ret = keyctl_pkey_params_parse(params);
- if (ret < 0)
- return ret;
- key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH);
- if (IS_ERR(key_ref))
- return PTR_ERR(key_ref);
- params->key = key_ref_to_ptr(key_ref);
- if (!params->key->type->asym_query)
- return -EOPNOTSUPP;
- return 0;
- }
- /*
- * Get parameters from userspace. Callers must always call the free function
- * on params, even if an error is returned.
- */
- static int keyctl_pkey_params_get_2(const struct keyctl_pkey_params __user *_params,
- const char __user *_info,
- int op,
- struct kernel_pkey_params *params)
- {
- struct keyctl_pkey_params uparams;
- struct kernel_pkey_query info;
- int ret;
- memset(params, 0, sizeof(*params));
- params->encoding = "raw";
- if (copy_from_user(&uparams, _params, sizeof(uparams)) != 0)
- return -EFAULT;
- ret = keyctl_pkey_params_get(uparams.key_id, _info, params);
- if (ret < 0)
- return ret;
- ret = params->key->type->asym_query(params, &info);
- if (ret < 0)
- return ret;
- switch (op) {
- case KEYCTL_PKEY_ENCRYPT:
- if (uparams.in_len > info.max_dec_size ||
- uparams.out_len > info.max_enc_size)
- return -EINVAL;
- break;
- case KEYCTL_PKEY_DECRYPT:
- if (uparams.in_len > info.max_enc_size ||
- uparams.out_len > info.max_dec_size)
- return -EINVAL;
- break;
- case KEYCTL_PKEY_SIGN:
- if (uparams.in_len > info.max_data_size ||
- uparams.out_len > info.max_sig_size)
- return -EINVAL;
- break;
- case KEYCTL_PKEY_VERIFY:
- if (uparams.in_len > info.max_data_size ||
- uparams.in2_len > info.max_sig_size)
- return -EINVAL;
- break;
- default:
- BUG();
- }
- params->in_len = uparams.in_len;
- params->out_len = uparams.out_len; /* Note: same as in2_len */
- return 0;
- }
- /*
- * Query information about an asymmetric key.
- */
- long keyctl_pkey_query(key_serial_t id,
- const char __user *_info,
- struct keyctl_pkey_query __user *_res)
- {
- struct kernel_pkey_params params;
- struct kernel_pkey_query res;
- long ret;
- ret = keyctl_pkey_params_get(id, _info, ¶ms);
- if (ret < 0)
- goto error;
- ret = params.key->type->asym_query(¶ms, &res);
- if (ret < 0)
- goto error;
- ret = -EFAULT;
- if (copy_to_user(_res, &res, sizeof(res)) == 0 &&
- clear_user(_res->__spare, sizeof(_res->__spare)) == 0)
- ret = 0;
- error:
- keyctl_pkey_params_free(¶ms);
- return ret;
- }
- /*
- * Encrypt/decrypt/sign
- *
- * Encrypt data, decrypt data or sign data using a public key.
- *
- * _info is a string of supplementary information in key=val format. For
- * instance, it might contain:
- *
- * "enc=pkcs1 hash=sha256"
- *
- * where enc= specifies the encoding and hash= selects the OID to go in that
- * particular encoding if required. If enc= isn't supplied, it's assumed that
- * the caller is supplying raw values.
- *
- * If successful, the amount of data written into the output buffer is
- * returned.
- */
- long 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)
- {
- struct kernel_pkey_params params;
- void *in, *out;
- long ret;
- ret = keyctl_pkey_params_get_2(_params, _info, op, ¶ms);
- if (ret < 0)
- goto error_params;
- ret = -EOPNOTSUPP;
- if (!params.key->type->asym_eds_op)
- goto error_params;
- switch (op) {
- case KEYCTL_PKEY_ENCRYPT:
- params.op = kernel_pkey_encrypt;
- break;
- case KEYCTL_PKEY_DECRYPT:
- params.op = kernel_pkey_decrypt;
- break;
- case KEYCTL_PKEY_SIGN:
- params.op = kernel_pkey_sign;
- break;
- default:
- BUG();
- }
- in = memdup_user(_in, params.in_len);
- if (IS_ERR(in)) {
- ret = PTR_ERR(in);
- goto error_params;
- }
- ret = -ENOMEM;
- out = kmalloc(params.out_len, GFP_KERNEL);
- if (!out)
- goto error_in;
- ret = params.key->type->asym_eds_op(¶ms, in, out);
- if (ret < 0)
- goto error_out;
- if (copy_to_user(_out, out, ret) != 0)
- ret = -EFAULT;
- error_out:
- kfree(out);
- error_in:
- kfree(in);
- error_params:
- keyctl_pkey_params_free(¶ms);
- return ret;
- }
- /*
- * Verify a signature.
- *
- * Verify a public key signature using the given key, or if not given, search
- * for a matching key.
- *
- * _info is a string of supplementary information in key=val format. For
- * instance, it might contain:
- *
- * "enc=pkcs1 hash=sha256"
- *
- * where enc= specifies the signature blob encoding and hash= selects the OID
- * to go in that particular encoding. If enc= isn't supplied, it's assumed
- * that the caller is supplying raw values.
- *
- * If successful, 0 is returned.
- */
- long keyctl_pkey_verify(const struct keyctl_pkey_params __user *_params,
- const char __user *_info,
- const void __user *_in,
- const void __user *_in2)
- {
- struct kernel_pkey_params params;
- void *in, *in2;
- long ret;
- ret = keyctl_pkey_params_get_2(_params, _info, KEYCTL_PKEY_VERIFY,
- ¶ms);
- if (ret < 0)
- goto error_params;
- ret = -EOPNOTSUPP;
- if (!params.key->type->asym_verify_signature)
- goto error_params;
- in = memdup_user(_in, params.in_len);
- if (IS_ERR(in)) {
- ret = PTR_ERR(in);
- goto error_params;
- }
- in2 = memdup_user(_in2, params.in2_len);
- if (IS_ERR(in2)) {
- ret = PTR_ERR(in2);
- goto error_in;
- }
- params.op = kernel_pkey_verify;
- ret = params.key->type->asym_verify_signature(¶ms, in, in2);
- kfree(in2);
- error_in:
- kfree(in);
- error_params:
- keyctl_pkey_params_free(¶ms);
- return ret;
- }
|