]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[crypto] Extend asn1_enter() to handle partial object cursors
authorMichael Brown <mcb30@ipxe.org>
Wed, 7 Aug 2024 12:18:47 +0000 (13:18 +0100)
committerMichael Brown <mcb30@ipxe.org>
Wed, 7 Aug 2024 15:26:19 +0000 (16:26 +0100)
Handling large ASN.1 objects such as encrypted CMS files will require
the ability to use the asn1_enter() and asn1_skip() family of
functions on partial object cursors, where a defined additional length
is known to exist after the end of the data buffer pointed to by the
ASN.1 object cursor.

We already have support for partial object cursors in the underlying
asn1_start() operation used by both asn1_enter() and asn1_skip(), and
this is used by the DER image probe routine to check that the
potential DER file comprises a single ASN.1 SEQUENCE object.

Add asn1_enter_partial() to formalise the process of entering an ASN.1
partial object, and refactor the DER image probe routine to use this
instead of open-coding calls to the underlying asn1_start() operation.

There is no need for an equivalent asn1_skip_partial() function, since
only objects that are wholly contained within the partial cursor may
be successfully skipped.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/crypto/asn1.c
src/image/der.c
src/include/ipxe/asn1.h

index 90619dc0ec6aafc7189f6a77d048412bcb19fe0e..e66da45b945cb8c522085bf6521b599384100a91 100644 (file)
@@ -99,7 +99,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  * modified.  If any other error occurs, the object cursor will be
  * invalidated.
  */
-int asn1_start ( struct asn1_cursor *cursor, unsigned int type, size_t extra ) {
+static int asn1_start ( struct asn1_cursor *cursor, unsigned int type,
+                       size_t extra ) {
        unsigned int len_len;
        unsigned int len;
 
@@ -154,33 +155,58 @@ int asn1_start ( struct asn1_cursor *cursor, unsigned int type, size_t extra ) {
 }
 
 /**
- * Enter ASN.1 object
+ * Enter ASN.1 partial object
  *
  * @v cursor           ASN.1 object cursor
  * @v type             Expected type, or ASN1_ANY
+ * @v extra            Additional length beyond partial object
  * @ret rc             Return status code
  *
- * The object cursor will be updated to point to the body of the
- * current ASN.1 object.
+ * The object cursor and additional length will be updated to point to
+ * the body of the current ASN.1 object.
  *
  * If any error occurs, the object cursor will be invalidated.
  */
-int asn1_enter ( struct asn1_cursor *cursor, unsigned int type ) {
+int asn1_enter_partial ( struct asn1_cursor *cursor, unsigned int type,
+                        size_t *extra ) {
        int len;
 
-       len = asn1_start ( cursor, type, 0 );
+       /* Parse current object */
+       len = asn1_start ( cursor, type, *extra );
        if ( len < 0 ) {
                asn1_invalidate_cursor ( cursor );
                return len;
        }
 
-       cursor->len = len;
+       /* Update cursor and additional length */
+       if ( ( ( size_t ) len ) <= cursor->len )
+               cursor->len = len;
+       assert ( ( len - cursor->len ) <= *extra );
+       *extra = ( len - cursor->len );
+
        DBGC ( cursor, "ASN1 %p entered object type %02x (len %x)\n",
               cursor, type, len );
-
        return 0;
 }
 
+/**
+ * Enter ASN.1 object
+ *
+ * @v cursor           ASN.1 object cursor
+ * @v type             Expected type, or ASN1_ANY
+ * @ret rc             Return status code
+ *
+ * The object cursor will be updated to point to the body of the
+ * current ASN.1 object.
+ *
+ * If any error occurs, the object cursor will be invalidated.
+ */
+int asn1_enter ( struct asn1_cursor *cursor, unsigned int type ) {
+       static size_t no_extra = 0;
+
+       return asn1_enter_partial ( cursor, type, &no_extra );
+}
+
 /**
  * Skip ASN.1 object if present
  *
@@ -198,15 +224,17 @@ int asn1_enter ( struct asn1_cursor *cursor, unsigned int type ) {
 int asn1_skip_if_exists ( struct asn1_cursor *cursor, unsigned int type ) {
        int len;
 
+       /* Parse current object */
        len = asn1_start ( cursor, type, 0 );
        if ( len < 0 )
                return len;
 
+       /* Update cursor */
        cursor->data += len;
        cursor->len -= len;
+
        DBGC ( cursor, "ASN1 %p skipped object type %02x (len %x)\n",
               cursor, type, len );
-
        return 0;
 }
 
index fa17e5659f55b2af48a6efc510a303440453656a..9d31c253b3aae04e6490caa354eb8d15f7b72e31 100644 (file)
@@ -76,8 +76,6 @@ static int der_probe ( struct image *image ) {
        struct asn1_cursor cursor;
        uint8_t buf[8];
        size_t extra;
-       size_t total;
-       int len;
        int rc;
 
        /* Sanity check: no realistic DER image can be smaller than this */
@@ -90,21 +88,16 @@ static int der_probe ( struct image *image ) {
        copy_from_user ( buf, image->data, 0, sizeof ( buf ) );
        extra = ( image->len - sizeof ( buf ) );
 
-       /* Get length of ASN.1 sequence */
-       len = asn1_start ( &cursor, ASN1_SEQUENCE, extra );
-       if ( len < 0 ) {
-               rc = len;
+       /* Check that image begins with an ASN.1 sequence object */
+       if ( ( rc = asn1_enter_partial ( &cursor, ASN1_SEQUENCE,
+                                        &extra ) ) != 0 ) {
                DBGC ( image, "DER %s is not valid ASN.1: %s\n",
                       image->name, strerror ( rc ) );
                return rc;
        }
 
-       /* Add length of tag and length bytes consumed by asn1_start() */
-       total = ( len + ( cursor.data - ( ( void * ) buf ) ) );
-       assert ( total <= image->len );
-
        /* Check that image comprises a single well-formed ASN.1 object */
-       if ( total != image->len ) {
+       if ( extra != ( image->len - sizeof ( buf ) ) ) {
                DBGC ( image, "DER %s is not single ASN.1\n", image->name );
                return -ENOEXEC;
        }
index ac7ea5604d3b8325787e6a7793b5328973fde04c..1580c8baf115212420e82ff17974c8d7e41b78ab 100644 (file)
@@ -404,8 +404,8 @@ asn1_built ( struct asn1_builder *builder ) {
        return &u->cursor;
 }
 
-extern int asn1_start ( struct asn1_cursor *cursor, unsigned int type,
-                       size_t extra );
+extern int asn1_enter_partial ( struct asn1_cursor *cursor, unsigned int type,
+                               size_t *extra );
 extern int asn1_enter ( struct asn1_cursor *cursor, unsigned int type );
 extern int asn1_skip_if_exists ( struct asn1_cursor *cursor,
                                 unsigned int type );