krb5 commit: Use k5-der.h in GSS library

ghudson at mit.edu ghudson at mit.edu
Fri Mar 24 15:07:08 EDT 2023


https://github.com/krb5/krb5/commit/fdceb225f881e2b1337eebcb9a9443fa4a9be3fd
commit fdceb225f881e2b1337eebcb9a9443fa4a9be3fd
Author: Greg Hudson <ghudson at mit.edu>
Date:   Wed Mar 15 13:56:21 2023 -0400

    Use k5-der.h in GSS library
    
    Remove the DER implementations in lib/gssapi and use k5-der.h instead.

 src/lib/gssapi/generic/gssapiP_generic.h      |    4 +-
 src/lib/gssapi/generic/util_token.c           |  199 +----
 src/lib/gssapi/krb5/accept_sec_context.c      |   26 +-
 src/lib/gssapi/krb5/iakerb.c                  |   60 +-
 src/lib/gssapi/krb5/init_sec_context.c        |   18 +-
 src/lib/gssapi/krb5/k5seal.c                  |   59 +-
 src/lib/gssapi/krb5/k5sealiov.c               |   47 +-
 src/lib/gssapi/mechglue/g_encapsulate_token.c |   10 +-
 src/lib/gssapi/mechglue/g_glue.c              |  284 +-----
 src/lib/gssapi/mechglue/g_imp_name.c          |  112 +--
 src/lib/gssapi/mechglue/mglueP.h              |   17 -
 src/lib/gssapi/spnego/spnego_mech.c           | 1168 ++++++-------------------
 12 files changed, 474 insertions(+), 1530 deletions(-)

diff --git a/src/lib/gssapi/generic/gssapiP_generic.h b/src/lib/gssapi/generic/gssapiP_generic.h
index 686a21772..5d01fb492 100644
--- a/src/lib/gssapi/generic/gssapiP_generic.h
+++ b/src/lib/gssapi/generic/gssapiP_generic.h
@@ -153,8 +153,8 @@ int g_make_string_buffer (const char *str, gss_buffer_t buffer);
 
 unsigned int g_token_size (const gss_OID_desc * mech, unsigned int body_size);
 
-void g_make_token_header (const gss_OID_desc * mech, unsigned int body_size,
-                          unsigned char **buf, int tok_type);
+void g_make_token_header (struct k5buf *buf, const gss_OID_desc *mech,
+                          size_t body_size, int tok_type);
 
 /* flags for g_verify_token_header() */
 #define G_VFY_TOKEN_HDR_WRAPPER_REQUIRED        0x01
diff --git a/src/lib/gssapi/generic/util_token.c b/src/lib/gssapi/generic/util_token.c
index 6e339f4ac..2369cae22 100644
--- a/src/lib/gssapi/generic/util_token.c
+++ b/src/lib/gssapi/generic/util_token.c
@@ -22,6 +22,7 @@
  */
 
 #include "gssapiP_generic.h"
+#include "k5-der.h"
 #ifdef HAVE_MEMORY_H
 #include <memory.h>
 #endif
@@ -31,131 +32,33 @@
  * $Id$
  */
 
-/* XXXX this code currently makes the assumption that a mech oid will
-   never be longer than 127 bytes.  This assumption is not inherent in
-   the interfaces, so the code can be fixed if the OSI namespace
-   balloons unexpectedly. */
-
-/*
- * Each token looks like this:
- * 0x60                 tag for APPLICATION 0, SEQUENCE
- *                              (constructed, definite-length)
- * <length>             possible multiple bytes, need to parse/generate
- * 0x06                 tag for OBJECT IDENTIFIER
- * <moid_length>        compile-time constant string (assume 1 byte)
- * <moid_bytes>         compile-time constant string
- * <inner_bytes>        the ANY containing the application token
- * bytes 0,1 are the token type
- * bytes 2,n are the token data
- *
- * Note that the token type field is a feature of RFC 1964 mechanisms and
- * is not used by other GSSAPI mechanisms.  As such, a token type of -1
- * is interpreted to mean that no token type should be expected or
- * generated.
- *
- * For the purposes of this abstraction, the token "header" consists of
- * the sequence tag and length octets, the mech OID DER encoding, and the
- * first two inner bytes, which indicate the token type.  The token
- * "body" consists of everything else.
- */
-static unsigned int
-der_length_size(int length)
-{
-    if (length < (1<<7))
-        return(1);
-    else if (length < (1<<8))
-        return(2);
-#if INT_MAX == 0x7fff
-    else
-        return(3);
-#else
-    else if (length < (1<<16))
-        return(3);
-    else if (length < (1<<24))
-        return(4);
-    else
-        return(5);
-#endif
-}
-
-static void
-der_write_length(unsigned char **buf, int length)
-{
-    if (length < (1<<7)) {
-        *(*buf)++ = (unsigned char) length;
-    } else {
-        *(*buf)++ = (unsigned char) (der_length_size(length)+127);
-#if INT_MAX > 0x7fff
-        if (length >= (1<<24))
-            *(*buf)++ = (unsigned char) (length>>24);
-        if (length >= (1<<16))
-            *(*buf)++ = (unsigned char) ((length>>16)&0xff);
-#endif
-        if (length >= (1<<8))
-            *(*buf)++ = (unsigned char) ((length>>8)&0xff);
-        *(*buf)++ = (unsigned char) (length&0xff);
-    }
-}
-
-/* returns decoded length, or < 0 on failure.  Advances buf and
-   decrements bufsize */
-
-static int
-der_read_length(unsigned char **buf, int *bufsize)
-{
-    unsigned char sf;
-    int ret;
-
-    if (*bufsize < 1)
-        return(-1);
-    sf = *(*buf)++;
-    (*bufsize)--;
-    if (sf & 0x80) {
-        if ((sf &= 0x7f) > ((*bufsize)-1))
-            return(-1);
-        if (sf > sizeof(int))
-            return (-1);
-        ret = 0;
-        for (; sf; sf--) {
-            ret = (ret<<8) + (*(*buf)++);
-            (*bufsize)--;
-        }
-    } else {
-        ret = sf;
-    }
-
-    return(ret);
-}
-
-/* returns the length of a token, given the mech oid and the body size */
-
+/* Return the length of an RFC 4121 token with RFC 2743 token framing, given
+ * the mech oid and the body size (without the two-byte RFC 4121 token ID). */
 unsigned int
 g_token_size(const gss_OID_desc * mech, unsigned int body_size)
 {
-    /* set body_size to sequence contents size */
-    body_size += 4 + (unsigned int)mech->length;         /* NEED overflow check */
-    return(1 + der_length_size(body_size) + body_size);
-}
+    size_t mech_der_len = k5_der_value_len(mech->length);
 
-/* fills in a buffer with the token header.  The buffer is assumed to
-   be the right size.  buf is advanced past the token header */
+    return k5_der_value_len(mech_der_len + 2 + body_size);
+}
 
+/*
+ * Add RFC 2743 generic token framing to buf with room left for body_size bytes
+ * in the sequence to be added by the caller.  If tok_type is not -1, add it as
+ * a two-byte RFC 4121 token identifier after the framing and include room for
+ * it in the sequence.
+ */
 void
-g_make_token_header(
-    const gss_OID_desc * mech,
-    unsigned int body_size,
-    unsigned char **buf,
-    int tok_type)
+g_make_token_header(struct k5buf *buf, const gss_OID_desc *mech,
+                    size_t body_size, int tok_type)
 {
-    *(*buf)++ = 0x60;
-    der_write_length(buf, ((tok_type == -1) ? 2 : 4) + mech->length + body_size);
-    *(*buf)++ = 0x06;
-    *(*buf)++ = (unsigned char) mech->length;
-    TWRITE_STR(*buf, mech->elements, mech->length);
-    if (tok_type != -1) {
-        *(*buf)++ = (unsigned char) ((tok_type>>8)&0xff);
-        *(*buf)++ = (unsigned char) (tok_type&0xff);
-    }
+    size_t tok_len = (tok_type == -1) ? 0 : 2;
+    size_t seq_len = k5_der_value_len(mech->length) + body_size + tok_len;
+
+    k5_der_add_taglen(buf, 0x60, seq_len);
+    k5_der_add_value(buf, 0x06, mech->elements, mech->length);
+    if (tok_type != -1)
+        k5_buf_add_uint16_be(buf, tok_type);
 }
 
 /*
@@ -176,54 +79,30 @@ g_verify_token_header(
     unsigned int toksize_in,
     int flags)
 {
-    unsigned char *buf = *buf_in;
-    int seqsize;
+    struct k5input in, mech_der;
     gss_OID_desc toid;
-    int toksize = toksize_in;
 
-    if ((toksize-=1) < 0)
-        return(G_BAD_TOK_HEADER);
-    if (*buf++ != 0x60) {
-        if (flags & G_VFY_TOKEN_HDR_WRAPPER_REQUIRED)
-            return(G_BAD_TOK_HEADER);
-        buf--;
-        toksize++;
-        goto skip_wrapper;
+    k5_input_init(&in, *buf_in, toksize_in);
+
+    if (k5_der_get_value(&in, 0x60, &in)) {
+        if (in.ptr + in.len != *buf_in + toksize_in)
+            return G_BAD_TOK_HEADER;
+        if (!k5_der_get_value(&in, 0x06, &mech_der))
+            return G_BAD_TOK_HEADER;
+        toid.elements = (uint8_t *)mech_der.ptr;
+        toid.length = mech_der.len;
+        if (!g_OID_equal(&toid, mech))
+            return G_WRONG_MECH;
+    } else if (flags & G_VFY_TOKEN_HDR_WRAPPER_REQUIRED) {
+        return G_BAD_TOK_HEADER;
     }
 
-    if ((seqsize = der_read_length(&buf, &toksize)) < 0)
-        return(G_BAD_TOK_HEADER);
-
-    if (seqsize != toksize)
-        return(G_BAD_TOK_HEADER);
-
-    if ((toksize-=1) < 0)
-        return(G_BAD_TOK_HEADER);
-    if (*buf++ != 0x06)
-        return(G_BAD_TOK_HEADER);
-
-    if ((toksize-=1) < 0)
-        return(G_BAD_TOK_HEADER);
-    toid.length = *buf++;
-
-    if ((toksize-=toid.length) < 0)
-        return(G_BAD_TOK_HEADER);
-    toid.elements = buf;
-    buf+=toid.length;
-
-    if (! g_OID_equal(&toid, mech))
-        return  G_WRONG_MECH;
-skip_wrapper:
     if (tok_type != -1) {
-        if ((toksize-=2) < 0)
-            return(G_BAD_TOK_HEADER);
-
-        if ((*buf++ != ((tok_type>>8)&0xff)) ||
-            (*buf++ != (tok_type&0xff)))
-            return(G_WRONG_TOKID);
+        if (k5_input_get_uint16_be(&in) != tok_type)
+            return in.status ? G_BAD_TOK_HEADER : G_WRONG_TOKID;
     }
-    *buf_in = buf;
-    *body_size = toksize;
 
+    *buf_in = (uint8_t *)in.ptr;
+    *body_size = in.len;
     return 0;
 }
diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c
index 44c056afd..dd4caf380 100644
--- a/src/lib/gssapi/krb5/accept_sec_context.c
+++ b/src/lib/gssapi/krb5/accept_sec_context.c
@@ -684,6 +684,7 @@ kg_accept_krb5(minor_status, context_handle,
     krb5_enctype negotiated_etype;
     krb5_authdata_context ad_context = NULL;
     krb5_ap_req *request = NULL;
+    struct k5buf buf;
 
     code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
     if (code) {
@@ -1009,7 +1010,6 @@ kg_accept_krb5(minor_status, context_handle,
     /* generate an AP_REP if necessary */
 
     if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
-        unsigned char * ptr3;
         krb5_int32 seq_temp;
         int cfx_generate_subkey;
 
