Merge remote branch 'origin/master' into HEAD
authorSam Hartman <hartmans@painless-security.com>
Wed, 27 Apr 2011 13:56:32 +0000 (09:56 -0400)
committerSam Hartman <hartmans@painless-security.com>
Wed, 27 Apr 2011 13:56:32 +0000 (09:56 -0400)
Conflicts:
moonshot/mech_eap/userok.c

13 files changed:
1  2 
moonshot/mech_eap/Makefile.am
moonshot/mech_eap/accept_sec_context.c
moonshot/mech_eap/export_sec_context.c
moonshot/mech_eap/gssapiP_eap.h
moonshot/mech_eap/import_sec_context.c
moonshot/mech_eap/init_sec_context.c
moonshot/mech_eap/inquire_name.c
moonshot/mech_eap/mech_eap.exports
moonshot/mech_eap/util.h
moonshot/mech_eap/util_attr.cpp
moonshot/mech_eap/util_context.c
moonshot/mech_eap/util_sm.c
moonshot/mech_eap/util_token.c

@@@ -3,19 -3,22 +3,22 @@@ AUTOMAKE_OPTIONS = foreig
  gssincludedir = $(includedir)/gssapi
  gssinclude_HEADERS = gssapi_eap.h
  
+ EAP_CFLAGS = -I$(srcdir)/../libeap/src -I$(srcdir)/../libeap/src/common -I$(srcdir)/../libeap/src/eap_common  \
+     -I$(srcdir)/../libeap/src/utils -DIEEE8021X_EAPOL
  gssdir = $(libdir)/gss
  gss_LTLIBRARIES = mech_eap.la
  
  mech_eap_la_CPPFLAGS = -DBUILD_GSSEAP_LIB -DSYSCONFDIR=\"${sysconfdir}\" -DDATAROOTDIR=\"${datarootdir}\"
  mech_eap_la_CFLAGS   = -Werror -Wall -Wunused-parameter \
-                       @KRB5_CFLAGS@ @EAP_CFLAGS@ @RADSEC_CFLAGS@ @TARGET_CFLAGS@
+                       @KRB5_CFLAGS@  @RADSEC_CFLAGS@ @TARGET_CFLAGS@ $(EAP_CFLAGS)
  mech_eap_la_CXXFLAGS = -Werror -Wall -Wunused-parameter \
-                       @KRB5_CFLAGS@ @EAP_CFLAGS@ @RADSEC_CFLAGS@ \
-                       @SHIBRESOLVER_CXXFLAGS@ @SHIBSP_CXXFLAGS@ @TARGET_CFLAGS@
+                       @KRB5_CFLAGS@  @RADSEC_CFLAGS@ \
+                       @SHIBRESOLVER_CXXFLAGS@ @SHIBSP_CXXFLAGS@ @TARGET_CFLAGS@ $(EAP_CFLAGS)
  mech_eap_la_LDFLAGS  = -avoid-version -module \
                        -export-symbols mech_eap.exports -no-undefined \
                        @EAP_LDFLAGS@ @RADSEC_LDFLAGS@ @TARGET_LDFLAGS@
- mech_eap_la_LIBADD   = @KRB5_LIBS@ @EAP_LIBS@ @RADSEC_LIBS@ \
+ mech_eap_la_LIBADD   = @KRB5_LIBS@ ../libeap/libeap.la @RADSEC_LIBS@ \
                       @SHIBRESOLVER_LIBS@ @SHIBSP_LIBS@ @JANSSON_LIBS@
  
  mech_eap_la_SOURCES =                         \
@@@ -24,7 -27,6 +27,7 @@@
        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                          \
@@@ -69,6 -71,7 +72,6 @@@
        store_cred.c                            \
        unwrap.c                                \
        unwrap_iov.c                            \
 -      userok.c                                \
        util_attr.cpp                           \
        util_base64.c                           \
        util_buffer.c                           \
@@@ -124,3 -127,4 +127,4 @@@ radsec_err.c: radsec_err.
  
  clean-generic:
        rm -f gsseap_err.[ch] radsec_err.[ch]
@@@ -131,49 -131,6 +131,49 @@@ acceptReadyEap(OM_uint32 *minor, gss_ct
  }
  
  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,
@@@ -274,7 -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;
  }
