/*
- * WPA Supplicant / SSL/TLS interface functions for openssl
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * SSL/TLS interface functions for OpenSSL
+ * Copyright (c) 2004-2010, 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
#endif /* OPENSSL_NO_ENGINE */
#include "common.h"
+#include "crypto.h"
#include "tls.h"
#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
#define OPENSSL_d2i_TYPE unsigned char **
#endif
+#ifdef SSL_F_SSL_SET_SESSION_TICKET_EXT
+#ifdef SSL_OP_NO_TICKET
+/*
+ * 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.
+ */
+#define CONFIG_OPENSSL_TICKET_OVERRIDE
+#endif
+#endif
+
static int tls_openssl_ref_count = 0;
+struct tls_global {
+ void (*event_cb)(void *ctx, enum tls_event ev,
+ union tls_event_data *data);
+ void *cb_ctx;
+};
+
+static struct tls_global *tls_global = NULL;
+
+
struct tls_connection {
SSL *ssl;
BIO *ssl_in, *ssl_out;
/* SessionTicket received from OpenSSL hello_extension_cb (server) */
u8 *session_ticket;
size_t session_ticket_len;
+
+ unsigned int ca_cert_verify:1;
+ unsigned int cert_probe:1;
+ unsigned int server_cert_only:1;
+
+ u8 srv_cert_hash[32];
};
* MinGW does not yet include all the needed definitions for CryptoAPI, so
* define here whatever extra is needed.
*/
-#define CALG_SSL3_SHAMD5 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SSL3SHAMD5)
#define CERT_SYSTEM_STORE_CURRENT_USER (1 << 16)
#define CERT_STORE_READONLY_FLAG 0x00008000
#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000
-#define CRYPT_ACQUIRE_COMPARE_KEY_FLAG 0x00000004
-
-static BOOL WINAPI
-(*CryptAcquireCertificatePrivateKey)(PCCERT_CONTEXT pCert, DWORD dwFlags,
- void *pvReserved, HCRYPTPROV *phCryptProv,
- DWORD *pdwKeySpec, BOOL *pfCallerFreeProv)
-= NULL; /* to be loaded from crypt32.dll */
-
-static PCCERT_CONTEXT WINAPI
-(*CertEnumCertificatesInStore)(HCERTSTORE hCertStore,
- PCCERT_CONTEXT pPrevCertContext)
-= NULL; /* to be loaded from crypt32.dll */
-
-static int mingw_load_crypto_func(void)
-{
- HINSTANCE dll;
-
- /* MinGW does not yet have full CryptoAPI support, so load the needed
- * function here. */
-
- if (CryptAcquireCertificatePrivateKey)
- return 0;
-
- dll = LoadLibrary("crypt32");
- if (dll == NULL) {
- wpa_printf(MSG_DEBUG, "CryptoAPI: Could not load crypt32 "
- "library");
- return -1;
- }
-
- CryptAcquireCertificatePrivateKey = GetProcAddress(
- dll, "CryptAcquireCertificatePrivateKey");
- if (CryptAcquireCertificatePrivateKey == NULL) {
- wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get "
- "CryptAcquireCertificatePrivateKey() address from "
- "crypt32 library");
- return -1;
- }
-
- CertEnumCertificatesInStore = (void *) GetProcAddress(
- dll, "CertEnumCertificatesInStore");
- if (CertEnumCertificatesInStore == NULL) {
- wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get "
- "CertEnumCertificatesInStore() address from "
- "crypt32 library");
- return -1;
- }
-
- return 0;
-}
-
-#else /* __MINGW32_VERSION */
-
-static int mingw_load_crypto_func(void)
-{
- return 0;
-}
#endif /* __MINGW32_VERSION */
goto err;
}
- if (mingw_load_crypto_func())
- goto err;
-
if (!CryptAcquireCertificatePrivateKey(priv->cert,
CRYPT_ACQUIRE_COMPARE_KEY_FLAG,
NULL, &priv->crypt_prov,
WCHAR *wstore;
#endif /* UNICODE */
- if (mingw_load_crypto_func())
- return -1;
-
if (name == NULL || strncmp(name, "cert_store://", 13) != 0)
return -1;
SSL_CTX *ssl;
if (tls_openssl_ref_count == 0) {
+ tls_global = os_zalloc(sizeof(*tls_global));
+ if (tls_global == 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)) {
+ wpa_printf(MSG_ERROR, "Failed to enable FIPS "
+ "mode");
+ ERR_load_crypto_strings();
+ ERR_print_errors_fp(stderr);
+ return NULL;
+ } else
+ wpa_printf(MSG_INFO, "Running in FIPS mode");
+ }
+#else /* OPENSSL_FIPS */
+ if (conf && conf->fips_mode) {
+ wpa_printf(MSG_ERROR, "FIPS mode requested, but not "
+ "supported");
+ return NULL;
+ }
+#endif /* OPENSSL_FIPS */
+#endif /* CONFIG_FIPS */
SSL_load_error_strings();
SSL_library_init();
+#ifndef OPENSSL_NO_SHA256
+ EVP_add_digest(EVP_sha256());
+#endif /* OPENSSL_NO_SHA256 */
/* TODO: if /dev/urandom is available, PRNG is seeded
* automatically. If this is not the case, random data should
* be added here. */
#ifdef PKCS12_FUNCS
+#ifndef OPENSSL_NO_RC2
+ /*
+ * 40-bit RC2 is commonly used in PKCS#12 files, so enable it.
+ * This is enabled by PKCS12_PBE_add() in OpenSSL 0.9.8
+ * versions, but it looks like OpenSSL 1.0.0 does not do that
+ * anymore.
+ */
+ EVP_add_cipher(EVP_rc2_40_cbc());
+#endif /* OPENSSL_NO_RC2 */
PKCS12_PBE_add();
#endif /* PKCS12_FUNCS */
}
ERR_remove_state(0);
ERR_free_strings();
EVP_cleanup();
+ os_free(tls_global);
+ tls_global = NULL;
}
}
}
+static enum tls_fail_reason openssl_tls_fail_reason(int err)
+{
+ switch (err) {
+ case X509_V_ERR_CERT_REVOKED:
+ return TLS_FAIL_REVOKED;
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ case X509_V_ERR_CRL_NOT_YET_VALID:
+ return TLS_FAIL_NOT_YET_VALID;
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ case X509_V_ERR_CRL_HAS_EXPIRED:
+ return TLS_FAIL_EXPIRED;
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ case X509_V_ERR_UNABLE_TO_GET_CRL:
+ case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+ case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+ case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+ case X509_V_ERR_INVALID_CA:
+ return TLS_FAIL_UNTRUSTED;
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+ case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+ case X509_V_ERR_CERT_UNTRUSTED:
+ case X509_V_ERR_CERT_REJECTED:
+ return TLS_FAIL_BAD_CERTIFICATE;
+ default:
+ return TLS_FAIL_UNSPECIFIED;
+ }
+}
+
+
+static struct wpabuf * get_x509_cert(X509 *cert)
+{
+ struct wpabuf *buf;
+ u8 *tmp;
+
+ int cert_len = i2d_X509(cert, NULL);
+ if (cert_len <= 0)
+ return NULL;
+
+ buf = wpabuf_alloc(cert_len);
+ if (buf == NULL)
+ return NULL;
+
+ tmp = wpabuf_put(buf, cert_len);
+ i2d_X509(cert, &tmp);
+ return buf;
+}
+
+
+static void openssl_tls_fail_event(struct tls_connection *conn,
+ X509 *err_cert, int err, int depth,
+ const char *subject, const char *err_str,
+ enum tls_fail_reason reason)
+{
+ union tls_event_data ev;
+ struct wpabuf *cert = NULL;
+
+ if (tls_global->event_cb == NULL)
+ return;
+
+ cert = get_x509_cert(err_cert);
+ os_memset(&ev, 0, sizeof(ev));
+ ev.cert_fail.reason = reason != TLS_FAIL_UNSPECIFIED ?
+ reason : openssl_tls_fail_reason(err);
+ ev.cert_fail.depth = depth;
+ 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);
+ wpabuf_free(cert);
+}
+
+
+static void openssl_tls_cert_event(struct tls_connection *conn,
+ X509 *err_cert, int depth,
+ const char *subject)
+{
+ struct wpabuf *cert = NULL;
+ union tls_event_data ev;
+#ifdef CONFIG_SHA256
+ u8 hash[32];
+#endif /* CONFIG_SHA256 */
+
+ if (tls_global->event_cb == NULL)
+ return;
+
+ os_memset(&ev, 0, sizeof(ev));
+ if (conn->cert_probe) {
+ cert = get_x509_cert(err_cert);
+ ev.peer_cert.cert = cert;
+ }
+#ifdef CONFIG_SHA256
+ if (cert) {
+ 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) {
+ ev.peer_cert.hash = hash;
+ ev.peer_cert.hash_len = sizeof(hash);
+ }
+ }
+#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);
+ wpabuf_free(cert);
+}
+
+
static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
char buf[256];
SSL *ssl;
struct tls_connection *conn;
char *match, *altmatch;
+ const char *err_str;
err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
err = X509_STORE_CTX_get_error(x509_ctx);
match = conn ? conn->subject_match : NULL;
altmatch = conn ? conn->altsubject_match : NULL;
- if (!preverify_ok) {
- wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
- " error %d (%s) depth %d for '%s'", err,
- X509_verify_cert_error_string(err), depth, buf);
- } else {
- wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - "
- "preverify_ok=%d err=%d (%s) depth=%d buf='%s'",
- preverify_ok, err,
- X509_verify_cert_error_string(err), depth, buf);
- if (depth == 0 && match && os_strstr(buf, match) == NULL) {
- wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
- "match with '%s'", buf, match);
- preverify_ok = 0;
- } else if (depth == 0 && altmatch &&
- !tls_match_altsubject(err_cert, altmatch)) {
- wpa_printf(MSG_WARNING, "TLS: altSubjectName match "
- "'%s' not found", altmatch);
+ if (!preverify_ok && !conn->ca_cert_verify)
+ preverify_ok = 1;
+ if (!preverify_ok && depth > 0 && conn->server_cert_only)
+ preverify_ok = 1;
+
+ err_str = X509_verify_cert_error_string(err);
+
+#ifdef CONFIG_SHA256
+ if (preverify_ok && depth == 0 && conn->server_cert_only) {
+ struct wpabuf *cert;
+ cert = get_x509_cert(err_cert);
+ if (!cert) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Could not fetch "
+ "server certificate data");
preverify_ok = 0;
+ } else {
+ u8 hash[32];
+ const u8 *addr[1];
+ size_t len[1];
+ addr[0] = wpabuf_head(cert);
+ len[0] = wpabuf_len(cert);
+ if (sha256_vector(1, addr, len, hash) < 0 ||
+ os_memcmp(conn->srv_cert_hash, hash, 32) != 0) {
+ err_str = "Server certificate mismatch";
+ err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
+ preverify_ok = 0;
+ }
+ wpabuf_free(cert);
}
}
+#endif /* CONFIG_SHA256 */
+
+ if (!preverify_ok) {
+ wpa_printf(MSG_WARNING, "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,
+ err_str, TLS_FAIL_UNSPECIFIED);
+ return preverify_ok;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - 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 "
+ "match with '%s'", buf, match);
+ preverify_ok = 0;
+ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "Subject mismatch",
+ TLS_FAIL_SUBJECT_MISMATCH);
+ } else if (depth == 0 && altmatch &&
+ !tls_match_altsubject(err_cert, altmatch)) {
+ wpa_printf(MSG_WARNING, "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
+ 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 "
+ "on probe-only run");
+ preverify_ok = 0;
+ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "Server certificate chain probe",
+ TLS_FAIL_SERVER_CHAIN_PROBE);
+ }
return preverify_ok;
}
return -1;
}
+ SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+ conn->ca_cert_verify = 1;
+
+ if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Probe for server certificate "
+ "chain");
+ conn->cert_probe = 1;
+ conn->ca_cert_verify = 0;
+ return 0;
+ }
+
+ if (ca_cert && os_strncmp(ca_cert, "hash://", 7) == 0) {
+#ifdef CONFIG_SHA256
+ const char *pos = ca_cert + 7;
+ if (os_strncmp(pos, "server/sha256/", 14) != 0) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Unsupported ca_cert "
+ "hash value '%s'", ca_cert);
+ return -1;
+ }
+ pos += 14;
+ if (os_strlen(pos) != 32 * 2) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Unexpected SHA256 "
+ "hash length in ca_cert '%s'", ca_cert);
+ return -1;
+ }
+ if (hexstr2bin(pos, conn->srv_cert_hash, 32) < 0) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Invalid SHA256 hash "
+ "value in ca_cert '%s'", ca_cert);
+ return -1;
+ }
+ conn->server_cert_only = 1;
+ wpa_printf(MSG_DEBUG, "OpenSSL: Checking only server "
+ "certificate match");
+ return 0;
+#else /* CONFIG_SHA256 */
+ wpa_printf(MSG_INFO, "No SHA256 included in the build - "
+ "cannot validate server certificate hash");
+ return -1;
+#endif /* CONFIG_SHA256 */
+ }
+
if (ca_cert_blob) {
X509 *cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ca_cert_blob,
ca_cert_blob_len);
X509_free(cert);
wpa_printf(MSG_DEBUG, "OpenSSL: %s - added ca_cert_blob "
"to certificate store", __func__);
- SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
return 0;
}
0) {
wpa_printf(MSG_DEBUG, "OpenSSL: Added CA certificates from "
"system certificate store");
- SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
return 0;
}
#endif /* CONFIG_NATIVE_WINDOWS */
"certificate(s) loaded");
tls_get_errors(ssl_ctx);
}
- SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
#else /* OPENSSL_NO_STDIO */
wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
__func__);
} else {
/* No ca_cert configured - do not try to verify server
* certificate */
- SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
+ conn->ca_cert_verify = 0;
}
return 0;
int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
int verify_peer)
{
+ static int counter = 0;
+
if (conn == NULL)
return -1;
if (verify_peer) {
+ conn->ca_cert_verify = 1;
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
SSL_VERIFY_CLIENT_ONCE, tls_verify_cb);
} else {
+ conn->ca_cert_verify = 0;
SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
}
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));
+
return 0;
}
}
-u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn,
- const u8 *in_data, size_t in_len,
- size_t *out_len, u8 **appl_data,
- size_t *appl_data_len)
+static struct wpabuf *
+openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data,
+ int server)
{
int res;
- u8 *out_data;
-
- if (appl_data)
- *appl_data = NULL;
+ struct wpabuf *out_data;
/*
* Give TLS handshake data from the server (if available) to OpenSSL
* for processing.
*/
if (in_data &&
- BIO_write(conn->ssl_in, in_data, in_len) < 0) {
+ BIO_write(conn->ssl_in, wpabuf_head(in_data), wpabuf_len(in_data))
+ < 0) {
tls_show_errors(MSG_INFO, __func__,
"Handshake failed - BIO_write");
return NULL;
}
/* Initiate TLS handshake or continue the existing handshake */
- res = SSL_connect(conn->ssl);
+ if (server)
+ res = SSL_accept(conn->ssl);
+ else
+ res = SSL_connect(conn->ssl);
if (res != 1) {
int err = SSL_get_error(conn->ssl, res);
if (err == SSL_ERROR_WANT_READ)
/* Get the TLS handshake data to be sent to the server */
res = BIO_ctrl_pending(conn->ssl_out);
wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res);
- out_data = os_malloc(res == 0 ? 1 : res);
+ out_data = wpabuf_alloc(res);
if (out_data == NULL) {
wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for "
"handshake output (%d bytes)", res);
tls_show_errors(MSG_INFO, __func__,
"BIO_reset failed");
}
- *out_len = 0;
return NULL;
}
- res = res == 0 ? 0 : BIO_read(conn->ssl_out, out_data, res);
+ res = res == 0 ? 0 : BIO_read(conn->ssl_out, wpabuf_mhead(out_data),
+ res);
if (res < 0) {
tls_show_errors(MSG_INFO, __func__,
"Handshake failed - BIO_read");
tls_show_errors(MSG_INFO, __func__,
"BIO_reset failed");
}
- *out_len = 0;
+ wpabuf_free(out_data);
return NULL;
}
- *out_len = res;
-
- if (SSL_is_init_finished(conn->ssl) && appl_data) {
- *appl_data = os_malloc(in_len);
- if (*appl_data) {
- res = SSL_read(conn->ssl, *appl_data, in_len);
- if (res < 0) {
- tls_show_errors(MSG_INFO, __func__,
- "Failed to read possible "
- "Application Data");
- os_free(*appl_data);
- *appl_data = NULL;
- } else {
- *appl_data_len = res;
- wpa_hexdump_key(MSG_MSGDUMP, "SSL: Application"
- " Data in Finish message",
- *appl_data, *appl_data_len);
- }
- }
- }
+ wpabuf_put(out_data, res);
return out_data;
}
-u8 * tls_connection_server_handshake(void *ssl_ctx,
- struct tls_connection *conn,
- const u8 *in_data, size_t in_len,
- size_t *out_len)
+static struct wpabuf *
+openssl_get_appl_data(struct tls_connection *conn, size_t max_len)
{
+ struct wpabuf *appl_data;
int res;
- u8 *out_data;
- char buf[10];
- if (in_data &&
- BIO_write(conn->ssl_in, in_data, in_len) < 0) {
- tls_show_errors(MSG_INFO, __func__,
- "Handshake failed - BIO_write");
+ appl_data = wpabuf_alloc(max_len + 100);
+ if (appl_data == NULL)
return NULL;
- }
-
- res = SSL_read(conn->ssl, buf, sizeof(buf));
- if (res >= 0) {
- wpa_printf(MSG_DEBUG, "SSL: Unexpected data from SSL_read "
- "(res=%d)", res);
- }
- res = BIO_ctrl_pending(conn->ssl_out);
- wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res);
- out_data = os_malloc(res == 0 ? 1 : res);
- if (out_data == NULL) {
- wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for "
- "handshake output (%d bytes)", res);
- if (BIO_reset(conn->ssl_out) < 0) {
- tls_show_errors(MSG_INFO, __func__,
- "BIO_reset failed");
- }
- *out_len = 0;
- return NULL;
- }
- res = res == 0 ? 0 : BIO_read(conn->ssl_out, out_data, res);
+ res = SSL_read(conn->ssl, wpabuf_mhead(appl_data),
+ wpabuf_size(appl_data));
if (res < 0) {
- tls_show_errors(MSG_INFO, __func__,
- "Handshake failed - BIO_read");
- if (BIO_reset(conn->ssl_out) < 0) {
+ int err = SSL_get_error(conn->ssl, res);
+ if (err == SSL_ERROR_WANT_READ ||
+ err == SSL_ERROR_WANT_WRITE) {
+ wpa_printf(MSG_DEBUG, "SSL: No Application Data "
+ "included");
+ } else {
tls_show_errors(MSG_INFO, __func__,
- "BIO_reset failed");
+ "Failed to read possible "
+ "Application Data");
}
- *out_len = 0;
+ wpabuf_free(appl_data);
return NULL;
}
- *out_len = res;
+
+ wpabuf_put(appl_data, res);
+ wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application Data in Finished "
+ "message", appl_data);
+
+ return appl_data;
+}
+
+
+static struct wpabuf *
+openssl_connection_handshake(struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data, int server)
+{
+ struct wpabuf *out_data;
+
+ if (appl_data)
+ *appl_data = NULL;
+
+ out_data = openssl_handshake(conn, in_data, server);
+ if (out_data == NULL)
+ return NULL;
+
+ if (SSL_is_init_finished(conn->ssl) && appl_data && in_data)
+ *appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data));
+
return out_data;
}
-int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn,
- const u8 *in_data, size_t in_len,
- u8 *out_data, size_t out_len)
+struct wpabuf *
+tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+ return openssl_connection_handshake(conn, in_data, appl_data, 0);
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data,
+ struct wpabuf **appl_data)
+{
+ return openssl_connection_handshake(conn, in_data, appl_data, 1);
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
{
int res;
+ struct wpabuf *buf;
if (conn == NULL)
- return -1;
+ return NULL;
/* Give plaintext data for OpenSSL to encrypt into the TLS tunnel. */
if ((res = BIO_reset(conn->ssl_in)) < 0 ||
(res = BIO_reset(conn->ssl_out)) < 0) {
tls_show_errors(MSG_INFO, __func__, "BIO_reset failed");
- return res;
+ return NULL;
}
- res = SSL_write(conn->ssl, in_data, in_len);
+ res = SSL_write(conn->ssl, wpabuf_head(in_data), wpabuf_len(in_data));
if (res < 0) {
tls_show_errors(MSG_INFO, __func__,
"Encryption failed - SSL_write");
- return res;
+ return NULL;
}
/* Read encrypted data to be sent to the server */
- res = BIO_read(conn->ssl_out, out_data, out_len);
+ buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
+ if (buf == NULL)
+ return NULL;
+ res = BIO_read(conn->ssl_out, wpabuf_mhead(buf), wpabuf_size(buf));
if (res < 0) {
tls_show_errors(MSG_INFO, __func__,
"Encryption failed - BIO_read");
- return res;
+ wpabuf_free(buf);
+ return NULL;
}
+ wpabuf_put(buf, res);
- return res;
+ return buf;
}
-int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn,
- const u8 *in_data, size_t in_len,
- u8 *out_data, size_t out_len)
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+ struct tls_connection *conn,
+ const struct wpabuf *in_data)
{
int res;
+ struct wpabuf *buf;
/* Give encrypted data from TLS tunnel for OpenSSL to decrypt. */
- res = BIO_write(conn->ssl_in, in_data, in_len);
+ res = BIO_write(conn->ssl_in, wpabuf_head(in_data),
+ wpabuf_len(in_data));
if (res < 0) {
tls_show_errors(MSG_INFO, __func__,
"Decryption failed - BIO_write");
- return res;
+ return NULL;
}
if (BIO_reset(conn->ssl_out) < 0) {
tls_show_errors(MSG_INFO, __func__, "BIO_reset failed");
- return res;
+ return NULL;
}
/* Read decrypted data for further processing */
- res = SSL_read(conn->ssl, out_data, out_len);
+ /*
+ * Even though we try to disable TLS compression, it is possible that
+ * this cannot be done with all TLS libraries. Add extra buffer space
+ * to handle the possibility of the decrypted data being longer than
+ * input data.
+ */
+ buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+ if (buf == NULL)
+ return NULL;
+ res = SSL_read(conn->ssl, wpabuf_mhead(buf), wpabuf_size(buf));
if (res < 0) {
tls_show_errors(MSG_INFO, __func__,
"Decryption failed - SSL_read");
- return res;
+ wpabuf_free(buf);
+ return NULL;
}
+ wpabuf_put(buf, res);
- return res;
+ return buf;
}
}
-#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC)
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
/* ClientHello TLS extensions require 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. */
int ext_type, const u8 *data,
size_t data_len)
{
- if (conn == NULL || conn->ssl == NULL)
+ 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;
}
-#endif /* EAP_FAST || EAP_FAST_DYNAMIC */
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
}
-int tls_connection_ia_send_phase_finished(void *tls_ctx,
- struct tls_connection *conn,
- int final,
- u8 *out_data, size_t out_len)
+struct wpabuf * tls_connection_ia_send_phase_finished(
+ void *tls_ctx, struct tls_connection *conn, int final)
{
- return -1;
+ return NULL;
}
}
-#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC)
+#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. */
}
+#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
+static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data,
+ int len, void *arg)
+{
+ struct tls_connection *conn = arg;
+
+ if (conn == NULL || conn->session_ticket_cb == NULL)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s: length=%d", __func__, len);
+
+ 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 0;
+
+ os_memcpy(conn->session_ticket, data, len);
+ conn->session_ticket_len = len;
+
+ 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)
return 0;
}
#endif /* SSL_OP_NO_TICKET */
-#endif /* EAP_FAST || EAP_FAST_DYNAMIC */
+#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
int tls_connection_set_session_ticket_cb(void *tls_ctx,
tls_session_ticket_cb cb,
void *ctx)
{
-#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC)
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
conn->session_ticket_cb = cb;
conn->session_ticket_cb_ctx = 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);
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);
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;
-#else /* EAP_FAST || EAP_FAST_DYNAMIC */
+#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
return -1;
-#endif /* EAP_FAST || EAP_FAST_DYNAMIC */
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
}