partial rewrite of the ASN.1 encoders
authorKen Raeburn <raeburn@mit.edu>
Sat, 25 Oct 2008 07:03:11 +0000 (07:03 +0000)
committerKen Raeburn <raeburn@mit.edu>
Sat, 25 Oct 2008 07:03:11 +0000 (07:03 +0000)
Instead of a pile of macros generating code, that have to be threaded
together in just the right way to get a valid ASN.1 encoding, we now
have a pile of macros for defining data structures describing the
objects and the ASN.1 types they should be encoded as, which
structures are interpreted by recursive invocations of an encoder
engine; there should be somewhat less rope for accidentally creating
invalid encodings.  The new macros are commented in asn1_k_encode.c.

Putting most of the work into the encoder engine also reduces the code
size (in one configuration, including LDAP-KDB and PKINIT encoders,
code size went from 37K to <16K, though 10K of tables were added, and
the PKINIT encoders are still open-coded).

Some encoder interfaces have been revised to be more regular -- all
now take one pointer to const argument (no two-input encoders, no
pointer-to-non-const-pointer-to-const).  A few encoders were
eliminated or disabled because they were neither used nor exported
from the library.

The LDAP-KDB encoder has been converted, but the PKINIT encoders have
not as there are no regression tests for them currently.

There is still plenty of room for improvement; some notes on specific
ideas have been added.

String encoding primitives have been combined to reduce code size.  A
primitive for encoding bit strings has been added.

Some miscellaneous warnings in the decoders have been cleaned up.

A new dejagnu test case is added that ensures that KRB-SAFE messages
get exercised.

ticket: new

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@20923 dc483132-0cff-0310-8789-dd5450dbe970

19 files changed:
src/include/k5-int.h
src/kdc/kdc_preauth.c
src/lib/krb5/asn.1/TODO.asn1 [new file with mode: 0644]
src/lib/krb5/asn.1/asn1_encode.c
src/lib/krb5/asn.1/asn1_encode.h
src/lib/krb5/asn.1/asn1_k_decode.c
src/lib/krb5/asn.1/asn1_k_encode.c
src/lib/krb5/asn.1/asn1_k_encode.h
src/lib/krb5/asn.1/asn1buf.c
src/lib/krb5/asn.1/asn1buf.h
src/lib/krb5/asn.1/krb5_encode.c
src/lib/krb5/asn.1/krbasn1.h
src/lib/krb5/asn.1/ldap_key_seq.c
src/lib/krb5/krb/chpw.c
src/lib/krb5/krb/rd_safe.c
src/lib/krb5/krb/send_tgs.c
src/tests/asn.1/Makefile.in
src/tests/asn.1/krb5_encode_test.c
src/tests/dejagnu/krb-standalone/simple.exp [new file with mode: 0644]

index bd038db50448a9b6cb1b6903274f0b47a336174b..a42fb7493f46a2a4b2e068b9a8bfc3d28bd4bb7e 100644 (file)
@@ -1376,8 +1376,12 @@ krb5_error_code encode_krb5_kdc_req_body
 krb5_error_code encode_krb5_safe
        (const krb5_safe *rep, krb5_data **code);
 
+struct krb5_safe_with_body {
+    krb5_safe *safe;
+    krb5_data *body;
+};
 krb5_error_code encode_krb5_safe_with_body
-       (const krb5_safe *rep, const krb5_data *body, krb5_data **code);
+       (const struct krb5_safe_with_body *rep, krb5_data **code);
 
 krb5_error_code encode_krb5_priv
        (const krb5_priv *rep, krb5_data **code);
@@ -1395,7 +1399,7 @@ krb5_error_code encode_krb5_error
        (const krb5_error *rep, krb5_data **code);
 
 krb5_error_code encode_krb5_authdata
-       (const krb5_authdata **rep, krb5_data **code);
+       (krb5_authdata *const *rep, krb5_data **code);
 
 krb5_error_code encode_krb5_authdata_elt
        (const krb5_authdata *rep, krb5_data **code);
@@ -1407,15 +1411,15 @@ krb5_error_code encode_krb5_pwd_data
        (const krb5_pwd_data *rep, krb5_data **code);
 
 krb5_error_code encode_krb5_padata_sequence
-       (const krb5_pa_data ** rep, krb5_data **code);
+       (krb5_pa_data *const *rep, krb5_data **code);
 
 krb5_error_code encode_krb5_alt_method
        (const krb5_alt_method *, krb5_data **code);
 
 krb5_error_code encode_krb5_etype_info
-       (const krb5_etype_info_entry **, krb5_data **code);
+       (krb5_etype_info_entry *const *, krb5_data **code);
 krb5_error_code encode_krb5_etype_info2
-       (const krb5_etype_info_entry **, krb5_data **code);
+       (krb5_etype_info_entry *const *, krb5_data **code);
 
 krb5_error_code encode_krb5_enc_data
        (const krb5_enc_data *, krb5_data **);
@@ -1435,11 +1439,13 @@ krb5_error_code encode_krb5_enc_sam_response_enc
 krb5_error_code encode_krb5_sam_response
        (const krb5_sam_response * , krb5_data **);
 
+#if 0 /* currently not compiled because we never use them */
 krb5_error_code encode_krb5_sam_challenge_2
        (const krb5_sam_challenge_2 * , krb5_data **);
 
 krb5_error_code encode_krb5_sam_challenge_2_body
        (const krb5_sam_challenge_2_body * , krb5_data **);
+#endif
 
 krb5_error_code encode_krb5_enc_sam_response_enc_2
        (const krb5_enc_sam_response_enc_2 * , krb5_data **);
@@ -1450,8 +1456,12 @@ krb5_error_code encode_krb5_sam_response_2
 krb5_error_code encode_krb5_predicted_sam_response
        (const krb5_predicted_sam_response * , krb5_data **);
 
+struct krb5_setpw_req {
+    krb5_principal target;
+    krb5_data password;
+};
 krb5_error_code encode_krb5_setpw_req
-(const krb5_principal target, char *password, krb5_data **code);
+(const struct krb5_setpw_req *rep, krb5_data **code);
 
 /*************************************************************************
  * End of prototypes for krb5_encode.c
@@ -1604,7 +1614,7 @@ struct ldap_seqof_key_data {
 typedef struct ldap_seqof_key_data ldap_seqof_key_data;
 
 krb5_error_code
-krb5int_ldap_encode_sequence_of_keys (ldap_seqof_key_data *val,
+krb5int_ldap_encode_sequence_of_keys (const ldap_seqof_key_data *val,
                                      krb5_data **code);
 
 krb5_error_code
index a250cf4ecd7a68b7cbdb1d8b07722a85d366bc71..9fd8d8d98f3280833a321fd5b85c70b93dcbce69 100644 (file)
@@ -899,8 +899,7 @@ void get_preauth_hint_list(krb5_kdc_req *request, krb5_db_entry *client,
                          "%spreauth required but hint list is empty",
                          hw_only ? "hw" : "");
     }
-    retval = encode_krb5_padata_sequence((const krb5_pa_data **) pa_data,
-                                        &edat);
+    retval = encode_krb5_padata_sequence(pa_data, &edat);
     if (retval)
        goto errout;
     *e_data = *edat;
@@ -1503,10 +1502,9 @@ etype_info_helper(krb5_context context, krb5_kdc_req *request,
        }
     }
     if (etype_info2)
-       retval = encode_krb5_etype_info2((const krb5_etype_info_entry **) entry,
-                                   &scratch);
-    else       retval = encode_krb5_etype_info((const krb5_etype_info_entry **) entry,
-                                   &scratch);
+       retval = encode_krb5_etype_info2(entry, &scratch);
+    else
+       retval = encode_krb5_etype_info(entry, &scratch);
     if (retval)
        goto cleanup;
     pa_data->contents = (unsigned char *)scratch->data;
@@ -1598,9 +1596,9 @@ etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata,
        goto cleanup;
 
     if (etype_info2)
-       retval = encode_krb5_etype_info2((const krb5_etype_info_entry **) entry, &scratch);
+       retval = encode_krb5_etype_info2(entry, &scratch);
     else
-       retval = encode_krb5_etype_info((const krb5_etype_info_entry **) entry, &scratch);
+       retval = encode_krb5_etype_info(entry, &scratch);
 
     if (retval)
        goto cleanup;
diff --git a/src/lib/krb5/asn.1/TODO.asn1 b/src/lib/krb5/asn.1/TODO.asn1
new file mode 100644 (file)
index 0000000..73f5fed
--- /dev/null
@@ -0,0 +1,90 @@
+-*- text -*-
+
+Stuff that should still be done on the ASN.1 encoder conversion:
+
+* Add support for opaque objects (pre-encoded fields, or ANY), and fix
+  up those remaining encoders (e.g., asn1_encode_sam_challenge_2) that
+  need them.
+
+* Convert PKINIT encoders, after we have test cases.
+
+* Make offsetof uses conforming.  Currently we may use foo.bar or
+  foo[0] as fields.
+
+* Script to generate the tables.  Then each type or field entry can
+  generate multiple bits of code, instead of forcing us to bury the
+  type consistency checking into the structure initializer
+  expression.  For example, we might generate these bits of code from
+  one field descriptor:
+
+  * Field table entry.
+
+  * Type-checking code: Create a pointer of the expected type and a
+    pointer of the actual type (address of field of automatic struct),
+    and verify consistency with comparison, assignment, or conditional
+    expr.  Plenty of comments to indicate what's being compared and
+    what a compiler complain means.
+
+  * Range-checking code for bitfields: Create an automatic field info
+    struct, fill in the computed offset or whatever, read it back,
+    make sure it matches.  Also with comments.
+
+  * Possibly header declarations describing the types that could be
+    imported, with correct handles *and* C types.
+
+  * Static declarations for non-exported types to keep symbol table
+    sizes down.
+
+  Then similar bits of code (e.g., all the field table entries) can be
+  pulled together into the appropriate places.
+
+* Some kind of "module" system for exporting and importing encoders,
+  better than relying on the "type_*" variable names.  Probably use
+  meaningful strings that indicate both the ASN.1 type and the
+  associated C type.  Find a way to fit "imported type" into this
+  scheme so that we can cleanly move the PKINIT types into the PKINIT
+  plugin, the LDAP types into the LDAP plugin, etc., and still let
+  them use the encoders in the code.  Only a subset of types would be
+  exported probably.
+
+* More compact encoding: For tags and optional-field bit positions,
+  encode N+1, and use 0 for "none", then make the field unsigned.
+  Currently the fields are signed, non-negative values hold useful
+  data, -1 means "none", and MIN..-2 are unused.  Changing this will
+  either let us reduce the field size one bit, or extend the maximum
+  tag/bitpos value from 2**(N-1)-1 to 2**N-2.
+
+* More compact encoding: Use a union with designated initializers, or
+  some ugly casting, to make the structures smaller by not having all
+  fields present when we never use all of them at once.  The union
+  approach is certainly more appealing, aside from the little detail
+  that it won't work on Windows unless we do all the initialization at
+  run time.
+
+* Pie in the sky: A verbose mode that can tell you "missing field
+  KDC-REP.cname.name-string[1].data" or some such.  This would require
+  tracking the stack of pending encodes and adding strings with type
+  and field names.
+
+* For ALL_POINTERS_ARE_THE_SAME mode (which is not strictly conforming
+  with the C standard, and thus not default currently, but makes
+  things a little smaller and faster), eliminate the loadptr structure
+  entry.
+
+* Maybe: Reorganize the data of a "module" so everything needing
+  relocation is put in some tables, referenced by index from other
+  structures without relocations.  E.g., for krb5_data, here's the
+  offset for the data pointer, here's the offset for the length value,
+  here's the index into the pointer reader function table, here's the
+  index into the length reader function table, here's an index into
+  the string-type encoder table.
+
+  Using an index into a set of pointer types, with a single function
+  taking an integer parameter used to switch between various
+  ptr-to-ptr-to-type code paths, will be a lot smaller -- with a good
+  compiler the function will probably collapse to a simple
+  fetch-a-pointer function ignoring the integer argument, while at the
+  C level it's strictly conforming by using the correct types for
+  access.
+
+* Table-driven decoders?
index d55e1832f38502a8773323b2de03ebfdade2cff4..d2caefded94f61c6dbdf9f4339574f0505c498a4 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * src/lib/krb5/asn.1/asn1_encode.c
  *
- * Copyright 1994 by the Massachusetts Institute of Technology.
+ * Copyright 1994, 2008 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
@@ -30,7 +30,8 @@
 #include "asn1_encode.h"
 #include "asn1_make.h"
 
-static asn1_error_code asn1_encode_integer_internal(asn1buf *buf,  long val,
+static asn1_error_code asn1_encode_integer_internal(asn1buf *buf,
+                                                    asn1_intmax val,
                                                     unsigned int *retlen)
 {
     asn1_error_code retval;
@@ -62,7 +63,7 @@ static asn1_error_code asn1_encode_integer_internal(asn1buf *buf,  long val,
     return 0;
 }
 
-asn1_error_code asn1_encode_integer(asn1buf * buf,  long val,
+asn1_error_code asn1_encode_integer(asn1buf * buf, asn1_intmax val,
                                     unsigned int *retlen)
 {
     asn1_error_code retval;
@@ -80,6 +81,7 @@ asn1_error_code asn1_encode_integer(asn1buf * buf,  long val,
     return 0;
 }
 
+#if 0
 asn1_error_code
 asn1_encode_enumerated(asn1buf * buf, long val,
                        unsigned int *retlen)
@@ -98,8 +100,9 @@ asn1_encode_enumerated(asn1buf * buf, long val,
     *retlen = length;
     return 0;
 }
+#endif
 
-asn1_error_code asn1_encode_unsigned_integer(asn1buf *buf, unsigned long val,
+asn1_error_code asn1_encode_unsigned_integer(asn1buf *buf, asn1_uintmax val,
                                              unsigned int *retlen)
 {
     asn1_error_code retval;
@@ -115,7 +118,7 @@ asn1_error_code asn1_encode_unsigned_integer(asn1buf *buf, unsigned long val,
         if (retval) return retval;
         length++;
         valcopy = valcopy >> 8;
-    } while (valcopy != 0 && valcopy != ~0);
+    } while (valcopy != 0);
 
     if (digit&0x80) {                     /* make sure the high bit is */
         retval = asn1buf_insert_octet(buf,0); /* of the proper signed-ness */
@@ -131,16 +134,18 @@ asn1_error_code asn1_encode_unsigned_integer(asn1buf *buf, unsigned long val,
     return 0;
 }
 
-asn1_error_code asn1_encode_oid(asn1buf *buf, unsigned int len,
-                                const asn1_octet *val,
-                                unsigned int *retlen)
+static asn1_error_code
+encode_bytestring_with_tag(asn1buf *buf, unsigned int len,
+                           const void *val, int tag,
+                           unsigned int *retlen)
 {
     asn1_error_code retval;
     unsigned int length;
 
+    if (len > 0 && val == 0) return ASN1_MISSING_FIELD;
     retval = asn1buf_insert_octetstring(buf, len, val);
     if (retval) return retval;
-    retval = asn1_make_tag(buf, UNIVERSAL, PRIMITIVE, ASN1_OBJECTIDENTIFIER,
+    retval = asn1_make_tag(buf, UNIVERSAL, PRIMITIVE, tag,
                            len, &length);
     if (retval) return retval;
 
@@ -148,37 +153,23 @@ asn1_error_code asn1_encode_oid(asn1buf *buf, unsigned int len,
     return 0;
 }
 
-asn1_error_code asn1_encode_octetstring(asn1buf *buf, unsigned int len,
-                                        const asn1_octet *val,
-                                        unsigned int *retlen)
+asn1_error_code asn1_encode_oid(asn1buf *buf, unsigned int len,
+                                const asn1_octet *val,
+                                unsigned int *retlen)
 {
-    asn1_error_code retval;
-    unsigned int length;
-
-    retval = asn1buf_insert_octetstring(buf,len,val);
-    if (retval) return retval;
-    retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_OCTETSTRING,len,&length);
-    if (retval) return retval;
-
-    *retlen = len + length;
-    return 0;
+    return encode_bytestring_with_tag(buf, len, val, ASN1_OBJECTIDENTIFIER,
+                                      retlen);
 }
 
-asn1_error_code asn1_encode_charstring(asn1buf *buf, unsigned int len,
-                                       const char *val, unsigned int *retlen)
+asn1_error_code asn1_encode_octetstring(asn1buf *buf, unsigned int len,
+                                        const void *val,
+                                        unsigned int *retlen)
 {
-    asn1_error_code retval;
-    unsigned int length;
-
-    retval = asn1buf_insert_charstring(buf,len,val);
-    if (retval) return retval;
-    retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_OCTETSTRING,len,&length);
-    if (retval) return retval;
-
-    *retlen = len + length;
-    return 0;
+    return encode_bytestring_with_tag(buf, len, val, ASN1_OCTETSTRING,
+                                      retlen);
 }
 
+#if 0
 asn1_error_code asn1_encode_null(asn1buf *buf, int *retlen)
 {
     asn1_error_code retval;
@@ -195,40 +186,23 @@ asn1_error_code asn1_encode_null(asn1buf *buf, int *retlen)
 asn1_error_code asn1_encode_printablestring(asn1buf *buf, unsigned int len,
                                             const char *val, int *retlen)
 {
-    asn1_error_code retval;
-    unsigned int length;
-
-    retval = asn1buf_insert_charstring(buf,len,val);
-    if (retval) return retval;
-    retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_PRINTABLESTRING,len,                     &length);
-    if (retval) return retval;
-
-    *retlen = len + length;
-    return 0;
+    return encode_bytestring_with_tag(buf, len, val, ASN1_PRINTABLESTRING,
+                                      retlen);
 }
 
 asn1_error_code asn1_encode_ia5string(asn1buf *buf, unsigned int len,
                                       const char *val, int *retlen)
 {
-    asn1_error_code retval;
-    unsigned int length;
-
-    retval = asn1buf_insert_charstring(buf,len,val);
-    if (retval) return retval;
-    retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_IA5STRING,len,                   &length);
-    if (retval) return retval;
-
-    *retlen = len + length;
-    return 0;
+    return encode_bytestring_with_tag(buf, len, val, ASN1_IA5STRING,
+                                      retlen);
 }
+#endif
 
 asn1_error_code asn1_encode_generaltime(asn1buf *buf, time_t val,
                                         unsigned int *retlen)
 {
-    asn1_error_code retval;
     struct tm *gtime, gtimebuf;
     char s[16], *sp;
-    unsigned int length, sum=0;
     time_t gmt_time = val;
 
     /*
@@ -237,6 +211,7 @@ asn1_error_code asn1_encode_generaltime(asn1buf *buf, time_t val,
     if (gmt_time == 0) {
         sp = "19700101000000Z";
     } else {
+        int len;
 
         /*
          * Sanity check this just to be paranoid, as gmtime can return NULL,
@@ -262,40 +237,435 @@ asn1_error_code asn1_encode_generaltime(asn1buf *buf, time_t val,
             gtime->tm_mday > 31 || gtime->tm_hour > 23 ||
             gtime->tm_min > 59 || gtime->tm_sec > 59)
             return ASN1_BAD_GMTIME;
-        if (snprintf(s, sizeof(s), "%04d%02d%02d%02d%02d%02dZ",
-                     1900+gtime->tm_year, gtime->tm_mon+1, gtime->tm_mday,
-                     gtime->tm_hour, gtime->tm_min, gtime->tm_sec)
-            >= sizeof(s))
+        len = snprintf(s, sizeof(s), "%04d%02d%02d%02d%02d%02dZ",
+                       1900+gtime->tm_year, gtime->tm_mon+1,
+                       gtime->tm_mday, gtime->tm_hour,
+                       gtime->tm_min, gtime->tm_sec);
+        if (SNPRINTF_OVERFLOW(len, sizeof(s)))
             /* Shouldn't be possible given above tests.  */
             return ASN1_BAD_GMTIME;
         sp = s;
     }
 
-    retval = asn1buf_insert_charstring(buf,15,sp);
+    return encode_bytestring_with_tag(buf, 15, sp, ASN1_GENERALTIME,
+                                      retlen);
+}
+
+asn1_error_code asn1_encode_generalstring(asn1buf *buf, unsigned int len,
+                                          const void *val,
+                                          unsigned int *retlen)
+{
+    return encode_bytestring_with_tag(buf, len, val, ASN1_GENERALSTRING,
+                                      retlen);
+}
+
+asn1_error_code asn1_encode_bitstring(asn1buf *buf, unsigned int len,
+                                      const void *val,
+                                      unsigned int *retlen)
+{
+    asn1_error_code retval;
+    unsigned int length;
+
+    retval = asn1buf_insert_octetstring(buf, len, val);
     if (retval) return retval;
-    sum = 15;
+    retval = asn1buf_insert_octet(buf, 0);
+    if (retval) return retval;
+    retval = asn1_make_tag(buf, UNIVERSAL, PRIMITIVE, ASN1_BITSTRING,
+                           len+1, &length);
+    if (retval) return retval;
+    *retlen = len + 1 + length;
+    return 0;
+}
 
-    retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_GENERALTIME,sum,&length);
+asn1_error_code asn1_encode_opaque(asn1buf *buf, unsigned int len,
+                                   const void *val, unsigned int *retlen)
+{
+    asn1_error_code retval;
+
+    retval = asn1buf_insert_octetstring(buf, len, val);
     if (retval) return retval;
-    sum += length;
+    *retlen = len;
+    return 0;
+}
+
+/* ASN.1 constructed type encoder engine
 
+   Two entry points here:
+
+   krb5int_asn1_encode_a_thing: Incrementally adds the partial
+   encoding of an object to an already-initialized asn1buf.
+
+   krb5int_asn1_do_full_encode: Returns a completed encoding, in the
+   correct byte order, in an allocated krb5_data.  */
+
+#ifdef POINTERS_ARE_ALL_THE_SAME
+#define LOADPTR(PTR,TYPE)      \
+    (assert((TYPE)->loadptr != NULL), (TYPE)->loadptr(PTR))
+#else
+#define LOADPTR(PTR,TYPE)      \
+    (*(const void *const *)(PTR))
+#endif
+
+static int
+get_nullterm_sequence_len(const void *valp, const struct atype_info *seq)
+{
+    int i;
+    const struct atype_info *a;
+    const void *elt, *eltptr;
+
+    a = seq;
+    i = 0;
+    assert(a->type == atype_ptr);
+    assert(seq->size != 0);
+
+    while (1) {
+        eltptr = (const char *) valp + i * seq->size;
+        elt = LOADPTR(eltptr, a);
+        if (elt == NULL)
+            break;
+        i++;
+    }
+    return i;
+}
+static asn1_error_code
+encode_sequence_of(asn1buf *buf, int seqlen, const void *val,
+                   const struct atype_info *eltinfo,
+                   unsigned int *retlen);
+
+static asn1_error_code
+encode_nullterm_sequence_of(asn1buf *buf, const void *val,
+                            const struct atype_info *type,
+                            int can_be_empty,
+                            unsigned int *retlen)
+{
+    int length = get_nullterm_sequence_len(val, type);
+    if (!can_be_empty && length == 0) return ASN1_MISSING_FIELD;
+    return encode_sequence_of(buf, length, val, type, retlen);
+}
+
+static asn1_error_code
+just_encode_sequence(asn1buf *buf, const void *val,
+                     const struct seq_info *seq,
+                     unsigned int *retlen);
+static asn1_error_code
+encode_a_field(asn1buf *buf, const void *val,
+               const struct field_info *field,
+               unsigned int *retlen);
+
+asn1_error_code
+krb5int_asn1_encode_a_thing(asn1buf *buf, const void *val,
+                            const struct atype_info *a, unsigned int *retlen)
+{
+    switch (a->type) {
+    case atype_fn:
+        assert(a->enc != NULL);
+        return a->enc(buf, val, retlen);
+    case atype_sequence:
+        assert(a->seq != NULL);
+        return just_encode_sequence(buf, val, a->seq, retlen);
+    case atype_ptr:
+        assert(a->basetype != NULL);
+        return krb5int_asn1_encode_a_thing(buf, LOADPTR(val, a),
+                                           a->basetype, retlen);
+    case atype_field:
+        assert(a->field != NULL);
+        return encode_a_field(buf, val, a->field, retlen);
+    case atype_nullterm_sequence_of:
+    case atype_nonempty_nullterm_sequence_of:
+        assert(a->basetype != NULL);
+        return encode_nullterm_sequence_of(buf, val, a->basetype,
+                                           a->type == atype_nullterm_sequence_of,
+                                           retlen);
+    case atype_tagged_thing:
+    {
+        asn1_error_code retval;
+        unsigned int length, sum = 0;
+        retval = krb5int_asn1_encode_a_thing(buf, val, a->basetype, &length);
+        if (retval) return retval;
+        sum = length;
+        retval = asn1_make_etag(buf, a->tagtype, a->tagval, sum, &length);
+        if (retval) return retval;
+        sum += length;
+        *retlen = sum;
+        return 0;
+    }
+    case atype_int:
+        assert(a->loadint != NULL);
+        return asn1_encode_integer(buf, a->loadint(val), retlen);
+    case atype_uint:
+        assert(a->loaduint != NULL);
+        return asn1_encode_unsigned_integer(buf, a->loaduint(val), retlen);
+    case atype_min:
+    case atype_max:
+    case atype_fn_len:
+    default:
+        assert(a->type > atype_min);
+        assert(a->type < atype_max);
+        assert(a->type != atype_fn_len);
+        abort();
+    }
+}
+
+static asn1_error_code
+encode_a_field(asn1buf *buf, const void *val,
+               const struct field_info *field,
+               unsigned int *retlen)
+{
+    asn1_error_code retval;
+    unsigned int sum = 0;
+
+    if (val == NULL) return ASN1_MISSING_FIELD;
+
+    switch (field->ftype) {
+    case field_immediate:
+    {
+        unsigned int length;
+
+        retval = asn1_encode_integer(buf, field->dataoff, &length);
+        if (retval) return retval;
+        sum += length;
+        break;
+    }
+    case field_sequenceof_len:
+    {
+        const void *dataptr, *lenptr;
+        int slen;
+        unsigned int length;
+        const struct atype_info *a;
+
+        /* The field holds a pointer to the array of objects.  So the
+           address we compute is a pointer-to-pointer, and that's what
+           field->atype must help us dereference.  */
+        dataptr = (const char *)val + field->dataoff;
+        lenptr = (const char *)val + field->lenoff;
+        assert(field->atype->type == atype_ptr);
+        dataptr = LOADPTR(dataptr, field->atype);
+        a = field->atype->basetype;
+        assert(field->lentype != 0);
+        assert(field->lentype->type == atype_int || field->lentype->type == atype_uint);
+        assert(sizeof(int) <= sizeof(asn1_intmax));
+        assert(sizeof(unsigned int) <= sizeof(asn1_uintmax));
+        if (field->lentype->type == atype_int) {
+            asn1_intmax xlen = field->lentype->loadint(lenptr);
+            if (xlen < 0)
+                return EINVAL;
+            if ((unsigned int) xlen != (asn1_uintmax) xlen)
+                return EINVAL;
+            if ((unsigned int) xlen > INT_MAX)
+                return EINVAL;
+            slen = (int) xlen;
+        } else {
+            asn1_uintmax xlen = field->lentype->loaduint(lenptr);
+            if ((unsigned int) xlen != xlen)
+                return EINVAL;
+            if (xlen > INT_MAX)
+                return EINVAL;
+            slen = (int) xlen;
+        }
+        if (slen != 0 && dataptr == NULL)
+            return ASN1_MISSING_FIELD;
+        retval = encode_sequence_of(buf, slen, dataptr, a, &length);
+        if (retval) return retval;
+        sum += length;
+        break;
+    }
+    case field_normal:
+    {
+        const void *dataptr;
+        const struct atype_info *a;
+        unsigned int length;
+
+        dataptr = (const char *)val + field->dataoff;
+
+        a = field->atype;
+        assert(a->type != atype_fn_len);
+        retval = krb5int_asn1_encode_a_thing(buf, dataptr, a, &length);
+        if (retval) {
+            return retval;
+        }
+        sum += length;
+        break;
+    }
+    case field_string:
+    {
+        const void *dataptr, *lenptr;
+        const struct atype_info *a;
+        size_t slen;
+        unsigned int length;
+
+        dataptr = (const char *)val + field->dataoff;
+        lenptr = (const char *)val + field->lenoff;
+
+        a = field->atype;
+        assert(a->type == atype_fn_len);
+        assert(field->lentype != 0);
+        assert(field->lentype->type == atype_int || field->lentype->type == atype_uint);
+        assert(sizeof(int) <= sizeof(asn1_intmax));
+        assert(sizeof(unsigned int) <= sizeof(asn1_uintmax));
+        if (field->lentype->type == atype_int) {
+            asn1_intmax xlen = field->lentype->loadint(lenptr);
+            if (xlen < 0)
+                return EINVAL;
+            if ((size_t) xlen != (asn1_uintmax) xlen)
+                return EINVAL;
+            slen = (size_t) xlen;
+        } else {
+            asn1_uintmax xlen = field->lentype->loaduint(lenptr);
+            if ((size_t) xlen != xlen)
+                return EINVAL;
+            slen = (size_t) xlen;
+        }
+
+        dataptr = LOADPTR(dataptr, a);
+        if (slen == SIZE_MAX)
+            /* Error - negative or out of size_t range.  */
+            return EINVAL;
+        if (dataptr == NULL && slen != 0)
+            return ASN1_MISSING_FIELD;
+        /* Currently our string encoders want "unsigned int" for
+           lengths.  */
+        if (slen != (unsigned int) slen)
+            return EINVAL;
+        assert(a->enclen != NULL);
+        retval = a->enclen(buf, (unsigned int) slen, dataptr, &length);
+        if (retval) {
+            return retval;
+        }
+        sum += length;
+        break;
+    }
+    default:
+        assert(field->ftype > field_min);
+        assert(field->ftype < field_max);
+        assert(__LINE__ == 0);
+        abort();
+    }
+    if (field->tag >= 0) {
+        unsigned int length;
+        retval = asn1_make_etag(buf, CONTEXT_SPECIFIC, field->tag, sum,
+                                &length);
+        if (retval) {
+            return retval;
+        }
+        sum += length;
+    }
     *retlen = sum;
     return 0;
 }
 
