diff options
Diffstat (limited to 'src/lib/krb5/asn.1/asn1_encode.c')
| -rw-r--r-- | src/lib/krb5/asn.1/asn1_encode.c | 1636 | 
1 files changed, 1636 insertions, 0 deletions
| diff --git a/src/lib/krb5/asn.1/asn1_encode.c b/src/lib/krb5/asn.1/asn1_encode.c new file mode 100644 index 000000000000..a7423b642a48 --- /dev/null +++ b/src/lib/krb5/asn.1/asn1_encode.c @@ -0,0 +1,1636 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* lib/krb5/asn.1/asn1_encode.c */ +/* + * Copyright 1994, 2008 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + *   require a specific license from the United States Government. + *   It is the responsibility of any person or organization contemplating + *   export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission.  Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose.  It is provided "as is" without express + * or implied warranty. + */ + +#include "asn1_encode.h" + +/**** Functions for encoding primitive types ****/ + +asn1_error_code +k5_asn1_encode_bool(asn1buf *buf, intmax_t val, size_t *len_out) +{ +    asn1_octet bval = val ? 0xFF : 0x00; + +    *len_out = 1; +    return asn1buf_insert_octet(buf, bval); +} + +asn1_error_code +k5_asn1_encode_int(asn1buf *buf, intmax_t val, size_t *len_out) +{ +    asn1_error_code ret; +    size_t len = 0; +    long valcopy; +    int digit; + +    valcopy = val; +    do { +        digit = valcopy & 0xFF; +        ret = asn1buf_insert_octet(buf, digit); +        if (ret) +            return ret; +        len++; +        valcopy = valcopy >> 8; +    } while (valcopy != 0 && valcopy != ~0); + +    if (val > 0 && (digit & 0x80) == 0x80) { /* make sure the high bit is */ +        ret = asn1buf_insert_octet(buf, 0);  /* of the proper signed-ness */ +        if (ret) +            return ret; +        len++; +    } else if (val < 0 && (digit & 0x80) != 0x80) { +        ret = asn1buf_insert_octet(buf, 0xFF); +        if (ret) +            return ret; +        len++; +    } + + +    *len_out = len; +    return 0; +} + +asn1_error_code +k5_asn1_encode_uint(asn1buf *buf, uintmax_t val, size_t *len_out) +{ +    asn1_error_code ret; +    size_t len = 0; +    uintmax_t valcopy; +    int digit; + +    valcopy = val; +    do { +        digit = valcopy & 0xFF; +        ret = asn1buf_insert_octet(buf, digit); +        if (ret) +            return ret; +        len++; +        valcopy = valcopy >> 8; +    } while (valcopy != 0); + +    if (digit & 0x80) {                     /* make sure the high bit is */ +        ret = asn1buf_insert_octet(buf, 0); /* of the proper signed-ness */ +        if (ret) +            return ret; +        len++; +    } + +    *len_out = len; +    return 0; +} + +asn1_error_code +k5_asn1_encode_bytestring(asn1buf *buf, unsigned char *const *val, size_t len, +                          size_t *len_out) +{ +    if (len > 0 && val == NULL) +        return ASN1_MISSING_FIELD; +    *len_out = len; +    return asn1buf_insert_octetstring(buf, len, *val); +} + +asn1_error_code +k5_asn1_encode_generaltime(asn1buf *buf, time_t val, size_t *len_out) +{ +    struct tm *gtime, gtimebuf; +    char s[16]; +    unsigned char *sp; +    time_t gmt_time = val; +    int len; + +    /* +     * Time encoding: YYYYMMDDhhmmssZ +     */ +    if (gmt_time == 0) { +        sp = (unsigned char *)"19700101000000Z"; +    } else { +        /* +         * Sanity check this just to be paranoid, as gmtime can return NULL, +         * and some bogus implementations might overrun on the sprintf. +         */ +#ifdef HAVE_GMTIME_R +#ifdef GMTIME_R_RETURNS_INT +        if (gmtime_r(&gmt_time, >imebuf) != 0) +            return ASN1_BAD_GMTIME; +#else +        if (gmtime_r(&gmt_time, >imebuf) == NULL) +            return ASN1_BAD_GMTIME; +#endif +#else /* HAVE_GMTIME_R */ +        gtime = gmtime(&gmt_time); +        if (gtime == NULL) +            return ASN1_BAD_GMTIME; +        memcpy(>imebuf, gtime, sizeof(gtimebuf)); +#endif /* HAVE_GMTIME_R */ +        gtime = >imebuf; + +        if (gtime->tm_year > 8099 || gtime->tm_mon > 11 || +            gtime->tm_mday > 31 || gtime->tm_hour > 23 || +            gtime->tm_min > 59 || gtime->tm_sec > 59) +            return ASN1_BAD_GMTIME; +        len = snprintf(s, sizeof(s), "%04d%02d%02d%02d%02d%02dZ", +                       1900 + gtime->tm_year, gtime->tm_mon + 1, +                       gtime->tm_mday, gtime->tm_hour, +                       gtime->tm_min, gtime->tm_sec); +        if (SNPRINTF_OVERFLOW(len, sizeof(s))) +            /* Shouldn't be possible given above tests.  */ +            return ASN1_BAD_GMTIME; +        sp = (unsigned char *)s; +    } + +    return k5_asn1_encode_bytestring(buf, &sp, 15, len_out); +} + +asn1_error_code +k5_asn1_encode_bitstring(asn1buf *buf, unsigned char *const *val, size_t len, +                         size_t *len_out) +{ +    asn1_error_code ret; + +    ret = asn1buf_insert_octetstring(buf, len, *val); +    if (ret) +        return ret; +    *len_out = len + 1; +    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, intmax_t *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, intmax_t *val) +{ +    intmax_t 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(intmax_t) + (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, uintmax_t *val) +{ +    uintmax_t n; +    size_t i; + +    if (len == 0) +        return ASN1_BAD_LENGTH; +    /* Check for negative values and check length. */ +    if ((asn1[0] & 0x80) || len > sizeof(uintmax_t) + (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; +    if (len == 0) +        return 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 parameters in t and the content + * length len.  Place the length of the encoded tag in *retlen. */ +static asn1_error_code +make_tag(asn1buf *buf, const taginfo *t, size_t len, size_t *retlen) +{ +    asn1_error_code ret; +    asn1_tagnum tag_copy; +    size_t sum = 0, length, len_copy; + +    if (t->tagnum > ASN1_TAGNUM_MAX) +        return ASN1_OVERFLOW; + +    /* Encode the length of the content within the tag. */ +    if (len < 128) { +        ret = asn1buf_insert_octet(buf, len & 0x7F); +        if (ret) +            return ret; +        length = 1; +    } else { +        length = 0; +        for (len_copy = len; len_copy != 0; len_copy >>= 8) { +            ret = asn1buf_insert_octet(buf, len_copy & 0xFF); +            if (ret) +                return ret; +            length++; +        } +        ret = asn1buf_insert_octet(buf, 0x80 | (length & 0x7F)); +        if (ret) +            return ret; +        length++; +    } +    sum += length; + +    /* Encode the tag and construction bit. */ +    if (t->tagnum < 31) { +        ret = asn1buf_insert_octet(buf, +                                   t->asn1class | t->construction | t->tagnum); +        if (ret) +            return ret; +        length = 1; +    } else { +        tag_copy = t->tagnum; +        length = 0; +        ret = asn1buf_insert_octet(buf, tag_copy & 0x7F); +        if (ret) +            return ret; +        tag_copy >>= 7; +        length++; + +        for (; tag_copy != 0; tag_copy >>= 7) { +            ret = asn1buf_insert_octet(buf, 0x80 | (tag_copy & 0x7F)); +            if (ret) +                return ret; +            length++; +        } + +        ret = asn1buf_insert_octet(buf, t->asn1class | t->construction | 0x1F); +        if (ret) +            return ret; +        length++; +    } +    sum += length; + +    *retlen = sum; +    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 +get_nullterm_sequence_len(const void *valp, const struct atype_info *seq) +{ +    size_t i; +    const struct atype_info *a; +    const struct ptr_info *ptr; +    const void *elt, *eltptr; + +    a = seq; +    i = 0; +    assert(a->type == atype_ptr); +    assert(seq->size != 0); +    ptr = a->tinfo; + +    while (1) { +        eltptr = (const char *)valp + i * seq->size; +        elt = LOADPTR(eltptr, ptr); +        if (elt == NULL) +            break; +        i++; +    } +    return i; +} +static asn1_error_code +encode_sequence_of(asn1buf *buf, size_t seqlen, const void *val, +                   const struct atype_info *eltinfo, size_t *len_out); + +static asn1_error_code +encode_nullterm_sequence_of(asn1buf *buf, const void *val, +                            const struct atype_info *type, +                            int can_be_empty, size_t *len_out) +{ +    size_t len = get_nullterm_sequence_len(val, type); + +    if (!can_be_empty && len == 0) +        return ASN1_MISSING_FIELD; +    return encode_sequence_of(buf, len, val, type, len_out); +} + +static intmax_t +load_int(const void *val, size_t size) +{ +    switch (size) { +    case 1: return *(signed char *)val; +    case 2: return *(krb5_int16 *)val; +    case 4: return *(krb5_int32 *)val; +    case 8: return *(int64_t *)val; +    default: abort(); +    } +} + +static uintmax_t +load_uint(const void *val, size_t size) +{ +    switch (size) { +    case 1: return *(unsigned char *)val; +    case 2: return *(krb5_ui_2 *)val; +    case 4: return *(krb5_ui_4 *)val; +    case 8: return *(uint64_t *)val; +    default: abort(); +    } +} + +static asn1_error_code +load_count(const void *val, const struct counted_info *counted, +           size_t *count_out) +{ +    const void *countptr = (const char *)val + counted->lenoff; + +    assert(sizeof(size_t) <= sizeof(uintmax_t)); +    if (counted->lensigned) { +        intmax_t xlen = load_int(countptr, counted->lensize); +        if (xlen < 0 || (uintmax_t)xlen > SIZE_MAX) +            return EINVAL; +        *count_out = xlen; +    } else { +        uintmax_t xlen = load_uint(countptr, counted->lensize); +        if ((size_t)xlen != xlen || xlen > SIZE_MAX) +            return EINVAL; +        *count_out = xlen; +    } +    return 0; +} + +static asn1_error_code +store_int(intmax_t 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_t)intval != intval) +            return ASN1_OVERFLOW; +        *(int64_t *)val = intval; +        return 0; +    default: +        abort(); +    } +} + +static asn1_error_code +store_uint(uintmax_t 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_t)intval != intval) +            return ASN1_OVERFLOW; +        *(uint64_t *)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 ((intmax_t)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 +split_der(asn1buf *buf, unsigned char *const *der, size_t len, +          taginfo *tag_out, size_t *len_out) +{ +    asn1_error_code ret; +    const unsigned char *contents, *remainder; +    size_t clen, rlen; + +    ret = get_tag(*der, len, tag_out, &contents, &clen, &remainder, &rlen); +    if (ret) +        return ret; +    if (rlen != 0) +        return ASN1_BAD_LENGTH; +    *len_out = clen; +    return asn1buf_insert_bytestring(buf, clen, contents); +} + +/* + * 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); +static asn1_error_code +encode_cntype(asn1buf *buf, const void *val, size_t len, +              const struct cntype_info *c, taginfo *tag_out, size_t *len_out); + +/* Encode a value (contents only, no outer tag) according to a type, and return + * its encoded tag information. */ +static asn1_error_code +encode_atype(asn1buf *buf, const void *val, const struct atype_info *a, +             taginfo *tag_out, size_t *len_out) +{ +    asn1_error_code ret; + +    if (val == NULL) +        return ASN1_MISSING_FIELD; + +    switch (a->type) { +    case atype_fn: { +        const struct fn_info *fn = a->tinfo; +        assert(fn->enc != NULL); +        return fn->enc(buf, val, tag_out, len_out); +    } +    case atype_sequence: +        assert(a->tinfo != NULL); +        ret = encode_sequence(buf, val, a->tinfo, len_out); +        if (ret) +            return ret; +        tag_out->asn1class = UNIVERSAL; +        tag_out->construction = CONSTRUCTED; +        tag_out->tagnum = ASN1_SEQUENCE; +        break; +    case atype_ptr: { +        const struct ptr_info *ptr = a->tinfo; +        assert(ptr->basetype != NULL); +        return encode_atype(buf, LOADPTR(val, ptr), ptr->basetype, tag_out, +                            len_out); +    } +    case atype_offset: { +        const struct offset_info *off = a->tinfo; +        assert(off->basetype != NULL); +        return encode_atype(buf, (const char *)val + off->dataoff, +                            off->basetype, tag_out, len_out); +    } +    case atype_optional: { +        const struct optional_info *opt = a->tinfo; +        assert(opt->is_present != NULL); +        if (opt->is_present(val)) +            return encode_atype(buf, val, opt->basetype, tag_out, len_out); +        else +            return ASN1_OMITTED; +    } +    case atype_counted: { +        const struct counted_info *counted = a->tinfo; +        const void *dataptr = (const char *)val + counted->dataoff; +        size_t count; +        assert(counted->basetype != NULL); +        ret = load_count(val, counted, &count); +        if (ret) +            return ret; +        return encode_cntype(buf, dataptr, count, counted->basetype, tag_out, +                             len_out); +    } +    case atype_nullterm_sequence_of: +    case atype_nonempty_nullterm_sequence_of: +        assert(a->tinfo != NULL); +        ret = encode_nullterm_sequence_of(buf, val, a->tinfo, +                                          a->type == +                                          atype_nullterm_sequence_of, +                                          len_out); +        if (ret) +            return ret; +        tag_out->asn1class = UNIVERSAL; +        tag_out->construction = CONSTRUCTED; +        tag_out->tagnum = ASN1_SEQUENCE; +        break; +    case atype_tagged_thing: { +        const struct tagged_info *tag = a->tinfo; +        ret = encode_atype(buf, val, tag->basetype, tag_out, len_out); +        if (ret) +            return ret; +        if (!tag->implicit) { +            size_t tlen; +            ret = make_tag(buf, tag_out, *len_out, &tlen); +            if (ret) +                return ret; +            *len_out += tlen; +            tag_out->construction = tag->construction; +        } +        tag_out->asn1class = tag->tagtype; +        tag_out->tagnum = tag->tagval; +        break; +    } +    case atype_bool: +        ret = k5_asn1_encode_bool(buf, load_int(val, a->size), len_out); +        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), len_out); +        if (ret) +            return ret; +        tag_out->asn1class = UNIVERSAL; +        tag_out->construction = PRIMITIVE; +        tag_out->tagnum = ASN1_INTEGER; +        break; +    case atype_uint: +        ret = k5_asn1_encode_uint(buf, load_uint(val, a->size), len_out); +        if (ret) +            return ret; +        tag_out->asn1class = UNIVERSAL; +        tag_out->construction = PRIMITIVE; +        tag_out->tagnum = ASN1_INTEGER; +        break; +    case atype_int_immediate: { +        const struct immediate_info *imm = a->tinfo; +        ret = k5_asn1_encode_int(buf, imm->val, len_out); +        if (ret) +            return ret; +        tag_out->asn1class = UNIVERSAL; +        tag_out->construction = PRIMITIVE; +        tag_out->tagnum = ASN1_INTEGER; +        break; +    } +    default: +        assert(a->type > atype_min); +        assert(a->type < atype_max); +        abort(); +    } + +    return 0; +} + +static asn1_error_code +encode_atype_and_tag(asn1buf *buf, const void *val, const struct atype_info *a, +                     size_t *len_out) +{ +    taginfo t; +    asn1_error_code ret; +    size_t clen, tlen; + +    ret = encode_atype(buf, val, a, &t, &clen); +    if (ret) +        return ret; +    ret = make_tag(buf, &t, clen, &tlen); +    if (ret) +        return ret; +    *len_out = clen + tlen; +    return 0; +} + +/* + * Encode an object and count according to a cntype_info structure.  val is a + * pointer to the object being encoded, which in most cases is itself a + * pointer (but is a union in the cntype_choice case). + */ +static asn1_error_code +encode_cntype(asn1buf *buf, const void *val, size_t count, +              const struct cntype_info *c, taginfo *tag_out, size_t *len_out) +{ +    asn1_error_code ret; + +    switch (c->type) { +    case cntype_string: { +        const struct string_info *string = c->tinfo; +        assert(string->enc != NULL); +        ret = string->enc(buf, val, count, len_out); +        if (ret) +            return ret; +        tag_out->asn1class = UNIVERSAL; +        tag_out->construction = PRIMITIVE; +        tag_out->tagnum = string->tagval; +        break; +    } +    case cntype_der: +        return split_der(buf, val, count, tag_out, len_out); +    case cntype_seqof: { +        const struct atype_info *a = c->tinfo; +        const struct ptr_info *ptr = a->tinfo; +        assert(a->type == atype_ptr); +        val = LOADPTR(val, ptr); +        ret = encode_sequence_of(buf, count, val, ptr->basetype, len_out); +        if (ret) +            return ret; +        tag_out->asn1class = UNIVERSAL; +        tag_out->construction = CONSTRUCTED; +        tag_out->tagnum = ASN1_SEQUENCE; +        break; +    } +    case cntype_choice: { +        const struct choice_info *choice = c->tinfo; +        if (count >= choice->n_options) +            return ASN1_MISSING_FIELD; +        return encode_atype(buf, val, choice->options[count], tag_out, +                            len_out); +    } + +    default: +        assert(c->type > cntype_min); +        assert(c->type < cntype_max); +        abort(); +    } + +    return 0; +} + +static asn1_error_code +encode_sequence(asn1buf *buf, const void *val, const struct seq_info *seq, +                size_t *len_out) +{ +    asn1_error_code ret; +    size_t i, len, sum = 0; + +    for (i = seq->n_fields; i > 0; i--) { +        ret = encode_atype_and_tag(buf, val, seq->fields[i - 1], &len); +        if (ret == ASN1_OMITTED) +            continue; +        else if (ret != 0) +            return ret; +        sum += len; +    } +    *len_out = sum; +    return 0; +} + +static asn1_error_code +encode_sequence_of(asn1buf *buf, size_t seqlen, const void *val, +                   const struct atype_info *eltinfo, size_t *len_out) +{ +    asn1_error_code ret; +    size_t sum = 0, i, len; +    const void *eltptr; + +    assert(eltinfo->size != 0); +    for (i = seqlen; i > 0; i--) { +        eltptr = (const char *)val + (i - 1) * eltinfo->size; +        ret = encode_atype_and_tag(buf, eltptr, eltinfo, &len); +        if (ret) +            return ret; +        sum += len; +    } +    *len_out = sum; +    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_func != NULL) +            fn->free_func(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: { +        intmax_t intval; +        ret = k5_asn1_decode_bool(asn1, len, &intval); +        if (ret) +            return ret; +        return store_int(intval, a->size, val); +    } +    case atype_int: { +        intmax_t intval; +        ret = k5_asn1_decode_int(asn1, len, &intval); +        if (ret) +            return ret; +        return store_int(intval, a->size, val); +    } +    case atype_uint: { +        uintmax_t 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; +        intmax_t 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, *elem, *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; +        elem = (char *)seq + count * elemtype->size; +        memset(elem, 0, elemtype->size); +        ret = decode_atype(&t, contents, clen, elemtype, elem); +        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, size_t *len_out) +{ +    return encode_atype(buf, val, a, tag_out, len_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) +{ +    size_t len; +    asn1_error_code ret; +    asn1buf *buf = NULL; +    krb5_data *d; + +    *code_out = NULL; + +    if (rep == NULL) +        return ASN1_MISSING_FIELD; +    ret = asn1buf_create(&buf); +    if (ret) +        return ret; +    ret = encode_atype_and_tag(buf, rep, a, &len); +    if (ret) +        goto cleanup; +    ret = asn12krb5_buf(buf, &d); +    if (ret) +        goto cleanup; +    *code_out = d; +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); +} | 
