Merged the hostap_2.6 updates, and the Leap of Faith work, from the hostap_update...
[mech_eap.git] / libeap / src / crypto / tls_openssl.c
index d155c09..9db8095 100644 (file)
 /*
  * SSL/TLS interface functions for OpenSSL
- * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
  *
- * 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 <openssl/ssl.h>
 #include <openssl/err.h>
+#include <openssl/opensslv.h>
 #include <openssl/pkcs12.h>
 #include <openssl/x509v3.h>
 #ifndef OPENSSL_NO_ENGINE
 #include <openssl/engine.h>
 #endif /* OPENSSL_NO_ENGINE */
+#ifndef OPENSSL_NO_DSA
+#include <openssl/dsa.h>
+#endif
+#ifndef OPENSSL_NO_DH
+#include <openssl/dh.h>
+#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 <openssl/ocsp.h>
+#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 <openssl/pem.h>
+#include <keystore/keystore_get.h>
+
+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,38 @@ 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 +517,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 +618,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 +632,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 +680,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 +688,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 +731,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 +777,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 +818,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 +864,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,70 +943,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) {
-               ERR_remove_state(0);
+#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]",
@@ -808,20 +1139,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 */
@@ -860,17 +1211,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 */
 }
 
 
@@ -888,15 +1241,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__,
@@ -905,7 +1350,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
@@ -942,10 +1390,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);
 }
@@ -967,7 +1425,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;
 }
 
 
@@ -976,7 +1434,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);
 
@@ -989,6 +1448,8 @@ static int tls_match_altsubject_component(X509 *cert, int type,
                        found++;
        }
 
+       sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
+
        return found;
 }
 
@@ -1036,6 +1497,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) {
@@ -1100,8 +1669,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);
@@ -1112,7 +1682,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);
 }
 
@@ -1123,15 +1693,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;
        }
@@ -1149,11 +1726,58 @@ 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]);
 }
 
 
+static void debug_print_cert(X509 *cert, const char *title);
+
 static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
 {
        char buf[256];
@@ -1161,10 +1785,16 @@ 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;
+
+    // debug_print_cert(err_cert, "\n\n***** tls_verify_cb:\n");
+
        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,
@@ -1172,43 +1802,90 @@ 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 (!preverify_ok && !conn->ca_cert_verify)
-               preverify_ok = 1;
-       if (!preverify_ok && depth > 0 && conn->server_cert_only)
-               preverify_ok = 1;
+       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;
 
-       err_str = X509_verify_cert_error_string(err);
+       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);
 
-#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);
-               }
+
+       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) {
+        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, "tls_verify_cb: OpenSSL: Ignore certificate validity "
+                          "time mismatch");
+               preverify_ok = 1;
        }
+
+       err_str = X509_verify_cert_error_string(err);
+
+#ifdef CONFIG_SHA256
+       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, "tls_verify_cb: 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) {
-               wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
+               wpa_printf(MSG_WARNING, "tls_verify_cb: TLS: Certificate verification failed,"
                           " error %d (%s) depth %d for '%s'", err, err_str,
                           depth, buf);
                openssl_tls_fail_event(conn, err_cert, err, depth, buf,
@@ -1216,12 +1893,12 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
                return preverify_ok;
        }
 
