Implemented callback in libeap/src/crypto to allow mech_eap / ID Selector to ask...
authorDan Breslau <dbreslau@painless-security.com>
Wed, 28 Sep 2016 22:39:33 +0000 (18:39 -0400)
committerDan Breslau <dbreslau@painless-security.com>
Wed, 28 Sep 2016 22:39:33 +0000 (18:39 -0400)
libeap/src/crypto/tls.h
libeap/src/crypto/tls_openssl.c
libeap/src/eap_peer/eap_config.h
libeap/src/eap_peer/eap_tls_common.c
libeap/src/utils/wpa_debug.c
mech_eap/init_sec_context.c

index 2e56233..7d80643 100644 (file)
@@ -9,6 +9,8 @@
 #ifndef TLS_H
 #define TLS_H
 
+#include <openssl/x509.h>
+
 struct tls_connection;
 
 struct tls_random {
@@ -96,6 +98,8 @@ struct tls_config {
 #define TLS_CONN_EAP_FAST BIT(7)
 #define TLS_CONN_DISABLE_TLSv1_0 BIT(8)
 
+struct X509; /* from OpenSSL */
+
 /**
  * struct tls_connection_params - Parameters for TLS connection
  * @ca_cert: File or reference name for CA X.509 certificate in PEM or DER
@@ -139,6 +143,9 @@ struct tls_config {
  * @flags: Parameter options (TLS_CONN_*)
  * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response
  *     or %NULL if OCSP is not enabled
+ * @validate_ca_cb: Optional callback to be used to validate server certificate
+ *  when no CA or path was specified. 
+ * @validate_ca_ctx: Optional context arg for validate_ca_cb.
  *
  * TLS connection parameters to be configured with tls_connection_set_params()
  * and tls_global_set_params().
@@ -179,6 +186,13 @@ struct tls_connection_params {
 
        unsigned int flags;
        const char *ocsp_stapling_response;
+
+    /**
+     * If non-null, specifies a callback method that can be used to
+     * confirm the validity of a peer certificate.
+     */
+    int (*validate_ca_cb)(int ok_so_far, X509* cert, void *ca_ctx);
+    void *validate_ca_ctx;
 };
 
 
index 0a8ae57..e82e949 100644 (file)
@@ -129,6 +129,9 @@ struct tls_connection {
        unsigned char client_random[SSL3_RANDOM_SIZE];
        unsigned char server_random[SSL3_RANDOM_SIZE];
 #endif
+
+    int (*validate_ca_cb)(int ok_so_far, X509* cert, void *ca_ctx);
+    void *validate_ca_ctx;
 };
 
 
@@ -145,7 +148,6 @@ static struct tls_context * tls_context_new(const struct tls_config *conf)
        return context;
 }
 
-
 #ifdef CONFIG_NO_STDOUT_DEBUG
 
 static void _tls_show_errors(void)
@@ -1591,20 +1593,36 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
        else if (depth == 2)
                conn->peer_issuer_issuer = err_cert;
 
+       wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb(enter) - preverify_ok=%d "
+                  "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
+                  preverify_ok, err, X509_verify_cert_error_string(err),
+                  conn->ca_cert_verify, depth, buf);
+
+
        context = conn->context;
        match = conn->subject_match;
        altmatch = conn->altsubject_match;
        suffix_match = conn->suffix_match;
        domain_match = conn->domain_match;
 
