Skip to content

Commit 9310d45

Browse files
committed
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>
1 parent 3ffc95b commit 9310d45

File tree

3 files changed

+44
-22
lines changed

3 files changed

+44
-22
lines changed

crypto/asn1/asn1.h

+1
Original file line numberDiff line numberDiff line change
@@ -1365,6 +1365,7 @@ void ERR_load_ASN1_strings(void);
13651365
# define ASN1_R_MSTRING_NOT_UNIVERSAL 139
13661366
# define ASN1_R_MSTRING_WRONG_TAG 140
13671367
# define ASN1_R_NESTED_ASN1_STRING 197
1368+
# define ASN1_R_NESTED_TOO_DEEP 219
13681369
# define ASN1_R_NON_HEX_CHARACTERS 141
13691370
# define ASN1_R_NOT_ASCII_FORMAT 190
13701371
# define ASN1_R_NOT_ENOUGH_DATA 142

crypto/asn1/asn1_err.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* crypto/asn1/asn1_err.c */
22
/* ====================================================================
3-
* Copyright (c) 1999-2014 The OpenSSL Project. All rights reserved.
3+
* Copyright (c) 1999-2018 The OpenSSL Project. All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
66
* modification, are permitted provided that the following conditions
@@ -279,6 +279,7 @@ static ERR_STRING_DATA ASN1_str_reasons[] = {
279279
{ERR_REASON(ASN1_R_MSTRING_NOT_UNIVERSAL), "mstring not universal"},
280280
{ERR_REASON(ASN1_R_MSTRING_WRONG_TAG), "mstring wrong tag"},
281281
{ERR_REASON(ASN1_R_NESTED_ASN1_STRING), "nested asn1 string"},
282+
{ERR_REASON(ASN1_R_NESTED_TOO_DEEP), "nested too deep"},
282283
{ERR_REASON(ASN1_R_NON_HEX_CHARACTERS), "non hex characters"},
283284
{ERR_REASON(ASN1_R_NOT_ASCII_FORMAT), "not ascii format"},
284285
{ERR_REASON(ASN1_R_NOT_ENOUGH_DATA), "not enough data"},

crypto/asn1/tasn_dec.c

+41-21
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@
6565
#include <openssl/buffer.h>
6666
#include <openssl/err.h>
6767

68+
/*
69+
* Constructed types with a recursive definition (such as can be found in PKCS7)
70+
* could eventually exceed the stack given malicious input with excessive
71+
* recursion. Therefore we limit the stack depth. This is the maximum number of
72+
* recursive invocations of asn1_item_embed_d2i().
73+
*/
74+
#define ASN1_MAX_CONSTRUCTED_NEST 30
75+
6876
static int asn1_check_eoc(const unsigned char **in, long len);
6977
static int asn1_find_end(const unsigned char **in, long len, char inf);
7078

@@ -81,11 +89,11 @@ static int asn1_check_tlen(long *olen, int *otag, unsigned char *oclass,
8189
static int asn1_template_ex_d2i(ASN1_VALUE **pval,
8290
const unsigned char **in, long len,
8391
const ASN1_TEMPLATE *tt, char opt,
84-
ASN1_TLC *ctx);
92+
ASN1_TLC *ctx, int depth);
8593
static int asn1_template_noexp_d2i(ASN1_VALUE **val,
8694
const unsigned char **in, long len,
8795
const ASN1_TEMPLATE *tt, char opt,
88-
ASN1_TLC *ctx);
96+
ASN1_TLC *ctx, int depth);
8997
static int asn1_d2i_ex_primitive(ASN1_VALUE **pval,
9098
const unsigned char **in, long len,
9199
const ASN1_ITEM *it,
@@ -154,17 +162,16 @@ int ASN1_template_d2i(ASN1_VALUE **pval,
154162
{
155163
ASN1_TLC c;
156164
asn1_tlc_clear_nc(&c);
157-
return asn1_template_ex_d2i(pval, in, len, tt, 0, &c);
165+
return asn1_template_ex_d2i(pval, in, len, tt, 0, &c, 0);
158166
}
159167

160168
/*
161169
* Decode an item, taking care of IMPLICIT tagging, if any. If 'opt' set and
162170
* tag mismatch return -1 to handle OPTIONAL
163171
*/
164-
165-
int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
166-
const ASN1_ITEM *it,
167-
int tag, int aclass, char opt, ASN1_TLC *ctx)
172+
static int asn1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in,
173+
long len, const ASN1_ITEM *it, int tag, int aclass,
174+
char opt, ASN1_TLC *ctx, int depth)
168175
{
169176
const ASN1_TEMPLATE *tt, *errtt = NULL;
170177
const ASN1_COMPAT_FUNCS *cf;
@@ -189,6 +196,11 @@ int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
189196
else
190197
asn1_cb = 0;
191198

199+
if (++depth > ASN1_MAX_CONSTRUCTED_NEST) {
200+
ASN1err(ASN1_F_ASN1_ITEM_EX_D2I, ASN1_R_NESTED_TOO_DEEP);
201+
goto err;
202+
}
203+
192204
switch (it->itype) {
193205
case ASN1_ITYPE_PRIMITIVE:
194206
if (it->templates) {
@@ -204,7 +216,7 @@ int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
204216
goto err;
205217
}
206218
return asn1_template_ex_d2i(pval, in, len,
207-
it->templates, opt, ctx);
219+
it->templates, opt, ctx, depth);
208220
}
209221
return asn1_d2i_ex_primitive(pval, in, len, it,
210222
tag, aclass, opt, ctx);
@@ -326,7 +338,7 @@ int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
326338
/*
327339
* We mark field as OPTIONAL so its absence can be recognised.
328340
*/
329-
ret = asn1_template_ex_d2i(pchptr, &p, len, tt, 1, ctx);
341+
ret = asn1_template_ex_d2i(pchptr, &p, len, tt, 1, ctx, depth);
330342
/* If field not present, try the next one */
331343
if (ret == -1)
332344
continue;
@@ -444,7 +456,8 @@ int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
444456
* attempt to read in field, allowing each to be OPTIONAL
445457
*/
446458