@@@ -639,7 -596,7 +639,7 @@@ eapGssSmAcceptAuthenticate(OM_uint32 *m
  
      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)
  }
  
  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,
      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;
@@@ -756,39 -677,29 +756,39 @@@ eapGssSmAcceptReauthCreds(OM_uint32 *mi
       * 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;
  }
  
  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,
          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
      {
          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
      {
  #endif
      {
          ITOK_TYPE_NONE,
 -        ITOK_TYPE_NONE,
 +        ITOK_TYPE_ACCEPTOR_MIC,
          GSSEAP_STATE_ACCEPTOR_EXTS,
          0,
 -        eapGssSmAcceptCompleteAcceptorExts
 +        eapGssSmAcceptAcceptorMIC
      },
  };
  
@@@ -1064,7 -952,7 +1064,7 @@@ eapGssSmAcceptGssReauth(OM_uint32 *mino
                          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)
      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
  
      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,
          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;
  }
@@@ -38,7 -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 +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;
      }
      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 */
  
      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);
  
@@@ -141,7 -141,6 +141,7 @@@ struct gss_cred_id_struc
  
  #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)
  
@@@ -204,7 -203,6 +204,7 @@@ struct gss_ctx_id_struc
          #define reauthCtx            ctxU.reauth
  #endif
      } ctxU;
 +    gss_buffer_desc conversation;
  };
  
  #define TOK_FLAG_SENDER_IS_ACCEPTOR         0x01
@@@ -49,7 -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,47 -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)
       * 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
@@@ -223,11 -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;
      }
@@@ -447,31 -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) {
                                (gss_OID)gss_mech_krb5,
                                reqFlags | GSS_C_MUTUAL_FLAG,
                                timeReq,
 -                              chanBindings,
 +                              &wireChanBindings,
                                inputToken,
                                &actualMech,
                                outputToken,
          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;
  }
@@@ -596,50 -577,6 +596,50 @@@ eapGssSmInitAcceptorName(OM_uint32 *min
  }
  
  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,
          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);
@@@ -768,7 -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);
  }
  
  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,
                                 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;
  }
  
@@@ -856,51 -774,39 +856,51 @@@ eapGssSmInitReauthCreds(OM_uint32 *mino
  #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;
      return GSS_S_COMPLETE;
  }
  
 +/*
 + * Initiator state machine.
 + */
  static struct gss_eap_sm eapGssInitiatorSm[] = {
      {
          ITOK_TYPE_CONTEXT_ERR,
          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,
      },
      {
          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
      {
  #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
      }
  };
  
@@@ -58,6 -58,9 +58,6 @@@ OM_uint32 gss_inquire_name(OM_uint32 *m
          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);
@@@ -2,7 -2,6 +2,7 @@@ gss_accept_sec_contex
  gss_acquire_cred
  gss_add_cred
  gss_add_cred_with_password
 +gss_authorize_localname
  gss_canonicalize_name
  gss_compare_name
  gss_context_time
@@@ -42,6 -41,7 +42,6 @@@ gss_set_sec_context_optio
  gss_store_cred
  gss_unwrap
  gss_unwrap_iov
 -gss_userok
  gss_verify_mic
  gss_wrap
  gss_wrap_iov
diff --combined moonshot/mech_eap/util.h
@@@ -164,65 -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);
@@@ -623,7 -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 */
@@@ -650,48 -631,24 +650,48 @@@ gssEapSmTransition(gss_ctx_id_t ctx, en
  
  /* 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,
@@@ -854,15 -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;
@@@ -130,7 -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);
  
  }
  
  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;
  }
@@@ -95,16 -95,21 +95,16 @@@ gssEapSmTransition(gss_ctx_id_t ctx, en
  #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.
      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
@@@ -178,13 -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);
  
      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;
      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];
                  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++) {
              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;
          }
      }
  
  
  cleanup:
      gss_release_buffer_set(&tmpMinor, &innerInputTokens);
 -    gss_release_buffer_set(&tmpMinor, &innerOutputTokens);
      if (inputTokenTypes != NULL)
          GSSEAP_FREE(inputTokenTypes);
      if (outputTokenTypes != NULL)
  #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,
          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;
          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:
  }
  
  /*
 + * 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 $
   */
  
@@@ -448,8 -305,12 +448,8 @@@ der_read_length(unsigned char **buf, ss
  /* 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;
  }
  
  
  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);
  }
  
  /*