Merge branch 'master' into tlv-mic
authorLuke Howard <lukeh@padl.com>
Fri, 22 Apr 2011 10:58:57 +0000 (12:58 +0200)
committerLuke Howard <lukeh@padl.com>
Fri, 22 Apr 2011 10:58:57 +0000 (12:58 +0200)
1  2 
mech_eap/accept_sec_context.c
mech_eap/gssapiP_eap.h
mech_eap/init_sec_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->reauthCtx,
-                                 cred->krbCred,
+                                 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;
  }
diff --combined mech_eap/gssapiP_eap.h
@@@ -135,13 -135,12 +135,13 @@@ struct gss_cred_id_struc
      char *radiusConfigStanza;
  #ifdef GSSEAP_ENABLE_REAUTH
      krb5_ccache krbCredCache;
-     gss_cred_id_t krbCred;
+     gss_cred_id_t reauthCred;
  #endif
  };
  
  #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
@@@ -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) {
          goto cleanup;
  
      major = gssInitSecContext(minor,
-                               cred->krbCred,
+                               cred->reauthCred,
                                &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,
          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
      }
  };