summaryrefslogtreecommitdiff
path: root/crypto/asn1/tasn_dec.c
diff options
context:
space:
mode:
authorMatt Caswell <matt@openssl.org>2018-03-22 09:39:53 +0000
committerMatt Caswell <matt@openssl.org>2018-03-27 10:22:49 +0100
commit4cabbb9f485ba7d1edcfbbd2aa8610159f94543e (patch)
tree354d2b718c38cc7b934fc10f5481e0e8d5b3a7c1 /crypto/asn1/tasn_dec.c
parentfaec5c4a8aa3943d835bdad26800a103426b0eda (diff)
downloadopenssl-new-4cabbb9f485ba7d1edcfbbd2aa8610159f94543e.tar.gz
Limit ASN.1 constructed types recursive definition depth
Constructed types with a recursive definition (such as can be found in PKCS7) could eventually exceed the stack given malicious input with excessive recursion. Therefore we limit the stack depth. CVE-2018-0739 Credit to OSSFuzz for finding this issue. Reviewed-by: Rich Salz <rsalz@openssl.org>
Diffstat (limited to 'crypto/asn1/tasn_dec.c')
-rw-r--r--crypto/asn1/tasn_dec.c48
1 files changed, 33 insertions, 15 deletions
diff --git a/crypto/asn1/tasn_dec.c b/crypto/asn1/tasn_dec.c
index 8667bec76a..9cdee2d6fe 100644
--- a/crypto/asn1/tasn_dec.c
+++ b/crypto/asn1/tasn_dec.c
@@ -17,9 +17,19 @@
#include "internal/numbers.h"
#include "asn1_locl.h"
+
+/*
+ * Constructed types with a recursive definition (such as can be found in PKCS7)
+ * could eventually exceed the stack given malicious input with excessive
+ * recursion. Therefore we limit the stack depth. This is the maximum number of
+ * recursive invocations of asn1_item_embed_d2i().
+ */
+#define ASN1_MAX_CONSTRUCTED_NEST 30
+
static int asn1_item_embed_d2i(ASN1_VALUE **pval, const unsigned char **in,
long len, const ASN1_ITEM *it,
- int tag, int aclass, char opt, ASN1_TLC *ctx);
+ int tag, int aclass, char opt, ASN1_TLC *ctx,
+ int depth);
static int asn1_check_eoc(const unsigned char **in, long len);
static int asn1_find_end(const unsigned char **in, long len, char inf);
@@ -37,11 +47,11 @@ static int asn1_check_tlen(long *olen, int *otag, unsigned char *oclass,
static int asn1_template_ex_d2i(ASN1_VALUE **pval,
const unsigned char **in, long len,
const ASN1_TEMPLATE *tt, char opt,
- ASN1_TLC *ctx);
+ ASN1_TLC *ctx, int depth);
static int asn1_template_noexp_d2i(ASN1_VALUE **val,
const unsigned char **in, long len,
const ASN1_TEMPLATE *tt, char opt,
- ASN1_TLC *ctx);
+ ASN1_TLC *ctx, int depth);
static int asn1_d2i_ex_primitive(ASN1_VALUE **pval,
const unsigned char **in, long len,
const ASN1_ITEM *it,
@@ -111,7 +121,7 @@ int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
int tag, int aclass, char opt, ASN1_TLC *ctx)
{
int rv;
- rv = asn1_item_embed_d2i(pval, in, len, it, tag, aclass, opt, ctx);
+ rv = asn1_item_embed_d2i(pval, in, len, it, tag, aclass, opt, ctx, 0);
if (rv <= 0)
ASN1_item_ex_free(pval, it);
return rv;
@@ -124,7 +134,8 @@ int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
static int asn1_item_embed_d2i(ASN1_VALUE **pval, const unsigned char **in,
long len, const ASN1_ITEM *it,
- int tag, int aclass, char opt, ASN1_TLC *ctx)
+ int tag, int aclass, char opt, ASN1_TLC *ctx,
+ int depth)
{
const ASN1_TEMPLATE *tt, *errtt = NULL;
const ASN1_EXTERN_FUNCS *ef;
@@ -145,6 +156,11 @@ static int asn1_item_embed_d2i(ASN1_VALUE **pval, const unsigned char **in,
else
asn1_cb = 0;
+ if (++depth > ASN1_MAX_CONSTRUCTED_NEST) {
+ ASN1err(ASN1_F_ASN1_ITEM_EMBED_D2I, ASN1_R_NESTED_TOO_DEEP);
+ goto err;
+ }
+
switch (it->itype) {
case ASN1_ITYPE_PRIMITIVE:
if (it->templates) {
@@ -160,7 +176,7 @@ static int asn1_item_embed_d2i(ASN1_VALUE **pval, const unsigned char **in,
goto err;
}
return asn1_template_ex_d2i(pval, in, len,
- it->templates, opt, ctx);
+ it->templates, opt, ctx, depth);
}
return asn1_d2i_ex_primitive(pval, in, len, it,
tag, aclass, opt, ctx);
@@ -221,7 +237,7 @@ static int asn1_item_embed_d2i(ASN1_VALUE **pval, const unsigned char **in,
/*
* We mark field as OPTIONAL so its absence can be recognised.
*/
- ret = asn1_template_ex_d2i(pchptr, &p, len, tt, 1, ctx);
+ ret = asn1_template_ex_d2i(pchptr, &p, len, tt, 1, ctx, depth);
/* If field not present, try the next one */
if (ret == -1)
continue;
@@ -344,7 +360,8 @@ static int asn1_item_embed_d2i(ASN1_VALUE **pval, const unsigned char **in,
* attempt to read in field, allowing each to be OPTIONAL
*/
- ret = asn1_template_ex_d2i(pseqval, &p, len, seqtt, isopt, ctx);
+ ret = asn1_template_ex_d2i(pseqval, &p, len, seqtt, isopt, ctx,
+ depth);
if (!ret) {
errtt = seqtt;
goto err;
@@ -420,7 +437,7 @@ static int asn1_item_embed_d2i(ASN1_VALUE **pval, const unsigned char **in,
static int asn1_template_ex_d2i(ASN1_VALUE **val,
const unsigned char **in, long inlen,
const ASN1_TEMPLATE *tt, char opt,
- ASN1_TLC *ctx)
+ ASN1_TLC *ctx, int depth)
{
int flags, aclass;
int ret;
@@ -455,7 +472,7 @@ static int asn1_template_ex_d2i(ASN1_VALUE **val,
return 0;
}
/* We've found the field so it can't be OPTIONAL now */
- ret = asn1_template_noexp_d2i(val, &p, len, tt, 0, ctx);
+ ret = asn1_template_noexp_d2i(val, &p, len, tt, 0, ctx, depth);
if (!ret) {
ASN1err(ASN1_F_ASN1_TEMPLATE_EX_D2I, ERR_R_NESTED_ASN1_ERROR);
return 0;
@@ -479,7 +496,7 @@ static int asn1_template_ex_d2i(ASN1_VALUE **val,
}
}
} else
- return asn1_template_noexp_d2i(val, in, inlen, tt, opt, ctx);
+ return asn1_template_noexp_d2i(val, in, inlen, tt, opt, ctx, depth);
*in = p;
return 1;
@@ -491,7 +508,7 @@ static int asn1_template_ex_d2i(ASN1_VALUE **val,
static int asn1_template_noexp_d2i(ASN1_VALUE **val,
const unsigned char **in, long len,
const ASN1_TEMPLATE *tt, char opt,
- ASN1_TLC *ctx)
+ ASN1_TLC *ctx, int depth)
{
int flags, aclass;
int ret;
@@ -573,7 +590,8 @@ static int asn1_template_noexp_d2i(ASN1_VALUE **val,
}
skfield = NULL;
if (!asn1_item_embed_d2i(&skfield, &p, len,
- ASN1_ITEM_ptr(tt->item), -1, 0, 0, ctx)) {
+ ASN1_ITEM_ptr(tt->item), -1, 0, 0, ctx,
+ depth)) {
ASN1err(ASN1_F_ASN1_TEMPLATE_NOEXP_D2I,
ERR_R_NESTED_ASN1_ERROR);
/* |skfield| may be partially allocated despite failure. */
@@ -595,7 +613,7 @@ static int asn1_template_noexp_d2i(ASN1_VALUE **val,
/* IMPLICIT tagging */
ret = asn1_item_embed_d2i(val, &p, len,
ASN1_ITEM_ptr(tt->item), tt->tag, aclass, opt,
- ctx);
+ ctx, depth);
if (!ret) {
ASN1err(ASN1_F_ASN1_TEMPLATE_NOEXP_D2I, ERR_R_NESTED_ASN1_ERROR);
goto err;
@@ -604,7 +622,7 @@ static int asn1_template_noexp_d2i(ASN1_VALUE **val,
} else {
/* Nothing special */
ret = asn1_item_embed_d2i(val, &p, len, ASN1_ITEM_ptr(tt->item),
- -1, 0, opt, ctx);
+ -1, 0, opt, ctx, depth);
if (!ret) {
ASN1err(ASN1_F_ASN1_TEMPLATE_NOEXP_D2I, ERR_R_NESTED_ASN1_ERROR);
goto err;