summaryrefslogtreecommitdiffstats
path: root/crypto/drbg.c
diff options
context:
space:
mode:
authorStephan Mueller <smueller@chronox.de>2016-11-29 09:45:04 +0100
committerHerbert Xu <herbert@gondor.apana.org.au>2016-11-30 19:46:44 +0800
commit5102981212454998d549273ff9847f19e97a1794 (patch)
tree2f1b0259571af794c5e1004d452b0f81966c646f /crypto/drbg.c
parentc8467f7a3620698bf3c22f0e199b550fb611a8ae (diff)
downloadlinux-5102981212454998d549273ff9847f19e97a1794.tar.gz
linux-5102981212454998d549273ff9847f19e97a1794.tar.bz2
linux-5102981212454998d549273ff9847f19e97a1794.zip
crypto: drbg - prevent invalid SG mappings
When using SGs, only heap memory (memory that is valid as per virt_addr_valid) is allowed to be referenced. The CTR DRBG used to reference the caller-provided memory directly in an SG. In case the caller provided stack memory pointers, the SG mapping is not considered to be valid. In some cases, this would even cause a paging fault. The change adds a new scratch buffer that is used unconditionally to catch the cases where the caller-provided buffer is not suitable for use in an SG. The crypto operation of the CTR DRBG produces its output with that scratch buffer and finally copies the content of the scratch buffer to the caller's buffer. The scratch buffer is allocated during allocation time of the CTR DRBG as its access is protected with the DRBG mutex. Signed-off-by: Stephan Mueller <smueller@chronox.de> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'crypto/drbg.c')
-rw-r--r--crypto/drbg.c29
1 files changed, 24 insertions, 5 deletions
diff --git a/crypto/drbg.c b/crypto/drbg.c
index fb33f7d3b052..053035b5c8f8 100644
--- a/crypto/drbg.c
+++ b/crypto/drbg.c
@@ -262,6 +262,7 @@ static int drbg_kcapi_sym_ctr(struct drbg_state *drbg,
u8 *inbuf, u32 inbuflen,
u8 *outbuf, u32 outlen);
#define DRBG_CTR_NULL_LEN 128
+#define DRBG_OUTSCRATCHLEN DRBG_CTR_NULL_LEN
/* BCC function for CTR DRBG as defined in 10.4.3 */
static int drbg_ctr_bcc(struct drbg_state *drbg,
@@ -1644,6 +1645,9 @@ static int drbg_fini_sym_kernel(struct drbg_state *drbg)
kfree(drbg->ctr_null_value_buf);
drbg->ctr_null_value = NULL;
+ kfree(drbg->outscratchpadbuf);
+ drbg->outscratchpadbuf = NULL;
+
return 0;
}
@@ -1708,6 +1712,15 @@ static int drbg_init_sym_kernel(struct drbg_state *drbg)
drbg->ctr_null_value = (u8 *)PTR_ALIGN(drbg->ctr_null_value_buf,
alignmask + 1);
+ drbg->outscratchpadbuf = kmalloc(DRBG_OUTSCRATCHLEN + alignmask,
+ GFP_KERNEL);
+ if (!drbg->outscratchpadbuf) {
+ drbg_fini_sym_kernel(drbg);
+ return -ENOMEM;
+ }
+ drbg->outscratchpad = (u8 *)PTR_ALIGN(drbg->outscratchpadbuf,
+ alignmask + 1);
+
return alignmask;
}
@@ -1737,15 +1750,16 @@ static int drbg_kcapi_sym_ctr(struct drbg_state *drbg,
u8 *outbuf, u32 outlen)
{
struct scatterlist sg_in;
+ int ret;
sg_init_one(&sg_in, inbuf, inlen);
while (outlen) {
- u32 cryptlen = min_t(u32, inlen, outlen);
+ u32 cryptlen = min3(inlen, outlen, (u32)DRBG_OUTSCRATCHLEN);
struct scatterlist sg_out;
- int ret;
- sg_init_one(&sg_out, outbuf, cryptlen);
+ /* Output buffer may not be valid for SGL, use scratchpad */
+ sg_init_one(&sg_out, drbg->outscratchpad, cryptlen);
skcipher_request_set_crypt(drbg->ctr_req, &sg_in, &sg_out,
cryptlen, drbg->V);
ret = crypto_skcipher_encrypt(drbg->ctr_req);
@@ -1761,14 +1775,19 @@ static int drbg_kcapi_sym_ctr(struct drbg_state *drbg,
break;
}
default:
- return ret;
+ goto out;
}
init_completion(&drbg->ctr_completion);
+ memcpy(outbuf, drbg->outscratchpad, cryptlen);
+
outlen -= cryptlen;
}
+ ret = 0;
- return 0;
+out:
+ memzero_explicit(drbg->outscratchpad, DRBG_OUTSCRATCHLEN);
+ return ret;
}
#endif /* CONFIG_CRYPTO_DRBG_CTR */