From: Dan Breslau Date: Wed, 28 Sep 2016 22:39:33 +0000 (-0400) Subject: Implemented callback in libeap/src/crypto to allow mech_eap / ID Selector to ask... X-Git-Tag: v0.9.6~2^2~5 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=mech_eap.git;a=commitdiff_plain;h=344d7f981a4d7d1ef7d8f8d7645aa9c1d153b6cf Implemented callback in libeap/src/crypto to allow mech_eap / ID Selector to ask user to confirm an unknown CA certificate --- diff --git a/libeap/src/crypto/tls.h b/libeap/src/crypto/tls.h index 2e56233..7d80643 100644 --- a/libeap/src/crypto/tls.h +++ b/libeap/src/crypto/tls.h @@ -9,6 +9,8 @@ #ifndef TLS_H #define TLS_H +#include + 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; }; diff --git a/libeap/src/crypto/tls_openssl.c b/libeap/src/crypto/tls_openssl.c index 0a8ae57..e82e949 100644 --- a/libeap/src/crypto/tls_openssl.c +++ b/libeap/src/crypto/tls_openssl.c @@ -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)) diff --git a/libeap/src/eap_peer/eap_config.h b/libeap/src/eap_peer/eap_config.h index 0b0824b..bd446c7 100644 --- a/libeap/src/eap_peer/eap_config.h +++ b/libeap/src/eap_peer/eap_config.h @@ -9,6 +9,9 @@ #ifndef EAP_CONFIG_H #define EAP_CONFIG_H +#include + + #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; }; diff --git a/libeap/src/eap_peer/eap_tls_common.c b/libeap/src/eap_peer/eap_tls_common.c index b1f9300..e7cbe62 100644 --- a/libeap/src/eap_peer/eap_tls_common.c +++ b/libeap/src/eap_peer/eap_tls_common.c @@ -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; } diff --git a/libeap/src/utils/wpa_debug.c b/libeap/src/utils/wpa_debug.c index 61c0d5c..7c72ea4 100644 --- a/libeap/src/utils/wpa_debug.c +++ b/libeap/src/utils/wpa_debug.c @@ -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 diff --git a/mech_eap/init_sec_context.c b/mech_eap/init_sec_context.c index a9d8891..de308b6 100644 --- a/mech_eap/init_sec_context.c +++ b/mech_eap/init_sec_context.c @@ -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; } +