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>
This commit is contained in:
parent
faec5c4a8a
commit
4cabbb9f48
4 changed files with 37 additions and 16 deletions
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Generated by util/mkerr.pl DO NOT EDIT
|
* Generated by util/mkerr.pl DO NOT EDIT
|
||||||
* Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved.
|
* Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the OpenSSL license (the "License"). You may not use
|
* Licensed under the OpenSSL license (the "License"). You may not use
|
||||||
* this file except in compliance with the License. You can obtain a copy
|
* this file except in compliance with the License. You can obtain a copy
|
||||||
|
@ -246,6 +246,7 @@ static const ERR_STRING_DATA ASN1_str_reasons[] = {
|
||||||
{ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_MSTRING_WRONG_TAG), "mstring wrong tag"},
|
{ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_MSTRING_WRONG_TAG), "mstring wrong tag"},
|
||||||
{ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_NESTED_ASN1_STRING),
|
{ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_NESTED_ASN1_STRING),
|
||||||
"nested asn1 string"},
|
"nested asn1 string"},
|
||||||
|
{ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_NESTED_TOO_DEEP), "nested too deep"},
|
||||||
{ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_NON_HEX_CHARACTERS),
|
{ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_NON_HEX_CHARACTERS),
|
||||||
"non hex characters"},
|
"non hex characters"},
|
||||||
{ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_NOT_ASCII_FORMAT), "not ascii format"},
|
{ERR_PACK(ERR_LIB_ASN1, 0, ASN1_R_NOT_ASCII_FORMAT), "not ascii format"},
|
||||||
|
|
|
@ -17,9 +17,19 @@
|
||||||
#include "internal/numbers.h"
|
#include "internal/numbers.h"
|
||||||
#include "asn1_locl.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,
|
static int asn1_item_embed_d2i(ASN1_VALUE **pval, const unsigned char **in,
|
||||||
long len, const ASN1_ITEM *it,
|
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_check_eoc(const unsigned char **in, long len);
|
||||||
static int asn1_find_end(const unsigned char **in, long len, char inf);
|
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,
|
static int asn1_template_ex_d2i(ASN1_VALUE **pval,
|
||||||
const unsigned char **in, long len,
|
const unsigned char **in, long len,
|
||||||
const ASN1_TEMPLATE *tt, char opt,
|
const ASN1_TEMPLATE *tt, char opt,
|
||||||
ASN1_TLC *ctx);
|
ASN1_TLC *ctx, int depth);
|
||||||
static int asn1_template_noexp_d2i(ASN1_VALUE **val,
|
static int asn1_template_noexp_d2i(ASN1_VALUE **val,
|
||||||
const unsigned char **in, long len,
|
const unsigned char **in, long len,
|
||||||
const ASN1_TEMPLATE *tt, char opt,
|
const ASN1_TEMPLATE *tt, char opt,
|
||||||
ASN1_TLC *ctx);
|
ASN1_TLC *ctx, int depth);
|
||||||
static int asn1_d2i_ex_primitive(ASN1_VALUE **pval,
|
static int asn1_d2i_ex_primitive(ASN1_VALUE **pval,
|
||||||
const unsigned char **in, long len,
|
const unsigned char **in, long len,
|
||||||
const ASN1_ITEM *it,
|
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 tag, int aclass, char opt, ASN1_TLC *ctx)
|
||||||
{
|
{
|
||||||
int rv;
|
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)
|
if (rv <= 0)
|
||||||
ASN1_item_ex_free(pval, it);
|
ASN1_item_ex_free(pval, it);
|
||||||
return rv;
|
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,
|
static int asn1_item_embed_d2i(ASN1_VALUE **pval, const unsigned char **in,
|
||||||
long len, const ASN1_ITEM *it,
|
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_TEMPLATE *tt, *errtt = NULL;
|
||||||
const ASN1_EXTERN_FUNCS *ef;
|
const ASN1_EXTERN_FUNCS *ef;
|
||||||
|
@ -145,6 +156,11 @@ static int asn1_item_embed_d2i(ASN1_VALUE **pval, const unsigned char **in,
|
||||||
else
|
else
|
||||||
asn1_cb = 0;
|
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) {
|
switch (it->itype) {
|
||||||
case ASN1_ITYPE_PRIMITIVE:
|
case ASN1_ITYPE_PRIMITIVE:
|
||||||
if (it->templates) {
|
if (it->templates) {
|
||||||
|
@ -160,7 +176,7 @@ static int asn1_item_embed_d2i(ASN1_VALUE **pval, const unsigned char **in,
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
return asn1_template_ex_d2i(pval, in, len,
|
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,
|
return asn1_d2i_ex_primitive(pval, in, len, it,
|
||||||
tag, aclass, opt, ctx);
|
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.
|
* 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 field not present, try the next one */
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
continue;
|
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
|
* 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) {
|
if (!ret) {
|
||||||
errtt = seqtt;
|
errtt = seqtt;
|
||||||
goto err;
|
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,
|
static int asn1_template_ex_d2i(ASN1_VALUE **val,
|
||||||
const unsigned char **in, long inlen,
|
const unsigned char **in, long inlen,
|
||||||
const ASN1_TEMPLATE *tt, char opt,
|
const ASN1_TEMPLATE *tt, char opt,
|
||||||
ASN1_TLC *ctx)
|
ASN1_TLC *ctx, int depth)
|
||||||
{
|
{
|
||||||
int flags, aclass;
|
int flags, aclass;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -455,7 +472,7 @@ static int asn1_template_ex_d2i(ASN1_VALUE **val,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* We've found the field so it can't be OPTIONAL now */
|
/* 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) {
|
if (!ret) {
|
||||||
ASN1err(ASN1_F_ASN1_TEMPLATE_EX_D2I, ERR_R_NESTED_ASN1_ERROR);
|
ASN1err(ASN1_F_ASN1_TEMPLATE_EX_D2I, ERR_R_NESTED_ASN1_ERROR);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -479,7 +496,7 @@ static int asn1_template_ex_d2i(ASN1_VALUE **val,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
} 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;
|
*in = p;
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -491,7 +508,7 @@ static int asn1_template_ex_d2i(ASN1_VALUE **val,
|
||||||
static int asn1_template_noexp_d2i(ASN1_VALUE **val,
|
static int asn1_template_noexp_d2i(ASN1_VALUE **val,
|
||||||
const unsigned char **in, long len,
|
const unsigned char **in, long len,
|
||||||
const ASN1_TEMPLATE *tt, char opt,
|
const ASN1_TEMPLATE *tt, char opt,
|
||||||
ASN1_TLC *ctx)
|
ASN1_TLC *ctx, int depth)
|
||||||
{
|
{
|
||||||
int flags, aclass;
|
int flags, aclass;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -573,7 +590,8 @@ static int asn1_template_noexp_d2i(ASN1_VALUE **val,
|
||||||
}
|
}
|
||||||
skfield = NULL;
|
skfield = NULL;
|
||||||
if (!asn1_item_embed_d2i(&skfield, &p, len,
|
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,
|
ASN1err(ASN1_F_ASN1_TEMPLATE_NOEXP_D2I,
|
||||||
ERR_R_NESTED_ASN1_ERROR);
|
ERR_R_NESTED_ASN1_ERROR);
|
||||||
/* |skfield| may be partially allocated despite failure. */
|
/* |skfield| may be partially allocated despite failure. */
|
||||||
|
@ -595,7 +613,7 @@ static int asn1_template_noexp_d2i(ASN1_VALUE **val,
|
||||||
/* IMPLICIT tagging */
|
/* IMPLICIT tagging */
|
||||||
ret = asn1_item_embed_d2i(val, &p, len,
|
ret = asn1_item_embed_d2i(val, &p, len,
|
||||||
ASN1_ITEM_ptr(tt->item), tt->tag, aclass, opt,
|
ASN1_ITEM_ptr(tt->item), tt->tag, aclass, opt,
|
||||||
ctx);
|
ctx, depth);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
ASN1err(ASN1_F_ASN1_TEMPLATE_NOEXP_D2I, ERR_R_NESTED_ASN1_ERROR);
|
ASN1err(ASN1_F_ASN1_TEMPLATE_NOEXP_D2I, ERR_R_NESTED_ASN1_ERROR);
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -604,7 +622,7 @@ static int asn1_template_noexp_d2i(ASN1_VALUE **val,
|
||||||
} else {
|
} else {
|
||||||
/* Nothing special */
|
/* Nothing special */
|
||||||
ret = asn1_item_embed_d2i(val, &p, len, ASN1_ITEM_ptr(tt->item),
|
ret = asn1_item_embed_d2i(val, &p, len, ASN1_ITEM_ptr(tt->item),
|
||||||
-1, 0, opt, ctx);
|
-1, 0, opt, ctx, depth);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
ASN1err(ASN1_F_ASN1_TEMPLATE_NOEXP_D2I, ERR_R_NESTED_ASN1_ERROR);
|
ASN1err(ASN1_F_ASN1_TEMPLATE_NOEXP_D2I, ERR_R_NESTED_ASN1_ERROR);
|
||||||
goto err;
|
goto err;
|
||||||
|
|
|
@ -1680,6 +1680,7 @@ ASN1_R_MISSING_VALUE:189:missing value
|
||||||
ASN1_R_MSTRING_NOT_UNIVERSAL:139:mstring not universal
|
ASN1_R_MSTRING_NOT_UNIVERSAL:139:mstring not universal
|
||||||
ASN1_R_MSTRING_WRONG_TAG:140:mstring wrong tag
|
ASN1_R_MSTRING_WRONG_TAG:140:mstring wrong tag
|
||||||
ASN1_R_NESTED_ASN1_STRING:197:nested asn1 string
|
ASN1_R_NESTED_ASN1_STRING:197:nested asn1 string
|
||||||
|
ASN1_R_NESTED_TOO_DEEP:201:nested too deep
|
||||||
ASN1_R_NON_HEX_CHARACTERS:141:non hex characters
|
ASN1_R_NON_HEX_CHARACTERS:141:non hex characters
|
||||||
ASN1_R_NOT_ASCII_FORMAT:190:not ascii format
|
ASN1_R_NOT_ASCII_FORMAT:190:not ascii format
|
||||||
ASN1_R_NOT_ENOUGH_DATA:142:not enough data
|
ASN1_R_NOT_ENOUGH_DATA:142:not enough data
|
||||||
|
|
|
@ -191,6 +191,7 @@ int ERR_load_ASN1_strings(void);
|
||||||
# define ASN1_R_MSTRING_NOT_UNIVERSAL 139
|
# define ASN1_R_MSTRING_NOT_UNIVERSAL 139
|
||||||
# define ASN1_R_MSTRING_WRONG_TAG 140
|
# define ASN1_R_MSTRING_WRONG_TAG 140
|
||||||
# define ASN1_R_NESTED_ASN1_STRING 197
|
# define ASN1_R_NESTED_ASN1_STRING 197
|
||||||
|
# define ASN1_R_NESTED_TOO_DEEP 201
|
||||||
# define ASN1_R_NON_HEX_CHARACTERS 141
|
# define ASN1_R_NON_HEX_CHARACTERS 141
|
||||||
# define ASN1_R_NOT_ASCII_FORMAT 190
|
# define ASN1_R_NOT_ASCII_FORMAT 190
|
||||||
# define ASN1_R_NOT_ENOUGH_DATA 142
|
# define ASN1_R_NOT_ENOUGH_DATA 142
|
||||||
|
|
Loading…
Reference in a new issue