cregit-Linux how code gets into the kernel

Release 4.15 security/keys/dh.c

Directory: security/keys
/* Crypto operations using stored keys
 *
 * Copyright (c) 2016, Intel Corporation
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */

#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/scatterlist.h>
#include <linux/crypto.h>
#include <crypto/hash.h>
#include <crypto/kpp.h>
#include <crypto/dh.h>
#include <keys/user-type.h>
#include "internal.h"


static ssize_t dh_data_from_key(key_serial_t keyid, void **data) { struct key *key; key_ref_t key_ref; long status; ssize_t ret; key_ref = lookup_user_key(keyid, 0, KEY_NEED_READ); if (IS_ERR(key_ref)) { ret = -ENOKEY; goto error; } key = key_ref_to_ptr(key_ref); ret = -EOPNOTSUPP; if (key->type == &key_type_user) { down_read(&key->sem); status = key_validate(key); if (status == 0) { const struct user_key_payload *payload; uint8_t *duplicate; payload = user_key_payload_locked(key); duplicate = kmemdup(payload->data, payload->datalen, GFP_KERNEL); if (duplicate) { *data = duplicate; ret = payload->datalen; } else { ret = -ENOMEM; } } up_read(&key->sem); } key_put(key); error: return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Mat Martineau13375.14%250.00%
Stephan Mueller4324.29%125.00%
David Howells10.56%125.00%
Total177100.00%4100.00%


static void dh_free_data(struct dh *dh) { kzfree(dh->key); kzfree(dh->p); kzfree(dh->g); }

Contributors

PersonTokensPropCommitsCommitProp
Mat Martineau32100.00%1100.00%
Total32100.00%1100.00%

struct dh_completion { struct completion completion; int err; };
static void dh_crypto_done(struct crypto_async_request *req, int err) { struct dh_completion *compl = req->data; if (err == -EINPROGRESS) return; compl->err = err; complete(&compl->completion); }

Contributors

PersonTokensPropCommitsCommitProp
Mat Martineau45100.00%1100.00%
Total45100.00%1100.00%

struct kdf_sdesc { struct shash_desc shash; char ctx[]; };
static int kdf_alloc(struct kdf_sdesc **sdesc_ret, char *hashname) { struct crypto_shash *tfm; struct kdf_sdesc *sdesc; int size; int err; /* allocate synchronous hash */ tfm = crypto_alloc_shash(hashname, 0, 0); if (IS_ERR(tfm)) { pr_info("could not allocate digest TFM handle %s\n", hashname); return PTR_ERR(tfm); } err = -EINVAL; if (crypto_shash_digestsize(tfm) == 0) goto out_free_tfm; err = -ENOMEM; size = sizeof(struct shash_desc) + crypto_shash_descsize(tfm); sdesc = kmalloc(size, GFP_KERNEL); if (!sdesc) goto out_free_tfm; sdesc->shash.tfm = tfm; sdesc->shash.flags = 0x0; *sdesc_ret = sdesc; return 0; out_free_tfm: crypto_free_shash(tfm); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Stephan Mueller11475.00%150.00%
Eric Biggers3825.00%150.00%
Total152100.00%2100.00%


static void kdf_dealloc(struct kdf_sdesc *sdesc) { if (!sdesc) return; if (sdesc->shash.tfm) crypto_free_shash(sdesc->shash.tfm); kzfree(sdesc); }

Contributors

PersonTokensPropCommitsCommitProp
Stephan Mueller39100.00%1100.00%
Total39100.00%1100.00%

/* * Implementation of the KDF in counter mode according to SP800-108 section 5.1 * as well as SP800-56A section 5.8.1 (Single-step KDF). * * SP800-56A: * The src pointer is defined as Z || other info where Z is the shared secret * from DH and other info is an arbitrary string (see SP800-56A section * 5.8.1.2). */
static int kdf_ctr(struct kdf_sdesc *sdesc, const u8 *src, unsigned int slen, u8 *dst, unsigned int dlen, unsigned int zlen) { struct shash_desc *desc = &sdesc->shash; unsigned int h = crypto_shash_digestsize(desc->tfm); int err = 0; u8 *dst_orig = dst; __be32 counter = cpu_to_be32(1); while (dlen) { err = crypto_shash_init(desc); if (err) goto err; err = crypto_shash_update(desc, (u8 *)&counter, sizeof(__be32)); if (err) goto err; if (zlen && h) { u8 tmpbuffer[h]; size_t chunk = min_t(size_t, zlen, h); memset(tmpbuffer, 0, chunk); do { err = crypto_shash_update(desc, tmpbuffer, chunk); if (err) goto err; zlen -= chunk; chunk = min_t(size_t, zlen, h); } while (zlen); } if (src && slen) { err = crypto_shash_update(desc, src, slen); if (err) goto err; } if (dlen < h) { u8 tmpbuffer[h]; err = crypto_shash_final(desc, tmpbuffer); if (err) goto err; memcpy(dst, tmpbuffer, dlen); memzero_explicit(tmpbuffer, h); return 0; } else { err = crypto_shash_final(desc, dst); if (err) goto err; dlen -= h; dst += h; counter = cpu_to_be32(be32_to_cpu(counter) + 1); } } return 0; err: memzero_explicit(dst_orig, dlen); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Stephan Mueller22168.42%133.33%
Mat Martineau8024.77%133.33%
Eric Biggers226.81%133.33%
Total323100.00%3100.00%


static int keyctl_dh_compute_kdf(struct kdf_sdesc *sdesc, char __user *buffer, size_t buflen, uint8_t *kbuf, size_t kbuflen, size_t lzero) { uint8_t *outbuf = NULL; int ret; outbuf = kmalloc(buflen, GFP_KERNEL); if (!outbuf) { ret = -ENOMEM; goto err; } ret = kdf_ctr(sdesc, kbuf, kbuflen, outbuf, buflen, lzero); if (ret) goto err; ret = buflen; if (copy_to_user(buffer, outbuf, buflen) != 0) ret = -EFAULT; err: kzfree(outbuf); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Stephan Mueller9177.12%133.33%
Mat Martineau2722.88%266.67%
Total118100.00%3100.00%


long __keyctl_dh_compute(struct keyctl_dh_params __user *params, char __user *buffer, size_t buflen, struct keyctl_kdf_params *kdfcopy) { long ret; ssize_t dlen; int secretlen; int outlen; struct keyctl_dh_params pcopy; struct dh dh_inputs; struct scatterlist outsg; struct dh_completion compl; struct crypto_kpp *tfm; struct kpp_request *req; uint8_t *secret; uint8_t *outbuf; struct kdf_sdesc *sdesc = NULL; if (!params || (!buffer && buflen)) { ret = -EINVAL; goto out1; } if (copy_from_user(&pcopy, params, sizeof(pcopy)) != 0) { ret = -EFAULT; goto out1; } if (kdfcopy) { char *hashname; if (memchr_inv(kdfcopy->__spare, 0, sizeof(kdfcopy->__spare))) { ret = -EINVAL; goto out1; } if (buflen > KEYCTL_KDF_MAX_OUTPUT_LEN || kdfcopy->otherinfolen > KEYCTL_KDF_MAX_OI_LEN) { ret = -EMSGSIZE; goto out1; } /* get KDF name string */ hashname = strndup_user(kdfcopy->hashname, CRYPTO_MAX_ALG_NAME); if (IS_ERR(hashname)) { ret = PTR_ERR(hashname); goto out1; } /* allocate KDF from the kernel crypto API */ ret = kdf_alloc(&sdesc, hashname); kfree(hashname); if (ret) goto out1; } memset(&dh_inputs, 0, sizeof(dh_inputs)); dlen = dh_data_from_key(pcopy.prime, &dh_inputs.p); if (dlen < 0) { ret = dlen; goto out1; } dh_inputs.p_size = dlen; dlen = dh_data_from_key(pcopy.base, &dh_inputs.g); if (dlen < 0) { ret = dlen; goto out2; } dh_inputs.g_size = dlen; dlen = dh_data_from_key(pcopy.private, &dh_inputs.key); if (dlen < 0) { ret = dlen; goto out2; } dh_inputs.key_size = dlen; secretlen = crypto_dh_key_len(&dh_inputs); secret = kmalloc(secretlen, GFP_KERNEL); if (!secret) { ret = -ENOMEM; goto out2; } ret = crypto_dh_encode_key(secret, secretlen, &dh_inputs); if (ret) goto out3; tfm = crypto_alloc_kpp("dh", CRYPTO_ALG_TYPE_KPP, 0); if (IS_ERR(tfm)) { ret = PTR_ERR(tfm); goto out3; } ret = crypto_kpp_set_secret(tfm, secret, secretlen); if (ret) goto out4; outlen = crypto_kpp_maxsize(tfm); if (!kdfcopy) { /* * When not using a KDF, buflen 0 is used to read the * required buffer length */ if (buflen == 0) { ret = outlen; goto out4; } else if (outlen > buflen) { ret = -EOVERFLOW; goto out4; } } outbuf = kzalloc(kdfcopy ? (outlen + kdfcopy->otherinfolen) : outlen, GFP_KERNEL); if (!outbuf) { ret = -ENOMEM; goto out4; } sg_init_one(&outsg, outbuf, outlen); req = kpp_request_alloc(tfm, GFP_KERNEL); if (!req) { ret = -ENOMEM; goto out5; } kpp_request_set_input(req, NULL, 0); kpp_request_set_output(req, &outsg, outlen); init_completion(&compl.completion); kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, dh_crypto_done, &compl); /* * For DH, generate_public_key and generate_shared_secret are * the same calculation */ ret = crypto_kpp_generate_public_key(req); if (ret == -EINPROGRESS) { wait_for_completion(&compl.completion); ret = compl.err; if (ret) goto out6; } if (kdfcopy) { /* * Concatenate SP800-56A otherinfo past DH shared secret -- the * input to the KDF is (DH shared secret || otherinfo) */ if (copy_from_user(outbuf + req->dst_len, kdfcopy->otherinfo, kdfcopy->otherinfolen) != 0) { ret = -EFAULT; goto out6; } ret = keyctl_dh_compute_kdf(sdesc, buffer, buflen, outbuf, req->dst_len + kdfcopy->otherinfolen, outlen - req->dst_len); } else if (copy_to_user(buffer, outbuf, req->dst_len) == 0) { ret = req->dst_len; } else { ret = -EFAULT; } out6: kpp_request_free(req); out5: kzfree(outbuf); out4: crypto_free_kpp(tfm); out3: kzfree(secret); out2: dh_free_data(&dh_inputs); out1: kdf_dealloc(sdesc); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Mat Martineau61977.96%240.00%
Stephan Mueller14718.51%240.00%
Eric Biggers283.53%120.00%
Total794100.00%5100.00%


long keyctl_dh_compute(struct keyctl_dh_params __user *params, char __user *buffer, size_t buflen, struct keyctl_kdf_params __user *kdf) { struct keyctl_kdf_params kdfcopy; if (!kdf) return __keyctl_dh_compute(params, buffer, buflen, NULL); if (copy_from_user(&kdfcopy, kdf, sizeof(kdfcopy)) != 0) return -EFAULT; return __keyctl_dh_compute(params, buffer, buflen, &kdfcopy); }

Contributors

PersonTokensPropCommitsCommitProp
Stephan Mueller80100.00%1100.00%
Total80100.00%1100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
Mat Martineau97053.47%225.00%
Stephan Mueller75541.62%225.00%
Eric Biggers884.85%337.50%
David Howells10.06%112.50%
Total1814100.00%8100.00%
Directory: security/keys
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.