ASN.1: Handle 'ANY OPTIONAL' in grammar
authorDavid Howells <dhowells@redhat.com>
Wed, 5 Aug 2015 11:54:46 +0000 (12:54 +0100)
committerDavid Howells <dhowells@redhat.com>
Wed, 5 Aug 2015 12:38:07 +0000 (13:38 +0100)
An ANY object in an ASN.1 grammar that is marked OPTIONAL should be skipped
if there is no more data to be had.

This can be tested by editing X.509 certificates or PKCS#7 messages to
remove the NULL from subobjects that look like the following:

SEQUENCE {
  OBJECT(2a864886f70d01010b);
  NULL();
}

This is an algorithm identifier plus an optional parameter.

The modified DER can be passed to one of:

keyctl padd asymmetric "" @s </tmp/modified.x509
keyctl padd pkcs7_test foo @s </tmp/modified.pkcs7

It should work okay with the patch and produce EBADMSG without.

Signed-off-by: David Howells <dhowells@redhat.com>
Tested-by: Marcel Holtmann <marcel@holtmann.org>
Reviewed-by: David Woodhouse <David.Woodhouse@intel.com>
include/linux/asn1_ber_bytecode.h
lib/asn1_decoder.c
scripts/asn1_compiler.c

index 27f35780aecf3de525a6a52d47f80aae2718cabd..ab3a6c002f7b9d2e265b08061d78e7f260b13633 100644 (file)
@@ -45,24 +45,27 @@ enum asn1_opcode {
        ASN1_OP_MATCH_JUMP              = 0x04,
        ASN1_OP_MATCH_JUMP_OR_SKIP      = 0x05,
        ASN1_OP_MATCH_ANY               = 0x08,
+       ASN1_OP_MATCH_ANY_OR_SKIP       = 0x09,
        ASN1_OP_MATCH_ANY_ACT           = 0x0a,
+       ASN1_OP_MATCH_ANY_ACT_OR_SKIP   = 0x0b,
        /* Everything before here matches unconditionally */
 
        ASN1_OP_COND_MATCH_OR_SKIP      = 0x11,
        ASN1_OP_COND_MATCH_ACT_OR_SKIP  = 0x13,
        ASN1_OP_COND_MATCH_JUMP_OR_SKIP = 0x15,
        ASN1_OP_COND_MATCH_ANY          = 0x18,
+       ASN1_OP_COND_MATCH_ANY_OR_SKIP  = 0x19,
        ASN1_OP_COND_MATCH_ANY_ACT      = 0x1a,
+       ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP = 0x1b,
 
        /* Everything before here will want a tag from the data */
-#define ASN1_OP__MATCHES_TAG ASN1_OP_COND_MATCH_ANY_ACT
+#define ASN1_OP__MATCHES_TAG ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP
 
        /* These are here to help fill up space */
-       ASN1_OP_COND_FAIL               = 0x1b,
-       ASN1_OP_COMPLETE                = 0x1c,
-       ASN1_OP_ACT                     = 0x1d,
-       ASN1_OP_MAYBE_ACT               = 0x1e,
-       ASN1_OP_RETURN                  = 0x1f,
+       ASN1_OP_COND_FAIL               = 0x1c,
+       ASN1_OP_COMPLETE                = 0x1d,
+       ASN1_OP_ACT                     = 0x1e,
+       ASN1_OP_MAYBE_ACT               = 0x1f,
 
        /* The following eight have bit 0 -> SET, 1 -> OF, 2 -> ACT */
        ASN1_OP_END_SEQ                 = 0x20,
@@ -77,6 +80,8 @@ enum asn1_opcode {
 #define ASN1_OP_END__OF                          0x02
 #define ASN1_OP_END__ACT                 0x04
 
+       ASN1_OP_RETURN                  = 0x28,
+
        ASN1_OP__NR
 };
 
index 3f74dd3e29107fa407df14cc8a8b0cf05087e9eb..2b3f46c049d458a590d080823b344da3b3229f7c 100644 (file)
@@ -24,12 +24,16 @@ static const unsigned char asn1_op_lengths[ASN1_OP__NR] = {
        [ASN1_OP_MATCH_JUMP]                    = 1 + 1 + 1,
        [ASN1_OP_MATCH_JUMP_OR_SKIP]            = 1 + 1 + 1,
        [ASN1_OP_MATCH_ANY]                     = 1,
+       [ASN1_OP_MATCH_ANY_OR_SKIP]             = 1,
        [ASN1_OP_MATCH_ANY_ACT]                 = 1         + 1,
+       [ASN1_OP_MATCH_ANY_ACT_OR_SKIP]         = 1         + 1,
        [ASN1_OP_COND_MATCH_OR_SKIP]            = 1 + 1,
        [ASN1_OP_COND_MATCH_ACT_OR_SKIP]        = 1 + 1     + 1,
        [ASN1_OP_COND_MATCH_JUMP_OR_SKIP]       = 1 + 1 + 1,
        [ASN1_OP_COND_MATCH_ANY]                = 1,
+       [ASN1_OP_COND_MATCH_ANY_OR_SKIP]        = 1,
        [ASN1_OP_COND_MATCH_ANY_ACT]            = 1         + 1,
+       [ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP]    = 1         + 1,
        [ASN1_OP_COND_FAIL]                     = 1,
        [ASN1_OP_COMPLETE]                      = 1,
        [ASN1_OP_ACT]                           = 1         + 1,
@@ -304,7 +308,9 @@ next_op:
        /* Decide how to handle the operation */
        switch (op) {
        case ASN1_OP_MATCH_ANY_ACT:
+       case ASN1_OP_MATCH_ANY_ACT_OR_SKIP:
        case ASN1_OP_COND_MATCH_ANY_ACT:
+       case ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP:
                ret = actions[machine[pc + 1]](context, hdr, tag, data + dp, len);
                if (ret < 0)
                        return ret;
@@ -321,8 +327,10 @@ next_op:
        case ASN1_OP_MATCH:
        case ASN1_OP_MATCH_OR_SKIP:
        case ASN1_OP_MATCH_ANY:
+       case ASN1_OP_MATCH_ANY_OR_SKIP:
        case ASN1_OP_COND_MATCH_OR_SKIP:
        case ASN1_OP_COND_MATCH_ANY:
+       case ASN1_OP_COND_MATCH_ANY_OR_SKIP:
        skip_data:
                if (!(flags & FLAG_CONS)) {
                        if (flags & FLAG_INDEFINITE_LENGTH) {
index 0515bced929a48198a0b96ba6350853dd27aaa23..1c75e22b6385fe77bf0488b705e5c1de7476d101 100644 (file)
@@ -1401,7 +1401,8 @@ static void render_element(FILE *out, struct element *e, struct element *tag)
        act = e->action ? "_ACT" : "";
        switch (e->compound) {
        case ANY:
-               render_opcode(out, "ASN1_OP_%sMATCH_ANY%s,", cond, act);
+               render_opcode(out, "ASN1_OP_%sMATCH_ANY%s%s,",
+                             cond, act, skippable ? "_OR_SKIP" : "");
                if (e->name)
                        render_more(out, "\t\t// %*.*s",
                                    (int)e->name->size, (int)e->name->size,