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
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
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
*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);
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);
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;
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)
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)
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);
+}
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
/* 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 *. */
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
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;
};
struct optional_info {
int (*is_present)(const void *);
+ void (*init)(void *);
const struct atype_info *basetype;
};
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
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;
};
* 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. */
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; \
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. */
#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), \
#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), \
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), \
}
/* 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
* + 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 \
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 \
} \
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!
#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);
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)
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);
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)
{
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);
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)
{
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);
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
DEFPTRTYPE(ptr_seqof_ticket, seqof_ticket);
DEFOPTIONALEMPTYTYPE(opt_ptr_seqof_ticket, ptr_seqof_ticket);
+static int
+is_enc_kdc_rep_start_set(const void *p)
+{
+ const krb5_enc_kdc_rep_part *val = p;
+ return (val->times.starttime != 0);
+}
+static void
+init_enc_kdc_rep_start(void *p)
+{
+ krb5_enc_kdc_rep_part *val = p;
+ val->times.starttime = val->times.authtime;
+}
static int
is_renewable_flag_set(const void *p)
{
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,
*/
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);
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);
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[] = {
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
};
/* 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
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[] = {
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,
DEFSEQTYPE(untagged_enc_tkt_part, krb5_enc_tkt_part, enc_tkt_part_fields);
DEFAPPTAGGEDTYPE(enc_tkt_part, 3, untagged_enc_tkt_part);
+DEFAPPTAGGEDTYPE(enc_as_rep_part, 24, enc_kdc_rep_part);
DEFAPPTAGGEDTYPE(enc_tgs_rep_part, 26, enc_kdc_rep_part);
-DEFINT_IMMEDIATE(as_rep_msg_type, KRB5_AS_REP);
DEFCTAGGEDTYPE(kdc_rep_0, 0, krb5_version);
-DEFCTAGGEDTYPE(as_rep_1, 1, as_rep_msg_type);
+DEFFIELD(kdc_rep_1, krb5_kdc_rep, msg_type, 1, uint);
DEFFIELD(kdc_rep_2, krb5_kdc_rep, padata, 2, opt_ptr_seqof_pa_data);
DEFFIELD(kdc_rep_3, krb5_kdc_rep, client, 3, realm_of_principal);
DEFFIELD(kdc_rep_4, krb5_kdc_rep, client, 4, principal);
DEFFIELD(kdc_rep_5, krb5_kdc_rep, ticket, 5, ticket_ptr);
DEFFIELD(kdc_rep_6, krb5_kdc_rep, enc_part, 6, encrypted_data);
-static const struct atype_info *as_rep_fields[] = {
- &k5_atype_kdc_rep_0, &k5_atype_as_rep_1, &k5_atype_kdc_rep_2,
- &k5_atype_kdc_rep_3, &k5_atype_kdc_rep_4, &k5_atype_kdc_rep_5,
- &k5_atype_kdc_rep_6
-};
-DEFSEQTYPE(untagged_as_rep, krb5_kdc_rep, as_rep_fields);
-DEFAPPTAGGEDTYPE(as_rep, 11, untagged_as_rep);
-
-/* TGS-REP ::= [APPLICATION 13] KDC-REP */
-/* But KDC-REP needs to know what type it's being encapsulated in, so use a
- * separate atype. Most fields are the same. */
-DEFINT_IMMEDIATE(tgs_rep_msg_type, KRB5_TGS_REP);
-DEFCTAGGEDTYPE(tgs_rep_1, 1, tgs_rep_msg_type);
-static const struct atype_info *tgs_rep_fields[] = {
- &k5_atype_kdc_rep_0, &k5_atype_tgs_rep_1, &k5_atype_kdc_rep_2,
+static const struct atype_info *kdc_rep_fields[] = {
+ &k5_atype_kdc_rep_0, &k5_atype_kdc_rep_1, &k5_atype_kdc_rep_2,
&k5_atype_kdc_rep_3, &k5_atype_kdc_rep_4, &k5_atype_kdc_rep_5,
&k5_atype_kdc_rep_6
};
-DEFSEQTYPE(untagged_tgs_rep, krb5_kdc_rep, tgs_rep_fields);
-DEFAPPTAGGEDTYPE(tgs_rep, 13, untagged_tgs_rep);
+DEFSEQTYPE(kdc_rep, krb5_kdc_rep, kdc_rep_fields);
+DEFAPPTAGGEDTYPE(as_rep, 11, kdc_rep);
+DEFAPPTAGGEDTYPE(tgs_rep, 13, kdc_rep);
-DEFINT_IMMEDIATE(ap_req_msg_type, ASN1_KRB_AP_REQ);
+DEFINT_IMMEDIATE(ap_req_msg_type, ASN1_KRB_AP_REQ, 0);
DEFCTAGGEDTYPE(ap_req_0, 0, krb5_version);
DEFCTAGGEDTYPE(ap_req_1, 1, ap_req_msg_type);
DEFFIELD(ap_req_2, krb5_ap_req, ap_options, 2, krb5_flags);
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);
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
/* 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);
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);
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[] = {
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);
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[] = {
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);
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);
};
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);
/* Exported complete encoders -- these produce a krb5_data with
the encoding in the correct byte order. */
-MAKE_ENCODER(encode_krb5_authenticator, authenticator);
-MAKE_ENCODER(encode_krb5_ticket, ticket);
-MAKE_ENCODER(encode_krb5_encryption_key, encryption_key);
-MAKE_ENCODER(encode_krb5_enc_tkt_part, enc_tkt_part);
-/* XXX We currently (for backwards compatibility) encode both
- EncASRepPart and EncTGSRepPart with application tag 26. */
-MAKE_ENCODER(encode_krb5_enc_kdc_rep_part, enc_tgs_rep_part);
-MAKE_ENCODER(encode_krb5_as_rep, as_rep);
-MAKE_ENCODER(encode_krb5_tgs_rep, tgs_rep);
-MAKE_ENCODER(encode_krb5_ap_req, ap_req);
-MAKE_ENCODER(encode_krb5_ap_rep, ap_rep);
-MAKE_ENCODER(encode_krb5_ap_rep_enc_part, ap_rep_enc_part);
-MAKE_ENCODER(encode_krb5_as_req, as_req);
-MAKE_ENCODER(encode_krb5_tgs_req, tgs_req);
-MAKE_ENCODER(encode_krb5_kdc_req_body, kdc_req_body);
-MAKE_ENCODER(encode_krb5_safe, safe);
+MAKE_CODEC(krb5_authenticator, authenticator);
+MAKE_CODEC(krb5_ticket, ticket);
+MAKE_CODEC(krb5_encryption_key, encryption_key);
+MAKE_CODEC(krb5_enc_tkt_part, enc_tkt_part);
/*
- * encode_krb5_safe_with_body
- *
- * Like encode_krb5_safe(), except takes a saved KRB-SAFE-BODY
- * encoding to avoid problems with re-encoding.
+ * For backwards compatibility, we encode both EncASRepPart and EncTGSRepPart
+ * with application tag 26. On decode, we accept either app tag and set the
+ * msg_type field of the resulting structure. This could be simplified and
+ * pushed up into libkrb5.
*/
+MAKE_ENCODER(encode_krb5_enc_kdc_rep_part, enc_tgs_rep_part);
+krb5_error_code
+decode_krb5_enc_kdc_rep_part(const krb5_data *code,
+ krb5_enc_kdc_rep_part **rep_out)
+{
+ asn1_error_code ret;
+ krb5_enc_kdc_rep_part *rep;
+ void *rep_ptr;
+ krb5_msgtype msg_type = KRB5_TGS_REP;
+
+ *rep_out = NULL;
+ ret = k5_asn1_full_decode(code, &k5_atype_enc_tgs_rep_part, &rep_ptr);
+ if (ret == ASN1_BAD_ID) {
+ msg_type = KRB5_AS_REP;
+ ret = k5_asn1_full_decode(code, &k5_atype_enc_as_rep_part, &rep_ptr);
+ }
+ if (ret)
+ return ret;
+ rep = rep_ptr;
+ rep->msg_type = msg_type;
+ *rep_out = rep;
+ return 0;
+}
+
+MAKE_CODEC(krb5_as_rep, as_rep);
+MAKE_CODEC(krb5_tgs_rep, tgs_rep);
+MAKE_CODEC(krb5_ap_req, ap_req);
+MAKE_CODEC(krb5_ap_rep, ap_rep);
+MAKE_CODEC(krb5_ap_rep_enc_part, ap_rep_enc_part);
+MAKE_ENCODER(encode_krb5_as_req, as_req_encode);
+MAKE_DECODER(decode_krb5_as_req, as_req);
+MAKE_ENCODER(encode_krb5_tgs_req, tgs_req_encode);
+MAKE_DECODER(decode_krb5_tgs_req, tgs_req);
+MAKE_CODEC(krb5_kdc_req_body, kdc_req_body);
+MAKE_CODEC(krb5_safe, safe);
+
+/* encode_krb5_safe_with_body takes a saved KRB-SAFE-BODY encoding to avoid
+ * mismatches from re-encoding if the sender isn't quite DER-compliant. */
MAKE_ENCODER(encode_krb5_safe_with_body, safe_with_body);
-MAKE_ENCODER(encode_krb5_priv, priv);
-MAKE_ENCODER(encode_krb5_enc_priv_part, priv_enc_part);
-MAKE_ENCODER(encode_krb5_checksum, checksum);
-
-MAKE_ENCODER(encode_krb5_cred, krb5_cred);
-MAKE_ENCODER(encode_krb5_enc_cred_part, enc_cred_part);
-MAKE_ENCODER(encode_krb5_error, krb5_error);
-MAKE_ENCODER(encode_krb5_authdata, auth_data);
-MAKE_ENCODER(encode_krb5_etype_info, etype_info);
-MAKE_ENCODER(encode_krb5_etype_info2, etype_info2);
-MAKE_ENCODER(encode_krb5_enc_data, encrypted_data);
-MAKE_ENCODER(encode_krb5_pa_enc_ts, pa_enc_ts);
-MAKE_ENCODER(encode_krb5_padata_sequence, seqof_pa_data);
+/*
+ * decode_krb5_safe_with_body fully decodes a KRB-SAFE, but also returns
+ * the KRB-SAFE-BODY encoding. This interface was designed for an earlier
+ * generation of decoder and should probably be re-thought.
+ */
+krb5_error_code
+decode_krb5_safe_with_body(const krb5_data *code, krb5_safe **rep_out,
+ krb5_data **body_out)
+{
+ asn1_error_code ret;
+ void *swb_ptr, *safe_ptr;
+ struct krb5_safe_with_body *swb;
+ krb5_safe *safe;
+
+ ret = k5_asn1_full_decode(code, &k5_atype_safe_with_body, &swb_ptr);
+ if (ret)
+ return ret;
+ swb = swb_ptr;
+ ret = k5_asn1_full_decode(swb->body, &k5_atype_safe_body, &safe_ptr);
+ if (ret) {
+ krb5_free_safe(NULL, swb->safe);
+ krb5_free_data(NULL, swb->body);
+ free(swb);
+ return ret;
+ }
+ safe = safe_ptr;
+ safe->checksum = swb->safe->checksum;
+ free(swb->safe);
+ *rep_out = safe;
+ *body_out = swb->body;
+ free(swb);
+ return 0;
+}
+
+MAKE_CODEC(krb5_priv, priv);
+MAKE_CODEC(krb5_enc_priv_part, priv_enc_part);
+MAKE_CODEC(krb5_checksum, checksum);
+
+MAKE_CODEC(krb5_cred, krb5_cred);
+MAKE_CODEC(krb5_enc_cred_part, enc_cred_part);
+MAKE_CODEC(krb5_error, krb5_error);
+MAKE_CODEC(krb5_authdata, auth_data);
+MAKE_CODEC(krb5_etype_info, etype_info);
+MAKE_CODEC(krb5_etype_info2, etype_info2);
+MAKE_CODEC(krb5_enc_data, encrypted_data);
+MAKE_CODEC(krb5_pa_enc_ts, pa_enc_ts);
+MAKE_CODEC(krb5_padata_sequence, seqof_pa_data);
/* sam preauth additions */
-MAKE_ENCODER(encode_krb5_sam_challenge_2, sam_challenge_2);
-MAKE_ENCODER(encode_krb5_sam_challenge_2_body, sam_challenge_2_body);
-MAKE_ENCODER(encode_krb5_enc_sam_response_enc_2, enc_sam_response_enc_2);
-MAKE_ENCODER(encode_krb5_sam_response_2, sam_response_2);
+MAKE_CODEC(krb5_sam_challenge_2, sam_challenge_2);
+MAKE_CODEC(krb5_sam_challenge_2_body, sam_challenge_2_body);
+MAKE_CODEC(krb5_enc_sam_response_enc_2, enc_sam_response_enc_2);
+MAKE_CODEC(krb5_sam_response_2, sam_response_2);
+
+/* setpw_req has an odd decoder interface which should probably be
+ * normalized. */
MAKE_ENCODER(encode_krb5_setpw_req, setpw_req);
-MAKE_ENCODER(encode_krb5_pa_for_user, pa_for_user);
+krb5_error_code
+decode_krb5_setpw_req(const krb5_data *code, krb5_data **password_out,
+ krb5_principal *target_out)
+{
+ asn1_error_code ret;
+ void *req_ptr;
+ struct krb5_setpw_req *req;
+ krb5_data *data;
+
+ *password_out = NULL;
+ *target_out = NULL;
+ data = malloc(sizeof(*data));
+ if (data == NULL)
+ return ENOMEM;
+ ret = k5_asn1_full_decode(code, &k5_atype_setpw_req, &req_ptr);
+ if (ret) {
+ free(data);
+ return ret;
+ }
+ req = req_ptr;
+ *data = req->password;
+ *password_out = data;
+ *target_out = req->target;
+ return 0;
+}
+
+MAKE_CODEC(krb5_pa_for_user, pa_for_user);
MAKE_ENCODER(encode_krb5_s4u_userid, s4u_userid);
-MAKE_ENCODER(encode_krb5_pa_s4u_x509_user, pa_s4u_x509_user);
-MAKE_ENCODER(encode_krb5_etype_list, etype_list);
+MAKE_CODEC(krb5_pa_s4u_x509_user, pa_s4u_x509_user);
+MAKE_DECODER(decode_krb5_pa_pac_req, pa_pac_req);
+MAKE_CODEC(krb5_etype_list, etype_list);
-MAKE_ENCODER(encode_krb5_pa_fx_fast_request, pa_fx_fast_request);
-MAKE_ENCODER(encode_krb5_fast_req, fast_req);
-MAKE_ENCODER(encode_krb5_pa_fx_fast_reply, pa_fx_fast_reply);
-MAKE_ENCODER(encode_krb5_fast_response, fast_response);
+MAKE_CODEC(krb5_pa_fx_fast_request, pa_fx_fast_request);
+MAKE_CODEC(krb5_fast_req, fast_req);
+MAKE_CODEC(krb5_pa_fx_fast_reply, pa_fx_fast_reply);
+MAKE_CODEC(krb5_fast_response, fast_response);
-MAKE_ENCODER(encode_krb5_ad_kdcissued, ad_kdc_issued);
+MAKE_CODEC(krb5_ad_kdcissued, ad_kdc_issued);
MAKE_ENCODER(encode_krb5_ad_signedpath_data, ad_signedpath_data);
-MAKE_ENCODER(encode_krb5_ad_signedpath, ad_signedpath);
-MAKE_ENCODER(encode_krb5_iakerb_header, iakerb_header);
-MAKE_ENCODER(encode_krb5_iakerb_finished, iakerb_finished);
+MAKE_CODEC(krb5_ad_signedpath, ad_signedpath);
+MAKE_CODEC(krb5_iakerb_header, iakerb_header);
+MAKE_CODEC(krb5_iakerb_finished, iakerb_finished);
/*
* PKINIT
#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);
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. */
};
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);
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 */
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);