Merge branch 'master' into tlv-mic
authorLuke Howard <lukeh@padl.com>
Tue, 26 Apr 2011 15:58:13 +0000 (17:58 +0200)
committerLuke Howard <lukeh@padl.com>
Tue, 26 Apr 2011 15:58:13 +0000 (17:58 +0200)
14 files changed:
mech_eap/Makefile.am
mech_eap/accept_sec_context.c
mech_eap/export_sec_context.c
mech_eap/gssapiP_eap.h
mech_eap/import_sec_context.c
mech_eap/init_sec_context.c
mech_eap/inquire_name.c
mech_eap/mech_eap.exports
mech_eap/userok.c [deleted file]
mech_eap/util.h
mech_eap/util_attr.cpp
mech_eap/util_context.c
mech_eap/util_sm.c
mech_eap/util_token.c

index 3bf752d..58d7b2c 100644 (file)
@@ -24,6 +24,7 @@ mech_eap_la_SOURCES =                         \
        acquire_cred_with_password.c            \
        add_cred.c                              \
        add_cred_with_password.c                \
+       authorize_localname.c                   \
        canonicalize_name.c                     \
        compare_name.c                          \
        context_time.c                          \
@@ -68,7 +69,6 @@ mech_eap_la_SOURCES =                         \
        store_cred.c                            \
        unwrap.c                                \
        unwrap_iov.c                            \
-       userok.c                                \
        util_attr.cpp                           \
        util_base64.c                           \
        util_buffer.c                           \
index cc8702d..17d1020 100644 (file)
@@ -131,6 +131,49 @@ acceptReadyEap(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred)
 }
 
 static OM_uint32
