svn rev #25693: trunk/src/ include/ lib/krb5/asn.1/ lib/krb5/krb/ plugins/kdb/ldap/libkdb_ldap/ ...

ghudson@MIT.EDU ghudson at MIT.EDU
Sat Feb 11 18:25:25 EST 2012


http://src.mit.edu/fisheye/changelog/krb5/?cs=25693
Commit By: ghudson
Log Message:
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.


Changed Files:
U   trunk/src/include/k5-int.h
U   trunk/src/lib/krb5/asn.1/asn1_encode.c
U   trunk/src/lib/krb5/asn.1/asn1_encode.h
U   trunk/src/lib/krb5/asn.1/asn1_get.h
U   trunk/src/lib/krb5/asn.1/asn1_k_encode.c
U   trunk/src/lib/krb5/asn.1/krb5_decode.c
U   trunk/src/lib/krb5/asn.1/krb5_decode_kdc.c
U   trunk/src/lib/krb5/asn.1/ldap_key_seq.c
U   trunk/src/lib/krb5/krb/rd_safe.c
U   trunk/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c
U   trunk/src/tests/asn.1/ktest.c
U   trunk/src/tests/asn.1/ktest_equal.c
Modified: trunk/src/include/k5-int.h
===================================================================
--- trunk/src/include/k5-int.h	2012-02-11 23:25:21 UTC (rev 25692)
+++ trunk/src/include/k5-int.h	2012-02-11 23:25:25 UTC (rev 25693)
@@ -1704,7 +1704,7 @@
 
 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 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 @@
                                      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);
 
 /*************************************************************************

Modified: trunk/src/lib/krb5/asn.1/asn1_encode.c
===================================================================
--- trunk/src/lib/krb5/asn.1/asn1_encode.c	2012-02-11 23:25:21 UTC (rev 25692)
+++ trunk/src/lib/krb5/asn.1/asn1_encode.c	2012-02-11 23:25:25 UTC (rev 25693)
@@ -176,6 +176,141 @@
     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 @@
     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 @@
     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,7 +662,30 @@
                                      *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);
 static asn1_error_code
@@ -448,6 +777,15 @@
         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 @@
         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 @@
     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 @@
     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 @@
     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);
+}

Modified: trunk/src/lib/krb5/asn.1/asn1_encode.h
===================================================================
--- trunk/src/lib/krb5/asn.1/asn1_encode.h	2012-02-11 23:25:21 UTC (rev 25692)
+++ trunk/src/lib/krb5/asn.1/asn1_encode.h	2012-02-11 23:25:25 UTC (rev 25693)
@@ -50,9 +50,25 @@
 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 @@
     /* 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 <thing>.  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 @@
     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 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 optional_info {
     int (*is_present)(const void *);
+    void (*init)(void *);
     const struct atype_info *basetype;
 };
 
@@ -151,29 +179,37 @@
     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 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 @@
  * 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 @@
     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 @@
     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 @@
 #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 @@
 #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 @@
     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 @@
     }
 /* 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 @@
  * + 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 @@
 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 @@
     }                                                                   \
     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 <stddef.h>
 /*
  * Ugly hack!

Modified: trunk/src/lib/krb5/asn.1/asn1_get.h
===================================================================
--- trunk/src/lib/krb5/asn.1/asn1_get.h	2012-02-11 23:25:21 UTC (rev 25692)
+++ trunk/src/lib/krb5/asn.1/asn1_get.h	2012-02-11 23:25:25 UTC (rev 25693)
@@ -49,6 +49,11 @@
     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);

Modified: trunk/src/lib/krb5/asn.1/asn1_k_encode.c
===================================================================
--- trunk/src/lib/krb5/asn.1/asn1_k_encode.c	2012-02-11 23:25:21 UTC (rev 25692)
+++ trunk/src/lib/krb5/asn.1/asn1_k_encode.c	2012-02-11 23:25:25 UTC (rev 25693)
@@ -27,8 +27,9 @@
 #include "asn1_encode.h"
 #include <assert.h>
 
-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(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 @@
 
 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,7 +88,46 @@
 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)
 {
     /* Range checking for time_t vs krb5_timestamp?  */
@@ -94,7 +137,26 @@
     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 @@
     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 @@
     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 @@
 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
