From: Daniel Sands Date: Thu, 12 Mar 2026 17:59:13 +0000 (-0600) Subject: Add intelligence to asn1_d2i_read_bio for reading entire header without blocking... X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=35852da1d9e24cb74034b2f418cef3a58203b127;p=thirdparty%2Fopenssl.git Add intelligence to asn1_d2i_read_bio for reading entire header without blocking for extra data Reviewed-by: Tomas Mraz Reviewed-by: Dmitry Belyavskiy (Merged from https://github.com/openssl/openssl/pull/30401) --- diff --git a/crypto/asn1/a_d2i_fp.c b/crypto/asn1/a_d2i_fp.c index 4204743cd23..629d5170280 100644 --- a/crypto/asn1/a_d2i_fp.c +++ b/crypto/asn1/a_d2i_fp.c @@ -104,7 +104,7 @@ void *ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x) } #endif -#define HEADER_SIZE 8 +#define HEADER_SIZE 2 #define ASN1_CHUNK_INITIAL_SIZE (16 * 1024) int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb) { @@ -140,7 +140,7 @@ int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb) } i = BIO_read(in, &(b->data[len]), (int)want); - if (i <= 0 && diff == 0) { + if (i <= 0) { ERR_raise(ERR_LIB_ASN1, ASN1_R_NOT_ENOUGH_DATA); goto err; } @@ -157,11 +157,57 @@ int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb) } /* else data already loaded */ + /* make sure there is enough data for a complete header */ p = (unsigned char *)&(b->data[off]); q = p; diff = len - off; - if (diff == 0) + if (diff < 2) { + /* Failed sanity check */ + ERR_raise(ERR_LIB_ASN1, ASN1_R_NOT_ENOUGH_DATA); goto err; + } + + diff--; + if ((*(q++) & V_ASN1_PRIMITIVE_TAG) == V_ASN1_PRIMITIVE_TAG) { + /* Multi-byte tag. See if we have the whole thing yet */ + do { + diff--; + } while (diff > 0 && *(q++) & 0x80); + + if (diff == 0) { + /* + * End of current data, will need at least 1 more byte for + * length. 2 if the tag is still incomplete + */ + want = q - p + 2; + if (*q & 0x80) { + want++; + } + continue; + } + } + + /* Check the length. This should also work for indefinite length */ + diff--; + if (*q & 0x80) { + unsigned int i = *q & 0x7f; + + if (i > sizeof(long)) { + ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_LONG); + goto err; + } + if (i > diff) { + want = q - p + i + 1; + continue; + } + } + + /* + * We have a complete header now, assuming we didn't hit EOF. Parse the + * tag and length + */ + q = p; + diff = len - off; inf = ASN1_get_object(&q, &slen, &tag, &xclass, (int)diff); if (inf & 0x80) { unsigned long e; diff --git a/crypto/asn1/asn1_lib.c b/crypto/asn1/asn1_lib.c index e122afd92de..4d61dfca549 100644 --- a/crypto/asn1/asn1_lib.c +++ b/crypto/asn1/asn1_lib.c @@ -129,7 +129,7 @@ static int asn1_get_length(const unsigned char **pp, int *inf, long *rl, *inf = 0; i = *p & 0x7f; if (*p++ & 0x80) { - if (max < i + 1) + if (max < i) return 0; /* Skip leading zeroes */ while (i > 0 && *p == 0) {