From: Greg Hudson Date: Sat, 11 Feb 2012 23:25:25 +0000 (+0000) Subject: Data-driven ASN.1 decoder X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=9f23e1a8133c11bff4ef5f41cb44d28041320a76;p=krb5.git Data-driven ASN.1 decoder Add a general ASN.1 decoder implementation in asn1_encode.c using the same data structures as the encoder (augmented where necessary), and use it to define decoder functions in asn1_k_encode.c. Add a boolean type to atype_info, as it is needed for the pa_pac_req decoder. For the moment, just #if out the old decoder functions; they and their support code can be cleaned up later after a a few remaining utility functions are addressed. Changes to encoder and decoder interfaces are minimized, but there are two small ones. ldap_seqof_key_data has a kvno field added, and some of the decoder logic is pushed up into the caller. The safe_with_body decoder now outputs an allocated krb5_data * instead of a krb5_data with aliases into the input buffer. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@25693 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/include/k5-int.h b/src/include/k5-int.h index 00cb5b113..21228babb 100644 --- a/src/include/k5-int.h +++ b/src/include/k5-int.h @@ -1704,7 +1704,7 @@ decode_krb5_safe(const krb5_data *output, krb5_safe **rep); krb5_error_code decode_krb5_safe_with_body(const krb5_data *output, krb5_safe **rep, - krb5_data *body); + krb5_data **body); krb5_error_code decode_krb5_priv(const krb5_data *output, krb5_priv **rep); @@ -1787,6 +1787,7 @@ struct _krb5_key_data; /* kdb.h */ struct ldap_seqof_key_data { krb5_int32 mkvno; /* Master key version number */ + krb5_int16 kvno; /* kvno of key_data elements (all the same) */ struct _krb5_key_data *key_data; krb5_int16 n_key_data; }; @@ -1797,7 +1798,7 @@ krb5int_ldap_encode_sequence_of_keys(const ldap_seqof_key_data *val, krb5_data **code); krb5_error_code -krb5int_ldap_decode_sequence_of_keys(krb5_data *in, +krb5int_ldap_decode_sequence_of_keys(const krb5_data *in, ldap_seqof_key_data **rep); /************************************************************************* diff --git a/src/lib/krb5/asn.1/asn1_encode.c b/src/lib/krb5/asn.1/asn1_encode.c index 85b804a38..62ca9c015 100644 --- a/src/lib/krb5/asn.1/asn1_encode.c +++ b/src/lib/krb5/asn.1/asn1_encode.c @@ -176,6 +176,141 @@ k5_asn1_encode_bitstring(asn1buf *buf, unsigned char *const *val, size_t len, return asn1buf_insert_octet(buf, '\0'); } +/**** Functions for decoding primitive types ****/ + +asn1_error_code +k5_asn1_decode_bool(const unsigned char *asn1, size_t len, asn1_intmax *val) +{ + if (len != 1) + return ASN1_BAD_LENGTH; + *val = (*asn1 != 0); + return 0; +} + +/* Decode asn1/len as the contents of a DER integer, placing the signed result + * in val. */ +asn1_error_code +k5_asn1_decode_int(const unsigned char *asn1, size_t len, asn1_intmax *val) +{ + asn1_intmax n; + size_t i; + + if (len == 0) + return ASN1_BAD_LENGTH; + n = (asn1[0] & 0x80) ? -1 : 0; + /* Check length; allow extra octet if first octet is 0. */ + if (len > sizeof(asn1_intmax) + (asn1[0] == 0)) + return ASN1_OVERFLOW; + for (i = 0; i < len; i++) + n = (n << 8) | asn1[i]; + *val = n; + return 0; +} + +/* Decode asn1/len as the contents of a DER integer, placing the unsigned + * result in val. */ +asn1_error_code +k5_asn1_decode_uint(const unsigned char *asn1, size_t len, asn1_uintmax *val) +{ + asn1_uintmax n; + size_t i; + + if (len == 0) + return ASN1_BAD_LENGTH; + /* Check for negative values and check length. */ + if ((asn1[0] & 0x80) || len > sizeof(asn1_uintmax) + (asn1[0] == 0)) + return ASN1_OVERFLOW; + for (i = 0, n = 0; i < len; i++) + n = (n << 8) | asn1[i]; + *val = n; + return 0; +} + +asn1_error_code +k5_asn1_decode_bytestring(const unsigned char *asn1, size_t len, + unsigned char **str_out, size_t *len_out) +{ + unsigned char *str; + + *str_out = NULL; + *len_out = 0; + str = malloc(len); + if (str == NULL) + return ENOMEM; + memcpy(str, asn1, len); + *str_out = str; + *len_out = len; + return 0; +} + +asn1_error_code +k5_asn1_decode_generaltime(const unsigned char *asn1, size_t len, + time_t *time_out) +{ + const char *s = (char *)asn1; + struct tm ts; + time_t t; + + *time_out = 0; + if (len != 15) + return ASN1_BAD_LENGTH; + /* Time encoding: YYYYMMDDhhmmssZ */ + if (s[14] != 'Z') + return ASN1_BAD_FORMAT; + if (memcmp(s, "19700101000000Z", 15) == 0) { + *time_out = 0; + return 0; + } +#define c2i(c) ((c) - '0') + ts.tm_year = 1000 * c2i(s[0]) + 100 * c2i(s[1]) + 10 * c2i(s[2]) + + c2i(s[3]) - 1900; + ts.tm_mon = 10 * c2i(s[4]) + c2i(s[5]) - 1; + ts.tm_mday = 10 * c2i(s[6]) + c2i(s[7]); + ts.tm_hour = 10 * c2i(s[8]) + c2i(s[9]); + ts.tm_min = 10 * c2i(s[10]) + c2i(s[11]); + ts.tm_sec = 10 * c2i(s[12]) + c2i(s[13]); + ts.tm_isdst = -1; + t = krb5int_gmt_mktime(&ts); + if (t == -1) + return ASN1_BAD_TIMEFORMAT; + *time_out = t; + return 0; +} + +/* + * Note: we return the number of bytes, not bits, in the bit string. If the + * number of bits is not a multiple of 8 we effectively round up to the next + * multiple of 8. + */ +asn1_error_code +k5_asn1_decode_bitstring(const unsigned char *asn1, size_t len, + unsigned char **bits_out, size_t *len_out) +{ + unsigned char unused, *bits; + + *bits_out = NULL; + *len_out = 0; + if (len == 0) + return ASN1_BAD_LENGTH; + unused = *asn1++; + len--; + if (unused > 7) + return ASN1_BAD_FORMAT; + + bits = malloc(len); + if (bits == NULL) + return ENOMEM; + memcpy(bits, asn1, len); + if (len > 1) + bits[len - 1] &= (0xff << unused); + + *bits_out = bits; + *len_out = len; + return 0; +} + +/**** Functions for encoding and decoding tags ****/ + /* Encode a DER tag into buf with the tag and length parameters in t. Place * the length of the encoded tag in *retlen. */ static asn1_error_code @@ -243,11 +378,106 @@ make_tag(asn1buf *buf, const taginfo *t, size_t *retlen) return 0; } +/* + * Read a BER tag and length from asn1/len. Place the tag parameters in + * tag_out. Set contents_out/clen_out to the octet range of the tag's + * contents, and remainder_out/rlen_out to the octet range after the end of the + * BER encoding. + * + * (krb5 ASN.1 encodings should be in DER, but for compatibility with some + * really ancient implementations we handle the indefinite length form in tags. + * However, we still insist on the primitive form of string types.) + */ +static asn1_error_code +get_tag(const unsigned char *asn1, size_t len, taginfo *tag_out, + const unsigned char **contents_out, size_t *clen_out, + const unsigned char **remainder_out, size_t *rlen_out) +{ + asn1_error_code ret; + unsigned char o; + const unsigned char *c, *p, *tag_start = asn1; + size_t clen, llen, i; + taginfo t; + + *contents_out = *remainder_out = NULL; + *clen_out = *rlen_out = 0; + if (len == 0) + return ASN1_OVERRUN; + o = *asn1++; + len--; + tag_out->asn1class = o & 0xC0; + tag_out->construction = o & 0x20; + if ((o & 0x1F) != 0x1F) { + tag_out->tagnum = o & 0x1F; + } else { + tag_out->tagnum = 0; + do { + if (len == 0) + return ASN1_OVERRUN; + o = *asn1++; + len--; + tag_out->tagnum = (tag_out->tagnum << 7) | (o & 0x7F); + } while (o & 0x80); + } + + if (len == 0) + return ASN1_OVERRUN; + o = *asn1++; + len--; + + if (o == 0x80) { + /* Indefinite form (should not be present in DER, but we accept it). */ + if (tag_out->construction != CONSTRUCTED) + return ASN1_MISMATCH_INDEF; + p = asn1; + while (!(len >= 2 && p[0] == 0 && p[1] == 0)) { + ret = get_tag(p, len, &t, &c, &clen, &p, &len); + if (ret) + return ret; + } + tag_out->tag_end_len = 2; + *contents_out = asn1; + *clen_out = p - asn1; + *remainder_out = p + 2; + *rlen_out = len - 2; + } else if ((o & 0x80) == 0) { + /* Short form (first octet gives content length). */ + if (o > len) + return ASN1_OVERRUN; + tag_out->tag_end_len = 0; + *contents_out = asn1; + *clen_out = o; + *remainder_out = asn1 + *clen_out; + *rlen_out = len - (*remainder_out - asn1); + } else { + /* Long form (first octet gives number of base-256 length octets). */ + llen = o & 0x7F; + if (llen > len) + return ASN1_OVERRUN; + if (llen > sizeof(*clen_out)) + return ASN1_OVERFLOW; + for (i = 0, clen = 0; i < llen; i++) + clen = (clen << 8) | asn1[i]; + if (clen > len - llen) + return ASN1_OVERRUN; + tag_out->tag_end_len = 0; + *contents_out = asn1 + llen; + *clen_out = clen; + *remainder_out = *contents_out + clen; + *rlen_out = len - (*remainder_out - asn1); + } + tag_out->tag_len = *contents_out - tag_start; + return 0; +} + #ifdef POINTERS_ARE_ALL_THE_SAME #define LOADPTR(PTR, TYPE) (*(const void *const *)(PTR)) +#define STOREPTR(PTR, TYPE, VAL) (*(void **)(VAL) = (PTR)) #else #define LOADPTR(PTR, PTRINFO) \ (assert((PTRINFO)->loadptr != NULL), (PTRINFO)->loadptr(PTR)) +#define STOREPTR(PTR, PTRINFO, VAL) \ + (assert((PTRINFO)->storeptr != NULL), (PTRINFO)->storeptr(PTR, VAL)) #endif static size_t @@ -334,6 +564,82 @@ load_count(const void *val, const struct counted_info *counted, return 0; } +static asn1_error_code +store_int(asn1_intmax intval, size_t size, void *val) +{ + switch (size) { + case 1: + if ((signed char)intval != intval) + return ASN1_OVERFLOW; + *(signed char *)val = intval; + return 0; + case 2: + if ((krb5_int16)intval != intval) + return ASN1_OVERFLOW; + *(krb5_int16 *)val = intval; + return 0; + case 4: + if ((krb5_int32)intval != intval) + return ASN1_OVERFLOW; + *(krb5_int32 *)val = intval; + return 0; + case 8: + if ((INT64_TYPE)intval != intval) + return ASN1_OVERFLOW; + *(INT64_TYPE *)intval = intval; + return 0; + default: + abort(); + } +} + +static asn1_error_code +store_uint(asn1_uintmax intval, size_t size, void *val) +{ + switch (size) { + case 1: + if ((unsigned char)intval != intval) + return ASN1_OVERFLOW; + *(unsigned char *)val = intval; + return 0; + case 2: + if ((krb5_ui_2)intval != intval) + return ASN1_OVERFLOW; + *(krb5_ui_2 *)val = intval; + return 0; + case 4: + if ((krb5_ui_4)intval != intval) + return ASN1_OVERFLOW; + *(krb5_ui_4 *)val = intval; + return 0; + case 8: + if ((UINT64_TYPE)intval != intval) + return ASN1_OVERFLOW; + *(UINT64_TYPE *)val = intval; + return 0; + default: + abort(); + } +} + +/* Store a count value in an integer field of a structure. If count is + * SIZE_MAX and the target is a signed field, store -1. */ +static asn1_error_code +store_count(size_t count, const struct counted_info *counted, void *val) +{ + void *countptr = (char *)val + counted->lenoff; + + if (counted->lensigned) { + if (count == SIZE_MAX) + return store_int(-1, counted->lensize, countptr); + else if ((asn1_intmax)count < 0) + return ASN1_OVERFLOW; + else + return store_int(count, counted->lensize, countptr); + } else + return store_uint(count, counted->lensize, countptr); +} + /* Split a DER encoding into tag and contents. Insert the contents into buf, * then return the length of the contents and the tag. */ static asn1_error_code @@ -356,6 +662,29 @@ split_der(asn1buf *buf, unsigned char *const *der, size_t len, *der + len - tag_out->length); } +/* + * Store the DER encoding given by t and asn1/len into the char * or + * unsigned char * pointed to by val. Set *count_out to the length of the + * DER encoding. + */ +static asn1_error_code +store_der(const taginfo *t, const unsigned char *asn1, size_t len, void *val, + size_t *count_out) +{ + unsigned char *der; + size_t der_len; + + *count_out = 0; + der_len = t->tag_len + len + t->tag_end_len; + der = malloc(der_len); + if (der == NULL) + return ENOMEM; + memcpy(der, asn1 - t->tag_len, der_len); + *(unsigned char **)val = der; + *count_out = der_len; + return 0; +} + static asn1_error_code encode_sequence(asn1buf *buf, const void *val, const struct seq_info *seq, size_t *len_out); @@ -448,6 +777,15 @@ encode_atype(asn1buf *buf, const void *val, const struct atype_info *a, tag_out->tagnum = tag->tagval; break; } + case atype_bool: + ret = k5_asn1_encode_bool(buf, load_int(val, a->size) + , &tag_out->length); + if (ret) + return ret; + tag_out->asn1class = UNIVERSAL; + tag_out->construction = PRIMITIVE; + tag_out->tagnum = ASN1_BOOLEAN; + break; case atype_int: ret = k5_asn1_encode_int(buf, load_int(val, a->size), &tag_out->length); @@ -467,8 +805,8 @@ encode_atype(asn1buf *buf, const void *val, const struct atype_info *a, tag_out->tagnum = ASN1_INTEGER; break; case atype_int_immediate: { - const int *iptr = a->tinfo; - ret = k5_asn1_encode_int(buf, *iptr, &tag_out->length); + const struct immediate_info *imm = a->tinfo; + ret = k5_asn1_encode_int(buf, imm->val, &tag_out->length); if (ret) return ret; tag_out->asn1class = UNIVERSAL; @@ -597,6 +935,645 @@ encode_sequence_of(asn1buf *buf, size_t seqlen, const void *val, return 0; } +/**** Functions for freeing C objects based on type info ****/ + +static void free_atype_ptr(const struct atype_info *a, void *val); +static void free_sequence(const struct seq_info *seq, void *val); +static void free_sequence_of(const struct atype_info *eltinfo, void *val, + size_t count); +static void free_cntype(const struct cntype_info *a, void *val, size_t count); + +/* + * Free a C object according to a type description. Do not free pointers at + * the first level; they may be referenced by other fields of a sequence, and + * will be freed by free_atype_ptr in a second pass. + */ +static void +free_atype(const struct atype_info *a, void *val) +{ + switch (a->type) { + case atype_fn: { + const struct fn_info *fn = a->tinfo; + if (fn->free != NULL) + fn->free(val); + break; + } + case atype_sequence: + free_sequence(a->tinfo, val); + break; + case atype_ptr: { + const struct ptr_info *ptrinfo = a->tinfo; + void *ptr = LOADPTR(val, ptrinfo); + if (ptr != NULL) { + free_atype(ptrinfo->basetype, ptr); + free_atype_ptr(ptrinfo->basetype, ptr); + } + break; + } + case atype_offset: { + const struct offset_info *off = a->tinfo; + assert(off->basetype != NULL); + free_atype(off->basetype, (char *)val + off->dataoff); + break; + } + case atype_optional: { + const struct optional_info *opt = a->tinfo; + free_atype(opt->basetype, val); + break; + } + case atype_counted: { + const struct counted_info *counted = a->tinfo; + void *dataptr = (char *)val + counted->dataoff; + size_t count; + if (load_count(val, counted, &count) == 0) + free_cntype(counted->basetype, dataptr, count); + break; + } + case atype_nullterm_sequence_of: + case atype_nonempty_nullterm_sequence_of: { + size_t count = get_nullterm_sequence_len(val, a->tinfo); + free_sequence_of(a->tinfo, val, count); + break; + } + case atype_tagged_thing: { + const struct tagged_info *tag = a->tinfo; + free_atype(tag->basetype, val); + break; + } + case atype_bool: + case atype_int: + case atype_uint: + case atype_int_immediate: + break; + default: + abort(); + } +} + +static void +free_atype_ptr(const struct atype_info *a, void *val) +{ + switch (a->type) { + case atype_fn: + case atype_sequence: + case atype_counted: + case atype_nullterm_sequence_of: + case atype_nonempty_nullterm_sequence_of: + case atype_bool: + case atype_int: + case atype_uint: + case atype_int_immediate: + break; + case atype_ptr: { + const struct ptr_info *ptrinfo = a->tinfo; + void *ptr = LOADPTR(val, ptrinfo); + free(ptr); + STOREPTR(NULL, ptrinfo, val); + break; + } + case atype_offset: { + const struct offset_info *off = a->tinfo; + assert(off->basetype != NULL); + free_atype_ptr(off->basetype, (char *)val + off->dataoff); + break; + } + case atype_optional: { + const struct optional_info *opt = a->tinfo; + free_atype_ptr(opt->basetype, val); + break; + } + case atype_tagged_thing: { + const struct tagged_info *tag = a->tinfo; + free_atype_ptr(tag->basetype, val); + break; + } + default: + abort(); + } +} + +static void +free_cntype(const struct cntype_info *c, void *val, size_t count) +{ + switch (c->type) { + case cntype_string: + case cntype_der: + free(*(char **)val); + *(char **)val = NULL; + break; + case cntype_seqof: { + const struct atype_info *a = c->tinfo; + const struct ptr_info *ptrinfo = a->tinfo; + void *seqptr = LOADPTR(val, ptrinfo); + free_sequence_of(ptrinfo->basetype, seqptr, count); + free(seqptr); + STOREPTR(NULL, ptrinfo, val); + break; + } + case cntype_choice: { + const struct choice_info *choice = c->tinfo; + if (count < choice->n_options) { + free_atype(choice->options[count], val); + free_atype_ptr(choice->options[count], val); + } + break; + } + default: + abort(); + } +} + +static void +free_sequence(const struct seq_info *seq, void *val) +{ + size_t i; + + for (i = 0; i < seq->n_fields; i++) + free_atype(seq->fields[i], val); + for (i = 0; i < seq->n_fields; i++) + free_atype_ptr(seq->fields[i], val); +} + +static void +free_sequence_of(const struct atype_info *eltinfo, void *val, size_t count) +{ + void *eltptr; + + assert(eltinfo->size != 0); + while (count-- > 0) { + eltptr = (char *)val + count * eltinfo->size; + free_atype(eltinfo, eltptr); + free_atype_ptr(eltinfo, eltptr); + } +} + +/**** Functions for decoding objects based on type info ****/ + +/* Return nonzero if t is an expected tag for an ASN.1 object of type a. */ +static int +check_atype_tag(const struct atype_info *a, const taginfo *t) +{ + switch (a->type) { + case atype_fn: { + const struct fn_info *fn = a->tinfo; + assert(fn->check_tag != NULL); + return fn->check_tag(t); + } + case atype_sequence: + case atype_nullterm_sequence_of: + case atype_nonempty_nullterm_sequence_of: + return (t->asn1class == UNIVERSAL && t->construction == CONSTRUCTED && + t->tagnum == ASN1_SEQUENCE); + case atype_ptr: { + const struct ptr_info *ptrinfo = a->tinfo; + return check_atype_tag(ptrinfo->basetype, t); + } + case atype_offset: { + const struct offset_info *off = a->tinfo; + return check_atype_tag(off->basetype, t); + } + case atype_optional: { + const struct optional_info *opt = a->tinfo; + return check_atype_tag(opt->basetype, t); + } + case atype_counted: { + const struct counted_info *counted = a->tinfo; + switch (counted->basetype->type) { + case cntype_string: { + const struct string_info *string = counted->basetype->tinfo; + return (t->asn1class == UNIVERSAL && + t->construction == PRIMITIVE && + t->tagnum == string->tagval); + } + case cntype_seqof: + return (t->asn1class == UNIVERSAL && + t->construction == CONSTRUCTED && + t->tagnum == ASN1_SEQUENCE); + case cntype_der: + /* + * We treat any tag as matching a stored DER encoding. In some + * cases we know what the tag should be; in others, we truly want + * to accept any tag. If it ever becomes an issue, we could add + * optional tag info to the type and check it here. + */ + return 1; + case cntype_choice: + /* + * ASN.1 choices may or may not be extensible. For now, we treat + * all choices as extensible and match any tag. We should consider + * modeling whether choices are extensible before making the + * encoder visible to plugins. + */ + return 1; + default: + abort(); + } + } + case atype_tagged_thing: { + const struct tagged_info *tag = a->tinfo; + /* NOTE: Doesn't check construction bit for implicit tags. */ + if (!tag->implicit && t->construction != tag->construction) + return 0; + return (t->asn1class == tag->tagtype && t->tagnum == tag->tagval); + } + case atype_bool: + return (t->asn1class == UNIVERSAL && t->construction == PRIMITIVE && + t->tagnum == ASN1_BOOLEAN); + case atype_int: + case atype_uint: + case atype_int_immediate: + return (t->asn1class == UNIVERSAL && t->construction == PRIMITIVE && + t->tagnum == ASN1_INTEGER); + default: + abort(); + } +} + +static asn1_error_code +decode_cntype(const taginfo *t, const unsigned char *asn1, size_t len, + const struct cntype_info *c, void *val, size_t *count_out); +static asn1_error_code +decode_atype_to_ptr(const taginfo *t, const unsigned char *asn1, size_t len, + const struct atype_info *basetype, void **ptr_out); +static asn1_error_code +decode_sequence(const unsigned char *asn1, size_t len, + const struct seq_info *seq, void *val); +static asn1_error_code +decode_sequence_of(const unsigned char *asn1, size_t len, + const struct atype_info *elemtype, void **seq_out, + size_t *count_out); + +/* Given the enclosing tag t, decode from asn1/len the contents of the ASN.1 + * type specified by a, placing the result into val (caller-allocated). */ +static asn1_error_code +decode_atype(const taginfo *t, const unsigned char *asn1, + size_t len, const struct atype_info *a, void *val) +{ + asn1_error_code ret; + + switch (a->type) { + case atype_fn: { + const struct fn_info *fn = a->tinfo; + assert(fn->dec != NULL); + return fn->dec(t, asn1, len, val); + } + case atype_sequence: + return decode_sequence(asn1, len, a->tinfo, val); + case atype_ptr: { + const struct ptr_info *ptrinfo = a->tinfo; + void *ptr = LOADPTR(val, ptrinfo); + assert(ptrinfo->basetype != NULL); + if (ptr != NULL) { + /* Container was already allocated by a previous sequence field. */ + return decode_atype(t, asn1, len, ptrinfo->basetype, ptr); + } else { + ret = decode_atype_to_ptr(t, asn1, len, ptrinfo->basetype, &ptr); + if (ret) + return ret; + STOREPTR(ptr, ptrinfo, val); + break; + } + } + case atype_offset: { + const struct offset_info *off = a->tinfo; + assert(off->basetype != NULL); + return decode_atype(t, asn1, len, off->basetype, + (char *)val + off->dataoff); + } + case atype_optional: { + const struct optional_info *opt = a->tinfo; + return decode_atype(t, asn1, len, opt->basetype, val); + } + case atype_counted: { + const struct counted_info *counted = a->tinfo; + void *dataptr = (char *)val + counted->dataoff; + size_t count; + assert(counted->basetype != NULL); + ret = decode_cntype(t, asn1, len, counted->basetype, dataptr, &count); + if (ret) + return ret; + return store_count(count, counted, val); + } + case atype_tagged_thing: { + const struct tagged_info *tag = a->tinfo; + taginfo inner_tag; + const taginfo *tp = t; + const unsigned char *rem; + size_t rlen; + if (!tag->implicit) { + ret = get_tag(asn1, len, &inner_tag, &asn1, &len, &rem, &rlen); + if (ret) + return ret; + /* Note: we don't check rlen (it should be 0). */ + tp = &inner_tag; + if (!check_atype_tag(tag->basetype, tp)) + return ASN1_BAD_ID; + } + return decode_atype(tp, asn1, len, tag->basetype, val); + } + case atype_bool: { + asn1_intmax intval; + ret = k5_asn1_decode_bool(asn1, len, &intval); + if (ret) + return ret; + return store_int(intval, a->size, val); + } + case atype_int: { + asn1_intmax intval; + ret = k5_asn1_decode_int(asn1, len, &intval); + if (ret) + return ret; + return store_int(intval, a->size, val); + } + case atype_uint: { + asn1_uintmax intval; + ret = k5_asn1_decode_uint(asn1, len, &intval); + if (ret) + return ret; + return store_uint(intval, a->size, val); + } + case atype_int_immediate: { + const struct immediate_info *imm = a->tinfo; + asn1_intmax intval; + ret = k5_asn1_decode_int(asn1, len, &intval); + if (ret) + return ret; + if (intval != imm->val && imm->err != 0) + return imm->err; + break; + } + default: + /* Null-terminated sequence types are handled in decode_atype_to_ptr, + * since they create variable-sized objects. */ + assert(a->type != atype_nullterm_sequence_of); + assert(a->type != atype_nonempty_nullterm_sequence_of); + assert(a->type > atype_min); + assert(a->type < atype_max); + abort(); + } + return 0; +} + +/* + * Given the enclosing tag t, decode from asn1/len the contents of the + * ASN.1 type described by c, placing the counted result into val/count_out. + * If the resulting count should be -1 (for an unknown union distinguisher), + * set *count_out to SIZE_MAX. + */ +static asn1_error_code +decode_cntype(const taginfo *t, const unsigned char *asn1, size_t len, + const struct cntype_info *c, void *val, size_t *count_out) +{ + asn1_error_code ret; + + switch (c->type) { + case cntype_string: { + const struct string_info *string = c->tinfo; + assert(string->dec != NULL); + return string->dec(asn1, len, val, count_out); + } + case cntype_der: + return store_der(t, asn1, len, val, count_out); + case cntype_seqof: { + const struct atype_info *a = c->tinfo; + const struct ptr_info *ptrinfo = a->tinfo; + void *seq; + assert(a->type == atype_ptr); + ret = decode_sequence_of(asn1, len, ptrinfo->basetype, &seq, + count_out); + if (ret) + return ret; + STOREPTR(seq, ptrinfo, val); + break; + } + case cntype_choice: { + const struct choice_info *choice = c->tinfo; + size_t i; + for (i = 0; i < choice->n_options; i++) { + if (check_atype_tag(choice->options[i], t)) { + ret = decode_atype(t, asn1, len, choice->options[i], val); + if (ret) + return ret; + *count_out = i; + return 0; + } + } + /* SIZE_MAX will be stored as -1 in the distinguisher. If we start + * modeling non-extensible choices we should check that here. */ + *count_out = SIZE_MAX; + break; + } + default: + assert(c->type > cntype_min); + assert(c->type < cntype_max); + abort(); + } + return 0; +} + +/* Add a null pointer to the end of a sequence. ptr is consumed on success + * (to be replaced by *ptr_out), left alone on failure. */ +static asn1_error_code +null_terminate(const struct atype_info *eltinfo, void *ptr, size_t count, + void **ptr_out) +{ + const struct ptr_info *ptrinfo = eltinfo->tinfo; + void *endptr; + + assert(eltinfo->type == atype_ptr); + ptr = realloc(ptr, (count + 1) * eltinfo->size); + if (ptr == NULL) + return ENOMEM; + endptr = (char *)ptr + count * eltinfo->size; + STOREPTR(NULL, ptrinfo, endptr); + *ptr_out = ptr; + return 0; +} + +static asn1_error_code +decode_atype_to_ptr(const taginfo *t, const unsigned char *asn1, + size_t len, const struct atype_info *a, + void **ptr_out) +{ + asn1_error_code ret; + void *ptr; + size_t count; + + *ptr_out = NULL; + switch (a->type) { + case atype_nullterm_sequence_of: + case atype_nonempty_nullterm_sequence_of: + ret = decode_sequence_of(asn1, len, a->tinfo, &ptr, &count); + if (ret) + return ret; + ret = null_terminate(a->tinfo, ptr, count, &ptr); + if (ret) { + free_sequence_of(a->tinfo, ptr, count); + return ret; + } + /* Historically we do not enforce non-emptiness of sequences when + * decoding, even when it is required by the ASN.1 type. */ + break; + default: + ptr = calloc(a->size, 1); + if (ptr == NULL) + return ENOMEM; + ret = decode_atype(t, asn1, len, a, ptr); + if (ret) { + free(ptr); + return ret; + } + break; + } + *ptr_out = ptr; + return 0; +} + +/* Initialize a C object when the corresponding ASN.1 type was omitted within a + * sequence. If the ASN.1 type is not optional, return ASN1_MISSING_FIELD. */ +static asn1_error_code +omit_atype(const struct atype_info *a, void *val) +{ + switch (a->type) + { + case atype_fn: + case atype_sequence: + case atype_nullterm_sequence_of: + case atype_nonempty_nullterm_sequence_of: + case atype_counted: + case atype_bool: + case atype_int: + case atype_uint: + case atype_int_immediate: + return ASN1_MISSING_FIELD; + case atype_ptr: { + const struct ptr_info *ptrinfo = a->tinfo; + return omit_atype(ptrinfo->basetype, val); + } + case atype_offset: { + const struct offset_info *off = a->tinfo; + return omit_atype(off->basetype, (char *)val + off->dataoff); + } + case atype_tagged_thing: { + const struct tagged_info *tag = a->tinfo; + return omit_atype(tag->basetype, val); + } + case atype_optional: { + const struct optional_info *opt = a->tinfo; + if (opt->init != NULL) + opt->init(val); + return 0; + } + default: + abort(); + } +} + +/* Decode an ASN.1 sequence into a C object. */ +static asn1_error_code +decode_sequence(const unsigned char *asn1, size_t len, + const struct seq_info *seq, void *val) +{ + asn1_error_code ret; + const unsigned char *contents; + size_t i, j, clen; + taginfo t; + + assert(seq->n_fields > 0); + for (i = 0; i < seq->n_fields; i++) { + if (len == 0) + break; + ret = get_tag(asn1, len, &t, &contents, &clen, &asn1, &len); + if (ret) + goto error; + /* + * Find the applicable sequence field. This logic is a little + * oversimplified; we could match an element to an optional extensible + * choice or optional stored-DER type when we ought to match a + * subsequent non-optional field. But it's unwise and (hopefully) very + * rare for ASN.1 modules to require such precision. + */ + for (; i < seq->n_fields; i++) { + if (check_atype_tag(seq->fields[i], &t)) + break; + ret = omit_atype(seq->fields[i], val); + if (ret) + goto error; + } + /* We currently model all sequences as extensible. We should consider + * changing this before making the encoder visible to plugins. */ + if (i == seq->n_fields) + break; + ret = decode_atype(&t, contents, clen, seq->fields[i], val); + if (ret) + goto error; + } + /* Initialize any fields in the C object which were not accounted for in + * the sequence. Error out if any of them aren't optional. */ + for (; i < seq->n_fields; i++) { + ret = omit_atype(seq->fields[i], val); + if (ret) + goto error; + } + return 0; + +error: + /* Free what we've decoded so far. Free pointers in a second pass in + * case multiple fields refer to the same pointer. */ + for (j = 0; j < i; j++) + free_atype(seq->fields[j], val); + for (j = 0; j < i; j++) + free_atype_ptr(seq->fields[j], val); + return ret; +} + +static asn1_error_code +decode_sequence_of(const unsigned char *asn1, size_t len, + const struct atype_info *elemtype, void **seq_out, + size_t *count_out) +{ + asn1_error_code ret; + void *seq = NULL, *newseq; + const unsigned char *contents; + size_t clen, count = 0; + taginfo t; + + *seq_out = NULL; + *count_out = 0; + while (len > 0) { + ret = get_tag(asn1, len, &t, &contents, &clen, &asn1, &len); + if (ret) + goto error; + if (!check_atype_tag(elemtype, &t)) { + ret = ASN1_BAD_ID; + goto error; + } + newseq = realloc(seq, (count + 1) * elemtype->size); + if (newseq == NULL) { + ret = ENOMEM; + goto error; + } + seq = newseq; + memset((char *)(seq + count * elemtype->size), 0, elemtype->size); + ret = decode_atype(&t, contents, clen, elemtype, + (char *)(seq + count * elemtype->size)); + if (ret) + goto error; + count++; + } + *seq_out = seq; + *count_out = count; + return 0; + +error: + free_sequence_of(elemtype, seq, count); + free(seq); + return ret; +} + +/* These three entry points are only needed for the kdc_req_body hack and may + * go away at some point. Define them here so we can use short names above. */ + asn1_error_code k5_asn1_encode_atype(asn1buf *buf, const void *val, const struct atype_info *a, taginfo *tag_out) @@ -604,6 +1581,13 @@ k5_asn1_encode_atype(asn1buf *buf, const void *val, const struct atype_info *a, return encode_atype(buf, val, a, tag_out); } +asn1_error_code +k5_asn1_decode_atype(const taginfo *t, const unsigned char *asn1, + size_t len, const struct atype_info *a, void *val) +{ + return decode_atype(t, asn1, len, a, val); +} + krb5_error_code k5_asn1_full_encode(const void *rep, const struct atype_info *a, krb5_data **code_out) @@ -631,3 +1615,24 @@ cleanup: asn1buf_destroy(&buf); return ret; } + +asn1_error_code +k5_asn1_full_decode(const krb5_data *code, const struct atype_info *a, + void **retrep) +{ + asn1_error_code ret; + const unsigned char *contents, *remainder; + size_t clen, rlen; + taginfo t; + + *retrep = NULL; + ret = get_tag((unsigned char *)code->data, code->length, &t, &contents, + &clen, &remainder, &rlen); + if (ret) + return ret; + /* rlen should be 0, but we don't check it (and due to padding in + * non-length-preserving enctypes, it will sometimes be nonzero). */ + if (!check_atype_tag(a, &t)) + return ASN1_BAD_ID; + return decode_atype_to_ptr(&t, contents, clen, a, retrep); +} diff --git a/src/lib/krb5/asn.1/asn1_encode.h b/src/lib/krb5/asn.1/asn1_encode.h index 5515aadf2..b7ae1704c 100644 --- a/src/lib/krb5/asn.1/asn1_encode.h +++ b/src/lib/krb5/asn.1/asn1_encode.h @@ -50,9 +50,25 @@ asn1_error_code k5_asn1_encode_bitstring(asn1buf *buf, asn1_error_code k5_asn1_encode_generaltime(asn1buf *buf, time_t val, size_t *len_out); +/* These functions are referenced by encoder structures. They handle the + * decoding of primitive ASN.1 types. */ +asn1_error_code k5_asn1_decode_bool(const unsigned char *asn1, size_t len, + asn1_intmax *val); +asn1_error_code k5_asn1_decode_int(const unsigned char *asn1, size_t len, + asn1_intmax *val); +asn1_error_code k5_asn1_decode_uint(const unsigned char *asn1, size_t len, + asn1_uintmax *val); +asn1_error_code k5_asn1_decode_generaltime(const unsigned char *asn1, + size_t len, time_t *time_out); +asn1_error_code k5_asn1_decode_bytestring(const unsigned char *asn1, + size_t len, unsigned char **str_out, + size_t *len_out); +asn1_error_code k5_asn1_decode_bitstring(const unsigned char *asn1, size_t len, + unsigned char **bits_out, + size_t *len_out); + /* - * An atype_info structure specifies how to encode a pointer to a C - * object as an ASN.1 type. + * An atype_info structure specifies how to map a C object to an ASN.1 value. * * We wind up with a lot of load-time relocations being done, which is * a bit annoying. Be careful about "fixing" that at the cost of too @@ -69,25 +85,26 @@ enum atype_type { /* For bounds checking only. By starting with 2, we guarantee that * zero-initialized storage will be recognized as invalid. */ atype_min = 1, - /* Encoder function to be called with address of . tinfo is a - * struct fn_info *. */ + /* Use a function table to handle encoding or decoding. tinfo is a struct + * fn_info *. */ atype_fn, - /* Pointer to actual thing to be encoded. tinfo is a struct ptr_info *. */ + /* C object is a pointer to the object to be encoded or decoded. tinfo is + * a struct ptr_info *. */ atype_ptr, - /* Actual thing to be encoded is at an offset from the original pointer. - * tinfo is a struct offset_info *. */ + /* C object to be encoded or decoded is at an offset from the original + * pointer. tinfo is a struct offset_info *. */ atype_offset, /* - * Indicates a sequence field which may or may not be present in an object. - * tinfo is a struct optional_info *. Must be used within a sequence, - * although the optional type may be nested within offset, ptr, and/or tag - * types. + * Indicates a sequence field which may or may not be present in the C + * object or ASN.1 sequence. tinfo is a struct optional_info *. Must be + * used within a sequence, although the optional type may be nested within + * offset, ptr, and/or tag types. */ atype_optional, /* - * Actual thing to be encoded is an object at an offset from the original - * pointer, combined with an integer at a different offset, in a manner - * specified by a cntype_info structure. tinfo is a struct counted_info *. + * C object contains an integer and another C object at specified offsets, + * to be combined and encoded or decoded as specified by a cntype_info + * structure. tinfo is a struct counted_info *. */ atype_counted, /* Sequence. tinfo is a struct seq_info *. */ @@ -102,12 +119,17 @@ enum atype_type { atype_nonempty_nullterm_sequence_of, /* Tagged version of another type. tinfo is a struct tagged_info *. */ atype_tagged_thing, - /* Signed or unsigned integer. tinfo is NULL (the atype_info size field is - * used to determine the width). */ + /* Boolean value. tinfo is NULL (size field determines C type width). */ + atype_bool, + /* Signed or unsigned integer. tinfo is NULL. */ atype_int, atype_uint, - /* Integer value taken from the type info, not from the object being - * encoded. tinfo is an int *. */ + /* + * Integer value taken from the type info, not from the object being + * encoded. tinfo is a struct immediate_info * giving the integer value + * and error code to return if a decoded object doesn't match it (or 0 if + * the value shouldn't be checked on decode). + */ atype_int_immediate, /* Unused except for bounds checking. */ atype_max @@ -121,10 +143,15 @@ struct atype_info { struct fn_info { asn1_error_code (*enc)(asn1buf *, const void *, taginfo *); + asn1_error_code (*dec)(const taginfo *, const unsigned char *, size_t, + void *); + int (*check_tag)(const taginfo *); + void (*free)(void *); }; struct ptr_info { - const void *(*loadptr)(const void *); + void *(*loadptr)(const void *); + void (*storeptr)(void *, void *); const struct atype_info *basetype; }; @@ -135,6 +162,7 @@ struct offset_info { struct optional_info { int (*is_present)(const void *); + void (*init)(void *); const struct atype_info *basetype; }; @@ -151,29 +179,37 @@ struct tagged_info { const struct atype_info *basetype; }; -/* A cntype_info structure specifies how to encode a pointer to a C object and - * count (length or union distinguisher) as an ASN.1 object. */ +struct immediate_info { + asn1_intmax val; + asn1_error_code err; +}; + +/* A cntype_info structure specifies how to map a C object and count (length or + * union distinguisher) to an ASN.1 value. */ enum cntype_type { cntype_min = 1, /* * Apply an encoder function (contents only) and wrap it in a universal - * primitive tag. The object must be a char * or unsigned char *. tinfo + * primitive tag. The C object must be a char * or unsigned char *. tinfo * is a struct string_info *. */ cntype_string, - /* Insert a pre-made DER encoding contained at the pointer and length. The - * object must be a char * or unsigned char *. tinfo is NULL. */ + /* + * The C object is a DER encoding (with tag), to be simply inserted on + * encode or stored on decode. The C object must be a char * or unsigned + * char *. tinfo is NULL. + */ cntype_der, - /* Encode a counted sequence of a given base type. tinfo is a struct + /* An ASN.1 sequence-of value, represtened in C as a counted array. struct * atype_info * giving the base type, which must be of type atype_ptr. */ cntype_seqof, - /* Encode one of several alternatives from a union object, using the count - * as a distinguisher. tinfo is a struct choice_info *. */ + /* An ASN.1 choice, represented in C as a distinguisher and union. tinfo + * is a struct choice_info *. */ cntype_choice, cntype_max @@ -187,6 +223,8 @@ struct cntype_info { struct string_info { asn1_error_code (*enc)(asn1buf *, unsigned char *const *, size_t, size_t *); + asn1_error_code (*dec)(const unsigned char *, size_t, unsigned char **, + size_t *); unsigned int tagval : 5; }; @@ -221,14 +259,14 @@ struct seq_info { * Nothing else should directly define the atype_info structures. */ -/* Define a type using an encoder function. */ -#define DEFFNTYPE(DESCNAME, CTYPENAME, ENCFN) \ - typedef CTYPENAME aux_type_##DESCNAME; \ - static const struct fn_info aux_info_##DESCNAME = { \ - ENCFN \ - }; \ - const struct atype_info k5_atype_##DESCNAME = { \ - atype_fn, sizeof(CTYPENAME), &aux_info_##DESCNAME \ +/* Define a type using a function table. */ +#define DEFFNTYPE(DESCNAME, CTYPENAME, ENCFN, DECFN, CHECKFN, FREEFN) \ + typedef CTYPENAME aux_type_##DESCNAME; \ + static const struct fn_info aux_info_##DESCNAME = { \ + ENCFN, DECFN, CHECKFN, FREEFN \ + }; \ + const struct atype_info k5_atype_##DESCNAME = { \ + atype_fn, sizeof(CTYPENAME), &aux_info_##DESCNAME \ } /* A sequence, defined by the indicated series of types, and an optional * function indicating which fields are not present. */ @@ -240,6 +278,12 @@ struct seq_info { const struct atype_info k5_atype_##DESCNAME = { \ atype_sequence, sizeof(CTYPENAME), &aux_seqinfo_##DESCNAME \ } +/* A boolean type. */ +#define DEFBOOLTYPE(DESCNAME, CTYPENAME) \ + typedef CTYPENAME aux_type_##DESCNAME; \ + const struct atype_info k5_atype_##DESCNAME = { \ + atype_bool, sizeof(CTYPENAME), NULL \ + } /* Integer types. */ #define DEFINTTYPE(DESCNAME, CTYPENAME) \ typedef CTYPENAME aux_type_##DESCNAME; \ @@ -251,11 +295,13 @@ struct seq_info { const struct atype_info k5_atype_##DESCNAME = { \ atype_uint, sizeof(CTYPENAME), NULL \ } -#define DEFINT_IMMEDIATE(DESCNAME, VAL) \ - typedef int aux_type_##DESCNAME; \ - static const int aux_int_##DESCNAME = VAL; \ - const struct atype_info k5_atype_##DESCNAME = { \ - atype_int_immediate, 0, &aux_int_##DESCNAME \ +#define DEFINT_IMMEDIATE(DESCNAME, VAL, ERR) \ + typedef int aux_type_##DESCNAME; \ + static const struct immediate_info aux_info_##DESCNAME = { \ + VAL, ERR \ + }; \ + const struct atype_info k5_atype_##DESCNAME = { \ + atype_int_immediate, 0, &aux_info_##DESCNAME \ } /* Pointers to other types, to be encoded as those other types. */ @@ -263,7 +309,7 @@ struct seq_info { #define DEFPTRTYPE(DESCNAME,BASEDESCNAME) \ typedef aux_type_##BASEDESCNAME *aux_type_##DESCNAME; \ static const struct ptr_info aux_info_##DESCNAME = { \ - NULL, &k5_atype_##BASEDESCNAME \ + NULL, NULL, &k5_atype_##BASEDESCNAME \ }; \ const struct atype_info k5_atype_##DESCNAME = { \ atype_ptr, sizeof(aux_type_##DESCNAME), \ @@ -272,13 +318,19 @@ struct seq_info { #else #define DEFPTRTYPE(DESCNAME,BASEDESCNAME) \ typedef aux_type_##BASEDESCNAME *aux_type_##DESCNAME; \ - static const void * \ + static void * \ aux_loadptr_##DESCNAME(const void *p) \ { \ return *(aux_type_##DESCNAME *)p; \ } \ + static void \ + aux_storeptr_##DESCNAME(void *ptr, void *val) \ + { \ + *(aux_type_##DESCNAME *)val = ptr; \ + } \ static const struct ptr_info aux_info_##DESCNAME = { \ - aux_loadptr_##DESCNAME, &k5_atype_##BASEDESCNAME \ + aux_loadptr_##DESCNAME, aux_storeptr_##DESCNAME, \ + &k5_atype_##BASEDESCNAME \ }; \ const struct atype_info k5_atype_##DESCNAME = { \ atype_ptr, sizeof(aux_type_##DESCNAME), \ @@ -314,11 +366,11 @@ struct seq_info { DEFCOUNTEDTYPE_base(DESCNAME, STYPE, DATAFIELD, COUNTFIELD, 1, CDESC) /* Optional sequence fields. The basic form allows arbitrary test and - * initializer functions to be used. */ -#define DEFOPTIONALTYPE(DESCNAME, PRESENT, BASEDESC) \ - typedef aux_type_##BASEDESC aux_type_##DESCNAME; \ + * initializer functions to be used. INIT may be null. */ +#define DEFOPTIONALTYPE(DESCNAME, PRESENT, INIT, BASEDESC) \ + typedef aux_type_##BASEDESC aux_type_##DESCNAME; \ static const struct optional_info aux_info_##DESCNAME = { \ - PRESENT, &k5_atype_##BASEDESC \ + PRESENT, INIT, &k5_atype_##BASEDESC \ }; \ const struct atype_info k5_atype_##DESCNAME = { \ atype_optional, sizeof(aux_type_##DESCNAME), \ @@ -326,23 +378,23 @@ struct seq_info { } /* This form defines an is_present function for a zero-valued integer or null * pointer of the base type's C type. */ -#define DEFOPTIONALZEROTYPE(DESCNAME, BASEDESC) \ - static int \ - aux_present_##DESCNAME(const void *p) \ - { \ - return *(aux_type_##BASEDESC *)p != 0; \ - } \ - DEFOPTIONALTYPE(DESCNAME, aux_present_##DESCNAME, BASEDESC) +#define DEFOPTIONALZEROTYPE(DESCNAME, BASEDESC) \ + static int \ + aux_present_##DESCNAME(const void *p) \ + { \ + return *(aux_type_##BASEDESC *)p != 0; \ + } \ + DEFOPTIONALTYPE(DESCNAME, aux_present_##DESCNAME, NULL, BASEDESC) /* This form defines an is_present function for a null or empty null-terminated * array of the base type's C type. */ -#define DEFOPTIONALEMPTYTYPE(DESCNAME, BASEDESC) \ - static int \ - aux_present_##DESCNAME(const void *p) \ - { \ - const aux_type_##BASEDESC *val = p; \ - return (*val != NULL && **val != NULL); \ - } \ - DEFOPTIONALTYPE(DESCNAME, aux_present_##DESCNAME, BASEDESC) +#define DEFOPTIONALEMPTYTYPE(DESCNAME, BASEDESC) \ + static int \ + aux_present_##DESCNAME(const void *p) \ + { \ + const aux_type_##BASEDESC *val = p; \ + return (*val != NULL && **val != NULL); \ + } \ + DEFOPTIONALTYPE(DESCNAME, aux_present_##DESCNAME, NULL, BASEDESC) /* * This encodes a pointer-to-pointer-to-thing where the passed-in @@ -419,11 +471,11 @@ struct seq_info { * + Accept a following semicolon syntactically. */ -#define DEFCOUNTEDSTRINGTYPE(DESCNAME, DTYPE, LTYPE, ENCFN, TAGVAL) \ +#define DEFCOUNTEDSTRINGTYPE(DESCNAME, DTYPE, LTYPE, ENCFN, DECFN, TAGVAL) \ typedef DTYPE aux_ptrtype_##DESCNAME; \ typedef LTYPE aux_counttype_##DESCNAME; \ static const struct string_info aux_info_##DESCNAME = { \ - ENCFN, TAGVAL \ + ENCFN, DECFN, TAGVAL \ }; \ const struct cntype_info k5_cntype_##DESCNAME = { \ cntype_string, &aux_info_##DESCNAME \ @@ -474,11 +526,20 @@ asn1_error_code k5_asn1_encode_atype(asn1buf *buf, const void *val, const struct atype_info *a, taginfo *tag_out); +/* Decode the tag and contents of a type, storing the result in the + * caller-allocated C object val. Used only by kdc_req_body. */ +asn1_error_code +k5_asn1_decode_atype(const taginfo *t, const unsigned char *asn1, + size_t len, const struct atype_info *a, void *val); + /* Returns a completed encoding, with tag and in the correct byte order, in an * allocated krb5_data. */ extern krb5_error_code k5_asn1_full_encode(const void *rep, const struct atype_info *a, krb5_data **code_out); +asn1_error_code +k5_asn1_full_decode(const krb5_data *code, const struct atype_info *a, + void **rep_out); #define MAKE_ENCODER(FNAME, DESC) \ krb5_error_code \ @@ -488,6 +549,25 @@ k5_asn1_full_encode(const void *rep, const struct atype_info *a, } \ extern int dummy /* gobble semicolon */ +#define MAKE_DECODER(FNAME, DESC) \ + krb5_error_code \ + FNAME(const krb5_data *code, aux_type_##DESC **rep_out) \ + { \ + asn1_error_code ret; \ + void *rep; \ + *rep_out = NULL; \ + ret = k5_asn1_full_decode(code, &k5_atype_##DESC, &rep); \ + if (ret) \ + return ret; \ + *rep_out = rep; \ + return 0; \ + } \ + extern int dummy /* gobble semicolon */ + +#define MAKE_CODEC(TYPENAME, DESC) \ + MAKE_ENCODER(encode_##TYPENAME, DESC); \ + MAKE_DECODER(decode_##TYPENAME, DESC) + #include /* * Ugly hack! diff --git a/src/lib/krb5/asn.1/asn1_get.h b/src/lib/krb5/asn.1/asn1_get.h index adc895e19..e4c6107e6 100644 --- a/src/lib/krb5/asn.1/asn1_get.h +++ b/src/lib/krb5/asn.1/asn1_get.h @@ -49,6 +49,11 @@ typedef struct { asn1_tagnum tagnum; size_t length; int indef; + + /* When decoding, stores the leading and trailing lengths of a tag. Used + * by store_der(). */ + size_t tag_len; + size_t tag_end_len; } taginfo; asn1_error_code asn1_get_tag_2 (asn1buf *buf, taginfo *tinfo); diff --git a/src/lib/krb5/asn.1/asn1_k_encode.c b/src/lib/krb5/asn.1/asn1_k_encode.c index ea4b02316..6be95f580 100644 --- a/src/lib/krb5/asn.1/asn1_k_encode.c +++ b/src/lib/krb5/asn.1/asn1_k_encode.c @@ -27,8 +27,9 @@ #include "asn1_encode.h" #include -DEFINT_IMMEDIATE(krb5_version, KVNO); +DEFINT_IMMEDIATE(krb5_version, KVNO, KRB5KDC_ERR_BAD_PVNO); +DEFBOOLTYPE(bool, krb5_boolean); DEFINTTYPE(int32, krb5_int32); DEFPTRTYPE(int32_ptr, int32); DEFCOUNTEDSEQOFTYPE(cseqof_int32, krb5_int32, int32_ptr); @@ -38,7 +39,6 @@ DEFUINTTYPE(uint, unsigned int); DEFUINTTYPE(octet, krb5_octet); DEFUINTTYPE(ui_4, krb5_ui_4); DEFOPTIONALZEROTYPE(opt_uint, uint); -DEFOPTIONALZEROTYPE(opt_ui_4, ui_4); static int nonempty_data(const void *p) @@ -49,23 +49,27 @@ nonempty_data(const void *p) DEFCOUNTEDDERTYPE(der, char *, unsigned int); DEFCOUNTEDTYPE(der_data, krb5_data, data, length, der); -DEFOPTIONALTYPE(opt_der_data, nonempty_data, der_data); +DEFOPTIONALTYPE(opt_der_data, nonempty_data, NULL, der_data); DEFCOUNTEDSTRINGTYPE(octetstring, unsigned char *, unsigned int, - k5_asn1_encode_bytestring, ASN1_OCTETSTRING); + k5_asn1_encode_bytestring, k5_asn1_decode_bytestring, + ASN1_OCTETSTRING); DEFCOUNTEDSTRINGTYPE(s_octetstring, char *, unsigned int, - k5_asn1_encode_bytestring, ASN1_OCTETSTRING); + k5_asn1_encode_bytestring, k5_asn1_decode_bytestring, + ASN1_OCTETSTRING); DEFCOUNTEDTYPE(ostring_data, krb5_data, data, length, s_octetstring); DEFPTRTYPE(ostring_data_ptr, ostring_data); -DEFOPTIONALTYPE(opt_ostring_data, nonempty_data, ostring_data); +DEFOPTIONALTYPE(opt_ostring_data, nonempty_data, NULL, ostring_data); DEFOPTIONALZEROTYPE(opt_ostring_data_ptr, ostring_data_ptr); DEFCOUNTEDSTRINGTYPE(generalstring, char *, unsigned int, - k5_asn1_encode_bytestring, ASN1_GENERALSTRING); + k5_asn1_encode_bytestring, k5_asn1_decode_bytestring, + ASN1_GENERALSTRING); DEFCOUNTEDSTRINGTYPE(u_generalstring, unsigned char *, unsigned int, - k5_asn1_encode_bytestring, ASN1_GENERALSTRING); + k5_asn1_encode_bytestring, k5_asn1_decode_bytestring, + ASN1_GENERALSTRING); DEFCOUNTEDTYPE(gstring_data, krb5_data, data, length, generalstring); -DEFOPTIONALTYPE(opt_gstring_data, nonempty_data, gstring_data); +DEFOPTIONALTYPE(opt_gstring_data, nonempty_data, NULL, gstring_data); DEFPTRTYPE(gstring_data_ptr, gstring_data); DEFCOUNTEDSEQOFTYPE(cseqof_gstring_data, krb5_int32, gstring_data_ptr); @@ -84,6 +88,45 @@ DEFSEQTYPE(principal_data, krb5_principal_data, princname_fields); DEFPTRTYPE(principal, principal_data); DEFOPTIONALZEROTYPE(opt_principal, principal); +/* + * Define the seqno type, which is an ASN.1 integer represented in a krb5_ui_4. + * When decoding, negative 32-bit numbers are accepted for interoperability + * with old implementations. + */ +static asn1_error_code +encode_seqno(asn1buf *buf, const void *p, taginfo *rettag) +{ + krb5_ui_4 val = *(krb5_ui_4 *)p; + rettag->asn1class = UNIVERSAL; + rettag->construction = PRIMITIVE; + rettag->tagnum = ASN1_INTEGER; + return k5_asn1_encode_uint(buf, val, &rettag->length); +} +static asn1_error_code +decode_seqno(const taginfo *t, const unsigned char *asn1, size_t len, void *p) +{ + asn1_error_code ret; + asn1_intmax val; + ret = k5_asn1_decode_int(asn1, len, &val); + if (ret) + return ret; + if (val < KRB5_INT32_MIN || val > 0xFFFFFFFF) + return ASN1_OVERFLOW; + /* Negative values will cast correctly to krb5_ui_4. */ + *(krb5_ui_4 *)p = val; + return 0; +} +static int +check_seqno(const taginfo *t) +{ + return (t->asn1class == UNIVERSAL && t->construction == PRIMITIVE && + t->tagnum == ASN1_INTEGER); +} +DEFFNTYPE(seqno, krb5_ui_4, encode_seqno, decode_seqno, check_seqno, NULL); +DEFOPTIONALZEROTYPE(opt_seqno, seqno); + +/* Define the kerberos_time type, which is an ASN.1 generaltime represented in + * a krb5_timestamp. */ static asn1_error_code encode_kerberos_time(asn1buf *buf, const void *p, taginfo *rettag) { @@ -94,7 +137,26 @@ encode_kerberos_time(asn1buf *buf, const void *p, taginfo *rettag) rettag->tagnum = ASN1_GENERALTIME; return k5_asn1_encode_generaltime(buf, val, &rettag->length); } -DEFFNTYPE(kerberos_time, krb5_timestamp, encode_kerberos_time); +static asn1_error_code +decode_kerberos_time(const taginfo *t, const unsigned char *asn1, size_t len, + void *p) +{ + asn1_error_code ret; + time_t val; + ret = k5_asn1_decode_generaltime(asn1, len, &val); + if (ret) + return ret; + *(krb5_timestamp *)p = val; + return 0; +} +static int +check_kerberos_time(const taginfo *t) +{ + return (t->asn1class == UNIVERSAL && t->construction == PRIMITIVE && + t->tagnum == ASN1_GENERALTIME); +} +DEFFNTYPE(kerberos_time, krb5_timestamp, encode_kerberos_time, + decode_kerberos_time, check_kerberos_time, NULL); DEFOPTIONALZEROTYPE(opt_kerberos_time, kerberos_time); DEFFIELD(address_0, krb5_address, addrtype, 0, int32); @@ -123,13 +185,10 @@ nonempty_enc_data(const void *p) const krb5_enc_data *val = p; return (val->ciphertext.data != NULL); } -DEFOPTIONALTYPE(opt_encrypted_data, nonempty_enc_data, encrypted_data); +DEFOPTIONALTYPE(opt_encrypted_data, nonempty_enc_data, NULL, encrypted_data); -/* - * The encode_bitstring function wants an array of bytes (since PKINIT - * may provide something that isn't 32 bits), but krb5_flags is stored - * as a 32-bit integer in host order. - */ +/* Define the krb5_flags type, which is an ASN.1 bit string represented in a + * 32-bit integer. */ static asn1_error_code encode_krb5_flags(asn1buf *buf, const void *p, taginfo *rettag) { @@ -140,7 +199,32 @@ encode_krb5_flags(asn1buf *buf, const void *p, taginfo *rettag) rettag->tagnum = ASN1_BITSTRING; return k5_asn1_encode_bitstring(buf, &cptr, 4, &rettag->length); } -DEFFNTYPE(krb5_flags, krb5_flags, encode_krb5_flags); +static asn1_error_code +decode_krb5_flags(const taginfo *t, const unsigned char *asn1, size_t len, + void *val) +{ + asn1_error_code ret; + size_t i, blen; + krb5_flags f = 0; + unsigned char *bits; + ret = k5_asn1_decode_bitstring(asn1, len, &bits, &blen); + if (ret) + return ret; + /* Copy up to 32 bits into f, starting at the most significant byte. */ + for (i = 0; i < blen && i < 4; i++) + f |= bits[i] << (8 * (3 - i)); + *(krb5_flags *)val = f; + free(bits); + return 0; +} +static int +check_krb5_flags(const taginfo *t) +{ + return (t->asn1class == UNIVERSAL && t->construction == PRIMITIVE && + t->tagnum == ASN1_BITSTRING); +} +DEFFNTYPE(krb5_flags, krb5_flags, encode_krb5_flags, decode_krb5_flags, + check_krb5_flags, NULL); DEFOPTIONALZEROTYPE(opt_krb5_flags, krb5_flags); DEFFIELD(authdata_0, krb5_authdata, ad_type, 0, int32); @@ -174,7 +258,46 @@ DEFNULLTERMSEQOFTYPE(seqof_checksum, checksum_ptr); DEFPTRTYPE(ptr_seqof_checksum, seqof_checksum); DEFOPTIONALZEROTYPE(opt_checksum_ptr, checksum_ptr); -DEFFIELD(last_req_0, krb5_last_req_entry, lr_type, 0, int32); +/* Define the last_req_type type, which is a krb5_int32 with some massaging + * on decode for backward compatibility. */ +static asn1_error_code +encode_lr_type(asn1buf *buf, const void *p, taginfo *rettag) +{ + krb5_int32 val = *(krb5_int32 *)p; + rettag->asn1class = UNIVERSAL; + rettag->construction = PRIMITIVE; + rettag->tagnum = ASN1_INTEGER; + return k5_asn1_encode_int(buf, val, &rettag->length); +} +static asn1_error_code +decode_lr_type(const taginfo *t, const unsigned char *asn1, size_t len, + void *p) +{ + asn1_error_code ret; + asn1_intmax val; + ret = k5_asn1_decode_int(asn1, len, &val); + if (ret) + return ret; + if (val > KRB5_INT32_MAX || val < KRB5_INT32_MIN) + return ASN1_OVERFLOW; +#ifdef KRB5_GENEROUS_LR_TYPE + /* If type is in the 128-255 range, treat it as a negative 8-bit value. */ + if (val >= 128 && val <= 255) + val -= 256; +#endif + *(krb5_int32 *)p = val; + return 0; +} +static int +check_lr_type(const taginfo *t) +{ + return (t->asn1class == UNIVERSAL && t->construction == PRIMITIVE && + t->tagnum == ASN1_INTEGER); +} +DEFFNTYPE(last_req_type, krb5_int32, encode_lr_type, decode_lr_type, + check_lr_type, NULL); + +DEFFIELD(last_req_0, krb5_last_req_entry, lr_type, 0, last_req_type); DEFFIELD(last_req_1, krb5_last_req_entry, value, 1, kerberos_time); static const struct atype_info *lr_fields[] = { &k5_atype_last_req_0, &k5_atype_last_req_1 @@ -214,6 +337,18 @@ DEFNONEMPTYNULLTERMSEQOFTYPE(seqof_ticket,ticket_ptr); DEFPTRTYPE(ptr_seqof_ticket, seqof_ticket); DEFOPTIONALEMPTYTYPE(opt_ptr_seqof_ticket, ptr_seqof_ticket); +static int +is_enc_kdc_rep_start_set(const void *p) +{ + const krb5_enc_kdc_rep_part *val = p; + return (val->times.starttime != 0); +} +static void +init_enc_kdc_rep_start(void *p) +{ + krb5_enc_kdc_rep_part *val = p; + val->times.starttime = val->times.authtime; +} static int is_renewable_flag_set(const void *p) { @@ -227,13 +362,15 @@ DEFFIELD(enc_kdc_rep_3, krb5_enc_kdc_rep_part, key_exp, 3, opt_kerberos_time); DEFFIELD(enc_kdc_rep_4, krb5_enc_kdc_rep_part, flags, 4, krb5_flags); DEFFIELD(enc_kdc_rep_5, krb5_enc_kdc_rep_part, times.authtime, 5, kerberos_time); -DEFFIELD(enc_kdc_rep_6, krb5_enc_kdc_rep_part, times.starttime, 6, - opt_kerberos_time); +DEFFIELD(enc_kdc_rep_6_def, krb5_enc_kdc_rep_part, times.starttime, 6, + kerberos_time); +DEFOPTIONALTYPE(enc_kdc_rep_6, is_enc_kdc_rep_start_set, + init_enc_kdc_rep_start, enc_kdc_rep_6_def); DEFFIELD(enc_kdc_rep_7, krb5_enc_kdc_rep_part, times.endtime, 7, kerberos_time); DEFFIELD(enc_kdc_rep_8_def, krb5_enc_kdc_rep_part, times.renew_till, 8, kerberos_time); -DEFOPTIONALTYPE(enc_kdc_rep_8, is_renewable_flag_set, enc_kdc_rep_8_def); +DEFOPTIONALTYPE(enc_kdc_rep_8, is_renewable_flag_set, NULL, enc_kdc_rep_8_def); DEFFIELD(enc_kdc_rep_9, krb5_enc_kdc_rep_part, server, 9, realm_of_principal); DEFFIELD(enc_kdc_rep_10, krb5_enc_kdc_rep_part, server, 10, principal); DEFFIELD(enc_kdc_rep_11, krb5_enc_kdc_rep_part, caddrs, 11, @@ -257,11 +394,11 @@ DEFSEQTYPE(enc_kdc_rep_part, krb5_enc_kdc_rep_part, enc_kdc_rep_part_fields); */ typedef struct kdc_req_hack { krb5_kdc_req v; - krb5_data *server_realm; + krb5_data server_realm; } kdc_req_hack; DEFFIELD(req_body_0, kdc_req_hack, v.kdc_options, 0, krb5_flags); DEFFIELD(req_body_1, kdc_req_hack, v.client, 1, opt_principal); -DEFFIELD(req_body_2, kdc_req_hack, server_realm, 2, gstring_data_ptr); +DEFFIELD(req_body_2, kdc_req_hack, server_realm, 2, gstring_data); DEFFIELD(req_body_3, kdc_req_hack, v.server, 3, opt_principal); DEFFIELD(req_body_4, kdc_req_hack, v.from, 4, opt_kerberos_time); DEFFIELD(req_body_5, kdc_req_hack, v.till, 5, kerberos_time); @@ -288,16 +425,75 @@ encode_kdc_req_body(asn1buf *buf, const void *p, taginfo *tag_out) h.v = *val; if (val->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) { if (val->second_ticket != NULL && val->second_ticket[0] != NULL) - h.server_realm = &val->second_ticket[0]->server->realm; + h.server_realm = val->second_ticket[0]->server->realm; else return ASN1_MISSING_FIELD; } else if (val->server != NULL) - h.server_realm = &val->server->realm; + h.server_realm = val->server->realm; else return ASN1_MISSING_FIELD; return k5_asn1_encode_atype(buf, &h, &k5_atype_kdc_req_body_hack, tag_out); } -DEFFNTYPE(kdc_req_body, krb5_kdc_req, encode_kdc_req_body); +static void +free_kdc_req_body(void *val) +{ + krb5_kdc_req *req = val; + krb5_free_principal(NULL, req->client); + krb5_free_principal(NULL, req->server); + free(req->ktype); + krb5_free_addresses(NULL, req->addresses); + free(req->authorization_data.ciphertext.data); + krb5_free_tickets(NULL, req->second_ticket); +} +static asn1_error_code +decode_kdc_req_body(const taginfo *t, const unsigned char *asn1, size_t len, + void *val) +{ + asn1_error_code ret; + kdc_req_hack h; + krb5_kdc_req *b = val; + memset(&h, 0, sizeof(h)); + ret = k5_asn1_decode_atype(t, asn1, len, &k5_atype_kdc_req_body_hack, &h); + if (ret) + return ret; + b->kdc_options = h.v.kdc_options; + b->client = h.v.client; + b->server = h.v.server; + b->from = h.v.from; + b->till = h.v.till; + b->rtime = h.v.rtime; + b->nonce = h.v.nonce; + b->ktype = h.v.ktype; + b->nktypes = h.v.nktypes; + b->addresses = h.v.addresses; + b->authorization_data = h.v.authorization_data; + b->second_ticket = h.v.second_ticket; + if (b->client != NULL && b->server != NULL) { + ret = krb5int_copy_data_contents(NULL, &h.server_realm, + &b->client->realm); + if (ret) { + free_kdc_req_body(b); + free(h.server_realm.data); + memset(&h, 0, sizeof(h)); + return ret; + } + b->server->realm = h.server_realm; + } else if (b->client != NULL) + b->client->realm = h.server_realm; + else if (b->server != NULL) + b->server->realm = h.server_realm; + else + free(h.server_realm.data); + return 0; +} +static int +check_kdc_req_body(const taginfo *t) +{ + return (t->asn1class == UNIVERSAL && t->construction == CONSTRUCTED && + t->tagnum == ASN1_SEQUENCE); +} +DEFFNTYPE(kdc_req_body, krb5_kdc_req, encode_kdc_req_body, decode_kdc_req_body, + check_kdc_req_body, free_kdc_req_body); /* end ugly hack */ DEFFIELD(transited_0, krb5_transited, tr_type, 0, octet); @@ -316,8 +512,8 @@ is_safe_timestamp_set(const void *p) DEFFIELD(safe_body_0, krb5_safe, user_data, 0, ostring_data); DEFFIELD(safe_body_1, krb5_safe, timestamp, 1, opt_kerberos_time); DEFFIELD(safe_body_2_def, krb5_safe, usec, 2, int32); -DEFOPTIONALTYPE(safe_body_2, is_safe_timestamp_set, safe_body_2_def); -DEFFIELD(safe_body_3, krb5_safe, seq_number, 3, opt_ui_4); +DEFOPTIONALTYPE(safe_body_2, is_safe_timestamp_set, NULL, safe_body_2_def); +DEFFIELD(safe_body_3, krb5_safe, seq_number, 3, opt_seqno); DEFFIELD(safe_body_4, krb5_safe, s_address, 4, address_ptr); DEFFIELD(safe_body_5, krb5_safe, r_address, 5, opt_address_ptr); static const struct atype_info *safe_body_fields[] = { @@ -351,15 +547,21 @@ DEFNULLTERMSEQOFTYPE(seqof_cred_info, cred_info_ptr); DEFPTRTYPE(ptrseqof_cred_info, seqof_cred_info); static int -is_etype_info_salt_present(const void *p) +is_salt_present(const void *p) { const krb5_etype_info_entry *val = p; return (val->length != KRB5_ETYPE_NO_SALT); } +static void +init_no_salt(void *p) +{ + krb5_etype_info_entry *val = p; + val->length = KRB5_ETYPE_NO_SALT; +} DEFFIELD(etype_info_0, krb5_etype_info_entry, etype, 0, int32); DEFCNFIELD(etype_info_1_def, krb5_etype_info_entry, salt, length, 1, octetstring); -DEFOPTIONALTYPE(etype_info_1, is_etype_info_salt_present, etype_info_1_def); +DEFOPTIONALTYPE(etype_info_1, is_salt_present, init_no_salt, etype_info_1_def); static const struct atype_info *etype_info_entry_fields[] = { &k5_atype_etype_info_0, &k5_atype_etype_info_1 }; @@ -368,7 +570,7 @@ DEFSEQTYPE(etype_info_entry, krb5_etype_info_entry, etype_info_entry_fields); /* First field is the same as etype-info. */ DEFCNFIELD(etype_info2_1_def, krb5_etype_info_entry, salt, length, 1, u_generalstring); -DEFOPTIONALTYPE(etype_info2_1, is_etype_info_salt_present, etype_info2_1_def); +DEFOPTIONALTYPE(etype_info2_1, is_salt_present, NULL, etype_info2_1_def); DEFFIELD(etype_info2_2, krb5_etype_info_entry, s2kparams, 2, opt_ostring_data); static const struct atype_info *etype_info2_entry_fields[] = { &k5_atype_etype_info_0, &k5_atype_etype_info2_1, &k5_atype_etype_info2_2 @@ -439,7 +641,7 @@ DEFFIELD(authenticator_4, krb5_authenticator, cusec, 4, int32); DEFFIELD(authenticator_5, krb5_authenticator, ctime, 5, kerberos_time); DEFFIELD(authenticator_6, krb5_authenticator, subkey, 6, opt_ptr_encryption_key); -DEFFIELD(authenticator_7, krb5_authenticator, seq_number, 7, opt_ui_4); +DEFFIELD(authenticator_7, krb5_authenticator, seq_number, 7, opt_seqno); DEFFIELD(authenticator_8, krb5_authenticator, authorization_data, 8, opt_auth_data_ptr); static const struct atype_info *authenticator_fields[] = { @@ -452,13 +654,27 @@ static const struct atype_info *authenticator_fields[] = { DEFSEQTYPE(untagged_authenticator, krb5_authenticator, authenticator_fields); DEFAPPTAGGEDTYPE(authenticator, 2, untagged_authenticator); +static int +is_enc_tkt_start_set(const void *p) +{ + const krb5_enc_tkt_part *val = p; + return (val->times.starttime != 0); +} +static void +init_enc_tkt_start(void *p) +{ + krb5_enc_tkt_part *val = p; + val->times.starttime = val->times.authtime; +} DEFFIELD(enc_tkt_0, krb5_enc_tkt_part, flags, 0, krb5_flags); DEFFIELD(enc_tkt_1, krb5_enc_tkt_part, session, 1, ptr_encryption_key); DEFFIELD(enc_tkt_2, krb5_enc_tkt_part, client, 2, realm_of_principal); DEFFIELD(enc_tkt_3, krb5_enc_tkt_part, client, 3, principal); DEFFIELD(enc_tkt_4, krb5_enc_tkt_part, transited, 4, transited); DEFFIELD(enc_tkt_5, krb5_enc_tkt_part, times.authtime, 5, kerberos_time); -DEFFIELD(enc_tkt_6, krb5_enc_tkt_part, times.starttime, 6, opt_kerberos_time); +DEFFIELD(enc_tkt_6_def, krb5_enc_tkt_part, times.starttime, 6, kerberos_time); +DEFOPTIONALTYPE(enc_tkt_6, is_enc_tkt_start_set, init_enc_tkt_start, + enc_tkt_6_def); DEFFIELD(enc_tkt_7, krb5_enc_tkt_part, times.endtime, 7, kerberos_time); DEFFIELD(enc_tkt_8, krb5_enc_tkt_part, times.renew_till, 8, opt_kerberos_time); DEFFIELD(enc_tkt_9, krb5_enc_tkt_part, caddrs, 9, @@ -474,38 +690,26 @@ static const struct atype_info *enc_tkt_part_fields[] = { DEFSEQTYPE(untagged_enc_tkt_part, krb5_enc_tkt_part, enc_tkt_part_fields); DEFAPPTAGGEDTYPE(enc_tkt_part, 3, untagged_enc_tkt_part); +DEFAPPTAGGEDTYPE(enc_as_rep_part, 24, enc_kdc_rep_part); DEFAPPTAGGEDTYPE(enc_tgs_rep_part, 26, enc_kdc_rep_part); -DEFINT_IMMEDIATE(as_rep_msg_type, KRB5_AS_REP); DEFCTAGGEDTYPE(kdc_rep_0, 0, krb5_version); -DEFCTAGGEDTYPE(as_rep_1, 1, as_rep_msg_type); +DEFFIELD(kdc_rep_1, krb5_kdc_rep, msg_type, 1, uint); DEFFIELD(kdc_rep_2, krb5_kdc_rep, padata, 2, opt_ptr_seqof_pa_data); DEFFIELD(kdc_rep_3, krb5_kdc_rep, client, 3, realm_of_principal); DEFFIELD(kdc_rep_4, krb5_kdc_rep, client, 4, principal); DEFFIELD(kdc_rep_5, krb5_kdc_rep, ticket, 5, ticket_ptr); DEFFIELD(kdc_rep_6, krb5_kdc_rep, enc_part, 6, encrypted_data); -static const struct atype_info *as_rep_fields[] = { - &k5_atype_kdc_rep_0, &k5_atype_as_rep_1, &k5_atype_kdc_rep_2, - &k5_atype_kdc_rep_3, &k5_atype_kdc_rep_4, &k5_atype_kdc_rep_5, - &k5_atype_kdc_rep_6 -}; -DEFSEQTYPE(untagged_as_rep, krb5_kdc_rep, as_rep_fields); -DEFAPPTAGGEDTYPE(as_rep, 11, untagged_as_rep); - -/* TGS-REP ::= [APPLICATION 13] KDC-REP */ -/* But KDC-REP needs to know what type it's being encapsulated in, so use a - * separate atype. Most fields are the same. */ -DEFINT_IMMEDIATE(tgs_rep_msg_type, KRB5_TGS_REP); -DEFCTAGGEDTYPE(tgs_rep_1, 1, tgs_rep_msg_type); -static const struct atype_info *tgs_rep_fields[] = { - &k5_atype_kdc_rep_0, &k5_atype_tgs_rep_1, &k5_atype_kdc_rep_2, +static const struct atype_info *kdc_rep_fields[] = { + &k5_atype_kdc_rep_0, &k5_atype_kdc_rep_1, &k5_atype_kdc_rep_2, &k5_atype_kdc_rep_3, &k5_atype_kdc_rep_4, &k5_atype_kdc_rep_5, &k5_atype_kdc_rep_6 }; -DEFSEQTYPE(untagged_tgs_rep, krb5_kdc_rep, tgs_rep_fields); -DEFAPPTAGGEDTYPE(tgs_rep, 13, untagged_tgs_rep); +DEFSEQTYPE(kdc_rep, krb5_kdc_rep, kdc_rep_fields); +DEFAPPTAGGEDTYPE(as_rep, 11, kdc_rep); +DEFAPPTAGGEDTYPE(tgs_rep, 13, kdc_rep); -DEFINT_IMMEDIATE(ap_req_msg_type, ASN1_KRB_AP_REQ); +DEFINT_IMMEDIATE(ap_req_msg_type, ASN1_KRB_AP_REQ, 0); DEFCTAGGEDTYPE(ap_req_0, 0, krb5_version); DEFCTAGGEDTYPE(ap_req_1, 1, ap_req_msg_type); DEFFIELD(ap_req_2, krb5_ap_req, ap_options, 2, krb5_flags); @@ -518,7 +722,7 @@ static const struct atype_info *ap_req_fields[] = { DEFSEQTYPE(untagged_ap_req, krb5_ap_req, ap_req_fields); DEFAPPTAGGEDTYPE(ap_req, 14, untagged_ap_req); -DEFINT_IMMEDIATE(ap_rep_msg_type, ASN1_KRB_AP_REP); +DEFINT_IMMEDIATE(ap_rep_msg_type, ASN1_KRB_AP_REP, 0); DEFCTAGGEDTYPE(ap_rep_0, 0, krb5_version); DEFCTAGGEDTYPE(ap_rep_1, 1, ap_rep_msg_type); DEFFIELD(ap_rep_2, krb5_ap_rep, enc_part, 2, encrypted_data); @@ -532,7 +736,7 @@ DEFFIELD(ap_rep_enc_part_0, krb5_ap_rep_enc_part, ctime, 0, kerberos_time); DEFFIELD(ap_rep_enc_part_1, krb5_ap_rep_enc_part, cusec, 1, int32); DEFFIELD(ap_rep_enc_part_2, krb5_ap_rep_enc_part, subkey, 2, opt_ptr_encryption_key); -DEFFIELD(ap_rep_enc_part_3, krb5_ap_rep_enc_part, seq_number, 3, opt_ui_4); +DEFFIELD(ap_rep_enc_part_3, krb5_ap_rep_enc_part, seq_number, 3, opt_seqno); static const struct atype_info *ap_rep_enc_part_fields[] = { &k5_atype_ap_rep_enc_part_0, &k5_atype_ap_rep_enc_part_1, &k5_atype_ap_rep_enc_part_2, &k5_atype_ap_rep_enc_part_3 @@ -543,29 +747,38 @@ DEFAPPTAGGEDTYPE(ap_rep_enc_part, 27, untagged_ap_rep_enc_part); /* First context tag is 1. Fourth field is the encoding of the krb5_kdc_req * structure as a KDC-REQ-BODY. */ -DEFINT_IMMEDIATE(as_req_msg_type, KRB5_AS_REQ); -DEFCTAGGEDTYPE(as_req_1, 1, krb5_version); +DEFCTAGGEDTYPE(kdc_req_1, 1, krb5_version); +DEFFIELD(kdc_req_2, krb5_kdc_req, msg_type, 2, uint); +DEFFIELD(kdc_req_3, krb5_kdc_req, padata, 3, opt_ptr_seqof_pa_data); +DEFCTAGGEDTYPE(kdc_req_4, 4, kdc_req_body); +static const struct atype_info *kdc_req_fields[] = { + &k5_atype_kdc_req_1, &k5_atype_kdc_req_2, &k5_atype_kdc_req_3, + &k5_atype_kdc_req_4 +}; +DEFSEQTYPE(kdc_req, krb5_kdc_req, kdc_req_fields); +DEFAPPTAGGEDTYPE(as_req, 10, kdc_req); +DEFAPPTAGGEDTYPE(tgs_req, 12, kdc_req); + +/* This is only needed because libkrb5 doesn't set msg_type when encoding + * krb5_kdc_reqs. If we fix that, we can use the above types for encoding. */ +DEFINT_IMMEDIATE(as_req_msg_type, KRB5_AS_REQ, 0); DEFCTAGGEDTYPE(as_req_2, 2, as_req_msg_type); -DEFFIELD(as_req_3, krb5_kdc_req, padata, 3, opt_ptr_seqof_pa_data); -DEFCTAGGEDTYPE(as_req_4, 4, kdc_req_body); +DEFINT_IMMEDIATE(tgs_req_msg_type, KRB5_TGS_REQ, 0); +DEFCTAGGEDTYPE(tgs_req_2, 2, tgs_req_msg_type); static const struct atype_info *as_req_fields[] = { - &k5_atype_as_req_1, &k5_atype_as_req_2, &k5_atype_as_req_3, - &k5_atype_as_req_4 + &k5_atype_kdc_req_1, &k5_atype_as_req_2, &k5_atype_kdc_req_3, + &k5_atype_kdc_req_4 }; -DEFSEQTYPE(untagged_as_req, krb5_kdc_req, as_req_fields); -DEFAPPTAGGEDTYPE(as_req, 10, untagged_as_req); - -/* Most fields are the same as as_req. */ -DEFINT_IMMEDIATE(tgs_req_msg_type, KRB5_TGS_REQ); -DEFCTAGGEDTYPE(tgs_req_2, 2, tgs_req_msg_type); static const struct atype_info *tgs_req_fields[] = { - &k5_atype_as_req_1, &k5_atype_tgs_req_2, &k5_atype_as_req_3, - &k5_atype_as_req_4 + &k5_atype_kdc_req_1, &k5_atype_tgs_req_2, &k5_atype_kdc_req_3, + &k5_atype_kdc_req_4 }; +DEFSEQTYPE(untagged_as_req, krb5_kdc_req, as_req_fields); +DEFAPPTAGGEDTYPE(as_req_encode, 10, untagged_as_req); DEFSEQTYPE(untagged_tgs_req, krb5_kdc_req, tgs_req_fields); -DEFAPPTAGGEDTYPE(tgs_req, 12, untagged_tgs_req); +DEFAPPTAGGEDTYPE(tgs_req_encode, 12, untagged_tgs_req); -DEFINT_IMMEDIATE(safe_msg_type, ASN1_KRB_SAFE); +DEFINT_IMMEDIATE(safe_msg_type, ASN1_KRB_SAFE, 0); DEFCTAGGEDTYPE(safe_0, 0, krb5_version); DEFCTAGGEDTYPE(safe_1, 1, safe_msg_type); DEFCTAGGEDTYPE(safe_2, 2, safe_body); @@ -594,7 +807,7 @@ DEFSEQTYPE(untagged_safe_with_body, struct krb5_safe_with_body, DEFAPPTAGGEDTYPE(safe_with_body, 20, untagged_safe_with_body); /* Third tag is [3] instead of [2]. */ -DEFINT_IMMEDIATE(priv_msg_type, ASN1_KRB_PRIV); +DEFINT_IMMEDIATE(priv_msg_type, ASN1_KRB_PRIV, 0); DEFCTAGGEDTYPE(priv_0, 0, krb5_version); DEFCTAGGEDTYPE(priv_1, 1, priv_msg_type); DEFFIELD(priv_3, krb5_priv, enc_part, 3, encrypted_data); @@ -613,8 +826,9 @@ is_priv_timestamp_set(const void *p) DEFFIELD(priv_enc_part_0, krb5_priv_enc_part, user_data, 0, ostring_data); DEFFIELD(priv_enc_part_1, krb5_priv_enc_part, timestamp, 1, opt_kerberos_time); DEFFIELD(priv_enc_part_2_def, krb5_priv_enc_part, usec, 2, int32); -DEFOPTIONALTYPE(priv_enc_part_2, is_priv_timestamp_set, priv_enc_part_2_def); -DEFFIELD(priv_enc_part_3, krb5_priv_enc_part, seq_number, 3, opt_ui_4); +DEFOPTIONALTYPE(priv_enc_part_2, is_priv_timestamp_set, NULL, + priv_enc_part_2_def); +DEFFIELD(priv_enc_part_3, krb5_priv_enc_part, seq_number, 3, opt_seqno); DEFFIELD(priv_enc_part_4, krb5_priv_enc_part, s_address, 4, address_ptr); DEFFIELD(priv_enc_part_5, krb5_priv_enc_part, r_address, 5, opt_address_ptr); static const struct atype_info *priv_enc_part_fields[] = { @@ -625,7 +839,7 @@ static const struct atype_info *priv_enc_part_fields[] = { DEFSEQTYPE(untagged_priv_enc_part, krb5_priv_enc_part, priv_enc_part_fields); DEFAPPTAGGEDTYPE(priv_enc_part, 28, untagged_priv_enc_part); -DEFINT_IMMEDIATE(cred_msg_type, ASN1_KRB_CRED); +DEFINT_IMMEDIATE(cred_msg_type, ASN1_KRB_CRED, 0); DEFCTAGGEDTYPE(cred_0, 0, krb5_version); DEFCTAGGEDTYPE(cred_1, 1, cred_msg_type); DEFFIELD(cred_2, krb5_cred, tickets, 2, ptr_seqof_ticket); @@ -647,7 +861,8 @@ DEFFIELD(enc_cred_part_0, krb5_cred_enc_part, ticket_info, 0, DEFFIELD(enc_cred_part_1, krb5_cred_enc_part, nonce, 1, opt_int32); DEFFIELD(enc_cred_part_2, krb5_cred_enc_part, timestamp, 2, opt_kerberos_time); DEFFIELD(enc_cred_part_3_def, krb5_cred_enc_part, usec, 3, int32); -DEFOPTIONALTYPE(enc_cred_part_3, is_cred_timestamp_set, enc_cred_part_3_def); +DEFOPTIONALTYPE(enc_cred_part_3, is_cred_timestamp_set, NULL, + enc_cred_part_3_def); DEFFIELD(enc_cred_part_4, krb5_cred_enc_part, s_address, 4, opt_address_ptr); DEFFIELD(enc_cred_part_5, krb5_cred_enc_part, r_address, 5, opt_address_ptr); static const struct atype_info *enc_cred_part_fields[] = { @@ -658,7 +873,7 @@ static const struct atype_info *enc_cred_part_fields[] = { DEFSEQTYPE(untagged_enc_cred_part, krb5_cred_enc_part, enc_cred_part_fields); DEFAPPTAGGEDTYPE(enc_cred_part, 29, untagged_enc_cred_part); -DEFINT_IMMEDIATE(error_msg_type, ASN1_KRB_ERROR); +DEFINT_IMMEDIATE(error_msg_type, ASN1_KRB_ERROR, 0); DEFCTAGGEDTYPE(error_0, 0, krb5_version); DEFCTAGGEDTYPE(error_1, 1, error_msg_type); DEFFIELD(error_2, krb5_error, ctime, 2, opt_kerberos_time); @@ -715,7 +930,7 @@ is_s4u_principal_present(const void *p) krb5_const_principal val = *(krb5_const_principal *)p; return (val->length != 0); } -DEFOPTIONALTYPE(opt_s4u_principal, is_s4u_principal_present, principal); +DEFOPTIONALTYPE(opt_s4u_principal, is_s4u_principal_present, NULL, principal); DEFFIELD(s4u_userid_0, krb5_s4u_userid, nonce, 0, int32); DEFFIELD(s4u_userid_1, krb5_s4u_userid, user, 1, opt_s4u_principal); DEFFIELD(s4u_userid_2, krb5_s4u_userid, user, 2, realm_of_principal); @@ -734,6 +949,12 @@ static const struct atype_info *pa_s4u_x509_user_fields[] = { }; DEFSEQTYPE(pa_s4u_x509_user, krb5_pa_s4u_x509_user, pa_s4u_x509_user_fields); +DEFFIELD(pa_pac_req_0, krb5_pa_pac_req, include_pac, 0, bool); +static const struct atype_info *pa_pac_req_fields[] = { + &k5_atype_pa_pac_req_0 +}; +DEFSEQTYPE(pa_pac_req, krb5_pa_pac_req, pa_pac_req_fields); + /* RFC 4537 */ DEFCOUNTEDTYPE(etype_list, krb5_etype_list, etypes, length, cseqof_int32); @@ -880,65 +1101,155 @@ DEFSEQTYPE(iakerb_finished, krb5_iakerb_finished, iakerb_finished_fields); /* Exported complete encoders -- these produce a krb5_data with the encoding in the correct byte order. */ -MAKE_ENCODER(encode_krb5_authenticator, authenticator); -MAKE_ENCODER(encode_krb5_ticket, ticket); -MAKE_ENCODER(encode_krb5_encryption_key, encryption_key); -MAKE_ENCODER(encode_krb5_enc_tkt_part, enc_tkt_part); -/* XXX We currently (for backwards compatibility) encode both - EncASRepPart and EncTGSRepPart with application tag 26. */ -MAKE_ENCODER(encode_krb5_enc_kdc_rep_part, enc_tgs_rep_part); -MAKE_ENCODER(encode_krb5_as_rep, as_rep); -MAKE_ENCODER(encode_krb5_tgs_rep, tgs_rep); -MAKE_ENCODER(encode_krb5_ap_req, ap_req); -MAKE_ENCODER(encode_krb5_ap_rep, ap_rep); -MAKE_ENCODER(encode_krb5_ap_rep_enc_part, ap_rep_enc_part); -MAKE_ENCODER(encode_krb5_as_req, as_req); -MAKE_ENCODER(encode_krb5_tgs_req, tgs_req); -MAKE_ENCODER(encode_krb5_kdc_req_body, kdc_req_body); -MAKE_ENCODER(encode_krb5_safe, safe); +MAKE_CODEC(krb5_authenticator, authenticator); +MAKE_CODEC(krb5_ticket, ticket); +MAKE_CODEC(krb5_encryption_key, encryption_key); +MAKE_CODEC(krb5_enc_tkt_part, enc_tkt_part); /* - * encode_krb5_safe_with_body - * - * Like encode_krb5_safe(), except takes a saved KRB-SAFE-BODY - * encoding to avoid problems with re-encoding. + * For backwards compatibility, we encode both EncASRepPart and EncTGSRepPart + * with application tag 26. On decode, we accept either app tag and set the + * msg_type field of the resulting structure. This could be simplified and + * pushed up into libkrb5. */ +MAKE_ENCODER(encode_krb5_enc_kdc_rep_part, enc_tgs_rep_part); +krb5_error_code +decode_krb5_enc_kdc_rep_part(const krb5_data *code, + krb5_enc_kdc_rep_part **rep_out) +{ + asn1_error_code ret; + krb5_enc_kdc_rep_part *rep; + void *rep_ptr; + krb5_msgtype msg_type = KRB5_TGS_REP; + + *rep_out = NULL; + ret = k5_asn1_full_decode(code, &k5_atype_enc_tgs_rep_part, &rep_ptr); + if (ret == ASN1_BAD_ID) { + msg_type = KRB5_AS_REP; + ret = k5_asn1_full_decode(code, &k5_atype_enc_as_rep_part, &rep_ptr); + } + if (ret) + return ret; + rep = rep_ptr; + rep->msg_type = msg_type; + *rep_out = rep; + return 0; +} + +MAKE_CODEC(krb5_as_rep, as_rep); +MAKE_CODEC(krb5_tgs_rep, tgs_rep); +MAKE_CODEC(krb5_ap_req, ap_req); +MAKE_CODEC(krb5_ap_rep, ap_rep); +MAKE_CODEC(krb5_ap_rep_enc_part, ap_rep_enc_part); +MAKE_ENCODER(encode_krb5_as_req, as_req_encode); +MAKE_DECODER(decode_krb5_as_req, as_req); +MAKE_ENCODER(encode_krb5_tgs_req, tgs_req_encode); +MAKE_DECODER(decode_krb5_tgs_req, tgs_req); +MAKE_CODEC(krb5_kdc_req_body, kdc_req_body); +MAKE_CODEC(krb5_safe, safe); + +/* encode_krb5_safe_with_body takes a saved KRB-SAFE-BODY encoding to avoid + * mismatches from re-encoding if the sender isn't quite DER-compliant. */ MAKE_ENCODER(encode_krb5_safe_with_body, safe_with_body); -MAKE_ENCODER(encode_krb5_priv, priv); -MAKE_ENCODER(encode_krb5_enc_priv_part, priv_enc_part); -MAKE_ENCODER(encode_krb5_checksum, checksum); - -MAKE_ENCODER(encode_krb5_cred, krb5_cred); -MAKE_ENCODER(encode_krb5_enc_cred_part, enc_cred_part); -MAKE_ENCODER(encode_krb5_error, krb5_error); -MAKE_ENCODER(encode_krb5_authdata, auth_data); -MAKE_ENCODER(encode_krb5_etype_info, etype_info); -MAKE_ENCODER(encode_krb5_etype_info2, etype_info2); -MAKE_ENCODER(encode_krb5_enc_data, encrypted_data); -MAKE_ENCODER(encode_krb5_pa_enc_ts, pa_enc_ts); -MAKE_ENCODER(encode_krb5_padata_sequence, seqof_pa_data); +/* + * decode_krb5_safe_with_body fully decodes a KRB-SAFE, but also returns + * the KRB-SAFE-BODY encoding. This interface was designed for an earlier + * generation of decoder and should probably be re-thought. + */ +krb5_error_code +decode_krb5_safe_with_body(const krb5_data *code, krb5_safe **rep_out, + krb5_data **body_out) +{ + asn1_error_code ret; + void *swb_ptr, *safe_ptr; + struct krb5_safe_with_body *swb; + krb5_safe *safe; + + ret = k5_asn1_full_decode(code, &k5_atype_safe_with_body, &swb_ptr); + if (ret) + return ret; + swb = swb_ptr; + ret = k5_asn1_full_decode(swb->body, &k5_atype_safe_body, &safe_ptr); + if (ret) { + krb5_free_safe(NULL, swb->safe); + krb5_free_data(NULL, swb->body); + free(swb); + return ret; + } + safe = safe_ptr; + safe->checksum = swb->safe->checksum; + free(swb->safe); + *rep_out = safe; + *body_out = swb->body; + free(swb); + return 0; +} + +MAKE_CODEC(krb5_priv, priv); +MAKE_CODEC(krb5_enc_priv_part, priv_enc_part); +MAKE_CODEC(krb5_checksum, checksum); + +MAKE_CODEC(krb5_cred, krb5_cred); +MAKE_CODEC(krb5_enc_cred_part, enc_cred_part); +MAKE_CODEC(krb5_error, krb5_error); +MAKE_CODEC(krb5_authdata, auth_data); +MAKE_CODEC(krb5_etype_info, etype_info); +MAKE_CODEC(krb5_etype_info2, etype_info2); +MAKE_CODEC(krb5_enc_data, encrypted_data); +MAKE_CODEC(krb5_pa_enc_ts, pa_enc_ts); +MAKE_CODEC(krb5_padata_sequence, seqof_pa_data); /* sam preauth additions */ -MAKE_ENCODER(encode_krb5_sam_challenge_2, sam_challenge_2); -MAKE_ENCODER(encode_krb5_sam_challenge_2_body, sam_challenge_2_body); -MAKE_ENCODER(encode_krb5_enc_sam_response_enc_2, enc_sam_response_enc_2); -MAKE_ENCODER(encode_krb5_sam_response_2, sam_response_2); +MAKE_CODEC(krb5_sam_challenge_2, sam_challenge_2); +MAKE_CODEC(krb5_sam_challenge_2_body, sam_challenge_2_body); +MAKE_CODEC(krb5_enc_sam_response_enc_2, enc_sam_response_enc_2); +MAKE_CODEC(krb5_sam_response_2, sam_response_2); + +/* setpw_req has an odd decoder interface which should probably be + * normalized. */ MAKE_ENCODER(encode_krb5_setpw_req, setpw_req); -MAKE_ENCODER(encode_krb5_pa_for_user, pa_for_user); +krb5_error_code +decode_krb5_setpw_req(const krb5_data *code, krb5_data **password_out, + krb5_principal *target_out) +{ + asn1_error_code ret; + void *req_ptr; + struct krb5_setpw_req *req; + krb5_data *data; + + *password_out = NULL; + *target_out = NULL; + data = malloc(sizeof(*data)); + if (data == NULL) + return ENOMEM; + ret = k5_asn1_full_decode(code, &k5_atype_setpw_req, &req_ptr); + if (ret) { + free(data); + return ret; + } + req = req_ptr; + *data = req->password; + *password_out = data; + *target_out = req->target; + return 0; +} + +MAKE_CODEC(krb5_pa_for_user, pa_for_user); MAKE_ENCODER(encode_krb5_s4u_userid, s4u_userid); -MAKE_ENCODER(encode_krb5_pa_s4u_x509_user, pa_s4u_x509_user); -MAKE_ENCODER(encode_krb5_etype_list, etype_list); +MAKE_CODEC(krb5_pa_s4u_x509_user, pa_s4u_x509_user); +MAKE_DECODER(decode_krb5_pa_pac_req, pa_pac_req); +MAKE_CODEC(krb5_etype_list, etype_list); -MAKE_ENCODER(encode_krb5_pa_fx_fast_request, pa_fx_fast_request); -MAKE_ENCODER(encode_krb5_fast_req, fast_req); -MAKE_ENCODER(encode_krb5_pa_fx_fast_reply, pa_fx_fast_reply); -MAKE_ENCODER(encode_krb5_fast_response, fast_response); +MAKE_CODEC(krb5_pa_fx_fast_request, pa_fx_fast_request); +MAKE_CODEC(krb5_fast_req, fast_req); +MAKE_CODEC(krb5_pa_fx_fast_reply, pa_fx_fast_reply); +MAKE_CODEC(krb5_fast_response, fast_response); -MAKE_ENCODER(encode_krb5_ad_kdcissued, ad_kdc_issued); +MAKE_CODEC(krb5_ad_kdcissued, ad_kdc_issued); MAKE_ENCODER(encode_krb5_ad_signedpath_data, ad_signedpath_data); -MAKE_ENCODER(encode_krb5_ad_signedpath, ad_signedpath); -MAKE_ENCODER(encode_krb5_iakerb_header, iakerb_header); -MAKE_ENCODER(encode_krb5_iakerb_finished, iakerb_finished); +MAKE_CODEC(krb5_ad_signedpath, ad_signedpath); +MAKE_CODEC(krb5_iakerb_header, iakerb_header); +MAKE_CODEC(krb5_iakerb_finished, iakerb_finished); /* * PKINIT @@ -947,7 +1258,8 @@ MAKE_ENCODER(encode_krb5_iakerb_finished, iakerb_finished); #ifndef DISABLE_PKINIT DEFCOUNTEDSTRINGTYPE(object_identifier, char *, unsigned int, - k5_asn1_encode_bytestring, ASN1_OBJECTIDENTIFIER); + k5_asn1_encode_bytestring, k5_asn1_decode_bytestring, + ASN1_OBJECTIDENTIFIER); DEFCOUNTEDTYPE(oid_data, krb5_data, data, length, object_identifier); DEFPTRTYPE(oid_data_ptr, oid_data); @@ -1040,7 +1352,8 @@ DEFSEQTYPE(pk_authenticator_draft9, krb5_pk_authenticator_draft9, pk_authenticator_draft9_fields); DEFCOUNTEDSTRINGTYPE(s_bitstring, char *, unsigned int, - k5_asn1_encode_bitstring, ASN1_BITSTRING); + k5_asn1_encode_bitstring, k5_asn1_decode_bitstring, + ASN1_BITSTRING); DEFCOUNTEDTYPE(bitstring_data, krb5_data, data, length, s_bitstring); /* RFC 3280. No context tags. */ @@ -1128,6 +1441,12 @@ static const struct atype_info *pa_pk_as_req_draft9_fields[] = { }; DEFSEQTYPE(pa_pk_as_req_draft9, krb5_pa_pk_as_req_draft9, pa_pk_as_req_draft9_fields); +/* For decoding, we only care about the first field; we can ignore the rest. */ +static const struct atype_info *pa_pk_as_req_draft9_decode_fields[] = { + &k5_atype_pa_pk_as_req9_0 +}; +DEFSEQTYPE(pa_pk_as_req_draft9_decode, krb5_pa_pk_as_req_draft9, + pa_pk_as_req_draft9_decode_fields); DEFFIELD_IMPLICIT(dh_rep_info_0, krb5_dh_rep_info, dhSignedData, 0, ostring_data); @@ -1193,18 +1512,19 @@ DEFCHOICETYPE(pa_pk_as_rep_draft9_choice, DEFCOUNTEDTYPE_SIGNED(pa_pk_as_rep_draft9, krb5_pa_pk_as_rep_draft9, u, choice, pa_pk_as_rep_draft9_choice); -MAKE_ENCODER(encode_krb5_pa_pk_as_req, pa_pk_as_req); +MAKE_CODEC(krb5_pa_pk_as_req, pa_pk_as_req); MAKE_ENCODER(encode_krb5_pa_pk_as_req_draft9, pa_pk_as_req_draft9); -MAKE_ENCODER(encode_krb5_pa_pk_as_rep, pa_pk_as_rep); +MAKE_DECODER(decode_krb5_pa_pk_as_req_draft9, pa_pk_as_req_draft9_decode); +MAKE_CODEC(krb5_pa_pk_as_rep, pa_pk_as_rep); MAKE_ENCODER(encode_krb5_pa_pk_as_rep_draft9, pa_pk_as_rep_draft9); -MAKE_ENCODER(encode_krb5_auth_pack, auth_pack); -MAKE_ENCODER(encode_krb5_auth_pack_draft9, auth_pack_draft9); -MAKE_ENCODER(encode_krb5_kdc_dh_key_info, kdc_dh_key_info); -MAKE_ENCODER(encode_krb5_reply_key_pack, reply_key_pack); -MAKE_ENCODER(encode_krb5_reply_key_pack_draft9, reply_key_pack_draft9); -MAKE_ENCODER(encode_krb5_td_trusted_certifiers, - seqof_external_principal_identifier); -MAKE_ENCODER(encode_krb5_td_dh_parameters, seqof_algorithm_identifier); +MAKE_CODEC(krb5_auth_pack, auth_pack); +MAKE_CODEC(krb5_auth_pack_draft9, auth_pack_draft9); +MAKE_CODEC(krb5_kdc_dh_key_info, kdc_dh_key_info); +MAKE_CODEC(krb5_reply_key_pack, reply_key_pack); +MAKE_CODEC(krb5_reply_key_pack_draft9, reply_key_pack_draft9); +MAKE_CODEC(krb5_td_trusted_certifiers, seqof_external_principal_identifier); +MAKE_CODEC(krb5_td_dh_parameters, seqof_algorithm_identifier); +MAKE_DECODER(decode_krb5_principal_name, pkinit_krb5_principal_name_data); #else /* DISABLE_PKINIT */ @@ -1235,4 +1555,4 @@ DEFSEQTYPE(typed_data, krb5_pa_data, typed_data_fields); DEFPTRTYPE(typed_data_ptr, typed_data); DEFNULLTERMSEQOFTYPE(seqof_typed_data, typed_data_ptr); -MAKE_ENCODER(encode_krb5_typed_data, seqof_typed_data); +MAKE_CODEC(krb5_typed_data, seqof_typed_data); diff --git a/src/lib/krb5/asn.1/krb5_decode.c b/src/lib/krb5/asn.1/krb5_decode.c index 388efd7b6..69d77c165 100644 --- a/src/lib/krb5/asn.1/krb5_decode.c +++ b/src/lib/krb5/asn.1/krb5_decode.c @@ -32,6 +32,7 @@ #include "krb5_decode_macros.h" #ifndef LEAN_CLIENT +#if 0 krb5_error_code decode_krb5_authenticator(const krb5_data *code, krb5_authenticator **repptr) { @@ -65,6 +66,7 @@ error_out: return retval; } #endif +#endif krb5_error_code KRB5_CALLCONV krb5_decode_ticket(const krb5_data *code, krb5_ticket **repptr) @@ -72,6 +74,7 @@ krb5_decode_ticket(const krb5_data *code, krb5_ticket **repptr) return decode_krb5_ticket(code, repptr); } +#if 0 krb5_error_code decode_krb5_ticket(const krb5_data *code, krb5_ticket **repptr) { @@ -812,6 +815,7 @@ decode_krb5_iakerb_finished(const krb5_data *code, krb5_iakerb_finished **repptr cleanup(free); } +#endif krb5_error_code KRB5_CALLCONV krb5int_get_authdata_containee_types(krb5_context context, @@ -837,6 +841,7 @@ krb5int_get_authdata_containee_types(krb5_context context, assert(0); /* NOTREACHED */ } +#if 0 #ifndef DISABLE_PKINIT krb5_error_code @@ -959,3 +964,4 @@ decode_krb5_typed_data(const krb5_data *code, krb5_pa_data ***repptr) cleanup(free); } +#endif diff --git a/src/lib/krb5/asn.1/krb5_decode_kdc.c b/src/lib/krb5/asn.1/krb5_decode_kdc.c index 4f1c91d87..56ce34e12 100644 --- a/src/lib/krb5/asn.1/krb5_decode_kdc.c +++ b/src/lib/krb5/asn.1/krb5_decode_kdc.c @@ -28,6 +28,7 @@ #include "krbasn1.h" #include "krb5_decode_macros.h" +#if 0 krb5_error_code decode_krb5_as_req(const krb5_data *code, krb5_kdc_req **repptr) { @@ -165,3 +166,4 @@ decode_krb5_pa_pk_as_req_draft9(const krb5_data *code, cleanup(free); } #endif /* DISABLE_PKINIT */ +#endif diff --git a/src/lib/krb5/asn.1/ldap_key_seq.c b/src/lib/krb5/asn.1/ldap_key_seq.c index f407d9809..cd6a6ac5f 100644 --- a/src/lib/krb5/asn.1/ldap_key_seq.c +++ b/src/lib/krb5/asn.1/ldap_key_seq.c @@ -55,7 +55,8 @@ IMPORT_TYPE(int32, krb5_int32); DEFINTTYPE(int16, krb5_int16); DEFCOUNTEDSTRINGTYPE(ui2_octetstring, unsigned char *, krb5_ui_2, - k5_asn1_encode_bytestring, ASN1_OCTETSTRING); + k5_asn1_encode_bytestring, k5_asn1_decode_bytestring, + ASN1_OCTETSTRING); static int is_salt_present(const void *p) @@ -65,7 +66,7 @@ is_salt_present(const void *p) } DEFCOUNTEDTYPE(krbsalt_salt, krb5_key_data, key_data_contents[1], key_data_length[1], ui2_octetstring); -DEFOPTIONALTYPE(krbsalt_salt_if_present, is_salt_present, krbsalt_salt); +DEFOPTIONALTYPE(krbsalt_salt_if_present, is_salt_present, NULL, krbsalt_salt); DEFFIELD(krbsalt_0, krb5_key_data, key_data_type[1], 0, int16); DEFCTAGGEDTYPE(krbsalt_1, 1, krbsalt_salt_if_present); static const struct atype_info *krbsalt_fields[] = { @@ -93,13 +94,10 @@ DEFSEQTYPE(key_data, krb5_key_data, key_data_fields); DEFPTRTYPE(ptr_key_data, key_data); DEFCOUNTEDSEQOFTYPE(cseqof_key_data, krb5_int16, ptr_key_data); -DEFOFFSETTYPE(key_data_kvno, krb5_key_data, key_data_kvno, int16); -DEFPTRTYPE(ptr_key_data_kvno, key_data_kvno); - -DEFINT_IMMEDIATE(one, 1); +DEFINT_IMMEDIATE(one, 1, ASN1_BAD_FORMAT); DEFCTAGGEDTYPE(ldap_key_seq_0, 0, one); DEFCTAGGEDTYPE(ldap_key_seq_1, 1, one); -DEFFIELD(ldap_key_seq_2, ldap_seqof_key_data, key_data, 2, ptr_key_data_kvno); +DEFFIELD(ldap_key_seq_2, ldap_seqof_key_data, kvno, 2, int16); DEFFIELD(ldap_key_seq_3, ldap_seqof_key_data, mkvno, 3, int32); DEFCNFIELD(ldap_key_seq_4, ldap_seqof_key_data, key_data, n_key_data, 4, cseqof_key_data); @@ -112,7 +110,9 @@ DEFSEQTYPE(ldap_key_seq, ldap_seqof_key_data, ldap_key_seq_fields); /* Export a function to do the whole encoding. */ MAKE_ENCODER(krb5int_ldap_encode_sequence_of_keys, ldap_key_seq); +MAKE_DECODER(krb5int_ldap_decode_sequence_of_keys, ldap_key_seq); +#if 0 /************************************************************************/ /* Decode the Principal's keys */ /************************************************************************/ @@ -393,3 +393,4 @@ last: return ret; } #endif +#endif diff --git a/src/lib/krb5/krb/rd_safe.c b/src/lib/krb5/krb/rd_safe.c index 13ba064cf..5d2cee232 100644 --- a/src/lib/krb5/krb/rd_safe.c +++ b/src/lib/krb5/krb/rd_safe.c @@ -46,7 +46,7 @@ rd_safe_basic(krb5_context context, krb5_auth_context ac, { krb5_error_code retval; krb5_safe * message; - krb5_data safe_body; + krb5_data *safe_body = NULL; krb5_checksum our_cksum, *his_cksum; krb5_octet zero_octet = 0; krb5_data *scratch; @@ -89,7 +89,7 @@ rd_safe_basic(krb5_context context, krb5_auth_context ac, message->checksum = &our_cksum; - swb.body = &safe_body; + swb.body = safe_body; swb.safe = message; retval = encode_krb5_safe_with_body(&swb, &scratch); message->checksum = his_cksum; @@ -110,7 +110,7 @@ rd_safe_basic(krb5_context context, krb5_auth_context ac, */ retval = krb5_k_verify_checksum(context, key, KRB5_KEYUSAGE_KRB_SAFE_CKSUM, - &safe_body, his_cksum, &valid); + safe_body, his_cksum, &valid); if (!valid) { retval = KRB5KRB_AP_ERR_MODIFIED; goto cleanup; @@ -127,6 +127,7 @@ rd_safe_basic(krb5_context context, krb5_auth_context ac, cleanup: krb5_free_safe(context, message); + krb5_free_data(context, safe_body); return retval; } diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c index 5546ba020..a7101a738 100644 --- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c +++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c @@ -365,6 +365,7 @@ asn1_encode_sequence_of_keys(krb5_key_data *key_data, krb5_int16 n_key_data, val.key_data = key_data; val.n_key_data = n_key_data; val.mkvno = mkvno; + val.kvno = key_data[0].key_data_kvno; return accessor.asn1_ldap_encode_sequence_of_keys(&val, code); } @@ -375,6 +376,7 @@ asn1_decode_sequence_of_keys(krb5_data *in, krb5_key_data **out, { krb5_error_code err; ldap_seqof_key_data *p; + int i; /* * This should be pushed back into other library initialization @@ -387,6 +389,14 @@ asn1_decode_sequence_of_keys(krb5_data *in, krb5_key_data **out, err = accessor.asn1_ldap_decode_sequence_of_keys(in, &p); if (err) return err; + + /* Set kvno and key_data_ver in each key_data element. */ + for (i = 0; i < p->n_key_data; i++) { + p->key_data[i].key_data_kvno = p->kvno; + p->key_data[i].key_data_ver = + (p->key_data[i].key_data_length[1] == 0) ? 1 : 2; + } + *out = p->key_data; *n_key_data = p->n_key_data; *mkvno = p->mkvno; diff --git a/src/tests/asn.1/ktest.c b/src/tests/asn.1/ktest.c index 6963c018c..e3e783926 100644 --- a/src/tests/asn.1/ktest.c +++ b/src/tests/asn.1/ktest.c @@ -808,8 +808,6 @@ ktest_make_sample_key_data(krb5_key_data *p, int i) char *str; int len; - p->key_data_ver = 2; - p->key_data_kvno = 42; len = asprintf(&str, "key%d", i); if (len < 0) abort(); @@ -832,6 +830,7 @@ ktest_make_sample_ldap_seqof_key_data(ldap_seqof_key_data *p) p->mkvno = 14; p->n_key_data = 3; p->key_data = calloc(3,sizeof(krb5_key_data)); + p->kvno = 42; for (i = 0; i < 3; i++) ktest_make_sample_key_data(&p->key_data[i], i); } diff --git a/src/tests/asn.1/ktest_equal.c b/src/tests/asn.1/ktest_equal.c index 0418e5daf..6953708ca 100644 --- a/src/tests/asn.1/ktest_equal.c +++ b/src/tests/asn.1/ktest_equal.c @@ -620,8 +620,6 @@ equal_key_data(krb5_key_data *ref, krb5_key_data *var) int p = TRUE; if (ref == var) return TRUE; else if (ref == NULL || var == NULL) return FALSE; - p = p && scalar_equal(key_data_ver); - p = p && scalar_equal(key_data_kvno); p = p && scalar_equal(key_data_type[0]); p = p && scalar_equal(key_data_type[1]); p = p && len_equal(key_data_length[0],key_data_contents[0], @@ -649,6 +647,7 @@ ktest_equal_ldap_sequence_of_keys(ldap_seqof_key_data *ref, if (ref == var) return TRUE; else if (ref == NULL || var == NULL) return FALSE; p = p && scalar_equal(mkvno); + p = p && scalar_equal(kvno); p = p && len_equal(n_key_data,key_data,equal_key_data_array); return p; }