Minor cleanups
[mech_eap.git] / mech_eap / init_sec_context.c
index 39929a6..0181a4f 100644 (file)
 #include "radius/radius.h"
 #include "util_radius.h"
 #include "utils/radius_utils.h"
+#include "openssl/err.h"
+#include "libmoonshot.h"
+
+/* 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)
@@ -73,6 +80,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;
@@ -180,6 +190,8 @@ peerGetConfigBlob(void *ctx,
         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;
 
@@ -191,6 +203,7 @@ peerNotifyPending(void *ctx GSSEAP_UNUSED)
 {
 }
 
+
 static struct eapol_callbacks gssEapPolicyCallbacks = {
     peerGetConfig,
     peerGetBool,
@@ -201,11 +214,10 @@ static struct eapol_callbacks gssEapPolicyCallbacks = {
     peerSetConfigBlob,
     peerGetConfigBlob,
     peerNotifyPending,
+    NULL,  /* eap_param_needed */
+    NULL   /* eap_notify_cert */
 };
 
-#ifdef GSSEAP_DEBUG
-extern int wpa_debug_level;
-#endif
 
 #define CHBIND_SERVICE_NAME_FLAG        0x01
 #define CHBIND_HOST_NAME_FLAG           0x02
@@ -274,6 +286,9 @@ peerInitEapChannelBinding(OM_uint32 *minor, gss_ctx_id_t ctx)
         major = gssEapRadiusAddAttr(minor, &buf,
                                     PW_GSS_ACCEPTOR_REALM_NAME,
                                     0, &nameBuf);
+        if (GSS_ERROR(major))
+            goto cleanup;
+
         chbindReqFlags |= CHBIND_REALM_NAME_FLAG;
     }
 
@@ -292,7 +307,7 @@ peerInitEapChannelBinding(OM_uint32 *minor, gss_ctx_id_t ctx)
     *minor = 0;
 
 cleanup:
-    krbFreeUnparsedName(krbContext, &nameBuf);
+    /*namebuf is freed when used and may be left with a unowned pointer*/
     wpabuf_free(buf);
 
     return major;
@@ -313,6 +328,8 @@ peerProcessChbindResponse(void *context, int code, int nsid,
     if (nsid != CHBIND_NSID_RADIUS)
         return;
 
+    if (data == NULL)
+        return;
     msg = radius_parser_start(data, len);
     if (msg == NULL)
         return;
@@ -344,6 +361,91 @@ peerProcessChbindResponse(void *context, int code, int nsid,
     } /* else log failures? */
 }
 
+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;
+}
+
+
 static OM_uint32
 peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
 {
@@ -361,16 +463,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) {
@@ -410,6 +510,8 @@ peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
     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) {
@@ -449,9 +551,12 @@ peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
             eapPeerConfig->client_cert = (unsigned char *)cred->clientCertificate.value;
             eapPeerConfig->private_key = (unsigned char *)cred->privateKey.value;
         }
-        eapPeerConfig->private_key_passwd = (unsigned char *)cred->password.value;
+        eapPeerConfig->private_key_passwd = (char *)cred->password.value;
     }
 
+    eapPeerConfig->server_cert_cb = peerValidateServerCert;
+    eapPeerConfig->server_cert_ctx = eapPeerConfig;
+
     *minor = 0;
     return GSS_S_COMPLETE;
 }
@@ -614,7 +719,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;
@@ -823,6 +931,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) {
@@ -839,11 +949,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;
@@ -962,6 +1070,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);
@@ -1099,11 +1212,6 @@ eapGssSmInitAcceptorMIC(OM_uint32 *minor,
     if (GSS_ERROR(major))
         return major;
 
-    /*
-     * As a temporary measure, force mutual authentication until channel binding is
-     * more widely deployed.
-     */
-    ctx->gssFlags |= GSS_C_MUTUAL_FLAG;
     GSSEAP_SM_TRANSITION(ctx, GSSEAP_STATE_ESTABLISHED);
 
     *minor = 0;
@@ -1350,5 +1458,7 @@ gss_init_sec_context(OM_uint32 *minor,
     if (GSS_ERROR(major))
         gssEapReleaseContext(&tmpMinor, context_handle);
 
+    gssEapTraceStatus( "gss_init_sec_context", major, *minor);
     return major;
 }
+