-asn1_error_code asn1_encode_generalstring(asn1buf *buf, unsigned int len,
-                                          const char *val,
-                                          unsigned int *retlen)
+static asn1_error_code
+encode_fields(asn1buf *buf, const void *val,
+              const struct field_info *fields, size_t nfields,
+              unsigned int optional,
+              unsigned int *retlen)
 {
+    size_t i;
+    unsigned int sum = 0;
+    for (i = nfields; i > 0; i--) {
+        const struct field_info *f = fields+i-1;
+        unsigned int length;
+        asn1_error_code retval;
+        int present;
+
+        if (f->opt == -1)
+            present = 1;
+        else if ((1u << f->opt) & optional)
+            present = 1;
+        else
+            present = 0;
+        if (present) {
+            retval = encode_a_field(buf, val, f, &length);
+            if (retval) return retval;
+            sum += length;
+        }
+    }
+    *retlen = sum;
+    return 0;
+}
+
+static asn1_error_code
+just_encode_sequence(asn1buf *buf, const void *val,
+                     const struct seq_info *seq,
+                     unsigned int *retlen)
+{
+    const struct field_info *fields = seq->fields;
+    size_t nfields = seq->n_fields;
+    unsigned int optional;
     asn1_error_code retval;
-    unsigned int length;
+    unsigned int sum = 0;
+
+    if (seq->optional)
+        optional = seq->optional(val);
+    else
+        /* In this case, none of the field descriptors should indicate
+           that we examine any bits of this value.  */
+        optional = 0;
+    {
+        unsigned int length;
+        retval = encode_fields(buf, val, fields, nfields, optional, &length);
+        if (retval) return retval;
+        sum += length;
+    }
+    {
+        unsigned int length;
+        retval = asn1_make_sequence(buf, sum, &length);
+        if (retval) return retval;
+        sum += length;
+    }
+    *retlen = sum;
+    return 0;
+}
 
-    retval = asn1buf_insert_charstring(buf,len,val);
-    if (retval) return retval;
-    retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_GENERALSTRING,len,
-                           &length);
-    if (retval) return retval;
+static asn1_error_code
+encode_sequence_of(asn1buf *buf, int seqlen, const void *val,
+                   const struct atype_info *eltinfo,
+                   unsigned int *retlen)
+{
+    asn1_error_code retval;
+    unsigned int sum = 0;
+    int i;
 
-    *retlen = len + length;
+    for (i = seqlen-1; i >= 0; i--) {
+        const void *eltptr;
+        unsigned int length;
+        const struct atype_info *a = eltinfo;
+
+        assert(eltinfo->size != 0);
+        eltptr = (const char *)val + i * eltinfo->size;
+        retval = krb5int_asn1_encode_a_thing(buf, eltptr, a, &length);
+        if (retval) return retval;
+        sum += length;
+    }
+    {
+        unsigned int length;
+        retval = asn1_make_sequence(buf, sum, &length);
+        if (retval) return retval;
+        sum += length;
+    }
+    *retlen = sum;
     return 0;
 }
+
+krb5_error_code
+krb5int_asn1_do_full_encode(const void *rep, krb5_data **code,
+                            const struct atype_info *a)
+{
+    unsigned int length;
+    asn1_error_code retval;
+    unsigned int sum = 0;
+    asn1buf *buf = NULL;
+
+    if (rep == NULL) return ASN1_MISSING_FIELD;
+
+    retval = asn1buf_create(&buf);
+    if (retval)
+        return retval;
+
+    retval = krb5int_asn1_encode_a_thing(buf, rep, a, &length);
+    if (retval)
+        return retval;
+    sum += length;
+    retval = asn12krb5_buf(buf, code);
+    asn1buf_destroy(&buf);
+    return retval;
+}
index c75f4e8791037dff654c7c09e6c3ac1192501f59..14863339d27e04b16d5eb797bcb07c8ca1eaae44 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * src/lib/krb5/asn.1/asn1_encode.h
  *
- * Copyright 1994 by the Massachusetts Institute of Technology.
+ * Copyright 1994, 2008 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
    Operations
 
      asn1_encode_integer
+     asn1_encode_unsigned_integer
      asn1_encode_octetstring
-     asn1_encode_null
-     asn1_encode_printablestring
-     asn1_encode_ia5string
      asn1_encode_generaltime
      asn1_encode_generalstring
+     asn1_encode_bitstring
+     asn1_encode_oid
 */
 
 asn1_error_code asn1_encode_integer
-        (asn1buf *buf, long val, unsigned int *retlen);
+        (asn1buf *buf, asn1_intmax val, unsigned int *retlen);
 /* requires  *buf is allocated
    modifies  *buf, *retlen
    effects   Inserts the encoding of val into *buf and returns
@@ -63,7 +63,7 @@ asn1_error_code asn1_encode_enumerated
 (asn1buf *buf, long val, unsigned int *retlen);
 
 asn1_error_code asn1_encode_unsigned_integer
-        (asn1buf *buf, unsigned long val,
+        (asn1buf *buf, asn1_uintmax val,
                    unsigned int *retlen);
 /* requires  *buf is allocated
    modifies  *buf, *retlen
@@ -74,7 +74,7 @@ asn1_error_code asn1_encode_unsigned_integer
 
 asn1_error_code asn1_encode_octetstring
         (asn1buf *buf,
-                   unsigned int len, const asn1_octet *val,
+                   unsigned int len, const void *val,
                    unsigned int *retlen);
 /* requires  *buf is allocated
    modifies  *buf, *retlen
@@ -82,6 +82,7 @@ asn1_error_code asn1_encode_octetstring
               the length of the encoding in *retlen.
              Returns ENOMEM to signal an unsuccesful attempt
               to expand the buffer. */
