From d4913c585ec9b62a667473878a7fd7d8600d3388 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 9 Dec 2014 15:57:03 +0200 Subject: [PATCH] OpenSSL: Fix EAP-FAST peer regression Commit 35efa2479ff19c3f13e69dc50d2708ce79a99beb ('OpenSSL: Allow TLS v1.1 and v1.2 to be negotiated by default') changed from using TLSv1_method() to SSLv23_method() to allow negotiation of TLS v1.0, v1.1, and v1.2. Unfortunately, it looks like EAP-FAST does not work with this due to OpenSSL not allowing ClientHello extensions to be configured with SSL_set_session_ticket_ext() when SSLv23_method() is used. Work around this regression by initiating a separate SSL_CTX instance for EAP-FAST phase 1 needs with TLSv1_method() while leaving all other EAP cases using TLS to work with the new default that allows v1.1 and v1.2 to be negotiated. This is not ideal and will hopefully get fixed in the future with a new OpenSSL method, but until that time, this can be used allow other methods use newer TLS versions while still allowing EAP-FAST to be used even if it remains to be constraint to TLS v1.0 only. Signed-off-by: Jouni Malinen --- src/crypto/tls.h | 1 + src/crypto/tls_openssl.c | 116 ++++++++++++++++++++++++++-------- src/eap_peer/eap_tls_common.c | 2 + 3 files changed, 93 insertions(+), 26 deletions(-) diff --git a/src/crypto/tls.h b/src/crypto/tls.h index 1d3f59287..345ebc7c2 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -88,6 +88,7 @@ struct tls_config { #define TLS_CONN_REQUIRE_OCSP BIT(4) #define TLS_CONN_DISABLE_TLSv1_1 BIT(5) #define TLS_CONN_DISABLE_TLSv1_2 BIT(6) +#define TLS_CONN_EAP_FAST BIT(7) /** * struct tls_connection_params - Parameters for TLS connection diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index ec1f560a4..984962c4d 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -118,6 +118,8 @@ struct tls_connection { X509 *peer_cert; X509 *peer_issuer; X509 *peer_issuer_issuer; + + SSL_CTX *ssl_ctx; /* separate context for EAP-FAST workaround */ }; @@ -1025,60 +1027,77 @@ static void tls_msg_cb(int write_p, int version, int content_type, } -struct tls_connection * tls_connection_init(void *ssl_ctx) +static int openssl_new_ssl(SSL_CTX *ssl_ctx, struct tls_connection *conn) { - SSL_CTX *ssl = ssl_ctx; - struct tls_connection *conn; long options; #ifdef OPENSSL_SUPPORTS_CTX_APP_DATA - struct tls_context *context = SSL_CTX_get_app_data(ssl); + struct tls_context *context = SSL_CTX_get_app_data(ssl_ctx); #else /* OPENSSL_SUPPORTS_CTX_APP_DATA */ struct tls_context *context = tls_global; #endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ + SSL *ssl; + BIO *ssl_in, *ssl_out; - conn = os_zalloc(sizeof(*conn)); - if (conn == NULL) - return NULL; - conn->ssl = SSL_new(ssl); - if (conn->ssl == NULL) { + ssl = SSL_new(ssl_ctx); + if (ssl == NULL) { tls_show_errors(MSG_INFO, __func__, "Failed to initialize new SSL connection"); - os_free(conn); - return NULL; + return -1; } - conn->context = context; - SSL_set_app_data(conn->ssl, conn); - SSL_set_msg_callback(conn->ssl, tls_msg_cb); - SSL_set_msg_callback_arg(conn->ssl, conn); + SSL_set_app_data(ssl, conn); + SSL_set_msg_callback(ssl, tls_msg_cb); + SSL_set_msg_callback_arg(ssl, conn); options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE; #ifdef SSL_OP_NO_COMPRESSION options |= SSL_OP_NO_COMPRESSION; #endif /* SSL_OP_NO_COMPRESSION */ - SSL_set_options(conn->ssl, options); + SSL_set_options(ssl, options); - conn->ssl_in = BIO_new(BIO_s_mem()); - if (!conn->ssl_in) { + ssl_in = BIO_new(BIO_s_mem()); + if (!ssl_in) { tls_show_errors(MSG_INFO, __func__, "Failed to create a new BIO for ssl_in"); - SSL_free(conn->ssl); - os_free(conn); - return NULL; + SSL_free(ssl); + return -1; } - conn->ssl_out = BIO_new(BIO_s_mem()); - if (!conn->ssl_out) { + ssl_out = BIO_new(BIO_s_mem()); + if (!ssl_out) { tls_show_errors(MSG_INFO, __func__, "Failed to create a new BIO for ssl_out"); + SSL_free(ssl); + BIO_free(ssl_in); + return -1; + } + + SSL_set_bio(ssl, ssl_in, ssl_out); + + if (conn->ssl) SSL_free(conn->ssl); - BIO_free(conn->ssl_in); + conn->ssl = ssl; + conn->ssl_in = ssl_in; + conn->ssl_out = ssl_out; + conn->context = context; + + return 0; +} + + +struct tls_connection * tls_connection_init(void *ssl_ctx) +{ + SSL_CTX *ssl = ssl_ctx; + struct tls_connection *conn; + + conn = os_zalloc(sizeof(*conn)); + if (conn == NULL) + return NULL; + if (openssl_new_ssl(ssl, conn) < 0) { os_free(conn); return NULL; } - SSL_set_bio(conn->ssl, conn->ssl_in, conn->ssl_out); - return conn; } @@ -1093,6 +1112,8 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) os_free(conn->altsubject_match); os_free(conn->suffix_match); os_free(conn->session_ticket); + if (conn->ssl_ctx) + SSL_CTX_free(conn->ssl_ctx); os_free(conn); } @@ -3198,6 +3219,44 @@ static int ocsp_status_cb(SSL *s, void *arg) #endif /* HAVE_OCSP */ +static int openssl_eap_fast_workaround( + struct tls_connection *conn, + const struct tls_connection_params *params) +{ +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) + if (!(params->flags & TLS_CONN_EAP_FAST)) + return 0; + if (conn->ssl_ctx) + return 0; /* already initialized */ + + /* + * The default SSL_CTX with SSLv23_method() does not allow session + * ticket from EAP-FAST to be added into ClientHello, so we have to + * create a separate SSL_CTX instance for EAP-FAST uses. + */ + wpa_printf(MSG_DEBUG, "OpenSSL: Create new SSL_CTX for EAP-FAST"); + + conn->ssl_ctx = SSL_CTX_new(TLSv1_method()); + if (conn->ssl_ctx == NULL) + return -1; + + SSL_CTX_set_info_callback(conn->ssl_ctx, ssl_info_cb); +#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA + SSL_CTX_set_app_data(conn->ssl_ctx, tls_global); +#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ + + if (openssl_new_ssl(conn->ssl_ctx, conn) < 0) { + SSL_CTX_free(conn->ssl_ctx); + conn->ssl_ctx = NULL; + return -1; + } +#endif /* EAP_FAST || EAP_FAST_DYNAMIC */ + + return 0; +} + + + int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params) { @@ -3207,6 +3266,11 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, if (conn == NULL) return -1; + if (openssl_eap_fast_workaround(conn, params) < 0) + return -1; + if (conn->ssl_ctx) + tls_ctx = conn->ssl_ctx; + while ((err = ERR_get_error())) { wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s", __func__, ERR_error_string(err, NULL)); diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index ab97966f2..934ab34b3 100644 --- a/src/eap_peer/eap_tls_common.c +++ b/src/eap_peer/eap_tls_common.c @@ -147,6 +147,8 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, } else { wpa_printf(MSG_DEBUG, "TLS: using phase1 config options"); eap_tls_params_from_conf1(params, config); + if (data->eap_type == EAP_TYPE_FAST) + params->flags |= TLS_CONN_EAP_FAST; } /*