-       if (!preverify_ok && !conn->ca_cert_verify)
-               preverify_ok = 1;
-       if (!preverify_ok && depth > 0 && conn->server_cert_only)
+       if (!preverify_ok && !conn->ca_cert_verify) {
+        if (conn->validate_ca_cb) {
+            preverify_ok = conn->validate_ca_cb(preverify_ok, err_cert, conn->validate_ca_ctx);
+            wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb: validate_ca_cb returned %d", preverify_ok);
+        }
+        else {
+            preverify_ok = 1;
+            wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb: allowing cert because !conn->ca_cert_verify\n");
+        }
+    }
+       if (!preverify_ok && depth > 0 && conn->server_cert_only) {
+        wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb: allowing cert because depth > 0 && conn->server_cert_only\n");
                preverify_ok = 1;
+    }
        if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) &&
            (err == X509_V_ERR_CERT_HAS_EXPIRED ||
             err == X509_V_ERR_CERT_NOT_YET_VALID)) {
-               wpa_printf(MSG_DEBUG, "OpenSSL: Ignore certificate validity "
+               wpa_printf(MSG_DEBUG, "tls_verify_cb: OpenSSL: Ignore certificate validity "
                           "time mismatch");
                preverify_ok = 1;
        }
@@ -1620,7 +1638,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
                struct wpabuf *cert;
                cert = get_x509_cert(err_cert);
                if (!cert) {
-                       wpa_printf(MSG_DEBUG, "OpenSSL: Could not fetch "
+                       wpa_printf(MSG_DEBUG, "tls_verify_cb: OpenSSL: Could not fetch "
                                   "server certificate data");
                        preverify_ok = 0;
                } else {
@@ -1640,7 +1658,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
                                 * regardless of other problems.
                                 */
                                wpa_printf(MSG_DEBUG,
-                                          "OpenSSL: Ignore validation issues for a pinned server certificate");
+                                          "tls_verify_cb: OpenSSL: Ignore validation issues for a pinned server certificate");
                                preverify_ok = 1;
                        }
                        wpabuf_free(cert);
@@ -1649,7 +1667,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
 #endif /* CONFIG_SHA256 */
 
        if (!preverify_ok) {
-               wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
+               wpa_printf(MSG_WARNING, "tls_verify_cb: TLS: Certificate verification failed,"
                           " error %d (%s) depth %d for '%s'", err, err_str,
                           depth, buf);
                openssl_tls_fail_event(conn, err_cert, err, depth, buf,
@@ -1657,12 +1675,12 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
                return preverify_ok;
        }
 
-       wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - preverify_ok=%d "
+       wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb(exit) - preverify_ok=%d "
                   "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
                   preverify_ok, err, err_str,
                   conn->ca_cert_verify, depth, buf);
        if (depth == 0 && match && os_strstr(buf, match) == NULL) {
-               wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
+               wpa_printf(MSG_WARNING, "tls_verify_cb: TLS: Subject '%s' did not "
                           "match with '%s'", buf, match);
                preverify_ok = 0;
                openssl_tls_fail_event(conn, err_cert, err, depth, buf,
@@ -1670,7 +1688,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
                                       TLS_FAIL_SUBJECT_MISMATCH);
        } else if (depth == 0 && altmatch &&
                   !tls_match_altsubject(err_cert, altmatch)) {
-               wpa_printf(MSG_WARNING, "TLS: altSubjectName match "
+               wpa_printf(MSG_WARNING, "tls_verify_cb: TLS: altSubjectName match "
                           "'%s' not found", altmatch);
                preverify_ok = 0;
                openssl_tls_fail_event(conn, err_cert, err, depth, buf,
@@ -1678,7 +1696,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
                                       TLS_FAIL_ALTSUBJECT_MISMATCH);
        } else if (depth == 0 && suffix_match &&
                   !tls_match_suffix(err_cert, suffix_match, 0)) {
-               wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found",
+               wpa_printf(MSG_WARNING, "tls_verify_cb: TLS: Domain suffix match '%s' not found",
                           suffix_match);
                preverify_ok = 0;
                openssl_tls_fail_event(conn, err_cert, err, depth, buf,
@@ -1686,7 +1704,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
                                       TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
        } else if (depth == 0 && domain_match &&
                   !tls_match_suffix(err_cert, domain_match, 1)) {
-               wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found",
+               wpa_printf(MSG_WARNING, "tls_verify_cb: TLS: Domain match '%s' not found",
                           domain_match);
                preverify_ok = 0;
                openssl_tls_fail_event(conn, err_cert, err, depth, buf,
@@ -1696,7 +1714,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
                openssl_tls_cert_event(conn, err_cert, depth, buf);
 
        if (conn->cert_probe && preverify_ok && depth == 0) {
-               wpa_printf(MSG_DEBUG, "OpenSSL: Reject server certificate "
+               wpa_printf(MSG_DEBUG, "tls_verify_cb: OpenSSL: Reject server certificate "
                           "on probe-only run");
                preverify_ok = 0;
                openssl_tls_fail_event(conn, err_cert, err, depth, buf,
@@ -1746,9 +1764,11 @@ static int tls_load_ca_der(struct tls_data *data, const char *ca_cert)
 
 
 static int tls_connection_ca_cert(struct tls_data *data,
-                                 struct tls_connection *conn,
-                                 const char *ca_cert, const u8 *ca_cert_blob,
-                                 size_t ca_cert_blob_len, const char *ca_path)
+                                  struct tls_connection *conn,
+                                  const char *ca_cert, const u8 *ca_cert_blob,
+                                  size_t ca_cert_blob_len, const char *ca_path,
+                                  int (*validate_ca_cb)(int ok_so_far, X509* cert, void *ca_ctx),
+                                  void *validate_ca_ctx)
 {
        SSL_CTX *ssl_ctx = data->ssl;
        X509_STORE *store;
@@ -1770,7 +1790,7 @@ static int tls_connection_ca_cert(struct tls_data *data,
 
        if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) {
                wpa_printf(MSG_DEBUG, "OpenSSL: Probe for server certificate "
-                          "chain");
+                          "chain; setting conn->ca_cert_verify=0");
                conn->cert_probe = 1;
                conn->ca_cert_verify = 0;
                return 0;
@@ -1904,7 +1924,10 @@ static int tls_connection_ca_cert(struct tls_data *data,
        } else {
                /* No ca_cert configured - do not try to verify server
                 * certificate */
+               wpa_printf(MSG_DEBUG, "OpenSSL: tls_connection_ca_cert: No ca_cert; setting conn->ca_cert_verify=0");
                conn->ca_cert_verify = 0;
+        conn->validate_ca_cb = validate_ca_cb;
+        conn->validate_ca_ctx = validate_ca_ctx;
        }
 
        return 0;
@@ -2049,6 +2072,7 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
                               SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
                               SSL_VERIFY_CLIENT_ONCE, tls_verify_cb);
        } else {
+               wpa_printf(MSG_DEBUG, "OpenSSL: tls_connection_set_verify: !verify_peer; setting conn->ca_cert_verify=0");
                conn->ca_cert_verify = 0;
                SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
        }
@@ -3838,11 +3862,14 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
        if (engine_id && ca_cert_id) {
                if (tls_connection_engine_ca_cert(data, conn, ca_cert_id))
                        return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
-       } else if (tls_connection_ca_cert(data, conn, params->ca_cert,
-                                         params->ca_cert_blob,
-                                         params->ca_cert_blob_len,
-                                         params->ca_path))
-               return -1;
+       } else {
+        if (tls_connection_ca_cert(data, conn, params->ca_cert,
+                                   params->ca_cert_blob,
+                                   params->ca_cert_blob_len,
+                                   params->ca_path, params->validate_ca_cb, 
+                                   params->validate_ca_ctx))
+            return -1;
+    }
 
        if (engine_id && cert_id) {
                if (tls_connection_engine_client_cert(conn, cert_id))
index 0b0824b..bd446c7 100644 (file)
@@ -9,6 +9,9 @@
 #ifndef EAP_CONFIG_H
 #define EAP_CONFIG_H
 
+#include <openssl/x509.h>
+
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -777,6 +780,13 @@ struct eap_peer_config {
         * erp - Whether EAP Re-authentication Protocol (ERP) is enabled
         */
        int erp;
+
+    /**
+     * If non-null, specifies a callback method that can be used to
+     * override the validity of a peer certificate.
+     */
+    int (*validate_ca_cb)(int ok_so_far, X509* cert, void *ca_ctx);
+    void *validate_ca_ctx;
 };
 
 
index b1f9300..e7cbe62 100644 (file)
@@ -103,6 +103,8 @@ static void eap_tls_params_from_conf1(struct tls_connection_params *params,
        params->cert_id = config->cert_id;
        params->ca_cert_id = config->ca_cert_id;
        eap_tls_params_flags(params, config->phase1);
+    params->validate_ca_cb = config->validate_ca_cb;
+    params->validate_ca_ctx = config->validate_ca_ctx;
 }
 
 
index 61c0d5c..7c72ea4 100644 (file)
@@ -549,7 +549,7 @@ int wpa_debug_open_file(const char *path)
        out_file = fopen(path, "a");
        if (out_file == NULL) {
                wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open "
-                          "output file, using standard output");
+                   "output file %s, using standard output", path);
                return -1;
        }
 #ifndef _WIN32
index a9d8891..de308b6 100644 (file)
@@ -39,6 +39,8 @@
 #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[] = {
@@ -201,6 +203,18 @@ peerNotifyPending(void *ctx GSSEAP_UNUSED)
 {
 }
 
+static void peerNotifyCert(void *ctx GSSEAP_UNUSED,
+                          int depth ,
+                          const char *subject GSSEAP_UNUSED,
+                          const char *altsubject[] GSSEAP_UNUSED,
+                          int num_altsubject GSSEAP_UNUSED,
+                          const char *cert_hash GSSEAP_UNUSED,
+                          const struct wpabuf *cert  GSSEAP_UNUSED)
+{
+    printf("peerNotifyCert: depth=%d; hash=%s (%p)\n", depth, cert_hash, cert_hash);
+}
+
+
 static struct eapol_callbacks gssEapPolicyCallbacks = {
     peerGetConfig,
     peerGetBool,
@@ -211,6 +225,8 @@ static struct eapol_callbacks gssEapPolicyCallbacks = {
     peerSetConfigBlob,
     peerGetConfigBlob,
     peerNotifyPending,
+    NULL,  /* eap_param_needed */
+    peerNotifyCert
 };
 
 
@@ -356,6 +372,90 @@ 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 peerValidateCA(int ok_so_far, X509* cert, void *ca_ctx)
+{
+    const 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);
+    char* at = strchr(identity, '@');
+
+    if (at != NULL) {
+        *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) {
+        printf("peerValidateCA: Error: hash_len=%d, not 32!\n", hash_len);
+        return FALSE;
+    }
+
+    /* This is ugly, but it works -- anonymous_identity is '@' + realm
+     *  (see peerConfigInit)
+     */
+    realm = ((char *) eap_config->anonymous_identity) + 1;
+
+    ok_so_far = moonshot_confirm_ca_certificate(identity, realm, hash, 32, &error);
+
+    printf("peerValidateCA: Returning %d\n", ok_so_far);
+    return ok_so_far;
+}
+
+
 static OM_uint32
 peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
 {
@@ -464,6 +564,9 @@ peerConfigInit(OM_uint32 *minor, gss_ctx_id_t ctx)
         eapPeerConfig->private_key_passwd = (char *)cred->password.value;
     }
 
+    eapPeerConfig->validate_ca_cb = peerValidateCA;
+    eapPeerConfig->validate_ca_ctx = eapPeerConfig;
+
     *minor = 0;
     return GSS_S_COMPLETE;
 }
@@ -838,6 +941,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) {
@@ -854,11 +959,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;
@@ -1368,3 +1471,4 @@ gss_init_sec_context(OM_uint32 *minor,
     gssEapTraceStatus( "gss_init_sec_context", major, *minor);
     return major;
 }
+