diff --git a/lib/asn1/canthandle.asn1 b/lib/asn1/canthandle.asn1 index e9b5a6f60..0e0822a9e 100644 --- a/lib/asn1/canthandle.asn1 +++ b/lib/asn1/canthandle.asn1 @@ -2,24 +2,14 @@ CANTHANDLE DEFINITIONS ::= BEGIN --- Code the tag [1] but not the [ CONTEXT CONS UT_Sequence ] for Kaka2 --- Workaround: use inline the structure directly --- Code the tag [2] but it should be primitive since KAKA3 is --- Workaround: use the INTEGER type directly +-- 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. -Kaka2 ::= SEQUENCE { - kaka2-1 [0] INTEGER -} +Foo31 ::= SEQUENCE { foo [31] INTEGER } -Kaka3 ::= INTEGER - -Foo ::= SEQUENCE { - kaka1 [0] IMPLICIT INTEGER OPTIONAL, - kaka2 [1] IMPLICIT Kaka2 OPTIONAL, - kaka3 [2] IMPLICIT Kaka3 OPTIONAL -} - --- Can't handle primitives in SET OF +-- 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 Baz ::= SET OF INTEGER diff --git a/lib/asn1/crmf.asn1 b/lib/asn1/crmf.asn1 index 4ec721bca..8fa29a655 100644 --- a/lib/asn1/crmf.asn1 +++ b/lib/asn1/crmf.asn1 @@ -51,7 +51,6 @@ SubsequentMessage ::= INTEGER { challengeResp (1) } --- XXX IMPLICIT brokenness POPOPrivKey ::= CHOICE { thisMessage [0] BIT STRING, -- Deprecated subsequentMessage [1] IMPLICIT SubsequentMessage, @@ -60,7 +59,6 @@ POPOPrivKey ::= CHOICE { encryptedKey [4] heim_any } --- XXX IMPLICIT brokenness ProofOfPossession ::= CHOICE { raVerified [0] NULL, signature [1] POPOSigningKey, diff --git a/lib/asn1/der_get.c b/lib/asn1/der_get.c index 6cb55dd82..14d8f90cd 100644 --- a/lib/asn1/der_get.c +++ b/lib/asn1/der_get.c @@ -576,7 +576,7 @@ der_get_tag (const unsigned char *p, size_t len, { size_t ret = 0; if (len < 1) - return ASN1_OVERRUN; + return ASN1_MISSING_FIELD; *cls = (Der_class)(((*p) >> 6) & 0x03); *type = (Der_type)(((*p) >> 5) & 0x01); *tag = (*p) & 0x1f; @@ -625,15 +625,20 @@ der_match_tag2 (const unsigned char *p, size_t len, unsigned int thistag; int e; - e = der_get_tag (p, len, &thisclass, type, &thistag, &l); + e = der_get_tag(p, len, &thisclass, type, &thistag, &l); if (e) return e; - if (cls != thisclass) - return ASN1_BAD_ID; - if(tag > thistag) - return ASN1_MISPLACED_FIELD; - if(tag < thistag) + /* + * We do depend on ASN1_BAD_ID being returned in places where we're + * essentially implementing an application-level CHOICE where we try to + * decode one way then the other. In Heimdal this happens only in lib/hdb/ + * where we try to decode a blob as an hdb_entry, then as an + * hdb_entry_alias. Applications should really not depend on this. + */ + if (cls != thisclass && (cls == ASN1_C_APPL || thisclass == ASN1_C_APPL)) + return ASN1_BAD_ID; + if (tag != thistag) return ASN1_MISSING_FIELD; - if(size) *size = l; + if (size) *size = l; return 0; } diff --git a/lib/asn1/der_put.c b/lib/asn1/der_put.c index 843d09f5c..f3d8ff73d 100644 --- a/lib/asn1/der_put.c +++ b/lib/asn1/der_put.c @@ -441,6 +441,25 @@ der_put_oid (unsigned char *p, size_t len, return 0; } +int +der_replace_tag(unsigned char *p, size_t len, 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; + 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; + return 0; +} + 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 2676c12e8..e7d2e423c 100644 --- a/lib/asn1/gen.c +++ b/lib/asn1/gen.c @@ -515,9 +515,34 @@ generate_constant (const Symbol *s) } int -is_primitive_type(int type) +is_primitive_type(const Type *t) { - switch(type) { + while (t->type == TType && + t->symbol && + t->symbol->type && + t->symbol->type->type == TType) + t = t->symbol->type; + /* EXPLICIT non-UNIVERSAL tags are constructed */ + if (t->type == TTag && t->tag.tagclass != ASN1_C_UNIV && + t->tag.tagenv == TE_EXPLICIT) + return 0; + if (t->symbol && t->symbol->type) { + /* EXPLICIT non-UNIVERSAL tags are constructed */ + if (t->symbol->type->type == TTag && + t->symbol->type->tag.tagclass != ASN1_C_UNIV && + t->symbol->type->tag.tagenv == TE_EXPLICIT) + return 0; + /* EXPLICIT UNIVERSAL tags are constructed if they are SEQUENCE/SET */ + if (t->symbol->type->type == TTag && + t->symbol->type->tag.tagclass == ASN1_C_UNIV) { + switch (t->symbol->type->tag.tagvalue) { + case UT_Sequence: return 0; + case UT_Set: return 0; + default: return 1; + } + } + } + switch(t->type) { case TInteger: case TBoolean: case TOctetString: diff --git a/lib/asn1/gen_decode.c b/lib/asn1/gen_decode.c index 9a2ec1b68..73ada1a85 100644 --- a/lib/asn1/gen_decode.c +++ b/lib/asn1/gen_decode.c @@ -137,8 +137,10 @@ find_tag (const Type *t, break; case TTag: *cl = t->tag.tagclass; - *ty = is_primitive_type(t->subtype->type) ? PRIM : CONS; - *tag = t->tag.tagvalue; + *ty = !(t->tag.tagclass != ASN1_C_UNIV && + t->tag.tagenv == TE_EXPLICIT) && + is_primitive_type(t->subtype) ? PRIM : CONS; + *tag = t->tag.tagvalue; /* XXX is this correct? */ break; case TType: if ((t->symbol->stype == Stype && t->symbol->type == NULL) @@ -224,13 +226,26 @@ decode_type(const char *name, const Type *t, int optional, struct value *defval, t->symbol->gen_name, name); if (optional) { fprintf (codefile, - "if(e) {\n" + "if(e == ASN1_MISSING_FIELD) {\n" "free(%s);\n" "%s = NULL;\n" + "} else if (e) { %s; \n" "} else {\n" "p += l; len -= l; ret += l;\n" "}\n", - name, name); + name, name, forwstr); + } else if (defval) { + fprintf(codefile, + "if (e == ASN1_MISSING_FIELD) {\n"); + /* + * `name' starts with an ampersand here and is not an lvalue. + * We skip the ampersand and then it is an lvalue. + */ + gen_assign_defval(name + 1, defval); + fprintf(codefile, + "} else if (e) { %s;\n" + "} else { p += l; len -= l; ret += l; }\n", + forwstr); } else { fprintf (codefile, "if(e) %s;\n", @@ -371,7 +386,7 @@ decode_type(const char *name, const Type *t, int optional, struct value *defval, fprintf(codefile, "case MAKE_TAG(%s, %s, %s):\n", classname(m->type->tag.tagclass), - is_primitive_type(m->type->subtype->type) ? "PRIM" : "CONS", + is_primitive_type(m->type->subtype) ? "PRIM" : "CONS", valuename(m->type->tag.tagclass, m->type->tag.tagvalue)); if (asprintf (&s, "%s(%s)->%s", m->optional ? "" : "&", name, m->gen_name) < 0 || s == NULL) @@ -481,6 +496,10 @@ decode_type(const char *name, const Type *t, int optional, struct value *defval, case TTag:{ char *tname = NULL, *typestring = NULL; char *ide = NULL; + int replace_tag = 0; + int prim = !(t->tag.tagclass != ASN1_C_UNIV && + t->tag.tagenv == TE_EXPLICIT) && + is_primitive_type(t->subtype); if (asprintf(&typestring, "%s_type", tmpstr) < 0 || typestring == NULL) errx(1, "malloc"); @@ -508,7 +527,7 @@ decode_type(const char *name, const Type *t, int optional, struct value *defval, fprintf(codefile, "if (e == 0 && %s != %s) { e = ASN1_BAD_ID; }\n", typestring, - is_primitive_type(t->subtype->type) ? "PRIM" : "CONS"); + prim ? "PRIM" : "CONS"); } if(optional) { @@ -519,21 +538,19 @@ decode_type(const char *name, const Type *t, int optional, struct value *defval, "%s = calloc(1, sizeof(*%s));\n" "if (%s == NULL) { e = ENOMEM; %s; }\n", name, name, name, name, forwstr); - } else { - if (defval) { - char *s; + } else if (defval) { + char *s; - if (asprintf(&s, "*(%s)", name) == -1 || s == NULL) - return ENOMEM; - fprintf(codefile, "if (e && e != ASN1_MISSING_FIELD) %s;\n", forwstr); - fprintf(codefile, "if (e == ASN1_MISSING_FIELD) {\n"); - gen_assign_defval(s, defval); - free(s); - fprintf(codefile, "e = 0; l= 0;\n} else {\n"); - } else { - fprintf(codefile, "if (e) %s;\n", forwstr); - } - } + if (asprintf(&s, "*(%s)", name) == -1 || s == NULL) + return ENOMEM; + fprintf(codefile, "if (e && e != ASN1_MISSING_FIELD) %s;\n", forwstr); + fprintf(codefile, "if (e == ASN1_MISSING_FIELD) {\n"); + gen_assign_defval(s, defval); + free(s); + fprintf(codefile, "e = 0; l= 0;\n} else {\n"); + } else { + fprintf(codefile, "if (e) %s;\n", forwstr); + } fprintf (codefile, "p += l; len -= l; ret += l;\n" "%s_oldlen = len;\n", @@ -550,7 +567,45 @@ decode_type(const char *name, const Type *t, int optional, struct value *defval, "len = %s_datalen;\n", tmpstr, forwstr, tmpstr); if (asprintf (&tname, "%s_Tag", tmpstr) < 0 || tname == NULL) errx(1, "malloc"); - decode_type(name, t->subtype, 0, NULL, forwstr, tname, ide, depth + 1); + /* + * 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 (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. + */ + 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) fprintf(codefile, "if(is_indefinite%u){\n" diff --git a/lib/asn1/gen_encode.c b/lib/asn1/gen_encode.c index 1149641f2..b2c1f995c 100644 --- a/lib/asn1/gen_encode.c +++ b/lib/asn1/gen_encode.c @@ -116,6 +116,7 @@ encode_type (const char *name, const Type *t, const char *tmpstr) "e = encode_%s(p, len, %s, &l);\n" "if (e) return e;\np -= l; len -= l; ret += l;\n\n", t->symbol->gen_name, name); + constructed = !is_primitive_type(t); break; case TInteger: if(t->members) { @@ -387,17 +388,89 @@ encode_type (const char *name, const Type *t, const char *tmpstr) break; case TTag: { char *tname = NULL; + int replace_tag = 0; + int prim = !(t->tag.tagclass != ASN1_C_UNIV && + t->tag.tagenv == TE_EXPLICIT) && + is_primitive_type(t->subtype); int c; if (asprintf (&tname, "%s_tag", tmpstr) < 0 || tname == NULL) errx(1, "malloc"); c = encode_type (name, t->subtype, tname); - fprintf (codefile, - "e = der_put_length_and_tag (p, len, ret, %s, %s, %s, &l);\n" - "if (e) return e;\np -= l; len -= l; ret += l;\n\n", - classname(t->tag.tagclass), - c ? "CONS" : "PRIM", - valuename(t->tag.tagclass, t->tag.tagvalue)); - free (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 + * named types as EXPLICIT. I.e. + * + * Foo ::= SEQUENCE { ... } + * Bar ::= SEQUENCE { foo [0] IMPLICIT Foo } + * + * would get a context [0] constructed tag *and* a universal sequence + * 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 + * 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. + * + * 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. + * + * 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. + */ + 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) + /* + * Because the subtype is named we are generating its codec + * functions, and those will be adding their UNIVERSAL or whatever + * 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", + classname(t->tag.tagclass), + c ? "CONS" : "PRIM", + valuename(t->tag.tagclass, t->tag.tagvalue)); + else + fprintf(codefile, + "e = der_put_length_and_tag (p, len, ret, %s, %s, %s, &l);\n" + "if (e) return e;\np -= l; len -= l; ret += l;\n\n", + classname(t->tag.tagclass), + c ? "CONS" : "PRIM", + valuename(t->tag.tagclass, t->tag.tagvalue)); + free(tname); break; } case TChoice:{ diff --git a/lib/asn1/gen_length.c b/lib/asn1/gen_length.c index 6cc4fd90d..8aaa71e5b 100644 --- a/lib/asn1/gen_length.c +++ b/lib/asn1/gen_length.c @@ -248,11 +248,31 @@ length_type (const char *name, const Type *t, break; case TTag:{ char *tname = NULL; + int replace_tag = 0; + int prim = !(t->tag.tagclass != ASN1_C_UNIV && + t->tag.tagenv == TE_EXPLICIT) && + is_primitive_type(t->subtype); + if (asprintf(&tname, "%s_tag", tmpstr) < 0 || tname == NULL) errx(1, "malloc"); length_type (name, t->subtype, variable, tname); - fprintf (codefile, "ret += %lu + der_length_len (ret);\n", - (unsigned long)length_tag(t->tag.tagvalue)); + /* + * 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 (!replace_tag) + fprintf(codefile, "ret += %lu + der_length_len (ret);\n", + (unsigned long)length_tag(t->tag.tagvalue)); free(tname); break; } diff --git a/lib/asn1/gen_locl.h b/lib/asn1/gen_locl.h index 064b7ba26..e92a43144 100644 --- a/lib/asn1/gen_locl.h +++ b/lib/asn1/gen_locl.h @@ -136,7 +136,7 @@ void add_import(const char *); void add_export(const char *); int is_export(const char *); int yyparse(void); -int is_primitive_type(int); +int is_primitive_type(const Type *); int preserve_type(const char *); int seq_type(const char *); diff --git a/lib/asn1/gen_template.c b/lib/asn1/gen_template.c index 7e5329bc8..81b3e137e 100644 --- a/lib/asn1/gen_template.c +++ b/lib/asn1/gen_template.c @@ -663,6 +663,9 @@ template_members(struct templatehead *temp, const char *basetype, const char *na int subtype_is_struct = is_struct(t->subtype, isstruct); static unsigned long tag_counter = 0; int tagimplicit = (t->tag.tagenv == TE_IMPLICIT); + int prim = !(t->tag.tagclass != ASN1_C_UNIV && + t->tag.tagenv == TE_EXPLICIT) && + is_primitive_type(t->subtype); struct type *subtype; fprintf(get_code_file(), "/* template_members: %s %s %s */\n", basetype, implicit ? "imp" : "exp", tagimplicit ? "imp" : "exp"); @@ -714,7 +717,7 @@ template_members(struct templatehead *temp, const char *basetype, const char *na add_line_pointer(temp, dupname, poffset, "A1_TAG_T(%s,%s,%s)%s%s", classname(t->tag.tagclass), - is_primitive_type(subtype->type) ? "PRIM" : "CONS", + prim ? "PRIM" : "CONS", valuename(t->tag.tagclass, t->tag.tagvalue), optional ? "|A1_FLAG_OPTIONAL" : "", tagimplicit ? "|A1_FLAG_IMPLICIT" : ""); diff --git a/lib/asn1/libasn1-exports.def b/lib/asn1/libasn1-exports.def index 1e2d5bfb0..52f1be4c1 100644 --- a/lib/asn1/libasn1-exports.def +++ b/lib/asn1/libasn1-exports.def @@ -1051,6 +1051,7 @@ EXPORTS der_put_utctime der_put_utf8string der_put_visible_string + der_replace_tag _der_timegm DigestTypes2int DistributionPointReasonFlags2int