diff --git a/lib/asn1/canthandle.asn1 b/lib/asn1/canthandle.asn1 index 0e0822a9e..6da1de5da 100644 --- a/lib/asn1/canthandle.asn1 +++ b/lib/asn1/canthandle.asn1 @@ -2,13 +2,6 @@ CANTHANDLE DEFINITIONS ::= BEGIN --- Can't handle tags larger than 30 because while we encode large tags --- correctly, we don't account for more than one byte of them in the length --- functions. The compiler shouldn't crash, but the code generate will be --- incorrect and should crash. - -Foo31 ::= SEQUENCE { foo [31] INTEGER } - -- Can't handle primitives in SET OF, causing the compiler to crash -- Workaround is to define a type that is only an integer and use that diff --git a/lib/asn1/check-gen.c b/lib/asn1/check-gen.c index b5d8dbd44..82a129fe4 100644 --- a/lib/asn1/check-gen.c +++ b/lib/asn1/check-gen.c @@ -1016,7 +1016,6 @@ test_choice (void) return ret; } -#ifdef IMPLICIT_TAGGING_WORKS static int cmp_TESTImplicit (void *a, void *b) { @@ -1028,7 +1027,20 @@ cmp_TESTImplicit (void *a, void *b) COMPARE_INTEGER(aa,ab,ti3); return 0; } -#endif + +static int +cmp_TESTImplicit2 (void *a, void *b) +{ + TESTImplicit2 *aa = a; + TESTImplicit2 *ab = b; + + COMPARE_INTEGER(aa,ab,ti1); + COMPARE_INTEGER(aa,ab,ti3); + IF_OPT_COMPARE(aa,ab,ti4) { + COMPARE_INTEGER(aa,ab,ti4[0]); + } + return 0; +} /* UNIV CONS Sequence 14 @@ -1043,16 +1055,20 @@ static int test_implicit (void) { int ret = 0; -#ifdef IMPLICIT_TAGGING_WORKS struct test_case tests[] = { - { NULL, 18, - "\x30\x10\x80\x01\x00\xa1\x06\xbf" - "\x7f\x03\x02\x01\x02\xa2\x03\x84\x01\x03", + { NULL, 16, + "\x30\x0e\x80\x01\x00\xa1\x06\xbf\x7f\x03\x02\x01\x02\x82\x01\x03", "implicit 1" } }; + struct test_case tests2[] = { + { NULL, 12, + "\x30\x0a\x80\x01\x01\x82\x01\x03\x9f\x33\x01\x04", + "implicit 2" } + }; - int ntests = sizeof(tests) / sizeof(*tests); TESTImplicit c0; + TESTImplicit2 c1; + int ti4 = 4; memset(&c0, 0, sizeof(c0)); c0.ti1 = 0; @@ -1060,23 +1076,32 @@ test_implicit (void) c0.ti3 = 3; tests[0].val = &c0; - ret += generic_test (tests, ntests, sizeof(TESTImplicit), - (generic_encode)encode_TESTImplicit, - (generic_length)length_TESTImplicit, - (generic_decode)decode_TESTImplicit, - (generic_free)free_TESTImplicit, - cmp_TESTImplicit, - (generic_copy)copy_TESTImplicit); + memset(&c1, 0, sizeof(c1)); + c1.ti1 = 1; + c1.ti3 = 3; + c1.ti4 = &ti4; + tests2[0].val = &c1; - ret += generic_test (tests, ntests, sizeof(TESTImplicit2), - (generic_encode)encode_TESTImplicit2, - (generic_length)length_TESTImplicit2, - (generic_decode)decode_TESTImplicit2, - (generic_free)free_TESTImplicit2, - cmp_TESTImplicit, - NULL); + ret += generic_test(tests, + sizeof(tests) / sizeof(*tests), + sizeof(TESTImplicit), + (generic_encode)encode_TESTImplicit, + (generic_length)length_TESTImplicit, + (generic_decode)decode_TESTImplicit, + (generic_free)free_TESTImplicit, + cmp_TESTImplicit, + (generic_copy)copy_TESTImplicit); + + ret += generic_test(tests2, + sizeof(tests2) / sizeof(*tests2), + sizeof(TESTImplicit2), + (generic_encode)encode_TESTImplicit2, + (generic_length)length_TESTImplicit2, + (generic_decode)decode_TESTImplicit2, + (generic_free)free_TESTImplicit2, + cmp_TESTImplicit2, + NULL); -#endif /* IMPLICIT_TAGGING_WORKS */ return ret; } @@ -1502,7 +1527,6 @@ check_TESTMechTypeList(void) return 0; } -#ifdef IMPLICIT_TAGGING_WORKS static int cmp_TESTSeqOf4(void *a, void *b) { @@ -1545,13 +1569,11 @@ cmp_TESTSeqOf4(void *a, void *b) } return 0; } -#endif /* IMPLICIT_TAGGING_WORKS */ static int test_seq4 (void) { int ret = 0; -#ifdef IMPLICIT_TAGGING_WORKS struct test_case tests[] = { { NULL, 2, "\x30\x00", @@ -1674,7 +1696,6 @@ test_seq4 (void) (generic_free)free_TESTSeqOf4, cmp_TESTSeqOf4, (generic_copy)copy_TESTSeqOf4); -#endif /* IMPLICIT_TAGGING_WORKS */ return ret; } @@ -1775,7 +1796,11 @@ test_seqof5(void) static int test_x690sample(void) { - /* Taken from X.690, Appendix A */ + /* + * Taken from X.690, Appendix A, though sadly it's not specified whether + * it's in BER, DER, or CER, but it turns out to be DER since, as you can + * see below, we re-encode and get the same encoding back. + */ X690SamplePersonnelRecord r; heim_octet_string os; unsigned char encoded_sample[] = { @@ -1803,6 +1828,7 @@ test_x690sample(void) free_X690SamplePersonnelRecord(&r); memset(&r, 0, sizeof(r)); + /* We re-construct the record manually to double-check the spec */ r.name.givenName = strdup("John"); r.name.initial = strdup("P"); r.name.familyName = strdup("Smith"); @@ -1839,40 +1865,41 @@ main(int argc, char **argv) { int ret = 0; - ret += test_principal (); - ret += test_authenticator(); - ret += test_krb_error(); - ret += test_Name(); - ret += test_bit_string(); - ret += test_bit_string_rfc1510(); - ret += test_time(); - ret += test_cert(); +#define DO_ONE(t) if (t()) { fprintf(stderr, "%s() failed!\n", #t); ret++; } + DO_ONE(test_principal); + DO_ONE(test_authenticator); + DO_ONE(test_krb_error); + DO_ONE(test_Name); + DO_ONE(test_bit_string); + DO_ONE(test_bit_string_rfc1510); + DO_ONE(test_time); + DO_ONE(test_cert); - ret += check_tag_length(); - ret += check_tag_length64(); - ret += check_tag_length64s(); - ret += test_large_tag(); - ret += test_choice(); + DO_ONE(check_tag_length); + DO_ONE(check_tag_length64); + DO_ONE(check_tag_length64s); + DO_ONE(test_large_tag); + DO_ONE(test_choice); - ret += test_implicit(); + DO_ONE(test_implicit); - ret += test_taglessalloc(); - ret += test_optional(); + DO_ONE(test_taglessalloc); + DO_ONE(test_optional); - ret += check_fail_largetag(); - ret += check_fail_sequence(); - ret += check_fail_choice(); - ret += check_fail_Ticket(); + DO_ONE(check_fail_largetag); + DO_ONE(check_fail_sequence); + DO_ONE(check_fail_choice); + DO_ONE(check_fail_Ticket); - ret += check_seq(); - ret += check_seq_of_size(); - ret += test_SignedData(); + DO_ONE(check_seq); + DO_ONE(check_seq_of_size); + DO_ONE(test_SignedData); - ret += check_TESTMechTypeList(); - ret += test_seq4(); - ret += test_seqof5(); + DO_ONE(check_TESTMechTypeList); + DO_ONE(test_seq4); + DO_ONE(test_seqof5); - ret += test_x690sample(); + DO_ONE(test_x690sample); return ret; } diff --git a/lib/asn1/cms.asn1 b/lib/asn1/cms.asn1 index ccbe68383..48bb2167d 100644 --- a/lib/asn1/cms.asn1 +++ b/lib/asn1/cms.asn1 @@ -75,12 +75,10 @@ SignerInfo ::= SEQUENCE { version CMSVersion, sid SignerIdentifier, digestAlgorithm DigestAlgorithmIdentifier, - signedAttrs [0] IMPLICIT -- CMSAttributes -- - SET OF Attribute OPTIONAL, + signedAttrs [0] IMPLICIT CMSAttributes OPTIONAL, signatureAlgorithm SignatureAlgorithmIdentifier, signature SignatureValue, - unsignedAttrs [1] IMPLICIT -- CMSAttributes -- - SET OF Attribute OPTIONAL + unsignedAttrs [1] IMPLICIT CMSAttributes OPTIONAL } SignerInfos ::= SET OF SignerInfo @@ -89,18 +87,14 @@ SignedData ::= SEQUENCE { version CMSVersion, digestAlgorithms DigestAlgorithmIdentifiers, encapContentInfo EncapsulatedContentInfo, - certificates [0] IMPLICIT -- CertificateSet -- - SET OF heim_any OPTIONAL, - crls [1] IMPLICIT -- CertificateRevocationLists -- - heim_any OPTIONAL, + certificates [0] IMPLICIT CertificateSet OPTIONAL, + crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, signerInfos SignerInfos } OriginatorInfo ::= SEQUENCE { - certs [0] IMPLICIT -- CertificateSet -- - SET OF heim_any OPTIONAL, - crls [1] IMPLICIT --CertificateRevocationLists -- - heim_any OPTIONAL + certs [0] IMPLICIT CertificateSet OPTIONAL, + crls [1] IMPLICIT CertificateRevocationLists OPTIONAL } KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier @@ -132,17 +126,15 @@ UnprotectedAttributes ::= SET OF Attribute -- SIZE (1..MAX) CMSEncryptedData ::= SEQUENCE { version CMSVersion, encryptedContentInfo EncryptedContentInfo, - unprotectedAttrs [1] IMPLICIT -- UnprotectedAttributes -- - heim_any OPTIONAL + unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL } EnvelopedData ::= SEQUENCE { version CMSVersion, - originatorInfo [0] IMPLICIT -- OriginatorInfo -- heim_any OPTIONAL, + originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL, recipientInfos RecipientInfos, encryptedContentInfo EncryptedContentInfo, - unprotectedAttrs [1] IMPLICIT -- UnprotectedAttributes -- - heim_any OPTIONAL + unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL } -- Data ::= OCTET STRING diff --git a/lib/asn1/der_get.c b/lib/asn1/der_get.c index 14d8f90cd..cb79ff7df 100644 --- a/lib/asn1/der_get.c +++ b/lib/asn1/der_get.c @@ -642,6 +642,15 @@ der_match_tag2 (const unsigned char *p, size_t len, return 0; } +/* + * Returns 0 if the encoded data at `p' of length `len' starts with the tag of + * class `cls`, type `type', and tag value `tag', and puts the length of the + * payload (i.e., the length of V in TLV, not the length of TLV) in + * `*length_ret', and the size of the whole thing (the TLV) in `*size' if + * `size' is not NULL. + * + * Else returns an error. + */ int ASN1CALL der_match_tag_and_length (const unsigned char *p, size_t len, Der_class cls, Der_type *type, unsigned int tag, diff --git a/lib/asn1/der_put.c b/lib/asn1/der_put.c index f3d8ff73d..9a5e7a356 100644 --- a/lib/asn1/der_put.c +++ b/lib/asn1/der_put.c @@ -441,25 +441,102 @@ der_put_oid (unsigned char *p, size_t len, return 0; } +/* + * Output a copy of the DER TLV at `p' with a different outermost tag. + * + * This is used in the implementation of IMPLICIT tags in generated decoder + * functions. + */ int -der_replace_tag(unsigned char *p, size_t len, Der_class class, Der_type type, +der_replace_tag(const unsigned char *p, size_t len, + unsigned char **out, size_t *outlen, + Der_class class, Der_type type, unsigned int tag) { Der_class found_class; Der_type found_type; unsigned int found_tag; - size_t found_size, actual_size; + size_t payload_len, l, tag_len, len_len; int e; - e = der_get_tag(p, len, &found_class, &found_type, &found_tag, - &found_size); - if (e == 0) - e = der_put_tag(p, len, class, type, tag, &actual_size); - if (e == 0 && actual_size != found_size) - e = ASN1_OVERFLOW; + e = der_get_tag(p, len, &found_class, &found_type, &found_tag, &l); + if (e) + return e; + if (found_type != type) + return ASN1_TYPE_MISMATCH; + /* We don't care what found_class and found_tag are though */ + tag_len = der_length_tag(tag); + p += l; + len -= l; + e = der_get_length(p, len, &payload_len, &len_len); + if (e) + return e; + /* + * `p' now points at the payload; `*out' + the length of the tag points at + * where we should copy the DER length and the payload. + */ + if ((*out = malloc(*outlen = tag_len + len_len + payload_len)) == NULL) + return ENOMEM; + memcpy(*out + tag_len, p, len_len + payload_len); + + /* Put the new tag */ + e = der_put_tag(*out + tag_len - 1, tag_len, class, type, tag, &l); + if (e) + return e; + if (l != tag_len) + return ASN1_OVERFLOW; return 0; } +#if 0 +int +der_encode_implicit(unsigned char *p, size_t len, + asn1_generic_encoder_f encoder, + void *obj, size_t *size, + Der_type type, + unsigned int ttag, Der_class iclass, unsigned int itag) +{ + size_t ttaglen = der_length_tag(ttag); + size_t itaglen = der_length_tag(itag); + size_t l; + unsigned char *p2; + int e; + + /* Attempt to encode in place */ + e = encoder(p, len, obj, size); + if (e == 0) { + /* Fits! Rewrite tag, adjust reported size. */ + e = der_put_tag(p + ttaglen - 1, itaglen, iclass, type, itag, &l); + if (e == 0) { + (*size) -= ttaglen; + (*size) += itaglen; + } + return e; + } + if (e != ASN1_OVERFLOW || itaglen <= ttaglen) + return e; + + /* + * Did not fit because ttaglen > itaglen and this was the last / only thing + * being encoded in a buffer of just the right size. + */ + if ((p2 = malloc(len + ttaglen - itaglen)) == NULL) + e = ENOMEM; + if (e == 0) + e = encoder(p2 + len + ttaglen - itaglen - 1, len + ttaglen - itaglen, + obj, size); + if (e == 0) + e = der_put_tag(p2 + ttaglen - 1, itaglen, iclass, type, itag, &l); + if (e == 0) { + (*size) -= ttaglen; + (*size) += itaglen; + memcpy(p - *size, p2 + ttaglen - itaglen, *size); + } + free(p2); + return e; +} +#endif + int der_put_tag (unsigned char *p, size_t len, Der_class class, Der_type type, unsigned int tag, size_t *size) diff --git a/lib/asn1/gen.c b/lib/asn1/gen.c index e7d2e423c..c2462b419 100644 --- a/lib/asn1/gen.c +++ b/lib/asn1/gen.c @@ -48,6 +48,21 @@ static const char *orig_filename; static char *privheader, *header, *template; static const char *headerbase = STEM; +/* XXX same as der_length_tag */ +static size_t +length_tag(unsigned int tag) +{ + size_t len = 0; + + if(tag <= 30) + return 1; + while(tag) { + tag /= 128; + len++; + } + return len + 1; +} + /* * list of all IMPORTs */ @@ -162,6 +177,7 @@ init_generate (const char *filename, const char *base) "#define __%s_h__\n\n", headerbase, headerbase); fprintf (headerfile, "#include \n" + "#include \n" "#include \n\n"); fprintf (headerfile, "#ifndef __asn1_common_definitions__\n" @@ -517,12 +533,36 @@ generate_constant (const Symbol *s) int is_primitive_type(const Type *t) { + /* + * Start by chasing aliasings like this: + * + * Type0 ::= ... + * Type1 ::= Type0 + * .. + * TypeN ::= TypeN-1 + * + * to , then check if is primitive. + */ while (t->type == TType && t->symbol && - t->symbol->type && - t->symbol->type->type == TType) - t = t->symbol->type; - /* EXPLICIT non-UNIVERSAL tags are constructed */ + t->symbol->type) { + if (t->symbol->type->type == TType) + t = t->symbol->type; /* Alias */ + else if (t->symbol->type->type == TTag && + t->symbol->type->tag.tagenv == TE_IMPLICIT) + /* + * IMPLICIT-tagged alias, something like: + * + * Type0 ::= [0] IMPLICIT ... + * + * Just recurse. + */ + return is_primitive_type(t->symbol->type); + else + break; + + } + /* EXPLICIT non-UNIVERSAL tags are always constructed */ if (t->type == TTag && t->tag.tagclass != ASN1_C_UNIV && t->tag.tagenv == TE_EXPLICIT) return 0; @@ -561,6 +601,8 @@ is_primitive_type(const Type *t) case TVisibleString: case TNull: return 1; + case TTag: + return is_primitive_type(t->subtype); default: return 0; } @@ -1270,30 +1312,70 @@ generate_subtypes_header(const Symbol *s) static void generate_type_header (const Symbol *s) { + Type *t = s->type; + if (!s->type) + return; /* * Recurse down the types of member fields of `s' to make sure that * referenced types have had their definitions emitted already if the * member fields are not OPTIONAL/DEFAULTed. */ - if (s->type) - generate_subtypes_header(s); - - if (!s->type) - return; - + generate_subtypes_header(s); fprintf(headerfile, "/*\n"); fprintf(headerfile, "%s ::= ", s->name); define_asn1 (0, s->type); fprintf(headerfile, "\n*/\n\n"); + /* + * Emit enums for the outermost tag of this type. These are needed for + * dealing with IMPLICIT tags so we know what to rewrite the tag to when + * decoding. + * + * See gen_encode.c and gen_decode.c for a complete explanation. Short + * version: we need to change the prototypes of the length/encode/decode + * functions to take an optional IMPLICIT tag to use instead of the type's + * outermost tag, but for now we hack it, and to do that we need to know + * the type's outermost tag outside the context of the bodies of the codec + * functions we generate for it. Using an enum means no extra space is + * needed in stripped objects. + */ + if (!s->emitted_tag_enums) { + while (t->type == TType && s->type->symbol && s->type->symbol->type) + t = s->type->symbol->type; + + if (t->type == TType && t->symbol && strcmp(t->symbol->name, "heim_any")) { + /* + * This type is ultimately an alias of an imported type, so we don't + * know its outermost tag here. + */ + fprintf(headerfile, + "enum { asn1_tag_length_%s = asn1_tag_length_%s,\n" + " asn1_tag_class_%s = asn1_tag_class_%s,\n" + " asn1_tag_tag_%s = asn1_tag_tag_%s };\n", + s->gen_name, s->type->symbol->gen_name, + s->gen_name, s->type->symbol->gen_name, + s->gen_name, s->type->symbol->gen_name); + emitted_tag_enums(s); + } else if (t->type != TType) { + /* This type's outermost tag is known here */ + fprintf(headerfile, + "enum { asn1_tag_length_%s = %lu,\n" + " asn1_tag_class_%s = %d,\n" + " asn1_tag_tag_%s = %d };\n", + s->gen_name, (unsigned long)length_tag(s->type->tag.tagvalue), + s->gen_name, s->type->tag.tagclass, + s->gen_name, s->type->tag.tagvalue); + emitted_tag_enums(s); + } + } + if (s->emitted_definition) return; fprintf(headerfile, "typedef "); define_type(0, s->gen_name, s->gen_name, s->type, TRUE, preserve_type(s->name) ? TRUE : FALSE); - fprintf(headerfile, "\n"); if (template_flag) diff --git a/lib/asn1/gen_decode.c b/lib/asn1/gen_decode.c index e56883446..2ebaa955f 100644 --- a/lib/asn1/gen_decode.c +++ b/lib/asn1/gen_decode.c @@ -362,6 +362,20 @@ decode_type(const char *name, const Type *t, int optional, struct value *defval, break; } case TSet: { + /* + * SET { ... } is the dumbest construct in ASN.1. It's semantically + * indistinguishable from SEQUENCE { ... } but in BER you can write the + * fields in any order you wish, and in DER you have to write them in + * lexicographic order. If you want a reason to hate ASN.1, this is + * certainly one, though the real reason to hate ASN.1 is BER/DER/CER, + * and, really, all tag-length-value (TLV) encodings ever invented, + * including Protocol Buffers. Fortunately not all ASN.1 encoding + * rules are TLV or otherwise self-describing. "Self-describing" + * encoding rules other than those meant to be [somewhat] readable by + * humans, such as XML and JSON, are simply dumb. But SET { ... } is a + * truly special sort of dumb. The only possible use of SET { ... } + * might well be for ASN.1 mappings of XML schemas(?). + */ Member *m; unsigned int memno; @@ -391,6 +405,7 @@ decode_type(const char *name, const Type *t, int optional, struct value *defval, fprintf(codefile, "case MAKE_TAG(%s, %s, %s):\n", classname(mst->tag.tagclass), + (mst->tag.tagclass == ASN1_C_UNIV || mst->tag.tagenv == TE_IMPLICIT) && is_primitive_type(mst->subtype) ? "PRIM" : "CONS", valuename(mst->tag.tagclass, mst->tag.tagvalue)); @@ -506,17 +521,41 @@ decode_type(const char *name, const Type *t, int optional, struct value *defval, t->tag.tagenv == TE_EXPLICIT) && is_primitive_type(t->subtype); + /* + * XXX See the comments in gen_encode() about this. + */ + if (t->tag.tagenv == TE_IMPLICIT && !prim && + t->subtype->type != TSequenceOf && t->subtype->type != TSetOf && + t->subtype->type != TChoice) { + if (t->subtype->symbol && + (t->subtype->type == TSequence || + t->subtype->type == TSet)) + replace_tag = 1; + else if (t->subtype->symbol && strcmp(t->subtype->symbol->name, "heim_any")) + replace_tag = 1; + } else if (t->tag.tagenv == TE_IMPLICIT && prim && t->subtype->symbol) + replace_tag = 1; + if (asprintf(&typestring, "%s_type", tmpstr) < 0 || typestring == NULL) errx(1, "malloc"); fprintf(codefile, "{\n" - "size_t %s_datalen, %s_oldlen;\n" + "size_t %s_datalen;\n" "Der_type %s;\n", - tmpstr, tmpstr, typestring); - if(support_ber) + tmpstr, typestring); + if (replace_tag) + fprintf(codefile, + "const unsigned char *psave%u = p;\n" + "unsigned char *pcopy%u;\n" + "size_t lensave%u, lsave%u;\n", + depth, depth, depth, depth); + else if (support_ber) fprintf(codefile, "int is_indefinite%u;\n", depth); + if (!replace_tag) + fprintf(codefile, + "size_t %s_oldlen;\n", tmpstr); fprintf(codefile, "e = der_match_tag_and_length(p, len, %s, &%s, %s, " "&%s_datalen, &l);\n", @@ -526,7 +565,7 @@ decode_type(const char *name, const Type *t, int optional, struct value *defval, tmpstr); /* XXX hardcode for now */ - if (support_ber && t->subtype->type == TOctetString) { + if (!replace_tag && support_ber && t->subtype->type == TOctetString) { ide = typestring; } else { fprintf(codefile, @@ -556,62 +595,50 @@ decode_type(const char *name, const Type *t, int optional, struct value *defval, } else { fprintf(codefile, "if (e) %s;\n", forwstr); } - fprintf (codefile, - "p += l; len -= l; ret += l;\n" - "%s_oldlen = len;\n", - tmpstr); - if(support_ber) + + if (replace_tag) + fprintf(codefile, + "lsave%u = %s_datalen + l;\n" + "lensave%u = len;\n" + "e = der_replace_tag(p, len, &pcopy%u, &len, asn1_tag_class_%s, %s, asn1_tag_tag_%s);\n" + "if (e) %s;\n" + "p = pcopy%u;\n", + depth, tmpstr, depth, depth, t->subtype->symbol->gen_name, + prim ? "PRIM" : "CONS", + t->subtype->symbol->gen_name, + forwstr, depth); + else + fprintf(codefile, + "p += l; len -= l; ret += l;\n"); + if (!replace_tag) + fprintf(codefile, + "%s_oldlen = len;\n", + tmpstr); + if (support_ber && !replace_tag) fprintf (codefile, "if((is_indefinite%u = _heim_fix_dce(%s_datalen, &len)) < 0)\n" "{ e = ASN1_BAD_FORMAT; %s; }\n" "if (is_indefinite%u) { if (len < 2) { e = ASN1_OVERRUN; %s; } len -= 2; }", depth, tmpstr, forwstr, depth, forwstr); - else + else if (!replace_tag) fprintf(codefile, "if (%s_datalen > len) { e = ASN1_OVERRUN; %s; }\n" "len = %s_datalen;\n", tmpstr, forwstr, tmpstr); if (asprintf (&tname, "%s_Tag", tmpstr) < 0 || tname == NULL) errx(1, "malloc"); /* - * XXX See the comments in gen_encode() about this. + * If `replace_tag' then here `p' and `len' will be the copy mutated by + * der_replace_tag(). */ - if (t->tag.tagenv == TE_IMPLICIT && !prim && - t->subtype->type != TSequenceOf && t->subtype->type != TSetOf && - t->subtype->type != TChoice) { - if (t->subtype->symbol && - (t->subtype->type == TSequence || - t->subtype->type == TSet)) - replace_tag = 1; - else if (t->subtype->symbol && strcmp(t->subtype->symbol->name, "heim_any")) - replace_tag = 1; - } else if (t->tag.tagenv == TE_IMPLICIT && prim && t->subtype->symbol) - replace_tag = 1; - if (replace_tag) { - /* - * XXX We're assuming the IMPLICIT and original tags have the same - * length. This is one of the places that needs fixing if we want - * to properly support tags > 30. - */ + decode_type(name, t->subtype, 0, NULL, forwstr, tname, ide, depth + 1); + if (replace_tag) fprintf(codefile, - "{ unsigned char *pcopy;\n" - "pcopy = calloc (1, len);\n" - "if (pcopy == 0) { e = ENOMEM; %s;}\n" - "memcpy (pcopy, p, len);\n" - "e = der_replace_tag (pcopy, len, %s, %s, %s);\n" - "if (e) %s;\n", - forwstr, - classname(t->subtype->tag.tagclass), - prim ? "PRIM" : "CONS", - valuename(t->subtype->tag.tagclass, t->subtype->tag.tagvalue), - forwstr); - decode_type(name, t->subtype, 0, NULL, forwstr, tname, ide, depth + 1); - fprintf(codefile, - "free(pcopy);" - "}\n"); - } else { - decode_type(name, t->subtype, 0, NULL, forwstr, tname, ide, depth + 1); - } - if(support_ber) + "p = psave%u + lsave%u;\n" + "len = lensave%u - lsave%u;\n" + "ret += lsave%u - l;\n" + "free(pcopy%u);\n", + depth, depth, depth, depth, depth, depth); + else if(support_ber) fprintf(codefile, "if(is_indefinite%u){\n" "len += 2;\n" @@ -627,9 +654,10 @@ decode_type(const char *name, const Type *t, int optional, struct value *defval, tmpstr, forwstr, typestring, forwstr); - fprintf(codefile, - "len = %s_oldlen - %s_datalen;\n", - tmpstr, tmpstr); + if (!replace_tag) + fprintf(codefile, + "len = %s_oldlen - %s_datalen;\n", + tmpstr, tmpstr); if (optional) fprintf(codefile, "}\n"); else if (defval) diff --git a/lib/asn1/gen_encode.c b/lib/asn1/gen_encode.c index b2c1f995c..742d2d182 100644 --- a/lib/asn1/gen_encode.c +++ b/lib/asn1/gen_encode.c @@ -33,6 +33,21 @@ #include "gen_locl.h" +/* XXX same as der_length_tag */ +static size_t +length_tag(unsigned int tag) +{ + size_t len = 0; + + if(tag <= 30) + return 1; + while(tag) { + tag /= 128; + len++; + } + return len + 1; +} + static void encode_primitive (const char *typename, const char *name) { @@ -395,14 +410,10 @@ encode_type (const char *name, const Type *t, const char *tmpstr) int c; if (asprintf (&tname, "%s_tag", tmpstr) < 0 || tname == NULL) errx(1, "malloc"); - c = encode_type (name, t->subtype, tname); - /* Explicit tags are always constructed */ - if (!c && t->tag.tagclass != ASN1_C_UNIV && t->tag.tagenv == TE_EXPLICIT) - c = 1; /* * HACK HACK HACK * - * This is part of the fix to the bug where we treat IMPLICIT tags of + * This is part of the fix to the bug where we treated IMPLICIT tags of * named types as EXPLICIT. I.e. * * Foo ::= SEQUENCE { ... } @@ -412,37 +423,29 @@ encode_type (const char *name, const Type *t, const char *tmpstr) * constructed tag when it should get only the first tag. * * Properly fixing this would require changing the signatures of the - * encode, lenght, and decode functions we generate to take an optional + * encode, length, and decode functions we generate to take an optional * tag to replace the one the encoder would generate / decoder would * expect. That would change the ABI, which... isn't stable, but it's * a bit soon to make that change. * - * So, we're looking for IMPLICIT tags of named SEQUENCE/SET types, and - * if we see any, we generate code to replace the tag. + * So, we're looking for IMPLICIT, and if we see any, we generate code + * to replace the tag. * - * NOTE WELL: We're assuming that the length of the encoding of the tag - * of the subtype and the length of the encoding of the - * IMPLICIT tag are the same. + * On the decode side we need to know what tag to restore. For this we + * generate enums in the generated header. * - * To avoid this we'll need to generate new length_tag_* - * functions or else we'll need to add a boolean argument to - * the length_* functions we generate to count only the - * length of the tag of the type. The latter is an ABI - * change. Or we'll need to enhance asn1_compile to be able - * to load multiple modules so that we use the AST of the - * modules to internally compute the length of types and - * tags. The latter would be great anyways as it would - * allow the computation of tag lengths for tagged types to - * be constant. - * - * NOTE WELL: We *do* "replace" the tags of IMPLICIT-tagged primitive - * types, but our primitive codec functions leave those tags - * out, which is why we don't have to der_replace_tag() them - * here. + * NOTE: We *do* "replace" the tags of IMPLICIT-tagged primitive types, + * but our primitive codec functions leave those tags out, which + * is why we don't have to der_replace_tag() them here. + */ + /* + * If the tag is IMPLICIT and it's not primitive and the subtype is not + * any kind of structure... */ if (t->tag.tagenv == TE_IMPLICIT && !prim && t->subtype->type != TSequenceOf && t->subtype->type != TSetOf && t->subtype->type != TChoice) { + /* If it is a named type for a structured thing */ if (t->subtype->symbol && (t->subtype->type == TSequence || t->subtype->type == TSet)) @@ -456,13 +459,132 @@ encode_type (const char *name, const Type *t, const char *tmpstr) * tags unlike our raw primtive codec library. */ replace_tag = 1; + if (replace_tag) fprintf(codefile, - "e = der_replace_tag (p, len, %s, %s, %s);\n" - "if (e) return e;\np -= l; len -= l; ret += l;\n\n", + "{ unsigned char *psave_%s = p;\n" + "size_t l2_%s, lensave_%s = len;\n" + "len = length_%s(%s);\n" + /* Allocate a temp buffer for the encoder */ + "if ((p = malloc(len)) == NULL) return ENOMEM;\n" + /* Make p point to the last byte of the allocated buf */ + "p += len - 1;\n", + tmpstr, tmpstr, tmpstr, + t->subtype->symbol->gen_name, name); + + c = encode_type (name, t->subtype, tname); + /* Explicit non-UNIVERSAL tags are always constructed */ + if (!c && t->tag.tagclass != ASN1_C_UNIV && t->tag.tagenv == TE_EXPLICIT) + c = 1; + if (replace_tag) + fprintf(codefile, + "if (len) abort();\n" + /* + * Here we have `p' pointing to one byte before the buffer + * we allocated above. + * + * [ T_wrong | LL | VVVV ] // temp buffer + * ^ ^ + * | | + * | \ + * \ +-- p + 1 + * +-- p + * + * psave_ still points to the last byte in the + * original buffer passed in where we should write the + * encoding of . + * + * We adjust psave_ to point to before the TLV + * encoding of (with wrong tag) in the original + * buffer (this may NOT be a valid pointer, but we won't + * dereference it): + * + * [ ... | T_wrong | LL | VVVVV | ... ] // original buffer + * ^ + * | + * \ + * +-- psave_ + */ + "psave_%s -= l;\n" + /* + * We further adjust psave_ to point to the last + * byte of what should be the T(ag) of the TLV encoding of + * (this is now a valid pointer), then... + * + * |<--->| (not written yet) + * | | |<-------->| (not written yet) + * [ ... | T_right | LL | VVVVV | ... ] // original buffer + * ^ + * | + * \ + * +-- psave_ + */ + "psave_%s += asn1_tag_length_%s;\n" + /* + * ...copy the L(ength)V(alue) of the TLV encoding of + * . + * + * [ ... | T_right | LL | VVVVV | ... ] // original buffer + * ^ + * | + * \ + * +-- psave_ + 1 + * + * |<----->| length is + * | | `l' - asn1_tag_length_ + * [ T_wrong | LL | VVVV ] // temp buffer + * ^ ^ + * | | + * | \ + * \ +-- p + 1 + asn1_tag_length_%s + * +-- p + 1 + */ + "memcpy(psave_%s + 1, p + 1 + asn1_tag_length_%s, l - asn1_tag_length_%s);\n" + /* + * Encode the IMPLICIT tag. Recall that encoders like + * der_put_tag() take a pointer to the last byte they + * should write to, and a length of bytes to the left of + * that that they are allowed to write into. + * + * [ ... | T_right | LL | VVVVV | ... ] // original buffer + * ^ + * | + * \ + * +-- psave_ + */ + "e = der_put_tag(psave_%s, %lu, %s, %s, %d, &l2_%s);\n" + "if (e) return e;\n" + /* Restore `len' and adjust it (see `p' below) */ + "len = lensave_%s - (l + %lu - asn1_tag_length_%s);\n" + /* + * Adjust `ret' to account for the difference in size + * between the length of the right and wrong tags. + */ + "ret += %lu - asn1_tag_length_%s;\n" + /* Free the buffer and restore `p' */ + "free(p + 1);\n" + /* + * Make `p' point into the original buffer again, to one + * byte before the bytes we wrote: + * + * [ ... | T_right | LL | VVVVV | ... ] // original buffer + * ^ + * | + * \ + * +-- p + */ + "p = psave_%s - (1 + %lu - asn1_tag_length_%s); }\n", + tmpstr, tmpstr, t->subtype->symbol->name, + tmpstr, t->subtype->symbol->name, t->subtype->symbol->name, + tmpstr, length_tag(t->tag.tagvalue), classname(t->tag.tagclass), c ? "CONS" : "PRIM", - valuename(t->tag.tagclass, t->tag.tagvalue)); + t->tag.tagvalue, + tmpstr, + + tmpstr, length_tag(t->tag.tagvalue), t->subtype->symbol->name, + length_tag(t->tag.tagvalue), t->subtype->symbol->name, + tmpstr, length_tag(t->tag.tagvalue), t->subtype->symbol->name); else fprintf(codefile, "e = der_put_length_and_tag (p, len, ret, %s, %s, %s, &l);\n" @@ -471,6 +593,7 @@ encode_type (const char *name, const Type *t, const char *tmpstr) c ? "CONS" : "PRIM", valuename(t->tag.tagclass, t->tag.tagvalue)); free(tname); + constructed = c; break; } case TChoice:{ diff --git a/lib/asn1/gen_length.c b/lib/asn1/gen_length.c index 8aaa71e5b..722508d68 100644 --- a/lib/asn1/gen_length.c +++ b/lib/asn1/gen_length.c @@ -256,9 +256,7 @@ length_type (const char *name, const Type *t, if (asprintf(&tname, "%s_tag", tmpstr) < 0 || tname == NULL) errx(1, "malloc"); length_type (name, t->subtype, variable, tname); - /* - * XXX See the comments in gen_encode() about this. - */ + /* See the comments in encode_type() about IMPLICIT tags */ if (t->tag.tagenv == TE_IMPLICIT && !prim && t->subtype->type != TSequenceOf && t->subtype->type != TSetOf && t->subtype->type != TChoice) { @@ -270,7 +268,18 @@ length_type (const char *name, const Type *t, replace_tag = 1; } else if (t->tag.tagenv == TE_IMPLICIT && prim && t->subtype->symbol) replace_tag = 1; - if (!replace_tag) + if (replace_tag) + /* + * We're replacing the tag of the underlying type. If that type is + * imported, then we don't know its tag, so we rely on the + * asn1_tag_tag_ enum value we generated for it, and we + * use the asn1_tag_length_ enum value to avoid having to + * call der_length_tag() at run-time. + */ + fprintf(codefile, "ret += %lu - asn1_tag_length_%s;\n", + (unsigned long)length_tag(t->tag.tagvalue), + t->subtype->symbol->gen_name); + else fprintf(codefile, "ret += %lu + der_length_len (ret);\n", (unsigned long)length_tag(t->tag.tagvalue)); free(tname); diff --git a/lib/asn1/symbol.c b/lib/asn1/symbol.c index 99b67d545..920ed16f7 100644 --- a/lib/asn1/symbol.c +++ b/lib/asn1/symbol.c @@ -165,3 +165,9 @@ emitted_definition(const Symbol *s) { ((Symbol *)(uintptr_t)s)->emitted_definition = 1; } + +void +emitted_tag_enums(const Symbol *s) +{ + ((Symbol *)(uintptr_t)s)->emitted_tag_enums = 1; +} diff --git a/lib/asn1/symbol.h b/lib/asn1/symbol.h index b4238550f..f8a5627b9 100644 --- a/lib/asn1/symbol.h +++ b/lib/asn1/symbol.h @@ -159,6 +159,7 @@ struct symbol { HEIM_TAILQ_ENTRY(symbol) symlist; unsigned int emitted_declaration:1; unsigned int emitted_definition:1; + unsigned int emitted_tag_enums:1; }; typedef struct symbol Symbol; @@ -179,4 +180,5 @@ int checkundefined(void); void generate_types(void); void emitted_declaration(const Symbol *); void emitted_definition(const Symbol *); +void emitted_tag_enums(const Symbol *); #endif diff --git a/lib/asn1/test.asn1 b/lib/asn1/test.asn1 index 07e2852fe..b43249ed7 100644 --- a/lib/asn1/test.asn1 +++ b/lib/asn1/test.asn1 @@ -74,9 +74,10 @@ TESTImplicit ::= SEQUENCE { } TESTImplicit2 ::= SEQUENCE { - ti1[0] IMPLICIT TESTInteger, --- ti2[1] IMPLICIT TESTLargeTag, this is disabled since the IMPLICT encoder does't get the types right when stepping inside an structure -- - ti3[2] IMPLICIT TESTInteger3 + ti1[0] IMPLICIT TESTInteger, +-- ti2[1] IMPLICIT TESTLargeTag, this is disabled since the IMPLICT encoder does't get the types right when stepping inside an structure -- + ti3[2] IMPLICIT TESTInteger3, + ti4[51] IMPLICIT TESTInteger OPTIONAL } TESTAllocInner ::= SEQUENCE {