--- sys/src/libsec/port/x509.c
+++ sys/src/libsec/port/x509.c
@@ -151,14 +151,23 @@ static mpint *asn1mpint(Elem *e);
#define CLASS_MASK 0xC0
#define MAXOBJIDLEN 20
-static int ber_decode(uchar** pp, uchar* pend, Elem* pelem);
+/*
+ * The decoders below recurse over attacker-supplied nesting, so bound
+ * the descent. Each SEQUENCE or SET level costs two, each constructed
+ * string level one.
+ */
+enum {
+ MAXDEPTH = 32,
+};
+
+static int ber_decode(uchar** pp, uchar* pend, Elem* pelem, int depth);
static int tag_decode(uchar** pp, uchar* pend, Tag* ptag, int* pisconstr);
static int length_decode(uchar** pp, uchar* pend, int* plength);
-static int value_decode(uchar** pp, uchar* pend, int length, int kind, int isconstr, Value* pval);
+static int value_decode(uchar** pp, uchar* pend, int length, int kind, int isconstr, Value* pval, int depth);
static int int_decode(uchar** pp, uchar* pend, int count, int unsgned, int* pint);
static int uint7_decode(uchar** pp, uchar* pend, int* pint);
-static int octet_decode(uchar** pp, uchar* pend, int length, int isconstr, Bytes** pbytes);
-static int seq_decode(uchar** pp, uchar* pend, int length, int isconstr, Elist** pelist);
+static int octet_decode(uchar** pp, uchar* pend, int length, int isconstr, Bytes** pbytes, int depth);
+static int seq_decode(uchar** pp, uchar* pend, int length, int isconstr, Elist** pelist, int depth);
static int enc(uchar** pp, Elem e, int lenonly);
static int val_enc(uchar** pp, Elem e, int *pconstr, int lenonly);
static void uint7_enc(uchar** pp, int num, int lenonly);
@@ -204,7 +213,7 @@ decode(uchar* a, int alen, Elem* pelem)
{
uchar* p = a;
- return ber_decode(&p, &a[alen], pelem);
+ return ber_decode(&p, &a[alen], pelem, 0);
}
/*
@@ -216,7 +225,7 @@ decode_seq(uchar* a, int alen, Elist** pelist)
{
uchar* p = a;
- return seq_decode(&p, &a[alen], -1, 1, pelist);
+ return seq_decode(&p, &a[alen], -1, 1, pelist, 0);
}
/*
@@ -233,7 +242,7 @@ decode_value(uchar* a, int alen, int kind, int isconst
{
uchar* p = a;
- return value_decode(&p, &a[alen], alen, kind, isconstr, pval);
+ return value_decode(&p, &a[alen], alen, kind, isconstr, pval, 0);
}
/*
@@ -251,7 +260,7 @@ static int
/* Decode an ASN1 'Elem' (tag, length, value) */
static int
-ber_decode(uchar** pp, uchar* pend, Elem* pelem)
+ber_decode(uchar** pp, uchar* pend, Elem* pelem, int depth)
{
int err;
int isconstr;
@@ -260,17 +269,19 @@ ber_decode(uchar** pp, uchar* pend, Elem* pelem)
Value val;
uchar* start;
+ if(depth > MAXDEPTH)
+ return ASN_ETOOBIG;
start = *pp;
err = tag_decode(pp, pend, &tag, &isconstr);
if(err == ASN_OK) {
err = length_decode(pp, pend, &length);
if(err == ASN_OK) {
if(tag.class == Universal) {
- err = value_decode(pp, pend, length, tag.num, isconstr, &val);
+ err = value_decode(pp, pend, length, tag.num, isconstr, &val, depth);
if(val.tag == VSeq || val.tag == VSet)
setmalloctag(val.u.seqval, getcallerpc(&pp));
}else
- err = value_decode(pp, pend, length, OCTET_STRING, 0, &val);
+ err = value_decode(pp, pend, length, OCTET_STRING, 0, &val, depth);
if(err == ASN_OK) {
pelem->tag = tag;
pelem->val = val;
@@ -338,7 +349,7 @@ static int
/* Decode a value field */
static int
-value_decode(uchar** pp, uchar* pend, int length, int kind, int isconstr, Value* pval)
+value_decode(uchar** pp, uchar* pend, int length, int kind, int isconstr, Value* pval, int depth)
{
int err;
Bytes* va;
@@ -435,7 +446,7 @@ value_decode(uchar** pp, uchar* pend, int length, int
case OCTET_STRING:
case ObjectDescriptor:
- err = octet_decode(&p, pend, length, isconstr, &va);
+ err = octet_decode(&p, pend, length, isconstr, &va, depth+1);
if(err == ASN_OK) {
pval->tag = VOctets;
pval->u.octetsval = va;
@@ -507,7 +518,7 @@ value_decode(uchar** pp, uchar* pend, int length, int
break;
case SEQUENCE:
- err = seq_decode(&p, pend, length, isconstr, &vl);
+ err = seq_decode(&p, pend, length, isconstr, &vl, depth+1);
setmalloctag(vl, getcallerpc(&pp));
if(err == ASN_OK) {
pval->tag = VSeq ;
@@ -516,7 +527,7 @@ value_decode(uchar** pp, uchar* pend, int length, int
break;
case SETOF:
- err = seq_decode(&p, pend, length, isconstr, &vl);
+ err = seq_decode(&p, pend, length, isconstr, &vl, depth+1);
setmalloctag(vl, getcallerpc(&pp));
if(err == ASN_OK) {
pval->tag = VSet;
@@ -537,7 +548,7 @@ value_decode(uchar** pp, uchar* pend, int length, int
case UniversalString:
case BMPString:
/* TODO: figure out when character set conversion is necessary */
- err = octet_decode(&p, pend, length, isconstr, &va);
+ err = octet_decode(&p, pend, length, isconstr, &va, depth+1);
if(err == ASN_OK) {
pval->tag = VString;
pval->u.stringval = (char*)emalloc(va->len+1);
@@ -640,7 +651,7 @@ static int
* and otherwise that specified length fits within (*pp..pend)
*/
static int
-octet_decode(uchar** pp, uchar* pend, int length, int isconstr, Bytes** pbytes)
+octet_decode(uchar** pp, uchar* pend, int length, int isconstr, Bytes** pbytes, int depth)
{
int err;
uchar* p;
@@ -667,12 +678,13 @@ octet_decode(uchar** pp, uchar* pend, int length, int
break;
}
pold = p;
- err = ber_decode(&p, pend, &elem);
+ err = ber_decode(&p, pend, &elem, depth);
if(err != ASN_OK)
break;
switch(elem.val.tag) {
case VOctets:
newans = catbytes(ans, elem.val.u.octetsval);
+ freevalfields(&elem.val);
freebytes(ans);
ans = newans;
break;
@@ -685,13 +697,17 @@ octet_decode(uchar** pp, uchar* pend, int length, int
goto cloop_done;
default:
+ freevalfields(&elem.val);
p = pold;
err = ASN_EINVAL;
goto cloop_done;
}
}
cloop_done:
- ;
+ if(err != ASN_OK) {
+ freebytes(ans);
+ ans = nil;
+ }
}
/* callers read va->len at once; an empty string is a Bytes of
* length zero, not nil */
@@ -708,7 +724,7 @@ static int
* and otherwise that specified length fits within (*p..pend)
*/
static int
-seq_decode(uchar** pp, uchar* pend, int length, int isconstr, Elist** pelist)
+seq_decode(uchar** pp, uchar* pend, int length, int isconstr, Elist** pelist, int depth)
{
int err;
uchar* p;
@@ -718,6 +734,7 @@ seq_decode(uchar** pp, uchar* pend, int length, int is
Elem elem;
Elist* lve;
Elist* lveold;
+ Elist* l;
err = ASN_OK;
ans = nil;
@@ -735,7 +752,7 @@ seq_decode(uchar** pp, uchar* pend, int length, int is
break;
}
pold = p;
- err = ber_decode(&p, pend, &elem);
+ err = ber_decode(&p, pend, &elem, depth+1);
if(err != ASN_OK)
break;
if(elem.val.tag == VEOC) {
@@ -757,6 +774,11 @@ seq_decode(uchar** pp, uchar* pend, int length, int is
ans = lveold;
}
}
+ else {
+ for(l = lve; l != nil; l = l->tl)
+ freevalfields(&l->hd.val);
+ freeelist(lve);
+ }
}
*pp = p;
*pelist = ans;
@@ -2803,7 +2825,7 @@ digest_certinfo(Bytes *cert, DigestFun digestfun, ucha
p+length < p)
return;
info = p;
- if(ber_decode(&p, pend, &elem) != ASN_OK)
+ if(ber_decode(&p, pend, &elem, 0) != ASN_OK)
return;
freevalfields(&elem.val);
if(elem.tag.num != SEQUENCE)
|