-       wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - preverify_ok=%d "
+       wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb(exit) - preverify_ok=%d "
                   "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
                   preverify_ok, err, err_str,
                   conn->ca_cert_verify, depth, buf);
        if (depth == 0 && match && os_strstr(buf, match) == NULL) {
-               wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
+               wpa_printf(MSG_WARNING, "tls_verify_cb: TLS: Subject '%s' did not "
                           "match with '%s'", buf, match);
                preverify_ok = 0;
                openssl_tls_fail_event(conn, err_cert, err, depth, buf,
@@ -1229,17 +1906,33 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
                                       TLS_FAIL_SUBJECT_MISMATCH);
        } else if (depth == 0 && altmatch &&
                   !tls_match_altsubject(err_cert, altmatch)) {
-               wpa_printf(MSG_WARNING, "TLS: altSubjectName match "
+               wpa_printf(MSG_WARNING, "tls_verify_cb: TLS: altSubjectName match "
                           "'%s' not found", altmatch);
                preverify_ok = 0;
                openssl_tls_fail_event(conn, err_cert, err, depth, buf,
                                       "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_verify_cb: 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_verify_cb: 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);
 
        if (conn->cert_probe && preverify_ok && depth == 0) {
-               wpa_printf(MSG_DEBUG, "OpenSSL: Reject server certificate "
+               wpa_printf(MSG_DEBUG, "tls_verify_cb: OpenSSL: Reject server certificate "
                           "on probe-only run");
                preverify_ok = 0;
                openssl_tls_fail_event(conn, err_cert, err, depth, buf,
@@ -1247,18 +1940,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__,
@@ -1284,30 +2007,36 @@ 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 "
-                          "chain");
+                          "chain; setting conn->ca_cert_verify=0");
                conn->cert_probe = 1;
                conn->ca_cert_verify = 0;
                return 0;
@@ -1344,7 +2073,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__,
@@ -1352,7 +2082,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 "
@@ -1374,6 +2105,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) {
@@ -1390,7 +2161,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__);
@@ -1399,7 +2170,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",
@@ -1409,6 +2180,7 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
        } else {
                /* No ca_cert configured - do not try to verify server
                 * certificate */
+               wpa_printf(MSG_DEBUG, "OpenSSL: tls_connection_ca_cert: No ca_cert; setting conn->ca_cert_verify=0");
                conn->ca_cert_verify = 0;
        }
 
@@ -1416,8 +2188,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)
                {
@@ -1445,7 +2219,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 "
@@ -1463,7 +2238,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;
@@ -1481,14 +2258,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;
@@ -1499,24 +2326,30 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
                               SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
                               SSL_VERIFY_CLIENT_ONCE, tls_verify_cb);
        } else {
+               wpa_printf(MSG_DEBUG, "OpenSSL: tls_connection_set_verify: !verify_peer; setting conn->ca_cert_verify=0");
                conn->ca_cert_verify = 0;
                SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
        }
 
+       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;
 }
@@ -1530,6 +2363,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) {
@@ -1544,26 +2388,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 */
@@ -1572,14 +2432,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__,
@@ -1607,7 +2471,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;
@@ -1619,6 +2483,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");
@@ -1636,7 +2502,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);
@@ -1648,13 +2514,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));
@@ -1664,26 +2581,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;
@@ -1702,7 +2622,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 "
@@ -1712,20 +2632,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 "
@@ -1750,9 +2670,13 @@ static int tls_engine_get_cert(struct tls_connection *conn,
 
        if (!ENGINE_ctrl_cmd(conn->engine, "LOAD_CERT_CTRL",
                             0, &params, 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) {
@@ -1792,27 +2716,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 "
@@ -1831,6 +2756,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 */
@@ -1841,7 +2768,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");
@@ -1861,14 +2788,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;
 
@@ -1894,10 +2821,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,
@@ -1907,10 +2830,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,
@@ -1920,12 +2839,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");
@@ -1944,10 +2860,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,
@@ -1956,17 +2868,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");
@@ -1985,15 +2893,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");
@@ -2005,9 +2913,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)
@@ -2029,7 +2939,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);
@@ -2039,7 +2949,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");
@@ -2124,7 +3034,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)
@@ -2133,6 +3043,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;
 
@@ -2198,34 +3109,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 */
 }
 
 
@@ -2240,7 +3292,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__,
@@ -2346,9 +3398,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;
 }
@@ -2451,20 +3524,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;
 
@@ -2492,13 +3571,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;
 
@@ -2507,6 +3592,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");
@@ -2517,6 +3617,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)
 {
@@ -2553,15 +3669,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;
 }
@@ -2592,56 +3702,367 @@ 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))
-               return -1;
-
-       if (params->engine && params->cert_id) {
-               if (tls_connection_engine_client_cert(conn, params->cert_id))
+       } 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 (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,
@@ -2657,7 +4078,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;
 }
@@ -2666,7 +4124,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())) {
@@ -2674,98 +4133,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;
 
@@ -2774,6 +4201,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;
 
@@ -2785,7 +4229,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)
 {
@@ -2811,62 +4254,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 */
 
 
@@ -2883,33 +4270,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;
@@ -2917,3 +4283,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");
+}