Fixes for Heimdal (macOS) builds from Stefan.
[mech_eap.git] / mech_eap / init_sec_context.c
index e99b479..89faf49 100644 (file)
  */
 
 #include "gssapiP_eap.h"
+#include "radius/radius.h"
+#include "util_radius.h"
+#include "utils/radius_utils.h"
+#include "openssl/err.h"
+#ifdef HAVE_MOONSHOT_GET_IDENTITY
+#include "libmoonshot.h"
+#endif
+
+/* methods allowed for phase1 authentication*/
+static const struct eap_method_type allowed_eap_method_types[] = {
+    {EAP_VENDOR_IETF, EAP_TYPE_TTLS},
+    {EAP_VENDOR_IETF, EAP_TYPE_NONE}};
 
 static OM_uint32
 policyVariableToFlag(enum eapol_bool_var variable)
@@ -70,6 +82,9 @@ policyVariableToFlag(enum eapol_bool_var variable)
     case EAPOL_altReject:
         flag = CTX_FLAG_EAP_ALT_REJECT;
         break;
+    case EAPOL_eapTriggerStart:
+        flag = CTX_FLAG_EAP_TRIGGER_START;
+        break;
     }
 
     return flag;
@@ -167,10 +182,22 @@ peerSetConfigBlob(void *ctx GSSEAP_UNUSED,
 }
 
 static const struct wpa_config_blob *
-peerGetConfigBlob(void *ctx GSSEAP_UNUSED,
-                  const char *name GSSEAP_UNUSED)
+peerGetConfigBlob(void *ctx,
+                  const char *name)
 {
-    return NULL;
+    gss_ctx_id_t gssCtx = (gss_ctx_id_t)ctx;
+    size_t index;
+
+    if (strcmp(name, "client-cert") == 0)
+        index = CONFIG_BLOB_CLIENT_CERT;
+    else if (strcmp(name, "private-key") == 0)
+        index = CONFIG_BLOB_PRIVATE_KEY;
+    else if (strcmp(name, "ca-cert") == 0)
+        index = CONFIG_BLOB_CA_CERT;
+    else
+        return NULL;
+
+    return &gssCtx->initiatorCtx.configBlobs[index];
 }
 
 static void
@@ -178,6 +205,7 @@ peerNotifyPending(void *ctx GSSEAP_UNUSED)
 {
 }
 
