krb5 commit: Refactor GSS token header parsing

ghudson at mit.edu ghudson at mit.edu
Tue Jul 30 20:23:45 EDT 2024


https://github.com/krb5/krb5/commit/3ebe2ec4c1c5afe39f094b50590dd6f56cbce5ca
commit 3ebe2ec4c1c5afe39f094b50590dd6f56cbce5ca
Author: Greg Hudson <ghudson at mit.edu>
Date:   Wed Jun 19 18:55:35 2024 -0400

    Refactor GSS token header parsing
    
    Rewrite g_verify_token_header() to use k5input and not to handle
    two-byte token IDs (which are specific to the krb5 mechanism).  Add a
    more general g_get_token_header() which can handle detached wrappers
    and cases where the mech is not known in advance.  Adjust all current
    callers, and get rid of the duplicate DER parsing code added in commit
    b0a2f8a5365f2eec3e27d78907de9f9d2c80505a.
    
    In k5-input.h, split out tag and length parsing into
    k5_der_get_taglen(), needed by g_get_token_header().

 src/include/k5-der.h                          |  36 ++++++--
 src/lib/gssapi/generic/gssapiP_generic.h      |  12 +--
 src/lib/gssapi/generic/util_token.c           |  83 +++++++++--------
 src/lib/gssapi/krb5/accept_sec_context.c      |  82 +++++++++--------
 src/lib/gssapi/krb5/iakerb.c                  |  32 +++----
 src/lib/gssapi/krb5/init_sec_context.c        |  42 ++++-----
 src/lib/gssapi/krb5/k5unseal.c                |  40 ++------
 src/lib/gssapi/krb5/k5unsealiov.c             | 128 +++++---------------------
 src/lib/gssapi/mechglue/g_decapsulate_token.c |  18 ++--
 src/lib/gssapi/mechglue/g_glue.c              |  28 ++----
 src/lib/gssapi/mechglue/mglueP.h              |   2 -
 11 files changed, 198 insertions(+), 305 deletions(-)

