Merge branch 'master' into tlv-mic
authorLuke Howard <lukeh@padl.com>
Thu, 21 Apr 2011 18:22:45 +0000 (20:22 +0200)
committerLuke Howard <lukeh@padl.com>
Thu, 21 Apr 2011 18:22:45 +0000 (20:22 +0200)
Conflicts:
mech_eap/accept_sec_context.c

1  2 
mech_eap/accept_sec_context.c
mech_eap/gssapiP_eap.h
mech_eap/init_sec_context.c
mech_eap/util_context.c

@@@ -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->kerberosCtx,
+                                 &ctx->reauthCtx,
                                  cred->krbCred,
                                  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->kerberosCtx, GSS_C_NO_BUFFER);
+         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;
  }
diff --combined mech_eap/gssapiP_eap.h
@@@ -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)
  
@@@ -202,11 -201,10 +202,11 @@@ struct gss_ctx_id_struc
          struct gss_eap_acceptor_ctx  acceptor;
          #define acceptorCtx          ctxU.acceptor
  #ifdef GSSEAP_ENABLE_REAUTH
-         gss_ctx_id_t                 kerberos;
-         #define kerberosCtx          ctxU.kerberos
+         gss_ctx_id_t                 reauth;
+         #define reauthCtx            ctxU.reauth
  #endif
      } ctxU;
 +    gss_buffer_desc conversation;
  };
  
  #define TOK_FLAG_SENDER_IS_ACCEPTOR         0x01
@@@ -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) {
  
      major = gssInitSecContext(minor,
                                cred->krbCred,
-                               &ctx->kerberosCtx,
+                               &ctx->reauthCtx,
                                mechTarget,
                                (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,
          OM_uint32 tmpMinor;
  
          /* server didn't support reauthentication, sent EAP request */
-         gssDeleteSecContext(&tmpMinor, &ctx->kerberosCtx, GSS_C_NO_BUFFER);
+         gssDeleteSecContext(&tmpMinor, &ctx->reauthCtx, GSS_C_NO_BUFFER);
          ctx->flags &= ~(CTX_FLAG_KRB_REAUTH);
          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
      }
  };
  
diff --combined mech_eap/util_context.c
@@@ -115,7 -115,7 +115,7 @@@ gssEapReleaseContext(OM_uint32 *minor
  
  #ifdef GSSEAP_ENABLE_REAUTH
      if (ctx->flags & CTX_FLAG_KRB_REAUTH) {
-         gssDeleteSecContext(&tmpMinor, &ctx->kerberosCtx, GSS_C_NO_BUFFER);
+         gssDeleteSecContext(&tmpMinor, &ctx->reauthCtx, GSS_C_NO_BUFFER);
      } else
  #endif
      if (CTX_IS_INITIATOR(ctx)) {
      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;
  }