+#define asn1_encode_charstring asn1_encode_octetstring
 
 asn1_error_code asn1_encode_oid
         (asn1buf *buf,
@@ -94,17 +95,6 @@ asn1_error_code asn1_encode_oid
              Returns ENOMEM to signal an unsuccesful attempt
               to expand the buffer. */
 
-asn1_error_code asn1_encode_charstring
-        (asn1buf *buf,
-                   unsigned int len, const char *val,
-                   unsigned int *retlen);
-/* requires  *buf is allocated
-   modifies  *buf, *retlen
-   effects   Inserts the encoding of val into *buf and returns
-              the length of the encoding in *retlen.
-             Returns ENOMEM to signal an unsuccesful attempt
-              to expand the buffer. */
-
 asn1_error_code asn1_encode_null
         (asn1buf *buf, int *retlen);
 /* requires  *buf is allocated
@@ -148,7 +138,7 @@ asn1_error_code asn1_encode_generaltime
 
 asn1_error_code asn1_encode_generalstring
         (asn1buf *buf,
-                   unsigned int len, const char *val,
+                   unsigned int len, const void *val,
                    unsigned int *retlen);
 /* requires  *buf is allocated,  val has a length of len characters
    modifies  *buf, *retlen
@@ -157,4 +147,515 @@ asn1_error_code asn1_encode_generalstring
              Returns ENOMEM to signal an unsuccesful attempt
               to expand the buffer. */
 
+asn1_error_code asn1_encode_bitstring(asn1buf *buf, unsigned int len,
+                                      const void *val,
+                                      unsigned int *retlen);
+/* requires  *buf is allocated,  val has a length of len characters
+   modifies  *buf, *retlen
+   effects   Inserts the encoding of val into *buf and returns
+              the length of the encoding in *retlen.
+             Returns ENOMEM to signal an unsuccesful attempt
+              to expand the buffer. */
+
+asn1_error_code asn1_encode_opaque(asn1buf *buf, unsigned int len,
+                                   const void *val,
+                                   unsigned int *retlen);
+/* requires  *buf is allocated,  val has a length of len characters
+   modifies  *buf, *retlen
+   effects   Inserts the encoding of val into *buf and returns
+              the length of the encoding in *retlen.
+             Returns ENOMEM to signal an unsuccesful attempt
+              to expand the buffer. */
+
+/* Type descriptor info.
+
+   In this context, a "type" is a combination of a C data type
+   and an ASN.1 encoding scheme for it.  So we would have to define
+   different "types" for:
+
+   * unsigned char* encoded as octet string
+   * char* encoded as octet string
+   * char* encoded as generalstring
+   * krb5_data encoded as octet string
+   * krb5_data encoded as generalstring
+   * int32_t encoded as integer
+   * unsigned char encoded as integer
+
+   Perhaps someday some kind of flags could be defined so that minor
+   variations on the C types could be handled via common routines.
+
+   The handling of strings is pretty messy.  Currently, we have a
+   separate kind of encoder function that takes an extra length
+   parameter.  Perhaps we should just give up on that, always deal
+   with just a single location, and handle strings by via encoder
+   functions for krb5_data, keyblock, etc.
+
+   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
+   much run-time performance.  It might work to have a master "module"
+   descriptor with pointers to various arrays (type descriptors,
+   strings, field descriptors, functions) most of which don't need
+   relocation themselves, and replace most of the pointers with table
+   indices.
+
+   It's a work in progress.  */
+
+enum atype_type {
+    /* For bounds checking only.  By starting with values above 1, we
+       guarantee that zero-initialized storage will be recognized as
+       invalid.  */
+    atype_min = 1,
+    /* Encoder function to be called with address of <thing>.  */
+    atype_fn,
+    /* Encoder function to be called with address of <thing> and a
+       length (unsigned int).  */
+    atype_fn_len,
+    /* Pointer to actual thing to be encoded.
+
+       Most of the fields are related only to the C type -- size, how
+       to fetch a pointer in a type-safe fashion -- but since the base
+       type descriptor encapsulates the encoding as well, different
+       encodings for the same C type may require different pointer-to
+       types as well.
+
+       Must not refer to atype_fn_len.  */
+    atype_ptr,
+    /* Sequence, with pointer to sequence descriptor header.  */
+    atype_sequence,
+    /* Sequence-of, with pointer to base type descriptor, represented
+       as a null-terminated array of pointers (and thus the "base"
+       type descriptor is actually an atype_ptr node).  */
+    atype_nullterm_sequence_of,
+    atype_nonempty_nullterm_sequence_of,
+    /* Encode this object using a single field descriptor.  This may
+       mean the atype/field breakdown needs revision....
+
+       Main expected uses: Encode realm component of principal as a
+       GENERALSTRING.  Pluck data and length fields out of a structure
+       and encode a counted SEQUENCE OF.  */
+    atype_field,
+    /* Tagged version of another type.  */
+    atype_tagged_thing,
+    /* Integer types.  */
+    atype_int,
+    atype_uint,
+    /* Unused except for bounds checking.  */
+    atype_max
+};
+
+/* Initialized structures could be a lot smaller if we could use C99
+   designated initializers, and a union for all the type-specific
+   stuff.  Maybe use the hack we use for krb5int_access, where we use
+   a run-time initialize if the compiler doesn't support designated
+   initializers?  That's a lot of work here, though, with so many
+   little structures.  Maybe if/when these get auto-generated.  */
+struct atype_info {
+    enum atype_type type;
+    /* used for sequence-of processing */
+    unsigned int size;
+    /* atype_fn */
+    asn1_error_code (*enc)(asn1buf *, const void *, unsigned int *);
+    /* atype_fn_len */
+    asn1_error_code (*enclen)(asn1buf *, unsigned int, const void *,
+                              unsigned int *);
+    /* atype_ptr, atype_fn_len */
+    const void *(*loadptr)(const void *);
+    /* atype_ptr, atype_nullterm_sequence_of */
+    const struct atype_info *basetype;
+    /* atype_sequence */
+    const struct seq_info *seq;
+    /* atype_field */
+    const struct field_info *field;
+    /* atype_tagged_thing */
+    unsigned int tagval : 8, tagtype : 8;
+    /* atype_[u]int */
+    asn1_intmax (*loadint)(const void *);
+    asn1_uintmax (*loaduint)(const void *);
+};
+
+/* The various DEF*TYPE macros must:
+
+   + Define a type named aux_typedefname_##DESCNAME, for use in any
+     types derived from the type being defined.
+
+   + Define an atype_info struct named krb5int_asn1type_##DESCNAME.
+
+   + Define any extra stuff needed in the type descriptor, like
+     pointer-load functions.
+
+   + Accept a following semicolon syntactically, to keep Emacs parsing
+     (and indentation calculating) code happy.
+
+   Nothing else should directly define the atype_info structures.  */
+
+/* Define a type for which we must use an explicit encoder function.
+   The DEFFNTYPE variant uses a function taking a void*, the
+   DEFFNXTYPE form wants a function taking a pointer to the actual C
+   type to be encoded; you should use the latter unless you've already
+   got the void* function supplied elsewhere.
+
+   Of course, we need a single, consistent type for the descriptor
+   structure field, so we use the function pointer type that uses
+   void*, and create a wrapper function in DEFFNXTYPE.  However, in
+   all our cases so far, the supplied function is static and not used
+   otherwise, so the compiler can merge it with the wrapper function
+   if the optimizer is good enough.  */
+#define DEFFNTYPE(DESCNAME, CTYPENAME, ENCFN)                   \
+    typedef CTYPENAME aux_typedefname_##DESCNAME;               \
+    const struct atype_info krb5int_asn1type_##DESCNAME = {     \
+        atype_fn, sizeof(CTYPENAME), ENCFN,                     \
+    }
+#define DEFFNXTYPE(DESCNAME, CTYPENAME, ENCFN)                  \
+    typedef CTYPENAME aux_typedefname_##DESCNAME;               \
+    static asn1_error_code                                      \
+    aux_encfn_##DESCNAME(asn1buf *buf, const void *val,         \
+                         unsigned int *retlen)                  \
+    {                                                           \
+        return ENCFN(buf,                                       \
+                     (const aux_typedefname_##DESCNAME *)val,   \
+                     retlen);                                   \
+    }                                                           \
+    const struct atype_info krb5int_asn1type_##DESCNAME = {     \
+        atype_fn, sizeof(CTYPENAME), aux_encfn_##DESCNAME,      \
+    }
+/* XXX The handling of data+length fields really needs reworking.
+   A type descriptor probably isn't the right way.
+
+   Also, the C type is likely to be one of char*, unsigned char*,
+   or (maybe) void*.  An enumerator or reference to an external
+   function would be more compact.
+
+   The supplied encoder function takes as an argument the data pointer
+   loaded from the indicated location, not the address of the field.
+   This isn't consistent with DEFFN[X]TYPE above, but all of the uses
+   of DEFFNLENTYPE are for string encodings, and that's how our
+   string-encoding primitives work.  So be it.  */
+#ifdef POINTERS_ARE_ALL_THE_SAME
+#define DEFFNLENTYPE(DESCNAME, CTYPENAME, ENCFN)                \
+    typedef CTYPENAME aux_typedefname_##DESCNAME;               \
+    const struct atype_info krb5int_asn1type_##DESCNAME = {     \
+        atype_fn_len, 0, 0, ENCFN,                              \
+    }
+#else
+#define DEFFNLENTYPE(DESCNAME, CTYPENAME, ENCFN)                \
+    typedef CTYPENAME aux_typedefname_##DESCNAME;               \
+    static const void *loadptr_for_##DESCNAME(const void *pv)   \
+    {                                                           \
+        const aux_typedefname_##DESCNAME *p = pv;               \
+        return *p;                                              \
+    }                                                           \
+    const struct atype_info krb5int_asn1type_##DESCNAME = {     \
+        atype_fn_len, 0, 0, ENCFN,                              \
+        loadptr_for_##DESCNAME                                  \
+    }
+#endif
+/* A sequence, defined by the indicated series of fields, and an
+   optional function indicating which fields are present.  */
+#define DEFSEQTYPE(DESCNAME, CTYPENAME, FIELDS, OPT)            \
+    typedef CTYPENAME aux_typedefname_##DESCNAME;               \
+    static const struct seq_info aux_seqinfo_##DESCNAME = {     \
+        OPT, FIELDS, sizeof(FIELDS)/sizeof(FIELDS[0])           \
+    };                                                          \
+    const struct atype_info krb5int_asn1type_##DESCNAME = {     \
+        atype_sequence, sizeof(CTYPENAME), 0,0,0,0,             \
+        &aux_seqinfo_##DESCNAME,                                \
+    }
+/* Integer types.  */
+#define DEFINTTYPE(DESCNAME, CTYPENAME)                         \
+    typedef CTYPENAME aux_typedefname_##DESCNAME;               \
+    static asn1_intmax loadint_##DESCNAME(const void *p)        \
+    {                                                           \
+        assert(sizeof(CTYPENAME) <= sizeof(asn1_intmax));       \
+        return *(const aux_typedefname_##DESCNAME *)p;          \
+    }                                                           \
+    const struct atype_info krb5int_asn1type_##DESCNAME = {     \
+        atype_int, sizeof(CTYPENAME), 0, 0, 0, 0, 0, 0, 0, 0,   \
+        loadint_##DESCNAME, 0,                                  \
+    }
+#define DEFUINTTYPE(DESCNAME, CTYPENAME)                        \
+    typedef CTYPENAME aux_typedefname_##DESCNAME;               \
+    static asn1_uintmax loaduint_##DESCNAME(const void *p)      \
+    {                                                           \
+        assert(sizeof(CTYPENAME) <= sizeof(asn1_uintmax));      \
+        return *(const aux_typedefname_##DESCNAME *)p;          \
+    }                                                           \
+    const struct atype_info krb5int_asn1type_##DESCNAME = {     \
+        atype_uint, sizeof(CTYPENAME), 0, 0, 0, 0, 0, 0, 0, 0,  \
+        0, loaduint_##DESCNAME,                                 \
+    }
+/* Pointers to other types, to be encoded as those other types.  */
+#ifdef POINTERS_ARE_ALL_THE_SAME
+#define DEFPTRTYPE(DESCNAME,BASEDESCNAME)                               \
+    typedef aux_typedefname_##BASEDESCNAME * aux_typedefname_##DESCNAME; \
+    const struct atype_info krb5int_asn1type_##DESCNAME = {             \
+        atype_ptr, sizeof(aux_typedefname_##DESCNAME), 0, 0, 0,         \
+        &krb5int_asn1type_##BASEDESCNAME, 0                             \
+    }
+#else
+#define DEFPTRTYPE(DESCNAME,BASEDESCNAME)                               \
+    typedef aux_typedefname_##BASEDESCNAME * aux_typedefname_##DESCNAME; \
+    static const void *                                                 \
+    loadptr_for_##BASEDESCNAME##_from_##DESCNAME(const void *p)         \
+    {                                                                   \
+        const aux_typedefname_##DESCNAME *inptr = p;                    \
+        const aux_typedefname_##BASEDESCNAME *retptr;                   \
+        retptr = *inptr;                                                \
+        return retptr;                                                  \
+    }                                                                   \
+    const struct atype_info krb5int_asn1type_##DESCNAME = {             \
+        atype_ptr, sizeof(aux_typedefname_##DESCNAME), 0, 0,            \
+        loadptr_for_##BASEDESCNAME##_from_##DESCNAME,                   \
+        &krb5int_asn1type_##BASEDESCNAME, 0                             \
+    }
+#endif
+/* This encodes a pointer-to-pointer-to-thing where the passed-in
+   value points to a null-terminated list of pointers to objects to be
+   encoded, and encodes a (possibly empty) SEQUENCE OF these objects.
+
+   BASEDESCNAME is a descriptor name for the pointer-to-thing
+   type.
+
+   When dealing with a structure containing a
+   pointer-to-pointer-to-thing field, make a DEFPTRTYPE of this type,
+   and use that type for the structure field.  */
+#define DEFNULLTERMSEQOFTYPE(DESCNAME,BASEDESCNAME)                     \
+    typedef aux_typedefname_##BASEDESCNAME aux_typedefname_##DESCNAME;  \
+    const struct atype_info krb5int_asn1type_##DESCNAME = {             \
+        atype_nullterm_sequence_of, sizeof(aux_typedefname_##DESCNAME), \
+        0, 0,                                                           \
+        0 /* loadptr */,                                                \
+        &krb5int_asn1type_##BASEDESCNAME, 0                             \
+    }
+#define DEFNONEMPTYNULLTERMSEQOFTYPE(DESCNAME,BASEDESCNAME)             \
+    typedef aux_typedefname_##BASEDESCNAME aux_typedefname_##DESCNAME;  \
+    const struct atype_info krb5int_asn1type_##DESCNAME = {             \
+        atype_nonempty_nullterm_sequence_of,                            \
+        sizeof(aux_typedefname_##DESCNAME),                             \
+        0, 0,                                                           \
+        0 /* loadptr */,                                                \
+        &krb5int_asn1type_##BASEDESCNAME, 0                             \
+    }
+/* Encode a thing (probably sub-fields within the structure) as a
+   single object.  */
+#define DEFFIELDTYPE(DESCNAME, CTYPENAME, FIELDINFO)                    \
+    typedef CTYPENAME aux_typedefname_##DESCNAME;                       \
+    static const struct field_info aux_fieldinfo_##DESCNAME = FIELDINFO; \
+    const struct atype_info krb5int_asn1type_##DESCNAME = {             \
+        atype_field, sizeof(CTYPENAME), 0, 0, 0, 0, 0,                  \
+        &aux_fieldinfo_##DESCNAME                                       \
+    }
+/* Objects with an APPLICATION tag added.  */
+#define DEFAPPTAGGEDTYPE(DESCNAME, TAG, BASEDESC)                       \
+    typedef aux_typedefname_##BASEDESC aux_typedefname_##DESCNAME;      \
+    const struct atype_info krb5int_asn1type_##DESCNAME = {             \
+        atype_tagged_thing, sizeof(aux_typedefname_##DESCNAME),         \
+        0, 0, 0, &krb5int_asn1type_##BASEDESC, 0, 0, TAG, APPLICATION   \
+    }
+
+/* Declare an externally-defined type.  This is a hack we should do
+   away with once we move to generating code from a script.  For now,
+   this macro is unfortunately not compatible with the defining macros
+   above, since you can't do the typedefs twice and we need the
+   declarations to produce typedefs.  (We could eliminate the typedefs
+   from the DEF* macros, but then every DEF* macro use, even the ones
+   for internal type nodes we only use to build other types, would
+   need an accompanying declaration which explicitly lists the
+   type.)  */
+#define IMPORT_TYPE(DESCNAME, CTYPENAME)                        \
+    typedef CTYPENAME aux_typedefname_##DESCNAME;               \
+    extern const struct atype_info krb5int_asn1type_##DESCNAME
+
+/* Create a partial-encoding function by the indicated name, for the
+   indicated type.  Should only be needed until we've converted all of
+   the encoders, then everything should use descriptor tables.  */
+extern asn1_error_code
+krb5int_asn1_encode_a_thing(asn1buf *buf, const void *val,
+                            const struct atype_info *a, unsigned int *retlen);
+#define MAKE_ENCFN(FNAME,DESC)                                          \
+   static asn1_error_code FNAME (asn1buf *buf,                          \
+                           const aux_typedefname_##DESC *val,           \
+                           unsigned int *retlen)                        \
+    {                                                                   \
+        return krb5int_asn1_encode_a_thing(buf, val,                    \
+                                           &krb5int_asn1type_##DESC,    \
+                                           retlen);                     \
+    }                                                                   \
+    extern int dummy /* gobble semicolon */
+
+/* Sequence field descriptor.
+
+   Currently we assume everything is a single object with a type
+   descriptor, and then we bolt on some ugliness on the side for
+   handling strings with length fields.
+
+   Anything with "interesting" encoding handling, like a sequence-of
+   or a pointer to the actual value to encode, is handled via opaque
+   types with their own encoder functions.  Most of that should
+   eventually change.  */
+
+enum field_type {
+    /* Unused except for range checking.  */
+    field_min = 1,
+    /* Field ATYPE describes processing of field at DATAOFF.  */
+    field_normal,
+    /* Encode an "immediate" integer value stored in DATAOFF, with no
+       reference to the data structure.  */
+    field_immediate,
+    /* Encode some kind of string field encoded with pointer and
+       length.  (A GENERALSTRING represented as a null-terminated C
+       string would be handled as field_normal.)  */
+    field_string,
+    /* LENOFF indicates a value describing the length of the array at
+       DATAOFF, encoded as a sequence-of with the element type
+       described by ATYPE.  */
+    field_sequenceof_len,
+    /* Unused except for range checking.  */
+    field_max
+};
+/* To do: Consider using bitfields.  */
+struct field_info {
+    /* Type of the field.  */
+    unsigned int /* enum field_type */ ftype : 3;
+
+    /* Use of DATAOFF and LENOFF are described by the value in FTYPE.
+       Generally DATAOFF will be the offset from the supplied pointer
+       at which we find the object to be encoded.  */
+    unsigned int dataoff : 9, lenoff : 9;
+
+    /* If TAG is non-negative, a context tag with that value is added
+       to the encoding of the thing.  (XXX This would encode more
+       compactly as an unsigned bitfield value tagnum+1, with 0=no
+       tag.)  The tag is omitted for optional fields that are not
+       present.
+
+       It's a bit illogical to combine the tag and other field info,
+       since really a sequence field could have zero or several
+       context tags, and of course a tag could be used elsewhere.  But
+       the normal mode in the Kerberos ASN.1 description is to use one
+       context tag on each sequence field, so for now let's address
+       that case primarily and work around the other cases (thus tag<0
+       means skip tagging).  */
+    signed int tag : 5;
+
+    /* If OPT is non-negative and the sequence header structure has a
+       function pointer describing which fields are present, OPT is
+       the bit position indicating whether the currently-described
+       element is present.  (XXX Similar encoding issue.)
+
+       Note: Most of the time, I'm using the same number here as for
+       the context tag.  This is just because it's easier for me to
+       keep track while working on the code by hand.  The *only*
+       meaningful correlation is of this value and the bits set by the
+       "optional" function when examining the data structure.  */
+    signed int opt : 5;
+
+    /* For some values of FTYPE, this describes the type of the
+       object(s) to be encoded.  */
+    const struct atype_info *atype;
+
+    /* We use different types for "length" fields in different places.
+       So we need a good way to retrieve the various kinds of lengths
+       in a compatible way.  This may be a string length, or the
+       length of an array of objects to encode in a SEQUENCE OF.
+
+       In case the field is signed and negative, or larger than
+       size_t, return SIZE_MAX as an error indication.  We'll assume
+       for now that we'll never have 4G-1 (or 2**64-1, or on tiny
+       systems, 65535) sized values.  On most if not all systems we
+       care about, SIZE_MAX is equivalent to "all of addressable
+       memory" minus one byte.  That wouldn't leave enough extra room
+       for the structure we're encoding, so it's pretty safe to assume
+       SIZE_MAX won't legitimately come up on those systems.
+
+       If this code gets ported to a segmented architecture or other
+       system where it might be possible... figure it out then.  */
+    const struct atype_info *lentype;
+};
+
+/* Normal or optional sequence fields at a particular offset, encoded
+   as indicated by the listed DESCRiptor.  */
+#define FIELDOF_OPT(TYPE,DESCR,FIELDNAME,TAG,OPT)                       \
+    {                                                                   \
+        field_normal, OFFOF(TYPE, FIELDNAME, aux_typedefname_##DESCR),  \
+        0, TAG, OPT, &krb5int_asn1type_##DESCR                          \
+    }
+#define FIELDOF_NORM(TYPE,DESCR,FIELDNAME,TAG)  \
+    FIELDOF_OPT(TYPE,DESCR,FIELDNAME,TAG,-1)
+/* If encoding a subset of the fields of the current structure (for
+   example, a flat structure describing data that gets encoded as a
+   sequence containing one or more sequences), use ENCODEAS, no struct
+   field name(s), and the indicated type descriptor must support the
+   current struct type.  */
+#define FIELDOF_ENCODEAS(TYPE,DESCR,TAG) \
+    FIELDOF_ENCODEAS_OPT(TYPE,DESCR,TAG,-1)
+#define FIELDOF_ENCODEAS_OPT(TYPE,DESCR,TAG,OPT)                        \
+    {                                                                   \
+        field_normal,                                                   \
+        0 * sizeof(0 ? (TYPE *)0 : (aux_typedefname_##DESCR *) 0),      \
+        0, TAG, OPT, &krb5int_asn1type_##DESCR                          \
+    }
+
+/* Reinterpret some subset of the structure itself as something
+   else.  */
+#define FIELD_SELF(DESCR, TAG) \
+    { field_normal, 0, 0, TAG, -1, &krb5int_asn1type_##DESCR }
+
+#define FIELDOF_OPTSTRINGL(STYPE,DESC,PTRFIELD,LENDESC,LENFIELD,TAG,OPT) \
+   {                                                                    \
+       field_string,                                                    \
+       OFFOF(STYPE, PTRFIELD, aux_typedefname_##DESC),                  \
+       OFFOF(STYPE, LENFIELD, aux_typedefname_##LENDESC),               \
+       TAG, OPT, &krb5int_asn1type_##DESC, &krb5int_asn1type_##LENDESC  \
+   }
+#define FIELDOF_OPTSTRING(STYPE,DESC,PTRFIELD,LENFIELD,TAG,OPT)         \
+    FIELDOF_OPTSTRINGL(STYPE,DESC,PTRFIELD,uint,LENFIELD,TAG,OPT)
+#define FIELDOF_STRINGL(STYPE,DESC,PTRFIELD,LENDESC,LENFIELD,TAG)       \
+    FIELDOF_OPTSTRINGL(STYPE,DESC,PTRFIELD,LENDESC,LENFIELD,TAG,-1)
+#define FIELDOF_STRING(STYPE,DESC,PTRFIELD,LENFIELD,TAG) \
+    FIELDOF_OPTSTRING(STYPE,DESC,PTRFIELD,LENFIELD,TAG,-1)
+#define FIELD_INT_IMM(VALUE,TAG)   \
+    { field_immediate, VALUE, 0, TAG, -1, 0, }
+
+#define FIELDOF_SEQOF_LEN(STYPE,DESC,PTRFIELD,LENFIELD,LENTYPE,TAG)     \
+    {                                                                   \
+        field_sequenceof_len,                                           \
+        OFFOF(STYPE, PTRFIELD, aux_typedefname_##DESC),                 \
+        OFFOF(STYPE, LENFIELD, aux_typedefname_##LENTYPE),              \
+        TAG, -1, &krb5int_asn1type_##DESC, &krb5int_asn1type_##LENTYPE  \
+    }
+#define FIELDOF_SEQOF_INT32(STYPE,DESC,PTRFIELD,LENFIELD,TAG)   \
+    FIELDOF_SEQOF_LEN(STYPE,DESC,PTRFIELD,LENFIELD,int32,TAG)
+
+struct seq_info {
+    /* If present, returns a bitmask indicating which fields are
+       present.  See the "opt" field in struct field_info.  */
+    unsigned int (*optional)(const void *);
+    /* Indicates an array of sequence field descriptors.  */
+    const struct field_info *fields;
+    size_t n_fields;
+    /* Missing: Extensibility handling.  (New field type?)  */
+};
+
+extern krb5_error_code
+krb5int_asn1_do_full_encode(const void *rep, krb5_data **code,
+                            const struct atype_info *a);
+
+#define MAKE_FULL_ENCODER(FNAME, DESC)                                  \
+    krb5_error_code FNAME(const aux_typedefname_##DESC *rep,            \
+                          krb5_data **code)                             \
+    {                                                                   \
+        return krb5int_asn1_do_full_encode(rep, code,                   \
+                                           &krb5int_asn1type_##DESC);   \
+    }                                                                   \
+    extern int dummy /* gobble semicolon */
+
+#include <stddef.h>
+/* Ugly hack!
+   Like "offsetof", but with type checking.  */
+#define WARN_IF_TYPE_MISMATCH(LVALUE, TYPE)  \
+    (sizeof(0 ? (TYPE *) 0 : &(LVALUE)))
+#define OFFOF(TYPE,FIELD,FTYPE)                                 \
+    (offsetof(TYPE, FIELD)                                      \
+     + 0 * WARN_IF_TYPE_MISMATCH(((TYPE*)0)->FIELD, FTYPE))
+
 #endif
index e47ca6f0c4776201b8821b6e437b0b61f4720213..2af3a00aa1244702940005b1e0461e46021d9267 100644 (file)
@@ -741,12 +741,12 @@ array_expand (void *array, int n_elts, size_t elt_size)
 
     if (n_elts <= 0)
         return NULL;
-    if (n_elts > SIZE_MAX / elt_size)
+    if ((unsigned int) n_elts > SIZE_MAX / elt_size)
         return NULL;
     new_size = n_elts * elt_size;
     if (new_size == 0)
         return NULL;
-    if (new_size / elt_size != n_elts)
+    if (new_size / elt_size != (unsigned int) n_elts)
         return NULL;
     new_array = realloc(array, new_size);
     return new_array;
@@ -1374,7 +1374,8 @@ asn1_error_code asn1_decode_algorithm_identifier(asn1buf *buf,  krb5_algorithm_i
         val->parameters.length = 0;
         val->parameters.data = NULL;
 
-        if (length > subbuf.next - subbuf.base) {
+        assert(subbuf.next >= subbuf.base);
+        if (length > (size_t)(subbuf.next - subbuf.base)) {
             unsigned int size = length - (subbuf.next - subbuf.base);
             retval = asn1buf_remove_octetstring(&subbuf, size,
                                                 &val->parameters.data);
index e2577d8f732eb6473a810d2aa6dae710960f22d5..a94ac3c4e8c1f8967ffff932d2d1791f4fa5ce40 100644 (file)
 #include "asn1_encode.h"
 #include <assert.h>
 
-/**** asn1 macros ****/
-#if 0
-   How to write an asn1 encoder function using these macros:
-
-   asn1_error_code asn1_encode_krb5_substructure(asn1buf *buf,
-                                                 const krb5_type *val,
-                                                 int *retlen)
-   {
-     asn1_setup();
-
-     asn1_addfield(val->last_field, n, asn1_type);
-     asn1_addfield(rep->next_to_last_field, n-1, asn1_type);
-     ...
-
-     /* for OPTIONAL fields */
-     if (rep->field_i == should_not_be_omitted)
-       asn1_addfield(rep->field_i, i, asn1_type);
-
-     /* for string fields (these encoders take an additional argument,
-        the length of the string) */
-     addlenfield(rep->field_length, rep->field, i-1, asn1_type);
-
-     /* if you really have to do things yourself... */
-     retval = asn1_encode_asn1_type(buf,rep->field,&length);
-     if (retval) return retval;
-     sum += length;
-     retval = asn1_make_etag(buf, CONTEXT_SPECIFIC, tag_number, length,
-                             &length);
-     if (retval) return retval;
-     sum += length;
-
-     ...
-     asn1_addfield(rep->second_field, 1, asn1_type);
-     asn1_addfield(rep->first_field, 0, asn1_type);
-     asn1_makeseq();
+/* helper macros
 
-     asn1_cleanup();
-   }
-#endif
+   These are mostly only needed for PKINIT, but there are three
+   basic-krb5 encoders not converted yet.  */
 
 /* setup() -- create and initialize bookkeeping variables
      retval: stores error codes returned from subroutines
      sum: cumulative length of the entire encoding */
 #define asn1_setup()\
   asn1_error_code retval;\
-  unsigned int length, sum=0
-
-/* asn1_addfield -- add a field, or component, to the encoding */
-#define asn1_addfield(value,tag,encoder)\
-{ retval = encoder(buf,value,&length);\
-  if (retval) {\
-    asn1buf_destroy(&buf);\
-    return retval; }\
-  sum += length;\
-  retval = asn1_make_etag(buf,CONTEXT_SPECIFIC,tag,length,&length);\
-  if (retval) {\
-    asn1buf_destroy(&buf);\
-    return retval; }\
-  sum += length; }
-
-/* asn1_addlenfield -- add a field whose length must be separately specified */
-#define asn1_addlenfield(len,value,tag,encoder)\
-{ retval = encoder(buf,len,value,&length);\
-  if (retval) {\
-    asn1buf_destroy(&buf);\
-    return retval; }\
-  sum += length;\
-  retval = asn1_make_etag(buf,CONTEXT_SPECIFIC,tag,length,&length);\
-  if (retval) {\
-    asn1buf_destroy(&buf);\
-    return retval; }\
-  sum += length; }
-
-/* asn1_addfield_implicit -- add an implicitly tagged field, or component, to the encoding */
-#define asn1_addfield_implicit(value,tag,encoder)\
-{ retval = encoder(buf,value,&length);\
-  if (retval) {\
-    asn1buf_destroy(&buf);\
-    return retval; }\
-  sum += length;\
-  retval = asn1_make_tag(buf,CONTEXT_SPECIFIC,PRIMITIVE,tag,length,&length); \
-  if (retval) {\
-    asn1buf_destroy(&buf);\
-    return retval; }\
-  sum += length; }
-
-/* asn1_insert_implicit_octetstring -- add an octet string with implicit tagging */
-#define asn1_insert_implicit_octetstring(len,value,tag)\
-{ retval = asn1buf_insert_octetstring(buf,len,value);\
-  if (retval) {\
-    asn1buf_destroy(&buf);\
-    return retval; }\
-  sum += len;\
-  retval = asn1_make_tag(buf,CONTEXT_SPECIFIC,PRIMITIVE,tag,len,&length); \
-  if (retval) {\
-    asn1buf_destroy(&buf);\
-    return retval; }\
-  sum += length; }
-
-/* asn1_insert_implicit_bitstring -- add a bitstring with implicit tagging */
-#define asn1_insert_implicit_bitstring(len,value,tag)\
-{ retval = asn1buf_insert_octetstring(buf,len,value);\
-  if (retval) {\
-    asn1buf_destroy(&buf);\
-    return retval; }\
-  sum += len;\
-  retval = asn1buf_insert_octet(buf, 0);\
-  if (retval) {\
-    asn1buf_destroy(&buf);\
-    return retval; }\
-  sum++;\
-  retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,tag,len+1,&length); \
-  if (retval) {\
-    asn1buf_destroy(&buf);\
-    return retval; }\
-  sum += length; }
+  unsigned int sum=0
 
 /* form a sequence (by adding a sequence header to the current encoding) */
 #define asn1_makeseq()\
+{ unsigned int length;\
   retval = asn1_make_sequence(buf,sum,&length);\
   if (retval) {\
-    asn1buf_destroy(&buf);\
-    return retval; }\
-  sum += length
-
-/* add an APPLICATION class tag to the current encoding */
-#define asn1_apptag(num)\
-  retval = asn1_make_etag(buf,APPLICATION,num,sum,&length);\
-  if (retval) {\
-    asn1buf_destroy(&buf);\
     return retval; }\
-  sum += length
+  sum += length; }
 
 /* produce the final output and clean up the workspace */
 #define asn1_cleanup()\
   *retlen = sum;\
   return 0
 
-asn1_error_code asn1_encode_ui_4(asn1buf *buf, const krb5_ui_4 val, unsigned int *retlen)
-{
-    return asn1_encode_unsigned_integer(buf,val,retlen);
-}
-
-
-asn1_error_code asn1_encode_realm(asn1buf *buf, const krb5_principal val, unsigned int *retlen)
-{
-    if (val == NULL ||
-        (val->realm.length && val->realm.data == NULL))
-        return ASN1_MISSING_FIELD;
-    return asn1_encode_generalstring(buf,val->realm.length,val->realm.data,
-                                     retlen);
-}
+/* asn1_addfield -- add a field, or component, to the encoding */
+#define asn1_addfield(value,tag,encoder)\
+{ unsigned int length; \
+  retval = encoder(buf,value,&length);  \
+  if (retval) {\
+    return retval; }\
+  sum += length;\
+  retval = asn1_make_etag(buf,CONTEXT_SPECIFIC,tag,length,&length);\
+  if (retval) {\
+    return retval; }\
+  sum += length; }
 
-asn1_error_code asn1_encode_principal_name(asn1buf *buf, const krb5_principal val, unsigned int *retlen)
-{
-    asn1_setup();
-    int n;
-
-    if (val == NULL || val->data == NULL) return ASN1_MISSING_FIELD;
-
-    for (n = (int) ((val->length)-1); n >= 0; n--) {
-        if (val->data[n].length &&
-            val->data[n].data == NULL)
-            return ASN1_MISSING_FIELD;
-        retval = asn1_encode_generalstring(buf,
-                                           (val->data)[n].length,
-                                           (val->data)[n].data,
-                                           &length);
-        if (retval) return retval;
-        sum += length;
-    }
-    asn1_makeseq();
-    retval = asn1_make_etag(buf,CONTEXT_SPECIFIC,1,sum,&length);
-    if (retval) return retval;
-    sum += length;
+DEFINTTYPE(int32, krb5_int32);
+DEFPTRTYPE(int32_ptr, int32);
 
-    asn1_addfield(val->type,0,asn1_encode_integer);
+DEFUINTTYPE(uint, unsigned int);
+DEFUINTTYPE(octet, krb5_octet);
+DEFUINTTYPE(ui_4, krb5_ui_4);
 
-    asn1_makeseq();
+DEFFNLENTYPE(octetstring, unsigned char *, asn1_encode_octetstring);
+DEFFNLENTYPE(s_octetstring, char *, asn1_encode_octetstring);
+DEFFNLENTYPE(charstring, char *, asn1_encode_charstring);
+DEFFNLENTYPE(generalstring, char *, asn1_encode_generalstring);
+DEFFNLENTYPE(u_generalstring, unsigned char *, asn1_encode_generalstring);
+DEFFNLENTYPE(opaque, char *, asn1_encode_opaque);
 
-    asn1_cleanup();
-}
+DEFFIELDTYPE(gstring_data, krb5_data, 
+             FIELDOF_STRING(krb5_data, generalstring, data, length, -1));
+DEFPTRTYPE(gstring_data_ptr,gstring_data);
 
-asn1_error_code asn1_encode_kerberos_time(asn1buf *buf, const krb5_timestamp val, unsigned int *retlen)
-{
-    return asn1_encode_generaltime(buf,val,retlen);
-}
+DEFFIELDTYPE(ostring_data, krb5_data,
+             FIELDOF_STRING(krb5_data, s_octetstring, data, length, -1));
+DEFPTRTYPE(ostring_data_ptr,ostring_data);
 
-asn1_error_code asn1_encode_host_address(asn1buf *buf, const krb5_address *val, unsigned int *retlen)
-{
-    asn1_setup();
+DEFFIELDTYPE(opaque_data, krb5_data,
+             FIELDOF_STRING(krb5_data, opaque, data, length, -1));
 
-    if (val == NULL || val->contents == NULL) return ASN1_MISSING_FIELD;
+DEFFIELDTYPE(realm_of_principal_data, krb5_principal_data,
+             FIELDOF_NORM(krb5_principal_data, gstring_data, realm, -1));
+DEFPTRTYPE(realm_of_principal, realm_of_principal_data);
 
-    asn1_addlenfield(val->length,val->contents,1,asn1_encode_octetstring);
-    asn1_addfield(val->addrtype,0,asn1_encode_integer);
-    asn1_makeseq();
 
-    asn1_cleanup();
-}
+static const struct field_info princname_fields[] = {
+    FIELDOF_NORM(krb5_principal_data, int32, type, 0),
+    FIELDOF_SEQOF_INT32(krb5_principal_data, gstring_data_ptr, data, length, 1),
+};
+/* krb5_principal is a typedef for krb5_principal_data*, so this is
+   effectively "encode_principal_data_at" with an address arg.  */
+DEFSEQTYPE(principal_data, krb5_principal_data, princname_fields, 0);
+DEFPTRTYPE(principal, principal_data);
 
-asn1_error_code asn1_encode_host_addresses(asn1buf *buf, const krb5_address **val, unsigned int *retlen)
+static asn1_error_code
+asn1_encode_kerberos_time_at(asn1buf *buf, const krb5_timestamp *val,
+                             unsigned int *retlen)
 {
-    asn1_setup();
-    int i;
-
-    if (val == NULL || val[0] == NULL) return ASN1_MISSING_FIELD;
-
-    for (i=0; val[i] != NULL; i++); /* go to end of array */
-    for (i--; i>=0; i--) {
-        retval = asn1_encode_host_address(buf,val[i],&length);
-        if (retval) return retval;
-        sum += length;
-    }
-    asn1_makeseq();
-
-    asn1_cleanup();
+    /* Range checking for time_t vs krb5_timestamp?  */
+    time_t tval = *val;
+    return asn1_encode_generaltime(buf, tval, retlen);
 }
+DEFFNXTYPE(kerberos_time, krb5_timestamp, asn1_encode_kerberos_time_at);
 
-asn1_error_code asn1_encode_encrypted_data(asn1buf *buf, const krb5_enc_data *val, unsigned int *retlen)
-{
-    asn1_setup();
-
-    if (val == NULL ||
-        (val->ciphertext.length && val->ciphertext.data == NULL))
-        return ASN1_MISSING_FIELD;
-
-    asn1_addlenfield(val->ciphertext.length,val->ciphertext.data,2,asn1_encode_charstring);
-    /* krb5_kvno should be int */
-    if (val->kvno)
-        asn1_addfield((int) val->kvno,1,asn1_encode_integer);
-    asn1_addfield(val->enctype,0,asn1_encode_integer);
+const static struct field_info address_fields[] = {
+    FIELDOF_NORM(krb5_address, int32, addrtype, 0),
+    FIELDOF_STRING(krb5_address, octetstring, contents, length, 1),
+};
+DEFSEQTYPE(address, krb5_address, address_fields, 0);
+DEFPTRTYPE(address_ptr, address);
 
-    asn1_makeseq();
-
-    asn1_cleanup();
-}
+DEFNULLTERMSEQOFTYPE(seq_of_host_addresses, address_ptr);
+DEFPTRTYPE(ptr_seqof_host_addresses, seq_of_host_addresses);
 
-asn1_error_code asn1_encode_krb5_flags(asn1buf *buf, const krb5_flags val, unsigned int *retlen)
+static unsigned int
+optional_encrypted_data (const void *vptr)
 {
-    asn1_setup();
-    krb5_flags valcopy = val;
-    int i;
-
-    for (i=0; i<4; i++) {
-        retval = asn1buf_insert_octet(buf,(asn1_octet) (valcopy&0xFF));
-        if (retval) return retval;
-        valcopy >>= 8;
-    }
-    retval = asn1buf_insert_octet(buf,0);       /* 0 padding bits */
-    if (retval) return retval;
-    sum = 5;
-
-    retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_BITSTRING,sum,
-                           &length);
-    if (retval) return retval;
-    sum += length;
+    const krb5_enc_data *val = vptr;
+    unsigned int optional = 0;
 
-    *retlen = sum;
-    return 0;
-}
+    if (val->kvno != 0)
+        optional |= (1u << 1);
 
-asn1_error_code asn1_encode_ap_options(asn1buf *buf, const krb5_flags val, unsigned int *retlen)
-{
-    return asn1_encode_krb5_flags(buf,val,retlen);
+    return optional;
 }
 
-asn1_error_code asn1_encode_ticket_flags(asn1buf *buf, const krb5_flags val, unsigned int *retlen)
-{
-    return asn1_encode_krb5_flags(buf,val,retlen);
-}
-
-asn1_error_code asn1_encode_kdc_options(asn1buf *buf, const krb5_flags val, unsigned int *retlen)
-{
-    return asn1_encode_krb5_flags(buf,val,retlen);
-}
+static const struct field_info encrypted_data_fields[] = {
+    FIELDOF_NORM(krb5_enc_data, int32, enctype, 0),
+    FIELDOF_OPT(krb5_enc_data, uint, kvno, 1, 1),
+    FIELDOF_NORM(krb5_enc_data, ostring_data, ciphertext, 2),
+};
+DEFSEQTYPE(encrypted_data, krb5_enc_data, encrypted_data_fields,
+           optional_encrypted_data);
 
-asn1_error_code asn1_encode_authorization_data(asn1buf *buf, const krb5_authdata **val, unsigned int *retlen)
+/* 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.  */
+static asn1_error_code
+asn1_encode_krb5_flags_at(asn1buf *buf, const krb5_flags *val,
+                          unsigned int *retlen)
 {
-    asn1_setup();
-    int i;
-
-    if (val == NULL || val[0] == NULL) return ASN1_MISSING_FIELD;
-
-    for (i=0; val[i] != NULL; i++); /* get to the end of the array */
-    for (i--; i>=0; i--) {
-        retval = asn1_encode_krb5_authdata_elt(buf,val[i],&length);
-        if (retval) return retval;
-        sum += length;
-    }
-    asn1_makeseq();
-
-    asn1_cleanup();
+    unsigned char cbuf[4];
+    store_32_be(*val, cbuf);
+    return asn1_encode_bitstring(buf, 4, cbuf, retlen);
 }
+DEFFNXTYPE(krb5_flags, krb5_flags, asn1_encode_krb5_flags_at);
 
-asn1_error_code asn1_encode_krb5_authdata_elt(asn1buf *buf, const krb5_authdata *val, unsigned int *retlen)
-{
-    asn1_setup();
-
-    if (val == NULL ||
-        (val->length && val->contents == NULL))
-        return ASN1_MISSING_FIELD;
-
-    /* ad-data[1]               OCTET STRING */
-    asn1_addlenfield(val->length,val->contents,1,asn1_encode_octetstring);
+const static struct field_info authdata_elt_fields[] = {
     /* ad-type[0]               INTEGER */
-    asn1_addfield(val->ad_type,0,asn1_encode_integer);
-    /* SEQUENCE */
-    asn1_makeseq();
-
-    asn1_cleanup();
-}
-
-asn1_error_code asn1_encode_kdc_rep(int msg_type, asn1buf *buf, const krb5_kdc_rep *val, unsigned int *retlen)
-{
-    asn1_setup();
-
-    if (val == NULL) return ASN1_MISSING_FIELD;
-
-    asn1_addfield(&(val->enc_part),6,asn1_encode_encrypted_data);
-    asn1_addfield(val->ticket,5,asn1_encode_ticket);
-    asn1_addfield(val->client,4,asn1_encode_principal_name);
-    asn1_addfield(val->client,3,asn1_encode_realm);
-    if (val->padata != NULL && val->padata[0] != NULL)
-        asn1_addfield((const krb5_pa_data**)val->padata,2,asn1_encode_sequence_of_pa_data);
-    if (msg_type != KRB5_AS_REP && msg_type != KRB5_TGS_REP)
-        return KRB5_BADMSGTYPE;
-    asn1_addfield(msg_type,1,asn1_encode_integer);
-    asn1_addfield(KVNO,0,asn1_encode_integer);
-    asn1_makeseq();
-
-    asn1_cleanup();
-}
-
-asn1_error_code asn1_encode_enc_kdc_rep_part(asn1buf *buf, const krb5_enc_kdc_rep_part *val, unsigned int *retlen)
-{
-    asn1_setup();
-
-    if (val == NULL) return ASN1_MISSING_FIELD;
-
-    /* caddr[11]                HostAddresses OPTIONAL */
-    if (val->caddrs != NULL && val->caddrs[0] != NULL)
-        asn1_addfield((const krb5_address**)(val->caddrs),11,asn1_encode_host_addresses);
-
-    /* sname[10]                PrincipalName */
-    asn1_addfield(val->server,10,asn1_encode_principal_name);
-
-    /* srealm[9]                Realm */
-    asn1_addfield(val->server,9,asn1_encode_realm);
-
-    /* renew-till[8]    KerberosTime OPTIONAL */
-    if (val->flags & TKT_FLG_RENEWABLE)
-        asn1_addfield(val->times.renew_till,8,asn1_encode_kerberos_time);
-
-    /* endtime[7]               KerberosTime */
-    asn1_addfield(val->times.endtime,7,asn1_encode_kerberos_time);
-
-    /* starttime[6]     KerberosTime OPTIONAL */
-    if (val->times.starttime)
-        asn1_addfield(val->times.starttime,6,asn1_encode_kerberos_time);
-
-    /* authtime[5]      KerberosTime */
-    asn1_addfield(val->times.authtime,5,asn1_encode_kerberos_time);
-
-    /* flags[4]         TicketFlags */
-    asn1_addfield(val->flags,4,asn1_encode_ticket_flags);
-
-    /* key-expiration[3]        KerberosTime OPTIONAL */
-    if (val->key_exp)
-        asn1_addfield(val->key_exp,3,asn1_encode_kerberos_time);
-
-    /* nonce[2]         INTEGER */
-    asn1_addfield(val->nonce,2,asn1_encode_integer);
-
-    /* last-req[1]      LastReq */
-    asn1_addfield((const krb5_last_req_entry**)val->last_req,1,asn1_encode_last_req);
-
+    FIELDOF_NORM(krb5_authdata, int32, ad_type, 0),
+    /* ad-data[1]               OCTET STRING */
+    FIELDOF_STRING(krb5_authdata, octetstring, contents, length, 1),
+};
+DEFSEQTYPE(authdata_elt, krb5_authdata, authdata_elt_fields, 0);
+DEFPTRTYPE(authdata_elt_ptr, authdata_elt);
+DEFNONEMPTYNULLTERMSEQOFTYPE(auth_data, authdata_elt_ptr);
+DEFPTRTYPE(auth_data_ptr, auth_data);
+
+static const struct field_info encryption_key_fields[] = {
+    FIELDOF_NORM(krb5_keyblock, int32, enctype, 0),
+    FIELDOF_STRING(krb5_keyblock, octetstring, contents, length, 1),
+};
+DEFSEQTYPE(encryption_key, krb5_keyblock, encryption_key_fields, 0);
+DEFPTRTYPE(ptr_encryption_key, encryption_key);
+
+static const struct field_info checksum_fields[] = {
+    FIELDOF_NORM(krb5_checksum, int32, checksum_type, 0),
+    FIELDOF_STRING(krb5_checksum, octetstring, contents, length, 1),
+};
+DEFSEQTYPE(checksum, krb5_checksum, checksum_fields, 0);
+DEFPTRTYPE(checksum_ptr, checksum);
+DEFNULLTERMSEQOFTYPE(seq_of_checksum, checksum_ptr);
+DEFPTRTYPE(ptr_seqof_checksum, seq_of_checksum);
+
+static const struct field_info lr_fields[] = {
+    FIELDOF_NORM(krb5_last_req_entry, int32, lr_type, 0),
+    FIELDOF_NORM(krb5_last_req_entry, kerberos_time, value, 1),
+};
+DEFSEQTYPE(last_req_ent, krb5_last_req_entry, lr_fields, 0);
+
+DEFPTRTYPE(last_req_ent_ptr, last_req_ent);
+DEFNONEMPTYNULLTERMSEQOFTYPE(last_req, last_req_ent_ptr);
+DEFPTRTYPE(last_req_ptr, last_req);
+
+static const struct field_info ticket_fields[] = {
+    FIELD_INT_IMM(KVNO, 0),
+    FIELDOF_NORM(krb5_ticket, realm_of_principal, server, 1),
+    FIELDOF_NORM(krb5_ticket, principal, server, 2),
+    FIELDOF_NORM(krb5_ticket, encrypted_data, enc_part, 3),
+};
+DEFSEQTYPE(untagged_ticket, krb5_ticket, ticket_fields, 0);
+DEFAPPTAGGEDTYPE(ticket, 1, untagged_ticket);
+
+DEFPTRTYPE(ticket_ptr, ticket);
+DEFNONEMPTYNULLTERMSEQOFTYPE(seq_of_ticket,ticket_ptr);
+DEFPTRTYPE(ptr_seqof_ticket, seq_of_ticket);
+
+/* EncKDCRepPart ::= SEQUENCE */
+static const struct field_info enc_kdc_rep_part_fields[] = {
     /* key[0]           EncryptionKey */
-    asn1_addfield(val->session,0,asn1_encode_encryption_key);
-
-    /* EncKDCRepPart ::= SEQUENCE */
-    asn1_makeseq();
-
-    asn1_cleanup();
-}
-
-asn1_error_code asn1_encode_sequence_of_checksum(asn1buf *buf, const krb5_checksum ** val, unsigned int *retlen)
-{
-    asn1_setup();
-    int i;
-
-    if (val == NULL) return ASN1_MISSING_FIELD;
-
-    for (i=0; val[i] != NULL; i++);
-    for (i--; i>=0; i--) {
-        retval = asn1_encode_checksum(buf,val[i],&length);
-        if (retval) return retval;
-        sum += length;
-    }
-    asn1_makeseq();
-
-    asn1_cleanup();
-}
-
-asn1_error_code asn1_encode_kdc_req_body(asn1buf *buf, const krb5_kdc_req *rep, unsigned int *retlen)
+    FIELDOF_NORM(krb5_enc_kdc_rep_part, ptr_encryption_key, session, 0),
+    /* last-req[1]      LastReq */
+    FIELDOF_NORM(krb5_enc_kdc_rep_part, last_req_ptr, last_req, 1),
+    /* nonce[2]         INTEGER */
+    FIELDOF_NORM(krb5_enc_kdc_rep_part, int32, nonce, 2),
+    /* key-expiration[3]        KerberosTime OPTIONAL */
+    FIELDOF_OPT(krb5_enc_kdc_rep_part, kerberos_time, key_exp, 3, 3),
+    /* flags[4]         TicketFlags */
+    FIELDOF_NORM(krb5_enc_kdc_rep_part, krb5_flags, flags, 4),
+    /* authtime[5]      KerberosTime */
+    FIELDOF_NORM(krb5_enc_kdc_rep_part, kerberos_time, times.authtime, 5),
+    /* starttime[6]     KerberosTime OPTIONAL */
+    FIELDOF_OPT(krb5_enc_kdc_rep_part, kerberos_time, times.starttime, 6, 6),
+    /* endtime[7]               KerberosTime */
+    FIELDOF_NORM(krb5_enc_kdc_rep_part, kerberos_time, times.endtime, 7),
+    /* renew-till[8]    KerberosTime OPTIONAL */
+    FIELDOF_OPT(krb5_enc_kdc_rep_part, kerberos_time, times.renew_till, 8, 8),
+    /* srealm[9]                Realm */
+    FIELDOF_NORM(krb5_enc_kdc_rep_part, realm_of_principal, server, 9),
+    /* sname[10]                PrincipalName */
+    FIELDOF_NORM(krb5_enc_kdc_rep_part, principal, server, 10),
+    /* caddr[11]                HostAddresses OPTIONAL */
+    FIELDOF_OPT(krb5_enc_kdc_rep_part, ptr_seqof_host_addresses, caddrs,
+                11, 11),
+};
+static unsigned int optional_enc_kdc_rep_part(const void *p)
 {
-    asn1_setup();
-
-    if (rep == NULL) return ASN1_MISSING_FIELD;
-
-    /* additional-tickets[11]   SEQUENCE OF Ticket OPTIONAL */
-    if (rep->second_ticket != NULL && rep->second_ticket[0] != NULL)
-        asn1_addfield((const krb5_ticket**)rep->second_ticket,
-                      11,asn1_encode_sequence_of_ticket);
-
-    /* enc-authorization-data[10]       EncryptedData OPTIONAL, */
-    /*                          -- Encrypted AuthorizationData encoding */
-    if (rep->authorization_data.ciphertext.data != NULL)
-        asn1_addfield(&(rep->authorization_data),10,asn1_encode_encrypted_data);
-
-    /* addresses[9]             HostAddresses OPTIONAL, */
-    if (rep->addresses != NULL && rep->addresses[0] != NULL)
-        asn1_addfield((const krb5_address**)rep->addresses,9,asn1_encode_host_addresses);
-
-    /* etype[8]                 SEQUENCE OF INTEGER, -- EncryptionType, */
-    /*                          -- in preference order */
-    asn1_addlenfield(rep->nktypes,rep->ktype,8,asn1_encode_sequence_of_enctype);
-
-    /* nonce[7]                 INTEGER, */
-    asn1_addfield(rep->nonce,7,asn1_encode_integer);
-
-    /* rtime[6]                 KerberosTime OPTIONAL, */
-    if (rep->rtime)
-        asn1_addfield(rep->rtime,6,asn1_encode_kerberos_time);
-
-    /* till[5]                  KerberosTime, */
-    asn1_addfield(rep->till,5,asn1_encode_kerberos_time);
+    const krb5_enc_kdc_rep_part *val = p;
+    unsigned int optional = 0;
 
-    /* from[4]                  KerberosTime OPTIONAL, */
-    if (rep->from)
-        asn1_addfield(rep->from,4,asn1_encode_kerberos_time);
-
-    /* sname[3]                 PrincipalName OPTIONAL, */
-    if (rep->server != NULL)
-        asn1_addfield(rep->server,3,asn1_encode_principal_name);
-
-    /* realm[2]                 Realm, -- Server's realm */
-    /*                          -- Also client's in AS-REQ */
-    if (rep->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) {
-        if (rep->second_ticket != NULL && rep->second_ticket[0] != NULL) {
-            asn1_addfield(rep->second_ticket[0]->server,2,asn1_encode_realm)
-                } else return ASN1_MISSING_FIELD;
-    } else if (rep->server != NULL) {
-        asn1_addfield(rep->server,2,asn1_encode_realm);
+    if (val->key_exp)
+        optional |= (1u << 3);
+    if (val->times.starttime)
+        optional |= (1u << 6);
+    if (val->flags & TKT_FLG_RENEWABLE)
+        optional |= (1u << 8);
+    if (val->caddrs != NULL && val->caddrs[0] != NULL)
+        optional |= (1u << 11);
+
+    return optional;
+}
+DEFSEQTYPE(enc_kdc_rep_part, krb5_enc_kdc_rep_part, enc_kdc_rep_part_fields,
+           optional_enc_kdc_rep_part);
+
+/* Yuck!  Eventually push this *up* above the encoder API and make the
+   rest of the library put the realm name in one consistent place.  At
+   the same time, might as well add the msg-type field and encode both
+   AS-REQ and TGS-REQ through the same descriptor.  */
+struct kdc_req_hack {
+    krb5_kdc_req v;
+    krb5_data *server_realm;
+};
+static const struct field_info kdc_req_hack_fields[] = {
+    FIELDOF_NORM(struct kdc_req_hack, krb5_flags, v.kdc_options, 0),
+    FIELDOF_OPT(struct kdc_req_hack, principal, v.client, 1, 1),
+    FIELDOF_NORM(struct kdc_req_hack, gstring_data_ptr, server_realm, 2),
+    FIELDOF_OPT(struct kdc_req_hack, principal, v.server, 3, 3),
+    FIELDOF_OPT(struct kdc_req_hack, kerberos_time, v.from, 4, 4),
+    FIELDOF_NORM(struct kdc_req_hack, kerberos_time, v.till, 5),
+    FIELDOF_OPT(struct kdc_req_hack, kerberos_time, v.rtime, 6, 6),
+    FIELDOF_NORM(struct kdc_req_hack, int32, v.nonce, 7),
+    FIELDOF_SEQOF_INT32(struct kdc_req_hack, int32_ptr, v.ktype, v.nktypes, 8),
+    FIELDOF_OPT(struct kdc_req_hack, ptr_seqof_host_addresses, v.addresses, 9, 9),
+    FIELDOF_OPT(struct kdc_req_hack, encrypted_data, v.authorization_data, 10, 10),
+    FIELDOF_OPT(struct kdc_req_hack, ptr_seqof_ticket, v.second_ticket, 11, 11),
+};
+static unsigned int optional_kdc_req_hack(const void *p)
+{
+    const struct kdc_req_hack *val2 = p;
+    const krb5_kdc_req *val = &val2->v;
+    unsigned int optional = 0;
+
+    if (val->second_ticket != NULL && val->second_ticket[0] != NULL)
+        optional |= (1u << 11);
+    if (val->authorization_data.ciphertext.data != NULL)
+        optional |= (1u << 10);
+    if (val->addresses != NULL && val->addresses[0] != NULL)
+        optional |= (1u << 9);
+    if (val->rtime)
+        optional |= (1u << 6);
+    if (val->from)
+        optional |= (1u << 4);
+    if (val->server != NULL)
+        optional |= (1u << 3);
+    if (val->client != NULL)
+        optional |= (1u << 1);
+
+    return optional;
+}
+DEFSEQTYPE(kdc_req_body_hack, struct kdc_req_hack, kdc_req_hack_fields,
+           optional_kdc_req_hack);
+static asn1_error_code
+asn1_encode_kdc_req_hack(asn1buf *, const struct kdc_req_hack *,
+                         unsigned int *);
+MAKE_ENCFN(asn1_encode_kdc_req_hack, kdc_req_body_hack);
+static asn1_error_code
+asn1_encode_kdc_req_body(asn1buf *buf, const krb5_kdc_req *val,
+                         unsigned int *retlen)
+{
+    struct kdc_req_hack val2;
+    val2.v = *val;
+    if (val->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) {
+        if (val->second_ticket != NULL && val->second_ticket[0] != NULL) {
+            val2.server_realm = &val->second_ticket[0]->server->realm;
+        } else return ASN1_MISSING_FIELD;
+    } else if (val->server != NULL) {
+        val2.server_realm = &val->server->realm;
     } else return ASN1_MISSING_FIELD;
-
-    /* cname[1]                 PrincipalName OPTIONAL, */
-    /*                          -- Used only in AS-REQ */
-    if (rep->client != NULL)
-        asn1_addfield(rep->client,1,asn1_encode_principal_name);
-
-    /* kdc-options[0]           KDCOptions, */
-    asn1_addfield(rep->kdc_options,0,asn1_encode_kdc_options);
-
-    /* KDC-REQ-BODY ::= SEQUENCE */
-    asn1_makeseq();
-
-    asn1_cleanup();
-}
-
-asn1_error_code asn1_encode_encryption_key(asn1buf *buf, const krb5_keyblock *val, unsigned int *retlen)
-{
-    asn1_setup();
-
-    if (val == NULL ||
-        (val->length && val->contents == NULL))
-        return ASN1_MISSING_FIELD;
-
-    asn1_addlenfield(val->length,val->contents,1,asn1_encode_octetstring);
-    asn1_addfield(val->enctype,0,asn1_encode_integer);
-    asn1_makeseq();
-
-    asn1_cleanup();
-}
-
-asn1_error_code asn1_encode_checksum(asn1buf *buf, const krb5_checksum *val, unsigned int *retlen)
-{
-    asn1_setup();
-
-    if (val == NULL ||
-        (val->length && val->contents == NULL))
-        return ASN1_MISSING_FIELD;
-
-    asn1_addlenfield(val->length,val->contents,1,asn1_encode_octetstring);
-    asn1_addfield(val->checksum_type,0,asn1_encode_integer);
-    asn1_makeseq();
-
-    asn1_cleanup();
-}
-
-asn1_error_code asn1_encode_transited_encoding(asn1buf *buf, const krb5_transited *val, unsigned int *retlen)
-{
-    asn1_setup();
-
-    if (val == NULL ||
-        (val->tr_contents.length != 0 && val->tr_contents.data == NULL))
-        return ASN1_MISSING_FIELD;
-
-    asn1_addlenfield(val->tr_contents.length,val->tr_contents.data,
-                     1,asn1_encode_charstring);
-    asn1_addfield(val->tr_type,0,asn1_encode_integer);
-    asn1_makeseq();
-
-    asn1_cleanup();
-}
-
-asn1_error_code asn1_encode_last_req(asn1buf *buf, const krb5_last_req_entry **val, unsigned int *retlen)
-{
-    asn1_setup();
-    int i;
-
-    if (val == NULL || val[0] == NULL) return ASN1_MISSING_FIELD;
-
-    for (i=0; val[i] != NULL; i++); /* go to end of array */
-    for (i--; i>=0; i--) {
-        retval = asn1_encode_last_req_entry(buf,val[i],&length);
-        if (retval) return retval;
-        sum += length;
-    }
-    asn1_makeseq();
-
-    asn1_cleanup();
-}
-
-asn1_error_code asn1_encode_last_req_entry(asn1buf *buf, const krb5_last_req_entry *val, unsigned int *retlen)
-{
-    asn1_setup();
-
-    if (val == NULL) return ASN1_MISSING_FIELD;
-
-    asn1_addfield(val->value,1,asn1_encode_kerberos_time);
-    asn1_addfield(val->lr_type,0,asn1_encode_integer);
-    asn1_makeseq();
-
-    asn1_cleanup();
-}
-
-asn1_error_code asn1_encode_sequence_of_pa_data(asn1buf *buf, const krb5_pa_data **val, unsigned int *retlen)
-{
-    asn1_setup();
-    int i;
-
-    if (val == NULL) return ASN1_MISSING_FIELD;
-
-    for (i=0; val[i] != NULL; i++);
-    for (i--; i>=0; i--) {
-        retval = asn1_encode_pa_data(buf,val[i],&length);
-        if (retval) return retval;
-        sum += length;
-    }
-    asn1_makeseq();
-
-    asn1_cleanup();
-}
-
-asn1_error_code asn1_encode_pa_data(asn1buf *buf, const krb5_pa_data *val, unsigned int *retlen)
-{
-    asn1_setup();
-
-    if (val == NULL || (val->length != 0 && val->contents == NULL))
-        return ASN1_MISSING_FIELD;
-
-    asn1_addlenfield(val->length,val->contents,2,asn1_encode_octetstring);
-    asn1_addfield(val->pa_type,1,asn1_encode_integer);
-    asn1_makeseq();
-
-    asn1_cleanup();
-}
-
-asn1_error_code asn1_encode_sequence_of_ticket(asn1buf *buf, const krb5_ticket **val, unsigned int *retlen)
-{
-    asn1_setup();
-    int i;
-
-    if (val == NULL || val[0] == NULL) return ASN1_MISSING_FIELD;
-
-    for (i=0; val[i] != NULL; i++);
-    for (i--; i>=0; i--) {
-        retval = asn1_encode_ticket(buf,val[i],&length);
-        if (retval) return retval;
-        sum += length;
-    }
-    asn1_makeseq();
-
-    asn1_cleanup();
-}
-
-asn1_error_code asn1_encode_ticket(asn1buf *buf, const krb5_ticket *val, unsigned int *retlen)
-{
-    asn1_setup();
-
-    if (val == NULL) return ASN1_MISSING_FIELD;
-
-    asn1_addfield(&(val->enc_part),3,asn1_encode_encrypted_data);
-    asn1_addfield(val->server,2,asn1_encode_principal_name);
-    asn1_addfield(val->server,1,asn1_encode_realm);
-    asn1_addfield(KVNO,0,asn1_encode_integer);
-    asn1_makeseq();
-    asn1_apptag(1);
-
-    asn1_cleanup();
-}
-
-asn1_error_code asn1_encode_sequence_of_enctype(asn1buf *buf, const int len, const krb5_enctype *val, unsigned int *retlen)
-{
-    asn1_setup();
-    int i;
-
-    if (val == NULL) return ASN1_MISSING_FIELD;
-
-    for (i=len-1; i>=0; i--) {
-        retval = asn1_encode_integer(buf,val[i],&length);
-        if (retval) return retval;
-        sum += length;
-    }
-    asn1_makeseq();
-
-    asn1_cleanup();
+    return asn1_encode_kdc_req_hack(buf, &val2, retlen);
 }
+DEFFNXTYPE(kdc_req_body, krb5_kdc_req, asn1_encode_kdc_req_body);
+/* end ugly hack */
 
-asn1_error_code asn1_encode_kdc_req(int msg_type, asn1buf *buf, const krb5_kdc_req *val, unsigned int *retlen)
-{
-    asn1_setup();
+static const struct field_info transited_fields[] = {
+    FIELDOF_NORM(krb5_transited, octet, tr_type, 0),
+    FIELDOF_NORM(krb5_transited, ostring_data, tr_contents, 1),
+};
+DEFSEQTYPE(transited, krb5_transited, transited_fields, 0);
 
-    if (val == NULL) return ASN1_MISSING_FIELD;
+static const struct field_info pa_data_fields[] = {
+    FIELDOF_NORM(krb5_pa_data, int32, pa_type, 1),
+    FIELDOF_STRING(krb5_pa_data, octetstring, contents, length, 2),
+};
+DEFSEQTYPE(pa_data, krb5_pa_data, pa_data_fields, 0);
+DEFPTRTYPE(pa_data_ptr, pa_data);
 
-    asn1_addfield(val,4,asn1_encode_kdc_req_body);
-    if (val->padata != NULL && val->padata[0] != NULL)
-        asn1_addfield((const krb5_pa_data**)val->padata,3,asn1_encode_sequence_of_pa_data);
-    if (msg_type != KRB5_AS_REQ && msg_type != KRB5_TGS_REQ)
-        return KRB5_BADMSGTYPE;
-    asn1_addfield(msg_type,2,asn1_encode_integer);
-    asn1_addfield(KVNO,1,asn1_encode_integer);
-    asn1_makeseq();
+DEFNULLTERMSEQOFTYPE(seq_of_pa_data, pa_data_ptr);
+DEFPTRTYPE(ptr_seqof_pa_data, seq_of_pa_data);
 
-    asn1_cleanup();
-}
 
-asn1_error_code asn1_encode_krb_safe_body(asn1buf *buf, const krb5_safe *val, unsigned int *retlen)
+static const struct field_info krb_safe_body_fields[] = {
+    FIELDOF_NORM(krb5_safe, ostring_data, user_data, 0),
+    FIELDOF_OPT(krb5_safe, kerberos_time, timestamp, 1, 1),
+    FIELDOF_OPT(krb5_safe, int32, usec, 2, 2),
+    FIELDOF_OPT(krb5_safe, uint, seq_number, 3, 3),
+    FIELDOF_NORM(krb5_safe, address_ptr, s_address, 4),
+    FIELDOF_OPT(krb5_safe, address_ptr, r_address, 5, 5),
+};
+static unsigned int optional_krb_safe_body(const void *p)
 {
-    asn1_setup();
-
-    if (val == NULL) return ASN1_MISSING_FIELD;
+    const krb5_safe *val = p;
+    unsigned int optional = 0;
 
-    if (val->r_address != NULL)
-        asn1_addfield(val->r_address,5,asn1_encode_host_address);
-    asn1_addfield(val->s_address,4,asn1_encode_host_address);
-    if (val->seq_number)
-        asn1_addfield(val->seq_number,3,asn1_encode_unsigned_integer);
     if (val->timestamp) {
-        asn1_addfield(val->usec,2,asn1_encode_integer);
-        asn1_addfield(val->timestamp,1,asn1_encode_kerberos_time);
-    }
-    if (val->user_data.length && val->user_data.data == NULL)
-        return ASN1_MISSING_FIELD;
-    asn1_addlenfield(val->user_data.length,val->user_data.data,0,asn1_encode_charstring)
-        ;
-
-    asn1_makeseq();
-    asn1_cleanup();
-}
-
-asn1_error_code asn1_encode_sequence_of_krb_cred_info(asn1buf *buf, const krb5_cred_info **val, unsigned int *retlen)
-{
-    asn1_setup();
-    int i;
-
-    if (val == NULL) return ASN1_MISSING_FIELD;
-
-    for (i=0; val[i] != NULL; i++);
-    for (i--; i>=0; i--) {
-        retval = asn1_encode_krb_cred_info(buf,val[i],&length);
-        if (retval) return retval;
-        sum += length;
+        optional |= (1u << 1);
+        optional |= (1u << 2);
     }
-    asn1_makeseq();
-
-    asn1_cleanup();
-}
-
-asn1_error_code asn1_encode_krb_cred_info(asn1buf *buf, const krb5_cred_info *val, unsigned int *retlen)
-{
-    asn1_setup();
-
-    if (val == NULL) return ASN1_MISSING_FIELD;
+    if (val->seq_number)
+        optional |= (1u << 3);
+    if (val->r_address != NULL)
+        optional |= (1u << 5);
+
+    return optional;
+}
+DEFSEQTYPE(krb_safe_body, krb5_safe, krb_safe_body_fields,
+           optional_krb_safe_body);
+
+static const struct field_info krb_cred_info_fields[] = {
+    FIELDOF_NORM(krb5_cred_info, ptr_encryption_key, session, 0),
+    FIELDOF_OPT(krb5_cred_info, realm_of_principal, client, 1, 1),
+    FIELDOF_OPT(krb5_cred_info, principal, client, 2, 2),
+    FIELDOF_OPT(krb5_cred_info, krb5_flags, flags, 3, 3),
+    FIELDOF_OPT(krb5_cred_info, kerberos_time, times.authtime, 4, 4),
+    FIELDOF_OPT(krb5_cred_info, kerberos_time, times.starttime, 5, 5),
+    FIELDOF_OPT(krb5_cred_info, kerberos_time, times.endtime, 6, 6),
+    FIELDOF_OPT(krb5_cred_info, kerberos_time, times.renew_till, 7, 7),
+    FIELDOF_OPT(krb5_cred_info, realm_of_principal, server, 8, 8),
+    FIELDOF_OPT(krb5_cred_info, principal, server, 9, 9),
+    FIELDOF_OPT(krb5_cred_info, ptr_seqof_host_addresses, caddrs, 10, 10),
+};
+static unsigned int optional_krb_cred_info(const void *p)
+{
+    const krb5_cred_info *val = p;
+    unsigned int optional = 0;
 
     if (val->caddrs != NULL && val->caddrs[0] != NULL)
-        asn1_addfield((const krb5_address**)val->caddrs,10,asn1_encode_host_addresses);
+        optional |= (1u << 10);
     if (val->server != NULL) {
-        asn1_addfield(val->server,9,asn1_encode_principal_name);
-        asn1_addfield(val->server,8,asn1_encode_realm);
+        optional |= (1u << 9);
+        optional |= (1u << 8);
     }
     if (val->times.renew_till)
-        asn1_addfield(val->times.renew_till,7,asn1_encode_kerberos_time);
+        optional |= (1u << 7);
     if (val->times.endtime)
-        asn1_addfield(val->times.endtime,6,asn1_encode_kerberos_time);
+        optional |= (1u << 6);
     if (val->times.starttime)
-        asn1_addfield(val->times.starttime,5,asn1_encode_kerberos_time);
+        optional |= (1u << 5);
     if (val->times.authtime)
-        asn1_addfield(val->times.authtime,4,asn1_encode_kerberos_time);
+        optional |= (1u << 4);
     if (val->flags)
-        asn1_addfield(val->flags,3,asn1_encode_ticket_flags);
+        optional |= (1u << 3);
     if (val->client != NULL) {
-        asn1_addfield(val->client,2,asn1_encode_principal_name);
-        asn1_addfield(val->client,1,asn1_encode_realm);
-    }
-    asn1_addfield(val->session,0,asn1_encode_encryption_key);
-
-    asn1_makeseq();
+        optional |= (1u << 2);
+        optional |= (1u << 1);
+    }
 
-    asn1_cleanup();
+    return optional;
 }
+DEFSEQTYPE(cred_info, krb5_cred_info, krb_cred_info_fields,
+           optional_krb_cred_info);
+DEFPTRTYPE(cred_info_ptr, cred_info);
+DEFNULLTERMSEQOFTYPE(seq_of_cred_info, cred_info_ptr);
 
-asn1_error_code asn1_encode_etype_info_entry(asn1buf *buf, const krb5_etype_info_entry *val,
-                                             unsigned int *retlen, int etype_info2)
-{
-    asn1_setup();
+DEFPTRTYPE(ptrseqof_cred_info, seq_of_cred_info);
 
-    assert(val->s2kparams.data == NULL || etype_info2);
-    if (val == NULL || (val->length > 0 && val->length != KRB5_ETYPE_NO_SALT &&
-                        val->salt == NULL))
-        return ASN1_MISSING_FIELD;
-    if (val->s2kparams.data != NULL)
-        asn1_addlenfield(val->s2kparams.length, val->s2kparams.data, 2,
-                         asn1_encode_octetstring);
-    if (val->length >= 0 && val->length != KRB5_ETYPE_NO_SALT) {
-        if (etype_info2) {
-            asn1_addlenfield(val->length,val->salt,1,
-                             asn1_encode_generalstring);
-        } else {
-            asn1_addlenfield(val->length,val->salt,1,
-                             asn1_encode_octetstring);
-        }
-    }
-    asn1_addfield(val->etype,0,asn1_encode_integer);
-    asn1_makeseq();
 
-    asn1_cleanup();
-}
 
-asn1_error_code asn1_encode_etype_info(asn1buf *buf, const krb5_etype_info_entry **val,
-                                       unsigned int *retlen, int etype_info2)
+static unsigned int
+optional_etype_info_entry(const void *vptr)
 {
-    asn1_setup();
-    int i;
+    const krb5_etype_info_entry *val = vptr;
+    unsigned int optional = 0;
 
-    if (val == NULL) return ASN1_MISSING_FIELD;
+    if (val->length >= 0 && val->length != KRB5_ETYPE_NO_SALT)
+        optional |= (1u << 1);
 
-    for (i=0; val[i] != NULL; i++); /* get to the end of the array */
-    for (i--; i>=0; i--) {
-        retval = asn1_encode_etype_info_entry(buf,val[i],&length, etype_info2);
-        if (retval) return retval;
-        sum += length;
-    }
-    asn1_makeseq();
-    asn1_cleanup();
+    return optional;
 }
+static const struct field_info etype_info_entry_fields[] = {
+    FIELDOF_NORM(krb5_etype_info_entry, int32, etype, 0),
+    FIELDOF_OPTSTRING(krb5_etype_info_entry, octetstring, salt, length, 1, 1),
+};
+DEFSEQTYPE(etype_info_entry, krb5_etype_info_entry, etype_info_entry_fields,
+           optional_etype_info_entry);
 
-asn1_error_code asn1_encode_sequence_of_passwdsequence(asn1buf *buf, const passwd_phrase_element **val, unsigned int *retlen)
+static unsigned int
+optional_etype_info2_entry(const void *vptr)
 {
-    asn1_setup();
-    int i;
+    const krb5_etype_info_entry *val = vptr;
+    unsigned int optional = 0;
 
-    if (val == NULL || val[0] == NULL) return ASN1_MISSING_FIELD;
+    if (val->length >= 0 && val->length != KRB5_ETYPE_NO_SALT)
+        optional |= (1u << 1);
+    if (val->s2kparams.data)
+        optional |= (1u << 2);
 
-    for (i=0; val[i] != NULL; i++); /* get to the end of the array */
-    for (i--; i>=0; i--) {
-        retval = asn1_encode_passwdsequence(buf,val[i],&length);
-        if (retval) return retval;
-        sum += length;
-    }
-    asn1_makeseq();
-    asn1_cleanup();
+    return optional;
 }
 
-asn1_error_code asn1_encode_passwdsequence(asn1buf *buf, const passwd_phrase_element *val, unsigned int *retlen)
-{
-    asn1_setup();
-    asn1_addlenfield(val->phrase->length,val->phrase->data,1,asn1_encode_charstring);
-    asn1_addlenfield(val->passwd->length,val->passwd->data,0,asn1_encode_charstring);
-    asn1_makeseq();
-    asn1_cleanup();
-}
+static const struct field_info etype_info2_entry_fields[] = {
+    FIELDOF_NORM(krb5_etype_info_entry, int32, etype, 0),
+    FIELDOF_OPTSTRING(krb5_etype_info_entry, u_generalstring, salt, length,
+                      1, 1),
+    FIELDOF_OPT(krb5_etype_info_entry, ostring_data, s2kparams, 2, 2),
+};
+DEFSEQTYPE(etype_info2_entry, krb5_etype_info_entry, etype_info2_entry_fields,
+           optional_etype_info2_entry);
 
-asn1_error_code asn1_encode_sam_flags(asn1buf *buf, const krb5_flags val, unsigned int *retlen)
-{
-    return asn1_encode_krb5_flags(buf,val,retlen);
-}
+DEFPTRTYPE(etype_info_entry_ptr, etype_info_entry);
+DEFNULLTERMSEQOFTYPE(etype_info, etype_info_entry_ptr);
+
+DEFPTRTYPE(etype_info2_entry_ptr, etype_info2_entry);
+DEFNULLTERMSEQOFTYPE(etype_info2, etype_info2_entry_ptr);
+
+static const struct field_info passwdsequence_fields[] = {
+    FIELDOF_NORM(passwd_phrase_element, ostring_data_ptr, passwd, 0),
+    FIELDOF_NORM(passwd_phrase_element, ostring_data_ptr, phrase, 1),
+};
+DEFSEQTYPE(passwdsequence, passwd_phrase_element, passwdsequence_fields, 0);
 
-#define add_optstring(val,n,fn)                                         \
-    if ((val).length > 0) {asn1_addlenfield((val).length,(val).data,n,fn);}
+DEFPTRTYPE(passwdsequence_ptr, passwdsequence);
+DEFNONEMPTYNULLTERMSEQOFTYPE(seqof_passwdsequence, passwdsequence_ptr);
+DEFPTRTYPE(ptr_seqof_passwdsequence, seqof_passwdsequence);
 
-asn1_error_code asn1_encode_sam_challenge(asn1buf *buf, const krb5_sam_challenge *val, unsigned int *retlen)
+
+static const struct field_info sam_challenge_fields[] = {
+    FIELDOF_NORM(krb5_sam_challenge, int32, sam_type, 0),
+    FIELDOF_NORM(krb5_sam_challenge, krb5_flags, sam_flags, 1),
+    FIELDOF_OPT(krb5_sam_challenge, ostring_data, sam_type_name, 2, 2),
+    FIELDOF_OPT(krb5_sam_challenge, ostring_data, sam_track_id,3, 3),
+    FIELDOF_OPT(krb5_sam_challenge, ostring_data, sam_challenge_label,4, 4),
+    FIELDOF_OPT(krb5_sam_challenge, ostring_data, sam_challenge,5, 5),
+    FIELDOF_OPT(krb5_sam_challenge, ostring_data, sam_response_prompt,6, 6),
+    FIELDOF_OPT(krb5_sam_challenge, ostring_data, sam_pk_for_sad,7, 7),
+    FIELDOF_OPT(krb5_sam_challenge, int32, sam_nonce, 8, 8),
+    FIELDOF_OPT(krb5_sam_challenge, checksum, sam_cksum, 9, 9),
+};
+static unsigned int optional_sam_challenge(const void *p)
 {
-    asn1_setup();
-    /* possibly wrong */
+    const krb5_sam_challenge *val = p;
+    unsigned int optional = 0;
+
     if (val->sam_cksum.length)
-        asn1_addfield(&(val->sam_cksum),9,asn1_encode_checksum);
+        optional |= (1u << 9);
 
     if (val->sam_nonce)
-        asn1_addfield(val->sam_nonce,8,asn1_encode_integer);
-
-    add_optstring(val->sam_pk_for_sad,7,asn1_encode_charstring);
-    add_optstring(val->sam_response_prompt,6,asn1_encode_charstring);
-    add_optstring(val->sam_challenge,5,asn1_encode_charstring);
-    add_optstring(val->sam_challenge_label,4,asn1_encode_charstring);
-    add_optstring(val->sam_track_id,3,asn1_encode_charstring);
-    add_optstring(val->sam_type_name,2,asn1_encode_charstring);
+        optional |= (1u << 8);
 
-    asn1_addfield(val->sam_flags,1,asn1_encode_sam_flags);
-    asn1_addfield(val->sam_type,0,asn1_encode_integer);
+    if (val->sam_pk_for_sad.length > 0) optional |= (1u << 7);
+    if (val->sam_response_prompt.length > 0) optional |= (1u << 6);
+    if (val->sam_challenge.length > 0) optional |= (1u << 5);
+    if (val->sam_challenge_label.length > 0) optional |= (1u << 4);
+    if (val->sam_track_id.length > 0) optional |= (1u << 3);
+    if (val->sam_type_name.length > 0) optional |= (1u << 2);
 
-    asn1_makeseq();
-    asn1_cleanup();
+    return optional;
 }
+DEFSEQTYPE(sam_challenge,krb5_sam_challenge,sam_challenge_fields,
+           optional_sam_challenge);
 
-asn1_error_code asn1_encode_sam_challenge_2(asn1buf *buf, const krb5_sam_challenge_2 *val, unsigned int *retlen)
+#if 0 /* encoders not used! */
+MAKE_ENCFN(asn1_encode_sequence_of_checksum, seq_of_checksum);
+static asn1_error_code
+asn1_encode_sam_challenge_2(asn1buf *buf, const krb5_sam_challenge_2 *val,
+                            unsigned int *retlen)
 {
     asn1_setup();
     if ( (!val) || (!val->sam_cksum) || (!val->sam_cksum[0]))
         return ASN1_MISSING_FIELD;
 
-    asn1_addfield((const krb5_checksum **) val->sam_cksum, 1, asn1_encode_sequence_of_checksum);
-    retval = asn1buf_insert_octetstring(buf, val->sam_challenge_2_body.length,
-                                        (unsigned char *)val->sam_challenge_2_body.data);
-    if (retval) {
-        asn1buf_destroy(&buf);
-        return retval;
-    }
-    sum += val->sam_challenge_2_body.length;
-    retval = asn1_make_etag(buf, CONTEXT_SPECIFIC, 0,
-                            val->sam_challenge_2_body.length, &length);
-    if (retval) {
-        asn1buf_destroy(&buf);
-        return retval;
+    asn1_addfield(val->sam_cksum, 1, asn1_encode_sequence_of_checksum);
+
+    {
+        unsigned int length;
+
+        retval = asn1buf_insert_octetstring(buf, val->sam_challenge_2_body.length,
+                                            (unsigned char *)val->sam_challenge_2_body.data);
+        if (retval) {
+            return retval;
+        }
+        sum += val->sam_challenge_2_body.length;
+        retval = asn1_make_etag(buf, CONTEXT_SPECIFIC, 0,
+                                val->sam_challenge_2_body.length, &length);
+        if (retval) {
+            return retval;
+        }
+        sum += length;
     }
-    sum += length;
 
     asn1_makeseq();
     asn1_cleanup();
 }
+DEFFNXTYPE(sam_challenge_2, krb5_sam_challenge_2, asn1_encode_sam_challenge_2);
+
+static const struct field_info sam_challenge_2_body_fields[] = {
+    FIELDOF_NORM(krb5_sam_challenge_2_body, int32, sam_type, 0),
+    FIELDOF_NORM(krb5_sam_challenge_2_body, krb5_flags, sam_flags, 1),
+    FIELDOF_OPT(krb5_sam_challenge_2_body, ostring_data, sam_type_name, 2, 2),
+    FIELDOF_OPT(krb5_sam_challenge_2_body, ostring_data, sam_track_id,3, 3),
+    FIELDOF_OPT(krb5_sam_challenge_2_body, ostring_data, sam_challenge_label,4, 4),
+    FIELDOF_OPT(krb5_sam_challenge_2_body, ostring_data, sam_challenge,5, 5),
+    FIELDOF_OPT(krb5_sam_challenge_2_body, ostring_data, sam_response_prompt,6, 6),
+    FIELDOF_OPT(krb5_sam_challenge_2_body, ostring_data, sam_pk_for_sad,7, 7),
+    FIELDOF_NORM(krb5_sam_challenge_2_body, int32, sam_nonce, 8),
+    FIELDOF_NORM(krb5_sam_challenge_2_body, int32, sam_etype, 9),
+};
+static unsigned int optional_sam_challenge_2_body(const void *p)
+{
+    const krb5_sam_challenge_2_body *val = p;
+    unsigned int optional = 0;
+
+    if (val->sam_pk_for_sad.length > 0) optional |= (1u << 7);
+    if (val->sam_response_prompt.length > 0) optional |= (1u << 6);
+    if (val->sam_challenge.length > 0) optional |= (1u << 5);
+    if (val->sam_challenge_label.length > 0) optional |= (1u << 4);
+    if (val->sam_track_id.length > 0) optional |= (1u << 3);
+    if (val->sam_type_name.length > 0) optional |= (1u << 2);
+
+    return optional;
+}
+DEFSEQTYPE(sam_challenge_2_body,krb5_sam_challenge_2_body,sam_challenge_2_body_fields,
+           optional_sam_challenge_2_body);
+#endif
 
-asn1_error_code asn1_encode_sam_challenge_2_body(asn1buf *buf, const krb5_sam_challenge_2_body *val, unsigned int *retlen)
+static const struct field_info sam_key_fields[] = {
+    FIELDOF_NORM(krb5_sam_key, encryption_key, sam_key, 0),
+};
+DEFSEQTYPE(sam_key, krb5_sam_key, sam_key_fields, 0);
+
+static const struct field_info enc_sam_response_enc_fields[] = {
+    FIELDOF_NORM(krb5_enc_sam_response_enc, int32, sam_nonce, 0),
+    FIELDOF_NORM(krb5_enc_sam_response_enc, kerberos_time, sam_timestamp, 1),
+    FIELDOF_NORM(krb5_enc_sam_response_enc, int32, sam_usec, 2),
+    FIELDOF_OPT(krb5_enc_sam_response_enc, ostring_data, sam_sad, 3, 3),
+};
+static unsigned int optional_enc_sam_response_enc(const void *p)
 {
-    asn1_setup();
+    const krb5_enc_sam_response_enc *val = p;
+    unsigned int optional = 0;
 
-    asn1_addfield(val->sam_etype, 9, asn1_encode_integer);
-    asn1_addfield(val->sam_nonce,8,asn1_encode_integer);
-    add_optstring(val->sam_pk_for_sad,7,asn1_encode_charstring);
-    add_optstring(val->sam_response_prompt,6,asn1_encode_charstring);
-    add_optstring(val->sam_challenge,5,asn1_encode_charstring);
-    add_optstring(val->sam_challenge_label,4,asn1_encode_charstring);
-    add_optstring(val->sam_track_id,3,asn1_encode_charstring);
-    add_optstring(val->sam_type_name,2,asn1_encode_charstring);
+    if (val->sam_sad.length > 0) optional |= (1u << 3);
 
-    asn1_addfield(val->sam_flags,1,asn1_encode_sam_flags);
-    asn1_addfield(val->sam_type,0,asn1_encode_integer);
+    return optional;
+}
+DEFSEQTYPE(enc_sam_response_enc, krb5_enc_sam_response_enc,
+           enc_sam_response_enc_fields, optional_enc_sam_response_enc);
 
-    asn1_makeseq();
-    asn1_cleanup();
+static const struct field_info enc_sam_response_enc_2_fields[] = {
+    FIELDOF_NORM(krb5_enc_sam_response_enc_2, int32, sam_nonce, 0),
+    FIELDOF_OPT(krb5_enc_sam_response_enc_2, ostring_data, sam_sad, 1, 1),
+};
+static unsigned int optional_enc_sam_response_enc_2(const void *p)
+{
+    const krb5_enc_sam_response_enc_2 *val = p;
+    unsigned int optional = 0;
+
+    if (val->sam_sad.length > 0) optional |= (1u << 1);
+
+    return optional;
 }
+DEFSEQTYPE(enc_sam_response_enc_2, krb5_enc_sam_response_enc_2,
+           enc_sam_response_enc_2_fields, optional_enc_sam_response_enc_2);
 
-asn1_error_code asn1_encode_sam_key(asn1buf *buf, const krb5_sam_key *val, unsigned int *retlen)
+static const struct field_info sam_response_fields[] = {
+    FIELDOF_NORM(krb5_sam_response, int32, sam_type, 0),
+    FIELDOF_NORM(krb5_sam_response, krb5_flags, sam_flags, 1),
+    FIELDOF_OPT(krb5_sam_response, ostring_data, sam_track_id, 2, 2),
+    FIELDOF_OPT(krb5_sam_response, encrypted_data, sam_enc_key, 3, 3),
+    FIELDOF_NORM(krb5_sam_response, encrypted_data, sam_enc_nonce_or_ts, 4),
+    FIELDOF_OPT(krb5_sam_response, int32, sam_nonce, 5, 5),
+    FIELDOF_OPT(krb5_sam_response, kerberos_time, sam_patimestamp, 6, 6),
+};
+static unsigned int optional_sam_response(const void *p)
 {
-    asn1_setup();
-    asn1_addfield(&(val->sam_key),0,asn1_encode_encryption_key);
+    const krb5_sam_response *val = p;
+    unsigned int optional = 0;
 
-    asn1_makeseq();
+    if (val->sam_patimestamp)
+        optional |= (1u << 6);
+    if (val->sam_nonce)
+        optional |= (1u << 5);
+    if (val->sam_enc_key.ciphertext.length)
+        optional |= (1u << 3);
+    if (val->sam_track_id.length > 0) optional |= (1u << 2);
+
+    return optional;
+}
+DEFSEQTYPE(sam_response, krb5_sam_response, sam_response_fields,
+           optional_sam_response);
+
+static const struct field_info sam_response_2_fields[] = {
+    FIELDOF_NORM(krb5_sam_response_2, int32, sam_type, 0),
+    FIELDOF_NORM(krb5_sam_response_2, krb5_flags, sam_flags, 1),
+    FIELDOF_OPT(krb5_sam_response_2, ostring_data, sam_track_id, 2, 2),
+    FIELDOF_NORM(krb5_sam_response_2, encrypted_data, sam_enc_nonce_or_sad, 3),
+    FIELDOF_NORM(krb5_sam_response_2, int32, sam_nonce, 4),
+};
+static unsigned int optional_sam_response_2(const void *p)
+{
+    const krb5_sam_response_2 *val = p;
+    unsigned int optional = 0;
+
+    if (val->sam_track_id.length > 0) optional |= (1u << 2);
+
+    return optional;
+}
+DEFSEQTYPE(sam_response_2, krb5_sam_response_2, sam_response_2_fields,
+           optional_sam_response_2);
+
+static const struct field_info predicted_sam_response_fields[] = {
+    FIELDOF_NORM(krb5_predicted_sam_response, encryption_key, sam_key, 0),
+    FIELDOF_NORM(krb5_predicted_sam_response, krb5_flags, sam_flags, 1),
+    FIELDOF_NORM(krb5_predicted_sam_response, kerberos_time, stime, 2),
+    FIELDOF_NORM(krb5_predicted_sam_response, int32, susec, 3),
+    FIELDOF_NORM(krb5_predicted_sam_response, realm_of_principal, client, 4),
+    FIELDOF_NORM(krb5_predicted_sam_response, principal, client, 5),
+    FIELDOF_OPT(krb5_predicted_sam_response, ostring_data, msd, 6, 6),
+};
+static unsigned int optional_predicted_sam_response(const void *p)
+{
+    const krb5_predicted_sam_response *val = p;
+    unsigned int optional = 0;
+
+    if (val->msd.length > 0) optional |= (1u << 6);
+
+    return optional;
+}
+DEFSEQTYPE(predicted_sam_response, krb5_predicted_sam_response,
+           predicted_sam_response_fields,
+           optional_predicted_sam_response);
+
+static const struct field_info krb5_authenticator_fields[] = {
+    /* Authenticator ::= [APPLICATION 2] SEQUENCE */
+    /* authenticator-vno[0]     INTEGER */
+    FIELD_INT_IMM(KVNO, 0),
+    /* crealm[1]                        Realm */
+    FIELDOF_NORM(krb5_authenticator, realm_of_principal, client, 1),
+    /* cname[2]                 PrincipalName */
+    FIELDOF_NORM(krb5_authenticator, principal, client, 2),
+    /* cksum[3]                 Checksum OPTIONAL */
+    FIELDOF_OPT(krb5_authenticator, checksum_ptr, checksum, 3, 3),
+    /* cusec[4]                 INTEGER */
+    FIELDOF_NORM(krb5_authenticator, int32, cusec, 4),
+    /* ctime[5]                 KerberosTime */
+    FIELDOF_NORM(krb5_authenticator, kerberos_time, ctime, 5),
+    /* subkey[6]                        EncryptionKey OPTIONAL */
+    FIELDOF_OPT(krb5_authenticator, ptr_encryption_key, subkey, 6, 6),
+    /* seq-number[7]            INTEGER OPTIONAL */
+    FIELDOF_OPT(krb5_authenticator, uint, seq_number, 7, 7),
+    /* authorization-data[8]    AuthorizationData OPTIONAL */
+    FIELDOF_OPT(krb5_authenticator, auth_data_ptr, authorization_data, 8, 8),
+};
+static unsigned int optional_krb5_authenticator(const void *p)
+{
+    const krb5_authenticator *val = p;
+    unsigned int optional = 0;
+
+    if (val->authorization_data != NULL && val->authorization_data[0] != NULL)
+        optional |= (1u << 8);
+
+    if (val->seq_number != 0)
+        optional |= (1u << 7);
+
+    if (val->subkey != NULL)
+        optional |= (1u << 6);
+
+    if (val->checksum != NULL)
+        optional |= (1u << 3);
+
+    return optional;
+}
+DEFSEQTYPE(untagged_krb5_authenticator, krb5_authenticator, krb5_authenticator_fields,
+           optional_krb5_authenticator);
+DEFAPPTAGGEDTYPE(krb5_authenticator, 2, untagged_krb5_authenticator);
+
+static const struct field_info enc_tkt_part_fields[] = {
+    /* EncTicketPart ::= [APPLICATION 3] SEQUENCE */
+    /* flags[0]                 TicketFlags */
+    FIELDOF_NORM(krb5_enc_tkt_part, krb5_flags, flags, 0),
+    /* key[1]                   EncryptionKey */
+    FIELDOF_NORM(krb5_enc_tkt_part, ptr_encryption_key, session, 1),
+    /* crealm[2]                        Realm */
+    FIELDOF_NORM(krb5_enc_tkt_part, realm_of_principal, client, 2),
+    /* cname[3]                 PrincipalName */
+    FIELDOF_NORM(krb5_enc_tkt_part, principal, client, 3),
+    /* transited[4]             TransitedEncoding */
+    FIELDOF_NORM(krb5_enc_tkt_part, transited, transited, 4),
+    /* authtime[5]              KerberosTime */
+    FIELDOF_NORM(krb5_enc_tkt_part, kerberos_time, times.authtime, 5),
+    /* starttime[6]             KerberosTime OPTIONAL */
+    FIELDOF_OPT(krb5_enc_tkt_part, kerberos_time, times.starttime, 6, 6),
+    /* endtime[7]                       KerberosTime */
+    FIELDOF_NORM(krb5_enc_tkt_part, kerberos_time, times.endtime, 7),
+    /* renew-till[8]            KerberosTime OPTIONAL */
+    FIELDOF_OPT(krb5_enc_tkt_part, kerberos_time, times.renew_till, 8, 8),
+    /* caddr[9]                 HostAddresses OPTIONAL */
+    FIELDOF_OPT(krb5_enc_tkt_part, ptr_seqof_host_addresses, caddrs, 9, 9),
+    /* authorization-data[10]   AuthorizationData OPTIONAL */
+    FIELDOF_OPT(krb5_enc_tkt_part, auth_data_ptr, authorization_data, 10, 10),
+};
+static unsigned int optional_enc_tkt_part(const void *p)
+{
+    const krb5_enc_tkt_part *val = p;
+    unsigned int optional = 0;
+
+    if (val->authorization_data != NULL && val->authorization_data[0] != NULL)
+        optional |= (1u << 10);
+    if (val->caddrs != NULL && val->caddrs[0] != NULL)
+        optional |= (1u << 9);
+    if (val->times.renew_till)
+        optional |= (1u << 8);
+    if (val->times.starttime)
+        optional |= (1u << 6);
 
-    asn1_cleanup();
+    return optional;
 }
+DEFSEQTYPE(untagged_enc_tkt_part, krb5_enc_tkt_part, enc_tkt_part_fields,
+           optional_enc_tkt_part);
+DEFAPPTAGGEDTYPE(enc_tkt_part, 3, untagged_enc_tkt_part);
 
+DEFAPPTAGGEDTYPE(enc_tgs_rep_part, 26, enc_kdc_rep_part);
 
-asn1_error_code asn1_encode_enc_sam_response_enc(asn1buf *buf, const krb5_enc_sam_response_enc *val, unsigned int *retlen)
+static const struct field_info as_rep_fields[] = {
+    /* AS-REP ::= [APPLICATION 11] KDC-REP */
+    /* But KDC-REP needs to know what type it's being encapsulated
+       in, so expand each version.  */
+    FIELD_INT_IMM(KVNO, 0),
+    FIELD_INT_IMM(KRB5_AS_REP, 1),
+    FIELDOF_OPT(krb5_kdc_rep, ptr_seqof_pa_data, padata, 2, 2),
+    FIELDOF_NORM(krb5_kdc_rep, realm_of_principal, client, 3),
+    FIELDOF_NORM(krb5_kdc_rep, principal, client, 4),
+    FIELDOF_NORM(krb5_kdc_rep, ticket_ptr, ticket, 5),
+    FIELDOF_NORM(krb5_kdc_rep, encrypted_data, enc_part, 6),
+};
+static unsigned int optional_as_rep(const void *p)
 {
-    asn1_setup();
-    add_optstring(val->sam_sad,3,asn1_encode_charstring);
-    asn1_addfield(val->sam_usec,2,asn1_encode_integer);
-    asn1_addfield(val->sam_timestamp,1,asn1_encode_kerberos_time);
-    asn1_addfield(val->sam_nonce,0,asn1_encode_integer);
+    const krb5_kdc_rep *val = p;
+    unsigned int optional = 0;
 
-    asn1_makeseq();
+    if (val->padata != NULL && val->padata[0] != NULL)
+        optional |= (1u << 2);
+
+    return optional;
+}
+DEFSEQTYPE(untagged_as_rep, krb5_kdc_rep, as_rep_fields, optional_as_rep);
+DEFAPPTAGGEDTYPE(as_rep, 11, untagged_as_rep);
+
+static const struct field_info tgs_rep_fields[] = {
+    /* TGS-REP ::= [APPLICATION 13] KDC-REP */
+    /* But KDC-REP needs to know what type it's being encapsulated
+       in, so expand each version.  */
+    FIELD_INT_IMM(KVNO, 0),
+    FIELD_INT_IMM(KRB5_TGS_REP, 1),
+    FIELDOF_OPT(krb5_kdc_rep, ptr_seqof_pa_data, padata, 2, 2),
+    FIELDOF_NORM(krb5_kdc_rep, realm_of_principal, client, 3),
+    FIELDOF_NORM(krb5_kdc_rep, principal, client, 4),
+    FIELDOF_NORM(krb5_kdc_rep, ticket_ptr, ticket, 5),
+    FIELDOF_NORM(krb5_kdc_rep, encrypted_data, enc_part, 6),
+};
+static unsigned int optional_tgs_rep(const void *p)
+{
+    const krb5_kdc_rep *val = p;
+    unsigned int optional = 0;
 
-    asn1_cleanup();
+    if (val->padata != NULL && val->padata[0] != NULL)
+        optional |= (1u << 2);
+
+    return optional;
+}
+DEFSEQTYPE(untagged_tgs_rep, krb5_kdc_rep, tgs_rep_fields, optional_tgs_rep);
+DEFAPPTAGGEDTYPE(tgs_rep, 13, untagged_tgs_rep);
+
+static const struct field_info ap_req_fields[] = {
+    /* AP-REQ ::=       [APPLICATION 14] SEQUENCE */
+    /* pvno[0]          INTEGER */
+    FIELD_INT_IMM(KVNO, 0),
+    /* msg-type[1]      INTEGER */
+    FIELD_INT_IMM(ASN1_KRB_AP_REQ, 1),
+    /* ap-options[2]    APOptions */
+    FIELDOF_NORM(krb5_ap_req, krb5_flags, ap_options, 2),
+    /* ticket[3]                Ticket */
+    FIELDOF_NORM(krb5_ap_req, ticket_ptr, ticket, 3),
+    /* authenticator[4] EncryptedData */
+    FIELDOF_NORM(krb5_ap_req, encrypted_data, authenticator, 4),
+};
+DEFSEQTYPE(untagged_ap_req, krb5_ap_req, ap_req_fields, 0);
+DEFAPPTAGGEDTYPE(ap_req, 14, untagged_ap_req);
+
+static const struct field_info ap_rep_fields[] = {
+    /* AP-REP ::=       [APPLICATION 15] SEQUENCE */
+    /* pvno[0]          INTEGER */
+    FIELD_INT_IMM(KVNO, 0),
+    /* msg-type[1]      INTEGER */
+    FIELD_INT_IMM(ASN1_KRB_AP_REP, 1),
+    /* enc-part[2]      EncryptedData */
+    FIELDOF_NORM(krb5_ap_rep, encrypted_data, enc_part, 2),
+};
+DEFSEQTYPE(untagged_ap_rep, krb5_ap_rep, ap_rep_fields, 0);
+DEFAPPTAGGEDTYPE(ap_rep, 15, untagged_ap_rep);
+
+static const struct field_info ap_rep_enc_part_fields[] = {
+    /* EncAPRepPart ::= [APPLICATION 27] SEQUENCE */
+    /* ctime[0]         KerberosTime */
+    FIELDOF_NORM(krb5_ap_rep_enc_part, kerberos_time, ctime, 0),
+    /* cusec[1]         INTEGER */
+    FIELDOF_NORM(krb5_ap_rep_enc_part, int32, cusec, 1),
+    /* subkey[2]                EncryptionKey OPTIONAL */
+    FIELDOF_OPT(krb5_ap_rep_enc_part, ptr_encryption_key, subkey, 2, 2),
+    /* seq-number[3]    INTEGER OPTIONAL */
+    FIELDOF_OPT(krb5_ap_rep_enc_part, uint, seq_number, 3, 3),
+};
+static unsigned int optional_ap_rep_enc_part(const void *p)
+{
+    const krb5_ap_rep_enc_part *val = p;
+    unsigned int optional = 0;
+
+    if (val->seq_number)
+        optional |= (1u << 3);
+    if (val->subkey != NULL)
+        optional |= (1u << 2);
+
+    return optional;
 }
+DEFSEQTYPE(untagged_ap_rep_enc_part, krb5_ap_rep_enc_part,
+           ap_rep_enc_part_fields, optional_ap_rep_enc_part);
+DEFAPPTAGGEDTYPE(ap_rep_enc_part, 27, untagged_ap_rep_enc_part);
 
-asn1_error_code asn1_encode_enc_sam_response_enc_2(asn1buf *buf, const krb5_enc_sam_response_enc_2 *val, unsigned int *retlen)
+static const struct field_info as_req_fields[] = {
+    /* AS-REQ ::= [APPLICATION 10] KDC-REQ */
+    FIELD_INT_IMM(KVNO, 1),
+    FIELD_INT_IMM(KRB5_AS_REQ, 2),
+    FIELDOF_OPT(krb5_kdc_req, ptr_seqof_pa_data, padata, 3, 3),
+    FIELDOF_ENCODEAS(krb5_kdc_req, kdc_req_body, 4),
+};
+static unsigned int optional_as_req(const void *p)
 {
-    asn1_setup();
-    add_optstring(val->sam_sad,1,asn1_encode_charstring);
-    asn1_addfield(val->sam_nonce,0,asn1_encode_integer);
+    const krb5_kdc_req *val = p;
+    unsigned int optional = 0;
 
-    asn1_makeseq();
+    if (val->padata != NULL && val->padata[0] != NULL)
+        optional |= (1u << 3);
 
-    asn1_cleanup();
+    return optional;
 }
+DEFSEQTYPE(untagged_as_req, krb5_kdc_req, as_req_fields, optional_as_req);
+DEFAPPTAGGEDTYPE(as_req, 10, untagged_as_req);
 
-asn1_error_code asn1_encode_sam_response(asn1buf *buf, const krb5_sam_response *val, unsigned int *retlen)
+static const struct field_info tgs_req_fields[] = {
+    /* TGS-REQ ::= [APPLICATION 12] KDC-REQ */
+    FIELD_INT_IMM(KVNO, 1),
+    FIELD_INT_IMM(KRB5_TGS_REQ, 2),
+    FIELDOF_OPT(krb5_kdc_req, ptr_seqof_pa_data, padata, 3, 3),
+    FIELDOF_ENCODEAS(krb5_kdc_req, kdc_req_body, 4),
+};
+static unsigned int optional_tgs_req(const void *p)
 {
-    asn1_setup();
+    const krb5_kdc_req *val = p;
+    unsigned int optional = 0;
 
-    if (val->sam_patimestamp)
-        asn1_addfield(val->sam_patimestamp,6,asn1_encode_kerberos_time);
-    if (val->sam_nonce)
-        asn1_addfield(val->sam_nonce,5,asn1_encode_integer);
-    asn1_addfield(&(val->sam_enc_nonce_or_ts),4,asn1_encode_encrypted_data);
-    if (val->sam_enc_key.ciphertext.length)
-        asn1_addfield(&(val->sam_enc_key),3,asn1_encode_encrypted_data);
-    add_optstring(val->sam_track_id,2,asn1_encode_charstring);
-    asn1_addfield(val->sam_flags,1,asn1_encode_sam_flags);
-    asn1_addfield(val->sam_type,0,asn1_encode_integer);
+    if (val->padata != NULL && val->padata[0] != NULL)
+        optional |= (1u << 3);
+
+    return optional;
+}
+DEFSEQTYPE(untagged_tgs_req, krb5_kdc_req, tgs_req_fields,
+           optional_tgs_req);
+DEFAPPTAGGEDTYPE(tgs_req, 12, untagged_tgs_req);
+
+static const struct field_info krb5_safe_fields[] = {
+    FIELD_INT_IMM(KVNO, 0),
+    FIELD_INT_IMM(ASN1_KRB_SAFE,1),
+    FIELD_SELF(krb_safe_body, 2),
+    FIELDOF_NORM(krb5_safe, checksum_ptr, checksum, 3),
+};
+DEFSEQTYPE(untagged_krb5_safe, krb5_safe, krb5_safe_fields, 0);
+DEFAPPTAGGEDTYPE(krb5_safe, 20, untagged_krb5_safe);
+
+DEFPTRTYPE(krb_saved_safe_body_ptr, opaque_data);
+DEFFIELDTYPE(krb5_safe_checksum_only, krb5_safe,
+             FIELDOF_NORM(krb5_safe, checksum_ptr, checksum, -1));
+DEFPTRTYPE(krb5_safe_checksum_only_ptr, krb5_safe_checksum_only);
+static const struct field_info krb5_safe_with_body_fields[] = {
+    FIELD_INT_IMM(KVNO, 0),
+    FIELD_INT_IMM(ASN1_KRB_SAFE,1),
+    FIELDOF_NORM(struct krb5_safe_with_body, krb_saved_safe_body_ptr, body, 2),
+    FIELDOF_NORM(struct krb5_safe_with_body, krb5_safe_checksum_only_ptr, safe, 3),
+};
+DEFSEQTYPE(untagged_krb5_safe_with_body, struct krb5_safe_with_body,
+           krb5_safe_with_body_fields, 0);
+DEFAPPTAGGEDTYPE(krb5_safe_with_body, 20, untagged_krb5_safe_with_body);
+
+static const struct field_info priv_fields[] = {
+    FIELD_INT_IMM(KVNO, 0),
+    FIELD_INT_IMM(ASN1_KRB_PRIV, 1),
+    FIELDOF_NORM(krb5_priv, encrypted_data, enc_part, 3),
+};
+DEFSEQTYPE(untagged_priv, krb5_priv, priv_fields, 0);
+DEFAPPTAGGEDTYPE(krb5_priv, 21, untagged_priv);
+
+static const struct field_info priv_enc_part_fields[] = {
+    FIELDOF_NORM(krb5_priv_enc_part, ostring_data, user_data, 0),
+    FIELDOF_OPT(krb5_priv_enc_part, kerberos_time, timestamp, 1, 1),
+    FIELDOF_OPT(krb5_priv_enc_part, int32, usec, 2, 2),
+    FIELDOF_OPT(krb5_priv_enc_part, uint, seq_number, 3, 3),
+    FIELDOF_NORM(krb5_priv_enc_part, address_ptr, s_address, 4),
+    FIELDOF_OPT(krb5_priv_enc_part, address_ptr, r_address, 5, 5),
+};
+static unsigned int optional_priv_enc_part(const void *p)
+{
+    const krb5_priv_enc_part *val = p;
+    unsigned int optional = 0;
 
-    asn1_makeseq();
+    if (val->timestamp) {
+        optional |= (1u << 2);
+        optional |= (1u << 1);
+    }
+    if (val->seq_number)
+        optional |= (1u << 3);
+    if (val->r_address)
+        optional |= (1u << 5);
+
+    return optional;
+}
+DEFSEQTYPE(untagged_priv_enc_part, krb5_priv_enc_part, priv_enc_part_fields,
+           optional_priv_enc_part);
+DEFAPPTAGGEDTYPE(priv_enc_part, 28, untagged_priv_enc_part);
+
+static const struct field_info cred_fields[] = {
+    /* KRB-CRED ::= [APPLICATION 22] SEQUENCE */
+    /* pvno[0]          INTEGER */
+    FIELD_INT_IMM(KVNO, 0),
+    /* msg-type[1]      INTEGER, -- KRB_CRED */
+    FIELD_INT_IMM(ASN1_KRB_CRED, 1),
+    /* tickets[2]       SEQUENCE OF Ticket */
+    FIELDOF_NORM(krb5_cred, ptr_seqof_ticket, tickets, 2),
+    /* enc-part[3]      EncryptedData */
+    FIELDOF_NORM(krb5_cred, encrypted_data, enc_part, 3),
+};
+DEFSEQTYPE(untagged_cred, krb5_cred, cred_fields, 0);
+DEFAPPTAGGEDTYPE(krb5_cred, 22, untagged_cred);
+
+static const struct field_info enc_cred_part_fields[] = {
+    /* EncKrbCredPart ::= [APPLICATION 29] SEQUENCE */
+    /* ticket-info[0]   SEQUENCE OF KrbCredInfo */
+    FIELDOF_NORM(krb5_cred_enc_part, ptrseqof_cred_info, ticket_info, 0),
+    /* nonce[1]         INTEGER OPTIONAL */
+    FIELDOF_OPT(krb5_cred_enc_part, int32, nonce, 1, 1),
+    /* timestamp[2]     KerberosTime OPTIONAL */
+    FIELDOF_OPT(krb5_cred_enc_part, kerberos_time, timestamp, 2, 2),
+    /* usec[3]          INTEGER OPTIONAL */
+    FIELDOF_OPT(krb5_cred_enc_part, int32, usec, 3, 3),
+    /* s-address[4]     HostAddress OPTIONAL */
+    FIELDOF_OPT(krb5_cred_enc_part, address_ptr, s_address, 4, 4),
+    /* r-address[5]     HostAddress OPTIONAL */
+    FIELDOF_OPT(krb5_cred_enc_part, address_ptr, r_address, 5, 5),
+};
+static unsigned int optional_enc_cred_part(const void *p)
+{
+    const krb5_cred_enc_part *val = p;
+    unsigned int optional = 0;
 
-    asn1_cleanup();
-}
+    if (val->r_address != NULL)
+        optional |= (1u << 5);
 
-asn1_error_code asn1_encode_sam_response_2(asn1buf *buf, const krb5_sam_response_2 *val, unsigned int *retlen)
-{
-    asn1_setup();
+    if (val->s_address != NULL)
+        optional |= (1u << 4);
 
-    asn1_addfield(val->sam_nonce,4,asn1_encode_integer);
-    asn1_addfield(&(val->sam_enc_nonce_or_sad),3,asn1_encode_encrypted_data);
-    add_optstring(val->sam_track_id,2,asn1_encode_charstring);
-    asn1_addfield(val->sam_flags,1,asn1_encode_sam_flags);
-    asn1_addfield(val->sam_type,0,asn1_encode_integer);
+    if (val->timestamp) {
+        optional |= (1u << 2);
+        optional |= (1u << 3);
+    }
 
-    asn1_makeseq();
+    if (val->nonce)
+        optional |= (1u << 1);
+
+    return optional;
+}
+DEFSEQTYPE(untagged_enc_cred_part, krb5_cred_enc_part, enc_cred_part_fields,
+           optional_enc_cred_part);
+DEFAPPTAGGEDTYPE(enc_cred_part, 29, untagged_enc_cred_part);
+
+static const struct field_info error_fields[] = {
+    /* KRB-ERROR ::= [APPLICATION 30] SEQUENCE */
+    /* pvno[0]          INTEGER */
+    FIELD_INT_IMM(KVNO, 0),
+    /* msg-type[1]      INTEGER */
+    FIELD_INT_IMM(ASN1_KRB_ERROR, 1),
+    /* ctime[2]         KerberosTime OPTIONAL */
+    FIELDOF_OPT(krb5_error, kerberos_time, ctime, 2, 2),
+    /* cusec[3]         INTEGER OPTIONAL */
+    FIELDOF_OPT(krb5_error, int32, cusec, 3, 3),
+    /* stime[4]         KerberosTime */
+    FIELDOF_NORM(krb5_error, kerberos_time, stime, 4),
+    /* susec[5]         INTEGER */
+    FIELDOF_NORM(krb5_error, int32, susec, 5),
+    /* error-code[6]    INTEGER */
+    FIELDOF_NORM(krb5_error, ui_4, error, 6),
+    /* crealm[7]        Realm OPTIONAL */
+    FIELDOF_OPT(krb5_error, realm_of_principal, client, 7, 7),
+    /* cname[8]         PrincipalName OPTIONAL */
+    FIELDOF_OPT(krb5_error, principal, client, 8, 8),
+    /* realm[9]         Realm -- Correct realm */
+    FIELDOF_NORM(krb5_error, realm_of_principal, server, 9),
+    /* sname[10]        PrincipalName -- Correct name */
+    FIELDOF_NORM(krb5_error, principal, server, 10),
+    /* e-text[11]       GeneralString OPTIONAL */
+    FIELDOF_OPT(krb5_error, gstring_data, text, 11, 11),
+    /* e-data[12]       OCTET STRING OPTIONAL */
+    FIELDOF_OPT(krb5_error, ostring_data, e_data, 12, 12),
+};
+static unsigned int optional_error(const void *p)
+{
+    const krb5_error *val = p;
+    unsigned int optional = 0;
+
+    if (val->ctime)
+        optional |= (1u << 2);
+    if (val->cusec)
+        optional |= (1u << 3);
+    if (val->client) {
+        optional |= (1u << 7);
+        optional |= (1u << 8);
+    }
+    if (val->text.data != NULL && val->text.length > 0)
+        optional |= (1u << 11);
+    if (val->e_data.data != NULL && val->e_data.length > 0)
+        optional |= (1u << 12);
 
-    asn1_cleanup();
+    return optional;
 }
+DEFSEQTYPE(untagged_krb5_error, krb5_error, error_fields, optional_error);
+DEFAPPTAGGEDTYPE(krb5_error, 30, untagged_krb5_error);
 
-asn1_error_code asn1_encode_predicted_sam_response(asn1buf *buf, const krb5_predicted_sam_response *val, unsigned int *retlen)
+static const struct field_info alt_method_fields[] = {
+    FIELDOF_NORM(krb5_alt_method, int32, method, 0),
+    FIELDOF_OPTSTRING(krb5_alt_method, octetstring, data, length, 1, 1),
+};
+static unsigned int
+optional_alt_method(const void *p)
 {
-    asn1_setup();
+    const krb5_alt_method *a = p;
+    unsigned int optional = 0;
 
-    add_optstring(val->msd,6,asn1_encode_charstring);
-    asn1_addfield(val->client,5,asn1_encode_principal_name);
-    asn1_addfield(val->client,4,asn1_encode_realm);
-    asn1_addfield(val->susec,3,asn1_encode_integer);
-    asn1_addfield(val->stime,2,asn1_encode_kerberos_time);
-    asn1_addfield(val->sam_flags,1,asn1_encode_sam_flags);
-    asn1_addfield(&(val->sam_key),0,asn1_encode_encryption_key);
+    if (a->data != NULL && a->length > 0)
+        optional |= (1u << 1);
 
-    asn1_makeseq();
+    return optional;
+}
+DEFSEQTYPE(alt_method, krb5_alt_method, alt_method_fields, optional_alt_method);
 
-    asn1_cleanup();
+static const struct field_info pa_enc_ts_fields[] = {
+    FIELDOF_NORM(krb5_pa_enc_ts, kerberos_time, patimestamp, 0),
+    FIELDOF_OPT(krb5_pa_enc_ts, int32, pausec, 1, 1),
+};
+static unsigned int
+optional_pa_enc_ts(const void *p)
+{
+    const krb5_pa_enc_ts *val = p;
+    unsigned int optional = 0;
+
+    if (val->pausec)
+        optional |= (1u << 1);
+
+    return optional;
 }
+DEFSEQTYPE(pa_enc_ts, krb5_pa_enc_ts, pa_enc_ts_fields, optional_pa_enc_ts);
+
+static const struct field_info pwd_data_fields[] = {
+    FIELDOF_NORM(krb5_pwd_data, int32, sequence_count, 0),
+    FIELDOF_NORM(krb5_pwd_data, ptr_seqof_passwdsequence, element, 1),
+};
+DEFSEQTYPE(pwd_data, krb5_pwd_data, pwd_data_fields, 0);
+
+static const struct field_info setpw_req_fields[] = {
+    FIELDOF_NORM(struct krb5_setpw_req, ostring_data, password, 0),
+    FIELDOF_NORM(struct krb5_setpw_req, principal, target, 1),
+    FIELDOF_NORM(struct krb5_setpw_req, realm_of_principal, target, 2),
+};
+
+DEFSEQTYPE(setpw_req, struct krb5_setpw_req, setpw_req_fields, 0);
+
+
+
+/* Exported complete encoders -- these produce a krb5_data with
+   the encoding in the correct byte order.  */
+
+MAKE_FULL_ENCODER(encode_krb5_authenticator, krb5_authenticator);
+MAKE_FULL_ENCODER(encode_krb5_ticket, ticket);
+MAKE_FULL_ENCODER(encode_krb5_encryption_key, encryption_key);
+MAKE_FULL_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_FULL_ENCODER(encode_krb5_enc_kdc_rep_part, enc_tgs_rep_part);
+MAKE_FULL_ENCODER(encode_krb5_as_rep, as_rep);
+MAKE_FULL_ENCODER(encode_krb5_tgs_rep, tgs_rep);
+MAKE_FULL_ENCODER(encode_krb5_ap_req, ap_req);
+MAKE_FULL_ENCODER(encode_krb5_ap_rep, ap_rep);
+MAKE_FULL_ENCODER(encode_krb5_ap_rep_enc_part, ap_rep_enc_part);
+MAKE_FULL_ENCODER(encode_krb5_as_req, as_req);
+MAKE_FULL_ENCODER(encode_krb5_tgs_req, tgs_req);
+MAKE_FULL_ENCODER(encode_krb5_kdc_req_body, kdc_req_body);
+MAKE_FULL_ENCODER(encode_krb5_safe, krb5_safe);
 
 /*
- * Do some ugliness to insert a raw pre-encoded KRB-SAFE-BODY.
+ * encode_krb5_safe_with_body
+ *
+ * Like encode_krb5_safe(), except takes a saved KRB-SAFE-BODY
+ * encoding to avoid problems with re-encoding.
  */
-asn1_error_code asn1_encode_krb_saved_safe_body(asn1buf *buf, const krb5_data *body, unsigned int *retlen)
-{
-    asn1_error_code retval;
+MAKE_FULL_ENCODER(encode_krb5_safe_with_body, krb5_safe_with_body);
+
+MAKE_FULL_ENCODER(encode_krb5_priv, krb5_priv);
+MAKE_FULL_ENCODER(encode_krb5_enc_priv_part, priv_enc_part);
+MAKE_FULL_ENCODER(encode_krb5_cred, krb5_cred);
+MAKE_FULL_ENCODER(encode_krb5_enc_cred_part, enc_cred_part);
+MAKE_FULL_ENCODER(encode_krb5_error, krb5_error);
+MAKE_FULL_ENCODER(encode_krb5_authdata, auth_data);
+MAKE_FULL_ENCODER(encode_krb5_authdata_elt, authdata_elt);
+MAKE_FULL_ENCODER(encode_krb5_alt_method, alt_method);
+MAKE_FULL_ENCODER(encode_krb5_etype_info, etype_info);
+MAKE_FULL_ENCODER(encode_krb5_etype_info2, etype_info2);
+MAKE_FULL_ENCODER(encode_krb5_enc_data, encrypted_data);
+MAKE_FULL_ENCODER(encode_krb5_pa_enc_ts, pa_enc_ts);
+/* Sandia Additions */
+MAKE_FULL_ENCODER(encode_krb5_pwd_sequence, passwdsequence);
+MAKE_FULL_ENCODER(encode_krb5_pwd_data, pwd_data);
+MAKE_FULL_ENCODER(encode_krb5_padata_sequence, seq_of_pa_data);
+/* sam preauth additions */
+MAKE_FULL_ENCODER(encode_krb5_sam_challenge, sam_challenge);
+#if 0 /* encoders not used! */
+MAKE_FULL_ENCODER(encode_krb5_sam_challenge_2, sam_challenge_2);
+MAKE_FULL_ENCODER(encode_krb5_sam_challenge_2_body,
+                  sam_challenge_2_body);
+#endif
+MAKE_FULL_ENCODER(encode_krb5_sam_key, sam_key);
+MAKE_FULL_ENCODER(encode_krb5_enc_sam_response_enc,
+                  enc_sam_response_enc);
+MAKE_FULL_ENCODER(encode_krb5_enc_sam_response_enc_2,
+                  enc_sam_response_enc_2);
+MAKE_FULL_ENCODER(encode_krb5_sam_response, sam_response);
+MAKE_FULL_ENCODER(encode_krb5_sam_response_2, sam_response_2);
+MAKE_FULL_ENCODER(encode_krb5_predicted_sam_response,
+                  predicted_sam_response);
+MAKE_FULL_ENCODER(encode_krb5_setpw_req, setpw_req);
+
+
+
+
+
+
+
 
-    retval = asn1buf_insert_octetstring(buf, body->length,
-                                        (krb5_octet *)body->data);
-    if (retval) {
-        asn1buf_destroy(&buf);
-        return retval;
-    }
-    *retlen = body->length;
-    return 0;
-}
 
 #ifndef DISABLE_PKINIT
 /*
  * PKINIT
  */
 
+/* This code hasn't been converted to use the above framework yet,
+   because we currently have no test cases to validate the new
+   version.  It *also* appears that some of the encodings may disagree
+   with the specifications, but that's a separate problem.  */
+
+/**** asn1 macros ****/
+#if 0
+   How to write an asn1 encoder function using these macros:
+
+   asn1_error_code asn1_encode_krb5_substructure(asn1buf *buf,
+                                                 const krb5_type *val,
+                                                 int *retlen)
+   {
+     asn1_setup();
+
+     asn1_addfield(val->last_field, n, asn1_type);
+     asn1_addfield(rep->next_to_last_field, n-1, asn1_type);
+     ...
+
+     /* for OPTIONAL fields */
+     if (rep->field_i == should_not_be_omitted)
+       asn1_addfield(rep->field_i, i, asn1_type);
+
+     /* for string fields (these encoders take an additional argument,
+        the length of the string) */
+     addlenfield(rep->field_length, rep->field, i-1, asn1_type);
+
+     /* if you really have to do things yourself... */
+     retval = asn1_encode_asn1_type(buf,rep->field,&length);
+     if (retval) return retval;
+     sum += length;
+     retval = asn1_make_etag(buf, CONTEXT_SPECIFIC, tag_number, length,
+                             &length);
+     if (retval) return retval;
+     sum += length;
+
+     ...
+     asn1_addfield(rep->second_field, 1, asn1_type);
+     asn1_addfield(rep->first_field, 0, asn1_type);
+     asn1_makeseq();
+
+     asn1_cleanup();
+   }
+#endif
+
+/* asn1_addlenfield -- add a field whose length must be separately specified */
+#define asn1_addlenfield(len,value,tag,encoder)\
+{ unsigned int length; \
+  retval = encoder(buf,len,value,&length);      \
+  if (retval) {\
+    asn1buf_destroy(&buf);\
+    return retval; }\
+  sum += length;\
+  retval = asn1_make_etag(buf,CONTEXT_SPECIFIC,tag,length,&length);\
+  if (retval) {\
+    asn1buf_destroy(&buf);\
+    return retval; }\
+  sum += length; }
+
+/* asn1_addfield_implicit -- add an implicitly tagged field, or component, to the encoding */
+#define asn1_addfield_implicit(value,tag,encoder)\
+{ unsigned int length;\
+  retval = encoder(buf,value,&length);\
+  if (retval) {\
+    return retval; }\
+  sum += length;\
+  retval = asn1_make_tag(buf,CONTEXT_SPECIFIC,PRIMITIVE,tag,length,&length); \
+  if (retval) {\
+    return retval; }\
+  sum += length; }
+
+/* asn1_insert_implicit_octetstring -- add an octet string with implicit tagging */
+#define asn1_insert_implicit_octetstring(len,value,tag)\
+{ unsigned int length;\
+  retval = asn1buf_insert_octetstring(buf,len,value);\
+  if (retval) {\
+    return retval; }\
+  sum += len;\
+  retval = asn1_make_tag(buf,CONTEXT_SPECIFIC,PRIMITIVE,tag,len,&length); \
+  if (retval) {\
+    return retval; }\
+  sum += length; }
+
+/* asn1_insert_implicit_bitstring -- add a bitstring with implicit tagging */
+/* needs "length" declared in enclosing context */
+#define asn1_insert_implicit_bitstring(len,value,tag)\
+{ retval = asn1buf_insert_octetstring(buf,len,value); \
+  if (retval) {\
+    return retval; }\
+  sum += len;\
+  retval = asn1buf_insert_octet(buf, 0);\
+  if (retval) {\
+    return retval; }\
+  sum++;\
+  retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,tag,len+1,&length); \
+  if (retval) {\
+    return retval; }\
+  sum += length; }
+
+/* Callable encoders for the types defined above, until the PKINIT
+   encoders get converted.  */
+MAKE_ENCFN(asn1_encode_realm, realm_of_principal_data);
+MAKE_ENCFN(asn1_encode_principal_name, principal_data);
+MAKE_ENCFN(asn1_encode_encryption_key, encryption_key);
+MAKE_ENCFN(asn1_encode_checksum, checksum);
+
+static asn1_error_code
+asn1_encode_kerberos_time(asn1buf *buf, const krb5_timestamp val,
+                          unsigned int *retlen)
+{
+    return asn1_encode_kerberos_time_at(buf,&val,retlen);
+}
+
+/* Now the real PKINIT encoder functions.  */
 asn1_error_code asn1_encode_pk_authenticator(asn1buf *buf, const krb5_pk_authenticator *val, unsigned int *retlen)
 {
     asn1_setup();
@@ -1053,15 +1366,18 @@ asn1_error_code asn1_encode_algorithm_identifier(asn1buf *buf, const krb5_algori
         sum += val->parameters.length;
     }
 
-    retval = asn1_encode_oid(buf, val->algorithm.length,
-                             val->algorithm.data,
-                             &length);
+    {
+        unsigned int length;
+        retval = asn1_encode_oid(buf, val->algorithm.length,
+                                 val->algorithm.data,
+                                 &length);
 
-    if (retval) {
-        asn1buf_destroy(&buf);
-        return retval;
+        if (retval) {
+            asn1buf_destroy(&buf);
+            return retval;
+        }
+        sum += length;
     }
-    sum += length;
 
     asn1_makeseq();
     asn1_cleanup();
@@ -1071,9 +1387,14 @@ asn1_error_code asn1_encode_subject_pk_info(asn1buf *buf, const krb5_subject_pk_
 {
     asn1_setup();
 
-    asn1_insert_implicit_bitstring(val->subjectPublicKey.length,val->subjectPublicKey.data,ASN1_BITSTRING);
+    {
+        unsigned int length;
+        asn1_insert_implicit_bitstring(val->subjectPublicKey.length,val->subjectPublicKey.data,ASN1_BITSTRING);
+    }
 
     if (val->algorithm.parameters.length != 0) {
+        unsigned int length;
+
         retval = asn1buf_insert_octetstring(buf, val->algorithm.parameters.length,
                                             val->algorithm.parameters.data);
         if (retval) {
@@ -1081,27 +1402,28 @@ asn1_error_code asn1_encode_subject_pk_info(asn1buf *buf, const krb5_subject_pk_
             return retval;
         }
         sum += val->algorithm.parameters.length;
-    }
 
-    retval = asn1_encode_oid(buf, val->algorithm.algorithm.length,
-                             val->algorithm.algorithm.data,
-                             &length);
+        retval = asn1_encode_oid(buf, val->algorithm.algorithm.length,
+                                 val->algorithm.algorithm.data,
+                                 &length);
+
+        if (retval) {
+            asn1buf_destroy(&buf);
+            return retval;
+        }
+        sum += length;
 
-    if (retval) {
-        asn1buf_destroy(&buf);
-        return retval;
-    }
-    sum += length;
 
-    retval = asn1_make_etag(buf, UNIVERSAL, ASN1_SEQUENCE,
-                            val->algorithm.parameters.length + length,
-                            &length);
+        retval = asn1_make_etag(buf, UNIVERSAL, ASN1_SEQUENCE,
+                                val->algorithm.parameters.length + length,
+                                &length);
 
-    if (retval) {
-        asn1buf_destroy(&buf);
-        return retval;
+        if (retval) {
+            asn1buf_destroy(&buf);
+            return retval;
+        }
+        sum += length;
     }
-    sum += length;
 
     asn1_makeseq();
     asn1_cleanup();
@@ -1116,6 +1438,7 @@ asn1_error_code asn1_encode_sequence_of_algorithm_identifier(asn1buf *buf, const
 
     for (i=0; val[i] != NULL; i++);
     for (i--; i>=0; i--) {
+        unsigned int length;
         retval = asn1_encode_algorithm_identifier(buf,val[i],&length);
         if (retval) return retval;
         sum += length;
@@ -1183,6 +1506,7 @@ asn1_error_code asn1_encode_sequence_of_external_principal_identifier(asn1buf *b
 
     for (i=0; val[i] != NULL; i++);
     for (i--; i>=0; i--) {
+        unsigned int length;
         retval = asn1_encode_external_principal_identifier(buf,val[i],&length);
         if (retval) return retval;
         sum += length;
@@ -1238,6 +1562,7 @@ asn1_error_code asn1_encode_sequence_of_trusted_ca(asn1buf *buf, const krb5_trus
 
     for (i=0; val[i] != NULL; i++);
     for (i--; i>=0; i--) {
+        unsigned int length;
         retval = asn1_encode_trusted_ca(buf,val[i],&length);
         if (retval) return retval;
         sum += length;
@@ -1286,15 +1611,19 @@ asn1_error_code asn1_encode_kdc_dh_key_info(asn1buf *buf, const krb5_kdc_dh_key_
         asn1_addfield(val->dhKeyExpiration, 2, asn1_encode_kerberos_time);
     asn1_addfield(val->nonce, 1, asn1_encode_integer);
 
-    asn1_insert_implicit_bitstring(val->subjectPublicKey.length,val->subjectPublicKey.data,3);
-    retval = asn1_make_etag(buf, CONTEXT_SPECIFIC, 0,
-                            val->subjectPublicKey.length + 1 + length,
-                            &length);
-    if (retval) {
-        asn1buf_destroy(&buf);
-        return retval;
+    {
+        unsigned int length;
+
+        asn1_insert_implicit_bitstring(val->subjectPublicKey.length,val->subjectPublicKey.data,3);
+        retval = asn1_make_etag(buf, CONTEXT_SPECIFIC, 0,
+                                val->subjectPublicKey.length + 1 + length,
+                                &length);
+        if (retval) {
+            asn1buf_destroy(&buf);
+            return retval;
+        }
+        sum += length;
     }
-    sum += length;
 
     asn1_makeseq();
     asn1_cleanup();
@@ -1363,10 +1692,14 @@ asn1_error_code asn1_encode_pa_pk_as_rep_draft9(asn1buf *buf, const krb5_pa_pk_a
 asn1_error_code asn1_encode_td_trusted_certifiers(asn1buf *buf, const krb5_external_principal_identifier **val, unsigned int *retlen)
 {
     asn1_setup();
-    retval = asn1_encode_sequence_of_external_principal_identifier(buf, val, &length);
-    if (retval) {
-        asn1buf_destroy(&buf);
-        return retval;
+    {
+        unsigned int length;
+        retval = asn1_encode_sequence_of_external_principal_identifier(buf, val, &length);
+        if (retval) {
+            asn1buf_destroy(&buf);
+            return retval;
+        }
+        /* length set but ignored?  sum not updated?  */
     }
     asn1_cleanup();
 }
@@ -1380,6 +1713,8 @@ asn1_error_code asn1_encode_sequence_of_typed_data(asn1buf *buf, const krb5_type
 
     for (i=0; val[i] != NULL; i++);
     for (i--; i>=0; i--) {
+        unsigned int length;
+
         retval = asn1_encode_typed_data(buf,val[i],&length);
         if (retval) return retval;
         sum += length;
index 7ec2b063215f8fec02dccce293874857daec88fb..94b8f7b600b9cf19a549da7e9d3a09886e8646c9 100644 (file)
@@ -2,7 +2,7 @@
 /*
  * src/lib/krb5/asn.1/asn1_k_encode.h
  *
- * Copyright 1994 by the Massachusetts Institute of Technology.
+ * Copyright 1994, 2008 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
 #include <stdio.h>
 #include "asn1buf.h"
 
-/*
-   Overview
-
-     Encoding routines for various ASN.1 "substructures" as defined in
-     the krb5 protocol.
-
-   Operations
-
-    asn1_encode_krb5_flags
-    asn1_encode_ap_options
-    asn1_encode_ticket_flags
-    asn1_encode_kdc_options
-    asn1_encode_kerberos_time
-
-    asn1_encode_realm
-    asn1_encode_principal_name
-    asn1_encode_encrypted_data
-    asn1_encode_authorization_data
-    asn1_encode_krb5_authdata_elt
-    asn1_encode_kdc_rep
-    asn1_encode_ticket
-    asn1_encode_encryption_key
-    asn1_encode_checksum
-    asn1_encode_host_address
-    asn1_encode_transited_encoding
-    asn1_encode_enc_kdc_rep_part
-    asn1_encode_kdc_req
-    asn1_encode_kdc_req_body
-    asn1_encode_krb_safe_body
-    asn1_encode_krb_cred_info
-    asn1_encode_last_req_entry
-    asn1_encode_pa_data
-
-    asn1_encode_host_addresses
-    asn1_encode_last_req
-    asn1_encode_sequence_of_pa_data
-    asn1_encode_sequence_of_ticket
-    asn1_encode_sequence_of_enctype
-    asn1_encode_sequence_of_checksum
-    asn1_encode_sequence_of_krb_cred_info
-*/
-
 /*
 **** for simple val's ****
 asn1_error_code asn1_encode_asn1_type(asn1buf *buf,
@@ -107,169 +65,6 @@ asn1_error_code asn1_encode_asn1_type(asn1buf *buf,
              Returns ENOMEM if memory runs out.
 */
 
-asn1_error_code asn1_encode_ui_4 (asn1buf *buf,
-                                            const krb5_ui_4 val,
-                                            unsigned int *retlen);
-
-asn1_error_code asn1_encode_msgtype (asn1buf *buf,
-                                               const /*krb5_msgtype*/int val,
-                                               unsigned int *retlen);
-
-asn1_error_code asn1_encode_realm
-        (asn1buf *buf, const krb5_principal val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_principal_name
-        (asn1buf *buf, const krb5_principal val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_encrypted_data
-        (asn1buf *buf, const krb5_enc_data *val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_krb5_flags
-        (asn1buf *buf, const krb5_flags val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_ap_options
-        (asn1buf *buf, const krb5_flags val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_ticket_flags
-        (asn1buf *buf, const krb5_flags val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_kdc_options
-        (asn1buf *buf, const krb5_flags val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_authorization_data
-        (asn1buf *buf, const krb5_authdata **val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_krb5_authdata_elt
-        (asn1buf *buf, const krb5_authdata *val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_kdc_rep
-        (int msg_type, asn1buf *buf, const krb5_kdc_rep *val,
-                   unsigned int *retlen);
-
-asn1_error_code asn1_encode_enc_kdc_rep_part
-        (asn1buf *buf, const krb5_enc_kdc_rep_part *val,
-                   unsigned int *retlen);
-
-asn1_error_code asn1_encode_ticket
-        (asn1buf *buf, const krb5_ticket *val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_encryption_key
-        (asn1buf *buf, const krb5_keyblock *val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_kerberos_time
-        (asn1buf *buf, const krb5_timestamp val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_checksum
-        (asn1buf *buf, const krb5_checksum *val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_host_address
-        (asn1buf *buf, const krb5_address *val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_host_addresses
-        (asn1buf *buf, const krb5_address **val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_transited_encoding
-        (asn1buf *buf, const krb5_transited *val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_last_req
-        (asn1buf *buf, const krb5_last_req_entry **val,
-                   unsigned int *retlen);
-
-asn1_error_code asn1_encode_sequence_of_pa_data
-        (asn1buf *buf, const krb5_pa_data **val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_sequence_of_ticket
-        (asn1buf *buf, const krb5_ticket **val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_sequence_of_enctype
-        (asn1buf *buf,
-                   const int len, const krb5_enctype *val,
-                   unsigned int *retlen);
-
-asn1_error_code asn1_encode_sequence_of_checksum
-        (asn1buf *buf, const krb5_checksum **val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_kdc_req
-        (int msg_type,
-                   asn1buf *buf,
-                   const krb5_kdc_req *val,
-                   unsigned int *retlen);
-
-asn1_error_code asn1_encode_kdc_req_body
-        (asn1buf *buf, const krb5_kdc_req *val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_krb_safe_body
-        (asn1buf *buf, const krb5_safe *val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_sequence_of_krb_cred_info
-        (asn1buf *buf, const krb5_cred_info **val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_krb_cred_info
-        (asn1buf *buf, const krb5_cred_info *val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_last_req_entry
-        (asn1buf *buf, const krb5_last_req_entry *val,
-                   unsigned int *retlen);
-
-asn1_error_code asn1_encode_pa_data
-        (asn1buf *buf, const krb5_pa_data *val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_alt_method
-        (asn1buf *buf, const krb5_alt_method *val,
-                   unsigned int *retlen);
-
-asn1_error_code asn1_encode_etype_info_entry
-        (asn1buf *buf, const krb5_etype_info_entry *val,
-                   unsigned int *retlen, int etype_info2);
-
-asn1_error_code asn1_encode_etype_info
-        (asn1buf *buf, const krb5_etype_info_entry **val,
-                   unsigned int *retlen, int etype_info2);
-
-asn1_error_code asn1_encode_passwdsequence
-        (asn1buf *buf, const passwd_phrase_element *val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_sequence_of_passwdsequence
-        (asn1buf *buf, const passwd_phrase_element **val,
-        unsigned int *retlen);
-
-asn1_error_code asn1_encode_sam_flags
-        (asn1buf * buf, const krb5_flags val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_sam_challenge
-        (asn1buf *buf, const krb5_sam_challenge * val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_sam_challenge_2
-        (asn1buf *buf, const krb5_sam_challenge_2 * val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_sam_challenge_2_body
-        (asn1buf *buf, const krb5_sam_challenge_2_body * val,
-                   unsigned int *retlen);
-
-asn1_error_code asn1_encode_sam_key
-        (asn1buf *buf, const krb5_sam_key *val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_enc_sam_response_enc
-        (asn1buf *buf, const krb5_enc_sam_response_enc *val,
-                   unsigned int *retlen);
-
-asn1_error_code asn1_encode_enc_sam_response_enc_2
-        (asn1buf *buf, const krb5_enc_sam_response_enc_2 *val,
-                   unsigned int *retlen);
-
-asn1_error_code asn1_encode_sam_response
-        (asn1buf *buf, const krb5_sam_response *val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_sam_response_2
-        (asn1buf *buf, const krb5_sam_response_2 *val, unsigned int *retlen);
-
-asn1_error_code asn1_encode_predicted_sam_response
-        (asn1buf *buf, const krb5_predicted_sam_response *val,
-                   unsigned int *retlen);
-
-asn1_error_code asn1_encode_krb_saved_safe_body
-        (asn1buf *buf, const krb5_data *body, unsigned int *retlen);
-
 /* PKINIT */
 
 asn1_error_code asn1_encode_pk_authenticator
@@ -337,4 +132,5 @@ asn1_error_code asn1_encode_typed_data
 
 asn1_error_code asn1_encode_sequence_of_typed_data
         (asn1buf *buf, const krb5_typed_data **val, unsigned int *retlen);
+
 #endif
index 4162c9ee5740f9d8387820e1fdb39688c67bd475..0779bfd2fec3e028dc91d0928694868952081841 100644 (file)
@@ -117,7 +117,7 @@ asn1_error_code asn1buf_skiptail(asn1buf *buf, const unsigned int length, const
 
     nestlevel = 1 + indef;
     if (!indef) {
-        if (length <= buf->bound - buf->next + 1)
+        if (length <= (size_t)(buf->bound - buf->next + 1))
             buf->next += length;
         else
             return ASN1_OVERRUN;
@@ -128,7 +128,7 @@ asn1_error_code asn1buf_skiptail(asn1buf *buf, const unsigned int length, const
         retval = asn1_get_tag_2(buf, &t);
         if (retval) return retval;
         if (!t.indef) {
-            if (t.length <= buf->bound - buf->next + 1)
+            if (t.length <= (size_t)(buf->bound - buf->next + 1))
                 buf->next += t.length;
             else
                 return ASN1_OVERRUN;
@@ -165,29 +165,20 @@ asn1_error_code asn1buf_insert_octet(asn1buf *buf, const int o)
     return 0;
 }
 
-asn1_error_code asn1buf_insert_octetstring(asn1buf *buf, const unsigned int len, const krb5_octet *s)
+asn1_error_code
+asn1buf_insert_bytestring(asn1buf *buf, const unsigned int len, const void *sv)
 {
     asn1_error_code retval;
     unsigned int length;
+    const char *s = sv;
 
     retval = asn1buf_ensure_space(buf,len);
     if (retval) return retval;
     for (length=1; length<=len; length++,(buf->next)++)
-        *(buf->next) = (char)(s[len-length]);
+        *(buf->next) = (s[len-length]);
     return 0;
 }
 
-asn1_error_code asn1buf_insert_charstring(asn1buf *buf, const unsigned int len, const char *s)
-{
-    asn1_error_code retval;
-    unsigned int length;
-
-    retval = asn1buf_ensure_space(buf,len);
-    if (retval) return retval;
-    for (length=1; length<=len; length++,(buf->next)++)
-        *(buf->next) = (char)(s[len-length]);
-    return 0;
-}
 
 #undef asn1buf_remove_octet
 asn1_error_code asn1buf_remove_octet(asn1buf *buf, asn1_octet *o)
@@ -201,7 +192,7 @@ asn1_error_code asn1buf_remove_octetstring(asn1buf *buf, const unsigned int len,
 {
     unsigned int i;
 
-    if (len > buf->bound + 1 - buf->next) return ASN1_OVERRUN;
+    if (len > (size_t)(buf->bound + 1 - buf->next)) return ASN1_OVERRUN;
     if (len == 0) {
         *s = 0;
         return 0;
@@ -219,7 +210,7 @@ asn1_error_code asn1buf_remove_charstring(asn1buf *buf, const unsigned int len,
 {
     unsigned int i;
 
-    if (len > buf->bound + 1 - buf->next) return ASN1_OVERRUN;
+    if (len > (size_t)(buf->bound + 1 - buf->next)) return ASN1_OVERRUN;
     if (len == 0) {
         *s = 0;
         return 0;
@@ -344,7 +335,7 @@ unsigned int asn1buf_free(const asn1buf *buf)
 #undef asn1buf_ensure_space
 asn1_error_code asn1buf_ensure_space(asn1buf *buf, const unsigned int amount)
 {
-    int avail = asn1buf_free(buf);
+    unsigned int avail = asn1buf_free(buf);
     if (avail < amount) {
         asn1_error_code retval = asn1buf_expand(buf, amount-avail);
         if (retval) return retval;
index c4920764832d34ad2e2b6614e47780d8c31cc19a..33578f98a0d73a25537592bbc5a99bdd8e65bd93 100644 (file)
@@ -162,21 +162,16 @@ extern __inline__ asn1_error_code asn1buf_insert_octet(asn1buf *buf, const int o
 }
 #endif
 
-asn1_error_code asn1buf_insert_octetstring
-        (asn1buf *buf, const unsigned int len, const asn1_octet *s);
+asn1_error_code asn1buf_insert_bytestring
+        (asn1buf *buf, const unsigned int len, const void *s);
 /* requires  *buf is allocated
    modifies  *buf
-   effects   Inserts the contents of s (an octet array of length len)
+   effects   Inserts the contents of s (an array of length len)
               into the buffer *buf, expanding the buffer if necessary.
              Returns ENOMEM if memory is exhausted. */
 
-asn1_error_code asn1buf_insert_charstring
-        (asn1buf *buf, const unsigned int len, const char *s);
-/* requires  *buf is allocated
-   modifies  *buf
-   effects   Inserts the contents of s (a character array of length len)
-              into the buffer *buf, expanding the buffer if necessary.
-             Returns ENOMEM if memory is exhausted. */
+#define asn1buf_insert_octetstring asn1buf_insert_bytestring
+#define asn1buf_insert_charstring  asn1buf_insert_bytestring
 
 asn1_error_code asn1buf_remove_octet
         (asn1buf *buf, asn1_octet *o);
index 7efbbae9f32237406c23f6184783059bf8b90f07..a6fa305e6236b9d847b4ccef15e683f87c4c9fec 100644 (file)
 
 /**************** Macros (these save a lot of typing) ****************/
 
-/**** krb5 macros ****/
-#if 0
-   How to write a krb5 encoder function using these macros:
-
-   asn1_error_code encode_krb5_structure(const krb5_type *rep,
-                                         krb5_data **code)
-   {
-     krb5_setup();
-
-     krb5_addfield(rep->last_field, n, asn1_type);
-     krb5_addfield(rep->next_to_last_field, n-1, asn1_type);
-     ...
-
-     /* for OPTIONAL fields */
-     if (rep->field_i == should_not_be_omitted)
-       krb5_addfield(rep->field_i, i, asn1_type);
-
-     /* for string fields (these encoders take an additional argument,
-        the length of the string) */
-     addlenfield(rep->field_length, rep->field, i-1, asn1_type);
-
-     /* if you really have to do things yourself... */
-     retval = asn1_encode_asn1_type(buf,rep->field,&length);
-     if (retval) return retval;
-     sum += length;
-     retval = asn1_make_etag(buf,
-                            [UNIVERSAL/APPLICATION/CONTEXT_SPECIFIC/PRIVATE],
-                            tag_number, length, &length);
-     if (retval) return retval;
-     sum += length;
-
-     ...
-     krb5_addfield(rep->second_field, 1, asn1_type);
-     krb5_addfield(rep->first_field, 0, asn1_type);
-     krb5_makeseq();
-     krb5_apptag(tag_number);
-
-     krb5_cleanup();
-   }
-#endif
-
+#ifndef DISABLE_PKINIT
 /* setup() -- create and initialize bookkeeping variables
      retval: stores error codes returned from subroutines
      buf: the coding buffer
      sum: cumulative length of the entire encoding */
 #define krb5_setup()\
   asn1_error_code retval;\
+  unsigned int length, sum = 0;\
   asn1buf *buf=NULL;\
-  unsigned int length, sum=0;\
 \
   if (rep == NULL) return ASN1_MISSING_FIELD;\
 \
   retval = asn1buf_create(&buf);\
   if (retval) return retval
 
-/* krb5_addfield -- add a field, or component, to the encoding */
-#define krb5_addfield(value,tag,encoder)\
-{ retval = encoder(buf,value,&length);\
-  if (retval) {\
-    asn1buf_destroy(&buf);\
-    return retval; }\
-  sum += length;\
-  retval = asn1_make_etag(buf,CONTEXT_SPECIFIC,tag,length,&length);\
-  if (retval) {\
-    asn1buf_destroy(&buf);\
-    return retval; }\
-  sum += length; }
-
-/* krb5_addlenfield -- add a field whose length must be separately specified */
-#define krb5_addlenfield(len,value,tag,encoder)\
-{ retval = encoder(buf,len,value,&length);\
-  if (retval) {\
-    asn1buf_destroy(&buf);\
-    return retval; }\
-  sum += length;\
-  retval = asn1_make_etag(buf,CONTEXT_SPECIFIC,tag,length,&length);\
-  if (retval) {\
-    asn1buf_destroy(&buf);\
-    return retval; }\
-  sum += length; }
-
-/* form a sequence (by adding a sequence header to the current encoding) */
-#define krb5_makeseq()\
-  retval = asn1_make_sequence(buf,sum,&length);\
-  if (retval) {\
-    asn1buf_destroy(&buf);\
-    return retval; }\
-  sum += length
-
-/* add an APPLICATION class tag to the current encoding */
-#define krb5_apptag(num)\
-  retval = asn1_make_etag(buf,APPLICATION,num,sum,&length);\
-  if (retval) {\
-    asn1buf_destroy(&buf);\
-    return retval; }\
-  sum += length
-
 /* produce the final output and clean up the workspace */
 #define krb5_cleanup()\
   retval = asn12krb5_buf(buf,code);\
 \
   return 0
 
-krb5_error_code encode_krb5_authenticator(const krb5_authenticator *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    /* authorization-data[8]    AuthorizationData OPTIONAL */
-    if (rep->authorization_data != NULL &&
-        rep->authorization_data[0] != NULL) {
-        retval = asn1_encode_authorization_data(buf, (const krb5_authdata **)
-                                                rep->authorization_data,
-                                                &length);
-        if (retval) {
-            asn1buf_destroy(&buf);
-            return retval; }
-        sum += length;
-        retval = asn1_make_etag(buf,CONTEXT_SPECIFIC,8,length,&length);
-        if (retval) {
-            asn1buf_destroy(&buf);
-            return retval; }
-        sum += length;
-    }
-
-    /* seq-number[7]            INTEGER OPTIONAL */
-    if (rep->seq_number != 0)
-        krb5_addfield(rep->seq_number,7,asn1_encode_unsigned_integer);
-
-    /* subkey[6]                        EncryptionKey OPTIONAL */
-    if (rep->subkey != NULL)
-        krb5_addfield(rep->subkey,6,asn1_encode_encryption_key);
-
-    /* ctime[5]                 KerberosTime */
-    krb5_addfield(rep->ctime,5,asn1_encode_kerberos_time);
-
-    /* cusec[4]                 INTEGER */
-    krb5_addfield(rep->cusec,4,asn1_encode_integer);
-
-    /* cksum[3]                 Checksum OPTIONAL */
-    if (rep->checksum != NULL)
-        krb5_addfield(rep->checksum,3,asn1_encode_checksum);
-
-    /* cname[2]                 PrincipalName */
-    krb5_addfield(rep->client,2,asn1_encode_principal_name);
-
-    /* crealm[1]                        Realm */
-    krb5_addfield(rep->client,1,asn1_encode_realm);
-
-    /* authenticator-vno[0]     INTEGER */
-    krb5_addfield(KVNO,0,asn1_encode_integer);
-
-    /* Authenticator ::= [APPLICATION 2] SEQUENCE */
-    krb5_makeseq();
-    krb5_apptag(2);
-
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_ticket(const krb5_ticket *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    /* enc-part[3]      EncryptedData */
-    krb5_addfield(&(rep->enc_part),3,asn1_encode_encrypted_data);
-
-    /* sname [2]                PrincipalName */
-    krb5_addfield(rep->server,2,asn1_encode_principal_name);
-
-    /* realm [1]                Realm */
-    krb5_addfield(rep->server,1,asn1_encode_realm);
-
-    /* tkt-vno [0]      INTEGER */
-    krb5_addfield(KVNO,0,asn1_encode_integer);
-
-    /* Ticket ::= [APPLICATION 1] SEQUENCE */
-    krb5_makeseq();
-    krb5_apptag(1);
-
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_encryption_key(const krb5_keyblock *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    /* keyvalue[1]      OCTET STRING */
-    krb5_addlenfield(rep->length,rep->contents,1,asn1_encode_octetstring);
-
-    /* enctype[0]               INTEGER */
-    krb5_addfield(rep->enctype,0,asn1_encode_integer);
-
-    /* EncryptionKey ::= SEQUENCE */
-    krb5_makeseq();
-
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_enc_tkt_part(const krb5_enc_tkt_part *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    /* authorization-data[10]   AuthorizationData OPTIONAL */
-    if (rep->authorization_data != NULL &&
-        rep->authorization_data[0] != NULL)
-        krb5_addfield((const krb5_authdata**)rep->authorization_data,
-                      10,asn1_encode_authorization_data);
-
-    /* caddr[9]                 HostAddresses OPTIONAL */
-    if (rep->caddrs != NULL && rep->caddrs[0] != NULL)
-        krb5_addfield((const krb5_address**)rep->caddrs,9,asn1_encode_host_addresses);
-
-    /* renew-till[8]            KerberosTime OPTIONAL */
-    if (rep->times.renew_till)
-        krb5_addfield(rep->times.renew_till,8,asn1_encode_kerberos_time);
-
-    /* endtime[7]                       KerberosTime */
-    krb5_addfield(rep->times.endtime,7,asn1_encode_kerberos_time);
-
-    /* starttime[6]             KerberosTime OPTIONAL */
-    if (rep->times.starttime)
-        krb5_addfield(rep->times.starttime,6,asn1_encode_kerberos_time);
-
-    /* authtime[5]              KerberosTime */
-    krb5_addfield(rep->times.authtime,5,asn1_encode_kerberos_time);
-
-    /* transited[4]             TransitedEncoding */
-    krb5_addfield(&(rep->transited),4,asn1_encode_transited_encoding);
-
-    /* cname[3]                 PrincipalName */
-    krb5_addfield(rep->client,3,asn1_encode_principal_name);
-
-    /* crealm[2]                        Realm */
-    krb5_addfield(rep->client,2,asn1_encode_realm);
-
-    /* key[1]                   EncryptionKey */
-    krb5_addfield(rep->session,1,asn1_encode_encryption_key);
-
-    /* flags[0]                 TicketFlags */
-    krb5_addfield(rep->flags,0,asn1_encode_ticket_flags);
-
-    /* EncTicketPart ::= [APPLICATION 3] SEQUENCE */
-    krb5_makeseq();
-    krb5_apptag(3);
-
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_enc_kdc_rep_part(const krb5_enc_kdc_rep_part *rep, krb5_data **code)
-{
-    asn1_error_code retval;
-    asn1buf *buf=NULL;
-    unsigned int length, sum=0;
-
-    if (rep == NULL) return ASN1_MISSING_FIELD;
-
-    retval = asn1buf_create(&buf);
-    if (retval) return retval;
-
-    retval = asn1_encode_enc_kdc_rep_part(buf,rep,&length);
-    if (retval) return retval;
-    sum += length;
-
-#ifdef KRB5_ENCKRB5KDCREPPART_COMPAT
-    krb5_apptag(26);
-#else
-    /* XXX WRONG!!! Should use 25 || 26, not the outer KDC_REP tags! */
-    if (rep->msg_type == KRB5_AS_REP) { krb5_apptag(ASN1_KRB_AS_REP); }
-    else if (rep->msg_type == KRB5_TGS_REP) { krb5_apptag(ASN1_KRB_TGS_REP); }
-    else return KRB5_BADMSGTYPE;
-#endif
-    krb5_cleanup();
-}
-
-/* yes, the translation is identical to that used for KDC__REP */
-krb5_error_code encode_krb5_as_rep(const krb5_kdc_rep *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    /* AS-REP ::= [APPLICATION 11] KDC-REP */
-    retval = asn1_encode_kdc_rep(KRB5_AS_REP,buf,rep,&length);
-    if (retval) return retval;
-    sum += length;
-
-    krb5_apptag(11);
-
-    krb5_cleanup();
-}
-
-/* yes, the translation is identical to that used for KDC__REP */
-krb5_error_code encode_krb5_tgs_rep(const krb5_kdc_rep *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    /* TGS-REP ::= [APPLICATION 13] KDC-REP */
-    retval = asn1_encode_kdc_rep(KRB5_TGS_REP,buf,rep,&length);
-    if (retval) return retval;
-    sum += length;
-
-    krb5_apptag(13);
-
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_ap_req(const krb5_ap_req *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    /* authenticator[4] EncryptedData */
-    krb5_addfield(&(rep->authenticator),4,asn1_encode_encrypted_data);
-
-    /* ticket[3]                Ticket */
-    krb5_addfield(rep->ticket,3,asn1_encode_ticket);
-
-    /* ap-options[2]    APOptions */
-    krb5_addfield(rep->ap_options,2,asn1_encode_ap_options);
-
-    /* msg-type[1]      INTEGER */
-    krb5_addfield(ASN1_KRB_AP_REQ,1,asn1_encode_integer);
-
-    /* pvno[0]          INTEGER */
-    krb5_addfield(KVNO,0,asn1_encode_integer);
-
-    /* AP-REQ ::=       [APPLICATION 14] SEQUENCE */
-    krb5_makeseq();
-    krb5_apptag(14);
-
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_ap_rep(const krb5_ap_rep *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    /* enc-part[2]      EncryptedData */
-    krb5_addfield(&(rep->enc_part),2,asn1_encode_encrypted_data);
-
-    /* msg-type[1]      INTEGER */
-    krb5_addfield(ASN1_KRB_AP_REP,1,asn1_encode_integer);
-
-    /* pvno[0]          INTEGER */
-    krb5_addfield(KVNO,0,asn1_encode_integer);
-
-    /* AP-REP ::=       [APPLICATION 15] SEQUENCE */
-    krb5_makeseq();
-    krb5_apptag(15);
-
-    krb5_cleanup();
-}
-
-
-krb5_error_code encode_krb5_ap_rep_enc_part(const krb5_ap_rep_enc_part *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    /* seq-number[3]    INTEGER OPTIONAL */
-    if (rep->seq_number)
-        krb5_addfield(rep->seq_number,3,asn1_encode_unsigned_integer);
-
-    /* subkey[2]                EncryptionKey OPTIONAL */
-    if (rep->subkey != NULL)
-        krb5_addfield(rep->subkey,2,asn1_encode_encryption_key);
-
-    /* cusec[1]         INTEGER */
-    krb5_addfield(rep->cusec,1,asn1_encode_integer);
-
-    /* ctime[0]         KerberosTime */
-    krb5_addfield(rep->ctime,0,asn1_encode_kerberos_time);
-
-    /* EncAPRepPart ::= [APPLICATION 27] SEQUENCE */
-    krb5_makeseq();
-    krb5_apptag(27);
-
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_as_req(const krb5_kdc_req *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    /* AS-REQ ::= [APPLICATION 10] KDC-REQ */
-    retval = asn1_encode_kdc_req(KRB5_AS_REQ,buf,rep,&length);
-    if (retval) return retval;
-    sum += length;
-
-    krb5_apptag(10);
-
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_tgs_req(const krb5_kdc_req *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    /* TGS-REQ ::= [APPLICATION 12] KDC-REQ */
-    retval = asn1_encode_kdc_req(KRB5_TGS_REQ,buf,rep,&length);
-    if (retval) return retval;
-    sum += length;
-
-    krb5_apptag(12);
-
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_kdc_req_body(const krb5_kdc_req *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    retval = asn1_encode_kdc_req_body(buf,rep,&length);
-    if (retval) return retval;
-    sum += length;
-
-    krb5_cleanup();
-}
-
-
-krb5_error_code encode_krb5_safe(const krb5_safe *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    /* cksum[3]         Checksum */
-    krb5_addfield(rep->checksum,3,asn1_encode_checksum);
-
-    /* safe-body[2]     KRB-SAFE-BODY */
-    krb5_addfield(rep,2,asn1_encode_krb_safe_body);
-
-    /* msg-type[1]      INTEGER */
-    krb5_addfield(ASN1_KRB_SAFE,1,asn1_encode_integer);
-
-    /* pvno[0]          INTEGER */
-    krb5_addfield(KVNO,0,asn1_encode_integer);
-
-    /* KRB-SAFE ::= [APPLICATION 20] SEQUENCE */
-    krb5_makeseq();
-    krb5_apptag(20);
-
-    krb5_cleanup();
-}
-
-/*
- * encode_krb5_safe_with_body
- *
- * Like encode_krb5_safe(), except takes a saved KRB-SAFE-BODY
- * encoding to avoid problems with re-encoding.
- */
-krb5_error_code encode_krb5_safe_with_body(
-    const krb5_safe *rep,
-    const krb5_data *body,
-    krb5_data **code)
-{
-    krb5_setup();
-
-    if (body == NULL) {
-        asn1buf_destroy(&buf);
-        return ASN1_MISSING_FIELD;
-    }
-
-    /* cksum[3]         Checksum */
-    krb5_addfield(rep->checksum,3,asn1_encode_checksum);
-
-    /* safe-body[2]     KRB-SAFE-BODY */
-    krb5_addfield(body,2,asn1_encode_krb_saved_safe_body);
-
-    /* msg-type[1]      INTEGER */
-    krb5_addfield(ASN1_KRB_SAFE,1,asn1_encode_integer);
-
-    /* pvno[0]          INTEGER */
-    krb5_addfield(KVNO,0,asn1_encode_integer);
-
-    /* KRB-SAFE ::= [APPLICATION 20] SEQUENCE */
-    krb5_makeseq();
-    krb5_apptag(20);
-
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_priv(const krb5_priv *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    /* enc-part[3]      EncryptedData */
-    krb5_addfield(&(rep->enc_part),3,asn1_encode_encrypted_data);
-
-    /* msg-type[1]      INTEGER */
-    krb5_addfield(ASN1_KRB_PRIV,1,asn1_encode_integer);
-
-    /* pvno[0]          INTEGER */
-    krb5_addfield(KVNO,0,asn1_encode_integer);
-
-    /* KRB-PRIV ::= [APPLICATION 21] SEQUENCE */
-    krb5_makeseq();
-    krb5_apptag(21);
-
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_enc_priv_part(const krb5_priv_enc_part *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    /* r-address[5]     HostAddress OPTIONAL -- recip's addr */
-    if (rep->r_address)
-        krb5_addfield(rep->r_address,5,asn1_encode_host_address);
-
-    /* s-address[4]     HostAddress -- sender's addr */
-    krb5_addfield(rep->s_address,4,asn1_encode_host_address);
-
-    /* seq-number[3]    INTEGER OPTIONAL */
-    if (rep->seq_number)
-        krb5_addfield(rep->seq_number,3,asn1_encode_unsigned_integer);
-
-    /* usec[2]          INTEGER OPTIONAL */
-    if (rep->timestamp) {
-        krb5_addfield(rep->usec,2,asn1_encode_integer);
-        /* timestamp[1] KerberosTime OPTIONAL */
-        krb5_addfield(rep->timestamp,1,asn1_encode_kerberos_time);
-    }
-
-    /* user-data[0]     OCTET STRING */
-    krb5_addlenfield(rep->user_data.length,rep->user_data.data,0,asn1_encode_charstring);
-
-    /* EncKrbPrivPart ::= [APPLICATION 28] SEQUENCE */
-    krb5_makeseq();
-    krb5_apptag(28);
-
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_cred(const krb5_cred *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    /* enc-part[3]      EncryptedData */
-    krb5_addfield(&(rep->enc_part),3,asn1_encode_encrypted_data);
-
-    /* tickets[2]               SEQUENCE OF Ticket */
-    krb5_addfield((const krb5_ticket**)rep->tickets,2,asn1_encode_sequence_of_ticket);
-
-    /* msg-type[1]      INTEGER, -- KRB_CRED */
-    krb5_addfield(ASN1_KRB_CRED,1,asn1_encode_integer);
-
-    /* pvno[0]          INTEGER */
-    krb5_addfield(KVNO,0,asn1_encode_integer);
-
-    /* KRB-CRED ::= [APPLICATION 22] SEQUENCE */
-    krb5_makeseq();
-    krb5_apptag(22);
-
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_enc_cred_part(const krb5_cred_enc_part *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    /* r-address[5]     HostAddress OPTIONAL */
-    if (rep->r_address != NULL)
-        krb5_addfield(rep->r_address,5,asn1_encode_host_address);
-
-    /* s-address[4]     HostAddress OPTIONAL */
-    if (rep->s_address != NULL)
-        krb5_addfield(rep->s_address,4,asn1_encode_host_address);
-
-    /* usec[3]          INTEGER OPTIONAL */
-    if (rep->timestamp) {
-        krb5_addfield(rep->usec,3,asn1_encode_integer);
-        /* timestamp[2] KerberosTime OPTIONAL */
-        krb5_addfield(rep->timestamp,2,asn1_encode_kerberos_time);
-    }
-
-    /* nonce[1]         INTEGER OPTIONAL */
-    if (rep->nonce)
-        krb5_addfield(rep->nonce,1,asn1_encode_integer);
-
-    /* ticket-info[0]   SEQUENCE OF KrbCredInfo */
-    krb5_addfield((const krb5_cred_info**)rep->ticket_info,
-                  0,asn1_encode_sequence_of_krb_cred_info);
-
-    /* EncKrbCredPart ::= [APPLICATION 29] SEQUENCE */
-    krb5_makeseq();
-    krb5_apptag(29);
-
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_error(const krb5_error *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    /* e-data[12]               OCTET STRING OPTIONAL */
-    if (rep->e_data.data != NULL && rep->e_data.length > 0)
-        krb5_addlenfield(rep->e_data.length,rep->e_data.data,12,asn1_encode_charstring);
-
-    /* e-text[11]               GeneralString OPTIONAL */
-    if (rep->text.data != NULL && rep->text.length > 0)
-        krb5_addlenfield(rep->text.length,rep->text.data,11,asn1_encode_generalstring);
-
-    /* sname[10]                PrincipalName -- Correct name */
-    krb5_addfield(rep->server,10,asn1_encode_principal_name);
-
-    /* realm[9]         Realm -- Correct realm */
-    krb5_addfield(rep->server,9,asn1_encode_realm);
-
-    /* cname[8]         PrincipalName OPTIONAL */
-    if (rep->client != NULL) {
-        krb5_addfield(rep->client,8,asn1_encode_principal_name);
-        /* crealm[7]            Realm OPTIONAL */
-        krb5_addfield(rep->client,7,asn1_encode_realm);
-    }
-
-    /* error-code[6]    INTEGER */
-    krb5_addfield(rep->error,6,asn1_encode_ui_4);
-
-    /* susec[5]         INTEGER */
-    krb5_addfield(rep->susec,5,asn1_encode_integer);
-
-    /* stime[4]         KerberosTime */
-    krb5_addfield(rep->stime,4,asn1_encode_kerberos_time);
-
-    /* cusec[3]         INTEGER OPTIONAL */
-    if (rep->cusec)
-        krb5_addfield(rep->cusec,3,asn1_encode_integer);
-
-    /* ctime[2]         KerberosTime OPTIONAL */
-    if (rep->ctime)
-        krb5_addfield(rep->ctime,2,asn1_encode_kerberos_time);
-
-    /* msg-type[1]      INTEGER */
-    krb5_addfield(ASN1_KRB_ERROR,1,asn1_encode_integer);
-
-    /* pvno[0]          INTEGER */
-    krb5_addfield(KVNO,0,asn1_encode_integer);
-
-    /* KRB-ERROR ::= [APPLICATION 30] SEQUENCE */
-    krb5_makeseq();
-    krb5_apptag(30);
-
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_authdata(const krb5_authdata **rep, krb5_data **code)
-{
-    asn1_error_code retval;
-    asn1buf *buf=NULL;
-    unsigned int length;
-
-    if (rep == NULL) return ASN1_MISSING_FIELD;
-
-    retval = asn1buf_create(&buf);
-    if (retval) return retval;
-
-    retval = asn1_encode_authorization_data(buf,(const krb5_authdata**)rep,
-                                            &length);
-    if (retval) return retval;
-
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_authdata_elt(const krb5_authdata *rep, krb5_data **code)
-{
-    asn1_error_code retval;
-    asn1buf *buf=NULL;
-    unsigned int length;
-
-    if (rep == NULL) return ASN1_MISSING_FIELD;
-
-    retval = asn1buf_create(&buf);
-    if (retval) return retval;
-
-    retval = asn1_encode_krb5_authdata_elt(buf,rep, &length);
-    if (retval) return retval;
-
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_alt_method(const krb5_alt_method *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    /* method-data[1]           OctetString OPTIONAL */
-    if (rep->data != NULL && rep->length > 0)
-        krb5_addlenfield(rep->length,rep->data,1,asn1_encode_octetstring);
-
-    /* method-type[0]           Integer */
-    krb5_addfield(rep->method,0,asn1_encode_integer);
-
-    krb5_makeseq();
-
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_etype_info(const krb5_etype_info_entry **rep, krb5_data **code)
-{
-    krb5_setup();
-    retval = asn1_encode_etype_info(buf,rep,&length, 0);
-    if (retval) return retval;
-    sum += length;
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_etype_info2(const krb5_etype_info_entry **rep, krb5_data **code)
-{
-    krb5_setup();
-    retval = asn1_encode_etype_info(buf,rep,&length, 1);
-    if (retval) return retval;
-    sum += length;
-    krb5_cleanup();
-}
-
-
-krb5_error_code encode_krb5_enc_data(const krb5_enc_data *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    retval = asn1_encode_encrypted_data(buf,rep,&length);
-    if (retval) return retval;
-    sum += length;
-
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_pa_enc_ts(const krb5_pa_enc_ts *rep, krb5_data **code)
-{
-    krb5_setup();
-
-    /* pausec[1]                    INTEGER OPTIONAL */
-    if (rep->pausec)
-        krb5_addfield(rep->pausec,1,asn1_encode_integer);
-
-    /* patimestamp[0]               KerberosTime, -- client's time */
-    krb5_addfield(rep->patimestamp,0,asn1_encode_kerberos_time);
-
-    krb5_makeseq();
-
-    krb5_cleanup();
-}
-
-/* Sandia Additions */
-krb5_error_code encode_krb5_pwd_sequence(const passwd_phrase_element *rep, krb5_data **code)
-{
-    krb5_setup();
-    retval = asn1_encode_passwdsequence(buf,rep,&length);
-    if (retval) return retval;
-    sum += length;
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_pwd_data(const krb5_pwd_data *rep, krb5_data **code)
-{
-    krb5_setup();
-    krb5_addfield((const passwd_phrase_element**)rep->element,1,asn1_encode_sequence_of_passwdsequence);
-    krb5_addfield(rep->sequence_count,0,asn1_encode_integer);
-    krb5_makeseq();
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_padata_sequence(const krb5_pa_data **rep, krb5_data **code)
-{
-    krb5_setup();
-
-    retval = asn1_encode_sequence_of_pa_data(buf,rep,&length);
-    if (retval) return retval;
-    sum += length;
-
-    krb5_cleanup();
-}
-
-/* sam preauth additions */
-krb5_error_code encode_krb5_sam_challenge(const krb5_sam_challenge *rep, krb5_data **code)
-{
-    krb5_setup();
-    retval = asn1_encode_sam_challenge(buf,rep,&length);
-    if (retval) return retval;
-    sum += length;
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_sam_challenge_2(const krb5_sam_challenge_2 *rep, krb5_data **code)
-{
-    krb5_setup();
-    retval = asn1_encode_sam_challenge_2(buf,rep,&length);
-    if (retval) return retval;
-    sum += length;
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_sam_challenge_2_body(const krb5_sam_challenge_2_body *rep, krb5_data **code)
-{
-    krb5_setup();
-    retval = asn1_encode_sam_challenge_2_body(buf,rep,&length);
-    if (retval) return retval;
-    sum += length;
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_sam_key(const krb5_sam_key *rep, krb5_data **code)
-{
-    krb5_setup();
-    retval = asn1_encode_sam_key(buf,rep,&length);
-    if (retval) return retval;
-    sum += length;
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_enc_sam_response_enc(const krb5_enc_sam_response_enc *rep, krb5_data **code)
-{
-    krb5_setup();
-    retval = asn1_encode_enc_sam_response_enc(buf,rep,&length);
-    if (retval) return retval;
-    sum += length;
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_enc_sam_response_enc_2(const krb5_enc_sam_response_enc_2 *rep, krb5_data **code)
-{
-    krb5_setup();
-    retval = asn1_encode_enc_sam_response_enc_2(buf,rep,&length);
-    if (retval) return retval;
-    sum += length;
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_sam_response(const krb5_sam_response *rep, krb5_data **code)
-{
-    krb5_setup();
-    retval = asn1_encode_sam_response(buf,rep,&length);
-    if (retval) return retval;
-    sum += length;
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_sam_response_2(const krb5_sam_response_2 *rep, krb5_data **code)
-{
-    krb5_setup();
-    retval = asn1_encode_sam_response_2(buf,rep,&length);
-    if (retval) return retval;
-    sum += length;
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_predicted_sam_response(const krb5_predicted_sam_response *rep, krb5_data **code)
-{
-    krb5_setup();
-    retval = asn1_encode_predicted_sam_response(buf,rep,&length);
-    if (retval) return retval;
-    sum += length;
-    krb5_cleanup();
-}
-
-krb5_error_code encode_krb5_setpw_req(const krb5_principal target,
-                                      char *password, krb5_data **code)
-{
-    /* Macros really want us to have a variable called rep which we do not need*/
-    const char *rep = "dummy string";
-
-    krb5_setup();
-
-    krb5_addfield(target,2,asn1_encode_realm);
-    krb5_addfield(target,1,asn1_encode_principal_name);
-    krb5_addlenfield(strlen(password), password,0,asn1_encode_octetstring);
-    krb5_makeseq();
-
-
-    krb5_cleanup();
-}
-
-#ifndef DISABLE_PKINIT
 krb5_error_code encode_krb5_pa_pk_as_req(const krb5_pa_pk_as_req *rep, krb5_data **code)
 {
     krb5_setup();
index cc41dfdceca0673d267e1027126a911f70d756fa..da08ec1590c6dcce058b76cb6171bcd9927c253e 100644 (file)
@@ -9,16 +9,6 @@
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
 #endif
-/*
- * Older versions of the Kerberos are always sending the
- * enc_kdc_rep_part structure with an application tag of #26, instead
- * of using the application tag of #25 (AS REP) or #26 (AS REP) as
- * necessary.  Worse yet, they will only accept a tag of #26, so we
- * need to follow this for backwards compatibility.  #defining
- * KRB5_ENCKRB5KDCREPPART_COMPAT will preserve this wrong (but
- * compatible) behavior.
- */
-#define KRB5_ENCKRB5KDCREPPART_COMPAT
 
 /*
  * If KRB5_MSGTYPE_STRICT is defined, then be strict about checking
@@ -45,6 +35,9 @@ typedef enum { PRIMITIVE = 0x00, CONSTRUCTED = 0x20 } asn1_construction;
 typedef enum { UNIVERSAL = 0x00, APPLICATION = 0x40,
                  CONTEXT_SPECIFIC = 0x80, PRIVATE = 0xC0 } asn1_class;
 
+typedef INT64_TYPE asn1_intmax;
+typedef UINT64_TYPE asn1_uintmax;
+
 typedef int asn1_tagnum;
 #define ASN1_TAGNUM_CEILING INT_MAX
 #define ASN1_TAGNUM_MAX (ASN1_TAGNUM_CEILING-1)
index 1d48f9b5795001063838f11c3af897bc6c0ba690..2807a51011f0c2348aef837b90790cc1f478801e 100644 (file)
 #include "asn1_decode.h"
 #include "asn1_make.h"
 #include "asn1_get.h"
+#include "asn1_k_encode.h"
 
 #ifdef ENABLE_LDAP
 
-#define asn1_encode_sequence_of_keys krb5int_ldap_encode_sequence_of_keys
-#define asn1_decode_sequence_of_keys krb5int_ldap_decode_sequence_of_keys
-
-#define cleanup(err)                                                    \
-        {                                                               \
-                ret = err;                                              \
-                goto last;                                              \
-        }
-
-#define checkerr                                                        \
-                if (ret != 0)                                           \
-                        goto last
-
 /************************************************************************/
 /* Encode the Principal's keys                                          */
 /************************************************************************/
 
-static asn1_error_code
-asn1_encode_key(asn1buf *buf,
-                krb5_key_data key_data,
-                unsigned int *retlen)
-{
-    asn1_error_code ret = 0;
-    unsigned int length, sum = 0;
-
-    /* Encode the key type and value.  */
-    {
-        unsigned int key_len = 0;
-        /* key value */
-        ret = asn1_encode_octetstring (buf,
-                                       key_data.key_data_length[0],
-                                       key_data.key_data_contents[0],
-                                       &length); checkerr;
-        key_len += length;
-        ret = asn1_make_etag(buf, CONTEXT_SPECIFIC, 1, length, &length); checkerr;
-        key_len += length;
-        /* key type */
-        ret = asn1_encode_integer (buf, key_data.key_data_type[0], &length);
-        checkerr;
-        key_len += length;
-        ret = asn1_make_etag(buf, CONTEXT_SPECIFIC, 0, length, &length); checkerr;
-        key_len += length;
-
-        ret = asn1_make_sequence(buf, key_len, &length); checkerr;
-        key_len += length;
-        ret = asn1_make_etag(buf, CONTEXT_SPECIFIC, 1, key_len, &length); checkerr;
-        key_len += length;
-
-        sum += key_len;
-    }
-    /* Encode the salt type and value (optional) */
-    if (key_data.key_data_ver > 1) {
-        unsigned int salt_len = 0;
-        /* salt value (optional) */
-        if (key_data.key_data_length[1] > 0) {
-            ret = asn1_encode_octetstring (buf,
-                                           key_data.key_data_length[1],
-                                           key_data.key_data_contents[1],
-                                           &length); checkerr;
-            salt_len += length;
-            ret = asn1_make_etag(buf, CONTEXT_SPECIFIC, 1, length, &length);
-            checkerr;
-            salt_len += length;
-        }
-        /* salt type */
-        ret = asn1_encode_integer (buf, key_data.key_data_type[1], &length);
-        checkerr;
-        salt_len += length;
-        ret = asn1_make_etag(buf, CONTEXT_SPECIFIC, 0, length, &length); checkerr;
-        salt_len += length;
-
-        ret = asn1_make_sequence(buf, salt_len, &length); checkerr;
-        salt_len += length;
-        ret = asn1_make_etag(buf, CONTEXT_SPECIFIC, 0, salt_len, &length); checkerr;
-        salt_len += length;
+/* Imports from asn1_k_encode.c.
+   XXX Must be manually synchronized for now.  */
+IMPORT_TYPE(octetstring, unsigned char *);
+IMPORT_TYPE(int32, krb5_int32);
 
-        sum += salt_len;
-    }
-
-    ret = asn1_make_sequence(buf, sum, &length); checkerr;
-    sum += length;
-
-    *retlen = sum;
-
-last:
-    return ret;
-}
+DEFINTTYPE(int16, krb5_int16);
+DEFINTTYPE(ui_2, krb5_ui_2);
 
-/* Major version and minor version are both '1' - first version */
-/* asn1_error_code asn1_encode_sequence_of_keys (krb5_key_data *key_data, */
-krb5_error_code
-asn1_encode_sequence_of_keys (ldap_seqof_key_data *val, krb5_data **code)
+static const struct field_info krbsalt_fields[] = {
+    FIELDOF_NORM(krb5_key_data, int16, key_data_type[1], 0),
+    FIELDOF_OPTSTRINGL(krb5_key_data, octetstring, key_data_contents[1],
+                       ui_2, key_data_length[1], 1, 1),
+};
+static unsigned int optional_krbsalt (const void *p)
 {
-    krb5_key_data *key_data = val->key_data;
-    krb5_int16 n_key_data = val->n_key_data;
-    krb5_int32 mkvno = val->mkvno;
-    asn1_error_code ret = 0;
-    asn1buf *buf = NULL;
-    unsigned int length, sum = 0;
-    unsigned long tmp_ul;
+    const krb5_key_data *k = p;
+    unsigned int optional = 0;
 
-    *code = NULL;
+    if (k->key_data_length[1] > 0)
+        optional |= (1u << 1);
 
-    if (n_key_data == 0) cleanup (ASN1_MISSING_FIELD);
-
-    /* Allocate the buffer */
-    ret = asn1buf_create(&buf);
-    checkerr;
-
-    /* Sequence of keys */
-    {
-        int i;
-        unsigned int seq_len = 0;
-
-        for (i = n_key_data - 1; i >= 0; i--) {
-            ret = asn1_encode_key (buf, key_data[i], &length); checkerr;
-            seq_len += length;
-        }
-        ret = asn1_make_sequence(buf, seq_len, &length); checkerr;
-        seq_len += length;
-        ret = asn1_make_etag(buf, CONTEXT_SPECIFIC, 4, seq_len, &length); checkerr;
-        seq_len += length;
-
-        sum += seq_len;
-    }
-
-    /* mkvno */
-    if (mkvno < 0)
-        cleanup (ASN1_BAD_FORMAT);
-    tmp_ul = (unsigned long)mkvno;
-    ret = asn1_encode_unsigned_integer (buf, tmp_ul, &length); checkerr;
-    sum += length;
-    ret = asn1_make_etag(buf, CONTEXT_SPECIFIC, 3, length, &length); checkerr;
-    sum += length;
-
-    /* kvno (assuming all keys in array have same version) */
-    if (key_data[0].key_data_kvno < 0)
-        cleanup (ASN1_BAD_FORMAT);
-    tmp_ul = (unsigned long)key_data[0].key_data_kvno;
-    ret = asn1_encode_unsigned_integer (buf, tmp_ul, &length);
-    checkerr;
-    sum += length;
-    ret = asn1_make_etag(buf, CONTEXT_SPECIFIC, 2, length, &length); checkerr;
-    sum += length;
-
-    /* attribute-minor-vno == 1 */
-    ret = asn1_encode_unsigned_integer (buf, 1, &length); checkerr;
-    sum += length;
-    ret = asn1_make_etag(buf, CONTEXT_SPECIFIC, 1, length, &length); checkerr;
-    sum += length;
-
-    /* attribute-major-vno == 1 */
-    ret = asn1_encode_unsigned_integer (buf, 1, &length); checkerr;
-    sum += length;
-    ret = asn1_make_etag(buf, CONTEXT_SPECIFIC, 0, length, &length); checkerr;
-    sum += length;
-
-    ret = asn1_make_sequence(buf, sum, &length); checkerr;
-    sum += length;
-
-    /* The reverse encoding is straightened out here */
-    ret = asn12krb5_buf (buf, code); checkerr;
-
-last:
-    asn1buf_destroy (&buf);
-
-    if (ret != 0 && *code != NULL) {
-        free ((*code)->data);
-        free (*code);
-    }
-
-    return ret;
+    return optional;
 }
+DEFSEQTYPE(krbsalt, krb5_key_data, krbsalt_fields, optional_krbsalt);
+static const struct field_info encryptionkey_fields[] = {
+    FIELDOF_NORM(krb5_key_data, int16, key_data_type[0], 0),
+    FIELDOF_STRINGL(krb5_key_data, octetstring, key_data_contents[0],
+                    ui_2, key_data_length[0], 1),
+};
+DEFSEQTYPE(encryptionkey, krb5_key_data, encryptionkey_fields, 0);
+
+static const struct field_info key_data_fields[] = {
+    FIELDOF_ENCODEAS(krb5_key_data, krbsalt, 0),
+    FIELDOF_ENCODEAS(krb5_key_data, encryptionkey, 1),
+#if 0 /* We don't support this field currently.  */
+    FIELDOF_blah(krb5_key_data, s2kparams, ...),
+#endif
+};
+DEFSEQTYPE(key_data, krb5_key_data, key_data_fields, 0);
+DEFPTRTYPE(ptr_key_data, key_data);
+
+DEFFIELDTYPE(key_data_kvno, krb5_key_data,
+             FIELDOF_NORM(krb5_key_data, int16, key_data_kvno, -1));
+DEFPTRTYPE(ptr_key_data_kvno, key_data_kvno);
+
+static const struct field_info ldap_key_seq_fields[] = {
+    FIELD_INT_IMM(1, 0),
+    FIELD_INT_IMM(1, 1),
+    FIELDOF_NORM(ldap_seqof_key_data, ptr_key_data_kvno, key_data, 2),
+    FIELDOF_NORM(ldap_seqof_key_data, int32, mkvno, 3), /* mkvno */
+    FIELDOF_SEQOF_LEN(ldap_seqof_key_data, ptr_key_data, key_data, n_key_data,
+                      int16, 4),
+};
+DEFSEQTYPE(ldap_key_seq, ldap_seqof_key_data, ldap_key_seq_fields, 0);
+
+/* Export a function to do the whole encoding.  */
+MAKE_FULL_ENCODER(krb5int_ldap_encode_sequence_of_keys, ldap_key_seq);
 
 /************************************************************************/
 /* Decode the Principal's keys                                          */
 /************************************************************************/
 
+#define cleanup(err)                                                    \
+        {                                                               \
+                ret = err;                                              \
+                goto last;                                              \
+        }
+
+#define checkerr                                                        \
+                if (ret != 0)                                           \
+                        goto last
+
 #define safe_syncbuf(outer,inner,buflen)                                \
         if (! ((inner)->next == (inner)->bound + 1 &&                   \
                (inner)->next == (outer)->next + buflen))                \
@@ -279,7 +178,8 @@ last:
 #endif
 
 static asn1_error_code
-decode_tagged_octetstring (asn1buf *buf, asn1_tagnum expectedtag, int *len,
+decode_tagged_octetstring (asn1buf *buf, asn1_tagnum expectedtag,
+                           unsigned int *len,
                            asn1_octet **val)
 {
     int buflen;
@@ -328,8 +228,8 @@ static asn1_error_code asn1_decode_key(asn1buf *buf, krb5_key_data *key)
     if (t.tagnum == 0) {
         int salt_buflen;
         asn1buf slt;
-        unsigned long keytype;
-        int keylen;
+        long keytype;
+        unsigned int keylen;
 
         key->key_data_ver = 2;
         asn1_get_sequence(&subbuf, &length, &seqindef);
@@ -358,7 +258,7 @@ static asn1_error_code asn1_decode_key(asn1buf *buf, krb5_key_data *key)
         int key_buflen;
         asn1buf kbuf;
         long lval;
-        int ival;
+        unsigned int ival;
 
         if (t.tagnum != 1)
             cleanup (ASN1_MISSING_FIELD);
@@ -390,9 +290,8 @@ last:
     return ret;
 }
 
-/* asn1_error_code asn1_decode_sequence_of_keys (krb5_data *in, */
-krb5_error_code asn1_decode_sequence_of_keys (krb5_data *in,
-                                              ldap_seqof_key_data **rep)
+krb5_error_code krb5int_ldap_decode_sequence_of_keys (krb5_data *in,
+                                                      ldap_seqof_key_data **rep)
 {
     ldap_seqof_key_data *repval;
     krb5_key_data **out;
index 287adfa0f36036932bb2b2306fcf1a89b840f996..851a9ecdda6224db3c1ee9a03e5a3f5e2d621556 100644 (file)
@@ -269,6 +269,7 @@ krb5int_mk_setpw_req(krb5_context context,
     krb5_error_code ret;
     krb5_data  cipherpw;
     krb5_data  *encoded_setpw;
+    struct krb5_setpw_req req;
 
     char *ptr;
 
@@ -279,7 +280,10 @@ krb5int_mk_setpw_req(krb5_context context,
                                      KRB5_AUTH_CONTEXT_DO_SEQUENCE)))
        return(ret);
 
-    ret = encode_krb5_setpw_req(targprinc, passwd, &encoded_setpw);
+    req.target = targprinc;
+    req.password.data = passwd;
+    req.password.length = strlen(passwd);
+    ret = encode_krb5_setpw_req(&req, &encoded_setpw);
     if (ret) {
        return ret;
     }
index 021fd803e8ee33b7897c006ab04a66806b7b2794..98d73733c3794afa71fced1bf2f6d567b356bfee 100644 (file)
@@ -58,6 +58,7 @@ krb5_rd_safe_basic(krb5_context context, const krb5_data *inbuf,
     krb5_octet zero_octet = 0;
     krb5_data *scratch;
     krb5_boolean valid;
+    struct krb5_safe_with_body swb;
 
     if (!krb5_is_krb_safe(inbuf))
        return KRB5KRB_AP_ERR_MSG_TYPE;
@@ -116,7 +117,9 @@ krb5_rd_safe_basic(krb5_context context, const krb5_data *inbuf,
 
     message->checksum = &our_cksum;
 
-    retval = encode_krb5_safe_with_body(message, &safe_body, &scratch);
+    swb.body = &safe_body;
+    swb.safe = message;
+    retval = encode_krb5_safe_with_body(&swb, &scratch);
     message->checksum = his_cksum;
     if (retval)
        goto cleanup;
index aa881e16f0c6980066c2b432d31aa7520dcb007d..138599804ac438c1312c4ca71d994e5ce8923ef9 100644 (file)
@@ -167,8 +167,7 @@ krb5_send_tgs(krb5_context context, krb5_flags kdcoptions,
     if (authorization_data) {
        /* need to encrypt it in the request */
 
-       if ((retval = encode_krb5_authdata((const krb5_authdata**)authorization_data,
-                                          &scratch)))
+       if ((retval = encode_krb5_authdata(authorization_data, &scratch)))
            return(retval);
 
        if ((retval = krb5_encrypt_helper(context, &in_cred->keyblock,
index 0542ea9d32a578cacc0ba6b9c48dface487483b8..fb72bbe63c10ff6a906e1481c1ec0953cd0503b7 100644 (file)
@@ -29,7 +29,7 @@ krb5_decode_test: $(DECOBJS) $(KRB5_BASE_DEPLIBS)
 t_trval: t_trval.o
        $(CC) -o t_trval $(ALL_CFLAGS) t_trval.o
 
-check:: check-encode check-decode
+check:: check-encode check-encode-trval check-decode
 
 check-decode: krb5_decode_test
        KRB5_CONFIG=$(SRCTOP)/config-files/krb5.conf ; \
@@ -50,22 +50,22 @@ expected_trval.out: trval_reference.out ldap_trval.out
          cat $(srcdir)/trval_reference.out > expected_trval.out; \
        fi
 
-check-encode: krb5_encode_test expected_encode.out expected_trval.out
-       $(RM) test.out
+check-encode: krb5_encode_test expected_encode.out
        KRB5_CONFIG=$(SRCTOP)/config-files/krb5.conf ; \
                export KRB5_CONFIG ;\
                $(RUN_SETUP) $(VALGRIND) ./krb5_encode_test > test.out
        cmp test.out expected_encode.out
+
+check-encode-trval: krb5_encode_test expected_trval.out
        KRB5_CONFIG=$(SRCTOP)/config-files/krb5.conf ; \
                export KRB5_CONFIG ;\
-               $(RUN_SETUP) $(VALGRIND) ./krb5_encode_test -t > test.out
-       cmp test.out expected_trval.out
-       $(RM) test.out  
+               $(RUN_SETUP) $(VALGRIND) ./krb5_encode_test -t > trval.out
+       cmp trval.out expected_trval.out
 
 install::
 
 clean::
-       rm -f *~ *.o krb5_encode_test krb5_decode_test test.out trval t_trval expected_encode.out expected_trval.out
+       rm -f *~ *.o krb5_encode_test krb5_decode_test test.out trval t_trval expected_encode.out expected_trval.out trval.out
 
 
 ################ Dependencies ################
index c6dab46c2e09c8aeecbac21f426756285eb32d6a..ed056703ca995ad2a9c0ea829573cc08dc2d3dc5 100644 (file)
@@ -467,7 +467,7 @@ main(argc, argv)
        krb5_authdata **ad;
        setup(ad,authorization_data,"authorization_data",ktest_make_sample_authorization_data);
 
-       retval = encode_krb5_authdata((const krb5_authdata**)ad,&(code));
+       retval = encode_krb5_authdata(ad,&(code));
        if (retval) {
            com_err("encoding authorization_data",retval,"");
            exit(1);
@@ -502,7 +502,7 @@ main(argc, argv)
        krb5_pa_data **pa;
     
        setup(pa,krb5_pa_data,"PreauthData",ktest_make_sample_pa_data_array);
-       retval = encode_krb5_padata_sequence((const krb5_pa_data**)pa,&(code));
+       retval = encode_krb5_padata_sequence(pa,&(code));
        if (retval) {
            com_err("encoding padata_sequence",retval,"");
            exit(1);
@@ -518,7 +518,7 @@ main(argc, argv)
        krb5_pa_data **pa;
     
        setup(pa,krb5_pa_data,"EmptyPreauthData",ktest_make_sample_empty_pa_data_array);
-       retval = encode_krb5_padata_sequence((const krb5_pa_data**)pa,&(code));
+       retval = encode_krb5_padata_sequence(pa,&(code));
        if (retval) {
            com_err("encoding padata_sequence(empty)",retval,"");
            exit(1);
@@ -550,7 +550,7 @@ main(argc, argv)
     
        setup(info,krb5_etype_info_entry **,"etype_info",
              ktest_make_sample_etype_info);
-       retval = encode_krb5_etype_info((const krb5_etype_info_entry **)info,&(code));
+       retval = encode_krb5_etype_info(info,&(code));
        if (retval) {
            com_err("encoding etype_info",retval,"");
            exit(1);
@@ -559,7 +559,7 @@ main(argc, argv)
        ktest_destroy_etype_info_entry(info[2]);      info[2] = 0;
        ktest_destroy_etype_info_entry(info[1]);      info[1] = 0;
 
-       retval = encode_krb5_etype_info((const krb5_etype_info_entry **)info,&(code));
+       retval = encode_krb5_etype_info(info,&(code));
        if (retval) {
            com_err("encoding etype_info (only 1)",retval,"");
            exit(1);
@@ -568,7 +568,7 @@ main(argc, argv)
 
        ktest_destroy_etype_info_entry(info[0]);      info[0] = 0;
     
-       retval = encode_krb5_etype_info((const krb5_etype_info_entry **)info,&(code));
+       retval = encode_krb5_etype_info(info,&(code));
        if (retval) {
            com_err("encoding etype_info (no info)",retval,"");
            exit(1);
@@ -584,7 +584,7 @@ main(argc, argv)
     
        setup(info,krb5_etype_info_entry **,"etype_info2",
              ktest_make_sample_etype_info2);
-       retval = encode_krb5_etype_info2((const krb5_etype_info_entry **)info,&(code));
+       retval = encode_krb5_etype_info2(info,&(code));
        if (retval) {
            com_err("encoding etype_info",retval,"");
            exit(1);
@@ -593,7 +593,7 @@ main(argc, argv)
        ktest_destroy_etype_info_entry(info[2]);      info[2] = 0;
        ktest_destroy_etype_info_entry(info[1]);      info[1] = 0;
 
-       retval = encode_krb5_etype_info2((const krb5_etype_info_entry **)info,&(code));
+       retval = encode_krb5_etype_info2(info,&(code));
        if (retval) {
            com_err("encoding etype_info (only 1)",retval,"");
            exit(1);
diff --git a/src/tests/dejagnu/krb-standalone/simple.exp b/src/tests/dejagnu/krb-standalone/simple.exp
new file mode 100644 (file)
index 0000000..7f2763c
--- /dev/null
@@ -0,0 +1,214 @@
+# Test for the simple clients
+# This is a DejaGnu test script.
+# This script tests that krb-safe and krb-priv messages work.
+
+# This mostly just calls procedures in test/dejagnu/config/default.exp.
+
+if ![info exists KLIST] {
+    set KLIST [findfile $objdir/../../clients/klist/klist]
+}
+
+if ![info exists KDESTROY] {
+    set KDESTROY [findfile $objdir/../../clients/kdestroy/kdestroy]
+}
+
+if ![info exists SIM_SERVER] {
+    set SIM_SERVER [findfile $objdir/../../appl/simple/server/sim_server]
+}
+if ![info exists SIM_CLIENT] {
+    set SIM_CLIENT [findfile $objdir/../../appl/simple/client/sim_client]
+}
+
+# Set up the Kerberos files and environment.
+if {![get_hostname] || ![setup_kerberos_files] || ![setup_kerberos_env]} {
+    return
+}
+
+# Initialize the Kerberos database.  The argument tells
+# setup_kerberos_db that it is being called from here.
+if ![setup_kerberos_db 0] {
+    return
+}
+
+proc start_sim_server_daemon { } {
+    global spawn_id
+    global sim_server_pid
+    global sim_server_spawn_id
+    global SIM_SERVER
+    global T_INETD
+    global tmppwd
+    global portbase
+
+    # Start the sim_server
+    spawn $SIM_SERVER -p [expr 8 + $portbase] -S $tmppwd/srvtab
+    set sim_server_pid [exp_pid]
+    set sim_server_spawn_id $spawn_id
+
+    verbose "sim_server_spawn is $sim_server_spawn_id" 1
+
+    # Give sim_server some time to start
+    sleep 2
+
+    return 1
+}
+
+
+proc stop_sim_server_daemon { } {
+    global sim_server_pid
+    global sim_server_spawn_id
+
+    if [info exists sim_server_pid] {
+       catch "close -i $sim_server_spawn_id"
+       catch "exec kill $sim_server_pid"
+       wait -i $sim_server_spawn_id
+       unset sim_server_pid
+    }
+
+    return 1
+}
+
+proc stop_check_sim_server_daemon { } {
+    global sim_server_spawn_id
+    global sim_server_pid
+
+    # Check the exit status of sim_server - should exit here
+    set status_list [wait -i $sim_server_spawn_id]
+    verbose "wait -i $sim_server_spawn_id returned $status_list (sim_server)"
+    catch "close -i $sim_server_spawn_id"
+    if { [lindex $status_list 2] != 0 || [lindex $status_list 3] != 0 } {
+       send_log "exit status: $status_list\n"
+       verbose "exit status: $status_list"
+       fail "sim_server"
+    } else {
+       pass "sim_server"
+    }
+    # In either case the server shutdown
+    unset sim_server_pid
+}
+
+proc test_sim_client { msg } {
+    global REALMNAME
+    global SIM_CLIENT
+    global hostname
+    global spawn_id
+    global portbase
+    global sim_server_spawn_id
+
+    # Test the client
+    spawn $SIM_CLIENT -p [expr 8 + $portbase] $hostname
+    verbose "sim_client_spawn is  $spawn_id" 1
+
+    expect {
+       "Sent checksummed message: " {
+               verbose "received safe message"
+       }
+       timeout {
+               fail $msg 
+               return 0
+       }
+       eof {
+               fail $msg 
+               return 0
+       }       
+     }
+
+    expect {
+       "Sent encrypted message: " {
+               verbose "received private message"
+       }
+       eof {
+               fail $msg 
+               return 0
+           }
+    }
+    expect {
+        "\r" { }
+    }
+
+    expect {
+       -i $sim_server_spawn_id
+       "Safe message is: 'hi there!'" { }
+       timeout {
+           fail $msg
+           return 0
+       }
+       eof {
+           fail $msg
+           return 0
+       }
+    }
+
+    expect {
+       -i $sim_server_spawn_id
+       "Decrypted message is: 'hi there!'" { }
+       timeout {
+           fail $msg
+           return 0
+       }
+       eof {
+           fail $msg
+           return 0
+       }
+    }
+
+    if ![check_exit_status "simple"] {
+       return 0
+    }
+
+    return 1
+}
+# We are about to start up a couple of daemon processes.  We do all
+# the rest of the tests inside a proc, so that we can easily kill the
+# processes when the procedure ends.
+
+proc doit { } {
+    global hostname
+    global KEY
+    global sim_server_pid
+    global sim_server_spawn_id
+
+    # Start up the kerberos and kadmind daemons.
+    if ![start_kerberos_daemons 0] {
+       return
+    }
+
+    # Use kadmin to add an host key.
+    if ![add_random_key sample/$hostname 1] {
+       return
+    }
+
+    # Use ksrvutil to create a srvtab entry for sample
+    if ![setup_srvtab 1 sample] {
+       return
+    }
+
+    # Use kinit to get a ticket.
+    if ![kinit krbtest/admin adminpass$KEY 1] {
+       return
+    }
+
+    if ![start_sim_server_daemon] {
+       return 
+    }
+
+    if ![test_sim_client sim_client] {
+       return
+    }
+    
+    pass "simple - standalone"
+
+    stop_check_sim_server_daemon
+    return
+}
+
+set status [catch doit msg]
+
+stop_sim_server_daemon
+
+stop_kerberos_daemons
+
+if { $status != 0 } {
+    send_error "ERROR: error in simple.exp\n"
+    send_error "$msg\n"
+    exit 1
+}