+gssEapSupportedInitiatorExts[] = {
+};
+
+static struct gss_eap_itok_map
+gssEapAcceptorExtsFlagMap[] = {
+    { ITOK_TYPE_REAUTH_CREDS, CTX_FLAG_KRB_REAUTH_SUPPORTED },
+};
+
+static OM_uint32
+eapGssSmAcceptExts(OM_uint32 *minor,
+                   gss_cred_id_t cred GSSEAP_UNUSED,
+                   gss_ctx_id_t ctx GSSEAP_UNUSED,
+                   gss_name_t target GSSEAP_UNUSED,
+                   gss_OID mech GSSEAP_UNUSED,
+                   OM_uint32 reqFlags GSSEAP_UNUSED,
+                   OM_uint32 timeReq GSSEAP_UNUSED,
+                   gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+                   gss_buffer_t inputToken,
+                   gss_buffer_t outputToken,
+                   OM_uint32 *smFlags GSSEAP_UNUSED)
+{
+    OM_uint32 major;
+
+    major = gssEapProcessSupportedExts(minor, inputToken,
+                                       gssEapAcceptorExtsFlagMap,
+                                        sizeof(gssEapAcceptorExtsFlagMap) /
+                                       sizeof(gssEapAcceptorExtsFlagMap[0]),
+                                       &ctx->flags);
+    if (GSS_ERROR(major))
+        return major;
+
+    major = gssEapEncodeSupportedExts(minor,
+                                      gssEapSupportedInitiatorExts,
+                                      sizeof(gssEapSupportedInitiatorExts) /
+                                        sizeof(gssEapSupportedInitiatorExts[0]),
+                                      outputToken);
+    if (GSS_ERROR(major))
+        return major;
+
+    return GSS_S_CONTINUE_NEEDED;
+}
+
+static OM_uint32
 eapGssSmAcceptAcceptorName(OM_uint32 *minor,
                            gss_cred_id_t cred GSSEAP_UNUSED,
                            gss_ctx_id_t ctx,
@@ -231,7 +274,7 @@ eapGssSmAcceptIdentity(OM_uint32 *minor,
     GSSEAP_SM_TRANSITION_NEXT(ctx);
 
     *minor = 0;
-    *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
+    *smFlags |= SM_FLAG_SEND_TOKEN | SM_FLAG_OUTPUT_TOKEN_CRITICAL;
 
     return GSS_S_CONTINUE_NEEDED;
 }
@@ -596,7 +639,7 @@ eapGssSmAcceptAuthenticate(OM_uint32 *minor,
 
     major = GSS_S_CONTINUE_NEEDED;
     *minor = 0;
-    *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
+    *smFlags |= SM_FLAG_SEND_TOKEN | SM_FLAG_OUTPUT_TOKEN_CRITICAL;
 
 cleanup:
     if (request != NULL)
@@ -616,6 +659,40 @@ cleanup:
 }
 
 static OM_uint32
+eapGssSmAcceptGssFlags(OM_uint32 *minor,
+                       gss_cred_id_t cred GSSEAP_UNUSED,
+                       gss_ctx_id_t ctx,
+                       gss_name_t target GSSEAP_UNUSED,
+                       gss_OID mech GSSEAP_UNUSED,
+                       OM_uint32 reqFlags GSSEAP_UNUSED,
+                       OM_uint32 timeReq GSSEAP_UNUSED,
+                       gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+                       gss_buffer_t inputToken,
+                       gss_buffer_t outputToken GSSEAP_UNUSED,
+                       OM_uint32 *smFlags GSSEAP_UNUSED)
+{
+    unsigned char *p;
+    OM_uint32 initiatorGssFlags;
+
+    assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
+
+    if (inputToken->length < 4) {
+        *minor = GSSEAP_TOK_TRUNC;
+        return GSS_S_DEFECTIVE_TOKEN;
+    }
+
+    /* allow flags to grow for future expansion */
+    p = (unsigned char *)inputToken->value + inputToken->length - 4;
+
+    initiatorGssFlags = load_uint32_be(p);
+    initiatorGssFlags &= GSSEAP_WIRE_FLAGS_MASK;
+
+    ctx->gssFlags |= initiatorGssFlags;
+
+    return GSS_S_CONTINUE_NEEDED;
+}
+
+static OM_uint32
 eapGssSmAcceptGssChannelBindings(OM_uint32 *minor,
                                  gss_cred_id_t cred GSSEAP_UNUSED,
                                  gss_ctx_id_t ctx,
@@ -631,6 +708,8 @@ eapGssSmAcceptGssChannelBindings(OM_uint32 *minor,
     OM_uint32 major, tmpMinor;
     gss_iov_buffer_desc iov[2];
 
+    assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
+
     iov[0].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE;
     iov[0].buffer.length = 0;
     iov[0].buffer.value = NULL;
@@ -677,29 +756,39 @@ eapGssSmAcceptReauthCreds(OM_uint32 *minor,
      * If we're built with fast reauthentication enabled, then
      * fabricate a ticket from the initiator to ourselves.
      */
-    major = gssEapMakeReauthCreds(minor, ctx, cred, outputToken);
+    if (ctx->flags & CTX_FLAG_KRB_REAUTH_SUPPORTED)
+        major = gssEapMakeReauthCreds(minor, ctx, cred, outputToken);
+    else
+        major = GSS_S_UNAVAILABLE;
+
     if (major == GSS_S_UNAVAILABLE)
         major = GSS_S_COMPLETE;
-    if (major == GSS_S_COMPLETE)
-        major = GSS_S_CONTINUE_NEEDED;
 
-    return major;
+    return GSS_ERROR(major) ? major : GSS_S_CONTINUE_NEEDED;
 }
 #endif
 
 static OM_uint32
-eapGssSmAcceptCompleteInitiatorExts(OM_uint32 *minor,
-                                    gss_cred_id_t cred GSSEAP_UNUSED,
-                                    gss_ctx_id_t ctx,
-                                    gss_name_t target GSSEAP_UNUSED,
-                                    gss_OID mech GSSEAP_UNUSED,
-                                    OM_uint32 reqFlags GSSEAP_UNUSED,
-                                    OM_uint32 timeReq GSSEAP_UNUSED,
-                                    gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
-                                    gss_buffer_t inputToken GSSEAP_UNUSED,
-                                    gss_buffer_t outputToken GSSEAP_UNUSED,
-                                    OM_uint32 *smFlags GSSEAP_UNUSED)
+eapGssSmAcceptInitiatorMIC(OM_uint32 *minor,
+                           gss_cred_id_t cred GSSEAP_UNUSED,
+                           gss_ctx_id_t ctx,
+                           gss_name_t target GSSEAP_UNUSED,
+                           gss_OID mech GSSEAP_UNUSED,
+                           OM_uint32 reqFlags GSSEAP_UNUSED,
+                           OM_uint32 timeReq GSSEAP_UNUSED,
+                           gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+                           gss_buffer_t inputToken,
+                           gss_buffer_t outputToken GSSEAP_UNUSED,
+                           OM_uint32 *smFlags GSSEAP_UNUSED)
 {
+    OM_uint32 major;
+
+    assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
+
+    major = gssEapVerifyConversationMIC(minor, ctx, inputToken);
+    if (GSS_ERROR(major))
+        return major;
+
     GSSEAP_SM_TRANSITION_NEXT(ctx);
 
     *minor = 0;
@@ -708,27 +797,45 @@ eapGssSmAcceptCompleteInitiatorExts(OM_uint32 *minor,
 }
 
 static OM_uint32
-eapGssSmAcceptCompleteAcceptorExts(OM_uint32 *minor,
-                                   gss_cred_id_t cred GSSEAP_UNUSED,
-                                   gss_ctx_id_t ctx,
-                                   gss_name_t target GSSEAP_UNUSED,
-                                   gss_OID mech GSSEAP_UNUSED,
-                                   OM_uint32 reqFlags GSSEAP_UNUSED,
-                                   OM_uint32 timeReq GSSEAP_UNUSED,
-                                   gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
-                                   gss_buffer_t inputToken GSSEAP_UNUSED,
-                                   gss_buffer_t outputToken GSSEAP_UNUSED,
-                                   OM_uint32 *smFlags)
+eapGssSmAcceptAcceptorMIC(OM_uint32 *minor,
+                          gss_cred_id_t cred GSSEAP_UNUSED,
+                          gss_ctx_id_t ctx,
+                          gss_name_t target GSSEAP_UNUSED,
+                          gss_OID mech GSSEAP_UNUSED,
+                          OM_uint32 reqFlags GSSEAP_UNUSED,
+                          OM_uint32 timeReq GSSEAP_UNUSED,
+                          gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+                          gss_buffer_t inputToken GSSEAP_UNUSED,
+                          gss_buffer_t outputToken,
+                          OM_uint32 *smFlags)
 {
+    OM_uint32 major;
+
+    major = gssEapGetConversationMIC(minor, ctx, outputToken);
+    if (GSS_ERROR(major))
+        return major;
+
     GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
 
     *minor = 0;
-    *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
+    *smFlags |= SM_FLAG_SEND_TOKEN | SM_FLAG_OUTPUT_TOKEN_CRITICAL;
 
     return GSS_S_COMPLETE;
 }
 
+/*
+ * Acceptor state machine.
+ */
 static struct gss_eap_sm eapGssAcceptorSm[] = {
+#ifdef GSSEAP_ENABLE_REAUTH
+    {
+        ITOK_TYPE_REAUTH_REQ,
+        ITOK_TYPE_REAUTH_RESP,
+        GSSEAP_STATE_INITIAL | GSSEAP_STATE_REAUTHENTICATE,
+        0,
+        eapGssSmAcceptGssReauth,
+    },
+#endif
     {
         ITOK_TYPE_ACCEPTOR_NAME_REQ,
         ITOK_TYPE_ACCEPTOR_NAME_RESP,
@@ -736,22 +843,20 @@ static struct gss_eap_sm eapGssAcceptorSm[] = {
         0,
         eapGssSmAcceptAcceptorName
     },
-#ifdef GSSEAP_DEBUG
     {
-        ITOK_TYPE_VENDOR_INFO,
-        ITOK_TYPE_NONE,
+        ITOK_TYPE_SUPPORTED_ACCEPTOR_EXTS,
+        ITOK_TYPE_SUPPORTED_INITIATOR_EXTS,
         GSSEAP_STATE_INITIAL,
         0,
-        eapGssSmAcceptVendorInfo,
+        eapGssSmAcceptExts,
     },
-#endif
-#ifdef GSSEAP_ENABLE_REAUTH
+#ifdef GSSEAP_DEBUG
     {
-        ITOK_TYPE_REAUTH_REQ,
-        ITOK_TYPE_REAUTH_RESP,
+        ITOK_TYPE_VENDOR_INFO,
+        ITOK_TYPE_NONE,
         GSSEAP_STATE_INITIAL,
         0,
-        eapGssSmAcceptGssReauth,
+        eapGssSmAcceptVendorInfo,
     },
 #endif
     {
@@ -769,18 +874,25 @@ static struct gss_eap_sm eapGssAcceptorSm[] = {
         eapGssSmAcceptAuthenticate
     },
     {
+        ITOK_TYPE_GSS_FLAGS,
+        ITOK_TYPE_NONE,
+        GSSEAP_STATE_INITIATOR_EXTS,
+        0,
+        eapGssSmAcceptGssFlags
+    },
+    {
         ITOK_TYPE_GSS_CHANNEL_BINDINGS,
         ITOK_TYPE_NONE,
         GSSEAP_STATE_INITIATOR_EXTS,
-        SM_ITOK_FLAG_REQUIRED,
+        0,
         eapGssSmAcceptGssChannelBindings,
     },
     {
-        ITOK_TYPE_NONE,
+        ITOK_TYPE_INITIATOR_MIC,
         ITOK_TYPE_NONE,
         GSSEAP_STATE_INITIATOR_EXTS,
-        0,
-        eapGssSmAcceptCompleteInitiatorExts,
+        SM_ITOK_FLAG_REQUIRED,
+        eapGssSmAcceptInitiatorMIC,
     },
 #ifdef GSSEAP_ENABLE_REAUTH
     {
@@ -793,10 +905,10 @@ static struct gss_eap_sm eapGssAcceptorSm[] = {
 #endif
     {
         ITOK_TYPE_NONE,
-        ITOK_TYPE_NONE,
+        ITOK_TYPE_ACCEPTOR_MIC,
         GSSEAP_STATE_ACCEPTOR_EXTS,
         0,
-        eapGssSmAcceptCompleteAcceptorExts
+        eapGssSmAcceptAcceptorMIC
     },
 };
 
@@ -952,7 +1064,7 @@ eapGssSmAcceptGssReauth(OM_uint32 *minor,
                         gss_OID mech,
                         OM_uint32 reqFlags GSSEAP_UNUSED,
                         OM_uint32 timeReq GSSEAP_UNUSED,
-                        gss_channel_bindings_t chanBindings,
+                        gss_channel_bindings_t userChanBindings,
                         gss_buffer_t inputToken,
                         gss_buffer_t outputToken,
                         OM_uint32 *smFlags)
@@ -960,6 +1072,7 @@ eapGssSmAcceptGssReauth(OM_uint32 *minor,
     OM_uint32 major, tmpMinor;
     gss_name_t krbInitiator = GSS_C_NO_NAME;
     OM_uint32 gssFlags, timeRec = GSS_C_INDEFINITE;
+    struct gss_channel_bindings_struct wireChanBindings = { 0 };
 
     /*
      * If we're built with fast reauthentication support, it's valid
@@ -970,11 +1083,25 @@ eapGssSmAcceptGssReauth(OM_uint32 *minor,
 
     ctx->flags |= CTX_FLAG_KRB_REAUTH;
 
+    /*
+     * To avoid an additional round trip, we use GSS channel bindings
+     * to integrity protect the rest of the initiator exchange. This
+     * does have the disadvantage of making it impossible for the
+     * acceptor to ignore application channel bindings, behaviour
+     * which differs from normal Kerberos and GSS-EAP itself.
+     */
+    major = gssEapMakeTokenChannelBindings(minor, ctx,
+                                           userChanBindings,
+                                           inputToken,
+                                           &wireChanBindings);
+    if (GSS_ERROR(major))
+        return major;
+
     major = gssAcceptSecContext(minor,
                                 &ctx->reauthCtx,
                                 cred->reauthCred,
                                 inputToken,
-                                chanBindings,
+                                &wireChanBindings,
                                 &krbInitiator,
                                 &mech,
                                 outputToken,
@@ -985,19 +1112,24 @@ eapGssSmAcceptGssReauth(OM_uint32 *minor,
         major = acceptReadyKrb(minor, ctx, cred,
                                krbInitiator, mech, timeRec);
         if (major == GSS_S_COMPLETE) {
-            GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
+            /* Generate acceptor MIC */
+            GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ACCEPTOR_EXTS);
         }
         ctx->gssFlags = gssFlags;
     } else if (GSS_ERROR(major) &&
         (*smFlags & SM_FLAG_INPUT_TOKEN_CRITICAL) == 0) {
-        /* pretend reauthentication attempt never happened */
+        /* Fall back to EAP */
         gssDeleteSecContext(&tmpMinor, &ctx->reauthCtx, GSS_C_NO_BUFFER);
         ctx->flags &= ~(CTX_FLAG_KRB_REAUTH);
         GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL);
-        major = GSS_S_CONTINUE_NEEDED;
+    } else {
+        GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_REAUTHENTICATE);
     }
 
+    major = GSS_S_CONTINUE_NEEDED;
+
     gssReleaseName(&tmpMinor, &krbInitiator);
+    gss_release_buffer(&tmpMinor, &wireChanBindings.application_data);
 
     return major;
 }
index 43f3f28..1426d62 100644 (file)
@@ -38,7 +38,7 @@
 #include "gssapiP_eap.h"
 
 static OM_uint32
-gssEapExportPartialContext(OM_uint32 *minor,
+exportPartialRadiusContext(OM_uint32 *minor,
                            gss_ctx_id_t ctx,
                            gss_buffer_t token)
 {
@@ -143,7 +143,7 @@ gssEapExportSecContext(OM_uint32 *minor,
      */
     if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx) &&
         (ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) {
-        major = gssEapExportPartialContext(minor, ctx, &partialCtx);
+        major = exportPartialRadiusContext(minor, ctx, &partialCtx);
         if (GSS_ERROR(major))
             goto cleanup;
     }
@@ -155,6 +155,9 @@ gssEapExportSecContext(OM_uint32 *minor,
     length += 4 + acceptorName.length;          /* acceptorName.value */
     length += 24 + sequenceSize(ctx->seqState); /* seqState */
 
+    if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx))
+        length += 4 + ctx->conversation.length;
+
     if (partialCtx.value != NULL)
         length += 4 + partialCtx.length;        /* partialCtx.value */
 
@@ -190,6 +193,9 @@ gssEapExportSecContext(OM_uint32 *minor,
     if (GSS_ERROR(major))
         goto cleanup;
 
+    if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx))
+        p = store_buffer(&ctx->conversation, p, FALSE);
+
     if (partialCtx.value != NULL)
         p = store_buffer(&partialCtx, p, FALSE);
 
index d1d6bce..19e7fb2 100644 (file)
@@ -141,6 +141,7 @@ struct gss_cred_id_struct
 
 #define CTX_FLAG_INITIATOR                  0x00000001
 #define CTX_FLAG_KRB_REAUTH                 0x00000002
+#define CTX_FLAG_KRB_REAUTH_SUPPORTED       0x00000004
 
 #define CTX_IS_INITIATOR(ctx)               (((ctx)->flags & CTX_FLAG_INITIATOR) != 0)
 
@@ -203,6 +204,7 @@ struct gss_ctx_id_struct
         #define reauthCtx            ctxU.reauth
 #endif
     } ctxU;
+    gss_buffer_desc conversation;
 };
 
 #define TOK_FLAG_SENDER_IS_ACCEPTOR         0x01
index d571bca..978970a 100644 (file)
@@ -49,7 +49,7 @@
     } while (0)
 
 static OM_uint32
-gssEapImportPartialContext(OM_uint32 *minor,
+importPartialRadiusContext(OM_uint32 *minor,
                            unsigned char **pBuf,
                            size_t *pRemain,
                            gss_ctx_id_t ctx)
@@ -233,6 +233,47 @@ importName(OM_uint32 *minor,
 }
 
 static OM_uint32
+importConversation(OM_uint32 *minor,
+                   unsigned char **pBuf,
+                   size_t *pRemain,
+                   gss_ctx_id_t ctx)
+{
+    OM_uint32 major;
+    unsigned char *p = *pBuf;
+    size_t remain = *pRemain;
+    gss_buffer_desc tmp;
+
+    if (remain < 4) {
+        *minor = GSSEAP_TOK_TRUNC;
+        return GSS_S_DEFECTIVE_TOKEN;
+    }
+
+    tmp.length = load_uint32_be(p);
+    if (tmp.length == 0 ||
+        remain - 4 < tmp.length) {
+        *minor = GSSEAP_TOK_TRUNC;
+        return GSS_S_DEFECTIVE_TOKEN;
+    }
+
+    if (p[4] != 0x06) {
+        *minor = GSSEAP_BAD_TOK_HEADER;
+        return GSS_S_DEFECTIVE_TOKEN;
+    }
+
+    tmp.value = p + 4;
+
+    major = duplicateBuffer(minor, &tmp, &ctx->conversation);
+    if (GSS_ERROR(major))
+        return major;
+
+    *pBuf    += 4 + tmp.length;
+    *pRemain -= 4 + tmp.length;
+
+    *minor = 0;
+    return GSS_S_COMPLETE;
+}
+
+OM_uint32
 gssEapImportContext(OM_uint32 *minor,
                     gss_buffer_t token,
                     gss_ctx_id_t ctx)
@@ -308,11 +349,16 @@ gssEapImportContext(OM_uint32 *minor,
      * The partial context should only be expected for unestablished
      * acceptor contexts.
      */
-    if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx) &&
-        (ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) {
-        major = gssEapImportPartialContext(minor, &p, &remain, ctx);
+    if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx)) {
+        major = importConversation(minor, &p, &remain, ctx);
         if (GSS_ERROR(major))
             return major;
+
+        if ((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) {
+            major = importPartialRadiusContext(minor, &p, &remain, ctx);
+            if (GSS_ERROR(major))
+                return major;
+        }
     }
 
 #ifdef GSSEAP_DEBUG
index 930eb32..becb767 100644 (file)
@@ -223,7 +223,11 @@ peerConfigInit(OM_uint32 *minor,
 
     assert(cred->name != GSS_C_NO_NAME);
 
-    if ((cred->name->flags & (NAME_FLAG_NAI | NAME_FLAG_SERVICE)) == 0) {
+    /*
+     * draft-ietf-abfab-gss-eap-01: the host portion is empty
+     * for initiators.
+     */
+    if ((cred->name->flags & NAME_FLAG_NAI) == 0) {
         *minor = GSSEAP_BAD_INITIATOR_NAME;
         return GSS_S_BAD_NAME;
     }
@@ -443,21 +447,31 @@ eapGssSmInitGssReauth(OM_uint32 *minor,
                       gss_OID mech GSSEAP_UNUSED,
                       OM_uint32 reqFlags,
                       OM_uint32 timeReq,
-                      gss_channel_bindings_t chanBindings,
+                      gss_channel_bindings_t userChanBindings,
                       gss_buffer_t inputToken,
                       gss_buffer_t outputToken,
-                      OM_uint32 *smFlags GSSEAP_UNUSED)
+                      OM_uint32 *smFlags)
 {
     OM_uint32 major, tmpMinor;
     gss_name_t mechTarget = GSS_C_NO_NAME;
     gss_OID actualMech = GSS_C_NO_OID;
     OM_uint32 gssFlags, timeRec;
+    struct gss_channel_bindings_struct wireChanBindings = { 0 };
 
     assert(cred != GSS_C_NO_CREDENTIAL);
 
     if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL) {
-        if (!gssEapCanReauthP(cred, target, timeReq))
-            return GSS_S_CONTINUE_NEEDED;
+        if (!gssEapCanReauthP(cred, target, timeReq)) {
+            major = GSS_S_CONTINUE_NEEDED;
+            goto cleanup;
+        }
+
+        major = gssEapMakeTokenChannelBindings(minor, ctx,
+                                               userChanBindings,
+                                               GSS_C_NO_BUFFER,
+                                               &wireChanBindings);
+        if (GSS_ERROR(major))
+            goto cleanup;
 
         ctx->flags |= CTX_FLAG_KRB_REAUTH;
     } else if ((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0) {
@@ -477,7 +491,7 @@ eapGssSmInitGssReauth(OM_uint32 *minor,
                               (gss_OID)gss_mech_krb5,
                               reqFlags | GSS_C_MUTUAL_FLAG,
                               timeReq,
-                              chanBindings,
+                              &wireChanBindings,
                               inputToken,
                               &actualMech,
                               outputToken,
@@ -494,13 +508,18 @@ eapGssSmInitGssReauth(OM_uint32 *minor,
         major = gssEapReauthComplete(minor, ctx, cred, actualMech, timeRec);
         if (GSS_ERROR(major))
             goto cleanup;
-        GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
+
+        GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ACCEPTOR_EXTS);
     } else {
         GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_REAUTHENTICATE);
+        *smFlags |= SM_FLAG_SEND_TOKEN;
     }
 
+    major = GSS_S_CONTINUE_NEEDED;
+
 cleanup:
     gssReleaseName(&tmpMinor, &mechTarget);
+    gss_release_buffer(&tmpMinor, &wireChanBindings.application_data);
 
     return major;
 }
@@ -577,6 +596,50 @@ eapGssSmInitAcceptorName(OM_uint32 *minor,
 }
 
 static OM_uint32
+gssEapSupportedAcceptorExts[] = {
+    ITOK_TYPE_REAUTH_CREDS,
+};
+
+static struct gss_eap_itok_map
+gssEapInitiatorExtsFlagMap[] = {
+};
+
+static OM_uint32
+eapGssSmInitExts(OM_uint32 *minor,
+                 gss_cred_id_t cred GSSEAP_UNUSED,
+                 gss_ctx_id_t ctx GSSEAP_UNUSED,
+                 gss_name_t target GSSEAP_UNUSED,
+                 gss_OID mech GSSEAP_UNUSED,
+                 OM_uint32 reqFlags GSSEAP_UNUSED,
+                 OM_uint32 timeReq GSSEAP_UNUSED,
+                 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+                 gss_buffer_t inputToken,
+                 gss_buffer_t outputToken,
+                 OM_uint32 *smFlags GSSEAP_UNUSED)
+{
+    OM_uint32 major = GSS_S_COMPLETE;
+
+    if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL) {
+        major = gssEapEncodeSupportedExts(minor,
+                                          gssEapSupportedAcceptorExts,
+                                          sizeof(gssEapSupportedAcceptorExts) /
+                                            sizeof(gssEapSupportedAcceptorExts[0]),
+                                          outputToken);
+    } else if (inputToken != GSS_C_NO_BUFFER) {
+        major = gssEapProcessSupportedExts(minor, inputToken,
+                                          gssEapInitiatorExtsFlagMap,
+                                          sizeof(gssEapInitiatorExtsFlagMap) /
+                                            sizeof(gssEapInitiatorExtsFlagMap[0]),
+                                          &ctx->flags);
+    }
+
+    if (GSS_ERROR(major))
+        return major;
+
+    return GSS_S_CONTINUE_NEEDED;
+}
+
+static OM_uint32
 eapGssSmInitIdentity(OM_uint32 *minor,
                      gss_cred_id_t cred GSSEAP_UNUSED,
                      gss_ctx_id_t ctx,
@@ -601,7 +664,7 @@ eapGssSmInitIdentity(OM_uint32 *minor,
         GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_INITIAL);
     } else
 #endif
-        *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
+        *smFlags |= SM_FLAG_SEND_TOKEN;
 
     assert((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
     assert(inputToken == GSS_C_NO_BUFFER);
@@ -705,7 +768,7 @@ cleanup:
             *minor = tmpMinor;
         }
 
-        *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
+        *smFlags |= SM_FLAG_SEND_TOKEN | SM_FLAG_OUTPUT_TOKEN_CRITICAL;
     }
 
     wpabuf_set(&ctx->initiatorCtx.reqData, NULL, 0);
@@ -715,6 +778,30 @@ cleanup:
 }
 
 static OM_uint32
+eapGssSmInitGssFlags(OM_uint32 *minor,
+                     gss_cred_id_t cred GSSEAP_UNUSED,
+                     gss_ctx_id_t ctx,
+                     gss_name_t target GSSEAP_UNUSED,
+                     gss_OID mech GSSEAP_UNUSED,
+                     OM_uint32 reqFlags GSSEAP_UNUSED,
+                     OM_uint32 timeReq GSSEAP_UNUSED,
+                     gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+                     gss_buffer_t inputToken GSSEAP_UNUSED,
+                     gss_buffer_t outputToken,
+                     OM_uint32 *smFlags GSSEAP_UNUSED)
+{
+    unsigned char wireFlags[4];
+    gss_buffer_desc flagsBuf;
+
+    store_uint32_be(ctx->gssFlags & GSSEAP_WIRE_FLAGS_MASK, wireFlags);
+
+    flagsBuf.length = sizeof(wireFlags);
+    flagsBuf.value = wireFlags;
+
+    return duplicateBuffer(minor, &flagsBuf, outputToken);
+}
+
+static OM_uint32
 eapGssSmInitGssChannelBindings(OM_uint32 *minor,
                                gss_cred_id_t cred GSSEAP_UNUSED,
                                gss_ctx_id_t ctx,
@@ -725,24 +812,19 @@ eapGssSmInitGssChannelBindings(OM_uint32 *minor,
                                gss_channel_bindings_t chanBindings,
                                gss_buffer_t inputToken GSSEAP_UNUSED,
                                gss_buffer_t outputToken,
-                               OM_uint32 *smFlags)
+                               OM_uint32 *smFlags GSSEAP_UNUSED)
 {
     OM_uint32 major;
-    gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
 
-    if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
-        buffer = chanBindings->application_data;
-
-    major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT,
-                       &buffer, NULL, outputToken);
-    if (GSS_ERROR(major))
-        return major;
-
-    assert(outputToken->value != NULL);
+    if ((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0 &&
+        chanBindings != GSS_C_NO_CHANNEL_BINDINGS) {
+        major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT,
+                           &chanBindings->application_data, NULL, outputToken);
+        if (GSS_ERROR(major))
+            return major;
+    }
 
     *minor = 0;
-    *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
-
     return GSS_S_CONTINUE_NEEDED;
 }
 
@@ -774,39 +856,51 @@ eapGssSmInitReauthCreds(OM_uint32 *minor,
 #endif /* GSSEAP_ENABLE_REAUTH */
 
 static OM_uint32
-eapGssSmInitCompleteInitiatorExts(OM_uint32 *minor,
-                                  gss_cred_id_t cred GSSEAP_UNUSED,
-                                  gss_ctx_id_t ctx,
-                                  gss_name_t target GSSEAP_UNUSED,
-                                  gss_OID mech GSSEAP_UNUSED,
-                                  OM_uint32 reqFlags GSSEAP_UNUSED,
-                                  OM_uint32 timeReq GSSEAP_UNUSED,
-                                  gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
-                                  gss_buffer_t inputToken GSSEAP_UNUSED,
-                                  gss_buffer_t outputToken GSSEAP_UNUSED,
-                                  OM_uint32 *smFlags)
+eapGssSmInitInitiatorMIC(OM_uint32 *minor,
+                         gss_cred_id_t cred GSSEAP_UNUSED,
+                         gss_ctx_id_t ctx,
+                         gss_name_t target GSSEAP_UNUSED,
+                         gss_OID mech GSSEAP_UNUSED,
+                         OM_uint32 reqFlags GSSEAP_UNUSED,
+                         OM_uint32 timeReq GSSEAP_UNUSED,
+                         gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+                         gss_buffer_t inputToken GSSEAP_UNUSED,
+                         gss_buffer_t outputToken,
+                         OM_uint32 *smFlags)
 {
+    OM_uint32 major;
+
+    major = gssEapGetConversationMIC(minor, ctx, outputToken);
+    if (GSS_ERROR(major))
+        return major;
+
     GSSEAP_SM_TRANSITION_NEXT(ctx);
 
     *minor = 0;
-    *smFlags |= SM_FLAG_FORCE_SEND_TOKEN;
+    *smFlags |= SM_FLAG_SEND_TOKEN | SM_FLAG_OUTPUT_TOKEN_CRITICAL;
 
     return GSS_S_CONTINUE_NEEDED;
 }
 
 static OM_uint32
-eapGssSmInitCompleteAcceptorExts(OM_uint32 *minor,
-                                 gss_cred_id_t cred GSSEAP_UNUSED,
-                                 gss_ctx_id_t ctx,
-                                 gss_name_t target GSSEAP_UNUSED,
-                                 gss_OID mech GSSEAP_UNUSED,
-                                 OM_uint32 reqFlags GSSEAP_UNUSED,
-                                 OM_uint32 timeReq GSSEAP_UNUSED,
-                                 gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
-                                 gss_buffer_t inputToken GSSEAP_UNUSED,
-                                 gss_buffer_t outputToken GSSEAP_UNUSED,
-                                 OM_uint32 *smFlags GSSEAP_UNUSED)
+eapGssSmInitAcceptorMIC(OM_uint32 *minor,
+                        gss_cred_id_t cred GSSEAP_UNUSED,
+                        gss_ctx_id_t ctx,
+                        gss_name_t target GSSEAP_UNUSED,
+                        gss_OID mech GSSEAP_UNUSED,
+                        OM_uint32 reqFlags GSSEAP_UNUSED,
+                        OM_uint32 timeReq GSSEAP_UNUSED,
+                        gss_channel_bindings_t chanBindings GSSEAP_UNUSED,
+                        gss_buffer_t inputToken,
+                        gss_buffer_t outputToken GSSEAP_UNUSED,
+                        OM_uint32 *smFlags GSSEAP_UNUSED)
 {
+    OM_uint32 major;
+
+    major = gssEapVerifyConversationMIC(minor, ctx, inputToken);
+    if (GSS_ERROR(major))
+        return major;
+
     GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
 
     *minor = 0;
@@ -814,6 +908,9 @@ eapGssSmInitCompleteAcceptorExts(OM_uint32 *minor,
     return GSS_S_COMPLETE;
 }
 
+/*
+ * Initiator state machine.
+ */
 static struct gss_eap_sm eapGssInitiatorSm[] = {
     {
         ITOK_TYPE_CONTEXT_ERR,
@@ -829,6 +926,13 @@ static struct gss_eap_sm eapGssInitiatorSm[] = {
         0,
         eapGssSmInitAcceptorName
     },
+    {
+        ITOK_TYPE_SUPPORTED_INITIATOR_EXTS,
+        ITOK_TYPE_SUPPORTED_ACCEPTOR_EXTS,
+        GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE,
+        0,
+        eapGssSmInitExts
+    },
 #ifdef GSSEAP_DEBUG
     {
         ITOK_TYPE_NONE,
@@ -866,17 +970,24 @@ static struct gss_eap_sm eapGssInitiatorSm[] = {
     },
     {
         ITOK_TYPE_NONE,
+        ITOK_TYPE_GSS_FLAGS,
+        GSSEAP_STATE_INITIATOR_EXTS,
+        0,
+        eapGssSmInitGssFlags
+    },
+    {
+        ITOK_TYPE_NONE,
         ITOK_TYPE_GSS_CHANNEL_BINDINGS,
         GSSEAP_STATE_INITIATOR_EXTS,
-        SM_ITOK_FLAG_REQUIRED,
+        0,
         eapGssSmInitGssChannelBindings
     },
     {
         ITOK_TYPE_NONE,
-        ITOK_TYPE_NONE,
+        ITOK_TYPE_INITIATOR_MIC,
         GSSEAP_STATE_INITIATOR_EXTS,
         0,
-        eapGssSmInitCompleteInitiatorExts
+        eapGssSmInitInitiatorMIC
     },
 #ifdef GSSEAP_ENABLE_REAUTH
     {
@@ -889,11 +1000,11 @@ static struct gss_eap_sm eapGssInitiatorSm[] = {
 #endif
     /* other extensions go here */
     {
-        ITOK_TYPE_NONE,
+        ITOK_TYPE_ACCEPTOR_MIC,
         ITOK_TYPE_NONE,
         GSSEAP_STATE_ACCEPTOR_EXTS,
-        0,
-        eapGssSmInitCompleteAcceptorExts
+        SM_ITOK_FLAG_REQUIRED,
+        eapGssSmInitAcceptorMIC
     }
 };
 
index 2a4e0e3..a9bbd5c 100644 (file)
@@ -58,9 +58,6 @@ OM_uint32 gss_inquire_name(OM_uint32 *minor,
         return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
     }
 
-    if (attrs == NULL)
-        return GSS_S_COMPLETE;
-
     GSSEAP_MUTEX_LOCK(&name->mutex);
 
     major = gssEapInquireName(minor, name, name_is_MN, MN_mech, attrs);
index 449f013..3ba5892 100644 (file)
@@ -2,6 +2,7 @@ gss_accept_sec_context
 gss_acquire_cred
 gss_add_cred
 gss_add_cred_with_password
+gss_authorize_localname
 gss_canonicalize_name
 gss_compare_name
 gss_context_time
@@ -41,7 +42,6 @@ gss_set_sec_context_option
 gss_store_cred
 gss_unwrap
 gss_unwrap_iov
-gss_userok
 gss_verify_mic
 gss_wrap
 gss_wrap_iov
diff --git a/mech_eap/userok.c b/mech_eap/userok.c
deleted file mode 100644 (file)
index 9853992..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (c) 2011, JANET(UK)
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * 3. Neither the name of JANET(UK) nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/*
- * Local authorization services.
- */
-
-#include "gssapiP_eap.h"
-
-OM_uint32 KRB5_CALLCONV
-gss_userok(OM_uint32 *minor,
-           const gss_name_t name GSSEAP_UNUSED,
-           const char *user GSSEAP_UNUSED,
-           int *user_ok)
-{
-    /*
-     * The MIT mechglue will fallback to comparing names in the absence
-     * of a mechanism implementation of gss_userok. To avoid this and
-     * force the mechglue to use attribute-based authorization, always
-     * return access denied here.
-     */
-
-    *minor = 0;
-    *user_ok = 0;
-    return GSS_S_COMPLETE;
-}
index b3399be..e5376a6 100644 (file)
@@ -164,46 +164,65 @@ enum gss_eap_token_type {
     TOK_TYPE_ACCEPTOR_CONTEXT        = 0x0602,  /* acceptor-sent context token */
 };
 
+struct gss_eap_itok_map {
+    OM_uint32 type;                             /* inner token type */
+    OM_uint32 flag;                             /* context flag */
+};
+
 /* inner token types and flags */
-#define ITOK_TYPE_NONE                  0x00000000
-#define ITOK_TYPE_CONTEXT_ERR           0x00000001 /* critical */
-#define ITOK_TYPE_ACCEPTOR_NAME_REQ     0x00000002 /* TBD */
-#define ITOK_TYPE_ACCEPTOR_NAME_RESP    0x00000003 /* TBD */
-#define ITOK_TYPE_EAP_RESP              0x00000004 /* critical, required, if not reauth */
-#define ITOK_TYPE_EAP_REQ               0x00000005 /* critical, required, if not reauth */
-#define ITOK_TYPE_GSS_CHANNEL_BINDINGS  0x00000006 /* critical, required, if not reauth */
-#define ITOK_TYPE_REAUTH_CREDS          0x00000007 /* optional */
-#define ITOK_TYPE_REAUTH_REQ            0x00000008 /* optional */
-#define ITOK_TYPE_REAUTH_RESP           0x00000009 /* optional */
-#define ITOK_TYPE_VERSION_INFO          0x0000000A /* optional */
-#define ITOK_TYPE_VENDOR_INFO           0x0000000B /* optional */
-
-#define ITOK_FLAG_CRITICAL              0x80000000  /* critical, wire flag */
-#define ITOK_FLAG_VERIFIED              0x40000000  /* verified, API flag */
-
-#define ITOK_TYPE_MASK                  (~(ITOK_FLAG_CRITICAL | ITOK_FLAG_VERIFIED))
+#define ITOK_TYPE_NONE                      0x00000000
+#define ITOK_TYPE_CONTEXT_ERR               0x00000001 /* critical */
+#define ITOK_TYPE_ACCEPTOR_NAME_REQ         0x00000002 /* TBD */
+#define ITOK_TYPE_ACCEPTOR_NAME_RESP        0x00000003 /* TBD */
+#define ITOK_TYPE_EAP_RESP                  0x00000004 /* critical, required, if not reauth */
+#define ITOK_TYPE_EAP_REQ                   0x00000005 /* critical, required, if not reauth */
+#define ITOK_TYPE_GSS_CHANNEL_BINDINGS      0x00000006 /* optional */
+#define ITOK_TYPE_REAUTH_CREDS              0x00000007 /* optional */
+#define ITOK_TYPE_REAUTH_REQ                0x00000008 /* optional */
+#define ITOK_TYPE_REAUTH_RESP               0x00000009 /* optional */
+#define ITOK_TYPE_GSS_FLAGS                 0x0000000A /* optional */
+#define ITOK_TYPE_INITIATOR_MIC             0x0000000B /* required */
+#define ITOK_TYPE_ACCEPTOR_MIC              0x0000000C /* required */
+#define ITOK_TYPE_SUPPORTED_ACCEPTOR_EXTS   0x0000000D /* optional */
+#define ITOK_TYPE_SUPPORTED_INITIATOR_EXTS  0x0000000E /* optional */
+
+/* experimental */
+#define ITOK_TYPE_VERSION_INFO              0x00000080 /* optional */
+#define ITOK_TYPE_VENDOR_INFO               0x00000081 /* optional */
+
+#define ITOK_FLAG_CRITICAL                  0x80000000  /* critical, wire flag */
+#define ITOK_FLAG_VERIFIED                  0x40000000  /* verified, API flag */
+
+#define ITOK_TYPE_MASK                      (~(ITOK_FLAG_CRITICAL | ITOK_FLAG_VERIFIED))
+
+#define ITOK_HEADER_LENGTH                  8           /* type || length */
+
+#define GSSEAP_WIRE_FLAGS_MASK              ( GSS_C_MUTUAL_FLAG )
 
 OM_uint32 gssEapAllocContext(OM_uint32 *minor, gss_ctx_id_t *pCtx);
 OM_uint32 gssEapReleaseContext(OM_uint32 *minor, gss_ctx_id_t *pCtx);
 
 OM_uint32
-gssEapMakeToken(OM_uint32 *minor,
-                gss_ctx_id_t ctx,
-                const gss_buffer_t innerToken,
-                enum gss_eap_token_type tokenType,
-                gss_buffer_t outputToken);
+gssEapContextTime(OM_uint32 *minor,
+                  gss_ctx_id_t context_handle,
+                  OM_uint32 *time_rec);
 
 OM_uint32
-gssEapVerifyToken(OM_uint32 *minor,
-                  gss_ctx_id_t ctx,
-                  const gss_buffer_t inputToken,
-                  enum gss_eap_token_type *tokenType,
-                  gss_buffer_t innerInputToken);
+gssEapGetConversationMIC(OM_uint32 *minor,
+                         gss_ctx_id_t ctx,
+                         gss_buffer_t convMIC);
 
 OM_uint32
-gssEapContextTime(OM_uint32 *minor,
-                  gss_ctx_id_t context_handle,
-                  OM_uint32 *time_rec);
+gssEapVerifyConversationMIC(OM_uint32 *minor,
+                            gss_ctx_id_t ctx,
+                            const gss_buffer_t convMIC);
+
+OM_uint32
+gssEapMakeTokenChannelBindings(OM_uint32 *minor,
+                               gss_ctx_id_t ctx,
+                               gss_channel_bindings_t userBindings,
+                               gss_buffer_t inputToken,
+                               gss_channel_bindings_t wireBindings);
 
 /* util_cred.c */
 OM_uint32 gssEapAllocCred(OM_uint32 *minor, gss_cred_id_t *pCred);
@@ -604,7 +623,7 @@ struct gss_eap_sm {
 };
 
 /* state machine flags, set by handler */
-#define SM_FLAG_FORCE_SEND_TOKEN            0x00000001  /* send token even if no inner tokens */
+#define SM_FLAG_SEND_TOKEN                  0x00000001  /* exit state machine, send token */
 #define SM_FLAG_OUTPUT_TOKEN_CRITICAL       0x00000002  /* output token is critical */
 
 /* state machine flags, set by state machine */
@@ -631,24 +650,48 @@ gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state);
 
 /* util_token.c */
 OM_uint32
-gssEapEncodeInnerTokens(OM_uint32 *minor,
-                        gss_buffer_set_t extensions,
-                        OM_uint32 *types,
-                        gss_buffer_t buffer);
-OM_uint32
 gssEapDecodeInnerTokens(OM_uint32 *minor,
                         const gss_buffer_t buffer,
                         gss_buffer_set_t *pExtensions,
                         OM_uint32 **pTypes);
 
+OM_uint32
+gssEapRecordContextTokenHeader(OM_uint32 *minor,
+                               gss_ctx_id_t ctx,
+                               enum gss_eap_token_type tokType);
+
+OM_uint32
+gssEapRecordInnerContextToken(OM_uint32 *minor,
+                              gss_ctx_id_t ctx,
+                              gss_buffer_t innerToken,
+                              OM_uint32 type);
+
+OM_uint32
+gssEapVerifyContextToken(OM_uint32 *minor,
+                         gss_ctx_id_t ctx,
+                         const gss_buffer_t inputToken,
+                         enum gss_eap_token_type tokenType,
+                         gss_buffer_t innerInputToken);
+
+OM_uint32
+gssEapEncodeSupportedExts(OM_uint32 *minor,
+                          OM_uint32 *types,
+                          size_t typesCount,
+                          gss_buffer_t outputToken);
+
+OM_uint32
+gssEapProcessSupportedExts(OM_uint32 *minor,
+                           gss_buffer_t inputToken,
+                           struct gss_eap_itok_map *map,
+                           size_t mapCount,
+                           OM_uint32 *flags);
+
 size_t
-tokenSize(const gss_OID_desc *mech, size_t body_size);
+tokenSize(size_t bodySize);
 
 void
-makeTokenHeader(const gss_OID_desc *mech,
-                size_t body_size,
-                unsigned char **buf,
-                enum gss_eap_token_type tok_type);
+makeTokenHeader(size_t body_size,
+                unsigned char **buf);
 
 OM_uint32
 verifyTokenHeader(OM_uint32 *minor,
index 01c3135..4ce40fa 100644 (file)
@@ -854,13 +854,15 @@ gssEapInquireName(OM_uint32 *minor,
         return GSS_S_UNAVAILABLE;
     }
 
-    try {
-        if (!name->attrCtx->getAttributeTypes(attrs)) {
-            *minor = GSSEAP_NO_ATTR_CONTEXT;
-            return GSS_S_UNAVAILABLE;
+    if (attrs != NULL) {
+        try {
+            if (!name->attrCtx->getAttributeTypes(attrs)) {
+                *minor = GSSEAP_NO_ATTR_CONTEXT;
+                return GSS_S_UNAVAILABLE;
+            }
+        } catch (std::exception &e) {
+            return name->attrCtx->mapException(minor, e);
         }
-    } catch (std::exception &e) {
-        return name->attrCtx->mapException(minor, e);
     }
 
     return GSS_S_COMPLETE;
index e9a9308..148afed 100644 (file)
@@ -130,6 +130,7 @@ gssEapReleaseContext(OM_uint32 *minor,
     gssEapReleaseOid(&tmpMinor, &ctx->mechanismUsed);
     sequenceFree(&tmpMinor, &ctx->seqState);
     gssEapReleaseCred(&tmpMinor, &ctx->defaultCred);
+    gss_release_buffer(&tmpMinor, &ctx->conversation);
 
     GSSEAP_MUTEX_DESTROY(&ctx->mutex);
 
@@ -142,88 +143,140 @@ gssEapReleaseContext(OM_uint32 *minor,
 }
 
 OM_uint32
-gssEapMakeToken(OM_uint32 *minor,
-                gss_ctx_id_t ctx,
-                const gss_buffer_t innerToken,
-                enum gss_eap_token_type tokenType,
-                gss_buffer_t outputToken)
+gssEapContextTime(OM_uint32 *minor,
+                  gss_ctx_id_t context_handle,
+                  OM_uint32 *time_rec)
 {
-    unsigned char *p;
+    *minor = 0;
 
-    outputToken->length = tokenSize(ctx->mechanismUsed, innerToken->length);
-    outputToken->value = GSSEAP_MALLOC(outputToken->length);
-    if (outputToken->value == NULL) {
-        *minor = ENOMEM;
-        return GSS_S_FAILURE;
-    }
+    if (context_handle->expiryTime == 0) {
+        *time_rec = GSS_C_INDEFINITE;
+    } else {
+        time_t now, lifetime;
 
-    p = (unsigned char *)outputToken->value;
-    makeTokenHeader(ctx->mechanismUsed, innerToken->length, &p, tokenType);
-    memcpy(p, innerToken->value, innerToken->length);
+        time(&now);
+        lifetime = context_handle->expiryTime - now;
+        if (lifetime <= 0) {
+            *time_rec = 0;
+            return GSS_S_CONTEXT_EXPIRED;
+        }
+        *time_rec = lifetime;
+    }
 
-    *minor = 0;
     return GSS_S_COMPLETE;
 }
 
 OM_uint32
-gssEapVerifyToken(OM_uint32 *minor,
-                  gss_ctx_id_t ctx,
-                  const gss_buffer_t inputToken,
-                  enum gss_eap_token_type *actualToken,
-                  gss_buffer_t innerInputToken)
+gssEapGetConversationMIC(OM_uint32 *minor,
+                         gss_ctx_id_t ctx,
+                         gss_buffer_t convMIC)
 {
     OM_uint32 major;
-    size_t bodySize;
-    unsigned char *p = (unsigned char *)inputToken->value;
-    gss_OID_desc oidBuf;
-    gss_OID oid;
+    gss_iov_buffer_desc iov[2];
 
-    if (ctx->mechanismUsed != GSS_C_NO_OID) {
-        oid = ctx->mechanismUsed;
-    } else {
-        oidBuf.elements = NULL;
-        oidBuf.length = 0;
-        oid = &oidBuf;
-    }
+    iov[0].type = GSS_IOV_BUFFER_TYPE_DATA;
+    iov[0].buffer = ctx->conversation;
 
-    major = verifyTokenHeader(minor, oid, &bodySize, &p,
-                              inputToken->length, actualToken);
+    iov[1].type = GSS_IOV_BUFFER_TYPE_HEADER | GSS_IOV_BUFFER_FLAG_ALLOCATE;
+    iov[1].buffer.length = 0;
+    iov[1].buffer.value = NULL;
+
+    major = gssEapWrapOrGetMIC(minor, ctx, FALSE, NULL, iov, 2, TOK_TYPE_MIC);
     if (GSS_ERROR(major))
         return major;
 
-    if (ctx->mechanismUsed == GSS_C_NO_OID) {
-        major = gssEapCanonicalizeOid(minor, oid, 0, &ctx->mechanismUsed);
-        if (GSS_ERROR(major))
-            return major;
-    }
-
-    innerInputToken->length = bodySize;
-    innerInputToken->value = p;
+    *convMIC = iov[1].buffer;
 
     *minor = 0;
     return GSS_S_COMPLETE;
 }
 
 OM_uint32
-gssEapContextTime(OM_uint32 *minor,
-                  gss_ctx_id_t context_handle,
-                  OM_uint32 *time_rec)
+gssEapVerifyConversationMIC(OM_uint32 *minor,
+                            gss_ctx_id_t ctx,
+                            const gss_buffer_t convMIC)
 {
-    *minor = 0;
+    OM_uint32 major;
+    gss_iov_buffer_desc iov[3];
+    int confState;
+    size_t tokenHeaderLength;
 
-    if (context_handle->expiryTime == 0) {
-        *time_rec = GSS_C_INDEFINITE;
-    } else {
-        time_t now, lifetime;
+    if (convMIC == GSS_C_NO_BUFFER || convMIC->length < 16) {
+        *minor = GSSEAP_TOK_TRUNC;
+        return GSS_S_BAD_SIG;
+    }
 
-        time(&now);
-        lifetime = context_handle->expiryTime - now;
-        if (lifetime <= 0) {
-            *time_rec = 0;
-            return GSS_S_CONTEXT_EXPIRED;
-        }
-        *time_rec = lifetime;
+    iov[0].type = GSS_IOV_BUFFER_TYPE_DATA;
+    iov[0].buffer = ctx->conversation;
+
+    /*
+     * The conversation state already includes the MIC and its
+     * TLV header, as well as a header for emiting a subsequent
+     * token. These should not be included as input to verifyMIC.
+     */
+    tokenHeaderLength = ITOK_HEADER_LENGTH + convMIC->length
+        + 2 + ctx->mechanismUsed->length + 2;
+    assert(ctx->conversation.length >= tokenHeaderLength);
+    iov[0].buffer.length -= tokenHeaderLength;
+
+    iov[1].type = GSS_IOV_BUFFER_TYPE_HEADER;
+    iov[1].buffer.length = 16;
+    iov[1].buffer.value = convMIC->value;
+
+    iov[2].type = GSS_IOV_BUFFER_TYPE_TRAILER;
+    iov[2].buffer.length = convMIC->length - 16;
+    iov[2].buffer.value = (unsigned char *)convMIC->value + 16;
+
+    major = gssEapUnwrapOrVerifyMIC(minor, ctx, &confState, NULL,
+                                    iov, 3, TOK_TYPE_MIC);
+
+
+    return major;
+}
+
+OM_uint32
+gssEapMakeTokenChannelBindings(OM_uint32 *minor,
+                               gss_ctx_id_t ctx,
+                               gss_channel_bindings_t userChanBindings,
+                               gss_buffer_t inputToken,
+                               gss_channel_bindings_t wireChanBindings)
+{
+    gss_buffer_t wireData = &wireChanBindings->application_data;
+    unsigned char *p;
+    size_t tokenHeaderLength = 0;
+
+    memset(wireChanBindings, 0, sizeof(*wireChanBindings));
+
+    if (!CTX_IS_INITIATOR(ctx)) {
+        assert(inputToken != GSS_C_NO_BUFFER);
+
+        tokenHeaderLength = ITOK_HEADER_LENGTH + inputToken->length +
+            2 + ctx->mechanismUsed->length + 2;
+        assert(ctx->conversation.length >= tokenHeaderLength);
     }
 
+    wireData->length = ctx->conversation.length - tokenHeaderLength;
+
+    if (userChanBindings != GSS_C_NO_CHANNEL_BINDINGS)
+        wireData->length += userChanBindings->application_data.length;
+
+    wireData->value = GSSEAP_MALLOC(wireData->length);
+    if (wireData->value == NULL) {
+        *minor = ENOMEM;
+        return GSS_S_FAILURE;
+    }
+
+    p = (unsigned char *)wireData->value;
+
+    memcpy(p, ctx->conversation.value, ctx->conversation.length - tokenHeaderLength);
+    p += ctx->conversation.length - tokenHeaderLength;
+
+    if (userChanBindings != GSS_C_NO_CHANNEL_BINDINGS) {
+        memcpy(p, userChanBindings->application_data.value,
+               userChanBindings->application_data.length);
+        p += userChanBindings->application_data.length;
+    }
+
+    *minor = 0;
     return GSS_S_COMPLETE;
 }
index ca69923..3bb28b8 100644 (file)
@@ -95,21 +95,16 @@ gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state)
 #endif /* GSSEAP_DEBUG */
 
 static OM_uint32
-makeErrorToken(OM_uint32 *minor,
-               OM_uint32 majorStatus,
-               OM_uint32 minorStatus,
-               gss_buffer_set_t *outputToken)
+recordErrorToken(OM_uint32 *minor,
+                 gss_ctx_id_t ctx,
+                 OM_uint32 majorStatus,
+                 OM_uint32 minorStatus)
 {
-    OM_uint32 major;
     unsigned char errorData[8];
     gss_buffer_desc errorBuffer;
 
     assert(GSS_ERROR(majorStatus));
 
-    major = gss_create_empty_buffer_set(minor, outputToken);
-    if (GSS_ERROR(major))
-        return major;
-
     /*
      * Only return error codes that the initiator could have caused,
      * to avoid information leakage.
@@ -130,61 +125,39 @@ makeErrorToken(OM_uint32 *minor,
     errorBuffer.length = sizeof(errorData);
     errorBuffer.value = errorData;
 
-    major = gss_add_buffer_set_member(minor, &errorBuffer, outputToken);
-    if (GSS_ERROR(major))
-        return major;
-
-    return GSS_S_COMPLETE;
+    return gssEapRecordInnerContextToken(minor, ctx, &errorBuffer,
+                                         ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL);
 }
 
 static OM_uint32
-allocInnerTokens(OM_uint32 *minor,
-                 size_t count,
-                 gss_buffer_set_t *pTokens,
-                 OM_uint32 **pTokenTypes)
+makeContextToken(OM_uint32 *minor,
+                 gss_ctx_id_t ctx,
+                 size_t headerOffset,
+                 gss_buffer_t outputToken)
 {
-    OM_uint32 major, tmpMinor;
-    gss_buffer_set_t tokens = GSS_C_NO_BUFFER_SET;
-    OM_uint32 *tokenTypes = NULL;
+    size_t tokSize, bodySize;
+    unsigned char *p;
 
-    major = gss_create_empty_buffer_set(minor, &tokens);
-    if (GSS_ERROR(major))
-        goto cleanup;
+    assert(ctx->conversation.length > headerOffset);
 
-    assert(tokens->count == 0);
-    assert(tokens->elements == NULL);
+    bodySize = ctx->conversation.length - headerOffset;
+    tokSize = tokenSize(bodySize);
 
-    tokens->elements = (gss_buffer_desc *)GSSEAP_CALLOC(count, sizeof(gss_buffer_desc));
-    if (tokens->elements == NULL) {
-        major = GSS_S_FAILURE;
+    outputToken->value = GSSEAP_MALLOC(tokSize);
+    if (outputToken->value == NULL) {
         *minor = ENOMEM;
-        goto cleanup;
+        return GSS_S_FAILURE;
     }
 
-    tokenTypes = (OM_uint32 *)GSSEAP_CALLOC(count, sizeof(OM_uint32));
-    if (tokenTypes == NULL) {
-        major = GSS_S_FAILURE;
-        *minor = ENOMEM;
-        goto cleanup;
-    }
+    outputToken->length = tokSize;
 
-    major = GSS_S_COMPLETE;
-    *minor = 0;
+    p = (unsigned char *)outputToken->value;
 
-cleanup:
-    if (GSS_ERROR(major)) {
-        gss_release_buffer_set(&tmpMinor, &tokens);
-        tokens = GSS_C_NO_BUFFER_SET;
-        if (tokenTypes != NULL) {
-            GSSEAP_FREE(tokenTypes);
-            tokenTypes = NULL;
-        }
-    }
-
-    *pTokens = tokens;
-    *pTokenTypes = tokenTypes;
+    makeTokenHeader(bodySize, &p);
+    memcpy(p, (unsigned char *)ctx->conversation.value + headerOffset, bodySize);
 
-    return major;
+    *minor = 0;
+    return GSS_S_COMPLETE;
 }
 
 OM_uint32
@@ -205,12 +178,13 @@ gssEapSmStep(OM_uint32 *minor,
     gss_buffer_desc unwrappedInputToken = GSS_C_EMPTY_BUFFER;
     gss_buffer_desc unwrappedOutputToken = GSS_C_EMPTY_BUFFER;
     gss_buffer_set_t innerInputTokens = GSS_C_NO_BUFFER_SET;
-    gss_buffer_set_t innerOutputTokens = GSS_C_NO_BUFFER_SET;
     OM_uint32 *inputTokenTypes = NULL, *outputTokenTypes = NULL;
     unsigned int smFlags = 0;
     size_t i, j;
     int initialContextToken = 0;
     enum gss_eap_token_type tokType;
+    size_t headerOffset, firstTokenOffset;
+    size_t innerOutputTokenCount = 0;
 
     assert(smCount > 0);
 
@@ -220,17 +194,13 @@ gssEapSmStep(OM_uint32 *minor,
     outputToken->value = NULL;
 
     if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) {
-        major = gssEapVerifyToken(minor, ctx, inputToken, &tokType,
-                                  &unwrappedInputToken);
-        if (GSS_ERROR(major))
-            goto cleanup;
+        tokType = CTX_IS_INITIATOR(ctx) ?
+            TOK_TYPE_ACCEPTOR_CONTEXT : TOK_TYPE_INITIATOR_CONTEXT;
 
-        if (tokType != (CTX_IS_INITIATOR(ctx)
-                    ? TOK_TYPE_ACCEPTOR_CONTEXT : TOK_TYPE_INITIATOR_CONTEXT)) {
-            major = GSS_S_DEFECTIVE_TOKEN;
-            *minor = GSSEAP_WRONG_TOK_ID;
+        major = gssEapVerifyContextToken(minor, ctx, inputToken, tokType,
+                                         &unwrappedInputToken);
+        if (GSS_ERROR(major))
             goto cleanup;
-        }
     } else if (!CTX_IS_INITIATOR(ctx) || ctx->state != GSSEAP_STATE_INITIAL) {
         major = GSS_S_DEFECTIVE_TOKEN;
         *minor = GSSEAP_WRONG_SIZE;
@@ -252,12 +222,20 @@ gssEapSmStep(OM_uint32 *minor,
     if (GSS_ERROR(major))
         goto cleanup;
 
+    headerOffset = ctx->conversation.length;
+
     assert(innerInputTokens != GSS_C_NO_BUFFER_SET);
 
-    major = allocInnerTokens(minor, smCount, &innerOutputTokens, &outputTokenTypes);
+    /* Get ready to emit an output token */
+    tokType = CTX_IS_INITIATOR(ctx) ?
+        TOK_TYPE_INITIATOR_CONTEXT : TOK_TYPE_ACCEPTOR_CONTEXT;
+
+    major = gssEapRecordContextTokenHeader(minor, ctx, tokType);
     if (GSS_ERROR(major))
         goto cleanup;
 
+    firstTokenOffset = ctx->conversation.length;
+
     /* Process all the tokens that are valid for the current state. */
     for (i = 0; i < smCount; i++) {
         struct gss_eap_sm *smp = &sm[i];
@@ -321,32 +299,45 @@ gssEapSmStep(OM_uint32 *minor,
                 smFlags |= SM_FLAG_TRANSITED;
 
             if (innerOutputToken.value != NULL) {
-                innerOutputTokens->elements[innerOutputTokens->count] = innerOutputToken;
-                assert(smp->outputTokenType != ITOK_TYPE_NONE);
-                outputTokenTypes[innerOutputTokens->count] = smp->outputTokenType;
+                OM_uint32 outputTokenType = smp->outputTokenType;
+
                 if (smFlags & SM_FLAG_OUTPUT_TOKEN_CRITICAL)
-                    outputTokenTypes[innerOutputTokens->count] |= ITOK_FLAG_CRITICAL;
-                innerOutputTokens->count++;
+                    outputTokenType |= ITOK_FLAG_CRITICAL;
+
+                assert(smp->outputTokenType != ITOK_TYPE_NONE);
+
+                tmpMajor = gssEapRecordInnerContextToken(&tmpMinor, ctx,
+                                                         &innerOutputToken,
+                                                         outputTokenType);
+                if (GSS_ERROR(tmpMajor)) {
+                    major = tmpMajor;
+                    *minor = tmpMinor;
+                    break;
+                }
+
+                innerOutputTokenCount++;
             }
+
             /*
              * Break out if we made a state transition and have some tokens to send.
              */
-            if ((smFlags & SM_FLAG_TRANSITED) &&
-                 ((smFlags & SM_FLAG_FORCE_SEND_TOKEN) || innerOutputTokens->count != 0)) {
+            if (smFlags & SM_FLAG_SEND_TOKEN) {
                 SM_ASSERT_VALID(ctx, major);
                 break;
             }
         } else if ((smp->itokFlags & SM_ITOK_FLAG_REQUIRED) &&
             smp->inputTokenType != ITOK_TYPE_NONE) {
             /* Check for required inner tokens */
+#ifdef GSSEAP_DEBUG
+            fprintf(stderr, "GSS-EAP: missing required token %08X\n",
+                    smp->inputTokenType);
+#endif
             major = GSS_S_DEFECTIVE_TOKEN;
             *minor = GSSEAP_MISSING_REQUIRED_ITOK;
             break;
         }
     }
 
-    assert(innerOutputTokens->count <= smCount);
-
     /* Check we understood all critical tokens sent by peer */
     if (!GSS_ERROR(major)) {
         for (j = 0; j < innerInputTokens->count; j++) {
@@ -365,38 +356,27 @@ gssEapSmStep(OM_uint32 *minor,
             goto cleanup; /* return error directly to caller */
 
         /* replace any emitted tokens with error token */
-        gss_release_buffer_set(&tmpMinor, &innerOutputTokens);
+        ctx->conversation.length = firstTokenOffset;
 
-        tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &innerOutputTokens);
+        tmpMajor = recordErrorToken(&tmpMinor, ctx, major, *minor);
         if (GSS_ERROR(tmpMajor)) {
             major = tmpMajor;
             *minor = tmpMinor;
             goto cleanup;
         }
 
-        if (innerOutputTokens->count != 0)
-            outputTokenTypes[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL;
+        innerOutputTokenCount = 1;
     }
 
     /* Format output token from inner tokens */
-    if (innerOutputTokens->count != 0 ||            /* inner tokens to send */
+    if (innerOutputTokenCount != 0 ||               /* inner tokens to send */
         !CTX_IS_INITIATOR(ctx) ||                   /* any leg acceptor */
         !CTX_IS_ESTABLISHED(ctx)) {                 /* non-last leg initiator */
-        tmpMajor = gssEapEncodeInnerTokens(&tmpMinor, innerOutputTokens,
-                                           outputTokenTypes, &unwrappedOutputToken);
-        if (tmpMajor == GSS_S_COMPLETE) {
-            if (CTX_IS_INITIATOR(ctx))
-                tokType = TOK_TYPE_INITIATOR_CONTEXT;
-            else
-                tokType = TOK_TYPE_ACCEPTOR_CONTEXT;
-
-            tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &unwrappedOutputToken,
-                                       tokType, outputToken);
-            if (GSS_ERROR(tmpMajor)) {
-                major = tmpMajor;
-                *minor = tmpMinor;
-                goto cleanup;
-            }
+        tmpMajor = makeContextToken(&tmpMinor, ctx, headerOffset, outputToken);
+        if (GSS_ERROR(tmpMajor)) {
+            major = tmpMajor;
+            *minor = tmpMinor;
+            goto cleanup;
         }
     }
 
@@ -407,7 +387,6 @@ gssEapSmStep(OM_uint32 *minor,
 
 cleanup:
     gss_release_buffer_set(&tmpMinor, &innerInputTokens);
-    gss_release_buffer_set(&tmpMinor, &innerOutputTokens);
     if (inputTokenTypes != NULL)
         GSSEAP_FREE(inputTokenTypes);
     if (outputTokenTypes != NULL)
index a929198..5e69b26 100644 (file)
 #include "gssapiP_eap.h"
 
 OM_uint32
-gssEapEncodeInnerTokens(OM_uint32 *minor,
-                        gss_buffer_set_t extensions,
-                        OM_uint32 *types,
-                        gss_buffer_t buffer)
-{
-    OM_uint32 major, tmpMinor;
-    size_t required = 0, i;
-    unsigned char *p;
-
-    buffer->value = NULL;
-    buffer->length = 0;
-
-    if (extensions != GSS_C_NO_BUFFER_SET) {
-        for (i = 0; i < extensions->count; i++) {
-            required += 8 + extensions->elements[i].length;
-        }
-    }
-
-    /*
-     * We must always return a non-NULL token otherwise the calling state
-     * machine assumes we are finished. Hence care in case malloc(0) does
-     * return NULL.
-     */
-    buffer->value = GSSEAP_MALLOC(required ? required : 1);
-    if (buffer->value == NULL) {
-        major = GSS_S_FAILURE;
-        *minor = ENOMEM;
-        goto cleanup;
-    }
-
-    buffer->length = required;
-    p = (unsigned char *)buffer->value;
-
-    if (extensions != GSS_C_NO_BUFFER_SET) {
-        for (i = 0; i < extensions->count; i++) {
-            gss_buffer_t extension = &extensions->elements[i];
-
-            assert((types[i] & ITOK_FLAG_VERIFIED) == 0); /* private flag */
-
-             /*
-              * Extensions are encoded as type-length-value, where the upper
-              * bit of the type indicates criticality.
-              */
-            store_uint32_be(types[i], &p[0]);
-            store_uint32_be(extension->length, &p[4]);
-            memcpy(&p[8], extension->value, extension->length);
-
-            p += 8 + extension->length;
-        }
-    }
-
-    assert(p == (unsigned char *)buffer->value + required);
-    assert(buffer->value != NULL);
-
-    major = GSS_S_COMPLETE;
-    *minor = 0;
-
-cleanup:
-    if (GSS_ERROR(major)) {
-        gss_release_buffer(&tmpMinor, buffer);
-    }
-
-    return major;
-}
-
-OM_uint32
 gssEapDecodeInnerTokens(OM_uint32 *minor,
                         const gss_buffer_t buffer,
                         gss_buffer_set_t *pExtensions,
@@ -172,7 +106,7 @@ gssEapDecodeInnerTokens(OM_uint32 *minor,
         types[extensions->count] = load_uint32_be(&p[0]);
         extension.length = load_uint32_be(&p[4]);
 
-        if (remain < 8 + extension.length) {
+        if (remain < ITOK_HEADER_LENGTH + extension.length) {
             major = GSS_S_DEFECTIVE_TOKEN;
             *minor = GSSEAP_TOK_TRUNC;
             goto cleanup;
@@ -183,8 +117,8 @@ gssEapDecodeInnerTokens(OM_uint32 *minor,
         if (GSS_ERROR(major))
             goto cleanup;
 
-        p      += 8 + extension.length;
-        remain -= 8 + extension.length;
+        p      += ITOK_HEADER_LENGTH + extension.length;
+        remain -= ITOK_HEADER_LENGTH + extension.length;
     } while (remain != 0);
 
 cleanup:
@@ -201,6 +135,215 @@ cleanup:
 }
 
 /*
+ * Add some data to the initiator/acceptor conversation.
+ */
+static OM_uint32
+recordTokens(OM_uint32 *minor,
+             gss_ctx_id_t ctx,
+             gss_buffer_t tokens,
+             size_t tokensCount)
+{
+    unsigned char *buf;
+    size_t i, size, offset;
+
+    size = ctx->conversation.length;
+
+    for (i = 0; i < tokensCount; i++)
+        size += tokens[i].length;
+
+    buf = GSSEAP_REALLOC(ctx->conversation.value, size);
+    if (buf == NULL) {
+        *minor = ENOMEM;
+        return GSS_S_FAILURE;
+    }
+
+    offset = ctx->conversation.length;
+
+    ctx->conversation.length = size;
+    ctx->conversation.value = buf;
+
+    for (i = 0; i < tokensCount; i++) {
+        memcpy(buf + offset, tokens[i].value, tokens[i].length);
+        offset += tokens[i].length;
+    }
+
+    *minor = 0;
+    return GSS_S_COMPLETE;
+}
+
+/*
+ * Record the context token header.
+ */
+OM_uint32
+gssEapRecordContextTokenHeader(OM_uint32 *minor,
+                               gss_ctx_id_t ctx,
+                               enum gss_eap_token_type tokType)
+{
+    unsigned char wireOidHeader[2], wireTokType[2];
+    gss_buffer_desc buffers[3];
+
+    assert(ctx->mechanismUsed != GSS_C_NO_OID);
+
+    wireOidHeader[0] = 0x06;
+    wireOidHeader[1] = ctx->mechanismUsed->length;
+    buffers[0].length = sizeof(wireOidHeader);
+    buffers[0].value  = wireOidHeader;
+
+    buffers[1].length = ctx->mechanismUsed->length;
+    buffers[1].value  = ctx->mechanismUsed->elements;
+
+    store_uint16_be(tokType, wireTokType);
+    buffers[2].length = sizeof(wireTokType);
+    buffers[2].value = wireTokType;
+
+    return recordTokens(minor, ctx, buffers, sizeof(buffers)/sizeof(buffers[0]));
+}
+
+/*
+ * Record an inner context token.
+ */
+OM_uint32
+gssEapRecordInnerContextToken(OM_uint32 *minor,
+                              gss_ctx_id_t ctx,
+                              gss_buffer_t innerToken,
+                              OM_uint32 itokType)
+{
+    gss_buffer_desc buffers[2];
+    unsigned char itokHeader[ITOK_HEADER_LENGTH];
+
+    assert(innerToken != GSS_C_NO_BUFFER);
+
+    store_uint32_be(itokType,           &itokHeader[0]);
+    store_uint32_be(innerToken->length, &itokHeader[4]);
+    buffers[0].length = sizeof(itokHeader);
+    buffers[0].value  = itokHeader;
+
+    buffers[1] = *innerToken;
+
+    return recordTokens(minor, ctx, buffers, sizeof(buffers)/sizeof(buffers[0]));
+}
+
+OM_uint32
+gssEapVerifyContextToken(OM_uint32 *minor,
+                         gss_ctx_id_t ctx,
+                         const gss_buffer_t inputToken,
+                         enum gss_eap_token_type tokType,
+                         gss_buffer_t innerInputToken)
+{
+    OM_uint32 major;
+    size_t bodySize;
+    unsigned char *p = (unsigned char *)inputToken->value;
+    gss_OID_desc oidBuf;
+    gss_OID oid;
+    enum gss_eap_token_type actualTokType;
+    gss_buffer_desc tokenBuf;
+
+    if (ctx->mechanismUsed != GSS_C_NO_OID) {
+        oid = ctx->mechanismUsed;
+    } else {
+        oidBuf.elements = NULL;
+        oidBuf.length = 0;
+        oid = &oidBuf;
+    }
+
+    major = verifyTokenHeader(minor, oid, &bodySize, &p,
+                              inputToken->length, &actualTokType);
+    if (GSS_ERROR(major))
+        return major;
+
+    if (actualTokType != tokType) {
+        *minor = GSSEAP_WRONG_TOK_ID;
+        return GSS_S_DEFECTIVE_TOKEN;
+    }
+
+    if (ctx->mechanismUsed == GSS_C_NO_OID) {
+        major = gssEapCanonicalizeOid(minor, oid, 0, &ctx->mechanismUsed);
+        if (GSS_ERROR(major))
+            return major;
+    }
+
+    innerInputToken->length = bodySize;
+    innerInputToken->value = p;
+
+    /*
+     * Add OID, tokenType, body to conversation; variable length
+     * header omitted. A better API to verifyTokenHeader would
+     * avoid this ugly pointer arithmetic. XXX FIXME
+     */
+    tokenBuf.value = p - (2 + oid->length + 2);
+    tokenBuf.length = 2 + oid->length + 2 + bodySize;
+
+    major = recordTokens(minor, ctx, &tokenBuf, 1);
+    if (GSS_ERROR(major))
+        return major;
+
+    *minor = 0;
+    return GSS_S_COMPLETE;
+}
+
+OM_uint32
+gssEapEncodeSupportedExts(OM_uint32 *minor,
+                          OM_uint32 *types,
+                          size_t typesCount,
+                          gss_buffer_t outputToken)
+{
+    size_t i;
+    unsigned char *p;
+
+    outputToken->value = GSSEAP_MALLOC(4 * typesCount);
+    if (outputToken->value == NULL) {
+        *minor = ENOMEM;
+        return GSS_S_FAILURE;
+    }
+    p = (unsigned char *)outputToken->value;
+
+    outputToken->length = 4 * typesCount;
+
+    for (i = 0; i < typesCount; i++) {
+        store_uint32_be(types[i], p);
+        p += 4;
+    }
+
+    *minor = 0;
+    return GSS_S_COMPLETE;
+}
+
+OM_uint32
+gssEapProcessSupportedExts(OM_uint32 *minor,
+                           gss_buffer_t inputToken,
+                           struct gss_eap_itok_map *map,
+                           size_t mapCount,
+                           OM_uint32 *flags)
+{
+    size_t i;
+    unsigned char *p;
+
+    if ((inputToken->length % 4) != 0) {
+        *minor = GSSEAP_TOK_TRUNC;
+        return GSS_S_DEFECTIVE_TOKEN;
+    }
+
+    p = (unsigned char *)inputToken->value;
+
+    for (i = 0; i < inputToken->length / 4; i++) {
+        OM_uint32 type = load_uint32_be(p);
+        size_t j;
+
+        for (j = 0; j < mapCount; j++) {
+            if (map->type == type) {
+                *flags |= map->flag;
+                break;
+            }
+        }
+
+        p += 4;
+    }
+
+    *minor = 0;
+    return GSS_S_COMPLETE;
+}
+
+/*
  * $Id: util_token.c 23457 2009-12-08 00:04:48Z tlyu $
  */
 
@@ -305,12 +448,8 @@ der_read_length(unsigned char **buf, ssize_t *bufsize)
 /* returns the length of a token, given the mech oid and the body size */
 
 size_t
-tokenSize(const gss_OID_desc *mech, size_t body_size)
+tokenSize(size_t body_size)
 {
-    assert(mech != GSS_C_NO_OID);
-
-    /* set body_size to sequence contents size */
-    body_size += 4 + (size_t) mech->length;         /* NEED overflow check */
     return 1 + der_length_size(body_size) + body_size;
 }
 
@@ -319,20 +458,11 @@ tokenSize(const gss_OID_desc *mech, size_t body_size)
 
 void
 makeTokenHeader(
-    const gss_OID_desc *mech,
     size_t body_size,
-    unsigned char **buf,
-    enum gss_eap_token_type tok_type)
+    unsigned char **buf)
 {
     *(*buf)++ = 0x60;
-    der_write_length(buf, (tok_type == -1) ?2:4 + mech->length + body_size);
-    *(*buf)++ = 0x06;
-    *(*buf)++ = (unsigned char)mech->length;
-    memcpy(*buf, mech->elements, mech->length);
-    *buf += mech->length;
-    assert(tok_type != TOK_TYPE_NONE);
-    *(*buf)++ = (unsigned char)((tok_type>>8) & 0xff);
-    *(*buf)++ = (unsigned char)(tok_type & 0xff);
+    der_write_length(buf, body_size);
 }
 
 /*