]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Add intelligence to asn1_d2i_read_bio for reading entire header without blocking...
authorDaniel Sands <dnsands@sandia.gov>
Thu, 12 Mar 2026 17:59:13 +0000 (11:59 -0600)
committerDmitry Belyavskiy <beldmit@gmail.com>
Fri, 24 Apr 2026 14:27:09 +0000 (16:27 +0200)
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
(Merged from https://github.com/openssl/openssl/pull/30401)

crypto/asn1/a_d2i_fp.c
crypto/asn1/asn1_lib.c

index 4204743cd23dab5d56468a650ef719c351671a03..629d517028050cf26cf901750f52f93b2faac411 100644 (file)
@@ -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;
index e122afd92de56b845897e3a1ec0db2febcf2779f..4d61dfca5497be90f58506993a09b949d37db059 100644 (file)
@@ -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) {