@@ -215,6 +338,18 @@
 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)
 {
     const krb5_enc_kdc_rep_part *val = p;
@@ -227,13 +362,15 @@
 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 @@
  */
 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 @@
     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 @@
 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 @@
 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 @@
 /* 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_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 @@
 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 @@
 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,
+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_as_rep, krb5_kdc_rep, as_rep_fields);
-DEFAPPTAGGEDTYPE(as_rep, 11, untagged_as_rep);
+DEFSEQTYPE(kdc_rep, krb5_kdc_rep, kdc_rep_fields);
+DEFAPPTAGGEDTYPE(as_rep, 11, kdc_rep);
+DEFAPPTAGGEDTYPE(tgs_rep, 13, kdc_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,
-    &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);
-
-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 @@
 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_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 @@
 
 /* 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 @@
 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 @@
 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 @@
 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_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 @@
 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 @@
     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 @@
 };
 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 @@
 /* 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);
+/*
+ * 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;
 
-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);
+    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 @@
 #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 @@
            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 @@
 };
 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 @@
 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 @@
 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);

Modified: trunk/src/lib/krb5/asn.1/krb5_decode.c
===================================================================
--- trunk/src/lib/krb5/asn.1/krb5_decode.c	2012-02-11 23:25:21 UTC (rev 25692)
+++ trunk/src/lib/krb5/asn.1/krb5_decode.c	2012-02-11 23:25:25 UTC (rev 25693)
@@ -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 @@
     return retval;
 }
 #endif
+#endif
 
 krb5_error_code KRB5_CALLCONV
 krb5_decode_ticket(const krb5_data *code, krb5_ticket **repptr)
@@ -72,6 +74,7 @@
     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 @@
 
     cleanup(free);
 }
+#endif
 
 krb5_error_code KRB5_CALLCONV
 krb5int_get_authdata_containee_types(krb5_context context,
@@ -837,6 +841,7 @@
     assert(0); /* NOTREACHED */
 }
 
+#if 0
 #ifndef DISABLE_PKINIT
 
 krb5_error_code
@@ -959,3 +964,4 @@
 
     cleanup(free);
 }
+#endif

Modified: trunk/src/lib/krb5/asn.1/krb5_decode_kdc.c
===================================================================
--- trunk/src/lib/krb5/asn.1/krb5_decode_kdc.c	2012-02-11 23:25:21 UTC (rev 25692)
+++ trunk/src/lib/krb5/asn.1/krb5_decode_kdc.c	2012-02-11 23:25:25 UTC (rev 25693)
@@ -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 @@
     cleanup(free);
 }
 #endif /* DISABLE_PKINIT */
+#endif

Modified: trunk/src/lib/krb5/asn.1/ldap_key_seq.c
===================================================================
--- trunk/src/lib/krb5/asn.1/ldap_key_seq.c	2012-02-11 23:25:21 UTC (rev 25692)
+++ trunk/src/lib/krb5/asn.1/ldap_key_seq.c	2012-02-11 23:25:25 UTC (rev 25693)
@@ -55,7 +55,8 @@
 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 @@
 }
 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 @@
 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 @@
 
 /* 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 @@
     return ret;
 }
 #endif
+#endif

Modified: trunk/src/lib/krb5/krb/rd_safe.c
===================================================================
--- trunk/src/lib/krb5/krb/rd_safe.c	2012-02-11 23:25:21 UTC (rev 25692)
+++ trunk/src/lib/krb5/krb/rd_safe.c	2012-02-11 23:25:25 UTC (rev 25693)
@@ -46,7 +46,7 @@
 {
     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 @@
 
     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 @@
          */
         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 @@
 
 cleanup:
     krb5_free_safe(context, message);
+    krb5_free_data(context, safe_body);
     return retval;
 }
 

Modified: trunk/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c
===================================================================
--- trunk/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c	2012-02-11 23:25:21 UTC (rev 25692)
+++ trunk/src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c	2012-02-11 23:25:25 UTC (rev 25693)
@@ -365,6 +365,7 @@
     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 @@
 {
     krb5_error_code err;
     ldap_seqof_key_data *p;
+    int i;
 
     /*
      * This should be pushed back into other library initialization
@@ -387,6 +389,14 @@
     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;

Modified: trunk/src/tests/asn.1/ktest.c
===================================================================
--- trunk/src/tests/asn.1/ktest.c	2012-02-11 23:25:21 UTC (rev 25692)
+++ trunk/src/tests/asn.1/ktest.c	2012-02-11 23:25:25 UTC (rev 25693)
@@ -808,8 +808,6 @@
     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 @@
     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);
 }

Modified: trunk/src/tests/asn.1/ktest_equal.c
===================================================================
--- trunk/src/tests/asn.1/ktest_equal.c	2012-02-11 23:25:21 UTC (rev 25692)
+++ trunk/src/tests/asn.1/ktest_equal.c	2012-02-11 23:25:25 UTC (rev 25693)
@@ -620,8 +620,6 @@
     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 @@
     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;
 }



More information about the cvs-krb5 mailing list