@@ -1114,18 +1114,16 @@ kg_accept_krb5(minor_status, context_handle,
         ctx->established = 1;
 
         token.length = g_token_size(mech_used, ap_rep.length);
-
-        if ((token.value = (unsigned char *) gssalloc_malloc(token.length))
-            == NULL) {
+        token.value = gssalloc_malloc(token.length);
+        if (token.value == NULL) {
             major_status = GSS_S_FAILURE;
             code = ENOMEM;
             goto fail;
         }
-        ptr3 = token.value;
-        g_make_token_header(mech_used, ap_rep.length,
-                            &ptr3, KG_TOK_CTX_AP_REP);
-
-        TWRITE_STR(ptr3, ap_rep.data, ap_rep.length);
+        k5_buf_init_fixed(&buf, token.value, token.length);
+        g_make_token_header(&buf, mech_used, ap_rep.length, KG_TOK_CTX_AP_REP);
+        k5_buf_add_len(&buf, ap_rep.data, ap_rep.length);
+        assert(buf.len == token.length);
 
         ctx->established = 1;
 
@@ -1220,7 +1218,6 @@ fail:
          (request->ap_options & AP_OPTS_MUTUAL_REQUIRED) ||
          major_status == GSS_S_CONTINUE_NEEDED)) {
         unsigned int tmsglen;
-        int toktype;
 
         /*
          * The client is expecting a response, so we can send an
@@ -1242,17 +1239,16 @@ fail:
             goto done;
 
         tmsglen = scratch.length;
-        toktype = KG_TOK_CTX_ERROR;
 
         token.length = g_token_size(mech_used, tmsglen);
         token.value = gssalloc_malloc(token.length);
         if (!token.value)
             goto done;
+        k5_buf_init_fixed(&buf, token.value, token.length);
+        g_make_token_header(&buf, mech_used, tmsglen, KG_TOK_CTX_ERROR);
+        k5_buf_add_len(&buf, scratch.data, scratch.length);
+        assert(buf.len == token.length);
 
-        ptr = token.value;
-        g_make_token_header(mech_used, tmsglen, &ptr, toktype);
-
-        TWRITE_STR(ptr, scratch.data, scratch.length);
         krb5_free_data_contents(context, &scratch);
 
         *output_token = token;
diff --git a/src/lib/gssapi/krb5/iakerb.c b/src/lib/gssapi/krb5/iakerb.c
index b0d0ede17..a0d298c49 100644
--- a/src/lib/gssapi/krb5/iakerb.c
+++ b/src/lib/gssapi/krb5/iakerb.c
@@ -23,14 +23,13 @@
  * or implied warranty.
  */
 #include "k5-int.h"
+#include "k5-der.h"
 #include "gssapiP_krb5.h"
 
 /*
  * IAKERB implementation
  */
 
-extern int gssint_get_der_length(unsigned char **, OM_uint32, unsigned int*);
-
 enum iakerb_state {
     IAKERB_AS_REQ,      /* acquiring ticket with initial creds */
     IAKERB_TGS_REQ,     /* acquiring ticket with TGT */
@@ -172,11 +171,11 @@ iakerb_parse_token(iakerb_ctx_id_t ctx,
 {
     krb5_error_code code;
     krb5_iakerb_header *iah = NULL;
-    unsigned int bodysize, lenlen;
-    int length;
-    unsigned char *ptr;
+    unsigned int bodysize;
+    uint8_t *body;
     int flags = 0;
     krb5_data data;
+    struct k5input in, seq;
 
     if (token == GSS_C_NO_BUFFER || token->length == 0) {
         code = KRB5_BAD_MSIZE;
@@ -186,32 +185,20 @@ iakerb_parse_token(iakerb_ctx_id_t ctx,
     if (initialContextToken)
         flags |= G_VFY_TOKEN_HDR_WRAPPER_REQUIRED;
 
-    ptr = token->value;
-
-    code = g_verify_token_header(gss_mech_iakerb,
-                                 &bodysize, &ptr,
-                                 IAKERB_TOK_PROXY,
-                                 token->length, flags);
+    body = token->value;
+    code = g_verify_token_header(gss_mech_iakerb, &bodysize, &body,
+                                 IAKERB_TOK_PROXY, token->length, flags);
     if (code != 0)
         goto cleanup;
 
-    data.data = (char *)ptr;
-
-    if (bodysize-- == 0 || *ptr++ != 0x30 /* SEQUENCE */) {
+    /* Find the end of the DER sequence tag and decode it (with the tag) as the
+     * IAKERB jeader. */
+    k5_input_init(&in, body, bodysize);
+    if (!k5_der_get_value(&in, 0x30, &seq)) {
         code = ASN1_BAD_ID;
         goto cleanup;
     }
-
-    length = gssint_get_der_length(&ptr, bodysize, &lenlen);
-    if (length < 0 || bodysize - lenlen < (unsigned int)length) {
-        code = KRB5_BAD_MSIZE;
-        goto cleanup;
-    }
-    data.length = 1 /* SEQUENCE */ + lenlen + length;
-
-    ptr += length;
-    bodysize -= (lenlen + length);
-
+    data = make_data(body, seq.ptr + seq.len - body);
     code = decode_krb5_iakerb_header(&data, &iah);
     if (code != 0)
         goto cleanup;
@@ -226,9 +213,8 @@ iakerb_parse_token(iakerb_ctx_id_t ctx,
         iah->cookie = NULL;
     }
 
-    request->data = (char *)ptr;
-    request->length = bodysize;
-
+    /* The remainder of the token body is the request. */
+    *request = make_data((uint8_t *)in.ptr, in.len);
     assert(request->data + request->length ==
            (char *)token->value + token->length);
 
@@ -254,7 +240,7 @@ iakerb_make_token(iakerb_ctx_id_t ctx,
     krb5_data *data = NULL;
     char *p;
     unsigned int tokenSize;
-    unsigned char *q;
+    struct k5buf buf;
 
     token->value = NULL;
     token->length = 0;
@@ -288,24 +274,22 @@ iakerb_make_token(iakerb_ctx_id_t ctx,
     else
         tokenSize = 2 + data->length;
 
-    token->value = q = gssalloc_malloc(tokenSize);
-    if (q == NULL) {
+    token->value = gssalloc_malloc(tokenSize);
+    if (token->value == NULL) {
         code = ENOMEM;
         goto cleanup;
     }
     token->length = tokenSize;
+    k5_buf_init_fixed(&buf, token->value, token->length);
 
     if (initialContextToken) {
-        g_make_token_header(gss_mech_iakerb, data->length, &q,
+        g_make_token_header(&buf, gss_mech_iakerb, data->length,
                             IAKERB_TOK_PROXY);
     } else {
-        store_16_be(IAKERB_TOK_PROXY, q);
-        q += 2;
+        k5_buf_add_uint16_be(&buf, IAKERB_TOK_PROXY);
     }
-    memcpy(q, data->data, data->length);
-    q += data->length;
-
-    assert(q == (unsigned char *)token->value + token->length);
+    k5_buf_add_len(&buf, data->data, data->length);
+    assert(buf.len == token->length);
 
 cleanup:
     krb5_free_data(ctx->k5c, data);
diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c
index f0f094ccb..83fd514c1 100644
--- a/src/lib/gssapi/krb5/init_sec_context.c
+++ b/src/lib/gssapi/krb5/init_sec_context.c
@@ -393,9 +393,9 @@ make_ap_req_v1(context, ctx, cred, k_cred, ad_context,
     struct gss_checksum_data cksum_struct;
     krb5_checksum md5;
     krb5_data ap_req;
-    unsigned char *ptr;
     unsigned char *t;
     unsigned int tlen;
+    struct k5buf buf;
 
     k5_mutex_assert_locked(&cred->lock);
     ap_req.data = 0;
@@ -447,19 +447,15 @@ make_ap_req_v1(context, ctx, cred, k_cred, ad_context,
     } else {
         /* allocate space for the token */
         tlen = g_token_size((gss_OID) mech_type, ap_req.length);
-
-        if ((t = (unsigned char *) gssalloc_malloc(tlen)) == NULL) {
+        t = gssalloc_malloc(tlen);
+        if (t == NULL) {
             code = ENOMEM;
             goto cleanup;
         }
-
-        /* fill in the buffer */
-        ptr = t;
-
-        g_make_token_header(mech_type, ap_req.length,
-                            &ptr, KG_TOK_CTX_AP_REQ);
-
-        TWRITE_STR(ptr, ap_req.data, ap_req.length);
+        k5_buf_init_fixed(&buf, t, tlen);
+        g_make_token_header(&buf, mech_type, ap_req.length, KG_TOK_CTX_AP_REQ);
+        k5_buf_add_len(&buf, ap_req.data, ap_req.length);
+        assert(buf.len == tlen);
 
         /* pass it back */
 
diff --git a/src/lib/gssapi/krb5/k5seal.c b/src/lib/gssapi/krb5/k5seal.c
index d1cdce486..99275be53 100644
--- a/src/lib/gssapi/krb5/k5seal.c
+++ b/src/lib/gssapi/krb5/k5seal.c
@@ -78,11 +78,11 @@ make_seal_token_v1 (krb5_context context,
      * tlen is the length of the token
      * including header. */
     unsigned int conflen=0, tmsglen, tlen, msglen;
-    unsigned char *t, *ptr;
+    unsigned char *t, *metadata, *checksum, *payload;
     unsigned char *plain;
     unsigned char pad;
     krb5_keyusage sign_usage = KG_USAGE_SIGN;
-
+    struct k5buf buf;
 
     assert((!do_encrypt) || (toktype == KG_TOK_SEAL_MSG));
     /* create the token buffer */
@@ -108,31 +108,37 @@ make_seal_token_v1 (krb5_context context,
         msglen = text->length;
         pad = 0;
     }
-    tlen = g_token_size((gss_OID) oid, 14+cksum_size+tmsglen);
 
-    if ((t = (unsigned char *) gssalloc_malloc(tlen)) == NULL)
+    tlen = g_token_size(oid, 14 + cksum_size + tmsglen);
+    t = gssalloc_malloc(tlen);
+    if (t == NULL)
         return(ENOMEM);
+    k5_buf_init_fixed(&buf, t, tlen);
 
     /*** fill in the token */
 
-    ptr = t;
-    g_make_token_header(oid, 14+cksum_size+tmsglen, &ptr, toktype);
+    g_make_token_header(&buf, oid, 14 + cksum_size + tmsglen, toktype);
+    metadata = k5_buf_get_space(&buf, 14);
+    checksum = k5_buf_get_space(&buf, cksum_size);
+    payload = k5_buf_get_space(&buf, tmsglen);
+    assert(metadata != NULL && checksum != NULL && payload != NULL);
+    assert(buf.len == tlen);
 
     /* 0..1 SIGN_ALG */
-    store_16_le(signalg, &ptr[0]);
+    store_16_le(signalg, &metadata[0]);
 
     /* 2..3 SEAL_ALG or Filler */
     if ((toktype == KG_TOK_SEAL_MSG) && do_encrypt) {
-        store_16_le(sealalg, &ptr[2]);
+        store_16_le(sealalg, &metadata[2]);
     } else {
         /* No seal */
-        ptr[2] = 0xff;
-        ptr[3] = 0xff;
+        metadata[2] = 0xFF;
+        metadata[3] = 0xFF;
     }
 
     /* 4..5 Filler */
-    ptr[4] = 0xff;
-    ptr[5] = 0xff;
+    metadata[4] = 0xFF;
+    metadata[5] = 0xFF;
 
     /* pad the plaintext, encrypt if needed, and stick it in the token */
 
@@ -183,8 +189,9 @@ make_seal_token_v1 (krb5_context context,
         gssalloc_free(t);
         return(ENOMEM);
     }
-    (void) memcpy(data_ptr, ptr-2, 8);
-    (void) memcpy(data_ptr+8, plain, msglen);
+    /* Checksum over the token ID, metadata bytes, and plaintext. */
+    memcpy(data_ptr, metadata - 2, 8);
+    memcpy(data_ptr + 8, plain, msglen);
     plaind.length = 8 + msglen;
     plaind.data = data_ptr;
     code = krb5_k_make_checksum(context, md5cksum.checksum_type, seq,
@@ -204,10 +211,10 @@ make_seal_token_v1 (krb5_context context,
          */
         if (md5cksum.length != cksum_size)
             abort ();
-        memcpy (ptr+14, md5cksum.contents, md5cksum.length);
+        memcpy(checksum, md5cksum.contents, md5cksum.length);
         break;
     case SGN_ALG_HMAC_MD5:
-        memcpy (ptr+14, md5cksum.contents, cksum_size);
+        memcpy(checksum, md5cksum.contents, cksum_size);
         break;
     }
 
@@ -215,8 +222,9 @@ make_seal_token_v1 (krb5_context context,
 
     /* create the seq_num */
 
-    if ((code = kg_make_seq_num(context, seq, direction?0:0xff,
-                                (krb5_ui_4)*seqnum, ptr+14, ptr+6))) {
+    code = kg_make_seq_num(context, seq, direction?0:0xff,
+                           (krb5_ui_4)*seqnum, checksum, metadata + 6);
+    if (code) {
         xfree (plain);
         gssalloc_free(t);
         return(code);
@@ -240,10 +248,8 @@ make_seal_token_v1 (krb5_context context,
             assert (enc_key->length == 16);
             for (i = 0; i <= 15; i++)
                 ((char *) enc_key->contents)[i] ^=0xf0;
-            code = kg_arcfour_docrypt (enc_key, 0,
-                                       bigend_seqnum, 4,
-                                       plain, tmsglen,
-                                       ptr+14+cksum_size);
+            code = kg_arcfour_docrypt(enc_key, 0, bigend_seqnum, 4, plain,
+                                      tmsglen, payload);
             krb5_free_keyblock (context, enc_key);
             if (code)
             {
@@ -254,10 +260,9 @@ make_seal_token_v1 (krb5_context context,
         }
         break;
         default:
-            if ((code = kg_encrypt(context, enc, KG_USAGE_SEAL, NULL,
-                                   (krb5_pointer) plain,
-                                   (krb5_pointer) (ptr+cksum_size+14),
-                                   tmsglen))) {
+            code = kg_encrypt(context, enc, KG_USAGE_SEAL, NULL,  plain,
+                              payload, tmsglen);
+            if (code) {
                 xfree(plain);
                 gssalloc_free(t);
                 return(code);
@@ -265,7 +270,7 @@ make_seal_token_v1 (krb5_context context,
         }
     }else {
         if (tmsglen)
-            memcpy(ptr+14+cksum_size, plain, tmsglen);
+            memcpy(payload, plain, tmsglen);
     }
     xfree(plain);
 
diff --git a/src/lib/gssapi/krb5/k5sealiov.c b/src/lib/gssapi/krb5/k5sealiov.c
index 9bb2ee109..7bf7609a4 100644
--- a/src/lib/gssapi/krb5/k5sealiov.c
+++ b/src/lib/gssapi/krb5/k5sealiov.c
@@ -44,9 +44,10 @@ make_seal_token_v1_iov(krb5_context context,
     krb5_checksum cksum;
     size_t k5_headerlen = 0, k5_trailerlen = 0;
     size_t data_length = 0, assoc_data_length = 0;
-    size_t tmsglen = 0, tlen;
-    unsigned char *ptr;
+    size_t tmsglen = 0, cnflen = 0, tlen;
+    uint8_t *metadata, *checksum, *confounder;
     krb5_keyusage sign_usage = KG_USAGE_SIGN;
+    struct k5buf buf;
 
     md5cksum.length = cksum.length = 0;
     md5cksum.contents = cksum.contents = NULL;
@@ -65,17 +66,15 @@ make_seal_token_v1_iov(krb5_context context,
         trailer->buffer.length = 0;
 
     /* Determine confounder length */
-    if (toktype == KG_TOK_WRAP_MSG || conf_req_flag)
-        k5_headerlen = kg_confounder_size(context, ctx->enc->keyblock.enctype);
-
-    /* Check padding length */
     if (toktype == KG_TOK_WRAP_MSG) {
         size_t k5_padlen = (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) ? 1 : 8;
         size_t gss_padlen;
         size_t conf_data_length;
 
+        cnflen = kg_confounder_size(context, ctx->enc->keyblock.enctype);
+
         kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length);
-        conf_data_length = k5_headerlen + data_length - assoc_data_length;
+        conf_data_length = cnflen + data_length - assoc_data_length;
 
         if (k5_padlen == 1)
             gss_padlen = 1; /* one byte to indicate one byte of padding */
@@ -103,7 +102,7 @@ make_seal_token_v1_iov(krb5_context context,
         }
 
         if (ctx->gss_flags & GSS_C_DCE_STYLE)
-            tmsglen = k5_headerlen; /* confounder length */
+            tmsglen = cnflen; /* confounder length */
         else
             tmsglen = conf_data_length + padding->buffer.length;
     }
@@ -111,7 +110,7 @@ make_seal_token_v1_iov(krb5_context context,
     /* Determine token size */
     tlen = g_token_size(ctx->mech_used, 14 + ctx->cksum_size + tmsglen);
 
-    k5_headerlen += tlen - tmsglen;
+    k5_headerlen = cnflen + tlen - tmsglen;
 
     if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
         code = kg_allocate_iov(header, k5_headerlen);
@@ -122,24 +121,28 @@ make_seal_token_v1_iov(krb5_context context,
 
     header->buffer.length = k5_headerlen;
 
-    ptr = (unsigned char *)header->buffer.value;
-    g_make_token_header(ctx->mech_used, 14 + ctx->cksum_size + tmsglen, &ptr, toktype);
+    k5_buf_init_fixed(&buf, header->buffer.value, k5_headerlen);
+    g_make_token_header(&buf, ctx->mech_used, 14 + ctx->cksum_size + tmsglen,
+                        toktype);
+    metadata = k5_buf_get_space(&buf, 14);
+    checksum = k5_buf_get_space(&buf, ctx->cksum_size);
+    assert(metadata != NULL && checksum != NULL);
 
     /* 0..1 SIGN_ALG */
-    store_16_le(ctx->signalg, &ptr[0]);
+    store_16_le(ctx->signalg, &metadata[0]);
 
     /* 2..3 SEAL_ALG or Filler */
     if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) {
-        store_16_le(ctx->sealalg, &ptr[2]);
+        store_16_le(ctx->sealalg, &metadata[2]);
     } else {
         /* No seal */
-        ptr[2] = 0xFF;
-        ptr[3] = 0xFF;
+        metadata[2] = 0xFF;
+        metadata[3] = 0xFF;
     }
 
     /* 4..5 Filler */
-    ptr[4] = 0xFF;
-    ptr[5] = 0xFF;
+    metadata[4] = 0xFF;
+    metadata[5] = 0xFF;
 
     /* pad the plaintext, encrypt if needed, and stick it in the token */
 
@@ -163,8 +166,10 @@ make_seal_token_v1_iov(krb5_context context,
     md5cksum.length = k5_trailerlen;
 
     if (k5_headerlen != 0 && toktype == KG_TOK_WRAP_MSG) {
+        confounder = k5_buf_get_space(&buf, cnflen);
+        assert(confounder != NULL);
         code = kg_make_confounder(context, ctx->enc->keyblock.enctype,
-                                  ptr + 14 + ctx->cksum_size);
+                                  confounder);
         if (code != 0)
             goto cleanup;
     }
@@ -180,16 +185,16 @@ make_seal_token_v1_iov(krb5_context context,
     switch (ctx->signalg) {
     case SGN_ALG_HMAC_SHA1_DES3_KD:
         assert(md5cksum.length == ctx->cksum_size);
-        memcpy(ptr + 14, md5cksum.contents, md5cksum.length);
+        memcpy(checksum, md5cksum.contents, md5cksum.length);
         break;
     case SGN_ALG_HMAC_MD5:
-        memcpy(ptr + 14, md5cksum.contents, ctx->cksum_size);
+        memcpy(checksum, md5cksum.contents, ctx->cksum_size);
         break;
     }
 
     /* create the seq_num */
     code = kg_make_seq_num(context, ctx->seq, ctx->initiate ? 0 : 0xFF,
-                           (OM_uint32)ctx->seq_send, ptr + 14, ptr + 6);
+                           (OM_uint32)ctx->seq_send, checksum, metadata + 6);
     if (code != 0)
         goto cleanup;
 
diff --git a/src/lib/gssapi/mechglue/g_encapsulate_token.c b/src/lib/gssapi/mechglue/g_encapsulate_token.c
index 850e3ee65..1ccd3cd73 100644
--- a/src/lib/gssapi/mechglue/g_encapsulate_token.c
+++ b/src/lib/gssapi/mechglue/g_encapsulate_token.c
@@ -38,7 +38,7 @@ gss_encapsulate_token(gss_const_buffer_t input_token,
                       gss_buffer_t output_token)
 {
     unsigned int tokenSize;
-    unsigned char *buf;
+    struct k5buf buf;
 
     if (input_token == GSS_C_NO_BUFFER || token_oid == GSS_C_NO_OID)
         return GSS_S_CALL_INACCESSIBLE_READ;
@@ -55,10 +55,10 @@ gss_encapsulate_token(gss_const_buffer_t input_token,
     if (output_token->value == NULL)
         return GSS_S_FAILURE;
 
-    buf = output_token->value;
-
-    g_make_token_header(token_oid, input_token->length, &buf, -1);
-    memcpy(buf, input_token->value, input_token->length);
+    k5_buf_init_fixed(&buf, output_token->value, tokenSize);
+    g_make_token_header(&buf, token_oid, input_token->length, -1);
+    k5_buf_add_len(&buf, input_token->value, input_token->length);
+    assert(buf.len == tokenSize);
     output_token->length = tokenSize;
 
     return GSS_S_COMPLETE;
diff --git a/src/lib/gssapi/mechglue/g_glue.c b/src/lib/gssapi/mechglue/g_glue.c
index dfef49e0c..176fbe63e 100644
--- a/src/lib/gssapi/mechglue/g_glue.c
+++ b/src/lib/gssapi/mechglue/g_glue.c
@@ -23,6 +23,7 @@
  */
 
 #include "mglueP.h"
+#include "k5-der.h"
 #include <stdio.h>
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
@@ -38,219 +39,24 @@ extern gss_mechanism *gssint_mechs_array;
  * This file contains the support routines for the glue layer.
  */
 
-/*
- * get_der_length: Givin a pointer to a buffer that contains a DER encoded
- * length, decode the length updating the buffer to point to the character
- * after the DER encoding. The parameter bytes will point to the number of
- * bytes that made up the DER encoding of the length originally pointed to
- * by the buffer. Note we return -1 on error.
- */
-int
-gssint_get_der_length(unsigned char **buf, unsigned int buf_len, unsigned int *bytes)
+/* Retrieve the mechanism OID from an RFC 2743 InitialContextToken.  Place
+ * the result into *oid_out, aliasing memory from token. */
+OM_uint32 gssint_get_mech_type_oid(gss_OID oid_out, gss_buffer_t token)
 {
-    /* p points to the beginning of the buffer */
-    unsigned char *p = *buf;
-    int length, new_length;
-    unsigned int octets;
-
-    if (buf_len < 1)
-	return (-1);
-
-    /* We should have at least one byte */
-    *bytes = 1;
-
-    /*
-     * If the High order bit is not set then the length is just the value
-     * of *p.
-     */
-    if (*p < 128) {
-	*buf = p+1;	/* Advance the buffer */
-	return (*p);		/* return the length */
-    }
-
-    /*
-     * if the High order bit is set, then the low order bits represent
-     * the number of bytes that contain the DER encoding of the length.
-     */
+    struct k5input in;
 
-    octets = *p++ & 0x7f;
-    *bytes += octets;
-
-    /* See if the supplied buffer contains enough bytes for the length. */
-    if (octets > buf_len - 1)
-	return (-1);
-
-    /*
-     * Calculate a multibyte length. The length is encoded as an
-     * unsigned integer base 256.
-     */
-    for (length = 0; octets; octets--) {
-	new_length = (length << 8) + *p++;
-	if (new_length < length)  /* overflow */
-	    return (-1);
-	length = new_length;
-    }
-
-    *buf = p; /* Advance the buffer */
-
-    return (length);
-}
-
-/*
- * der_length_size: Return the number of bytes to encode a given length.
- */
-unsigned int
-gssint_der_length_size(unsigned int len)
-{
-    int i;
-
-    if (len < 128)
-	return (1);
-
-    for (i = 0; len; i++) {
-	len >>= 8;
-    }
-
-    return (i+1);
-}
-
-/*
- * put_der_length: Encode the supplied length into the buffer pointed to
- * by buf. max_length represents the maximum length of the buffer pointed
- * to by buff. We will advance buf to point to the character after the newly
- * DER encoded length. We return 0 on success or -l it the length cannot
- * be encoded in max_len characters.
- */
-int
-gssint_put_der_length(unsigned int length, unsigned char **buf, unsigned int max_len)
-{
-    unsigned char *s, *p;
-    unsigned int buf_len = 0;
-    int i, first;
-
-    /* Oops */
-    if (buf == 0 || max_len < 1)
-	return (-1);
-
-    s = *buf;
-
-    /* Single byte is the length */
-    if (length < 128) {
-	*s++ = length;
-	*buf = s;
-	return (0);
-    }
-
-    /* First byte contains the number of octets */
-    p = s + 1;
-
-    /* Running total of the DER encoding length */
-    buf_len = 0;
-
-    /*
-     * Encode MSB first. We do the encoding by setting a shift
-     * factor to MSO_BIT (24 for 32 bit words) and then shifting the length
-     * by the factor. We then encode the resulting low order byte.
-     * We subtract 8 from the shift factor and repeat to ecnode the next
-     * byte. We stop when the shift factor is zero or we've run out of
-     * buffer to encode into.
-     */
-    first = 0;
-    for (i = MSO_BIT; i >= 0 && buf_len <= max_len; i -= 8) {
-	unsigned int v;
-	v = (length >> i) & 0xff;
-	if ((v) || first) {
-	    buf_len += 1;
-	    *p++ = v;
-	    first = 1;
-	}
-    }
-    if (i >= 0)			/* buffer overflow */
-	return (-1);
-
-    /*
-     * We go back now and set the first byte to be the length with
-     * the high order bit set.
-     */
-    *s = buf_len | 0x80;
-    *buf = p;
-
-    return (0);
-}
-
-
-/*
- *  glue routine for get_mech_type
- *
- */
-
-OM_uint32 gssint_get_mech_type_oid(OID, token)
-    gss_OID		OID;
-    gss_buffer_t	token;
-{
-    unsigned char * buffer_ptr;
-    size_t buflen, lenbytes, length, oidlen;
-
-    /*
-     * This routine reads the prefix of "token" in order to determine
-     * its mechanism type. It assumes the encoding suggested in
-     * Appendix B of RFC 1508. This format starts out as follows :
-     *
-     * tag for APPLICATION 0, Sequence[constructed, definite length]
-     * length of remainder of token
-     * tag of OBJECT IDENTIFIER
-     * length of mechanism OID
-     * encoding of mechanism OID
-     * <the rest of the token>
-     *
-     * Numerically, this looks like :
-     *
-     * 0x60
-     * <length> - could be multiple bytes
-     * 0x06
-     * <length> - assume only one byte, hence OID length < 127
-     * <mech OID bytes>
-     *
-     * The routine fills in the OID value and returns an error as necessary.
-     */
-
-	if (OID == NULL)
-		return (GSS_S_CALL_INACCESSIBLE_WRITE);
-
-	if ((token == NULL) || (token->value == NULL))
-	return (GSS_S_DEFECTIVE_TOKEN);
-
-    /* Skip past the APP/Sequnce byte and the token length */
-
-    buffer_ptr = (unsigned char *) token->value;
-    buflen = token->length;
-
-    if (buflen < 2 || *buffer_ptr++ != 0x60)
+    if (oid_out == NULL)
+	return (GSS_S_CALL_INACCESSIBLE_WRITE);
+    if (token == NULL || token->value == NULL)
 	return (GSS_S_DEFECTIVE_TOKEN);
-    length = *buffer_ptr++;
-    buflen -= 2;
-
-	/* check if token length is null */
-	if (length == 0)
-	    return (GSS_S_DEFECTIVE_TOKEN);
-
-    if (length & 0x80) {
-	lenbytes = length & 0x7f;
-	if (lenbytes > 4 || lenbytes > buflen)
-	    return (GSS_S_DEFECTIVE_TOKEN);
-	buffer_ptr += lenbytes;
-	buflen -= lenbytes;
-    }
 
-    if (buflen < 2 || *buffer_ptr++ != 0x06)
+    k5_input_init(&in, token->value, token->length);
+    if (!k5_der_get_value(&in, 0x60, &in))
 	return (GSS_S_DEFECTIVE_TOKEN);
-    oidlen = *buffer_ptr++;
-    buflen -= 2;
-    if (oidlen > 0x7f || oidlen > buflen)
+    if (!k5_der_get_value(&in, 0x06, &in))
 	return (GSS_S_DEFECTIVE_TOKEN);
-
-    OID->length = oidlen;
-    OID->elements = (void *) buffer_ptr;
+    oid_out->length = in.len;
+    oid_out->elements = (uint8_t *)in.ptr;
     return (GSS_S_COMPLETE);
 }
 
@@ -425,12 +231,8 @@ OM_uint32 gssint_export_internal_name(minor_status, mech_type,
     gss_mechanism mech;
     gss_buffer_desc dispName;
     gss_OID nameOid;
-    unsigned char *buf = NULL;
-    const unsigned char tokId[] = "\x04\x01";
-    const unsigned int tokIdLen = 2;
-    const int mechOidLenLen = 2, mechOidTagLen = 1, nameLenLen = 4;
-    int mechOidDERLen = 0;
-    int mechOidLen = 0;
+    int mech_der_len = 0;
+    struct k5buf buf;
 
     mech = gssint_get_mechanism(mech_type);
     if (!mech)
@@ -481,52 +283,24 @@ OM_uint32 gssint_export_internal_name(minor_status, mech_type,
 	return (status);
     }
 
-    /* determine the size of the buffer needed */
-    mechOidDERLen = gssint_der_length_size(mech_type->length);
-    name_buf->length = tokIdLen + mechOidLenLen +
-	mechOidTagLen + mechOidDERLen +
-	mech_type->length +
-	nameLenLen + dispName.length;
-    if ((name_buf->value = (void*)gssalloc_malloc(name_buf->length)) ==
-	(void*)NULL) {
+    /* Allocate space and prepare a buffer. */
+    mech_der_len = k5_der_value_len(mech_type->length);
+    name_buf->length = 2 + 2 + mech_der_len + 4 + dispName.length;
+    name_buf->value = gssalloc_malloc(name_buf->length);
+    if (name_buf->value == NULL) {
 	name_buf->length = 0;
 	(void) gss_release_buffer(&status, &dispName);
 	return (GSS_S_FAILURE);
     }
-
-    /* now create the name ..... */
-    buf = (unsigned char *)name_buf->value;
-    (void) memset(name_buf->value, 0, name_buf->length);
-    (void) memcpy(buf, tokId, tokIdLen);
-    buf += tokIdLen;
-
-    /* spec allows only 2 bytes for the mech oid length */
-    mechOidLen = mechOidDERLen + mechOidTagLen + mech_type->length;
-    store_16_be(mechOidLen, buf);
-    buf += 2;
-
-    /*
-     * DER Encoding of mech OID contains OID Tag (0x06), length and
-     * mech OID value
-     */
-    *buf++ = 0x06;
-    if (gssint_put_der_length(mech_type->length, &buf,
-		       (name_buf->length - tokIdLen -2)) != 0) {
-	name_buf->length = 0;
-	free(name_buf->value);
-	(void) gss_release_buffer(&status, &dispName);
-	return (GSS_S_FAILURE);
-    }
-
-    (void) memcpy(buf, mech_type->elements, mech_type->length);
-    buf += mech_type->length;
-
-    /* spec designates the next 4 bytes for the name length */
-    store_32_be(dispName.length, buf);
-    buf += 4;
-
-    /* for the final ingredient - add the name from gss_display_name */
-    (void) memcpy(buf, dispName.value, dispName.length);
+    k5_buf_init_fixed(&buf, name_buf->value, name_buf->length);
+
+    /* Assemble the name. */
+    k5_buf_add_len(&buf, "\x04\x01", 2);
+    k5_buf_add_uint16_be(&buf, mech_der_len);
+    k5_der_add_value(&buf, 0x06, mech_type->elements, mech_type->length);
+    k5_buf_add_uint32_be(&buf, dispName.length);
+    k5_buf_add_len(&buf, dispName.value, dispName.length);
+    assert(buf.len == name_buf->length);
 
     /* release the buffer obtained from gss_display_name */
     (void) gss_release_buffer(minor_status, &dispName);
diff --git a/src/lib/gssapi/mechglue/g_imp_name.c b/src/lib/gssapi/mechglue/g_imp_name.c
index c3e809c87..a805078a8 100644
--- a/src/lib/gssapi/mechglue/g_imp_name.c
+++ b/src/lib/gssapi/mechglue/g_imp_name.c
@@ -28,6 +28,7 @@
  */
 
 #include "mglueP.h"
+#include "k5-der.h"
 #include <stdio.h>
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
@@ -181,13 +182,6 @@ allocation_failure:
     return (major_status);
 }
 
-/*
- * GSS export name constants
- */
-static const unsigned int expNameTokIdLen = 2;
-static const unsigned int mechOidLenLen = 2;
-static const unsigned int nameTypeLenLen = 2;
-
 static OM_uint32
 importExportName(minor, unionName, inputNameType)
     OM_uint32 *minor;
@@ -196,59 +190,31 @@ importExportName(minor, unionName, inputNameType)
 {
     gss_OID_desc mechOid;
     gss_buffer_desc expName;
-    unsigned char *buf;
     gss_mechanism mech;
-    OM_uint32 major, mechOidLen, nameLen, curLength;
-    unsigned int bytes;
+    OM_uint32 major, mechOidLen, nameLen;
+    uint8_t b2;
+    const uint8_t *name;
+    struct k5input in, oid, old_format;
 
     expName.value = unionName->external_name->value;
     expName.length = unionName->external_name->length;
+    k5_input_init(&in, expName.value, expName.length);
 
-    curLength = expNameTokIdLen + mechOidLenLen;
-    if (expName.length < curLength)
+    if (k5_input_get_byte(&in) != 0x04)
 	return (GSS_S_DEFECTIVE_TOKEN);
-
-    buf = (unsigned char *)expName.value;
-    if (buf[0] != 0x04)
-	return (GSS_S_DEFECTIVE_TOKEN);
-    if (buf[1] != 0x01 && buf[1] != 0x02) /* allow composite names */
+    b2 = k5_input_get_byte(&in);
+    if (b2 != 0x01 && b2 != 0x02) /* allow composite names */
 	return (GSS_S_DEFECTIVE_TOKEN);
 
-    buf += expNameTokIdLen;
+    mechOidLen = k5_input_get_uint16_be(&in);
 
-    /* extract the mechanism oid length */
-    mechOidLen = (*buf++ << 8);
-    mechOidLen |= (*buf++);
-    curLength += mechOidLen;
-    if (expName.length < curLength)
+    if (!k5_der_get_value(&in, 0x06, &oid))
 	return (GSS_S_DEFECTIVE_TOKEN);
-    /*
-     * The mechOid itself is encoded in DER format, OID Tag (0x06)
-     * length and the value of mech_OID
-     */
-    if (*buf++ != 0x06)
-	return (GSS_S_DEFECTIVE_TOKEN);
-
-    /*
-     * mechoid Length is encoded twice; once in 2 bytes as
-     * explained in RFC2743 (under mechanism independent exported
-     * name object format) and once using DER encoding
-     *
-     * We verify both lengths.
-     */
-
-    mechOid.length = gssint_get_der_length(&buf,
-				    (expName.length - curLength), &bytes);
-    mechOid.elements = (void *)buf;
-
-    /*
-     * 'bytes' is the length of the DER length, '1' is for the DER
-     * tag for OID
-     */
-    if ((bytes + mechOid.length + 1) != mechOidLen)
+    /* Verify that mechOidLen is consistent with the DER OID length. */
+    if (mechOidLen != k5_der_value_len(oid.len))
 	return (GSS_S_DEFECTIVE_TOKEN);
-
-    buf += mechOid.length;
+    mechOid.length = oid.len;
+    mechOid.elements = (uint8_t *)oid.ptr;
     if ((mech = gssint_get_mechanism(&mechOid)) == NULL)
 	return (GSS_S_BAD_MECH);
 
@@ -297,21 +263,11 @@ importExportName(minor, unionName, inputNameType)
      * that included a null terminator which was counted in the
      * display name gss_buffer_desc.
      */
-    curLength += 4;		/* 4 bytes for name len */
-    if (expName.length < curLength)
-	return (GSS_S_DEFECTIVE_TOKEN);
 
     /* next 4 bytes in the name are the name length */
-    nameLen = load_32_be(buf);
-    buf += 4;
-
-    /*
-     * we use < here because bad code in rpcsec_gss rounds up exported
-     * name token lengths and pads with nulls, otherwise != would be
-     * appropriate
-     */
-    curLength += nameLen;   /* this is the total length */
-    if (expName.length < curLength)
+    nameLen = k5_input_get_uint32_be(&in);
+    name = k5_input_get_bytes(&in, nameLen);
+    if (name == NULL)
 	return (GSS_S_DEFECTIVE_TOKEN);
 
     /*
@@ -324,29 +280,19 @@ importExportName(minor, unionName, inputNameType)
      * and length) there's the name itself, though null-terminated;
      * this null terminator should also not be there, but it is.
      */
-    if (nameLen > 0 && *buf == '\0') {
+    if (nameLen > 0 && *name == '\0') {
 	OM_uint32 nameTypeLen;
-	/* next two bytes are the name oid */
-	if (nameLen < nameTypeLenLen)
-	    return (GSS_S_DEFECTIVE_TOKEN);
-
-	nameLen -= nameTypeLenLen;
 
-	nameTypeLen = (*buf++) << 8;
-	nameTypeLen |= (*buf++);
-
-	if (nameLen < nameTypeLen)
+	/* Skip the name type. */
+	k5_input_init(&old_format, name, nameLen);
+	nameTypeLen = k5_input_get_uint16_be(&old_format);
+	if (k5_input_get_bytes(&old_format, nameTypeLen) == NULL)
 	    return (GSS_S_DEFECTIVE_TOKEN);
-
-	buf += nameTypeLen;
-	nameLen -= nameTypeLen;
-
-	/*
-	 * adjust for expected null terminator that should
-	 * really not be there
-	 */
-	if (nameLen > 0 && *(buf + nameLen - 1) == '\0')
-	    nameLen--;
+	/* Remove a null terminator if one is present. */
+	if (old_format.len > 0 && old_format.ptr[old_format.len - 1] == 0)
+	    old_format.len--;
+	name = old_format.ptr;
+	nameLen = old_format.len;
     }
 
     /*
@@ -365,7 +311,7 @@ importExportName(minor, unionName, inputNameType)
      *	 IDN is thrown in with Kerberos V extensions).
      */
     expName.length = nameLen;
-    expName.value = nameLen ? (void *)buf : NULL;
+    expName.value = nameLen ? (uint8_t *)name : NULL;
     if (mech->gssspi_import_name_by_mech) {
 	major = mech->gssspi_import_name_by_mech(minor, &mechOid, &expName,
 						 GSS_C_NULL_OID,
diff --git a/src/lib/gssapi/mechglue/mglueP.h b/src/lib/gssapi/mechglue/mglueP.h
index dbd244b72..7f836fbb0 100644
--- a/src/lib/gssapi/mechglue/mglueP.h
+++ b/src/lib/gssapi/mechglue/mglueP.h
@@ -819,23 +819,6 @@ OM_uint32 gss_add_mech_name_type
  * Sun extensions to GSS-API v2
  */
 
-int
-gssint_get_der_length(
-	unsigned char **,	/* buf */
-	unsigned int,		/* buf_len */
-	unsigned int *		/* bytes */
-);
-
-unsigned int
-gssint_der_length_size(unsigned int /* len */);
-
-int
-gssint_put_der_length(
-	unsigned int,		/* length */
-	unsigned char **,	/* buf */
-	unsigned int		/* max_len */
-);
-
 OM_uint32
 gssint_wrap_aead (gss_mechanism,	/* mech */
 		  OM_uint32 *,		/* minor_status */
diff --git a/src/lib/gssapi/spnego/spnego_mech.c b/src/lib/gssapi/spnego/spnego_mech.c
index 654964c62..bdd75868a 100644
--- a/src/lib/gssapi/spnego/spnego_mech.c
+++ b/src/lib/gssapi/spnego/spnego_mech.c
@@ -60,40 +60,24 @@
 /* #pragma ident	"@(#)spnego_mech.c	1.7	04/09/28 SMI" */
 
 #include	<k5-int.h>
+#include	<k5-der.h>
 #include	<krb5.h>
 #include	<mglueP.h>
 #include	"gssapiP_spnego.h"
 #include	<gssapi_err_generic.h>
 
 
-#undef g_token_size
-#undef g_verify_token_header
-#undef g_make_token_header
-
 #define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)
 typedef const gss_OID_desc *gss_OID_const;
 
-/* der routines defined in libgss */
-extern unsigned int gssint_der_length_size(unsigned int);
-extern int gssint_get_der_length(unsigned char **, unsigned int,
-				 unsigned int*);
-extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int);
-
-
 /* private routines for spnego_mechanism */
 static spnego_token_t make_spnego_token(const char *);
 static gss_buffer_desc make_err_msg(const char *);
-static int g_token_size(gss_OID_const, unsigned int);
-static int g_make_token_header(gss_OID_const, unsigned int,
-			       unsigned char **, unsigned int);
-static int g_verify_token_header(gss_OID_const, unsigned int *,
-				 unsigned char **,
-				 int, unsigned int);
-static int g_verify_neg_token_init(unsigned char **, unsigned int);
-static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);
-static gss_buffer_t get_input_token(unsigned char **, unsigned int);
-static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);
-static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);
+static int verify_token_header(struct k5input *, gss_OID_const);
+static gss_OID get_mech_oid(OM_uint32 *minor_status, struct k5input *);
+static gss_buffer_t get_octet_string(struct k5input *);
+static gss_OID_set get_mech_set(OM_uint32 *, struct k5input *);
+static OM_uint32 get_req_flags(struct k5input *, OM_uint32 *);
 static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
 				     gss_const_key_value_set_t,
 				     gss_cred_id_t *, gss_OID_set *,
@@ -103,9 +87,6 @@ static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_ctx_id_t,
 static void release_spnego_ctx(spnego_gss_ctx_id_t *);
 static spnego_gss_ctx_id_t create_spnego_ctx(int);
 static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);
-static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);
-static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);
-static int put_negResult(unsigned char **, OM_uint32, unsigned int);
 
 static OM_uint32
 process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
@@ -150,8 +131,6 @@ acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
 
 static gss_OID
 negotiate_mech(spnego_gss_ctx_id_t, gss_OID_set, OM_uint32 *);
-static int
-g_get_tag_and_length(unsigned char **, int, unsigned int, unsigned int *);
 
 static int
 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
@@ -159,8 +138,8 @@ make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
 			gss_buffer_t,
 			OM_uint32, gss_buffer_t, send_token_flag,
 			gss_buffer_t);
-static int
-make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
+static OM_uint32
+make_spnego_tokenTarg_msg(uint8_t, gss_OID, gss_buffer_t,
 			gss_buffer_t, send_token_flag,
 			gss_buffer_t);
 
@@ -169,8 +148,8 @@ get_negTokenInit(OM_uint32 *, gss_buffer_t, gss_buffer_t,
 		 gss_OID_set *, OM_uint32 *, gss_buffer_t *,
 		 gss_buffer_t *);
 static OM_uint32
-get_negTokenResp(OM_uint32 *, unsigned char *, unsigned int,
-		 OM_uint32 *, gss_OID *, gss_buffer_t *, gss_buffer_t *);
+get_negTokenResp(OM_uint32 *, struct k5input *, OM_uint32 *, gss_OID *,
+		 gss_buffer_t *, gss_buffer_t *);
 
 static int
 is_kerb_mech(gss_OID oid);
@@ -189,7 +168,6 @@ const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0;
 static gss_OID_desc negoex_mech = { NEGOEX_OID_LENGTH, NEGOEX_OID };
 
 static int make_NegHints(OM_uint32 *, gss_buffer_t *);
-static int put_neg_hints(unsigned char **, gss_buffer_t, unsigned int);
 static OM_uint32
 acc_ctx_hints(OM_uint32 *, spnego_gss_cred_id_t, gss_buffer_t *, OM_uint32 *,
 	      send_token_flag *, spnego_gss_ctx_id_t *);
@@ -720,15 +698,15 @@ init_ctx_cont(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
 	      send_token_flag *tokflag)
 {
 	OM_uint32 ret, tmpmin;
-	unsigned char *ptr;
 	gss_OID supportedMech = GSS_C_NO_OID;
+	struct k5input in;
 
 	*acc_negState = UNSPECIFIED;
 	*tokflag = ERROR_TOKEN_SEND;
 
-	ptr = buf->value;
-	ret = get_negTokenResp(minor_status, ptr, buf->length, acc_negState,
-			       &supportedMech, responseToken, mechListMIC);
+	k5_input_init(&in, buf->value, buf->length);
+	ret = get_negTokenResp(minor_status, &in, acc_negState, &supportedMech,
+			       responseToken, mechListMIC);
 	if (ret != GSS_S_COMPLETE)
 		goto cleanup;
 
@@ -1178,33 +1156,6 @@ static const gss_OID_desc gss_mech_krb5_oid =
 static const gss_OID_desc gss_mech_krb5_wrong_oid =
 	{ 9, "\052\206\110\202\367\022\001\002\002" };
 
-/*
- * verify that the input token length is not 0. If it is, just return.
- * If the token length is greater than 0, der encode as a sequence
- * and place in buf_out, advancing buf_out.
- */
-
-static int
-put_neg_hints(unsigned char **buf_out, gss_buffer_t input_token,
-	      unsigned int buflen)
-{
-	int ret;
-
-	/* if token length is 0, we do not want to send */
-	if (input_token->length == 0)
-		return (0);
-
-	if (input_token->length > buflen)
-		return (-1);
-
-	*(*buf_out)++ = SEQUENCE;
-	if ((ret = gssint_put_der_length(input_token->length, buf_out,
-			    input_token->length)))
-		return (ret);
-	TWRITE_STR(*buf_out, input_token->value, input_token->length);
-	return (0);
-}
-
 /*
  * NegHints ::= SEQUENCE {
  *    hintName       [0]  GeneralString      OPTIONAL,
@@ -1221,42 +1172,28 @@ static int
 make_NegHints(OM_uint32 *minor_status, gss_buffer_t *outbuf)
 {
 	OM_uint32 major_status;
-	unsigned int tlen = 0;
-	unsigned int hintNameSize = 0;
-	unsigned char *ptr;
-	unsigned char *t;
+	size_t hint_len, tlen;
+	uint8_t *t;
 	const char *hintname = "not_defined_in_RFC4178 at please_ignore";
 	const size_t hintname_len = strlen(hintname);
+	struct k5buf buf;
 
 	*outbuf = GSS_C_NO_BUFFER;
 	major_status = GSS_S_FAILURE;
 
-	/* Length of DER encoded GeneralString */
-	tlen = 1 + gssint_der_length_size(hintname_len) + hintname_len;
-	hintNameSize = tlen;
-
-	/* Length of DER encoded hintName */
-	tlen += 1 + gssint_der_length_size(hintNameSize);
+	hint_len = k5_der_value_len(hintname_len);
+	tlen = k5_der_value_len(hint_len);
 
 	t = gssalloc_malloc(tlen);
 	if (t == NULL) {
 		*minor_status = ENOMEM;
 		goto errout;
 	}
+	k5_buf_init_fixed(&buf, t, tlen);
 
-	ptr = t;
-
-	*ptr++ = CONTEXT | 0x00; /* hintName identifier */
-	if (gssint_put_der_length(hintNameSize,
-				  &ptr, tlen - (int)(ptr-t)))
-		goto errout;
-
-	*ptr++ = GENERAL_STRING;
-	if (gssint_put_der_length(hintname_len, &ptr, tlen - (int)(ptr-t)))
-		goto errout;
-
-	memcpy(ptr, hintname, hintname_len);
-	ptr += hintname_len;
+	k5_der_add_taglen(&buf, CONTEXT | 0x00, hint_len);
+	k5_der_add_value(&buf, GENERAL_STRING, hintname, hintname_len);
+	assert(buf.len == tlen);
 
 	*outbuf = (gss_buffer_t)malloc(sizeof(gss_buffer_desc));
 	if (*outbuf == NULL) {
@@ -1264,7 +1201,7 @@ make_NegHints(OM_uint32 *minor_status, gss_buffer_t *outbuf)
 		goto errout;
 	}
 	(*outbuf)->value = (void *)t;
-	(*outbuf)->length = ptr - t;
+	(*outbuf)->length = tlen;
 
 	t = NULL; /* don't free */
 
@@ -1431,8 +1368,7 @@ acc_ctx_cont(OM_uint32 *minstat,
 {
 	OM_uint32 ret, tmpmin;
 	gss_OID supportedMech;
-	unsigned int len;
-	unsigned char *ptr, *bufstart;
+	struct k5input in;
 
 	ret = GSS_S_DEFECTIVE_TOKEN;
 	*negState = REJECT;
@@ -1441,27 +1377,18 @@ acc_ctx_cont(OM_uint32 *minstat,
 	*return_token = ERROR_TOKEN_SEND;
 	*responseToken = *mechListMIC = GSS_C_NO_BUFFER;
 
-	ptr = bufstart = buf->value;
-#define REMAIN (buf->length - (ptr - bufstart))
-	if (REMAIN == 0 || REMAIN > INT_MAX)
-		return GSS_S_DEFECTIVE_TOKEN;
+	k5_input_init(&in, buf->value, buf->length);
 
-	/*
-	 * Attempt to work with old Sun SPNEGO.
-	 */
-	if (*ptr == HEADER_ID) {
-		ret = g_verify_token_header(gss_mech_spnego,
-					    &len, &ptr, 0, REMAIN);
+	/* Attempt to work with old Sun SPNEGO. */
+	if (in.len > 0 && *in.ptr == HEADER_ID) {
+		ret = verify_token_header(&in, gss_mech_spnego);
 		if (ret) {
 			*minstat = ret;
 			return GSS_S_DEFECTIVE_TOKEN;
 		}
 	}
-	if (*ptr != (CONTEXT | 0x01)) {
-		return GSS_S_DEFECTIVE_TOKEN;
-	}
-	ret = get_negTokenResp(minstat, ptr, REMAIN,
-			       negState, &supportedMech,
+
+	ret = get_negTokenResp(minstat, &in, negState, &supportedMech,
 			       responseToken, mechListMIC);
 	if (ret != GSS_S_COMPLETE)
 		goto cleanup;
@@ -1484,7 +1411,6 @@ cleanup:
 		generic_gss_release_oid(&tmpmin, &supportedMech);
 	}
 	return ret;
-#undef REMAIN
 }
 
 /*
@@ -3329,34 +3255,24 @@ cleanup:
 /* following are token creation and reading routines */
 
 /*
- * If buff_in is not pointing to a MECH_OID, then return NULL and do not
- * advance the buffer, otherwise, decode the mech_oid from the buffer and
- * place in gss_OID.
+ * If in contains a tagged OID encoding, return a copy of the contents as a
+ * gss_OID and advance in past the encoding.  Otherwise return NULL and do not
+ * advance in.
  */
 static gss_OID
-get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
+get_mech_oid(OM_uint32 *minor_status, struct k5input *in)
 {
-	OM_uint32	status;
-	gss_OID_desc 	toid;
-	gss_OID		mech_out = NULL;
-	unsigned int	bytes;
-	int		oid_length;
-
-	if (length < 1 || **buff_in != MECH_OID)
-		return (NULL);
-	(*buff_in)++;
-	length--;
+	struct k5input oidrep;
+	OM_uint32 status;
+	gss_OID_desc oid;
+	gss_OID mech_out = NULL;
 
-	oid_length = gssint_get_der_length(buff_in, length, &bytes);
-	if (oid_length < 0 || length - bytes < (size_t)oid_length)
+	if (!k5_der_get_value(in, MECH_OID, &oidrep))
 		return (NULL);
 
-	toid.length = oid_length;
-	toid.elements = *buff_in;
-	*buff_in += toid.length;
-
-	status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
-
+	oid.length = oidrep.len;
+	oid.elements = (uint8_t *)oidrep.ptr;
+	status = generic_gss_copy_oid(minor_status, &oid, &mech_out);
 	if (status != GSS_S_COMPLETE) {
 		map_errcode(minor_status);
 		mech_out = NULL;
@@ -3366,42 +3282,24 @@ get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
 }
 
 /*
- * der encode the given mechanism oid into buf_out, advancing the
- * buffer pointer.
- */
-
-static int
-put_mech_oid(unsigned char **buf_out, gss_OID_const mech, unsigned int buflen)
-{
-	if (buflen < mech->length + 2)
-		return (-1);
-	*(*buf_out)++ = MECH_OID;
-	*(*buf_out)++ = (unsigned char) mech->length;
-	memcpy(*buf_out, mech->elements, mech->length);
-	*buf_out += mech->length;
-	return (0);
-}
-
-/*
- * verify that buff_in points to an octet string, if it does not,
- * return NULL and don't advance the pointer. If it is an octet string
- * decode buff_in into a gss_buffer_t and return it, advancing the
- * buffer pointer.
+ * If in contains a tagged octet string encoding, return a copy of the contents
+ * as a gss_buffer_t and advance in past the encoding.  Otherwise return NULL
+ * and do not advance in.
  */
 static gss_buffer_t
-get_input_token(unsigned char **buff_in, unsigned int buff_length)
+get_octet_string(struct k5input *in)
 {
 	gss_buffer_t input_token;
-	unsigned int len;
+	struct k5input ostr;
 
-	if (g_get_tag_and_length(buff_in, OCTET_STRING, buff_length, &len) < 0)
+	if (!k5_der_get_value(in, OCTET_STRING, &ostr))
 		return (NULL);
 
 	input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
 	if (input_token == NULL)
 		return (NULL);
 
-	input_token->length = len;
+	input_token->length = ostr.len;
 	if (input_token->length > 0) {
 		input_token->value = gssalloc_malloc(input_token->length);
 		if (input_token->value == NULL) {
@@ -3409,41 +3307,13 @@ get_input_token(unsigned char **buff_in, unsigned int buff_length)
 			return (NULL);
 		}
 
-		memcpy(input_token->value, *buff_in, input_token->length);
+		memcpy(input_token->value, ostr.ptr, input_token->length);
 	} else {
 		input_token->value = NULL;
 	}
-	*buff_in += input_token->length;
 	return (input_token);
 }
 
-/*
- * verify that the input token length is not 0. If it is, just return.
- * If the token length is greater than 0, der encode as an octet string
- * and place in buf_out, advancing buf_out.
- */
-
-static int
-put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
-		unsigned int buflen)
-{
-	int ret;
-
-	/* if token length is 0, we do not want to send */
-	if (input_token->length == 0)
-		return (0);
-
-	if (input_token->length > buflen)
-		return (-1);
-
-	*(*buf_out)++ = OCTET_STRING;
-	if ((ret = gssint_put_der_length(input_token->length, buf_out,
-			    input_token->length)))
-		return (ret);
-	TWRITE_STR(*buf_out, input_token->value, input_token->length);
-	return (0);
-}
-
 /*
  * verify that buff_in points to a sequence of der encoding. The mech
  * set is the only sequence of encoded object in the token, so if it is
@@ -3451,46 +3321,35 @@ put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
  * return it, advancing the buffer pointer.
  */
 static gss_OID_set
-get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in,
-	     unsigned int buff_length)
+get_mech_set(OM_uint32 *minor_status, struct k5input *in)
 {
 	gss_OID_set returned_mechSet;
 	OM_uint32 major_status, tmpmin;
-	int length;
-	unsigned int bytes;
-	OM_uint32 set_length;
-	unsigned char		*start;
-	int i;
+	struct k5input seq;
 
-	if (buff_length < 1 || **buff_in != SEQUENCE_OF)
+	if (!k5_der_get_value(in, SEQUENCE_OF, &seq))
 		return (NULL);
 
-	start = *buff_in;
-	(*buff_in)++;
-
-	length = gssint_get_der_length(buff_in, buff_length - 1, &bytes);
-	if (length < 0 || buff_length - 1 - bytes < (unsigned int)length)
-		return NULL;
-
 	major_status = gss_create_empty_oid_set(minor_status,
 						&returned_mechSet);
 	if (major_status != GSS_S_COMPLETE)
 		return (NULL);
 
-	for (set_length = 0, i = 0; set_length < (unsigned int)length; i++) {
-		gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
-			buff_length - (*buff_in - start));
-		if (temp == NULL)
-			break;
+	while (!seq.status && seq.len > 0) {
+		gss_OID_desc *oid = get_mech_oid(minor_status, &seq);
+
+		if (oid == NULL) {
+			gss_release_oid_set(&tmpmin, &returned_mechSet);
+			return (NULL);
+		}
 
 		major_status = gss_add_oid_set_member(minor_status,
-						      temp, &returned_mechSet);
-		generic_gss_release_oid(minor_status, &temp);
+						      oid, &returned_mechSet);
+		generic_gss_release_oid(minor_status, &oid);
 		if (major_status != GSS_S_COMPLETE) {
 			gss_release_oid_set(&tmpmin, &returned_mechSet);
 			return (NULL);
 		}
-		set_length += returned_mechSet->elements[i].length + 2;
 	}
 
 	return (returned_mechSet);
@@ -3500,74 +3359,48 @@ get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in,
  * Encode mechSet into buf.
  */
 static int
-put_mech_set(gss_OID_set mechSet, gss_buffer_t buf)
+put_mech_set(gss_OID_set mechSet, gss_buffer_t buffer_out)
 {
-	unsigned char *ptr;
-	unsigned int i;
-	unsigned int tlen, ilen;
+	uint8_t *ptr;
+	size_t ilen, tlen, i;
+	struct k5buf buf;
+
+	ilen = 0;
+	for (i = 0; i < mechSet->count; i++)
+	    ilen += k5_der_value_len(mechSet->elements[i].length);
+	tlen = k5_der_value_len(ilen);
 
-	tlen = ilen = 0;
-	for (i = 0; i < mechSet->count; i++) {
-		/*
-		 * 0x06 [DER LEN] [OID]
-		 */
-		ilen += 1 +
-			gssint_der_length_size(mechSet->elements[i].length) +
-			mechSet->elements[i].length;
-	}
-	/*
-	 * 0x30 [DER LEN]
-	 */
-	tlen = 1 + gssint_der_length_size(ilen) + ilen;
 	ptr = gssalloc_malloc(tlen);
 	if (ptr == NULL)
 		return -1;
+	k5_buf_init_fixed(&buf, ptr, tlen);
 
-	buf->value = ptr;
-	buf->length = tlen;
-#define REMAIN (buf->length - ((unsigned char *)buf->value - ptr))
-
-	*ptr++ = SEQUENCE_OF;
-	if (gssint_put_der_length(ilen, &ptr, REMAIN) < 0)
-		return -1;
+	k5_der_add_taglen(&buf, SEQUENCE_OF, ilen);
 	for (i = 0; i < mechSet->count; i++) {
-		if (put_mech_oid(&ptr, &mechSet->elements[i], REMAIN) < 0) {
-			return -1;
-		}
+		k5_der_add_value(&buf, MECH_OID,
+				 mechSet->elements[i].elements,
+				 mechSet->elements[i].length);
 	}
+	assert(buf.len == tlen);
+
+	buffer_out->value = ptr;
+	buffer_out->length = tlen;
 	return 0;
-#undef REMAIN
 }
 
-/*
- * Verify that buff_in is pointing to a BIT_STRING with the correct
- * length and padding for the req_flags. If it is, decode req_flags
- * and return them, otherwise, return NULL.
- */
+/* Decode SPNEGO request flags from the DER encoding of a bit string and set
+ * them in *ret_flags. */
 static OM_uint32
-get_req_flags(unsigned char **buff_in, OM_uint32 bodysize,
-	      OM_uint32 *req_flags)
+get_req_flags(struct k5input *in, OM_uint32 *req_flags)
 {
-	unsigned int len;
-
-	if (bodysize < 1 || **buff_in != (CONTEXT | 0x01))
-		return (0);
-
-	if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
-				 bodysize, &len) < 0 || len != 4)
+	if (in->status || in->len != 4 ||
+	    k5_input_get_byte(in) != BIT_STRING ||
+	    k5_input_get_byte(in) != BIT_STRING_LENGTH ||
+	    k5_input_get_byte(in) != BIT_STRING_PADDING)
 		return GSS_S_DEFECTIVE_TOKEN;
 
-	if (*(*buff_in)++ != BIT_STRING)
-		return GSS_S_DEFECTIVE_TOKEN;
-
-	if (*(*buff_in)++ != BIT_STRING_LENGTH)
-		return GSS_S_DEFECTIVE_TOKEN;
-
-	if (*(*buff_in)++ != BIT_STRING_PADDING)
-		return GSS_S_DEFECTIVE_TOKEN;
-
-	*req_flags = (OM_uint32) (*(*buff_in)++ >> 1);
-	return (0);
+	*req_flags = k5_input_get_byte(in) >> 1;
+	return GSS_S_COMPLETE;
 }
 
 static OM_uint32
@@ -3580,9 +3413,7 @@ get_negTokenInit(OM_uint32 *minor_status,
 		 gss_buffer_t *mechListMIC)
 {
 	OM_uint32 err;
-	unsigned char *ptr, *bufstart;
-	unsigned int len;
-	gss_buffer_desc tmpbuf;
+	struct k5input in, seq, field;
 
 	*minor_status = 0;
 	der_mechSet->length = 0;
@@ -3591,148 +3422,100 @@ get_negTokenInit(OM_uint32 *minor_status,
 	*req_flags = 0;
 	*mechtok = *mechListMIC = GSS_C_NO_BUFFER;
 
-	ptr = bufstart = buf->value;
-	if ((buf->length - (ptr - bufstart)) > INT_MAX)
-		return GSS_S_FAILURE;
-#define REMAIN (buf->length - (ptr - bufstart))
+	k5_input_init(&in, buf->value, buf->length);
 
-	err = g_verify_token_header(gss_mech_spnego,
-				    &len, &ptr, 0, REMAIN);
-	if (err) {
-		*minor_status = err;
-		map_errcode(minor_status);
-		return GSS_S_FAILURE;
-	}
-	*minor_status = g_verify_neg_token_init(&ptr, REMAIN);
-	if (*minor_status) {
-		map_errcode(minor_status);
-		return GSS_S_FAILURE;
-	}
+	/* Advance past the framing header. */
+	err = verify_token_header(&in, gss_mech_spnego);
+	if (err)
+		return GSS_S_DEFECTIVE_TOKEN;
 
-	/* alias into input_token */
-	tmpbuf.value = ptr;
-	tmpbuf.length = REMAIN;
-	*mechSet = get_mech_set(minor_status, &ptr, REMAIN);
-	if (*mechSet == NULL)
-		return GSS_S_FAILURE;
+	/* Advance past the [0] tag for the NegotiationToken choice. */
+	if (!k5_der_get_value(&in, CONTEXT, &seq))
+		return GSS_S_DEFECTIVE_TOKEN;
+
+	/* Advance past the SEQUENCE tag. */
+	if (!k5_der_get_value(&seq, SEQUENCE, &seq))
+		return GSS_S_DEFECTIVE_TOKEN;
+
+	/* Get the contents of the mechTypes field. */
+	if (!k5_der_get_value(&seq, CONTEXT, &field))
+		return GSS_S_DEFECTIVE_TOKEN;
 
-	tmpbuf.length = ptr - (unsigned char *)tmpbuf.value;
-	der_mechSet->value = gssalloc_malloc(tmpbuf.length);
+	/* Store a copy of the contents for MIC computation. */
+	der_mechSet->value = gssalloc_malloc(field.len);
 	if (der_mechSet->value == NULL)
 		return GSS_S_FAILURE;
-	memcpy(der_mechSet->value, tmpbuf.value, tmpbuf.length);
-	der_mechSet->length = tmpbuf.length;
+	memcpy(der_mechSet->value, field.ptr, field.len);
+	der_mechSet->length = field.len;
 
-	err = get_req_flags(&ptr, REMAIN, req_flags);
-	if (err != GSS_S_COMPLETE) {
-		return err;
+	/* Decode the contents into an OID set. */
+	*mechSet = get_mech_set(minor_status, &field);
+	if (*mechSet == NULL)
+		return GSS_S_FAILURE;
+
+	if (k5_der_get_value(&seq, CONTEXT | 0x01, &field)) {
+		err = get_req_flags(&field, req_flags);
+		if (err != GSS_S_COMPLETE)
+			return err;
 	}
-	if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
-				 REMAIN, &len) >= 0) {
-		*mechtok = get_input_token(&ptr, len);
-		if (*mechtok == GSS_C_NO_BUFFER) {
+
+	if (k5_der_get_value(&seq, CONTEXT | 0x02, &field)) {
+		*mechtok = get_octet_string(&field);
+		if (*mechtok == GSS_C_NO_BUFFER)
 			return GSS_S_FAILURE;
-		}
 	}
-	if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
-				 REMAIN, &len) >= 0) {
-		*mechListMIC = get_input_token(&ptr, len);
-		if (*mechListMIC == GSS_C_NO_BUFFER) {
+
+	if (k5_der_get_value(&seq, CONTEXT | 0x03, &field)) {
+		*mechListMIC = get_octet_string(&field);
+		if (*mechListMIC == GSS_C_NO_BUFFER)
 			return GSS_S_FAILURE;
-		}
 	}
-	return GSS_S_COMPLETE;
-#undef REMAIN
+
+	return seq.status ? GSS_S_DEFECTIVE_TOKEN : GSS_S_COMPLETE;
 }
 
+/* Decode a NegotiationToken of type negTokenResp. */
 static OM_uint32
-get_negTokenResp(OM_uint32 *minor_status,
-		 unsigned char *buf, unsigned int buflen,
-		 OM_uint32 *negState,
-		 gss_OID *supportedMech,
-		 gss_buffer_t *responseToken,
-		 gss_buffer_t *mechListMIC)
+get_negTokenResp(OM_uint32 *minor_status, struct k5input *in,
+		 OM_uint32 *negState, gss_OID *supportedMech,
+		 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC)
 {
-	unsigned char *ptr, *bufstart;
-	unsigned int len;
-	int tmplen;
-	unsigned int tag, bytes;
+	struct k5input seq, field, en;
 
 	*negState = UNSPECIFIED;
 	*supportedMech = GSS_C_NO_OID;
 	*responseToken = *mechListMIC = GSS_C_NO_BUFFER;
-	ptr = bufstart = buf;
-#define REMAIN (buflen - (ptr - bufstart))
 
-	if (g_get_tag_and_length(&ptr, (CONTEXT | 0x01), REMAIN, &len) < 0)
+	/* Advance past the [1] tag for the NegotiationToken choice. */
+	if (!k5_der_get_value(in, CONTEXT | 0x01, &seq))
 		return GSS_S_DEFECTIVE_TOKEN;
-	if (*ptr++ == SEQUENCE) {
-		tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
-		if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
-			return GSS_S_DEFECTIVE_TOKEN;
-	}
-	if (REMAIN < 1)
-		tag = 0;
-	else
-		tag = *ptr++;
-
-	if (tag == CONTEXT) {
-		tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
-		if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
-			return GSS_S_DEFECTIVE_TOKEN;
 
-		if (g_get_tag_and_length(&ptr, ENUMERATED,
-					 REMAIN, &len) < 0)
-			return GSS_S_DEFECTIVE_TOKEN;
+	/* Advance seq past the SEQUENCE tag (historically this code allows the
+	 * tag to be missing). */
+	(void)k5_der_get_value(&seq, SEQUENCE, &seq);
 
-		if (len != ENUMERATION_LENGTH)
+	if (k5_der_get_value(&seq, CONTEXT, &field)) {
+		if (!k5_der_get_value(&field, ENUMERATED, &en))
 			return GSS_S_DEFECTIVE_TOKEN;
-
-		if (REMAIN < 1)
+		if (en.len != ENUMERATION_LENGTH)
 			return GSS_S_DEFECTIVE_TOKEN;
-		*negState = *ptr++;
-
-		if (REMAIN < 1)
-			tag = 0;
-		else
-			tag = *ptr++;
+		*negState = *en.ptr;
 	}
-	if (tag == (CONTEXT | 0x01)) {
-		tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
-		if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
-			return GSS_S_DEFECTIVE_TOKEN;
 
-		*supportedMech = get_mech_oid(minor_status, &ptr, REMAIN);
+	if (k5_der_get_value(&seq, CONTEXT | 0x01, &field)) {
+		*supportedMech = get_mech_oid(minor_status, &field);
 		if (*supportedMech == GSS_C_NO_OID)
 			return GSS_S_DEFECTIVE_TOKEN;
-
-		if (REMAIN < 1)
-			tag = 0;
-		else
-			tag = *ptr++;
 	}
-	if (tag == (CONTEXT | 0x02)) {
-		tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
-		if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
-			return GSS_S_DEFECTIVE_TOKEN;
 
-		*responseToken = get_input_token(&ptr, REMAIN);
+	if (k5_der_get_value(&seq, CONTEXT | 0x02, &field)) {
+		*responseToken = get_octet_string(&field);
 		if (*responseToken == GSS_C_NO_BUFFER)
 			return GSS_S_DEFECTIVE_TOKEN;
-
-		if (REMAIN < 1)
-			tag = 0;
-		else
-			tag = *ptr++;
 	}
-	if (tag == (CONTEXT | 0x03)) {
-		tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
-		if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
-			return GSS_S_DEFECTIVE_TOKEN;
 
-		*mechListMIC = get_input_token(&ptr, REMAIN);
-		if (*mechListMIC == GSS_C_NO_BUFFER)
-			return GSS_S_DEFECTIVE_TOKEN;
+	if (k5_der_get_value(&seq, CONTEXT | 0x04, &field)) {
+		*mechListMIC = get_octet_string(&field);
 
                 /* Handle Windows 2000 duplicate response token */
                 if (*responseToken &&
@@ -3746,25 +3529,8 @@ get_negTokenResp(OM_uint32 *minor_status,
 			*mechListMIC = NULL;
 		}
 	}
-	return GSS_S_COMPLETE;
-#undef REMAIN
-}
 
-/*
- * der encode the passed negResults as an ENUMERATED type and
- * place it in buf_out, advancing the buffer.
- */
-
-static int
-put_negResult(unsigned char **buf_out, OM_uint32 negResult,
-	      unsigned int buflen)
-{
-	if (buflen < 3)
-		return (-1);
-	*(*buf_out)++ = ENUMERATED;
-	*(*buf_out)++ = ENUMERATION_LENGTH;
-	*(*buf_out)++ = (unsigned char) negResult;
-	return (0);
+	return seq.status ? GSS_S_DEFECTIVE_TOKEN : GSS_S_COMPLETE;
 }
 
 /*
@@ -3848,22 +3614,15 @@ make_err_msg(const char *name)
  * Use DER rules, definite length method per RFC 2478
  */
 static int
-make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
-			  int negHintsCompat,
-			  gss_buffer_t mechListMIC, OM_uint32 req_flags,
-			  gss_buffer_t data, send_token_flag sendtoken,
+make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx, int negHintsCompat,
+			  gss_buffer_t mic, OM_uint32 req_flags,
+			  gss_buffer_t token, send_token_flag sendtoken,
 			  gss_buffer_t outbuf)
 {
-	int ret = 0;
-	unsigned int tlen, dataLen = 0;
-	unsigned int negTokenInitSize = 0;
-	unsigned int negTokenInitSeqSize = 0;
-	unsigned int negTokenInitContSize = 0;
-	unsigned int rspTokenSize = 0;
-	unsigned int mechListTokenSize = 0;
-	unsigned int micTokenSize = 0;
-	unsigned char *t;
-	unsigned char *ptr;
+	size_t f0len, f2len, f3len, fields_len, seq_len, choice_len;
+	size_t mech_len, framed_len;
+	uint8_t *t;
+	struct k5buf buf;
 
 	if (outbuf == GSS_C_NO_BUFFER)
 		return (-1);
@@ -3871,141 +3630,66 @@ make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
 	outbuf->length = 0;
 	outbuf->value = NULL;
 
-	/* calculate the data length */
-
-	/*
-	 * 0xa0 [DER LEN] [mechTypes]
-	 */
-	mechListTokenSize = 1 +
-		gssint_der_length_size(spnego_ctx->DER_mechTypes.length) +
-		spnego_ctx->DER_mechTypes.length;
-	dataLen += mechListTokenSize;
-
-	/*
-	 * If a token from gss_init_sec_context exists,
-	 * add the length of the token + the ASN.1 overhead
-	 */
-	if (data != NULL) {
-		/*
-		 * Encoded in final output as:
-		 * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
-		 * -----s--------|--------s2----------
-		 */
-		rspTokenSize = 1 +
-			gssint_der_length_size(data->length) +
-			data->length;
-		dataLen += 1 + gssint_der_length_size(rspTokenSize) +
-			rspTokenSize;
-	}
-
-	if (mechListMIC) {
-		/*
-		 * Encoded in final output as:
-		 * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
-		 *	--s--     -----tlen------------
-		 */
-		micTokenSize = 1 +
-			gssint_der_length_size(mechListMIC->length) +
-			mechListMIC->length;
-		dataLen += 1 +
-			gssint_der_length_size(micTokenSize) +
-			micTokenSize;
-	}
-
-	/*
-	 * Add size of DER encoding
-	 * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
-	 *   0x30 [DER_LEN] [data]
-	 *
-	 */
-	negTokenInitContSize = dataLen;
-	negTokenInitSeqSize = 1 + gssint_der_length_size(dataLen) + dataLen;
-	dataLen = negTokenInitSeqSize;
-
-	/*
-	 * negTokenInitSize indicates the bytes needed to
-	 * hold the ASN.1 encoding of the entire NegTokenInit
-	 * SEQUENCE.
-	 * 0xa0 [DER_LEN] + data
-	 *
-	 */
-	negTokenInitSize = 1 +
-		gssint_der_length_size(negTokenInitSeqSize) +
-		negTokenInitSeqSize;
-
-	tlen = g_token_size(gss_mech_spnego, negTokenInitSize);
-
-	t = (unsigned char *) gssalloc_malloc(tlen);
-
-	if (t == NULL) {
+	/* Calculate the length of each field and the total fields length. */
+	fields_len = 0;
+	/* mechTypes [0] MechTypeList, previously assembled in spnego_ctx */
+	f0len = spnego_ctx->DER_mechTypes.length;
+	fields_len += k5_der_value_len(f0len);
+	if (token != NULL) {
+		/* mechToken [2] OCTET STRING OPTIONAL */
+		f2len = k5_der_value_len(token->length);
+		fields_len += k5_der_value_len(f2len);
+	}
+	if (mic != GSS_C_NO_BUFFER) {
+		/* mechListMIC [3] OCTET STRING OPTIONAL */
+		f3len = k5_der_value_len(mic->length);
+		fields_len += k5_der_value_len(f3len);
+	}
+
+	/* Calculate the length of the sequence and choice. */
+	seq_len = k5_der_value_len(fields_len);
+	choice_len = k5_der_value_len(seq_len);
+
+	/* Calculate the framed token length. */
+	mech_len = k5_der_value_len(gss_mech_spnego->length);
+	framed_len = k5_der_value_len(mech_len + choice_len);
+
+	/* Allocate space and prepare a buffer. */
+	t = gssalloc_malloc(framed_len);
+	if (t == NULL)
 		return (-1);
-	}
-
-	ptr = t;
+	k5_buf_init_fixed(&buf, t, framed_len);
 
-	/* create the message */
-	if ((ret = g_make_token_header(gss_mech_spnego, negTokenInitSize,
-			    &ptr, tlen)))
-		goto errout;
+	/* Add generic token framing. */
+	k5_der_add_taglen(&buf, HEADER_ID, mech_len + choice_len);
+	k5_der_add_value(&buf, MECH_OID, gss_mech_spnego->elements,
+			 gss_mech_spnego->length);
 
-	*ptr++ = CONTEXT; /* NegotiationToken identifier */
-	if ((ret = gssint_put_der_length(negTokenInitSeqSize, &ptr, tlen)))
-		goto errout;
+	/* Add NegotiationToken choice tag and NegTokenInit sequence tag. */
+	k5_der_add_taglen(&buf, CONTEXT | 0x00, seq_len);
+	k5_der_add_taglen(&buf, SEQUENCE, fields_len);
 
-	*ptr++ = SEQUENCE;
-	if ((ret = gssint_put_der_length(negTokenInitContSize, &ptr,
-					 tlen - (int)(ptr-t))))
-		goto errout;
+	/* Add the already-encoded mechanism list as mechTypes. */
+	k5_der_add_value(&buf, CONTEXT | 0x00, spnego_ctx->DER_mechTypes.value,
+			 spnego_ctx->DER_mechTypes.length);
 
-	*ptr++ = CONTEXT | 0x00; /* MechTypeList identifier */
-	if ((ret = gssint_put_der_length(spnego_ctx->DER_mechTypes.length,
-					 &ptr, tlen - (int)(ptr-t))))
-		goto errout;
-
-	/* We already encoded the MechSetList */
-	(void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
-		      spnego_ctx->DER_mechTypes.length);
-
-	ptr += spnego_ctx->DER_mechTypes.length;
-
-	if (data != NULL) {
-		*ptr++ = CONTEXT | 0x02;
-		if ((ret = gssint_put_der_length(rspTokenSize,
-				&ptr, tlen - (int)(ptr - t))))
-			goto errout;
-
-		if ((ret = put_input_token(&ptr, data,
-			tlen - (int)(ptr - t))))
-			goto errout;
+	if (token != NULL) {
+		k5_der_add_taglen(&buf, CONTEXT | 0x02, f2len);
+		k5_der_add_value(&buf, OCTET_STRING, token->value,
+				 token->length);
 	}
 
-	if (mechListMIC != GSS_C_NO_BUFFER) {
-		*ptr++ = CONTEXT | 0x03;
-		if ((ret = gssint_put_der_length(micTokenSize,
-				&ptr, tlen - (int)(ptr - t))))
-			goto errout;
-
-		if (negHintsCompat) {
-			ret = put_neg_hints(&ptr, mechListMIC,
-					    tlen - (int)(ptr - t));
-			if (ret)
-				goto errout;
-		} else if ((ret = put_input_token(&ptr, mechListMIC,
-				tlen - (int)(ptr - t))))
-			goto errout;
+	if (mic != GSS_C_NO_BUFFER) {
+		uint8_t id = negHintsCompat ? SEQUENCE : OCTET_STRING;
+		k5_der_add_taglen(&buf, CONTEXT | 0x03, f3len);
+		k5_der_add_value(&buf, id, mic->value, mic->length);
 	}
 
-errout:
-	if (ret != 0) {
-		if (t)
-			free(t);
-		t = NULL;
-		tlen = 0;
-	}
-	outbuf->length = tlen;
-	outbuf->value = (void *) t;
+	assert(buf.len == framed_len);
+	outbuf->length = framed_len;
+	outbuf->value = t;
 
-	return (ret);
+	return (0);
 }
 
 /*
@@ -4013,414 +3697,106 @@ errout:
  * gss_accept_sec_context and eventually up to the application program
  * and over to the client.
  */
-static int
-make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
-			  gss_buffer_t data, gss_buffer_t mechListMIC,
+static OM_uint32
+make_spnego_tokenTarg_msg(uint8_t status, gss_OID mech_wanted,
+			  gss_buffer_t token, gss_buffer_t mic,
 			  send_token_flag sendtoken,
 			  gss_buffer_t outbuf)
 {
-	unsigned int tlen = 0;
-	unsigned int ret = 0;
-	unsigned int NegTokenTargSize = 0;
-	unsigned int NegTokenSize = 0;
-	unsigned int rspTokenSize = 0;
-	unsigned int micTokenSize = 0;
-	unsigned int dataLen = 0;
-	unsigned char *t;
-	unsigned char *ptr;
+	size_t f0len, f1len, f2len, f3len, fields_len, seq_len, choice_len;
+	uint8_t *t;
+	struct k5buf buf;
 
 	if (outbuf == GSS_C_NO_BUFFER)
 		return (GSS_S_DEFECTIVE_TOKEN);
 	if (sendtoken == INIT_TOKEN_SEND && mech_wanted == GSS_C_NO_OID)
-	    return (GSS_S_DEFECTIVE_TOKEN);
+		return (GSS_S_DEFECTIVE_TOKEN);
 
 	outbuf->length = 0;
 	outbuf->value = NULL;
 
-	/*
-	 * ASN.1 encoding of the negResult
-	 * ENUMERATED type is 3 bytes
-	 *  ENUMERATED TAG, Length, Value,
-	 * Plus 2 bytes for the CONTEXT id and length.
-	 */
-	dataLen = 5;
-
-	/*
-	 * calculate data length
-	 *
-	 * If this is the initial token, include length of
-	 * mech_type and the negotiation result fields.
-	 */
+	/* Calculate the length of each field and the total fields length. */
+	fields_len = 0;
+	/* negState [0] ENUMERATED { ... } OPTIONAL */
+	f0len = k5_der_value_len(1);
+	fields_len += k5_der_value_len(f0len);
 	if (sendtoken == INIT_TOKEN_SEND) {
-		int mechlistTokenSize;
-		/*
-		 * 1 byte for the CONTEXT ID(0xa0),
-		 * 1 byte for the OID ID(0x06)
-		 * 1 byte for OID Length field
-		 * Plus the rest... (OID Length, OID value)
-		 */
-		mechlistTokenSize = 3 + mech_wanted->length +
-			gssint_der_length_size(mech_wanted->length);
-
-		dataLen += mechlistTokenSize;
+		/* supportedMech [1] MechType OPTIONAL */
+		f1len = k5_der_value_len(mech_wanted->length);
+		fields_len += k5_der_value_len(f1len);
 	}
-	if (data != NULL && data->length > 0) {
-		/* Length of the inner token */
-		rspTokenSize = 1 + gssint_der_length_size(data->length) +
-			data->length;
-
-		dataLen += rspTokenSize;
-
-		/* Length of the outer token */
-		dataLen += 1 + gssint_der_length_size(rspTokenSize);
+	if (token != NULL && token->length > 0) {
+		/* mechToken [2] OCTET STRING OPTIONAL */
+		f2len = k5_der_value_len(token->length);
+		fields_len += k5_der_value_len(f2len);
 	}
-	if (mechListMIC != NULL) {
-
-		/* Length of the inner token */
-		micTokenSize = 1 + gssint_der_length_size(mechListMIC->length) +
-			mechListMIC->length;
-
-		dataLen += micTokenSize;
-
-		/* Length of the outer token */
-		dataLen += 1 + gssint_der_length_size(micTokenSize);
+	if (mic != NULL) {
+		/* mechListMIC [3] OCTET STRING OPTIONAL */
+		f3len = k5_der_value_len(mic->length);
+		fields_len += k5_der_value_len(f3len);
 	}
-	/*
-	 * Add size of DER encoded:
-	 * NegTokenTarg [ SEQUENCE ] of
-	 *    NegResult[0] ENUMERATED {
-	 *	accept_completed(0),
-	 *	accept_incomplete(1),
-	 *	reject(2) }
-	 *    supportedMech [1] MechType OPTIONAL,
-	 *    responseToken [2] OCTET STRING OPTIONAL,
-	 *    mechListMIC   [3] OCTET STRING OPTIONAL
-	 *
-	 * size = data->length + MechListMic + SupportedMech len +
-	 *	Result Length + ASN.1 overhead
-	 */
-	NegTokenTargSize = dataLen;
-	dataLen += 1 + gssint_der_length_size(NegTokenTargSize);
-
-	/*
-	 * NegotiationToken [ CHOICE ]{
-	 *    negTokenInit  [0]	 NegTokenInit,
-	 *    negTokenTarg  [1]	 NegTokenTarg }
-	 */
-	NegTokenSize = dataLen;
-	dataLen += 1 + gssint_der_length_size(NegTokenSize);
 
-	tlen = dataLen;
-	t = (unsigned char *) gssalloc_malloc(tlen);
+	/* Calculate the length of the sequence and choice. */
+	seq_len = k5_der_value_len(fields_len);
+	choice_len = k5_der_value_len(seq_len);
 
-	if (t == NULL) {
-		ret = GSS_S_DEFECTIVE_TOKEN;
-		goto errout;
-	}
+	/* Allocate space and prepare a buffer. */
+	t = gssalloc_malloc(choice_len);
+	if (t == NULL)
+		return (GSS_S_DEFECTIVE_TOKEN);
+	k5_buf_init_fixed(&buf, t, choice_len);
 
-	ptr = t;
+	/* Add the choice tag and begin the sequence. */
+	k5_der_add_taglen(&buf, CONTEXT | 0x01, seq_len);
+	k5_der_add_taglen(&buf, SEQUENCE, fields_len);
 
-	/*
-	 * Indicate that we are sending CHOICE 1
-	 * (NegTokenTarg)
-	 */
-	*ptr++ = CONTEXT | 0x01;
-	if (gssint_put_der_length(NegTokenSize, &ptr, dataLen) < 0) {
-		ret = GSS_S_DEFECTIVE_TOKEN;
-		goto errout;
-	}
-	*ptr++ = SEQUENCE;
-	if (gssint_put_der_length(NegTokenTargSize, &ptr,
-				  tlen - (int)(ptr-t)) < 0) {
-		ret = GSS_S_DEFECTIVE_TOKEN;
-		goto errout;
-	}
+	/* Add the negState field. */
+	k5_der_add_taglen(&buf, CONTEXT | 0x00, f0len);
+	k5_der_add_value(&buf, ENUMERATED, &status, 1);
 
-	/*
-	 * First field of the NegTokenTarg SEQUENCE
-	 * is the ENUMERATED NegResult.
-	 */
-	*ptr++ = CONTEXT;
-	if (gssint_put_der_length(3, &ptr,
-				  tlen - (int)(ptr-t)) < 0) {
-		ret = GSS_S_DEFECTIVE_TOKEN;
-		goto errout;
-	}
-	if (put_negResult(&ptr, status, tlen - (int)(ptr - t)) < 0) {
-		ret = GSS_S_DEFECTIVE_TOKEN;
-		goto errout;
-	}
 	if (sendtoken == INIT_TOKEN_SEND) {
-		/*
-		 * Next, is the Supported MechType
-		 */
-		*ptr++ = CONTEXT | 0x01;
-		if (gssint_put_der_length(mech_wanted->length + 2,
-					  &ptr,
-					  tlen - (int)(ptr - t)) < 0) {
-			ret = GSS_S_DEFECTIVE_TOKEN;
-			goto errout;
-		}
-		if (put_mech_oid(&ptr, mech_wanted,
-				 tlen - (int)(ptr - t)) < 0) {
-			ret = GSS_S_DEFECTIVE_TOKEN;
-			goto errout;
-		}
-	}
-	if (data != NULL && data->length > 0) {
-		*ptr++ = CONTEXT | 0x02;
-		if (gssint_put_der_length(rspTokenSize, &ptr,
-					  tlen - (int)(ptr - t)) < 0) {
-			ret = GSS_S_DEFECTIVE_TOKEN;
-			goto errout;
-		}
-		if (put_input_token(&ptr, data,
-				    tlen - (int)(ptr - t)) < 0) {
-			ret = GSS_S_DEFECTIVE_TOKEN;
-			goto errout;
-		}
-	}
-	if (mechListMIC != NULL) {
-		*ptr++ = CONTEXT | 0x03;
-		if (gssint_put_der_length(micTokenSize, &ptr,
-					  tlen - (int)(ptr - t)) < 0) {
-			ret = GSS_S_DEFECTIVE_TOKEN;
-			goto errout;
-		}
-		if (put_input_token(&ptr, mechListMIC,
-				    tlen - (int)(ptr - t)) < 0) {
-			ret = GSS_S_DEFECTIVE_TOKEN;
-			goto errout;
-		}
-	}
-	ret = GSS_S_COMPLETE;
-errout:
-	if (ret != GSS_S_COMPLETE) {
-		if (t)
-			free(t);
-	} else {
-		outbuf->length = ptr - t;
-		outbuf->value = (void *) t;
-	}
-
-	return (ret);
-}
-
-/* determine size of token */
-static int
-g_token_size(gss_OID_const mech, unsigned int body_size)
-{
-	int hdrsize;
-
-	/*
-	 * Initialize the header size to the
-	 * MECH_OID byte + the bytes needed to indicate the
-	 * length of the OID + the OID itself.
-	 *
-	 * 0x06 [MECHLENFIELD] MECHDATA
-	 */
-	hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
-
-	/*
-	 * Now add the bytes needed for the initial header
-	 * token bytes:
-	 * 0x60 + [DER_LEN] + HDRSIZE
-	 */
-	hdrsize += 1 + gssint_der_length_size(body_size + hdrsize);
-
-	return (hdrsize + body_size);
-}
-
-/*
- * generate token header.
- *
- * Use DER Definite Length method per RFC2478
- * Use of indefinite length encoding will not be compatible
- * with Microsoft or others that actually follow the spec.
- */
-static int
-g_make_token_header(gss_OID_const mech,
-		    unsigned int body_size,
-		    unsigned char **buf,
-		    unsigned int totallen)
-{
-	int ret = 0;
-	unsigned int hdrsize;
-	unsigned char *p = *buf;
-
-	hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
-
-	*(*buf)++ = HEADER_ID;
-	if ((ret = gssint_put_der_length(hdrsize + body_size, buf, totallen)))
-		return (ret);
-
-	*(*buf)++ = MECH_OID;
-	if ((ret = gssint_put_der_length(mech->length, buf,
-			    totallen - (int)(p - *buf))))
-		return (ret);
-	TWRITE_STR(*buf, mech->elements, mech->length);
-	return (0);
-}
-
-/*
- * NOTE: This checks that the length returned by
- * gssint_get_der_length() is not greater than the number of octets
- * remaining, even though gssint_get_der_length() already checks, in
- * theory.
- */
-static int
-g_get_tag_and_length(unsigned char **buf, int tag,
-		     unsigned int buflen, unsigned int *outlen)
-{
-	unsigned char *ptr = *buf;
-	int ret = -1; /* pessimists, assume failure ! */
-	unsigned int encoded_len;
-	int tmplen = 0;
-
-	*outlen = 0;
-	if (buflen > 1 && *ptr == tag) {
-		ptr++;
-		tmplen = gssint_get_der_length(&ptr, buflen - 1,
-						&encoded_len);
-		if (tmplen < 0) {
-			ret = -1;
-		} else if ((unsigned int)tmplen > buflen - (ptr - *buf)) {
-			ret = -1;
-		} else
-			ret = 0;
+		/* Add the supportedMech field. */
+		k5_der_add_taglen(&buf, CONTEXT | 0x01, f1len);
+		k5_der_add_value(&buf, MECH_OID, mech_wanted->elements,
+				 mech_wanted->length);
 	}
-	*outlen = tmplen;
-	*buf = ptr;
-	return (ret);
-}
-
-static int
-g_verify_neg_token_init(unsigned char **buf_in, unsigned int cur_size)
-{
-	unsigned char *buf = *buf_in;
-	unsigned char *endptr = buf + cur_size;
-	int seqsize;
-	int ret = 0;
-	unsigned int bytes;
-
-	/*
-	 * Verify this is a NegotiationToken type token
-	 * - check for a0(context specific identifier)
-	 * - get length and verify that enoughd ata exists
-	 */
-	if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &bytes) < 0 ||
-	    bytes == 0)
-		return (G_BAD_TOK_HEADER);
 
-	cur_size = bytes; /* should indicate bytes remaining */
-
-	/*
-	 * Verify the next piece, it should identify this as
-	 * a strucure of type NegTokenInit.
-	 */
-	if (*buf++ == SEQUENCE) {
-		if ((seqsize = gssint_get_der_length(&buf, cur_size - 1, &bytes)) <= 0)
-			return (G_BAD_TOK_HEADER);
-		/*
-		 * Make sure we have the entire buffer as described
-		 */
-		if (seqsize > endptr - buf)
-			return (G_BAD_TOK_HEADER);
-	} else {
-		return (G_BAD_TOK_HEADER);
+	if (token != NULL && token->length > 0) {
+		/* Add the mechToken field. */
+		k5_der_add_taglen(&buf, CONTEXT | 0x02, f2len);
+		k5_der_add_value(&buf, OCTET_STRING, token->value,
+				 token->length);
 	}
 
-	cur_size = seqsize; /* should indicate bytes remaining */
-
-	/*
-	 * Verify that the first blob is a sequence of mechTypes
-	 */
-	if (*buf++ == CONTEXT) {
-		if ((seqsize = gssint_get_der_length(&buf, cur_size - 1, &bytes)) < 0)
-			return (G_BAD_TOK_HEADER);
-		/*
-		 * Make sure we have the entire buffer as described
-		 */
-		if (seqsize > endptr - buf)
-			return (G_BAD_TOK_HEADER);
-	} else {
-		return (G_BAD_TOK_HEADER);
+	if (mic != NULL) {
+		/* Add the mechListMIC field. */
+		k5_der_add_taglen(&buf, CONTEXT | 0x03, f3len);
+		k5_der_add_value(&buf, OCTET_STRING, mic->value, mic->length);
 	}
 
-	/*
-	 * At this point, *buf should be at the beginning of the
-	 * DER encoded list of mech types that are to be negotiated.
-	 */
-	*buf_in = buf;
-
-	return (ret);
+	assert(buf.len == choice_len);
+	outbuf->length = choice_len;
+	outbuf->value = t;
 
+	return (0);
 }
 
-/* verify token header. */
+/* Advance in past the [APPLICATION 0] tag and thisMech field of an
+ * InitialContextToken encoding, checking that thisMech matches mech. */
 static int
-g_verify_token_header(gss_OID_const mech,
-		    unsigned int *body_size,
-		    unsigned char **buf_in,
-		    int tok_type,
-		    unsigned int toksize)
+verify_token_header(struct k5input *in, gss_OID_const mech)
 {
-	unsigned char *buf = *buf_in;
-	int seqsize;
-	gss_OID_desc toid;
-	int ret = 0;
-	unsigned int bytes;
+	gss_OID_desc oid;
+	struct k5input field;
 
-	if (toksize-- < 1)
+	if (!k5_der_get_value(in, HEADER_ID, in))
 		return (G_BAD_TOK_HEADER);
-
-	if (*buf++ != HEADER_ID)
+	if (!k5_der_get_value(in, MECH_OID, &field))
 		return (G_BAD_TOK_HEADER);
 
-	if ((seqsize = gssint_get_der_length(&buf, toksize, &bytes)) < 0)
-		return (G_BAD_TOK_HEADER);
-
-	if ((seqsize + bytes) != toksize)
-		return (G_BAD_TOK_HEADER);
-
-	if (toksize-- < 1)
-		return (G_BAD_TOK_HEADER);
-
-
-	if (*buf++ != MECH_OID)
-		return (G_BAD_TOK_HEADER);
-
-	if (toksize-- < 1)
-		return (G_BAD_TOK_HEADER);
-
-	toid.length = *buf++;
-
-	if (toksize < toid.length)
-		return (G_BAD_TOK_HEADER);
-	else
-		toksize -= toid.length;
-
-	toid.elements = buf;
-	buf += toid.length;
-
-	if (!g_OID_equal(&toid, mech))
-		ret = G_WRONG_MECH;
-
-	/*
-	 * G_WRONG_MECH is not returned immediately because it's more important
-	 * to return G_BAD_TOK_HEADER if the token header is in fact bad
-	 */
-	if (toksize < 2)
-		return (G_BAD_TOK_HEADER);
-	else
-		toksize -= 2;
-
-	if (!ret) {
-		*buf_in = buf;
-		*body_size = toksize;
-	}
-
-	return (ret);
+	oid.length = field.len;
+	oid.elements = (uint8_t *)field.ptr;
+	return g_OID_equal(&oid, mech) ? 0 : G_WRONG_MECH;
 }
 
 /*


More information about the cvs-krb5 mailing list