diff --git a/src/include/k5-der.h b/src/include/k5-der.h
index b8371d9b4..aff45f1d5 100644
--- a/src/include/k5-der.h
+++ b/src/include/k5-der.h
@@ -105,19 +105,15 @@ k5_der_add_value(struct k5buf *buf, uint8_t idbyte, const void *contents,
 
 /*
  * If the next byte in in matches idbyte and the subsequent DER length is
- * valid, advance in past the value, set *contents_out to the value contents,
- * and return true.  Otherwise return false.  Only set an error on in if the
- * next bytes matches idbyte but the ensuing length is invalid.  contents_out
- * may be aliased to in; it will only be written to on successful decoding of a
- * value.
+ * valid, advance in past the tag and length, set *len_out to the decoded
+ * length, and return true.  Otherwise return false.  Only set an error on in
+ * if the next byte matches idbyte but the ensuing length is invalid.
  */
 static inline bool
-k5_der_get_value(struct k5input *in, uint8_t idbyte,
-                 struct k5input *contents_out)
+k5_der_get_taglen(struct k5input *in, uint8_t idbyte, size_t *len_out)
 {
     uint8_t lenbyte, i;
     size_t len;
-    const void *bytes;
 
     /* Do nothing if in is empty or the next byte doesn't match idbyte. */
     if (in->status || in->len == 0 || *in->ptr != idbyte)
@@ -139,6 +135,30 @@ k5_der_get_value(struct k5input *in, uint8_t idbyte,
         }
     }
 
+    if (in->status)
+        return false;
+
+    *len_out = len;
+    return true;
+}
+
+/*
+ * If the next byte in in matches idbyte and the subsequent DER length is
+ * valid, advance in past the value, set *contents_out to the value contents,
+ * and return true.  Otherwise return false.  Only set an error on in if the
+ * next byte matches idbyte but the ensuing length is invalid.  contents_out
+ * may be aliased to in; it will only be written to on successful decoding of a
+ * value.
+ */
+static inline bool
+k5_der_get_value(struct k5input *in, uint8_t idbyte,
+                 struct k5input *contents_out)
+{
+    size_t len;
+    const void *bytes;
+
+    if (!k5_der_get_taglen(in, idbyte, &len))
+        return false;
     bytes = k5_input_get_bytes(in, len);
     if (bytes == NULL)
         return false;
diff --git a/src/lib/gssapi/generic/gssapiP_generic.h b/src/lib/gssapi/generic/gssapiP_generic.h
index 7201f2ad5..96dd60546 100644
--- a/src/lib/gssapi/generic/gssapiP_generic.h
+++ b/src/lib/gssapi/generic/gssapiP_generic.h
@@ -47,6 +47,7 @@
 
 #include "k5-platform.h"
 #include "k5-buf.h"
+#include "k5-input.h"
 
 /** helper macros **/
 
@@ -69,6 +70,7 @@
 #define g_make_string_buffer    gssint_g_make_string_buffer
 #define g_token_size            gssint_g_token_size
 #define g_make_token_header     gssint_g_make_token_header
+#define g_get_token_header      gssint_g_get_token_header
 #define g_verify_token_header   gssint_g_verify_token_header
 #define g_display_major_status  gssint_g_display_major_status
 #define g_display_com_err_status gssint_g_display_com_err_status
@@ -89,14 +91,10 @@ unsigned int g_token_size (const gss_OID_desc * mech, unsigned int body_size);
 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
+int g_get_token_header (struct k5input *in, gss_OID oid_out,
+                        size_t *token_len_out);
 
-gss_int32 g_verify_token_header (const gss_OID_desc * mech,
-                                 unsigned int *body_size,
-                                 unsigned char **buf, int tok_type,
-                                 unsigned int toksize_in,
-                                 int flags);
+int g_verify_token_header(struct k5input *in, gss_const_OID expected_mech);
 
 OM_uint32 g_display_major_status (OM_uint32 *minor_status,
                                   OM_uint32 status_value,
diff --git a/src/lib/gssapi/generic/util_token.c b/src/lib/gssapi/generic/util_token.c
index 2369cae22..1ee948fcc 100644
--- a/src/lib/gssapi/generic/util_token.c
+++ b/src/lib/gssapi/generic/util_token.c
@@ -62,47 +62,56 @@ g_make_token_header(struct k5buf *buf, const gss_OID_desc *mech,
 }
 
 /*
- * Given a buffer containing a token, reads and verifies the token,
- * leaving buf advanced past the token header, and setting body_size
- * to the number of remaining bytes.  Returns 0 on success,
- * G_BAD_TOK_HEADER for a variety of errors, and G_WRONG_MECH if the
- * mechanism in the token does not match the mech argument.  buf and
- * *body_size are left unmodified on error.
+ * If a valid GSSAPI generic token header is present at the beginning of *in,
+ * advance past it, set *oid_out to the mechanism OID in the header, set
+ * *token_len_out to the total token length (including the header) as indicated
+ * by length of the outermost DER value, and return true.  Otherwise return
+ * false, leaving *in unchanged if it did not begin with a 0x60 byte.
+ *
+ * Do not verify that the outermost length matches or fits within in->len, as
+ * we need to be able to handle a detached header for krb5 IOV unwrap.  It is
+ * the caller's responsibility to validate *token_len_out if necessary.
  */
-
-gss_int32
-g_verify_token_header(
-    const gss_OID_desc * mech,
-    unsigned int *body_size,
-    unsigned char **buf_in,
-    int tok_type,
-    unsigned int toksize_in,
-    int flags)
+int
+g_get_token_header(struct k5input *in, gss_OID oid_out, size_t *token_len_out)
 {
-    struct k5input in, mech_der;
-    gss_OID_desc toid;
+    size_t len, tlen;
+    const uint8_t *orig_ptr = in->ptr;
+    struct k5input oidbytes;
 
-    k5_input_init(&in, *buf_in, toksize_in);
+    /* Read the outermost tag and length, and compute the full token length. */
+    if (!k5_der_get_taglen(in, 0x60, &len))
+        return 0;
+    tlen = len + (in->ptr - orig_ptr);
 
-    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;
-    }
+    /* Read the mechanism OID. */
+    if (!k5_der_get_value(in, 0x06, &oidbytes))
+        return 0;
+    oid_out->length = oidbytes.len;
+    oid_out->elements = (uint8_t *)oidbytes.ptr;
 
-    if (tok_type != -1) {
-        if (k5_input_get_uint16_be(&in) != tok_type)
-            return in.status ? G_BAD_TOK_HEADER : G_WRONG_TOKID;
-    }
+    *token_len_out = tlen;
+    return 1;
+}
 
-    *buf_in = (uint8_t *)in.ptr;
-    *body_size = in.len;
-    return 0;
+/*
+ * If a token header for expected_mech is present in *in and the token length
+ * indicated by the header is equal to in->len, advance past the header and
+ * return true.  Otherwise return false.  Leave *in unmodified if no token
+ * header is present or it is for a different mechanism.
+ */
+int
+g_verify_token_header(struct k5input *in, gss_const_OID expected_mech)
+{
+    struct k5input orig = *in;
+    gss_OID_desc mech;
+    size_t tlen, orig_len = in->len;
+
+    if (!g_get_token_header(in, &mech, &tlen) || tlen != orig_len)
+        return 0;
+    if (!g_OID_equal(&mech, expected_mech)) {
+        *in = orig;
+        return 0;
+    }
+    return 1;
 }
diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c
index c224ee9ac..c9987b757 100644
--- a/src/lib/gssapi/krb5/accept_sec_context.c
+++ b/src/lib/gssapi/krb5/accept_sec_context.c
@@ -625,6 +625,44 @@ fail:
     return status;
 }
 
