X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=libeap%2Fsrc%2Fcrypto%2Ftls_openssl.c;h=18b18db377bde55bc2db0f5d31d299c28a4bd642;hb=fc78c30acf20d98c3b37dd953aae34d353406925;hp=c0a40f95617c620458e95e3ae4b1d70ce5516877;hpb=32c7cb5841cb87eb434b1a802f0032b2b71c7d17;p=mech_eap.git diff --git a/libeap/src/crypto/tls_openssl.c b/libeap/src/crypto/tls_openssl.c index c0a40f9..18b18db 100644 --- a/libeap/src/crypto/tls_openssl.c +++ b/libeap/src/crypto/tls_openssl.c @@ -1,72 +1,213 @@ /* * SSL/TLS interface functions for OpenSSL - * Copyright (c) 2004-2010, Jouni Malinen + * Copyright (c) 2004-2015, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" #ifndef CONFIG_SMARTCARD #ifndef OPENSSL_NO_ENGINE +#ifndef ANDROID #define OPENSSL_NO_ENGINE #endif #endif +#endif #include #include +#include #include #include #ifndef OPENSSL_NO_ENGINE #include #endif /* OPENSSL_NO_ENGINE */ +#ifndef OPENSSL_NO_DSA +#include +#endif +#ifndef OPENSSL_NO_DH +#include +#endif #include "common.h" #include "crypto.h" +#include "sha1.h" +#include "sha256.h" #include "tls.h" +#include "tls_openssl.h" + +#if !defined(CONFIG_FIPS) && \ + (defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || \ + defined(EAP_SERVER_FAST)) +#define OPENSSL_NEED_EAP_FAST_PRF +#endif -#if OPENSSL_VERSION_NUMBER >= 0x0090800fL -#define OPENSSL_d2i_TYPE const unsigned char ** +#if defined(OPENSSL_IS_BORINGSSL) +/* stack_index_t is the return type of OpenSSL's sk_XXX_num() functions. */ +typedef size_t stack_index_t; #else -#define OPENSSL_d2i_TYPE unsigned char ** +typedef int stack_index_t; #endif -#ifdef SSL_F_SSL_SET_SESSION_TICKET_EXT -#ifdef SSL_OP_NO_TICKET +#ifdef SSL_set_tlsext_status_type +#ifndef OPENSSL_NO_TLSEXT +#define HAVE_OCSP +#include +#endif /* OPENSSL_NO_TLSEXT */ +#endif /* SSL_set_tlsext_status_type */ + +#if (OPENSSL_VERSION_NUMBER < 0x10100000L || \ + defined(LIBRESSL_VERSION_NUMBER)) && \ + !defined(BORINGSSL_API_VERSION) /* - * Session ticket override patch was merged into OpenSSL 0.9.9 tree on - * 2008-11-15. This version uses a bit different API compared to the old patch. + * SSL_get_client_random() and SSL_get_server_random() were added in OpenSSL + * 1.1.0 and newer BoringSSL revisions. Provide compatibility wrappers for + * older versions. */ -#define CONFIG_OPENSSL_TICKET_OVERRIDE -#endif + +static size_t SSL_get_client_random(const SSL *ssl, unsigned char *out, + size_t outlen) +{ + if (!ssl->s3 || outlen < SSL3_RANDOM_SIZE) + return 0; + os_memcpy(out, ssl->s3->client_random, SSL3_RANDOM_SIZE); + return SSL3_RANDOM_SIZE; +} + + +static size_t SSL_get_server_random(const SSL *ssl, unsigned char *out, + size_t outlen) +{ + if (!ssl->s3 || outlen < SSL3_RANDOM_SIZE) + return 0; + os_memcpy(out, ssl->s3->server_random, SSL3_RANDOM_SIZE); + return SSL3_RANDOM_SIZE; +} + + +#ifdef OPENSSL_NEED_EAP_FAST_PRF +static size_t SSL_SESSION_get_master_key(const SSL_SESSION *session, + unsigned char *out, size_t outlen) +{ + if (!session || session->master_key_length < 0 || + (size_t) session->master_key_length > outlen) + return 0; + if ((size_t) session->master_key_length < outlen) + outlen = session->master_key_length; + os_memcpy(out, session->master_key, outlen); + return outlen; +} +#endif /* OPENSSL_NEED_EAP_FAST_PRF */ + #endif +#ifdef ANDROID +#include +#include + +static BIO * BIO_from_keystore(const char *key) +{ + BIO *bio = NULL; + uint8_t *value = NULL; + int length = keystore_get(key, strlen(key), &value); + if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL) + BIO_write(bio, value, length); + free(value); + return bio; +} + + +static int tls_add_ca_from_keystore(X509_STORE *ctx, const char *key_alias) +{ + BIO *bio = BIO_from_keystore(key_alias); + STACK_OF(X509_INFO) *stack = NULL; + stack_index_t i; + + if (bio) { + stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL); + BIO_free(bio); + } + + if (!stack) { + wpa_printf(MSG_WARNING, "TLS: Failed to parse certificate: %s", + key_alias); + return -1; + } + + for (i = 0; i < sk_X509_INFO_num(stack); ++i) { + X509_INFO *info = sk_X509_INFO_value(stack, i); + + if (info->x509) + X509_STORE_add_cert(ctx, info->x509); + if (info->crl) + X509_STORE_add_crl(ctx, info->crl); + } + + sk_X509_INFO_pop_free(stack, X509_INFO_free); + + return 0; +} + + +static int tls_add_ca_from_keystore_encoded(X509_STORE *ctx, + const char *encoded_key_alias) +{ + int rc = -1; + int len = os_strlen(encoded_key_alias); + unsigned char *decoded_alias; + + if (len & 1) { + wpa_printf(MSG_WARNING, "Invalid hex-encoded alias: %s", + encoded_key_alias); + return rc; + } + + decoded_alias = os_malloc(len / 2 + 1); + if (decoded_alias) { + if (!hexstr2bin(encoded_key_alias, decoded_alias, len / 2)) { + decoded_alias[len / 2] = '\0'; + rc = tls_add_ca_from_keystore( + ctx, (const char *) decoded_alias); + } + os_free(decoded_alias); + } + + return rc; +} + +#endif /* ANDROID */ + static int tls_openssl_ref_count = 0; +static int tls_ex_idx_session = -1; -struct tls_global { +struct tls_context { void (*event_cb)(void *ctx, enum tls_event ev, union tls_event_data *data); void *cb_ctx; + int cert_in_cb; + char *ocsp_stapling_response; }; -static struct tls_global *tls_global = NULL; +static struct tls_context *tls_global = NULL; +struct tls_data { + SSL_CTX *ssl; + unsigned int tls_session_lifetime; +}; + struct tls_connection { + struct tls_context *context; + SSL_CTX *ssl_ctx; SSL *ssl; BIO *ssl_in, *ssl_out; -#ifndef OPENSSL_NO_ENGINE +#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE) ENGINE *engine; /* functional reference to the engine */ EVP_PKEY *private_key; /* the private key if using engine */ #endif /* OPENSSL_NO_ENGINE */ - char *subject_match, *altsubject_match; + char *subject_match, *altsubject_match, *suffix_match, *domain_match; int read_alerts, write_alerts, failed; tls_session_ticket_cb session_ticket_cb; @@ -79,11 +220,39 @@ struct tls_connection { unsigned int ca_cert_verify:1; unsigned int cert_probe:1; unsigned int server_cert_only:1; + unsigned int invalid_hb_used:1; + unsigned int success_data:1; u8 srv_cert_hash[32]; + + unsigned int flags; + + X509 *peer_cert; + X509 *peer_issuer; + X509 *peer_issuer_issuer; + + unsigned char client_random[SSL3_RANDOM_SIZE]; + unsigned char server_random[SSL3_RANDOM_SIZE]; + + int (*server_cert_cb)(int ok_so_far, X509* cert, void *ca_ctx); + void *server_cert_ctx; }; +static struct tls_context * tls_context_new(const struct tls_config *conf) +{ + struct tls_context *context = os_zalloc(sizeof(*context)); + if (context == NULL) + return NULL; + if (conf) { + context->event_cb = conf->event_cb; + context->cb_ctx = conf->cb_ctx; + context->cert_in_cb = conf->cert_in_cb; + } + return context; +} + + #ifdef CONFIG_NO_STDOUT_DEBUG static void _tls_show_errors(void) @@ -349,7 +518,8 @@ static int tls_cryptoapi_cert(SSL *ssl, const char *name) goto err; } - cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &priv->cert->pbCertEncoded, + cert = d2i_X509(NULL, + (const unsigned char **) &priv->cert->pbCertEncoded, priv->cert->cbCertEncoded); if (cert == NULL) { wpa_printf(MSG_INFO, "CryptoAPI: Could not process X509 DER " @@ -449,7 +619,8 @@ static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name) } while ((ctx = CertEnumCertificatesInStore(cs, ctx))) { - cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ctx->pbCertEncoded, + cert = d2i_X509(NULL, + (const unsigned char **) &ctx->pbCertEncoded, ctx->cbCertEncoded); if (cert == NULL) { wpa_printf(MSG_INFO, "CryptoAPI: Could not process " @@ -462,7 +633,8 @@ static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name) wpa_printf(MSG_DEBUG, "OpenSSL: Loaded CA certificate for " "system certificate store: subject='%s'", buf); - if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx), + cert)) { tls_show_errors(MSG_WARNING, __func__, "Failed to add ca_cert to OpenSSL " "certificate store"); @@ -509,6 +681,7 @@ static void ssl_info_cb(const SSL *ssl, int where, int ret) wpa_printf(MSG_DEBUG, "SSL: %s:%s", str, SSL_state_string_long(ssl)); } else if (where & SSL_CB_ALERT) { + struct tls_connection *conn = SSL_get_app_data((SSL *) ssl); wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s", where & SSL_CB_READ ? "read (remote end reported an error)" : @@ -516,13 +689,20 @@ static void ssl_info_cb(const SSL *ssl, int where, int ret) SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret)); if ((ret >> 8) == SSL3_AL_FATAL) { - struct tls_connection *conn = - SSL_get_app_data((SSL *) ssl); if (where & SSL_CB_READ) conn->read_alerts++; else conn->write_alerts++; } + if (conn->context->event_cb != NULL) { + union tls_event_data ev; + struct tls_context *context = conn->context; + os_memset(&ev, 0, sizeof(ev)); + ev.alert.is_local = !(where & SSL_CB_READ); + ev.alert.type = SSL_alert_type_string_long(ret); + ev.alert.description = SSL_alert_desc_string_long(ret); + context->event_cb(context->cb_ctx, TLS_ALERT, &ev); + } } else if (where & SSL_CB_EXIT && ret <= 0) { wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s", str, ret == 0 ? "failed" : "error", @@ -552,10 +732,16 @@ static int tls_engine_load_dynamic_generic(const char *pre[], engine = ENGINE_by_id(id); if (engine) { - ENGINE_free(engine); wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already " "available", id); - return 0; + /* + * If it was auto-loaded by ENGINE_by_id() we might still + * need to tell it which PKCS#11 module to use in legacy + * (non-p11-kit) environments. Do so now; even if it was + * properly initialised before, setting it again will be + * harmless. + */ + goto found; } ERR_clear_error(); @@ -592,7 +778,7 @@ static int tls_engine_load_dynamic_generic(const char *pre[], id, ERR_error_string(ERR_get_error(), NULL)); return -1; } - + found: while (post && post[0]) { wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", post[0], post[1]); if (ENGINE_ctrl_cmd_string(engine, post[0], post[1], 0) == 0) { @@ -633,12 +819,15 @@ static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path, NULL, NULL }; - if (!pkcs11_so_path || !pkcs11_module_path) + if (!pkcs11_so_path) return 0; pre_cmd[1] = pkcs11_so_path; pre_cmd[3] = engine_id; - post_cmd[1] = pkcs11_module_path; + if (pkcs11_module_path) + post_cmd[1] = pkcs11_module_path; + else + post_cmd[0] = NULL; wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s", pkcs11_so_path); @@ -676,39 +865,64 @@ static int tls_engine_load_dynamic_opensc(const char *opensc_so_path) #endif /* OPENSSL_NO_ENGINE */ +static void remove_session_cb(SSL_CTX *ctx, SSL_SESSION *sess) +{ + struct wpabuf *buf; + + if (tls_ex_idx_session < 0) + return; + buf = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session); + if (!buf) + return; + wpa_printf(MSG_DEBUG, + "OpenSSL: Free application session data %p (sess %p)", + buf, sess); + wpabuf_free(buf); + + SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL); +} + + void * tls_init(const struct tls_config *conf) { + struct tls_data *data; SSL_CTX *ssl; + struct tls_context *context; + const char *ciphers; if (tls_openssl_ref_count == 0) { - tls_global = os_zalloc(sizeof(*tls_global)); - if (tls_global == NULL) + tls_global = context = tls_context_new(conf); + if (context == NULL) return NULL; - if (conf) { - tls_global->event_cb = conf->event_cb; - tls_global->cb_ctx = conf->cb_ctx; - } - #ifdef CONFIG_FIPS #ifdef OPENSSL_FIPS if (conf && conf->fips_mode) { - if (!FIPS_mode_set(1)) { + static int fips_enabled = 0; + + if (!fips_enabled && !FIPS_mode_set(1)) { wpa_printf(MSG_ERROR, "Failed to enable FIPS " "mode"); ERR_load_crypto_strings(); ERR_print_errors_fp(stderr); + os_free(tls_global); + tls_global = NULL; return NULL; - } else + } else { wpa_printf(MSG_INFO, "Running in FIPS mode"); + fips_enabled = 1; + } } #else /* OPENSSL_FIPS */ if (conf && conf->fips_mode) { wpa_printf(MSG_ERROR, "FIPS mode requested, but not " "supported"); + os_free(tls_global); + tls_global = NULL; return NULL; } #endif /* OPENSSL_FIPS */ #endif /* CONFIG_FIPS */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L SSL_load_error_strings(); SSL_library_init(); #ifndef OPENSSL_NO_SHA256 @@ -730,76 +944,188 @@ void * tls_init(const struct tls_config *conf) #endif /* OPENSSL_NO_RC2 */ PKCS12_PBE_add(); #endif /* PKCS12_FUNCS */ +#endif /* < 1.1.0 */ + } else { + context = tls_context_new(conf); + if (context == NULL) + return NULL; } tls_openssl_ref_count++; - ssl = SSL_CTX_new(TLSv1_method()); - if (ssl == NULL) + data = os_zalloc(sizeof(*data)); + if (data) + ssl = SSL_CTX_new(SSLv23_method()); + else + ssl = NULL; + if (ssl == NULL) { + tls_openssl_ref_count--; + if (context != tls_global) + os_free(context); + if (tls_openssl_ref_count == 0) { + os_free(tls_global); + tls_global = NULL; + } + os_free(data); return NULL; + } + data->ssl = ssl; + if (conf) + data->tls_session_lifetime = conf->tls_session_lifetime; + + SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2); + SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3); SSL_CTX_set_info_callback(ssl, ssl_info_cb); + SSL_CTX_set_app_data(ssl, context); + if (data->tls_session_lifetime > 0) { + SSL_CTX_set_quiet_shutdown(ssl, 1); + /* + * Set default context here. In practice, this will be replaced + * by the per-EAP method context in tls_connection_set_verify(). + */ + SSL_CTX_set_session_id_context(ssl, (u8 *) "hostapd", 7); + SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_SERVER); + SSL_CTX_set_timeout(ssl, data->tls_session_lifetime); + SSL_CTX_sess_set_remove_cb(ssl, remove_session_cb); + } else { + SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_OFF); + } + + if (tls_ex_idx_session < 0) { + tls_ex_idx_session = SSL_SESSION_get_ex_new_index( + 0, NULL, NULL, NULL, NULL); + if (tls_ex_idx_session < 0) { + tls_deinit(data); + return NULL; + } + } #ifndef OPENSSL_NO_ENGINE + wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine"); + ERR_load_ENGINE_strings(); + ENGINE_load_dynamic(); + if (conf && (conf->opensc_engine_path || conf->pkcs11_engine_path || conf->pkcs11_module_path)) { - wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine"); - ERR_load_ENGINE_strings(); - ENGINE_load_dynamic(); - if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) || tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path, conf->pkcs11_module_path)) { - tls_deinit(ssl); + tls_deinit(data); return NULL; } } #endif /* OPENSSL_NO_ENGINE */ - return ssl; + if (conf && conf->openssl_ciphers) + ciphers = conf->openssl_ciphers; + else + ciphers = "DEFAULT:!EXP:!LOW"; + if (SSL_CTX_set_cipher_list(ssl, ciphers) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: Failed to set cipher string '%s'", + ciphers); + tls_deinit(data); + return NULL; + } + + return data; } void tls_deinit(void *ssl_ctx) { - SSL_CTX *ssl = ssl_ctx; + struct tls_data *data = ssl_ctx; + SSL_CTX *ssl = data->ssl; + struct tls_context *context = SSL_CTX_get_app_data(ssl); + if (context != tls_global) + os_free(context); + if (data->tls_session_lifetime > 0) + SSL_CTX_flush_sessions(ssl, 0); SSL_CTX_free(ssl); tls_openssl_ref_count--; if (tls_openssl_ref_count == 0) { -#ifndef OPENSSL_NO_ENGINE - ENGINE_cleanup(); -#endif /* OPENSSL_NO_ENGINE */ - CRYPTO_cleanup_all_ex_data(); - ERR_remove_state(0); - ERR_free_strings(); - EVP_cleanup(); +#if OPENSSL_VERSION_NUMBER < 0x10100000L +// The next four lines, and two more just below, deal with de-initializing +// global state in the OpenSSL engine. We (Moonshot) don't want that, since +// we use OpenSSL elsewhere in our apps (i.e., not only via hostap / libeap.) +//// #ifndef OPENSSL_NO_ENGINE +//// ENGINE_cleanup(); +//// #endif /* OPENSSL_NO_ENGINE */ +//// CRYPTO_cleanup_all_ex_data(); + ERR_remove_thread_state(NULL); +//// ERR_free_strings(); +//// EVP_cleanup(); +#endif /* < 1.1.0 */ + os_free(tls_global->ocsp_stapling_response); + tls_global->ocsp_stapling_response = NULL; os_free(tls_global); tls_global = NULL; } + + os_free(data); +} + + +#ifndef OPENSSL_NO_ENGINE + +/* Cryptoki return values */ +#define CKR_PIN_INCORRECT 0x000000a0 +#define CKR_PIN_INVALID 0x000000a1 +#define CKR_PIN_LEN_RANGE 0x000000a2 + +/* libp11 */ +#define ERR_LIB_PKCS11 ERR_LIB_USER + +static int tls_is_pin_error(unsigned int err) +{ + return ERR_GET_LIB(err) == ERR_LIB_PKCS11 && + (ERR_GET_REASON(err) == CKR_PIN_INCORRECT || + ERR_GET_REASON(err) == CKR_PIN_INVALID || + ERR_GET_REASON(err) == CKR_PIN_LEN_RANGE); } +#endif /* OPENSSL_NO_ENGINE */ + + +#ifdef ANDROID +/* EVP_PKEY_from_keystore comes from system/security/keystore-engine. */ +EVP_PKEY * EVP_PKEY_from_keystore(const char *key_id); +#endif /* ANDROID */ static int tls_engine_init(struct tls_connection *conn, const char *engine_id, const char *pin, const char *key_id, const char *cert_id, const char *ca_cert_id) { +#if defined(ANDROID) && defined(OPENSSL_IS_BORINGSSL) +#if !defined(OPENSSL_NO_ENGINE) +#error "This code depends on OPENSSL_NO_ENGINE being defined by BoringSSL." +#endif + if (!key_id) + return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + conn->engine = NULL; + conn->private_key = EVP_PKEY_from_keystore(key_id); + if (!conn->private_key) { + wpa_printf(MSG_ERROR, + "ENGINE: cannot load private key with id '%s' [%s]", + key_id, + ERR_error_string(ERR_get_error(), NULL)); + return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + } +#endif /* ANDROID && OPENSSL_IS_BORINGSSL */ + #ifndef OPENSSL_NO_ENGINE int ret = -1; if (engine_id == NULL) { wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set"); return -1; } - if (pin == NULL) { - wpa_printf(MSG_ERROR, "ENGINE: Smartcard PIN not set"); - return -1; - } - if (key_id == NULL) { - wpa_printf(MSG_ERROR, "ENGINE: Key Id not set"); - return -1; - } ERR_clear_error(); +#ifdef ANDROID + ENGINE_load_dynamic(); +#endif conn->engine = ENGINE_by_id(engine_id); if (!conn->engine) { wpa_printf(MSG_ERROR, "ENGINE: engine %s not available [%s]", @@ -814,20 +1140,40 @@ static int tls_engine_init(struct tls_connection *conn, const char *engine_id, } wpa_printf(MSG_DEBUG, "ENGINE: engine initialized"); - if (ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) { +#ifndef ANDROID + if (pin && ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) { wpa_printf(MSG_ERROR, "ENGINE: cannot set pin [%s]", ERR_error_string(ERR_get_error(), NULL)); goto err; } - /* load private key first in-case PIN is required for cert */ - conn->private_key = ENGINE_load_private_key(conn->engine, - key_id, NULL, NULL); - if (!conn->private_key) { - wpa_printf(MSG_ERROR, "ENGINE: cannot load private key with id" - " '%s' [%s]", key_id, - ERR_error_string(ERR_get_error(), NULL)); - ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; - goto err; +#endif + if (key_id) { + /* + * Ensure that the ENGINE does not attempt to use the OpenSSL + * UI system to obtain a PIN, if we didn't provide one. + */ + struct { + const void *password; + const char *prompt_info; + } key_cb = { "", NULL }; + + /* load private key first in-case PIN is required for cert */ + conn->private_key = ENGINE_load_private_key(conn->engine, + key_id, NULL, + &key_cb); + if (!conn->private_key) { + unsigned long err = ERR_get_error(); + + wpa_printf(MSG_ERROR, + "ENGINE: cannot load private key with id '%s' [%s]", + key_id, + ERR_error_string(err, NULL)); + if (tls_is_pin_error(err)) + ret = TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN; + else + ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + goto err; + } } /* handle a certificate and/or CA certificate */ @@ -866,17 +1212,19 @@ err: static void tls_engine_deinit(struct tls_connection *conn) { -#ifndef OPENSSL_NO_ENGINE +#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE) wpa_printf(MSG_DEBUG, "ENGINE: engine deinit"); if (conn->private_key) { EVP_PKEY_free(conn->private_key); conn->private_key = NULL; } if (conn->engine) { +#if !defined(OPENSSL_IS_BORINGSSL) ENGINE_finish(conn->engine); +#endif /* !OPENSSL_IS_BORINGSSL */ conn->engine = NULL; } -#endif /* OPENSSL_NO_ENGINE */ +#endif /* ANDROID || !OPENSSL_NO_ENGINE */ } @@ -894,15 +1242,107 @@ int tls_get_errors(void *ssl_ctx) return count; } + +static const char * openssl_content_type(int content_type) +{ + switch (content_type) { + case 20: + return "change cipher spec"; + case 21: + return "alert"; + case 22: + return "handshake"; + case 23: + return "application data"; + case 24: + return "heartbeat"; + case 256: + return "TLS header info"; /* pseudo content type */ + default: + return "?"; + } +} + + +static const char * openssl_handshake_type(int content_type, const u8 *buf, + size_t len) +{ + if (content_type != 22 || !buf || len == 0) + return ""; + switch (buf[0]) { + case 0: + return "hello request"; + case 1: + return "client hello"; + case 2: + return "server hello"; + case 4: + return "new session ticket"; + case 11: + return "certificate"; + case 12: + return "server key exchange"; + case 13: + return "certificate request"; + case 14: + return "server hello done"; + case 15: + return "certificate verify"; + case 16: + return "client key exchange"; + case 20: + return "finished"; + case 21: + return "certificate url"; + case 22: + return "certificate status"; + default: + return "?"; + } +} + + +static void tls_msg_cb(int write_p, int version, int content_type, + const void *buf, size_t len, SSL *ssl, void *arg) +{ + struct tls_connection *conn = arg; + const u8 *pos = buf; + + if (write_p == 2) { + wpa_printf(MSG_DEBUG, + "OpenSSL: session ver=0x%x content_type=%d", + version, content_type); + wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Data", buf, len); + return; + } + + wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d (%s/%s)", + write_p ? "TX" : "RX", version, content_type, + openssl_content_type(content_type), + openssl_handshake_type(content_type, buf, len)); + wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Message", buf, len); + if (content_type == 24 && len >= 3 && pos[0] == 1) { + size_t payload_len = WPA_GET_BE16(pos + 1); + if (payload_len + 3 > len) { + wpa_printf(MSG_ERROR, "OpenSSL: Heartbeat attack detected"); + conn->invalid_hb_used = 1; + } + } +} + + struct tls_connection * tls_connection_init(void *ssl_ctx) { - SSL_CTX *ssl = ssl_ctx; + struct tls_data *data = ssl_ctx; + SSL_CTX *ssl = data->ssl; struct tls_connection *conn; long options; + struct tls_context *context = SSL_CTX_get_app_data(ssl); conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; + conn->ssl_ctx = ssl; conn->ssl = SSL_new(ssl); if (conn->ssl == NULL) { tls_show_errors(MSG_INFO, __func__, @@ -911,7 +1351,10 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) return NULL; } + 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); options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE; #ifdef SSL_OP_NO_COMPRESSION @@ -948,10 +1391,20 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return; + if (conn->success_data) { + /* + * Make sure ssl_clear_bad_session() does not remove this + * session. + */ + SSL_set_quiet_shutdown(conn->ssl, 1); + SSL_shutdown(conn->ssl); + } SSL_free(conn->ssl); tls_engine_deinit(conn); os_free(conn->subject_match); os_free(conn->altsubject_match); + os_free(conn->suffix_match); + os_free(conn->domain_match); os_free(conn->session_ticket); os_free(conn); } @@ -973,7 +1426,7 @@ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) * and "close notify" shutdown alert would confuse AS. */ SSL_set_quiet_shutdown(conn->ssl, 1); SSL_shutdown(conn->ssl); - return 0; + return SSL_clear(conn->ssl) == 1 ? 0 : -1; } @@ -982,7 +1435,8 @@ static int tls_match_altsubject_component(X509 *cert, int type, { GENERAL_NAME *gen; void *ext; - int i, found = 0; + int found = 0; + stack_index_t i; ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); @@ -995,6 +1449,8 @@ static int tls_match_altsubject_component(X509 *cert, int type, found++; } + sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); + return found; } @@ -1042,6 +1498,114 @@ static int tls_match_altsubject(X509 *cert, const char *match) } +#ifndef CONFIG_NATIVE_WINDOWS +static int domain_suffix_match(const u8 *val, size_t len, const char *match, + int full) +{ + size_t i, match_len; + + /* Check for embedded nuls that could mess up suffix matching */ + for (i = 0; i < len; i++) { + if (val[i] == '\0') { + wpa_printf(MSG_DEBUG, "TLS: Embedded null in a string - reject"); + return 0; + } + } + + match_len = os_strlen(match); + if (match_len > len || (full && match_len != len)) + return 0; + + if (os_strncasecmp((const char *) val + len - match_len, match, + match_len) != 0) + return 0; /* no match */ + + if (match_len == len) + return 1; /* exact match */ + + if (val[len - match_len - 1] == '.') + return 1; /* full label match completes suffix match */ + + wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match"); + return 0; +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +static int tls_match_suffix(X509 *cert, const char *match, int full) +{ +#ifdef CONFIG_NATIVE_WINDOWS + /* wincrypt.h has conflicting X509_NAME definition */ + return -1; +#else /* CONFIG_NATIVE_WINDOWS */ + GENERAL_NAME *gen; + void *ext; + int i; + stack_index_t j; + int dns_name = 0; + X509_NAME *name; + + wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s", + full ? "": "suffix ", match); + + ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + + for (j = 0; ext && j < sk_GENERAL_NAME_num(ext); j++) { + gen = sk_GENERAL_NAME_value(ext, j); + if (gen->type != GEN_DNS) + continue; + dns_name++; + wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName", + gen->d.dNSName->data, + gen->d.dNSName->length); + if (domain_suffix_match(gen->d.dNSName->data, + gen->d.dNSName->length, match, full) == + 1) { + wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found", + full ? "Match" : "Suffix match"); + sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); + return 1; + } + } + sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); + + if (dns_name) { + wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched"); + return 0; + } + + name = X509_get_subject_name(cert); + i = -1; + for (;;) { + X509_NAME_ENTRY *e; + ASN1_STRING *cn; + + i = X509_NAME_get_index_by_NID(name, NID_commonName, i); + if (i == -1) + break; + e = X509_NAME_get_entry(name, i); + if (e == NULL) + continue; + cn = X509_NAME_ENTRY_get_data(e); + if (cn == NULL) + continue; + wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName", + cn->data, cn->length); + if (domain_suffix_match(cn->data, cn->length, match, full) == 1) + { + wpa_printf(MSG_DEBUG, "TLS: %s in commonName found", + full ? "Match" : "Suffix match"); + return 1; + } + } + + wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found", + full ? "": "suffix "); + return 0; +#endif /* CONFIG_NATIVE_WINDOWS */ +} + + static enum tls_fail_reason openssl_tls_fail_reason(int err) { switch (err) { @@ -1106,8 +1670,9 @@ static void openssl_tls_fail_event(struct tls_connection *conn, { union tls_event_data ev; struct wpabuf *cert = NULL; + struct tls_context *context = conn->context; - if (tls_global->event_cb == NULL) + if (context->event_cb == NULL) return; cert = get_x509_cert(err_cert); @@ -1118,7 +1683,7 @@ static void openssl_tls_fail_event(struct tls_connection *conn, ev.cert_fail.subject = subject; ev.cert_fail.reason_txt = err_str; ev.cert_fail.cert = cert; - tls_global->event_cb(tls_global->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); + context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); wpabuf_free(cert); } @@ -1129,15 +1694,22 @@ static void openssl_tls_cert_event(struct tls_connection *conn, { struct wpabuf *cert = NULL; union tls_event_data ev; + struct tls_context *context = conn->context; + char *altsubject[TLS_MAX_ALT_SUBJECT]; + int alt, num_altsubject = 0; + GENERAL_NAME *gen; + void *ext; + stack_index_t i; #ifdef CONFIG_SHA256 u8 hash[32]; #endif /* CONFIG_SHA256 */ - if (tls_global->event_cb == NULL) + if (context->event_cb == NULL) return; os_memset(&ev, 0, sizeof(ev)); - if (conn->cert_probe) { + if (conn->cert_probe || (conn->flags & TLS_CONN_EXT_CERT_CHECK) || + context->cert_in_cb) { cert = get_x509_cert(err_cert); ev.peer_cert.cert = cert; } @@ -1155,8 +1727,53 @@ static void openssl_tls_cert_event(struct tls_connection *conn, #endif /* CONFIG_SHA256 */ ev.peer_cert.depth = depth; ev.peer_cert.subject = subject; - tls_global->event_cb(tls_global->cb_ctx, TLS_PEER_CERTIFICATE, &ev); + + ext = X509_get_ext_d2i(err_cert, NID_subject_alt_name, NULL, NULL); + for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { + char *pos; + + if (num_altsubject == TLS_MAX_ALT_SUBJECT) + break; + gen = sk_GENERAL_NAME_value(ext, i); + if (gen->type != GEN_EMAIL && + gen->type != GEN_DNS && + gen->type != GEN_URI) + continue; + + pos = os_malloc(10 + gen->d.ia5->length + 1); + if (pos == NULL) + break; + altsubject[num_altsubject++] = pos; + + switch (gen->type) { + case GEN_EMAIL: + os_memcpy(pos, "EMAIL:", 6); + pos += 6; + break; + case GEN_DNS: + os_memcpy(pos, "DNS:", 4); + pos += 4; + break; + case GEN_URI: + os_memcpy(pos, "URI:", 4); + pos += 4; + break; + } + + os_memcpy(pos, gen->d.ia5->data, gen->d.ia5->length); + pos += gen->d.ia5->length; + *pos = '\0'; + } + sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free); + + for (alt = 0; alt < num_altsubject; alt++) + ev.peer_cert.altsubject[alt] = altsubject[alt]; + ev.peer_cert.num_altsubject = num_altsubject; + + context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev); wpabuf_free(cert); + for (alt = 0; alt < num_altsubject; alt++) + os_free(altsubject[alt]); } @@ -1167,10 +1784,14 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) int err, depth; SSL *ssl; struct tls_connection *conn; - char *match, *altmatch; + struct tls_context *context; + char *match, *altmatch, *suffix_match, *domain_match; const char *err_str; err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); + if (!err_cert) + return 0; + err = X509_STORE_CTX_get_error(x509_ctx); depth = X509_STORE_CTX_get_error_depth(x509_ctx); ssl = X509_STORE_CTX_get_ex_data(x509_ctx, @@ -1178,39 +1799,86 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); conn = SSL_get_app_data(ssl); - match = conn ? conn->subject_match : NULL; - altmatch = conn ? conn->altsubject_match : NULL; + if (conn == NULL) + return 0; + + if (depth == 0) + conn->peer_cert = err_cert; + else if (depth == 1) + conn->peer_issuer = err_cert; + 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' server_cert_cb=%p server_cert_only=%d", + * preverify_ok, err, X509_verify_cert_error_string(err), + * conn->ca_cert_verify, depth, buf, conn->server_cert_cb, conn->server_cert_only); + */ + + 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 && 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 " + "time mismatch"); preverify_ok = 1; + } err_str = X509_verify_cert_error_string(err); #ifdef CONFIG_SHA256 - if (preverify_ok && depth == 0 && conn->server_cert_only) { - struct wpabuf *cert; - cert = get_x509_cert(err_cert); - if (!cert) { - wpa_printf(MSG_DEBUG, "OpenSSL: Could not fetch " - "server certificate data"); - preverify_ok = 0; - } else { - u8 hash[32]; - const u8 *addr[1]; - size_t len[1]; - addr[0] = wpabuf_head(cert); - len[0] = wpabuf_len(cert); - if (sha256_vector(1, addr, len, hash) < 0 || - os_memcmp(conn->srv_cert_hash, hash, 32) != 0) { - err_str = "Server certificate mismatch"; - err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN; - preverify_ok = 0; - } - wpabuf_free(cert); - } - } + if (depth == 0) { + if (conn->server_cert_cb) { + preverify_ok = conn->server_cert_cb(preverify_ok, err_cert, conn->server_cert_ctx); + wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb: server_cert_cb returned %d", preverify_ok); + } + if (conn->server_cert_only) { + /* + * Do not require preverify_ok so we can explicity allow otherwise + * invalid pinned server certificates. + */ + struct wpabuf *cert; + cert = get_x509_cert(err_cert); + if (!cert) { + wpa_printf(MSG_DEBUG, "OpenSSL: Could not fetch " + "server certificate data"); + preverify_ok = 0; + } else { + u8 hash[32]; + const u8 *addr[1]; + size_t len[1]; + addr[0] = wpabuf_head(cert); + len[0] = wpabuf_len(cert); + if (sha256_vector(1, addr, len, hash) < 0 || + os_memcmp(conn->srv_cert_hash, hash, 32) != 0) { + err_str = "Server certificate mismatch"; + err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN; + preverify_ok = 0; + } else if (!preverify_ok) { + /* + * Certificate matches pinned certificate, allow + * regardless of other problems. + */ + wpa_printf(MSG_DEBUG, + "tls_verify_cb: OpenSSL: Ignore validation issues for a pinned server certificate"); + preverify_ok = 1; + } + wpabuf_free(cert); + } + } + } #endif /* CONFIG_SHA256 */ if (!preverify_ok) { @@ -1241,6 +1909,22 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) openssl_tls_fail_event(conn, err_cert, err, depth, buf, "AltSubject mismatch", 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", + suffix_match); + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Domain suffix mismatch", + 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", + domain_match); + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Domain mismatch", + TLS_FAIL_DOMAIN_MISMATCH); } else openssl_tls_cert_event(conn, err_cert, depth, buf); @@ -1253,18 +1937,48 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) TLS_FAIL_SERVER_CHAIN_PROBE); } +#ifdef OPENSSL_IS_BORINGSSL + if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) && + preverify_ok) { + enum ocsp_result res; + + res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert, + conn->peer_issuer, + conn->peer_issuer_issuer); + if (res == OCSP_REVOKED) { + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "certificate revoked", + TLS_FAIL_REVOKED); + if (err == X509_V_OK) + X509_STORE_CTX_set_error( + x509_ctx, X509_V_ERR_CERT_REVOKED); + } else if (res != OCSP_GOOD && + (conn->flags & TLS_CONN_REQUIRE_OCSP)) { + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "bad certificate status response", + TLS_FAIL_UNSPECIFIED); + } + } +#endif /* OPENSSL_IS_BORINGSSL */ + + if (depth == 0 && preverify_ok && context->event_cb != NULL) + context->event_cb(context->cb_ctx, + TLS_CERT_CHAIN_SUCCESS, NULL); + return preverify_ok; } #ifndef OPENSSL_NO_STDIO -static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert) +static int tls_load_ca_der(struct tls_data *data, const char *ca_cert) { - SSL_CTX *ssl_ctx = _ssl_ctx; + SSL_CTX *ssl_ctx = data->ssl; X509_LOOKUP *lookup; int ret = 0; - lookup = X509_STORE_add_lookup(ssl_ctx->cert_store, + lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(ssl_ctx), X509_LOOKUP_file()); if (lookup == NULL) { tls_show_errors(MSG_WARNING, __func__, @@ -1290,26 +2004,32 @@ static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert) #endif /* OPENSSL_NO_STDIO */ -static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, - const char *ca_cert, const u8 *ca_cert_blob, - size_t ca_cert_blob_len, const char *ca_path) +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, + int (*server_cert_cb)(int ok_so_far, X509* cert, void *ca_ctx), + void *server_cert_ctx) { - SSL_CTX *ssl_ctx = _ssl_ctx; + SSL_CTX *ssl_ctx = data->ssl; + X509_STORE *store; /* * Remove previously configured trusted CA certificates before adding * new ones. */ - X509_STORE_free(ssl_ctx->cert_store); - ssl_ctx->cert_store = X509_STORE_new(); - if (ssl_ctx->cert_store == NULL) { + store = X509_STORE_new(); + if (store == NULL) { wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new " "certificate store", __func__); return -1; } + SSL_CTX_set_cert_store(ssl_ctx, store); SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); conn->ca_cert_verify = 1; + conn->server_cert_cb = server_cert_cb; + conn->server_cert_ctx = server_cert_ctx; if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: Probe for server certificate " @@ -1350,7 +2070,8 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, } if (ca_cert_blob) { - X509 *cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ca_cert_blob, + X509 *cert = d2i_X509(NULL, + (const unsigned char **) &ca_cert_blob, ca_cert_blob_len); if (cert == NULL) { tls_show_errors(MSG_WARNING, __func__, @@ -1358,7 +2079,8 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, return -1; } - if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx), + cert)) { unsigned long err = ERR_peek_error(); tls_show_errors(MSG_WARNING, __func__, "Failed to add ca_cert_blob to " @@ -1380,6 +2102,46 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, return 0; } +#ifdef ANDROID + /* Single alias */ + if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) { + if (tls_add_ca_from_keystore(SSL_CTX_get_cert_store(ssl_ctx), + &ca_cert[11]) < 0) + return -1; + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + return 0; + } + + /* Multiple aliases separated by space */ + if (ca_cert && os_strncmp("keystores://", ca_cert, 12) == 0) { + char *aliases = os_strdup(&ca_cert[12]); + const char *delim = " "; + int rc = 0; + char *savedptr; + char *alias; + + if (!aliases) + return -1; + alias = strtok_r(aliases, delim, &savedptr); + for (; alias; alias = strtok_r(NULL, delim, &savedptr)) { + if (tls_add_ca_from_keystore_encoded( + SSL_CTX_get_cert_store(ssl_ctx), alias)) { + wpa_printf(MSG_WARNING, + "OpenSSL: %s - Failed to add ca_cert %s from keystore", + __func__, alias); + rc = -1; + break; + } + } + os_free(aliases); + if (rc) + return rc; + + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + return 0; + } +#endif /* ANDROID */ + #ifdef CONFIG_NATIVE_WINDOWS if (ca_cert && tls_cryptoapi_ca_cert(ssl_ctx, conn->ssl, ca_cert) == 0) { @@ -1396,7 +2158,7 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, tls_show_errors(MSG_WARNING, __func__, "Failed to load root certificates"); if (ca_cert && - tls_load_ca_der(ssl_ctx, ca_cert) == 0) { + tls_load_ca_der(data, ca_cert) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: %s - loaded " "DER format CA certificate", __func__); @@ -1405,7 +2167,7 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, } else { wpa_printf(MSG_DEBUG, "TLS: Trusted root " "certificate(s) loaded"); - tls_get_errors(ssl_ctx); + tls_get_errors(data); } #else /* OPENSSL_NO_STDIO */ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", @@ -1422,8 +2184,10 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, } -static int tls_global_ca_cert(SSL_CTX *ssl_ctx, const char *ca_cert) +static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert) { + SSL_CTX *ssl_ctx = data->ssl; + if (ca_cert) { if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1) { @@ -1451,7 +2215,8 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl) int flags; if (check_crl) { - X509_STORE *cs = SSL_CTX_get_cert_store(ssl_ctx); + struct tls_data *data = ssl_ctx; + X509_STORE *cs = SSL_CTX_get_cert_store(data->ssl); if (cs == NULL) { tls_show_errors(MSG_INFO, __func__, "Failed to get " "certificate store when enabling " @@ -1469,7 +2234,9 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl) static int tls_connection_set_subject_match(struct tls_connection *conn, const char *subject_match, - const char *altsubject_match) + const char *altsubject_match, + const char *suffix_match, + const char *domain_match) { os_free(conn->subject_match); conn->subject_match = NULL; @@ -1487,14 +2254,64 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, return -1; } + os_free(conn->suffix_match); + conn->suffix_match = NULL; + if (suffix_match) { + conn->suffix_match = os_strdup(suffix_match); + if (conn->suffix_match == NULL) + return -1; + } + + os_free(conn->domain_match); + conn->domain_match = NULL; + if (domain_match) { + conn->domain_match = os_strdup(domain_match); + if (conn->domain_match == NULL) + return -1; + } + return 0; } +static void tls_set_conn_flags(SSL *ssl, unsigned int flags) +{ +#ifdef SSL_OP_NO_TICKET + if (flags & TLS_CONN_DISABLE_SESSION_TICKET) + SSL_set_options(ssl, SSL_OP_NO_TICKET); +#ifdef SSL_clear_options + else + SSL_clear_options(ssl, SSL_OP_NO_TICKET); +#endif /* SSL_clear_options */ +#endif /* SSL_OP_NO_TICKET */ + +#ifdef SSL_OP_NO_TLSv1 + if (flags & TLS_CONN_DISABLE_TLSv1_0) + SSL_set_options(ssl, SSL_OP_NO_TLSv1); + else + SSL_clear_options(ssl, SSL_OP_NO_TLSv1); +#endif /* SSL_OP_NO_TLSv1 */ +#ifdef SSL_OP_NO_TLSv1_1 + if (flags & TLS_CONN_DISABLE_TLSv1_1) + SSL_set_options(ssl, SSL_OP_NO_TLSv1_1); + else + SSL_clear_options(ssl, SSL_OP_NO_TLSv1_1); +#endif /* SSL_OP_NO_TLSv1_1 */ +#ifdef SSL_OP_NO_TLSv1_2 + if (flags & TLS_CONN_DISABLE_TLSv1_2) + SSL_set_options(ssl, SSL_OP_NO_TLSv1_2); + else + SSL_clear_options(ssl, SSL_OP_NO_TLSv1_2); +#endif /* SSL_OP_NO_TLSv1_2 */ +} + + int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, - int verify_peer) + int verify_peer, unsigned int flags, + const u8 *session_ctx, size_t session_ctx_len) { static int counter = 0; + struct tls_data *data = ssl_ctx; if (conn == NULL) return -1; @@ -1509,20 +2326,25 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL); } + tls_set_conn_flags(conn->ssl, flags); + conn->flags = flags; + SSL_set_accept_state(conn->ssl); - /* - * Set session id context in order to avoid fatal errors when client - * tries to resume a session. However, set the context to a unique - * value in order to effectively disable session resumption for now - * since not all areas of the server code are ready for it (e.g., - * EAP-TTLS needs special handling for Phase 2 after abbreviated TLS - * handshake). - */ - counter++; - SSL_set_session_id_context(conn->ssl, - (const unsigned char *) &counter, - sizeof(counter)); + if (data->tls_session_lifetime == 0) { + /* + * Set session id context to a unique value to make sure + * session resumption cannot be used either through session + * caching or TLS ticket extension. + */ + counter++; + SSL_set_session_id_context(conn->ssl, + (const unsigned char *) &counter, + sizeof(counter)); + } else if (session_ctx) { + SSL_set_session_id_context(conn->ssl, session_ctx, + session_ctx_len); + } return 0; } @@ -1536,6 +2358,17 @@ static int tls_connection_client_cert(struct tls_connection *conn, if (client_cert == NULL && client_cert_blob == NULL) return 0; +#ifdef PKCS12_FUNCS +#if OPENSSL_VERSION_NUMBER < 0x10002000L + /* + * Clear previously set extra chain certificates, if any, from PKCS#12 + * processing in tls_parse_pkcs12() to allow OpenSSL to build a new + * chain properly. + */ + SSL_CTX_clear_extra_chain_certs(conn->ssl_ctx); +#endif /* OPENSSL_VERSION_NUMBER < 0x10002000L */ +#endif /* PKCS12_FUNCS */ + if (client_cert_blob && SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob, client_cert_blob_len) == 1) { @@ -1550,26 +2383,42 @@ static int tls_connection_client_cert(struct tls_connection *conn, if (client_cert == NULL) return -1; +#ifdef ANDROID + if (os_strncmp("keystore://", client_cert, 11) == 0) { + BIO *bio = BIO_from_keystore(&client_cert[11]); + X509 *x509 = NULL; + int ret = -1; + if (bio) { + x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); + BIO_free(bio); + } + if (x509) { + if (SSL_use_certificate(conn->ssl, x509) == 1) + ret = 0; + X509_free(x509); + } + return ret; + } +#endif /* ANDROID */ + #ifndef OPENSSL_NO_STDIO if (SSL_use_certificate_file(conn->ssl, client_cert, SSL_FILETYPE_ASN1) == 1) { wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (DER)" " --> OK"); return 0; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_certificate_file (DER) failed"); } if (SSL_use_certificate_file(conn->ssl, client_cert, SSL_FILETYPE_PEM) == 1) { + ERR_clear_error(); wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (PEM)" " --> OK"); return 0; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_certificate_file (PEM) failed"); } + + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_certificate_file failed"); #else /* OPENSSL_NO_STDIO */ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); #endif /* OPENSSL_NO_STDIO */ @@ -1578,14 +2427,18 @@ static int tls_connection_client_cert(struct tls_connection *conn, } -static int tls_global_client_cert(SSL_CTX *ssl_ctx, const char *client_cert) +static int tls_global_client_cert(struct tls_data *data, + const char *client_cert) { #ifndef OPENSSL_NO_STDIO + SSL_CTX *ssl_ctx = data->ssl; + if (client_cert == NULL) return 0; if (SSL_CTX_use_certificate_file(ssl_ctx, client_cert, SSL_FILETYPE_ASN1) != 1 && + SSL_CTX_use_certificate_chain_file(ssl_ctx, client_cert) != 1 && SSL_CTX_use_certificate_file(ssl_ctx, client_cert, SSL_FILETYPE_PEM) != 1) { tls_show_errors(MSG_INFO, __func__, @@ -1613,7 +2466,7 @@ static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) #ifdef PKCS12_FUNCS -static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, +static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12, const char *passwd) { EVP_PKEY *pkey; @@ -1625,6 +2478,8 @@ static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, pkey = NULL; cert = NULL; certs = NULL; + if (!passwd) + passwd = ""; if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) { tls_show_errors(MSG_DEBUG, __func__, "Failed to parse PKCS12 file"); @@ -1642,7 +2497,7 @@ static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, if (SSL_use_certificate(ssl, cert) != 1) res = -1; } else { - if (SSL_CTX_use_certificate(ssl_ctx, cert) != 1) + if (SSL_CTX_use_certificate(data->ssl, cert) != 1) res = -1; } X509_free(cert); @@ -1654,13 +2509,64 @@ static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, if (SSL_use_PrivateKey(ssl, pkey) != 1) res = -1; } else { - if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1) + if (SSL_CTX_use_PrivateKey(data->ssl, pkey) != 1) res = -1; } EVP_PKEY_free(pkey); } if (certs) { +#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER) + if (ssl) + SSL_clear_chain_certs(ssl); + else + SSL_CTX_clear_chain_certs(data->ssl); + while ((cert = sk_X509_pop(certs)) != NULL) { + X509_NAME_oneline(X509_get_subject_name(cert), buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "TLS: additional certificate" + " from PKCS12: subject='%s'", buf); + if ((ssl && SSL_add1_chain_cert(ssl, cert) != 1) || + (!ssl && SSL_CTX_add1_chain_cert(data->ssl, + cert) != 1)) { + tls_show_errors(MSG_DEBUG, __func__, + "Failed to add additional certificate"); + res = -1; + X509_free(cert); + break; + } + X509_free(cert); + } + if (!res) { + /* Try to continue anyway */ + } + sk_X509_pop_free(certs, X509_free); +#ifndef OPENSSL_IS_BORINGSSL + if (ssl) + res = SSL_build_cert_chain( + ssl, + SSL_BUILD_CHAIN_FLAG_CHECK | + SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR); + else + res = SSL_CTX_build_cert_chain( + data->ssl, + SSL_BUILD_CHAIN_FLAG_CHECK | + SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR); + if (!res) { + tls_show_errors(MSG_DEBUG, __func__, + "Failed to build certificate chain"); + } else if (res == 2) { + wpa_printf(MSG_DEBUG, + "TLS: Ignore certificate chain verification error when building chain with PKCS#12 extra certificates"); + } +#endif /* OPENSSL_IS_BORINGSSL */ + /* + * Try to continue regardless of result since it is possible for + * the extra certificates not to be required. + */ + res = 0; +#else /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ + SSL_CTX_clear_extra_chain_certs(data->ssl); while ((cert = sk_X509_pop(certs)) != NULL) { X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); @@ -1670,26 +2576,29 @@ static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, * There is no SSL equivalent for the chain cert - so * always add it to the context... */ - if (SSL_CTX_add_extra_chain_cert(ssl_ctx, cert) != 1) { + if (SSL_CTX_add_extra_chain_cert(data->ssl, cert) != 1) + { + X509_free(cert); res = -1; break; } } - sk_X509_free(certs); + sk_X509_pop_free(certs, X509_free); +#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ } PKCS12_free(p12); if (res < 0) - tls_get_errors(ssl_ctx); + tls_get_errors(data); return res; } #endif /* PKCS12_FUNCS */ -static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, - const char *passwd) +static int tls_read_pkcs12(struct tls_data *data, SSL *ssl, + const char *private_key, const char *passwd) { #ifdef PKCS12_FUNCS FILE *f; @@ -1708,7 +2617,7 @@ static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, return -1; } - return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); + return tls_parse_pkcs12(data, ssl, p12, passwd); #else /* PKCS12_FUNCS */ wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read " @@ -1718,20 +2627,20 @@ static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, } -static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl, +static int tls_read_pkcs12_blob(struct tls_data *data, SSL *ssl, const u8 *blob, size_t len, const char *passwd) { #ifdef PKCS12_FUNCS PKCS12 *p12; - p12 = d2i_PKCS12(NULL, (OPENSSL_d2i_TYPE) &blob, len); + p12 = d2i_PKCS12(NULL, (const unsigned char **) &blob, len); if (p12 == NULL) { tls_show_errors(MSG_INFO, __func__, "Failed to use PKCS#12 blob"); return -1; } - return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); + return tls_parse_pkcs12(data, ssl, p12, passwd); #else /* PKCS12_FUNCS */ wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse " @@ -1756,9 +2665,13 @@ static int tls_engine_get_cert(struct tls_connection *conn, if (!ENGINE_ctrl_cmd(conn->engine, "LOAD_CERT_CTRL", 0, ¶ms, NULL, 1)) { + unsigned long err = ERR_get_error(); + wpa_printf(MSG_ERROR, "ENGINE: cannot load client cert with id" " '%s' [%s]", cert_id, - ERR_error_string(ERR_get_error(), NULL)); + ERR_error_string(err, NULL)); + if (tls_is_pin_error(err)) + return TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN; return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; } if (!params.cert) { @@ -1798,27 +2711,28 @@ static int tls_connection_engine_client_cert(struct tls_connection *conn, } -static int tls_connection_engine_ca_cert(void *_ssl_ctx, +static int tls_connection_engine_ca_cert(struct tls_data *data, struct tls_connection *conn, const char *ca_cert_id) { #ifndef OPENSSL_NO_ENGINE X509 *cert; - SSL_CTX *ssl_ctx = _ssl_ctx; + SSL_CTX *ssl_ctx = data->ssl; + X509_STORE *store; if (tls_engine_get_cert(conn, ca_cert_id, &cert)) return -1; /* start off the same as tls_connection_ca_cert */ - X509_STORE_free(ssl_ctx->cert_store); - ssl_ctx->cert_store = X509_STORE_new(); - if (ssl_ctx->cert_store == NULL) { + store = X509_STORE_new(); + if (store == NULL) { wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new " "certificate store", __func__); X509_free(cert); return -1; } - if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + SSL_CTX_set_cert_store(ssl_ctx, store); + if (!X509_STORE_add_cert(store, cert)) { unsigned long err = ERR_peek_error(); tls_show_errors(MSG_WARNING, __func__, "Failed to add CA certificate from engine " @@ -1837,6 +2751,8 @@ static int tls_connection_engine_ca_cert(void *_ssl_ctx, wpa_printf(MSG_DEBUG, "OpenSSL: %s - added CA certificate from engine " "to certificate store", __func__); SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + conn->ca_cert_verify = 1; + return 0; #else /* OPENSSL_NO_ENGINE */ @@ -1847,7 +2763,7 @@ static int tls_connection_engine_ca_cert(void *_ssl_ctx, static int tls_connection_engine_private_key(struct tls_connection *conn) { -#ifndef OPENSSL_NO_ENGINE +#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE) if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) { tls_show_errors(MSG_ERROR, __func__, "ENGINE: cannot use private key for TLS"); @@ -1867,14 +2783,14 @@ static int tls_connection_engine_private_key(struct tls_connection *conn) } -static int tls_connection_private_key(void *_ssl_ctx, +static int tls_connection_private_key(struct tls_data *data, struct tls_connection *conn, const char *private_key, const char *private_key_passwd, const u8 *private_key_blob, size_t private_key_blob_len) { - SSL_CTX *ssl_ctx = _ssl_ctx; + SSL_CTX *ssl_ctx = data->ssl; char *passwd; int ok; @@ -1900,10 +2816,6 @@ static int tls_connection_private_key(void *_ssl_ctx, "ASN1(EVP_PKEY_RSA) --> OK"); ok = 1; break; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA)" - " failed"); } if (SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA, conn->ssl, @@ -1913,10 +2825,6 @@ static int tls_connection_private_key(void *_ssl_ctx, "ASN1(EVP_PKEY_DSA) --> OK"); ok = 1; break; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA)" - " failed"); } if (SSL_use_RSAPrivateKey_ASN1(conn->ssl, @@ -1926,12 +2834,9 @@ static int tls_connection_private_key(void *_ssl_ctx, "SSL_use_RSAPrivateKey_ASN1 --> OK"); ok = 1; break; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_RSAPrivateKey_ASN1 failed"); } - if (tls_read_pkcs12_blob(ssl_ctx, conn->ssl, private_key_blob, + if (tls_read_pkcs12_blob(data, conn->ssl, private_key_blob, private_key_blob_len, passwd) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> " "OK"); @@ -1950,10 +2855,6 @@ static int tls_connection_private_key(void *_ssl_ctx, "SSL_use_PrivateKey_File (DER) --> OK"); ok = 1; break; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_PrivateKey_File (DER) " - "failed"); } if (SSL_use_PrivateKey_file(conn->ssl, private_key, @@ -1962,17 +2863,13 @@ static int tls_connection_private_key(void *_ssl_ctx, "SSL_use_PrivateKey_File (PEM) --> OK"); ok = 1; break; - } else { - tls_show_errors(MSG_DEBUG, __func__, - "SSL_use_PrivateKey_File (PEM) " - "failed"); } #else /* OPENSSL_NO_STDIO */ wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); #endif /* OPENSSL_NO_STDIO */ - if (tls_read_pkcs12(ssl_ctx, conn->ssl, private_key, passwd) + if (tls_read_pkcs12(data, conn->ssl, private_key, passwd) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file " "--> OK"); @@ -1991,15 +2888,15 @@ static int tls_connection_private_key(void *_ssl_ctx, } if (!ok) { - wpa_printf(MSG_INFO, "OpenSSL: Failed to load private key"); + tls_show_errors(MSG_INFO, __func__, + "Failed to load private key"); os_free(passwd); - ERR_clear_error(); return -1; } ERR_clear_error(); SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); os_free(passwd); - + if (!SSL_check_private_key(conn->ssl)) { tls_show_errors(MSG_INFO, __func__, "Private key failed " "verification"); @@ -2011,9 +2908,11 @@ static int tls_connection_private_key(void *_ssl_ctx, } -static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key, +static int tls_global_private_key(struct tls_data *data, + const char *private_key, const char *private_key_passwd) { + SSL_CTX *ssl_ctx = data->ssl; char *passwd; if (private_key == NULL) @@ -2035,7 +2934,7 @@ static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key, SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, SSL_FILETYPE_PEM) != 1 && #endif /* OPENSSL_NO_STDIO */ - tls_read_pkcs12(ssl_ctx, NULL, private_key, passwd)) { + tls_read_pkcs12(data, NULL, private_key, passwd)) { tls_show_errors(MSG_INFO, __func__, "Failed to load private key"); os_free(passwd); @@ -2045,7 +2944,7 @@ static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key, os_free(passwd); ERR_clear_error(); SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); - + if (!SSL_CTX_check_private_key(ssl_ctx)) { tls_show_errors(MSG_INFO, __func__, "Private key failed verification"); @@ -2130,7 +3029,7 @@ static int tls_connection_dh(struct tls_connection *conn, const char *dh_file) } -static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file) +static int tls_global_dh(struct tls_data *data, const char *dh_file) { #ifdef OPENSSL_NO_DH if (dh_file == NULL) @@ -2139,6 +3038,7 @@ static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file) "dh_file specified"); return -1; #else /* OPENSSL_NO_DH */ + SSL_CTX *ssl_ctx = data->ssl; DH *dh; BIO *bio; @@ -2204,34 +3104,175 @@ static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file) } -int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, - struct tls_keys *keys) +int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, + struct tls_random *keys) { SSL *ssl; if (conn == NULL || keys == NULL) return -1; ssl = conn->ssl; - if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL) + if (ssl == NULL) return -1; os_memset(keys, 0, sizeof(*keys)); - keys->master_key = ssl->session->master_key; - keys->master_key_len = ssl->session->master_key_length; - keys->client_random = ssl->s3->client_random; - keys->client_random_len = SSL3_RANDOM_SIZE; - keys->server_random = ssl->s3->server_random; - keys->server_random_len = SSL3_RANDOM_SIZE; + keys->client_random = conn->client_random; + keys->client_random_len = SSL_get_client_random( + ssl, conn->client_random, sizeof(conn->client_random)); + keys->server_random = conn->server_random; + keys->server_random_len = SSL_get_server_random( + ssl, conn->server_random, sizeof(conn->server_random)); return 0; } -int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, - const char *label, int server_random_first, - u8 *out, size_t out_len) +#ifdef OPENSSL_NEED_EAP_FAST_PRF +static int openssl_get_keyblock_size(SSL *ssl) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + const EVP_CIPHER *c; + const EVP_MD *h; + int md_size; + + if (ssl->enc_read_ctx == NULL || ssl->enc_read_ctx->cipher == NULL || + ssl->read_hash == NULL) + return -1; + + c = ssl->enc_read_ctx->cipher; + h = EVP_MD_CTX_md(ssl->read_hash); + if (h) + md_size = EVP_MD_size(h); + else if (ssl->s3) + md_size = ssl->s3->tmp.new_mac_secret_size; + else + return -1; + + wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d " + "IV_len=%d", EVP_CIPHER_key_length(c), md_size, + EVP_CIPHER_iv_length(c)); + return 2 * (EVP_CIPHER_key_length(c) + + md_size + + EVP_CIPHER_iv_length(c)); +#else + const SSL_CIPHER *ssl_cipher; + int cipher, digest; + const EVP_CIPHER *c; + const EVP_MD *h; + + ssl_cipher = SSL_get_current_cipher(ssl); + if (!ssl_cipher) + return -1; + cipher = SSL_CIPHER_get_cipher_nid(ssl_cipher); + digest = SSL_CIPHER_get_digest_nid(ssl_cipher); + wpa_printf(MSG_DEBUG, "OpenSSL: cipher nid %d digest nid %d", + cipher, digest); + if (cipher < 0 || digest < 0) + return -1; + c = EVP_get_cipherbynid(cipher); + h = EVP_get_digestbynid(digest); + if (!c || !h) + return -1; + + wpa_printf(MSG_DEBUG, + "OpenSSL: keyblock size: key_len=%d MD_size=%d IV_len=%d", + EVP_CIPHER_key_length(c), EVP_MD_size(h), + EVP_CIPHER_iv_length(c)); + return 2 * (EVP_CIPHER_key_length(c) + EVP_MD_size(h) + + EVP_CIPHER_iv_length(c)); +#endif +} +#endif /* OPENSSL_NEED_EAP_FAST_PRF */ + + +int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, + const char *label, u8 *out, size_t out_len) +{ + if (!conn || + SSL_export_keying_material(conn->ssl, out, out_len, label, + os_strlen(label), NULL, 0, 0) != 1) + return -1; + return 0; +} + + +int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, + u8 *out, size_t out_len) +{ +#ifdef OPENSSL_NEED_EAP_FAST_PRF + SSL *ssl; + SSL_SESSION *sess; + u8 *rnd; + int ret = -1; + int skip = 0; + u8 *tmp_out = NULL; + u8 *_out = out; + unsigned char client_random[SSL3_RANDOM_SIZE]; + unsigned char server_random[SSL3_RANDOM_SIZE]; + unsigned char master_key[64]; + size_t master_key_len; + const char *ver; + + /* + * TLS library did not support EAP-FAST key generation, so get the + * needed TLS session parameters and use an internal implementation of + * TLS PRF to derive the key. + */ + + if (conn == NULL) + return -1; + ssl = conn->ssl; + if (ssl == NULL) + return -1; + ver = SSL_get_version(ssl); + sess = SSL_get_session(ssl); + if (!ver || !sess) + return -1; + + skip = openssl_get_keyblock_size(ssl); + if (skip < 0) + return -1; + tmp_out = os_malloc(skip + out_len); + if (!tmp_out) + return -1; + _out = tmp_out; + + rnd = os_malloc(2 * SSL3_RANDOM_SIZE); + if (!rnd) { + os_free(tmp_out); + return -1; + } + + SSL_get_client_random(ssl, client_random, sizeof(client_random)); + SSL_get_server_random(ssl, server_random, sizeof(server_random)); + master_key_len = SSL_SESSION_get_master_key(sess, master_key, + sizeof(master_key)); + + os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE); + os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random, SSL3_RANDOM_SIZE); + + if (os_strcmp(ver, "TLSv1.2") == 0) { + tls_prf_sha256(master_key, master_key_len, + "key expansion", rnd, 2 * SSL3_RANDOM_SIZE, + _out, skip + out_len); + ret = 0; + } else if (tls_prf_sha1_md5(master_key, master_key_len, + "key expansion", rnd, 2 * SSL3_RANDOM_SIZE, + _out, skip + out_len) == 0) { + ret = 0; + } + os_memset(master_key, 0, sizeof(master_key)); + os_free(rnd); + if (ret == 0) + os_memcpy(out, _out + skip, out_len); + bin_clear_free(tmp_out, skip); + + return ret; +#else /* OPENSSL_NEED_EAP_FAST_PRF */ + wpa_printf(MSG_ERROR, + "OpenSSL: EAP-FAST keys cannot be exported in FIPS mode"); return -1; +#endif /* OPENSSL_NEED_EAP_FAST_PRF */ } @@ -2246,7 +3287,7 @@ openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data, * Give TLS handshake data from the server (if available) to OpenSSL * for processing. */ - if (in_data && + if (in_data && wpabuf_len(in_data) > 0 && BIO_write(conn->ssl_in, wpabuf_head(in_data), wpabuf_len(in_data)) < 0) { tls_show_errors(MSG_INFO, __func__, @@ -2352,9 +3393,30 @@ openssl_connection_handshake(struct tls_connection *conn, out_data = openssl_handshake(conn, in_data, server); if (out_data == NULL) return NULL; + if (conn->invalid_hb_used) { + wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response"); + wpabuf_free(out_data); + return NULL; + } - if (SSL_is_init_finished(conn->ssl) && appl_data && in_data) - *appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data)); + if (SSL_is_init_finished(conn->ssl)) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Handshake finished - resumed=%d", + tls_connection_resumed(conn->ssl_ctx, conn)); + if (appl_data && in_data) + *appl_data = openssl_get_appl_data(conn, + wpabuf_len(in_data)); + } + + if (conn->invalid_hb_used) { + wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response"); + if (appl_data) { + wpabuf_free(*appl_data); + *appl_data = NULL; + } + wpabuf_free(out_data); + return NULL; + } return out_data; } @@ -2457,20 +3519,26 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx, } wpabuf_put(buf, res); + if (conn->invalid_hb_used) { + wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response"); + wpabuf_free(buf); + return NULL; + } + return buf; } int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) { - return conn ? conn->ssl->hit : 0; + return conn ? SSL_cache_hit(conn->ssl) : 0; } int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, u8 *ciphers) { - char buf[100], *pos, *end; + char buf[500], *pos, *end; u8 *c; int ret; @@ -2498,13 +3566,19 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, case TLS_CIPHER_ANON_DH_AES128_SHA: suite = "ADH-AES128-SHA"; break; + case TLS_CIPHER_RSA_DHE_AES256_SHA: + suite = "DHE-RSA-AES256-SHA"; + break; + case TLS_CIPHER_AES256_SHA: + suite = "AES256-SHA"; + break; default: wpa_printf(MSG_DEBUG, "TLS: Unsupported " "cipher selection: %d", *c); return -1; } ret = os_snprintf(pos, end - pos, ":%s", suite); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) break; pos += ret; @@ -2513,6 +3587,21 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1); +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) + if (os_strstr(buf, ":ADH-")) { + /* + * Need to drop to security level 0 to allow anonymous + * cipher suites for EAP-FAST. + */ + SSL_set_security_level(conn->ssl, 0); + } else if (SSL_get_security_level(conn->ssl) == 0) { + /* Force at least security level 1 */ + SSL_set_security_level(conn->ssl, 1); + } +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ +#endif + if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) { tls_show_errors(MSG_INFO, __func__, "Cipher suite configuration failed"); @@ -2523,6 +3612,22 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, } +int tls_get_version(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + const char *name; + if (conn == NULL || conn->ssl == NULL) + return -1; + + name = SSL_get_version(conn->ssl); + if (name == NULL) + return -1; + + os_strlcpy(buf, name, buflen); + return 0; +} + + int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, char *buf, size_t buflen) { @@ -2559,15 +3664,9 @@ int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, if (conn == NULL || conn->ssl == NULL || ext_type != 35) return -1; -#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE if (SSL_set_session_ticket_ext(conn->ssl, (void *) data, data_len) != 1) return -1; -#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ - if (SSL_set_hello_extension(conn->ssl, ext_type, (void *) data, - data_len) != 1) - return -1; -#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ return 0; } @@ -2598,56 +3697,366 @@ int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) } +#ifdef HAVE_OCSP + +static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp) +{ +#ifndef CONFIG_NO_STDOUT_DEBUG + BIO *out; + size_t rlen; + char *txt; + int res; + + if (wpa_debug_level > MSG_DEBUG) + return; + + out = BIO_new(BIO_s_mem()); + if (!out) + return; + + OCSP_RESPONSE_print(out, rsp, 0); + rlen = BIO_ctrl_pending(out); + txt = os_malloc(rlen + 1); + if (!txt) { + BIO_free(out); + return; + } + + res = BIO_read(out, txt, rlen); + if (res > 0) { + txt[res] = '\0'; + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP Response\n%s", txt); + } + os_free(txt); + BIO_free(out); +#endif /* CONFIG_NO_STDOUT_DEBUG */ +} + + +static void debug_print_cert(X509 *cert, const char *title) +{ +#ifndef CONFIG_NO_STDOUT_DEBUG + BIO *out; + size_t rlen; + char *txt; + int res; + + if (wpa_debug_level > MSG_DEBUG) + return; + + out = BIO_new(BIO_s_mem()); + if (!out) + return; + + X509_print(out, cert); + rlen = BIO_ctrl_pending(out); + txt = os_malloc(rlen + 1); + if (!txt) { + BIO_free(out); + return; + } + + res = BIO_read(out, txt, rlen); + if (res > 0) { + txt[res] = '\0'; + wpa_printf(MSG_DEBUG, "OpenSSL: %s\n%s", title, txt); + } + os_free(txt); + + BIO_free(out); +#endif /* CONFIG_NO_STDOUT_DEBUG */ +} + + +static int ocsp_resp_cb(SSL *s, void *arg) +{ + struct tls_connection *conn = arg; + const unsigned char *p; + int len, status, reason; + OCSP_RESPONSE *rsp; + OCSP_BASICRESP *basic; + OCSP_CERTID *id; + ASN1_GENERALIZEDTIME *produced_at, *this_update, *next_update; + X509_STORE *store; + STACK_OF(X509) *certs = NULL; + + len = SSL_get_tlsext_status_ocsp_resp(s, &p); + if (!p) { + wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received"); + return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1; + } + + wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", p, len); + + rsp = d2i_OCSP_RESPONSE(NULL, &p, len); + if (!rsp) { + wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSP response"); + return 0; + } + + ocsp_debug_print_resp(rsp); + + status = OCSP_response_status(rsp); + if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { + wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d (%s)", + status, OCSP_response_status_str(status)); + return 0; + } + + basic = OCSP_response_get1_basic(rsp); + if (!basic) { + wpa_printf(MSG_INFO, "OpenSSL: Could not find BasicOCSPResponse"); + return 0; + } + + store = SSL_CTX_get_cert_store(conn->ssl_ctx); + if (conn->peer_issuer) { + debug_print_cert(conn->peer_issuer, "Add OCSP issuer"); + + if (X509_STORE_add_cert(store, conn->peer_issuer) != 1) { + tls_show_errors(MSG_INFO, __func__, + "OpenSSL: Could not add issuer to certificate store"); + } + certs = sk_X509_new_null(); + if (certs) { + X509 *cert; + cert = X509_dup(conn->peer_issuer); + if (cert && !sk_X509_push(certs, cert)) { + tls_show_errors( + MSG_INFO, __func__, + "OpenSSL: Could not add issuer to OCSP responder trust store"); + X509_free(cert); + sk_X509_free(certs); + certs = NULL; + } + if (certs && conn->peer_issuer_issuer) { + cert = X509_dup(conn->peer_issuer_issuer); + if (cert && !sk_X509_push(certs, cert)) { + tls_show_errors( + MSG_INFO, __func__, + "OpenSSL: Could not add issuer's issuer to OCSP responder trust store"); + X509_free(cert); + } + } + } + } + + status = OCSP_basic_verify(basic, certs, store, OCSP_TRUSTOTHER); + sk_X509_pop_free(certs, X509_free); + if (status <= 0) { + tls_show_errors(MSG_INFO, __func__, + "OpenSSL: OCSP response failed verification"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded"); + + if (!conn->peer_cert) { + wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate not available for OCSP status check"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + if (!conn->peer_issuer) { + wpa_printf(MSG_DEBUG, "OpenSSL: Peer issuer certificate not available for OCSP status check"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer); + if (!id) { + wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at, + &this_update, &next_update)) { + wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s", + (conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" : + " (OCSP not required)"); + OCSP_CERTID_free(id); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1; + } + OCSP_CERTID_free(id); + + if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) { + tls_show_errors(MSG_INFO, __func__, + "OpenSSL: OCSP status times invalid"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status for server certificate: %s", + OCSP_cert_status_str(status)); + + if (status == V_OCSP_CERTSTATUS_GOOD) + return 1; + if (status == V_OCSP_CERTSTATUS_REVOKED) + return 0; + if (conn->flags & TLS_CONN_REQUIRE_OCSP) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required"); + return 0; + } + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue"); + return 1; +} + + +static int ocsp_status_cb(SSL *s, void *arg) +{ + char *tmp; + char *resp; + size_t len; + + if (tls_global->ocsp_stapling_response == NULL) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - no response configured"); + return SSL_TLSEXT_ERR_OK; + } + + resp = os_readfile(tls_global->ocsp_stapling_response, &len); + if (resp == NULL) { + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - could not read response file"); + /* TODO: Build OCSPResponse with responseStatus = internalError + */ + return SSL_TLSEXT_ERR_OK; + } + wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - send cached response"); + tmp = OPENSSL_malloc(len); + if (tmp == NULL) { + os_free(resp); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + os_memcpy(tmp, resp, len); + os_free(resp); + SSL_set_tlsext_status_ocsp_resp(s, tmp, len); + + return SSL_TLSEXT_ERR_OK; +} + +#endif /* HAVE_OCSP */ + + int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params) { + struct tls_data *data = tls_ctx; int ret; unsigned long err; + int can_pkcs11 = 0; + const char *key_id = params->key_id; + const char *cert_id = params->cert_id; + const char *ca_cert_id = params->ca_cert_id; + const char *engine_id = params->engine ? params->engine_id : NULL; if (conn == NULL) return -1; + if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) { + wpa_printf(MSG_INFO, + "OpenSSL: ocsp=3 not supported"); + return -1; + } + + /* + * If the engine isn't explicitly configured, and any of the + * cert/key fields are actually PKCS#11 URIs, then automatically + * use the PKCS#11 ENGINE. + */ + if (!engine_id || os_strcmp(engine_id, "pkcs11") == 0) + can_pkcs11 = 1; + + if (!key_id && params->private_key && can_pkcs11 && + os_strncmp(params->private_key, "pkcs11:", 7) == 0) { + can_pkcs11 = 2; + key_id = params->private_key; + } + + if (!cert_id && params->client_cert && can_pkcs11 && + os_strncmp(params->client_cert, "pkcs11:", 7) == 0) { + can_pkcs11 = 2; + cert_id = params->client_cert; + } + + if (!ca_cert_id && params->ca_cert && can_pkcs11 && + os_strncmp(params->ca_cert, "pkcs11:", 7) == 0) { + can_pkcs11 = 2; + ca_cert_id = params->ca_cert; + } + + /* If we need to automatically enable the PKCS#11 ENGINE, do so. */ + if (can_pkcs11 == 2 && !engine_id) + engine_id = "pkcs11"; + +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) +#if OPENSSL_VERSION_NUMBER < 0x10100000L + if (params->flags & TLS_CONN_EAP_FAST) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Use TLSv1_method() for EAP-FAST"); + if (SSL_set_ssl_method(conn->ssl, TLSv1_method()) != 1) { + tls_show_errors(MSG_INFO, __func__, + "Failed to set TLSv1_method() for EAP-FAST"); + return -1; + } + } +#endif +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ + while ((err = ERR_get_error())) { wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s", __func__, ERR_error_string(err, NULL)); } - if (params->engine) { + if (engine_id) { wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine"); - ret = tls_engine_init(conn, params->engine_id, params->pin, - params->key_id, params->cert_id, - params->ca_cert_id); + ret = tls_engine_init(conn, engine_id, params->pin, + key_id, cert_id, ca_cert_id); if (ret) return ret; } if (tls_connection_set_subject_match(conn, params->subject_match, - params->altsubject_match)) + params->altsubject_match, + params->suffix_match, + params->domain_match)) return -1; - if (params->engine && params->ca_cert_id) { - if (tls_connection_engine_ca_cert(tls_ctx, conn, - params->ca_cert_id)) + 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(tls_ctx, conn, params->ca_cert, - params->ca_cert_blob, - params->ca_cert_blob_len, - params->ca_path)) + } else if (tls_connection_ca_cert(data, conn, params->ca_cert, + params->ca_cert_blob, + params->ca_cert_blob_len, + params->ca_path, + params->server_cert_cb, + params->server_cert_ctx)) return -1; - if (params->engine && params->cert_id) { - if (tls_connection_engine_client_cert(conn, params->cert_id)) + if (engine_id && cert_id) { + if (tls_connection_engine_client_cert(conn, cert_id)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; } else if (tls_connection_client_cert(conn, params->client_cert, params->client_cert_blob, params->client_cert_blob_len)) return -1; - if (params->engine && params->key_id) { + if (engine_id && key_id) { wpa_printf(MSG_DEBUG, "TLS: Using private key from engine"); if (tls_connection_engine_private_key(conn)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; - } else if (tls_connection_private_key(tls_ctx, conn, + } else if (tls_connection_private_key(data, conn, params->private_key, params->private_key_passwd, params->private_key_blob, @@ -2663,7 +4072,44 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } - tls_get_errors(tls_ctx); + if (params->openssl_ciphers && + SSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set cipher string '%s'", + params->openssl_ciphers); + return -1; + } + + tls_set_conn_flags(conn->ssl, params->flags); + +#ifdef OPENSSL_IS_BORINGSSL + if (params->flags & TLS_CONN_REQUEST_OCSP) { + SSL_enable_ocsp_stapling(conn->ssl); + } +#else /* OPENSSL_IS_BORINGSSL */ +#ifdef HAVE_OCSP + if (params->flags & TLS_CONN_REQUEST_OCSP) { + SSL_CTX *ssl_ctx = data->ssl; + SSL_set_tlsext_status_type(conn->ssl, TLSEXT_STATUSTYPE_ocsp); + SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb); + SSL_CTX_set_tlsext_status_arg(ssl_ctx, conn); + } +#else /* HAVE_OCSP */ + if (params->flags & TLS_CONN_REQUIRE_OCSP) { + wpa_printf(MSG_INFO, + "OpenSSL: No OCSP support included - reject configuration"); + return -1; + } + if (params->flags & TLS_CONN_REQUEST_OCSP) { + wpa_printf(MSG_DEBUG, + "OpenSSL: No OCSP support included - allow optional OCSP case to continue"); + } +#endif /* HAVE_OCSP */ +#endif /* OPENSSL_IS_BORINGSSL */ + + conn->flags = params->flags; + + tls_get_errors(data); return 0; } @@ -2672,7 +4118,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, int tls_global_set_params(void *tls_ctx, const struct tls_connection_params *params) { - SSL_CTX *ssl_ctx = tls_ctx; + struct tls_data *data = tls_ctx; + SSL_CTX *ssl_ctx = data->ssl; unsigned long err; while ((err = ERR_get_error())) { @@ -2680,98 +4127,66 @@ int tls_global_set_params(void *tls_ctx, __func__, ERR_error_string(err, NULL)); } - if (tls_global_ca_cert(ssl_ctx, params->ca_cert)) - return -1; - - if (tls_global_client_cert(ssl_ctx, params->client_cert)) - return -1; - - if (tls_global_private_key(ssl_ctx, params->private_key, - params->private_key_passwd)) - return -1; - - if (tls_global_dh(ssl_ctx, params->dh_file)) { - wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'", - params->dh_file); + if (tls_global_ca_cert(data, params->ca_cert) || + tls_global_client_cert(data, params->client_cert) || + tls_global_private_key(data, params->private_key, + params->private_key_passwd) || + tls_global_dh(data, params->dh_file)) { + wpa_printf(MSG_INFO, "TLS: Failed to set global parameters"); return -1; } - return 0; -} - - -int tls_connection_get_keyblock_size(void *tls_ctx, - struct tls_connection *conn) -{ - const EVP_CIPHER *c; - const EVP_MD *h; - - if (conn == NULL || conn->ssl == NULL || - conn->ssl->enc_read_ctx == NULL || - conn->ssl->enc_read_ctx->cipher == NULL || - conn->ssl->read_hash == NULL) + if (params->openssl_ciphers && + SSL_CTX_set_cipher_list(ssl_ctx, params->openssl_ciphers) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set cipher string '%s'", + params->openssl_ciphers); return -1; + } - c = conn->ssl->enc_read_ctx->cipher; -#if OPENSSL_VERSION_NUMBER >= 0x00909000L - h = EVP_MD_CTX_md(conn->ssl->read_hash); -#else - h = conn->ssl->read_hash; -#endif - - return 2 * (EVP_CIPHER_key_length(c) + - EVP_MD_size(h) + - EVP_CIPHER_iv_length(c)); -} - +#ifdef SSL_OP_NO_TICKET + if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) + SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET); +#ifdef SSL_CTX_clear_options + else + SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET); +#endif /* SSL_clear_options */ +#endif /* SSL_OP_NO_TICKET */ + +#ifdef HAVE_OCSP + SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_status_cb); + SSL_CTX_set_tlsext_status_arg(ssl_ctx, ssl_ctx); + os_free(tls_global->ocsp_stapling_response); + if (params->ocsp_stapling_response) + tls_global->ocsp_stapling_response = + os_strdup(params->ocsp_stapling_response); + else + tls_global->ocsp_stapling_response = NULL; +#endif /* HAVE_OCSP */ -unsigned int tls_capabilities(void *tls_ctx) -{ return 0; } -int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, - int tls_ia) -{ - return -1; -} - - -struct wpabuf * tls_connection_ia_send_phase_finished( - void *tls_ctx, struct tls_connection *conn, int final) -{ - return NULL; -} - - -int tls_connection_ia_final_phase_finished(void *tls_ctx, - struct tls_connection *conn) -{ - return -1; -} - - -int tls_connection_ia_permute_inner_secret(void *tls_ctx, - struct tls_connection *conn, - const u8 *key, size_t key_len) -{ - return -1; -} - - #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) /* Pre-shared secred requires a patch to openssl, so this function is * commented out unless explicitly needed for EAP-FAST in order to be able to * build this file with unmodified openssl. */ +#if (defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) +static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, + const SSL_CIPHER **cipher, void *arg) +#else /* OPENSSL_IS_BORINGSSL */ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg) +#endif /* OPENSSL_IS_BORINGSSL */ { struct tls_connection *conn = arg; int ret; +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) if (conn == NULL || conn->session_ticket_cb == NULL) return 0; @@ -2780,6 +4195,23 @@ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, conn->session_ticket_len, s->s3->client_random, s->s3->server_random, secret); +#else + unsigned char client_random[SSL3_RANDOM_SIZE]; + unsigned char server_random[SSL3_RANDOM_SIZE]; + + if (conn == NULL || conn->session_ticket_cb == NULL) + return 0; + + SSL_get_client_random(s, client_random, sizeof(client_random)); + SSL_get_server_random(s, server_random, sizeof(server_random)); + + ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx, + conn->session_ticket, + conn->session_ticket_len, + client_random, + server_random, secret); +#endif + os_free(conn->session_ticket); conn->session_ticket = NULL; @@ -2791,7 +4223,6 @@ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, } -#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data, int len, void *arg) { @@ -2817,62 +4248,6 @@ static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data, return 1; } -#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ -#ifdef SSL_OP_NO_TICKET -static void tls_hello_ext_cb(SSL *s, int client_server, int type, - unsigned char *data, int len, void *arg) -{ - struct tls_connection *conn = arg; - - if (conn == NULL || conn->session_ticket_cb == NULL) - return; - - wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__, - type, len); - - if (type == TLSEXT_TYPE_session_ticket && !client_server) { - os_free(conn->session_ticket); - conn->session_ticket = NULL; - - wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket " - "extension", data, len); - conn->session_ticket = os_malloc(len); - if (conn->session_ticket == NULL) - return; - - os_memcpy(conn->session_ticket, data, len); - conn->session_ticket_len = len; - } -} -#else /* SSL_OP_NO_TICKET */ -static int tls_hello_ext_cb(SSL *s, TLS_EXTENSION *ext, void *arg) -{ - struct tls_connection *conn = arg; - - if (conn == NULL || conn->session_ticket_cb == NULL) - return 0; - - wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__, - ext->type, ext->length); - - os_free(conn->session_ticket); - conn->session_ticket = NULL; - - if (ext->type == 35) { - wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket " - "extension", ext->data, ext->length); - conn->session_ticket = os_malloc(ext->length); - if (conn->session_ticket == NULL) - return SSL_AD_INTERNAL_ERROR; - - os_memcpy(conn->session_ticket, ext->data, ext->length); - conn->session_ticket_len = ext->length; - } - - return 0; -} -#endif /* SSL_OP_NO_TICKET */ -#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ #endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ @@ -2889,33 +4264,12 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx, if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb, conn) != 1) return -1; -#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE SSL_set_session_ticket_ext_cb(conn->ssl, tls_session_ticket_ext_cb, conn); -#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ -#ifdef SSL_OP_NO_TICKET - SSL_set_tlsext_debug_callback(conn->ssl, tls_hello_ext_cb); - SSL_set_tlsext_debug_arg(conn->ssl, conn); -#else /* SSL_OP_NO_TICKET */ - if (SSL_set_hello_extension_cb(conn->ssl, tls_hello_ext_cb, - conn) != 1) - return -1; -#endif /* SSL_OP_NO_TICKET */ -#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ } else { if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1) return -1; -#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE SSL_set_session_ticket_ext_cb(conn->ssl, NULL, NULL); -#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ -#ifdef SSL_OP_NO_TICKET - SSL_set_tlsext_debug_callback(conn->ssl, NULL); - SSL_set_tlsext_debug_arg(conn->ssl, conn); -#else /* SSL_OP_NO_TICKET */ - if (SSL_set_hello_extension_cb(conn->ssl, NULL, NULL) != 1) - return -1; -#endif /* SSL_OP_NO_TICKET */ -#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ } return 0; @@ -2923,3 +4277,84 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx, return -1; #endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ } + + +int tls_get_library_version(char *buf, size_t buf_len) +{ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) + return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s", + OPENSSL_VERSION_TEXT, + OpenSSL_version(OPENSSL_VERSION)); +#else + return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s", + OPENSSL_VERSION_TEXT, + SSLeay_version(SSLEAY_VERSION)); +#endif +} + + +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data) +{ + SSL_SESSION *sess; + struct wpabuf *old; + + if (tls_ex_idx_session < 0) + goto fail; + sess = SSL_get_session(conn->ssl); + if (!sess) + goto fail; + old = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session); + if (old) { + wpa_printf(MSG_DEBUG, "OpenSSL: Replacing old success data %p", + old); + wpabuf_free(old); + } + if (SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1) + goto fail; + + wpa_printf(MSG_DEBUG, "OpenSSL: Stored success data %p", data); + conn->success_data = 1; + return; + +fail: + wpa_printf(MSG_INFO, "OpenSSL: Failed to store success data"); + wpabuf_free(data); +} + + +void tls_connection_set_success_data_resumed(struct tls_connection *conn) +{ + wpa_printf(MSG_DEBUG, + "OpenSSL: Success data accepted for resumed session"); + conn->success_data = 1; +} + + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) +{ + SSL_SESSION *sess; + + if (tls_ex_idx_session < 0 || + !(sess = SSL_get_session(conn->ssl))) + return NULL; + return SSL_SESSION_get_ex_data(sess, tls_ex_idx_session); +} + + +void tls_connection_remove_session(struct tls_connection *conn) +{ + SSL_SESSION *sess; + + sess = SSL_get_session(conn->ssl); + if (!sess) + return; + + if (SSL_CTX_remove_session(conn->ssl_ctx, sess) != 1) + wpa_printf(MSG_DEBUG, + "OpenSSL: Session was not cached"); + else + wpa_printf(MSG_DEBUG, + "OpenSSL: Removed cached session to disable session resumption"); +}