447-
ret = asn1_template_ex_d2i(pseqval, &p, len, seqtt, isopt, ctx);
459+
ret = asn1_template_ex_d2i(pseqval, &p, len, seqtt, isopt, ctx,
460+
depth);
448461
if (!ret) {
449462
errtt = seqtt;
450463
goto err;
@@ -514,6 +527,13 @@ int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
514527
return 0;
515528
}
516529

530+
int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
531+
const ASN1_ITEM *it,
532+
int tag, int aclass, char opt, ASN1_TLC *ctx)
533+
{
534+
return asn1_item_ex_d2i(pval, in, len, it, tag, aclass, opt, ctx, 0);
535+
}
536+
517537
/*
518538
* Templates are handled with two separate functions. One handles any
519539
* EXPLICIT tag and the other handles the rest.
@@ -522,7 +542,7 @@ int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len,
522542
static int asn1_template_ex_d2i(ASN1_VALUE **val,
523543
const unsigned char **in, long inlen,
524544
const ASN1_TEMPLATE *tt, char opt,
525-
ASN1_TLC *ctx)
545+
ASN1_TLC *ctx, int depth)
526546
{
527547
int flags, aclass;
528548
int ret;
@@ -557,7 +577,7 @@ static int asn1_template_ex_d2i(ASN1_VALUE **val,
557577
return 0;
558578
}
559579
/* We've found the field so it can't be OPTIONAL now */
560-
ret = asn1_template_noexp_d2i(val, &p, len, tt, 0, ctx);
580+
ret = asn1_template_noexp_d2i(val, &p, len, tt, 0, ctx, depth);
561581
if (!ret) {
562582
ASN1err(ASN1_F_ASN1_TEMPLATE_EX_D2I, ERR_R_NESTED_ASN1_ERROR);
563583
return 0;
@@ -581,7 +601,7 @@ static int asn1_template_ex_d2i(ASN1_VALUE **val,
581601
}
582602
}
583603
} else
584-
return asn1_template_noexp_d2i(val, in, inlen, tt, opt, ctx);
604+
return asn1_template_noexp_d2i(val, in, inlen, tt, opt, ctx, depth);
585605

586606
*in = p;
587607
return 1;
@@ -594,7 +614,7 @@ static int asn1_template_ex_d2i(ASN1_VALUE **val,
594614
static int asn1_template_noexp_d2i(ASN1_VALUE **val,
595615
const unsigned char **in, long len,
596616
const ASN1_TEMPLATE *tt, char opt,
597-
ASN1_TLC *ctx)
617+
ASN1_TLC *ctx, int depth)
598618
{
599619
int flags, aclass;
600620
int ret;
@@ -665,8 +685,8 @@ static int asn1_template_noexp_d2i(ASN1_VALUE **val,
665685
break;
666686
}
667687
skfield = NULL;
668-
if (!ASN1_item_ex_d2i(&skfield, &p, len,
669-
ASN1_ITEM_ptr(tt->item), -1, 0, 0, ctx)) {
688+
if (!asn1_item_ex_d2i(&skfield, &p, len, ASN1_ITEM_ptr(tt->item),
689+
-1, 0, 0, ctx, depth)) {
670690
ASN1err(ASN1_F_ASN1_TEMPLATE_NOEXP_D2I,
671691
ERR_R_NESTED_ASN1_ERROR);
672692
goto err;
@@ -684,18 +704,18 @@ static int asn1_template_noexp_d2i(ASN1_VALUE **val,
684704
}
685705
} else if (flags & ASN1_TFLG_IMPTAG) {
686706
/* IMPLICIT tagging */
687-
ret = ASN1_item_ex_d2i(val, &p, len,
688-
ASN1_ITEM_ptr(tt->item), tt->tag, aclass, opt,
689-
ctx);
707+
ret = asn1_item_ex_d2i(val, &p, len, ASN1_ITEM_ptr(tt->item), tt->tag,
708+
aclass, opt, ctx, depth);
690709
if (!ret) {
691710
ASN1err(ASN1_F_ASN1_TEMPLATE_NOEXP_D2I, ERR_R_NESTED_ASN1_ERROR);
692711
goto err;
693712
} else if (ret == -1)
694713
return -1;
695714
} else {
696715
/* Nothing special */
697-
ret = ASN1_item_ex_d2i(val, &p, len, ASN1_ITEM_ptr(tt->item),
698-
-1, tt->flags & ASN1_TFLG_COMBINE, opt, ctx);
716+
ret = asn1_item_ex_d2i(val, &p, len, ASN1_ITEM_ptr(tt->item),
717+
-1, tt->flags & ASN1_TFLG_COMBINE, opt, ctx,
718+
depth);
699719
if (!ret) {
700720
ASN1err(ASN1_F_ASN1_TEMPLATE_NOEXP_D2I, ERR_R_NESTED_ASN1_ERROR);
701721
goto err;

0 commit comments

Comments
 (0)