+/*
+ * Verify the ASN.1 framing and token type in an RFC 4121 initiator token.  Set
+ * *mech_used_out to the mechanism in the framing, as a pointer to a global OID
+ * for one of the expected mechanisms.  Set *ap_req_out to the portion of the
+ * token containing the AP-REQ encoding.  Return G_BAD_TOK_HEADER if the
+ * framing is invalid.  Return G_WRONG_TOKID if the token type is incorrect.
+ * Return G_WRONG_MECH if the mechanism OID in the framing is not one of the
+ * expected Kerberos mechanisms.
+ */
+static OM_uint32
+parse_init_token(gss_buffer_t input_token, gss_const_OID *mech_used_out,
+                 krb5_data *ap_req_out)
+{
+    struct k5input in;
+    gss_OID_desc mech;
+    size_t tlen;
+
+    k5_input_init(&in, input_token->value, input_token->length);
+    if (!g_get_token_header(&in, &mech, &tlen) || tlen != input_token->length)
+        return G_BAD_TOK_HEADER;
+    if (k5_input_get_uint16_be(&in) != KG_TOK_CTX_AP_REQ)
+        return G_WRONG_TOKID;
+
+    if (g_OID_equal(&mech, gss_mech_krb5))
+        *mech_used_out = gss_mech_krb5;
+    else if (g_OID_equal(&mech, gss_mech_iakerb))
+        *mech_used_out = gss_mech_iakerb;
+    else if (g_OID_equal(&mech, gss_mech_krb5_wrong))
+        *mech_used_out = gss_mech_krb5_wrong;
+    else if (g_OID_equal(&mech, gss_mech_krb5_old))
+        *mech_used_out = gss_mech_krb5_old;
+    else
+        return G_WRONG_MECH;
+
+    *ap_req_out = make_data((uint8_t *)in.ptr, in.len);
+    return 0;
+}
+
 static OM_uint32
 kg_accept_krb5(OM_uint32 *minor_status, gss_ctx_id_t *context_handle,
                gss_cred_id_t verifier_cred_handle, gss_buffer_t input_token,
@@ -635,7 +673,6 @@ kg_accept_krb5(OM_uint32 *minor_status, gss_ctx_id_t *context_handle,
                krb5_gss_ctx_ext_t exts)
 {
     krb5_context context;
-    unsigned char *ptr;
     krb5_gss_cred_id_t cred = 0;
     krb5_data ap_rep, ap_req;
     krb5_error_code code;
@@ -723,56 +760,21 @@ kg_accept_krb5(OM_uint32 *minor_status, gss_ctx_id_t *context_handle,
         goto fail;
     }
 
-    /* verify the token's integrity, and leave the token in ap_req.
-       figure out which mech oid was used, and save it */
-
-    ptr = (unsigned char *) input_token->value;
-
-    if (!(code = g_verify_token_header(gss_mech_krb5,
-                                       &(ap_req.length),
-                                       &ptr, KG_TOK_CTX_AP_REQ,
-                                       input_token->length, 1))) {
-        mech_used = gss_mech_krb5;
-    } else if ((code == G_WRONG_MECH)
-               &&!(code = g_verify_token_header((gss_OID) gss_mech_iakerb,
-                                                &(ap_req.length),
-                                                &ptr, KG_TOK_CTX_AP_REQ,
-                                                input_token->length, 1))) {
-        mech_used = gss_mech_iakerb;
-    } else if ((code == G_WRONG_MECH)
-               &&!(code = g_verify_token_header((gss_OID) gss_mech_krb5_wrong,
-                                                &(ap_req.length),
-                                                &ptr, KG_TOK_CTX_AP_REQ,
-                                                input_token->length, 1))) {
-        mech_used = gss_mech_krb5_wrong;
-    } else if ((code == G_WRONG_MECH) &&
-               !(code = g_verify_token_header(gss_mech_krb5_old,
-                                              &(ap_req.length),
-                                              &ptr, KG_TOK_CTX_AP_REQ,
-                                              input_token->length, 1))) {
-        /*
-         * Previous versions of this library used the old mech_id
-         * and some broken behavior (wrong IV on checksum
-         * encryption).  We support the old mech_id for
-         * compatibility, and use it to decide when to use the
-         * old behavior.
-         */
-        mech_used = gss_mech_krb5_old;
-    } else if (code == G_WRONG_TOKID) {
+    code = parse_init_token(input_token, &mech_used, &ap_req);
+    if (code == G_WRONG_TOKID) {
         major_status = GSS_S_CONTINUE_NEEDED;
         code = KRB5KRB_AP_ERR_MSG_TYPE;
         mech_used = gss_mech_krb5;
         goto fail;
     } else if (code == G_BAD_TOK_HEADER) {
         /* DCE style not encapsulated */
-        ap_req.length = input_token->length;
+        ap_req = make_data(input_token->value, input_token->length);
         mech_used = gss_mech_krb5;
         no_encap = 1;
-    } else {
+    } else if (code) {
         major_status = GSS_S_DEFECTIVE_TOKEN;
         goto fail;
     }
-    ap_req.data = (char *)ptr;
 
     /* construct the sender_addr */
 
diff --git a/src/lib/gssapi/krb5/iakerb.c b/src/lib/gssapi/krb5/iakerb.c
index 69c3445d6..539b23195 100644
--- a/src/lib/gssapi/krb5/iakerb.c
+++ b/src/lib/gssapi/krb5/iakerb.c
@@ -170,8 +170,7 @@ iakerb_parse_token(iakerb_ctx_id_t ctx,
 {
     krb5_error_code code;
     krb5_iakerb_header *iah = NULL;
-    unsigned int bodysize;
-    uint8_t *body;
+    const uint8_t *token_body;
     krb5_data data;
     struct k5input in, seq;
 
@@ -180,21 +179,21 @@ iakerb_parse_token(iakerb_ctx_id_t ctx,
         goto cleanup;
     }
 
-    body = token->value;
-    code = g_verify_token_header(gss_mech_iakerb, &bodysize, &body,
-                                 IAKERB_TOK_PROXY, token->length,
-                                 G_VFY_TOKEN_HDR_WRAPPER_REQUIRED);
-    if (code != 0)
+    k5_input_init(&in, token->value, token->length);
+    if (!g_verify_token_header(&in, gss_mech_iakerb) ||
+        k5_input_get_uint16_be(&in) != IAKERB_TOK_PROXY) {
+        code = G_BAD_TOK_HEADER;
         goto cleanup;
+    }
 
     /* Find the end of the DER sequence tag and decode it (with the tag) as the
-     * IAKERB jeader. */
-    k5_input_init(&in, body, bodysize);
+     * IAKERB header. */
+    token_body = in.ptr;
     if (!k5_der_get_value(&in, 0x30, &seq)) {
         code = ASN1_BAD_ID;
         goto cleanup;
     }
-    data = make_data(body, seq.ptr + seq.len - body);
+    data = make_data((uint8_t *)token_body, seq.ptr + seq.len - token_body);
     code = decode_krb5_iakerb_header(&data, &iah);
     if (code != 0)
         goto cleanup;
@@ -767,16 +766,11 @@ iakerb_gss_delete_sec_context(OM_uint32 *minor_status,
 static krb5_boolean
 iakerb_is_iakerb_token(const gss_buffer_t token)
 {
-    krb5_error_code code;
-    unsigned int bodysize = token->length;
-    unsigned char *ptr = token->value;
-
-    code = g_verify_token_header(gss_mech_iakerb,
-                                 &bodysize, &ptr,
-                                 IAKERB_TOK_PROXY,
-                                 token->length, 0);
+    struct k5input in;
 
-    return (code == 0);
+    k5_input_init(&in, token->value, token->length);
+    return g_verify_token_header(&in, gss_mech_iakerb) &&
+        k5_input_get_uint16_be(&in) == IAKERB_TOK_PROXY;
 }
 
 static void
diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c
index 0a088c574..27011d0c9 100644
--- a/src/lib/gssapi/krb5/init_sec_context.c
+++ b/src/lib/gssapi/krb5/init_sec_context.c
@@ -709,8 +709,9 @@ mutual_auth(
     krb5_context context)
 {
     OM_uint32 major_status;
-    unsigned char *ptr;
-    krb5_data ap_rep;
+    struct k5input in;
+    uint16_t toktype;
+    krb5_data body;
     krb5_ap_rep_enc_part *ap_rep_data;
     krb5_timestamp now;
     krb5_gss_ctx_id_rec *ctx;
@@ -753,24 +754,19 @@ mutual_auth(
         goto fail;
     }
 
-    ptr = (unsigned char *) input_token->value;
-
     if (ctx->gss_flags & GSS_C_DCE_STYLE) {
         /* Raw AP-REP */
-        ap_rep.length = input_token->length;
-    } else if (g_verify_token_header(ctx->mech_used,
-                                     &(ap_rep.length),
-                                     &ptr, KG_TOK_CTX_AP_REP,
-                                     input_token->length, 1)) {
-        if (g_verify_token_header((gss_OID) ctx->mech_used,
-                                  &(ap_rep.length),
-                                  &ptr, KG_TOK_CTX_ERROR,
-                                  input_token->length, 1) == 0) {
-
-            /* Handle a KRB_ERROR message from the server */
-
-            ap_rep.data = (char *)ptr;
-            code = krb5_rd_error(context, &ap_rep, &krb_error);
+        body = make_data(input_token->value, input_token->length);
+    } else {
+        k5_input_init(&in, input_token->value, input_token->length);
+        if (!g_verify_token_header(&in, ctx->mech_used)) {
+            *minor_status = 0;
+            return(GSS_S_DEFECTIVE_TOKEN);
+        }
+        toktype = k5_input_get_uint16_be(&in);
+        body = make_data((uint8_t *)in.ptr, in.len);
+        if (toktype == KG_TOK_CTX_ERROR) {
+            code = krb5_rd_error(context, &body, &krb_error);
             if (code)
                 goto fail;
             if (krb_error->error)
@@ -779,24 +775,22 @@ mutual_auth(
                 code = 0;
             krb5_free_error(context, krb_error);
             goto fail;
-        } else {
+        } else if (toktype != KG_TOK_CTX_AP_REP) {
             *minor_status = 0;
             return(GSS_S_DEFECTIVE_TOKEN);
         }
     }
-    ap_rep.data = (char *)ptr;
 
     /* decode the ap_rep */
-    if ((code = krb5_rd_rep(context, ctx->auth_context, &ap_rep,
-                            &ap_rep_data))) {
+    code = krb5_rd_rep(context, ctx->auth_context, &body, &ap_rep_data);
+    if (code) {
         /*
          * XXX A hack for backwards compatibility.
          * To be removed in 1999 -- proven
          */
         krb5_auth_con_setuseruserkey(context, ctx->auth_context,
                                      &ctx->subkey->keyblock);
-        if ((krb5_rd_rep(context, ctx->auth_context, &ap_rep,
-                         &ap_rep_data)))
+        if (krb5_rd_rep(context, ctx->auth_context, &body, &ap_rep_data) != 0)
             goto fail;
     }
 
diff --git a/src/lib/gssapi/krb5/k5unseal.c b/src/lib/gssapi/krb5/k5unseal.c
index c63e04bd1..5e57487da 100644
--- a/src/lib/gssapi/krb5/k5unseal.c
+++ b/src/lib/gssapi/krb5/k5unseal.c
@@ -358,12 +358,9 @@ kg_unseal(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
           int *conf_state, gss_qop_t *qop_state, int toktype)
 {
     krb5_gss_ctx_id_rec *ctx;
-    unsigned char *ptr;
-    unsigned int bodysize;
-    int err;
     int toktype2;
-    int vfyflags = 0;
     OM_uint32 ret;
+    struct k5input in;
 
     ctx = (krb5_gss_ctx_id_rec *) context_handle;
 
@@ -376,42 +373,25 @@ kg_unseal(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
 
     /* verify the header */
 
-    ptr = (unsigned char *) input_token_buffer->value;
-
-
-    err = g_verify_token_header(ctx->mech_used,
-                                &bodysize, &ptr, -1,
-                                input_token_buffer->length,
-                                vfyflags);
-    if (err) {
-        *minor_status = err;
-        return GSS_S_DEFECTIVE_TOKEN;
-    }
-
-    if (bodysize < 2) {
-        *minor_status = (OM_uint32)G_BAD_TOK_HEADER;
-        return GSS_S_DEFECTIVE_TOKEN;
-    }
-
-    toktype2 = load_16_be(ptr);
-
-    ptr += 2;
-    bodysize -= 2;
+    k5_input_init(&in, input_token_buffer->value, input_token_buffer->length);
+    (void)g_verify_token_header(&in, ctx->mech_used);
+    toktype2 = k5_input_get_uint16_be(&in);
 
     switch (toktype2) {
     case KG2_TOK_MIC_MSG:
     case KG2_TOK_WRAP_MSG:
     case KG2_TOK_DEL_CTX:
         ret = gss_krb5int_unseal_token_v3(&ctx->k5_context, minor_status, ctx,
-                                          ptr, bodysize, message_buffer,
-                                          conf_state, qop_state, toktype);
+                                          (uint8_t *)in.ptr, in.len,
+                                          message_buffer, conf_state,
+                                          qop_state, toktype);
         break;
     case KG_TOK_MIC_MSG:
     case KG_TOK_WRAP_MSG:
     case KG_TOK_DEL_CTX:
-        ret = kg_unseal_v1(ctx->k5_context, minor_status, ctx, ptr, bodysize,
-                           message_buffer, conf_state, qop_state,
-                           toktype);
+        ret = kg_unseal_v1(ctx->k5_context, minor_status, ctx,
+                           (uint8_t *)in.ptr, in.len, message_buffer,
+                           conf_state, qop_state, toktype);
         break;
     default:
         *minor_status = (OM_uint32)G_BAD_TOK_HEADER;
diff --git a/src/lib/gssapi/krb5/k5unsealiov.c b/src/lib/gssapi/krb5/k5unsealiov.c
index 21b501731..de79f3016 100644
--- a/src/lib/gssapi/krb5/k5unsealiov.c
+++ b/src/lib/gssapi/krb5/k5unsealiov.c
@@ -266,73 +266,6 @@ cleanup:
     return retval;
 }
 
-/* Similar to k5_der_get_value(), but output an unchecked content length
- * instead of a k5input containing the contents. */
-static inline bool
-get_der_tag(struct k5input *in, uint8_t idbyte, size_t *len_out)
-{
-    uint8_t lenbyte, i;
-    size_t len;
-
-    /* Do nothing if in is empty or the next byte doesn't match idbyte. */
-    if (in->status || in->len == 0 || *in->ptr != idbyte)
-        return false;
-
-    /* Advance past the identifier byte and decode the length. */
-    (void)k5_input_get_byte(in);
-    lenbyte = k5_input_get_byte(in);
-    if (lenbyte < 128) {
-        len = lenbyte;
-    } else {
-        len = 0;
-        for (i = 0; i < (lenbyte & 0x7F); i++) {
-            if (len > (SIZE_MAX >> 8)) {
-                k5_input_set_status(in, EOVERFLOW);
-                return false;
-            }
-            len = (len << 8) | k5_input_get_byte(in);
-        }
-    }
-
-    if (in->status)
-        return false;
-
-    *len_out = len;
-    return true;
-}
-
-/*
- * Similar to g_verify_token_header() without toktype or flags, but do not read
- * more than *header_len bytes of ASN.1 wrapper, and on output set *header_len
- * to the remaining number of header bytes.  Verify the outer DER tag's length
- * against token_len, which may be larger (but not smaller) than *header_len.
- */
-static gss_int32
-verify_detached_wrapper(const gss_OID_desc *mech, size_t *header_len,
-                        uint8_t **header_in, size_t token_len)
-{
-    struct k5input in, mech_der;
-    gss_OID_desc toid;
-    size_t len;
-
-    k5_input_init(&in, *header_in, *header_len);
-
-    if (get_der_tag(&in, 0x60, &len)) {
-        if (len != token_len - (in.ptr - *header_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;
-    }
-
-    *header_in = (uint8_t *)in.ptr;
-    *header_len = in.len;
-    return 0;
-}
-
 /*
  * Caller must provide TOKEN | DATA | PADDING | TRAILER, except
  * for DCE in which case it can just provide TOKEN | DATA (must
@@ -349,11 +282,12 @@ kg_unseal_iov_token(OM_uint32 *minor_status,
 {
     krb5_error_code code;
     krb5_context context = ctx->k5_context;
-    unsigned char *ptr;
+    struct k5input in;
+    gss_OID_desc mech;
+    size_t tlen, header_tlen;
     gss_iov_buffer_t header;
     gss_iov_buffer_t padding;
     gss_iov_buffer_t trailer;
-    size_t input_length, hlen;
     int toktype2;
 
     header = kg_locate_header_iov(iov, iov_count, toktype);
@@ -365,40 +299,32 @@ kg_unseal_iov_token(OM_uint32 *minor_status,
     padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
     trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
 
-    ptr = (unsigned char *)header->buffer.value;
-    input_length = header->buffer.length;
-
+    tlen = header->buffer.length;
     if ((ctx->gss_flags & GSS_C_DCE_STYLE) == 0 &&
         toktype == KG_TOK_WRAP_MSG) {
         size_t data_length, assoc_data_length;
 
         kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length);
 
-        input_length += data_length - assoc_data_length;
+        tlen += data_length - assoc_data_length;
 
         if (padding != NULL)
-            input_length += padding->buffer.length;
+            tlen += padding->buffer.length;
 
         if (trailer != NULL)
-            input_length += trailer->buffer.length;
+            tlen += trailer->buffer.length;
     }
 
-    hlen = header->buffer.length;
-    code = verify_detached_wrapper(ctx->mech_used, &hlen, &ptr, input_length);
-    if (code != 0) {
-        *minor_status = code;
-        return GSS_S_DEFECTIVE_TOKEN;
-    }
-
-    if (hlen < 2) {
-        *minor_status = (OM_uint32)G_BAD_TOK_HEADER;
-        return GSS_S_DEFECTIVE_TOKEN;
+    /* If there is a token header, advance past it and verify its mech and
+     * token length. */
+    k5_input_init(&in, header->buffer.value, header->buffer.length);
+    if (g_get_token_header(&in, &mech, &header_tlen)) {
+        if (!g_OID_equal(&mech, ctx->mech_used) || header_tlen != tlen) {
+            *minor_status = G_BAD_TOK_HEADER;
+            return GSS_S_DEFECTIVE_TOKEN;
+        }
     }
-
-    toktype2 = load_16_be(ptr);
-
-    ptr += 2;
-    hlen -= 2;
+    toktype2 = k5_input_get_uint16_be(&in);
 
     switch (toktype2) {
     case KG2_TOK_MIC_MSG:
@@ -411,7 +337,7 @@ kg_unseal_iov_token(OM_uint32 *minor_status,
     case KG_TOK_WRAP_MSG:
     case KG_TOK_DEL_CTX:
         code = kg_unseal_v1_iov(context, minor_status, ctx, iov, iov_count,
-                                (size_t)(ptr - (unsigned char *)header->buffer.value),
+                                (size_t)(in.ptr - (unsigned char *)header->buffer.value),
                                 conf_state, qop_state, toktype);
         break;
     default:
@@ -439,6 +365,7 @@ kg_unseal_stream_iov(OM_uint32 *minor_status,
                      int iov_count,
                      int toktype)
 {
+    struct k5input in;
     unsigned char *ptr;
     unsigned int bodysize;
     OM_uint32 code = 0, major_status = GSS_S_FAILURE;
@@ -461,23 +388,16 @@ kg_unseal_stream_iov(OM_uint32 *minor_status,
 
     ptr = (unsigned char *)stream->buffer.value;
 
-    code = g_verify_token_header(ctx->mech_used,
-                                 &bodysize, &ptr, -1,
-                                 stream->buffer.length, 0);
-    if (code != 0) {
-        major_status = GSS_S_DEFECTIVE_TOKEN;
-        goto cleanup;
-    }
-
-    if (bodysize < 2) {
+    k5_input_init(&in, stream->buffer.value, stream->buffer.length);
+    (void)g_verify_token_header(&in, ctx->mech_used);
+    toktype2 = k5_input_get_uint16_be(&in);
+    if (in.status) {
         *minor_status = (OM_uint32)G_BAD_TOK_HEADER;
         return GSS_S_DEFECTIVE_TOKEN;
     }
 
-    toktype2 = load_16_be(ptr);
-
-    ptr += 2;
-    bodysize -= 2;
+    ptr = (uint8_t *)in.ptr;
+    bodysize = in.len;
 
     tiov = (gss_iov_buffer_desc *)calloc((size_t)iov_count + 2, sizeof(gss_iov_buffer_desc));
     if (tiov == NULL) {
diff --git a/src/lib/gssapi/mechglue/g_decapsulate_token.c b/src/lib/gssapi/mechglue/g_decapsulate_token.c
index 1c04e2f27..0b7346373 100644
--- a/src/lib/gssapi/mechglue/g_decapsulate_token.c
+++ b/src/lib/gssapi/mechglue/g_decapsulate_token.c
@@ -37,9 +37,7 @@ gss_decapsulate_token(gss_const_buffer_t input_token,
                       gss_const_OID token_oid,
                       gss_buffer_t output_token)
 {
-    OM_uint32 minor;
-    unsigned int body_size = 0;
-    unsigned char *buf_in;
+    struct k5input in;
 
     if (input_token == GSS_C_NO_BUFFER || token_oid == GSS_C_NO_OID)
         return GSS_S_CALL_INACCESSIBLE_READ;
@@ -47,20 +45,16 @@ gss_decapsulate_token(gss_const_buffer_t input_token,
     if (output_token == GSS_C_NO_BUFFER)
         return GSS_S_CALL_INACCESSIBLE_WRITE;
 
-    buf_in = input_token->value;
-
-    minor = g_verify_token_header(token_oid, &body_size, &buf_in,
-                                  -1, input_token->length,
-                                  G_VFY_TOKEN_HDR_WRAPPER_REQUIRED);
-    if (minor != 0)
+    k5_input_init(&in, input_token->value, input_token->length);
+    if (!g_verify_token_header(&in, token_oid))
         return GSS_S_DEFECTIVE_TOKEN;
 
-    output_token->value = gssalloc_malloc(body_size);
+    output_token->value = gssalloc_malloc(in.len);
     if (output_token->value == NULL)
         return GSS_S_FAILURE;
 
-    memcpy(output_token->value, buf_in, body_size);
-    output_token->length = body_size;
+    memcpy(output_token->value, in.ptr, in.len);
+    output_token->length = in.len;
 
     return GSS_S_COMPLETE;
 }
diff --git a/src/lib/gssapi/mechglue/g_glue.c b/src/lib/gssapi/mechglue/g_glue.c
index 47f499307..76da8a83c 100644
--- a/src/lib/gssapi/mechglue/g_glue.c
+++ b/src/lib/gssapi/mechglue/g_glue.c
@@ -39,27 +39,6 @@ extern gss_mechanism *gssint_mechs_array;
  * This file contains the support routines for the glue layer.
  */
 
-/* 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)
-{
-    struct k5input in;
-
-    if (oid_out == NULL)
-	return (GSS_S_CALL_INACCESSIBLE_WRITE);
-    if (token == NULL || token->value == NULL)
-	return (GSS_S_DEFECTIVE_TOKEN);
-
-    k5_input_init(&in, token->value, token->length);
-    if (!k5_der_get_value(&in, 0x60, &in))
-	return (GSS_S_DEFECTIVE_TOKEN);
-    if (!k5_der_get_value(&in, 0x06, &in))
-	return (GSS_S_DEFECTIVE_TOKEN);
-    oid_out->length = in.len;
-    oid_out->elements = (uint8_t *)in.ptr;
-    return (GSS_S_COMPLETE);
-}
-
 /*
  * The following mechanisms do not always identify themselves
  * per the GSS-API specification, when interoperating with MS
@@ -78,6 +57,9 @@ static gss_OID_desc gss_krb5_mechanism_oid_desc =
 OM_uint32
 gssint_get_mech_type(gss_OID OID, gss_buffer_t token)
 {
+    struct k5input in;
+    size_t tlen;
+
     /* Check for interoperability exceptions */
     if (token->length >= sizeof(NTLMSSP_SIGNATURE) &&
 	memcmp(token->value, NTLMSSP_SIGNATURE,
@@ -90,7 +72,9 @@ gssint_get_mech_type(gss_OID OID, gss_buffer_t token)
     } else if (token->length == 0) {
 	*OID = gss_spnego_mechanism_oid_desc;
     } else {
-	return gssint_get_mech_type_oid(OID, token);
+	k5_input_init(&in, token->value, token->length);
+	return (g_get_token_header(&in, OID, &tlen) ? GSS_S_COMPLETE :
+		GSS_S_DEFECTIVE_TOKEN);
     }
 
     return (GSS_S_COMPLETE);
diff --git a/src/lib/gssapi/mechglue/mglueP.h b/src/lib/gssapi/mechglue/mglueP.h
index 7f836fbb0..edd759cb0 100644
--- a/src/lib/gssapi/mechglue/mglueP.h
+++ b/src/lib/gssapi/mechglue/mglueP.h
@@ -79,8 +79,6 @@ typedef struct gss_cred_id_struct {
 /* it to initialize the GSSAPI library		  */
 int gssint_mechglue_initialize_library(void);
 
-OM_uint32 gssint_get_mech_type_oid(gss_OID OID, gss_buffer_t token);
-
 /*
  * This table is used to access mechanism-specific versions of the GSSAPI
  * functions.  It contains all of the functions defined in gssapi.h except for


More information about the cvs-krb5 mailing list