+
 static struct eapol_callbacks gssEapPolicyCallbacks = {
     peerGetConfig,
     peerGetBool,
@@ -188,10 +216,236 @@ static struct eapol_callbacks gssEapPolicyCallbacks = {
     peerSetConfigBlob,
     peerGetConfigBlob,
     peerNotifyPending,
+    NULL,  /* eap_param_needed */
+    NULL   /* eap_notify_cert */
 };
 
-#ifdef GSSEAP_DEBUG
-extern int wpa_debug_level;
+
+#define CHBIND_SERVICE_NAME_FLAG        0x01
+#define CHBIND_HOST_NAME_FLAG           0x02
+#define CHBIND_SERVICE_SPECIFIC_FLAG    0x04
+#define CHBIND_REALM_NAME_FLAG          0x08
+
+static OM_uint32
+peerInitEapChannelBinding(OM_uint32 *minor, gss_ctx_id_t ctx)
+{
+    struct wpabuf *buf = NULL;
+    unsigned int chbindReqFlags = 0;
+    krb5_principal princ = NULL;
+    gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
+    OM_uint32 major = GSS_S_COMPLETE;
+    krb5_context krbContext = NULL;
+
+    /* XXX is this check redundant? */
+    if (ctx->acceptorName == GSS_C_NO_NAME) {
+        major = GSS_S_BAD_NAME;
+        *minor = GSSEAP_NO_ACCEPTOR_NAME;
+        goto cleanup;
+    }
+
+    princ = ctx->acceptorName->krbPrincipal;
+
+    krbPrincComponentToGssBuffer(princ, 0, &nameBuf);
+    if (nameBuf.length > 0) {
+        major = gssEapRadiusAddAttr(minor, &buf, PW_GSS_ACCEPTOR_SERVICE_NAME,
+                                    0, &nameBuf);
+        if (GSS_ERROR(major))
+            goto cleanup;
+
+        chbindReqFlags |= CHBIND_SERVICE_NAME_FLAG;
+    }
+
+    krbPrincComponentToGssBuffer(princ, 1, &nameBuf);
+    if (nameBuf.length > 0) {
+        major = gssEapRadiusAddAttr(minor, &buf, PW_GSS_ACCEPTOR_HOST_NAME,
+                                    0, &nameBuf);
+        if (GSS_ERROR(major))
+            goto cleanup;
+
+        chbindReqFlags |= CHBIND_HOST_NAME_FLAG;
+    }
+
+    GSSEAP_KRB_INIT(&krbContext);
+
+    *minor = krbPrincUnparseServiceSpecifics(krbContext, princ, &nameBuf);
+    if (*minor != 0)
+        goto cleanup;
+
+    if (nameBuf.length > 0) {
+        major = gssEapRadiusAddAttr(minor, &buf,
+                                    PW_GSS_ACCEPTOR_SERVICE_SPECIFICS,
+                                    0, &nameBuf);
+        if (GSS_ERROR(major))
+            goto cleanup;
+
+        chbindReqFlags |= CHBIND_SERVICE_SPECIFIC_FLAG;
+    }
+
+    krbFreeUnparsedName(krbContext, &nameBuf);
+    krbPrincRealmToGssBuffer(princ, &nameBuf);
+
+    if (nameBuf.length > 0) {
+        major = gssEapRadiusAddAttr(minor, &buf,
+                                    PW_GSS_ACCEPTOR_REALM_NAME,
+                                    0, &nameBuf);
+        if (GSS_ERROR(major))
+            goto cleanup;
+
+        chbindReqFlags |= CHBIND_REALM_NAME_FLAG;
+    }
+
+    if (chbindReqFlags == 0) {
+        major = GSS_S_BAD_NAME;
+        *minor = GSSEAP_BAD_ACCEPTOR_NAME;
+        goto cleanup;
+    }
+
+    ctx->initiatorCtx.chbindData = buf;
+    ctx->initiatorCtx.chbindReqFlags = chbindReqFlags;
+
+    buf = NULL;
+
+    major = GSS_S_COMPLETE;
+    *minor = 0;
+
+cleanup:
+    /*namebuf is freed when used and may be left with a unowned pointer*/
+    wpabuf_free(buf);
+
+    return major;
+}
+
+static void
+peerProcessChbindResponse(void *context, int code, int nsid,
+                          u8 *data, size_t len)
+{
+    radius_parser msg;
+    gss_ctx_id_t ctx = (gss_ctx_id_t )context;
+    void *vsadata;
+    u8 type;
+    u32 vendor_id;
+    u32 chbindRetFlags = 0;
+    size_t vsadata_len;
+
+    if (nsid != CHBIND_NSID_RADIUS)
+        return;
+
+    if (data == NULL)
+        return;
+    msg = radius_parser_start(data, len);
+    if (msg == NULL)
+        return;
+
+    while (radius_parser_parse_tlv(msg, &type, &vendor_id, &vsadata,
+                                   &vsadata_len) == 0) {
+        switch (type) {
+        case PW_GSS_ACCEPTOR_SERVICE_NAME:
+            chbindRetFlags |= CHBIND_SERVICE_NAME_FLAG;
+            break;
+        case PW_GSS_ACCEPTOR_HOST_NAME:
+            chbindRetFlags |= CHBIND_HOST_NAME_FLAG;
+            break;
+        case PW_GSS_ACCEPTOR_SERVICE_SPECIFICS:
+            chbindRetFlags |= CHBIND_SERVICE_SPECIFIC_FLAG;
+            break;
+        case PW_GSS_ACCEPTOR_REALM_NAME:
+            chbindRetFlags |= CHBIND_REALM_NAME_FLAG;
+            break;
+        }
+    }
+
+    radius_parser_finish(msg);
+
+    if (code == CHBIND_CODE_SUCCESS &&
+        ((chbindRetFlags & ctx->initiatorCtx.chbindReqFlags) == ctx->initiatorCtx.chbindReqFlags)) {
+        ctx->flags |= CTX_FLAG_EAP_CHBIND_ACCEPT;
+        ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
+    } /* else log failures? */
+}
+
+#ifdef HAVE_MOONSHOT_GET_IDENTITY
+static int cert_to_byte_array(X509 *cert, unsigned char **bytes)
+{
+       unsigned char *buf;
+    unsigned char *p;
+
+       int len = i2d_X509(cert, NULL);
+       if (len <= 0) {
+               return -1;
+    }
+
+       p = buf = GSSEAP_MALLOC(len);
+       if (buf == NULL) {
+               return -1;
+    }
+
+       i2d_X509(cert, &buf);
+
+    *bytes = p;
+    return len;
+}
+
+static int sha256(unsigned char *bytes, int len, unsigned char *hash)
+{
+       EVP_MD_CTX ctx;
+       unsigned int hash_len;
+
+       EVP_MD_CTX_init(&ctx);
+       if (!EVP_DigestInit_ex(&ctx, EVP_sha256(), NULL)) {
+               printf("sha256(init_sec_context.c): EVP_DigestInit_ex failed: %s",
+                          ERR_error_string(ERR_get_error(), NULL));
+               return -1;
+       }
+    if (!EVP_DigestUpdate(&ctx, bytes, len)) {
+               printf("sha256(init_sec_context.c): EVP_DigestUpdate failed: %s",
+                                  ERR_error_string(ERR_get_error(), NULL));
+        return -1;
+       }
+       if (!EVP_DigestFinal(&ctx, hash, &hash_len)) {
+               printf("sha256(init_sec_context.c): EVP_DigestFinal failed: %s",
+                                  ERR_error_string(ERR_get_error(), NULL));
+               return -1;
+       }
+
+       return hash_len;
+}
+
+static int peerValidateServerCert(int ok_so_far, X509* cert, void *ca_ctx)
+{
+    char                 *realm = NULL;
+    unsigned char        *cert_bytes = NULL;
+    int                   cert_len;
+    unsigned char         hash[32];
+    int                   hash_len;
+    MoonshotError        *error = NULL;
+    struct eap_peer_config *eap_config = (struct eap_peer_config *) ca_ctx;
+    char *identity = strdup((const char *) eap_config->identity);
+
+    // Truncate the identity to just the username; make a separate string for the realm.
+    char* at = strchr(identity, '@');
+    if (at != NULL) {
+        realm = strdup(at + 1);
+        *at = '\0';
+    }
+    
+    cert_len = cert_to_byte_array(cert, &cert_bytes);
+    hash_len = sha256(cert_bytes, cert_len, hash);
+    GSSEAP_FREE(cert_bytes);
+    
+    if (hash_len != 32) {
+        fprintf(stderr, "peerValidateServerCert: Error: hash_len=%d, not 32!\n", hash_len);
+        return FALSE;
+    }
+
+    ok_so_far = moonshot_confirm_ca_certificate(identity, realm, hash, 32, &error);
+    free(identity);
+    if (realm != NULL) {
+        free(realm);
+    }
+    
+    wpa_printf(MSG_INFO, "peerValidateServerCert: Returning %d\n", ok_so_far);
+    return ok_so_far;
+}
 #endif
 
 static OM_uint32
@@ -200,6 +454,7 @@ peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
     OM_uint32 major;
     krb5_context krbContext;
     struct eap_peer_config *eapPeerConfig = &ctx->initiatorCtx.eapPeerConfig;
+    struct wpa_config_blob *configBlobs = ctx->initiatorCtx.configBlobs;
     gss_buffer_desc identity = GSS_C_EMPTY_BUFFER;
     gss_buffer_desc realm = GSS_C_EMPTY_BUFFER;
     gss_cred_id_t cred = ctx->cred;
@@ -210,16 +465,14 @@ peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
     eapPeerConfig->anonymous_identity_len = 0;
     eapPeerConfig->password = NULL;
     eapPeerConfig->password_len = 0;
+    eapPeerConfig->eap_methods = (struct eap_method_type *) allowed_eap_method_types;
 
     GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
 
     GSSEAP_KRB_INIT(&krbContext);
 
     eapPeerConfig->fragment_size = 1024;
-#ifdef GSSEAP_DEBUG
-    wpa_debug_level = 0;
-#endif
-
+    
     GSSEAP_ASSERT(cred->name != GSS_C_NO_NAME);
 
     if ((cred->name->flags & (NAME_FLAG_NAI | NAME_FLAG_SERVICE)) == 0) {
@@ -250,13 +503,63 @@ peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
     eapPeerConfig->anonymous_identity_len = 1 + realm.length;
 
     /* password */
-    eapPeerConfig->password = (unsigned char *)cred->password.value;
-    eapPeerConfig->password_len = cred->password.length;
+    if ((cred->flags & CRED_FLAG_CERTIFICATE) == 0) {
+        eapPeerConfig->password = (unsigned char *)cred->password.value;
+        eapPeerConfig->password_len = cred->password.length;
+    }
 
     /* certs */
     eapPeerConfig->ca_cert = (unsigned char *)cred->caCertificate.value;
     eapPeerConfig->subject_match = (unsigned char *)cred->subjectNameConstraint.value;
     eapPeerConfig->altsubject_match = (unsigned char *)cred->subjectAltNameConstraint.value;
+    configBlobs[CONFIG_BLOB_CA_CERT].data = cred->caCertificateBlob.value;
+    configBlobs[CONFIG_BLOB_CA_CERT].len = cred->caCertificateBlob.length;
+
+    /* eap channel binding */
+    if (ctx->initiatorCtx.chbindData != NULL) {
+        struct eap_peer_chbind_config *chbind_config =
+            (struct eap_peer_chbind_config *)GSSEAP_MALLOC(sizeof(struct eap_peer_chbind_config));
+        if (chbind_config == NULL) {
+            *minor = ENOMEM;
+            return GSS_S_FAILURE;
+        }
+
+        chbind_config->req_data = wpabuf_mhead_u8(ctx->initiatorCtx.chbindData);
+        chbind_config->req_data_len = wpabuf_len(ctx->initiatorCtx.chbindData);
+        chbind_config->nsid = CHBIND_NSID_RADIUS;
+        chbind_config->response_cb = &peerProcessChbindResponse;
+        chbind_config->ctx = ctx;
+        eapPeerConfig->chbind_config = chbind_config;
+        eapPeerConfig->chbind_config_len = 1;
+    } else {
+        eapPeerConfig->chbind_config = NULL;
+        eapPeerConfig->chbind_config_len = 0;
+    }
+    if (cred->flags & CRED_FLAG_CERTIFICATE) {
+        /*
+         * CRED_FLAG_CONFIG_BLOB is an internal flag which will be used in the
+         * future to directly pass certificate and private key data to the
+         * EAP implementation, rather than an indirected string pointer.
+         */
+        if (cred->flags & CRED_FLAG_CONFIG_BLOB) {
+            eapPeerConfig->client_cert = (unsigned char *)"blob://client-cert";
+            configBlobs[CONFIG_BLOB_CLIENT_CERT].data = cred->clientCertificate.value;
+            configBlobs[CONFIG_BLOB_CLIENT_CERT].len  = cred->clientCertificate.length;
+
+            eapPeerConfig->client_cert = (unsigned char *)"blob://private-key";
+            configBlobs[CONFIG_BLOB_PRIVATE_KEY].data = cred->clientCertificate.value;
+            configBlobs[CONFIG_BLOB_PRIVATE_KEY].len  = cred->privateKey.length;
+        } else {
+            eapPeerConfig->client_cert = (unsigned char *)cred->clientCertificate.value;
+            eapPeerConfig->private_key = (unsigned char *)cred->privateKey.value;
+        }
+        eapPeerConfig->private_key_passwd = (char *)cred->password.value;
+    }
+
+#ifdef HAVE_MOONSHOT_GET_IDENTITY
+    eapPeerConfig->server_cert_cb = peerValidateServerCert;
+#endif
+    eapPeerConfig->server_cert_ctx = eapPeerConfig;
 
     *minor = 0;
     return GSS_S_COMPLETE;
@@ -288,18 +591,12 @@ peerConfigFree(OM_uint32 *minor,
  * Mark an initiator context as ready for cryptographic operations
  */
 static OM_uint32
-initReady(OM_uint32 *minor, gss_ctx_id_t ctx, OM_uint32 reqFlags)
+initReady(OM_uint32 *minor, gss_ctx_id_t ctx)
 {
     OM_uint32 major;
     const unsigned char *key;
     size_t keyLength;
 
-#if 1
-    /* XXX actually check for mutual auth */
-    if (reqFlags & GSS_C_MUTUAL_FLAG)
-        ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
-#endif
-
     /* Cache encryption type derived from selected mechanism OID */
     major = gssEapOidToEnctype(minor, ctx->mechanismUsed, &ctx->encryptionType);
     if (GSS_ERROR(major))
@@ -346,7 +643,7 @@ initReady(OM_uint32 *minor, gss_ctx_id_t ctx, OM_uint32 reqFlags)
 static OM_uint32
 initBegin(OM_uint32 *minor,
           gss_ctx_id_t ctx,
-          gss_name_t target,
+          gss_const_name_t target,
           gss_OID mech,
           OM_uint32 reqFlags GSSEAP_UNUSED,
           OM_uint32 timeReq,
@@ -374,15 +671,15 @@ initBegin(OM_uint32 *minor,
         return major;
 
     if (target != GSS_C_NO_NAME) {
-        GSSEAP_MUTEX_LOCK(&target->mutex);
+        GSSEAP_MUTEX_LOCK(&((gss_name_t)target)->mutex);
 
         major = gssEapDuplicateName(minor, target, &ctx->acceptorName);
         if (GSS_ERROR(major)) {
-            GSSEAP_MUTEX_UNLOCK(&target->mutex);
+            GSSEAP_MUTEX_LOCK(&((gss_name_t)target)->mutex);
             return major;
         }
 
-        GSSEAP_MUTEX_UNLOCK(&target->mutex);
+        GSSEAP_MUTEX_UNLOCK(&((gss_name_t)target)->mutex);
     }
 
     major = gssEapCanonicalizeOid(minor,
@@ -406,7 +703,7 @@ static OM_uint32
 eapGssSmInitError(OM_uint32 *minor,
                   gss_cred_id_t cred GSSEAP_UNUSED,
                   gss_ctx_id_t ctx GSSEAP_UNUSED,
-                  gss_name_t target GSSEAP_UNUSED,
+                  gss_const_name_t target GSSEAP_UNUSED,
                   gss_OID mech GSSEAP_UNUSED,
                   OM_uint32 reqFlags GSSEAP_UNUSED,
                   OM_uint32 timeReq GSSEAP_UNUSED,
@@ -426,7 +723,10 @@ eapGssSmInitError(OM_uint32 *minor,
     p = (unsigned char *)inputToken->value;
 
     major = load_uint32_be(&p[0]);
-    *minor = ERROR_TABLE_BASE_eapg + load_uint32_be(&p[4]);
+    *minor =  load_uint32_be(&p[4]);
+    if ((*minor >0) && (*minor < 128))
+      * minor += ERROR_TABLE_BASE_eapg;
+    else *minor = 0;
 
     if (!GSS_ERROR(major) || !IS_WIRE_ERROR(*minor)) {
         major = GSS_S_FAILURE;
@@ -443,7 +743,7 @@ static OM_uint32
 eapGssSmInitGssReauth(OM_uint32 *minor,
                       gss_cred_id_t cred,
                       gss_ctx_id_t ctx,
-                      gss_name_t target,
+                      gss_const_name_t target,
                       gss_OID mech GSSEAP_UNUSED,
                       OM_uint32 reqFlags,
                       OM_uint32 timeReq,
@@ -462,7 +762,7 @@ eapGssSmInitGssReauth(OM_uint32 *minor,
      * context credential does not currently have the reauth creds.
      */
     if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_INITIAL) {
-        if (!gssEapCanReauthP(cred, target, timeReq))
+      if (!gssEapCanReauthP(cred, (gss_name_t) target, timeReq))
             return GSS_S_CONTINUE_NEEDED;
 
         ctx->flags |= CTX_FLAG_KRB_REAUTH;
@@ -474,7 +774,7 @@ eapGssSmInitGssReauth(OM_uint32 *minor,
 
     GSSEAP_ASSERT(cred != GSS_C_NO_CREDENTIAL);
 
-    major = gssEapMechToGlueName(minor, target, &mechTarget);
+    major = gssEapMechToGlueName(minor, (gss_name_t) target, &mechTarget);
     if (GSS_ERROR(major))
         goto cleanup;
 
@@ -519,7 +819,7 @@ static OM_uint32
 eapGssSmInitVendorInfo(OM_uint32 *minor,
                        gss_cred_id_t cred GSSEAP_UNUSED,
                        gss_ctx_id_t ctx GSSEAP_UNUSED,
-                       gss_name_t target GSSEAP_UNUSED,
+                       gss_const_name_t target GSSEAP_UNUSED,
                        gss_OID mech GSSEAP_UNUSED,
                        OM_uint32 reqFlags GSSEAP_UNUSED,
                        OM_uint32 timeReq GSSEAP_UNUSED,
@@ -542,7 +842,7 @@ static OM_uint32
 eapGssSmInitAcceptorName(OM_uint32 *minor,
                          gss_cred_id_t cred GSSEAP_UNUSED,
                          gss_ctx_id_t ctx,
-                         gss_name_t target GSSEAP_UNUSED,
+                         gss_const_name_t target GSSEAP_UNUSED,
                          gss_OID mech GSSEAP_UNUSED,
                          OM_uint32 reqFlags GSSEAP_UNUSED,
                          OM_uint32 timeReq GSSEAP_UNUSED,
@@ -561,17 +861,45 @@ eapGssSmInitAcceptorName(OM_uint32 *minor,
                                   outputToken, NULL);
         if (GSS_ERROR(major))
             return major;
-    } else if (inputToken != GSS_C_NO_BUFFER &&
-               ctx->acceptorName == GSS_C_NO_NAME) {
-        /* Accept target name hint from acceptor */
+    } else if (inputToken != GSS_C_NO_BUFFER) {
+        OM_uint32 tmpMinor;
+        gss_name_t nameHint;
+        int equal;
+
+        /* Accept target name hint from acceptor or verify acceptor */
         major = gssEapImportName(minor, inputToken,
                                  GSS_C_NT_USER_NAME,
                                  ctx->mechanismUsed,
-                                 &ctx->acceptorName);
+                                 &nameHint);
         if (GSS_ERROR(major))
             return major;
+
+        if (ctx->acceptorName != GSS_C_NO_NAME) {
+            /* verify name hint matched asserted acceptor name  */
+            major = gssEapCompareName(minor,
+                                      nameHint,
+                                      ctx->acceptorName,
+                                      COMPARE_NAME_FLAG_IGNORE_EMPTY_REALMS,
+                                      &equal);
+            if (GSS_ERROR(major)) {
+                gssEapReleaseName(&tmpMinor, &nameHint);
+                return major;
+            }
+
+            gssEapReleaseName(&tmpMinor, &nameHint);
+
+            if (!equal) {
+                *minor = GSSEAP_WRONG_ACCEPTOR_NAME;
+                return GSS_S_DEFECTIVE_TOKEN;
+            }
+        } else { /* acceptor name is no_name */
+            /* accept acceptor name hint */
+            ctx->acceptorName = nameHint;
+            nameHint = GSS_C_NO_NAME;
+        }
     }
 
+
     /*
      * Currently, other parts of the code assume that the acceptor name
      * is available, hence this check.
@@ -581,6 +909,15 @@ eapGssSmInitAcceptorName(OM_uint32 *minor,
         return GSS_S_FAILURE;
     }
 
+    /*
+     * Generate channel binding data
+     */
+    if (ctx->initiatorCtx.chbindData == NULL) {
+        major = peerInitEapChannelBinding(minor, ctx);
+        if (GSS_ERROR(major))
+            return major;
+    }
+
     return GSS_S_CONTINUE_NEEDED;
 }
 
@@ -588,7 +925,7 @@ static OM_uint32
 eapGssSmInitIdentity(OM_uint32 *minor,
                      gss_cred_id_t cred GSSEAP_UNUSED,
                      gss_ctx_id_t ctx,
-                     gss_name_t target GSSEAP_UNUSED,
+                     gss_const_name_t target GSSEAP_UNUSED,
                      gss_OID mech GSSEAP_UNUSED,
                      OM_uint32 reqFlags GSSEAP_UNUSED,
                      OM_uint32 timeReq GSSEAP_UNUSED,
@@ -598,6 +935,8 @@ eapGssSmInitIdentity(OM_uint32 *minor,
                      OM_uint32 *smFlags)
 {
     struct eap_config eapConfig;
+    memset(&eapConfig, 0, sizeof(eapConfig));
+    eapConfig.cert_in_cb = 1;
 
 #ifdef GSSEAP_ENABLE_REAUTH
     if (GSSEAP_SM_STATE(ctx) == GSSEAP_STATE_REAUTHENTICATE) {
@@ -614,11 +953,9 @@ eapGssSmInitIdentity(OM_uint32 *minor,
     GSSEAP_ASSERT((ctx->flags & CTX_FLAG_KRB_REAUTH) == 0);
     GSSEAP_ASSERT(inputToken == GSS_C_NO_BUFFER);
 
-    memset(&eapConfig, 0, sizeof(eapConfig));
-
     ctx->initiatorCtx.eap = eap_peer_sm_init(ctx,
                                              &gssEapPolicyCallbacks,
-                                             ctx,
+                                             NULL, /* ctx?? */
                                              &eapConfig);
     if (ctx->initiatorCtx.eap == NULL) {
         *minor = GSSEAP_PEER_SM_INIT_FAILURE;
@@ -644,7 +981,7 @@ static OM_uint32
 eapGssSmInitAuthenticate(OM_uint32 *minor,
                          gss_cred_id_t cred GSSEAP_UNUSED,
                          gss_ctx_id_t ctx,
-                         gss_name_t target GSSEAP_UNUSED,
+                         gss_const_name_t target GSSEAP_UNUSED,
                          gss_OID mech GSSEAP_UNUSED,
                          OM_uint32 reqFlags GSSEAP_UNUSED,
                          OM_uint32 timeReq GSSEAP_UNUSED,
@@ -681,7 +1018,7 @@ eapGssSmInitAuthenticate(OM_uint32 *minor,
 
         resp = eap_get_eapRespData(ctx->initiatorCtx.eap);
     } else if (ctx->flags & CTX_FLAG_EAP_SUCCESS) {
-        major = initReady(minor, ctx, reqFlags);
+        major = initReady(minor, ctx);
         if (GSS_ERROR(major))
             goto cleanup;
 
@@ -725,7 +1062,7 @@ 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_const_name_t target GSSEAP_UNUSED,
                      gss_OID mech GSSEAP_UNUSED,
                      OM_uint32 reqFlags GSSEAP_UNUSED,
                      OM_uint32 timeReq GSSEAP_UNUSED,
@@ -737,6 +1074,11 @@ eapGssSmInitGssFlags(OM_uint32 *minor,
     unsigned char wireFlags[4];
     gss_buffer_desc flagsBuf;
 
+    /*
+     * As a temporary measure, force mutual authentication until channel binding is
+     * more widely deployed.
+     */
+    ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
     store_uint32_be(ctx->gssFlags & GSSEAP_WIRE_FLAGS_MASK, wireFlags);
 
     flagsBuf.length = sizeof(wireFlags);
@@ -749,7 +1091,7 @@ static OM_uint32
 eapGssSmInitGssChannelBindings(OM_uint32 *minor,
                                gss_cred_id_t cred GSSEAP_UNUSED,
                                gss_ctx_id_t ctx,
-                               gss_name_t target GSSEAP_UNUSED,
+                               gss_const_name_t target GSSEAP_UNUSED,
                                gss_OID mech GSSEAP_UNUSED,
                                OM_uint32 reqFlags GSSEAP_UNUSED,
                                OM_uint32 timeReq GSSEAP_UNUSED,
@@ -759,21 +1101,63 @@ eapGssSmInitGssChannelBindings(OM_uint32 *minor,
                                OM_uint32 *smFlags)
 {
     OM_uint32 major;
-    gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
+    krb5_error_code code;
+    krb5_context krbContext;
+    krb5_data data;
+    krb5_checksum cksum;
+    gss_buffer_desc cksumBuffer;
+#ifdef HAVE_HEIMDAL_VERSION
+    krb5_crypto krbCrypto;
+#endif
 
-    if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
-        buffer = chanBindings->application_data;
+    if (chanBindings == GSS_C_NO_CHANNEL_BINDINGS ||
+        chanBindings->application_data.length == 0)
+        return GSS_S_CONTINUE_NEEDED;
 
-    major = gssEapWrap(minor, ctx, TRUE, GSS_C_QOP_DEFAULT,
-                       &buffer, NULL, outputToken);
-    if (GSS_ERROR(major))
-        return major;
+    GSSEAP_KRB_INIT(&krbContext);
+
+    KRB_DATA_INIT(&data);
 
-    GSSEAP_ASSERT(outputToken->value != NULL);
+    gssBufferToKrbData(&chanBindings->application_data, &data);
+
+#ifdef HAVE_HEIMDAL_VERSION
+    code = krb5_crypto_init(krbContext, &ctx->rfc3961Key, 0, &krbCrypto);
+    if (code != 0) {
+        *minor = code;
+        return GSS_S_FAILURE;
+    }
+
+    code = krb5_create_checksum(krbContext, krbCrypto,
+                                KEY_USAGE_GSSEAP_CHBIND_MIC,
+                                ctx->checksumType,
+                                data.data, data.length,
+                                &cksum);
+    krb5_crypto_destroy(krbContext, krbCrypto);
+#else
+    code = krb5_c_make_checksum(krbContext, ctx->checksumType,
+                                &ctx->rfc3961Key,
+                                KEY_USAGE_GSSEAP_CHBIND_MIC,
+                                &data, &cksum);
+#endif /* HAVE_HEIMDAL_VERSION */
+    if (code != 0) {
+        *minor = code;
+        return GSS_S_FAILURE;
+    }
+
+    cksumBuffer.length = KRB_CHECKSUM_LENGTH(&cksum);
+    cksumBuffer.value  = KRB_CHECKSUM_DATA(&cksum);
+
+    major = duplicateBuffer(minor, &cksumBuffer, outputToken);
+    if (GSS_ERROR(major)) {
+        KRB_CHECKSUM_FREE(krbContext, &cksum);
+        return major;
+    }
 
     *minor = 0;
     *smFlags |= SM_FLAG_OUTPUT_TOKEN_CRITICAL;
 
+    KRB_CHECKSUM_FREE(krbContext, &cksum);
+
     return GSS_S_CONTINUE_NEEDED;
 }
 
@@ -781,7 +1165,7 @@ static OM_uint32
 eapGssSmInitInitiatorMIC(OM_uint32 *minor,
                          gss_cred_id_t cred GSSEAP_UNUSED,
                          gss_ctx_id_t ctx,
-                         gss_name_t target GSSEAP_UNUSED,
+                         gss_const_name_t target GSSEAP_UNUSED,
                          gss_OID mech GSSEAP_UNUSED,
                          OM_uint32 reqFlags GSSEAP_UNUSED,
                          OM_uint32 timeReq GSSEAP_UNUSED,
@@ -803,13 +1187,13 @@ eapGssSmInitInitiatorMIC(OM_uint32 *minor,
 
     return GSS_S_CONTINUE_NEEDED;
 }
+
 #ifdef GSSEAP_ENABLE_REAUTH
 static OM_uint32
 eapGssSmInitReauthCreds(OM_uint32 *minor,
                         gss_cred_id_t cred,
                         gss_ctx_id_t ctx,
-                        gss_name_t target GSSEAP_UNUSED,
+                        gss_const_name_t target GSSEAP_UNUSED,
                         gss_OID mech GSSEAP_UNUSED,
                         OM_uint32 reqFlags GSSEAP_UNUSED,
                         OM_uint32 timeReq GSSEAP_UNUSED,
@@ -835,7 +1219,7 @@ static OM_uint32
 eapGssSmInitAcceptorMIC(OM_uint32 *minor,
                         gss_cred_id_t cred GSSEAP_UNUSED,
                         gss_ctx_id_t ctx,
-                        gss_name_t target GSSEAP_UNUSED,
+                        gss_const_name_t target GSSEAP_UNUSED,
                         gss_OID mech GSSEAP_UNUSED,
                         OM_uint32 reqFlags GSSEAP_UNUSED,
                         OM_uint32 timeReq GSSEAP_UNUSED,
@@ -868,7 +1252,8 @@ static struct gss_eap_sm eapGssInitiatorSm[] = {
     {
         ITOK_TYPE_ACCEPTOR_NAME_RESP,
         ITOK_TYPE_ACCEPTOR_NAME_REQ,
-        GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE,
+        GSSEAP_STATE_INITIAL | GSSEAP_STATE_AUTHENTICATE |
+        GSSEAP_STATE_ACCEPTOR_EXTS,
         0,
         eapGssSmInitAcceptorName
     },
@@ -918,7 +1303,7 @@ static struct gss_eap_sm eapGssInitiatorSm[] = {
         ITOK_TYPE_NONE,
         ITOK_TYPE_GSS_CHANNEL_BINDINGS,
         GSSEAP_STATE_INITIATOR_EXTS,
-        SM_ITOK_FLAG_REQUIRED,
+        0,
         eapGssSmInitGssChannelBindings
     },
     {
@@ -951,7 +1336,7 @@ OM_uint32
 gssEapInitSecContext(OM_uint32 *minor,
                      gss_cred_id_t cred,
                      gss_ctx_id_t ctx,
-                     gss_name_t target_name,
+                     gss_const_name_t target_name,
                      gss_OID mech_type,
                      OM_uint32 req_flags,
                      OM_uint32 time_req,
@@ -1018,8 +1403,10 @@ gssEapInitSecContext(OM_uint32 *minor,
             goto cleanup;
         }
     }
+
     if (ret_flags != NULL)
         *ret_flags = ctx->gssFlags;
+
     if (time_rec != NULL)
         gssEapContextTime(&tmpMinor, ctx, time_rec);
 
@@ -1036,9 +1423,17 @@ cleanup:
 
 OM_uint32 GSSAPI_CALLCONV
 gss_init_sec_context(OM_uint32 *minor,
+#ifdef HAVE_HEIMDAL_VERSION
+                     gss_const_cred_id_t cred,
+#else
                      gss_cred_id_t cred,
+#endif
                      gss_ctx_id_t *context_handle,
+#ifdef HAVE_HEIMDAL_VERSION
+                     gss_const_name_t target_name,
+#else
                      gss_name_t target_name,
+#endif
                      gss_OID mech_type,
                      OM_uint32 req_flags,
                      OM_uint32 time_req,
@@ -1075,7 +1470,7 @@ gss_init_sec_context(OM_uint32 *minor,
     GSSEAP_MUTEX_LOCK(&ctx->mutex);
 
     major = gssEapInitSecContext(minor,
-                                 cred,
+                                 (gss_cred_id_t)cred,
                                  ctx,
                                  target_name,
                                  mech_type,
@@ -1093,5 +1488,8 @@ gss_init_sec_context(OM_uint32 *minor,
     if (GSS_ERROR(major))
         gssEapReleaseContext(&tmpMinor, context_handle);
 
+    gssEapTraceStatus("gss_init_sec_context", major, *minor);
+
     return major;
 }
+