shithub: riscv

Download patch

ref: 469438f4dfe9f97ab7d7a59c3e9c80b99ef4be7f
parent: d09e6e509b73961bbed6bdde5b718acdbdecd13d
author: Ori Bernstein <ori@eigenstate.org>
date: Mon Apr 21 21:44:43 EDT 2025

libsec: add recursion limit for x509 structures

We used to allow unbounded recursion for X509 certs,
which could lead to a stack overflow and crash for
invalid certs; this adds a recursion limit to avoid
things blowing up.

--- a/sys/src/libsec/port/x509.c
+++ b/sys/src/libsec/port/x509.c
@@ -143,16 +143,17 @@
 
 enum {
 	Domlen = 256,
+	Maxdepth = 32,
 };
 
-static int ber_decode(uchar** pp, uchar* pend, Elem* pelem);
+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);
@@ -197,7 +198,7 @@
 	uchar* p = a;
 	int err;
 
-	err = ber_decode(&p, &a[alen], pelem);
+	err = ber_decode(&p, &a[alen], pelem, 0);
 	if(err == ASN_OK && p != &a[alen])
 		err = ASN_EVALLEN;
 	return err;
@@ -218,7 +219,7 @@
 
 /* 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;
@@ -226,6 +227,8 @@
 	Tag tag;
 	Value val;
 
+	if(depth > Maxdepth)
+		return ASN_ETOOBIG;
 	memset(pelem, 0, sizeof(*pelem));
 	err = tag_decode(pp, pend, &tag, &isconstr);
 	if(err == ASN_OK) {
@@ -232,9 +235,9 @@
 		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);
 			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;
@@ -300,7 +303,7 @@
 
 /* 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;
@@ -396,7 +399,7 @@
 
 	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;
@@ -468,7 +471,7 @@
 		break;
 
 	case SEQUENCE:
-		err = seq_decode(&p, pend, length, isconstr, &vl);
+		err = seq_decode(&p, pend, length, isconstr, &vl, depth+1);
 		if(err == ASN_OK) {
 			pval->tag = VSeq ;
 			pval->u.seqval = vl;
@@ -476,7 +479,7 @@
 		break;
 
 	case SETOF:
-		err = seq_decode(&p, pend, length, isconstr, &vl);
+		err = seq_decode(&p, pend, length, isconstr, &vl, depth+1);
 		if(err == ASN_OK) {
 			pval->tag = VSet;
 			pval->u.setval = vl;
@@ -496,7 +499,7 @@
 	case GeneralString:
 	case UniversalString:
 	case BMPString:
-		err = octet_decode(&p, pend, length, isconstr, &va);
+		err = octet_decode(&p, pend, length, isconstr, &va, depth+1);
 		if(err == ASN_OK) {
 			uchar *s;
 			char *d;
@@ -649,7 +652,7 @@
  * 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;
@@ -676,7 +679,7 @@
 				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) {
@@ -715,7 +718,7 @@
  * 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;
@@ -742,7 +745,7 @@
 				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) {
@@ -2120,7 +2123,7 @@
 	   p+length < p)
 		return -1;
 	info = p;
-	if(ber_decode(&p, pend, &elem) != ASN_OK)
+	if(ber_decode(&p, pend, &elem, 0) != ASN_OK)
 		return -1;
 	freevalfields(&elem.val);
 	if(elem.tag.num != SEQUENCE)
--