diff options
author | sam <sam@FreeBSD.org> | 2006-03-07 05:47:04 +0000 |
---|---|---|
committer | sam <sam@FreeBSD.org> | 2006-03-07 05:47:04 +0000 |
commit | 84f883d618d0eca94f2e20b3d29474005c455cc6 (patch) | |
tree | 161a6132f61b543e3759ff389bacc462dacf3d72 /contrib/hostapd/tls_openssl.c | |
parent | f87f3cf9d3ac68ade33194941f85722ffdff2195 (diff) | |
parent | 8d55057fb42bf9070fd379acbcb6fc4ef793d2a7 (diff) | |
download | FreeBSD-src-84f883d618d0eca94f2e20b3d29474005c455cc6.zip FreeBSD-src-84f883d618d0eca94f2e20b3d29474005c455cc6.tar.gz |
This commit was generated by cvs2svn to compensate for changes in r156373,
which included commits to RCS files with non-trunk default branches.
Diffstat (limited to 'contrib/hostapd/tls_openssl.c')
-rw-r--r-- | contrib/hostapd/tls_openssl.c | 1614 |
1 files changed, 1439 insertions, 175 deletions
diff --git a/contrib/hostapd/tls_openssl.c b/contrib/hostapd/tls_openssl.c index 4e6ea53..08d5a80 100644 --- a/contrib/hostapd/tls_openssl.c +++ b/contrib/hostapd/tls_openssl.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / SSL/TLS interface functions for openssl - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * Copyright (c) 2004-2006, Jouni Malinen <jkmaline@cc.hut.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 @@ -15,21 +15,504 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> + +#ifndef CONFIG_SMARTCARD +#ifndef OPENSSL_NO_ENGINE +#define OPENSSL_NO_ENGINE +#endif +#endif + #include <openssl/ssl.h> #include <openssl/err.h> #include <openssl/pkcs12.h> +#include <openssl/x509v3.h> +#ifndef OPENSSL_NO_ENGINE +#include <openssl/engine.h> +#endif /* OPENSSL_NO_ENGINE */ #include "common.h" #include "tls.h" +#if OPENSSL_VERSION_NUMBER >= 0x0090800fL +#define OPENSSL_d2i_TYPE const unsigned char ** +#else +#define OPENSSL_d2i_TYPE unsigned char ** +#endif + +static int tls_openssl_ref_count = 0; struct tls_connection { SSL *ssl; BIO *ssl_in, *ssl_out; - char *subject_match; +#ifndef 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; + int read_alerts, write_alerts, failed; + + u8 *pre_shared_secret; + size_t pre_shared_secret_len; }; +#ifdef CONFIG_NO_STDOUT_DEBUG + +static void _tls_show_errors(void) +{ + unsigned long err; + + while ((err = ERR_get_error())) { + /* Just ignore the errors, since stdout is disabled */ + } +} +#define tls_show_errors(l, f, t) _tls_show_errors() + +#else /* CONFIG_NO_STDOUT_DEBUG */ + +static void tls_show_errors(int level, const char *func, const char *txt) +{ + unsigned long err; + + wpa_printf(level, "OpenSSL: %s - %s %s", + func, txt, ERR_error_string(ERR_get_error(), NULL)); + + while ((err = ERR_get_error())) { + wpa_printf(MSG_INFO, "OpenSSL: pending error: %s", + ERR_error_string(err, NULL)); + } +} + +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +#ifdef CONFIG_NATIVE_WINDOWS + +/* Windows CryptoAPI and access to certificate stores */ +#include <wincrypt.h> + +#ifdef __MINGW32_VERSION +/* + * 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 */ + + +struct cryptoapi_rsa_data { + const CERT_CONTEXT *cert; + HCRYPTPROV crypt_prov; + DWORD key_spec; + BOOL free_crypt_prov; +}; + + +static void cryptoapi_error(const char *msg) +{ + wpa_printf(MSG_INFO, "CryptoAPI: %s; err=%u", + msg, (unsigned int) GetLastError()); +} + + +static int cryptoapi_rsa_pub_enc(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); + return 0; +} + + +static int cryptoapi_rsa_pub_dec(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); + return 0; +} + + +static int cryptoapi_rsa_priv_enc(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + struct cryptoapi_rsa_data *priv = + (struct cryptoapi_rsa_data *) rsa->meth->app_data; + HCRYPTHASH hash; + DWORD hash_size, len, i; + unsigned char *buf = NULL; + int ret = 0; + + if (priv == NULL) { + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, + ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (padding != RSA_PKCS1_PADDING) { + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, + RSA_R_UNKNOWN_PADDING_TYPE); + return 0; + } + + if (flen != 16 /* MD5 */ + 20 /* SHA-1 */) { + wpa_printf(MSG_INFO, "%s - only MD5-SHA1 hash supported", + __func__); + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, + RSA_R_INVALID_MESSAGE_LENGTH); + return 0; + } + + if (!CryptCreateHash(priv->crypt_prov, CALG_SSL3_SHAMD5, 0, 0, &hash)) + { + cryptoapi_error("CryptCreateHash failed"); + return 0; + } + + len = sizeof(hash_size); + if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *) &hash_size, &len, + 0)) { + cryptoapi_error("CryptGetHashParam failed"); + goto err; + } + + if (hash_size != flen) { + wpa_printf(MSG_INFO, "CryptoAPI: Invalid hash size (%u != %d)", + (unsigned) hash_size, flen); + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, + RSA_R_INVALID_MESSAGE_LENGTH); + goto err; + } + if (!CryptSetHashParam(hash, HP_HASHVAL, (BYTE * ) from, 0)) { + cryptoapi_error("CryptSetHashParam failed"); + goto err; + } + + len = RSA_size(rsa); + buf = malloc(len); + if (buf == NULL) { + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!CryptSignHash(hash, priv->key_spec, NULL, 0, buf, &len)) { + cryptoapi_error("CryptSignHash failed"); + goto err; + } + + for (i = 0; i < len; i++) + to[i] = buf[len - i - 1]; + ret = len; + +err: + free(buf); + CryptDestroyHash(hash); + + return ret; +} + + +static int cryptoapi_rsa_priv_dec(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); + return 0; +} + + +static void cryptoapi_free_data(struct cryptoapi_rsa_data *priv) +{ + if (priv == NULL) + return; + if (priv->crypt_prov && priv->free_crypt_prov) + CryptReleaseContext(priv->crypt_prov, 0); + if (priv->cert) + CertFreeCertificateContext(priv->cert); + free(priv); +} + + +static int cryptoapi_finish(RSA *rsa) +{ + cryptoapi_free_data((struct cryptoapi_rsa_data *) rsa->meth->app_data); + free((void *) rsa->meth); + rsa->meth = NULL; + return 1; +} + + +static const CERT_CONTEXT * cryptoapi_find_cert(const char *name, DWORD store) +{ + HCERTSTORE cs; + const CERT_CONTEXT *ret = NULL; + + cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0, + store | CERT_STORE_OPEN_EXISTING_FLAG | + CERT_STORE_READONLY_FLAG, L"MY"); + if (cs == NULL) { + cryptoapi_error("Failed to open 'My system store'"); + return NULL; + } + + if (strncmp(name, "cert://", 7) == 0) { + unsigned short wbuf[255]; + MultiByteToWideChar(CP_ACP, 0, name + 7, -1, + wbuf, sizeof(wbuf)); + ret = CertFindCertificateInStore(cs, X509_ASN_ENCODING | + PKCS_7_ASN_ENCODING, + 0, CERT_FIND_SUBJECT_STR, + wbuf, NULL); + } else if (strncmp(name, "hash://", 7) == 0) { + CRYPT_HASH_BLOB blob; + int len; + const char *hash = name + 7; + unsigned char *buf; + + len = strlen(hash) / 2; + buf = malloc(len); + if (buf && hexstr2bin(hash, buf, len) == 0) { + blob.cbData = len; + blob.pbData = buf; + ret = CertFindCertificateInStore(cs, + X509_ASN_ENCODING | + PKCS_7_ASN_ENCODING, + 0, CERT_FIND_HASH, + &blob, NULL); + } + free(buf); + } + + CertCloseStore(cs, 0); + + return ret; +} + + +static int tls_cryptoapi_cert(SSL *ssl, const char *name) +{ + X509 *cert = NULL; + RSA *rsa = NULL, *pub_rsa; + struct cryptoapi_rsa_data *priv; + RSA_METHOD *rsa_meth; + + if (name == NULL || + (strncmp(name, "cert://", 7) != 0 && + strncmp(name, "hash://", 7) != 0)) + return -1; + + priv = malloc(sizeof(*priv)); + rsa_meth = malloc(sizeof(*rsa_meth)); + if (priv == NULL || rsa_meth == NULL) { + wpa_printf(MSG_WARNING, "CryptoAPI: Failed to allocate memory " + "for CryptoAPI RSA method"); + free(priv); + free(rsa_meth); + return -1; + } + memset(priv, 0, sizeof(*priv)); + memset(rsa_meth, 0, sizeof(*rsa_meth)); + + priv->cert = cryptoapi_find_cert(name, CERT_SYSTEM_STORE_CURRENT_USER); + if (priv->cert == NULL) { + priv->cert = cryptoapi_find_cert( + name, CERT_SYSTEM_STORE_LOCAL_MACHINE); + } + if (priv->cert == NULL) { + wpa_printf(MSG_INFO, "CryptoAPI: Could not find certificate " + "'%s'", name); + goto err; + } + + cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &priv->cert->pbCertEncoded, + priv->cert->cbCertEncoded); + if (cert == NULL) { + wpa_printf(MSG_INFO, "CryptoAPI: Could not process X509 DER " + "encoding"); + goto err; + } + + if (mingw_load_crypto_func()) + goto err; + + if (!CryptAcquireCertificatePrivateKey(priv->cert, + CRYPT_ACQUIRE_COMPARE_KEY_FLAG, + NULL, &priv->crypt_prov, + &priv->key_spec, + &priv->free_crypt_prov)) { + cryptoapi_error("Failed to acquire a private key for the " + "certificate"); + goto err; + } + + rsa_meth->name = "Microsoft CryptoAPI RSA Method"; + rsa_meth->rsa_pub_enc = cryptoapi_rsa_pub_enc; + rsa_meth->rsa_pub_dec = cryptoapi_rsa_pub_dec; + rsa_meth->rsa_priv_enc = cryptoapi_rsa_priv_enc; + rsa_meth->rsa_priv_dec = cryptoapi_rsa_priv_dec; + rsa_meth->finish = cryptoapi_finish; + rsa_meth->flags = RSA_METHOD_FLAG_NO_CHECK; + rsa_meth->app_data = (char *) priv; + + rsa = RSA_new(); + if (rsa == NULL) { + SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, + ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!SSL_use_certificate(ssl, cert)) + goto err; + pub_rsa = cert->cert_info->key->pkey->pkey.rsa; + X509_free(cert); + cert = NULL; + + rsa->n = BN_dup(pub_rsa->n); + rsa->e = BN_dup(pub_rsa->e); + if (!RSA_set_method(rsa, rsa_meth)) + goto err; + + if (!SSL_use_RSAPrivateKey(ssl, rsa)) + goto err; + RSA_free(rsa); + + return 0; + +err: + if (cert) + X509_free(cert); + if (rsa) + RSA_free(rsa); + else { + free(rsa_meth); + cryptoapi_free_data(priv); + } + return -1; +} + + +static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name) +{ + HCERTSTORE cs; + PCCERT_CONTEXT ctx = NULL; + X509 *cert; + char buf[128]; + + if (mingw_load_crypto_func()) + return -1; + + if (name == NULL || strncmp(name, "cert_store://", 13) != 0) + return -1; + + cs = CertOpenSystemStore(0, name + 13); + if (cs == NULL) { + wpa_printf(MSG_DEBUG, "%s: failed to open system cert store " + "'%s': error=%d", __func__, name + 13, + (int) GetLastError()); + return -1; + } + + while ((ctx = CertEnumCertificatesInStore(cs, ctx))) { + cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ctx->pbCertEncoded, + ctx->cbCertEncoded); + if (cert == NULL) { + wpa_printf(MSG_INFO, "CryptoAPI: Could not process " + "X509 DER encoding for CA cert"); + continue; + } + + X509_NAME_oneline(X509_get_subject_name(cert), buf, + sizeof(buf)); + 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)) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to add ca_cert to OpenSSL " + "certificate store"); + } + + X509_free(cert); + } + + if (!CertCloseStore(cs, 0)) { + wpa_printf(MSG_DEBUG, "%s: failed to close system cert store " + "'%s': error=%d", __func__, name + 13, + (int) GetLastError()); + } + + return 0; +} + + +#else /* CONFIG_NATIVE_WINDOWS */ + +static int tls_cryptoapi_cert(SSL *ssl, const char *name) +{ + return -1; +} + +#endif /* CONFIG_NATIVE_WINDOWS */ + + static void ssl_info_cb(const SSL *ssl, int where, int ret) { const char *str; @@ -50,10 +533,18 @@ static void ssl_info_cb(const SSL *ssl, int where, int ret) } else if (where & SSL_CB_ALERT) { wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s", where & SSL_CB_READ ? - "read (authentication server reported an error)" : + "read (remote end reported an error)" : "write (local SSL3 detected an error)", 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++; + } } else if (where & SSL_CB_EXIT && ret <= 0) { wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s", str, ret == 0 ? "failed" : "error", @@ -62,18 +553,160 @@ static void ssl_info_cb(const SSL *ssl, int where, int ret) } -void * tls_init(void) +#ifndef OPENSSL_NO_ENGINE +/** + * tls_engine_load_dynamic_generic - load any openssl engine + * @pre: an array of commands and values that load an engine initialized + * in the engine specific function + * @post: an array of commands and values that initialize an already loaded + * engine (or %NULL if not required) + * @id: the engine id of the engine to load (only required if post is not %NULL + * + * This function is a generic function that loads any openssl engine. + * + * Returns: 0 on success, -1 on failure + */ +static int tls_engine_load_dynamic_generic(const char *pre[], + const char *post[], const char *id) +{ + ENGINE *engine; + const char *dynamic_id = "dynamic"; + + engine = ENGINE_by_id(id); + if (engine) { + ENGINE_free(engine); + wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already " + "available", id); + return 0; + } + ERR_clear_error(); + + engine = ENGINE_by_id(dynamic_id); + if (engine == NULL) { + wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]", + dynamic_id, + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + /* Perform the pre commands. This will load the engine. */ + while (pre && pre[0]) { + wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", pre[0], pre[1]); + if (ENGINE_ctrl_cmd_string(engine, pre[0], pre[1], 0) == 0) { + wpa_printf(MSG_INFO, "ENGINE: ctrl cmd_string failed: " + "%s %s [%s]", pre[0], pre[1], + ERR_error_string(ERR_get_error(), NULL)); + ENGINE_free(engine); + return -1; + } + pre += 2; + } + + /* + * Free the reference to the "dynamic" engine. The loaded engine can + * now be looked up using ENGINE_by_id(). + */ + ENGINE_free(engine); + + engine = ENGINE_by_id(id); + if (engine == NULL) { + wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]", + id, ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + 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) { + wpa_printf(MSG_DEBUG, "ENGINE: ctrl cmd_string failed:" + " %s %s [%s]", post[0], post[1], + ERR_error_string(ERR_get_error(), NULL)); + ENGINE_remove(engine); + ENGINE_free(engine); + return -1; + } + post += 2; + } + ENGINE_free(engine); + + return 0; +} + + +/** + * tls_engine_load_dynamic_pkcs11 - load the pkcs11 engine provided by opensc + * @pkcs11_so_path: pksc11_so_path from the configuration + * @pcks11_module_path: pkcs11_module_path from the configuration + */ +static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path, + const char *pkcs11_module_path) +{ + char *engine_id = "pkcs11"; + const char *pre_cmd[] = { + "SO_PATH", pkcs11_so_path, + "ID", engine_id, + "LIST_ADD", "1", + /* "NO_VCHECK", "1", */ + "LOAD", NULL, + NULL, NULL + }; + const char *post_cmd[] = { + "MODULE_PATH", pkcs11_module_path, + NULL, NULL + }; + + if (!pkcs11_so_path || !pkcs11_module_path) + return 0; + + wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s", + pkcs11_so_path); + + return tls_engine_load_dynamic_generic(pre_cmd, post_cmd, engine_id); +} + + +/** + * tls_engine_load_dynamic_opensc - load the opensc engine provided by opensc + * @opensc_so_path: opensc_so_path from the configuration + */ +static int tls_engine_load_dynamic_opensc(const char *opensc_so_path) +{ + char *engine_id = "opensc"; + const char *pre_cmd[] = { + "SO_PATH", opensc_so_path, + "ID", engine_id, + "LIST_ADD", "1", + "LOAD", NULL, + NULL, NULL + }; + + if (!opensc_so_path) + return 0; + + wpa_printf(MSG_DEBUG, "ENGINE: Loading OpenSC Engine from %s", + opensc_so_path); + + return tls_engine_load_dynamic_generic(pre_cmd, NULL, engine_id); +} +#endif /* OPENSSL_NO_ENGINE */ + + +void * tls_init(const struct tls_config *conf) { SSL_CTX *ssl; - SSL_load_error_strings(); - SSL_library_init(); - /* TODO: if /dev/urandom is available, PRNG is seeded automatically. - * If this is not the case, random data should be added here. */ + if (tls_openssl_ref_count == 0) { + SSL_load_error_strings(); + SSL_library_init(); + /* 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 - PKCS12_PBE_add(); + PKCS12_PBE_add(); #endif /* PKCS12_FUNCS */ + } + tls_openssl_ref_count++; ssl = SSL_CTX_new(TLSv1_method()); if (ssl == NULL) @@ -81,6 +714,23 @@ void * tls_init(void) SSL_CTX_set_info_callback(ssl, ssl_info_cb); +#ifndef OPENSSL_NO_ENGINE + 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); + return NULL; + } + } +#endif /* OPENSSL_NO_ENGINE */ + return ssl; } @@ -89,8 +739,98 @@ void tls_deinit(void *ssl_ctx) { SSL_CTX *ssl = ssl_ctx; SSL_CTX_free(ssl); - ERR_free_strings(); - EVP_cleanup(); + + tls_openssl_ref_count--; + if (tls_openssl_ref_count == 0) { +#ifndef OPENSSL_NO_ENGINE + ENGINE_cleanup(); +#endif /* OPENSSL_NO_ENGINE */ + ERR_free_strings(); + EVP_cleanup(); + } +} + + +static int tls_engine_init(struct tls_connection *conn, const char *engine_id, + const char *pin, const char *key_id) +{ +#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(); + conn->engine = ENGINE_by_id(engine_id); + if (!conn->engine) { + wpa_printf(MSG_ERROR, "ENGINE: engine %s not available [%s]", + engine_id, ERR_error_string(ERR_get_error(), NULL)); + goto err; + } + if (ENGINE_init(conn->engine) != 1) { + wpa_printf(MSG_ERROR, "ENGINE: engine init failed " + "(engine: %s) [%s]", engine_id, + ERR_error_string(ERR_get_error(), NULL)); + goto err; + } + wpa_printf(MSG_DEBUG, "ENGINE: engine initialized"); + + if (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; + } + 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; + } + return 0; + +err: + if (conn->engine) { + ENGINE_free(conn->engine); + conn->engine = NULL; + } + + if (conn->private_key) { + EVP_PKEY_free(conn->private_key); + conn->private_key = NULL; + } + + return ret; +#else /* OPENSSL_NO_ENGINE */ + return 0; +#endif /* OPENSSL_NO_ENGINE */ +} + + +static void tls_engine_deinit(struct tls_connection *conn) +{ +#ifndef 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) { + ENGINE_finish(conn->engine); + conn->engine = NULL; + } +#endif /* OPENSSL_NO_ENGINE */ } @@ -119,22 +859,21 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) memset(conn, 0, sizeof(*conn)); conn->ssl = SSL_new(ssl); if (conn->ssl == NULL) { - wpa_printf(MSG_INFO, "TLS: Failed to initialize new SSL " - "connection: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Failed to initialize new SSL connection"); free(conn); return NULL; } + SSL_set_app_data(conn->ssl, conn); SSL_set_options(conn->ssl, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE); conn->ssl_in = BIO_new(BIO_s_mem()); if (!conn->ssl_in) { - wpa_printf(MSG_INFO, "SSL: Failed to create a new BIO for " - "ssl_in: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Failed to create a new BIO for ssl_in"); SSL_free(conn->ssl); free(conn); return NULL; @@ -142,9 +881,8 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) conn->ssl_out = BIO_new(BIO_s_mem()); if (!conn->ssl_out) { - wpa_printf(MSG_INFO, "SSL: Failed to create a new BIO for " - "ssl_out: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Failed to create a new BIO for ssl_out"); SSL_free(conn->ssl); BIO_free(conn->ssl_in); free(conn); @@ -161,8 +899,11 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return; + free(conn->pre_shared_secret); SSL_free(conn->ssl); + tls_engine_deinit(conn); free(conn->subject_match); + free(conn->altsubject_match); free(conn); } @@ -187,6 +928,55 @@ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) } +static int tls_match_altsubject(X509 *cert, const char *match) +{ + GENERAL_NAME *gen; + char *field, *tmp; + void *ext; + int i, found = 0; + size_t len; + + ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + + for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { + gen = sk_GENERAL_NAME_value(ext, i); + switch (gen->type) { + case GEN_EMAIL: + field = "EMAIL"; + break; + case GEN_DNS: + field = "DNS"; + break; + case GEN_URI: + field = "URI"; + break; + default: + field = NULL; + wpa_printf(MSG_DEBUG, "TLS: altSubjectName: " + "unsupported type=%d", gen->type); + break; + } + + if (!field) + continue; + + wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s", + field, gen->d.ia5->data); + len = strlen(field) + 1 + strlen((char *) gen->d.ia5->data) + + 1; + tmp = malloc(len); + if (tmp == NULL) + continue; + snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data); + if (strstr(tmp, match)) + found++; + free(tmp); + } + + return found; +} + + static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) { char buf[256]; @@ -194,17 +984,18 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) int err, depth; SSL *ssl; struct tls_connection *conn; - char *match; + char *match, *altmatch; err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); 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, SSL_get_ex_data_X509_STORE_CTX_idx()); - X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256); + 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 (!preverify_ok) { wpa_printf(MSG_WARNING, "TLS: Certificate verification failed," @@ -219,6 +1010,11 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) 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); + preverify_ok = 0; } } @@ -226,34 +1022,102 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) } -int tls_connection_ca_cert(void *ssl_ctx, struct tls_connection *conn, - const char *ca_cert, const char *subject_match) +#ifndef OPENSSL_NO_STDIO +static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert) { - if (conn == NULL) + SSL_CTX *ssl_ctx = _ssl_ctx; + X509_LOOKUP *lookup; + int ret = 0; + + lookup = X509_STORE_add_lookup(ssl_ctx->cert_store, + X509_LOOKUP_file()); + if (lookup == NULL) { + tls_show_errors(MSG_WARNING, __func__, + "Failed add lookup for X509 store"); return -1; + } - free(conn->subject_match); - conn->subject_match = NULL; - if (subject_match) { - conn->subject_match = strdup(subject_match); - if (conn->subject_match == NULL) - return -1; + if (!X509_LOOKUP_load_file(lookup, ca_cert, X509_FILETYPE_ASN1)) { + unsigned long err = ERR_peek_error(); + tls_show_errors(MSG_WARNING, __func__, + "Failed load CA in DER format"); + if (ERR_GET_LIB(err) == ERR_LIB_X509 && + ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring " + "cert already in hash table error", + __func__); + } else + ret = -1; } - if (ca_cert) { - if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1) - { - wpa_printf(MSG_WARNING, "TLS: Failed to load root " - "certificates: %s", - ERR_error_string(ERR_get_error(), NULL)); + return ret; +} +#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) +{ + SSL_CTX *ssl_ctx = _ssl_ctx; + + if (ca_cert_blob) { + X509 *cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ca_cert_blob, + ca_cert_blob_len); + if (cert == NULL) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to parse ca_cert_blob"); + return -1; + } + + if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to add ca_cert_blob to " + "certificate store"); + X509_free(cert); return -1; + } + 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; + } + +#ifdef CONFIG_NATIVE_WINDOWS + if (ca_cert && tls_cryptoapi_ca_cert(ssl_ctx, conn->ssl, ca_cert) == + 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 */ + + if (ca_cert || ca_path) { +#ifndef OPENSSL_NO_STDIO + if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, ca_path) != + 1) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to load root certificates"); + if (ca_cert && + tls_load_ca_der(ssl_ctx, ca_cert) == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s - loaded " + "DER format CA certificate", + __func__); + } else + return -1; } else { wpa_printf(MSG_DEBUG, "TLS: Trusted root " "certificate(s) loaded"); tls_get_errors(ssl_ctx); } - SSL_set_app_data(conn->ssl, conn); 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__); + return -1; +#endif /* OPENSSL_NO_STDIO */ } else { /* No ca_cert configured - do not try to verify server * certificate */ @@ -270,26 +1134,51 @@ int tls_global_ca_cert(void *_ssl_ctx, const char *ca_cert) if (ca_cert) { if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1) { - wpa_printf(MSG_WARNING, "TLS: Failed to load root " - "certificates: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_WARNING, __func__, + "Failed to load root certificates"); return -1; - } else { - wpa_printf(MSG_DEBUG, "TLS: Trusted root " - "certificate(s) loaded"); } + + wpa_printf(MSG_DEBUG, "TLS: Trusted root " + "certificate(s) loaded"); + +#ifndef OPENSSL_NO_STDIO + /* Add the same CAs to the client certificate requests */ + SSL_CTX_set_client_CA_list(ssl_ctx, + SSL_load_client_CA_file(ca_cert)); +#endif /* OPENSSL_NO_STDIO */ } return 0; } -int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, - int verify_peer, const char *subject_match) +int tls_global_set_verify(void *ssl_ctx, int check_crl) { - if (conn == NULL) - return -1; + int flags; + + if (check_crl) { + X509_STORE *cs = SSL_CTX_get_cert_store(ssl_ctx); + if (cs == NULL) { + tls_show_errors(MSG_INFO, __func__, "Failed to get " + "certificate store when enabling " + "check_crl"); + return -1; + } + flags = X509_V_FLAG_CRL_CHECK; + if (check_crl == 2) + flags |= X509_V_FLAG_CRL_CHECK_ALL; + X509_STORE_set_flags(cs, flags); + } + return 0; +} + +static int tls_connection_set_subject_match(void *ssl_ctx, + struct tls_connection *conn, + const char *subject_match, + const char *altsubject_match) +{ free(conn->subject_match); conn->subject_match = NULL; if (subject_match) { @@ -298,8 +1187,25 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, return -1; } + free(conn->altsubject_match); + conn->altsubject_match = NULL; + if (altsubject_match) { + conn->altsubject_match = strdup(altsubject_match); + if (conn->altsubject_match == NULL) + return -1; + } + + return 0; +} + + +int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, + int verify_peer) +{ + if (conn == NULL) + return -1; + if (verify_peer) { - SSL_set_app_data(conn->ssl, conn); SSL_set_verify(conn->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE, tls_verify_cb); @@ -313,29 +1219,60 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, } -int tls_connection_client_cert(void *ssl_ctx, struct tls_connection *conn, - const char *client_cert) +static int tls_connection_client_cert(void *ssl_ctx, + struct tls_connection *conn, + const char *client_cert, + const u8 *client_cert_blob, + size_t client_cert_blob_len) { - if (client_cert == NULL) + if (client_cert == NULL && client_cert_blob == NULL) return 0; - if (conn == NULL) + + if (client_cert_blob && + SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob, + client_cert_blob_len) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_ASN1 --> " + "OK"); + return 0; + } else if (client_cert_blob) { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_certificate_ASN1 failed"); + } + + if (client_cert == NULL) return -1; +#ifndef OPENSSL_NO_STDIO if (SSL_use_certificate_file(conn->ssl, client_cert, - SSL_FILETYPE_ASN1) != 1 && - SSL_use_certificate_file(conn->ssl, client_cert, - SSL_FILETYPE_PEM) != 1) { - wpa_printf(MSG_INFO, "TLS: Failed to load client " - "certificate: %s", - ERR_error_string(ERR_get_error(), NULL)); - return -1; + 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"); } - return 0; + + if (SSL_use_certificate_file(conn->ssl, client_cert, + SSL_FILETYPE_PEM) == 1) { + 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"); + } +#else /* OPENSSL_NO_STDIO */ + wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); +#endif /* OPENSSL_NO_STDIO */ + + return -1; } int tls_global_client_cert(void *_ssl_ctx, const char *client_cert) { +#ifndef OPENSSL_NO_STDIO SSL_CTX *ssl_ctx = _ssl_ctx; if (client_cert == NULL) return 0; @@ -344,12 +1281,17 @@ int tls_global_client_cert(void *_ssl_ctx, const char *client_cert) SSL_FILETYPE_ASN1) != 1 && SSL_CTX_use_certificate_file(ssl_ctx, client_cert, SSL_FILETYPE_PEM) != 1) { - wpa_printf(MSG_INFO, "TLS: Failed to load client " - "certificate: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Failed to load client certificate"); return -1; } return 0; +#else /* OPENSSL_NO_STDIO */ + if (client_cert == NULL) + return 0; + wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); + return -1; +#endif /* OPENSSL_NO_STDIO */ } @@ -364,42 +1306,31 @@ static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) } -static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, - const char *passwd) -{ #ifdef PKCS12_FUNCS - FILE *f; - PKCS12 *p12; +static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, + const char *passwd) +{ EVP_PKEY *pkey; X509 *cert; + STACK_OF(X509) *certs; int res = 0; - - f = fopen(private_key, "r"); - if (f == NULL) - return -1; - - p12 = d2i_PKCS12_fp(f, NULL); - if (p12 == NULL) { - wpa_printf(MSG_DEBUG, "TLS: Failed to read PKCS12 file '%s'", - private_key); - fclose(f); - return -1; - } - fclose(f); + char buf[256]; pkey = NULL; cert = NULL; - if (!PKCS12_parse(p12, passwd, &pkey, &cert, NULL)) { - wpa_printf(MSG_DEBUG, "TLS: Failed to parse PKCS12 file '%s': " - "%s", private_key, - ERR_error_string(ERR_get_error(), NULL)); + certs = NULL; + if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) { + tls_show_errors(MSG_DEBUG, __func__, + "Failed to parse PKCS12 file"); return -1; } - wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 file '%s'", - private_key); + wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 data"); if (cert) { - wpa_printf(MSG_DEBUG, "TLS: Got certificate from PKCS12"); + X509_NAME_oneline(X509_get_subject_name(cert), buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "TLS: Got certificate from PKCS12: " + "subject='%s'", buf); if (ssl) { if (SSL_use_certificate(ssl, cert) != 1) res = -1; @@ -422,9 +1353,56 @@ static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, EVP_PKEY_free(pkey); } + if (certs) { + 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); + /* + * 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) { + res = -1; + break; + } + } + sk_X509_free(certs); + } + PKCS12_free(p12); + if (res < 0) + tls_get_errors(ssl_ctx); + return res; +} +#endif /* PKCS12_FUNCS */ + + +static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, + const char *passwd) +{ +#ifdef PKCS12_FUNCS + FILE *f; + PKCS12 *p12; + + f = fopen(private_key, "r"); + if (f == NULL) + return -1; + + p12 = d2i_PKCS12_fp(f, NULL); + fclose(f); + + if (p12 == NULL) { + tls_show_errors(MSG_INFO, __func__, + "Failed to use PKCS#12 file"); + return -1; + } + + return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); + #else /* PKCS12_FUNCS */ wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read " "p12/pfx files"); @@ -433,17 +1411,65 @@ static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, } -int tls_connection_private_key(void *_ssl_ctx, struct tls_connection *conn, - const char *private_key, - const char *private_key_passwd) +static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, 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); + 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); + +#else /* PKCS12_FUNCS */ + wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse " + "p12/pfx blobs"); + return -1; +#endif /* PKCS12_FUNCS */ +} + + +static int tls_connection_engine_private_key(void *_ssl_ctx, + struct tls_connection *conn) +{ +#ifndef 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"); + return -1; + } + if (!SSL_check_private_key(conn->ssl)) { + tls_show_errors(MSG_INFO, __func__, + "Private key failed verification"); + return -1; + } + return 0; +#else /* OPENSSL_NO_ENGINE */ + wpa_printf(MSG_ERROR, "SSL: Configuration uses engine, but " + "engine support was not compiled in"); + return -1; +#endif /* OPENSSL_NO_ENGINE */ +} + + +static int tls_connection_private_key(void *_ssl_ctx, + 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; char *passwd; + int ok; - if (private_key == NULL) + if (private_key == NULL && private_key_blob == NULL) return 0; - if (conn == NULL) - return -1; if (private_key_passwd) { passwd = strdup(private_key_passwd); @@ -454,28 +1480,123 @@ int tls_connection_private_key(void *_ssl_ctx, struct tls_connection *conn, SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); - if (SSL_use_PrivateKey_file(conn->ssl, private_key, - SSL_FILETYPE_ASN1) != 1 && - SSL_use_PrivateKey_file(conn->ssl, private_key, - SSL_FILETYPE_PEM) != 1 && - tls_read_pkcs12(ssl_ctx, conn->ssl, private_key, passwd)) { - wpa_printf(MSG_INFO, "SSL: Failed to load private key: %s", - ERR_error_string(ERR_get_error(), NULL)); + + ok = 0; + while (private_key_blob) { + if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl, + (u8 *) private_key_blob, + private_key_blob_len) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_" + "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, + (u8 *) private_key_blob, + private_key_blob_len) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_" + "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, + (u8 *) private_key_blob, + private_key_blob_len) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: " + "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, + private_key_blob_len, passwd) == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> " + "OK"); + ok = 1; + break; + } + + break; + } + + while (!ok && private_key) { +#ifndef OPENSSL_NO_STDIO + if (SSL_use_PrivateKey_file(conn->ssl, private_key, + SSL_FILETYPE_ASN1) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: " + "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, + SSL_FILETYPE_PEM) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: " + "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) + == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file " + "--> OK"); + ok = 1; + break; + } + + if (tls_cryptoapi_cert(conn->ssl, private_key) == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: Using CryptoAPI to " + "access certificate store --> OK"); + ok = 1; + break; + } + + break; + } + + if (!ok) { + wpa_printf(MSG_INFO, "OpenSSL: Failed to load private key"); free(passwd); ERR_clear_error(); return -1; } ERR_clear_error(); - free(passwd); SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); + free(passwd); if (!SSL_check_private_key(conn->ssl)) { - wpa_printf(MSG_INFO, "SSL: Private key failed " - "verification: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, "Private key failed " + "verification"); return -1; } + wpa_printf(MSG_DEBUG, "SSL: Private key loaded successfully"); return 0; } @@ -498,13 +1619,16 @@ int tls_global_private_key(void *_ssl_ctx, const char *private_key, SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); - if (SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, + if ( +#ifndef OPENSSL_NO_STDIO + SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, SSL_FILETYPE_ASN1) != 1 && 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)) { - wpa_printf(MSG_INFO, "SSL: Failed to load private key: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Failed to load private key"); free(passwd); ERR_clear_error(); return -1; @@ -514,9 +1638,8 @@ int tls_global_private_key(void *_ssl_ctx, const char *private_key, SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); if (!SSL_CTX_check_private_key(ssl_ctx)) { - wpa_printf(MSG_INFO, "SSL: Private key failed " - "verification: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Private key failed verification"); return -1; } @@ -524,7 +1647,7 @@ int tls_global_private_key(void *_ssl_ctx, const char *private_key, } -int tls_connection_dh(void *ssl_ctx, struct tls_connection *conn, +static int tls_connection_dh(void *ssl_ctx, struct tls_connection *conn, const char *dh_file) { #ifdef OPENSSL_NO_DH @@ -537,6 +1660,7 @@ int tls_connection_dh(void *ssl_ctx, struct tls_connection *conn, DH *dh; BIO *bio; + /* TODO: add support for dh_blob */ if (dh_file == NULL) return 0; if (conn == NULL) @@ -609,6 +1733,7 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL) return -1; + 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; @@ -627,13 +1752,18 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, int res; u8 *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) { - wpa_printf(MSG_INFO, "TLS: Handshake failed - BIO_write: %s", - ERR_error_string(ERR_get_error(), NULL)); + 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 (res != 1) { int err = SSL_get_error(conn->ssl, res); @@ -644,27 +1774,33 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want to " "write"); else { - wpa_printf(MSG_INFO, "SSL: SSL_connect: %s", - ERR_error_string(ERR_get_error(), NULL)); - return NULL; + tls_show_errors(MSG_INFO, __func__, "SSL_connect"); + conn->failed++; } } + /* 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 = malloc(res == 0 ? 1 : res); if (out_data == NULL) { wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for " "handshake output (%d bytes)", res); - BIO_reset(conn->ssl_out); + 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); if (res < 0) { - wpa_printf(MSG_INFO, "TLS: Handshake failed - BIO_read: %s", - ERR_error_string(ERR_get_error(), NULL)); - BIO_reset(conn->ssl_out); + tls_show_errors(MSG_INFO, __func__, + "Handshake failed - BIO_read"); + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, + "BIO_reset failed"); + } *out_len = 0; return NULL; } @@ -684,8 +1820,8 @@ u8 * tls_connection_server_handshake(void *ssl_ctx, if (in_data && BIO_write(conn->ssl_in, in_data, in_len) < 0) { - wpa_printf(MSG_INFO, "TLS: Handshake failed - BIO_write: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Handshake failed - BIO_write"); return NULL; } @@ -701,15 +1837,21 @@ u8 * tls_connection_server_handshake(void *ssl_ctx, if (out_data == NULL) { wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for " "handshake output (%d bytes)", res); - BIO_reset(conn->ssl_out); + 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); if (res < 0) { - wpa_printf(MSG_INFO, "TLS: Handshake failed - BIO_read: %s", - ERR_error_string(ERR_get_error(), NULL)); - BIO_reset(conn->ssl_out); + tls_show_errors(MSG_INFO, __func__, + "Handshake failed - BIO_read"); + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, + "BIO_reset failed"); + } *out_len = 0; return NULL; } @@ -719,7 +1861,7 @@ u8 * tls_connection_server_handshake(void *ssl_ctx, int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, - u8 *in_data, size_t in_len, + const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len) { int res; @@ -727,19 +1869,24 @@ int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, if (conn == NULL) return -1; - BIO_reset(conn->ssl_in); - BIO_reset(conn->ssl_out); + /* 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; + } res = SSL_write(conn->ssl, in_data, in_len); if (res < 0) { - wpa_printf(MSG_INFO, "TLS: Encryption failed - SSL_write: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Encryption failed - SSL_write"); return res; } + /* Read encrypted data to be sent to the server */ res = BIO_read(conn->ssl_out, out_data, out_len); if (res < 0) { - wpa_printf(MSG_INFO, "TLS: Encryption failed - BIO_read: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Encryption failed - BIO_read"); return res; } @@ -748,23 +1895,28 @@ int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, - u8 *in_data, size_t in_len, + const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len) { int res; + /* Give encrypted data from TLS tunnel for OpenSSL to decrypt. */ res = BIO_write(conn->ssl_in, in_data, in_len); if (res < 0) { - wpa_printf(MSG_INFO, "TLS: Decryption failed - BIO_write: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Decryption failed - BIO_write"); + return res; + } + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, "BIO_reset failed"); return res; } - BIO_reset(conn->ssl_out); + /* Read decrypted data for further processing */ res = SSL_read(conn->ssl, out_data, out_len); if (res < 0) { - wpa_printf(MSG_INFO, "TLS: Decryption failed - SSL_read: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Decryption failed - SSL_read"); return res; } @@ -778,22 +1930,54 @@ int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) } +#ifdef EAP_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. */ + +static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, + SSL_CIPHER **cipher, void *arg) +{ + struct tls_connection *conn = arg; + + if (conn == NULL || conn->pre_shared_secret == 0) + return 0; + + memcpy(secret, conn->pre_shared_secret, conn->pre_shared_secret_len); + *secret_len = conn->pre_shared_secret_len; + + return 1; +} + + int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn, const u8 *key, size_t key_len) { - SSL *ssl; - - if (conn == NULL || key == NULL || key_len > SSL_MAX_MASTER_KEY_LENGTH) - return -1; - ssl = conn->ssl; - if (ssl == NULL || ssl->session == NULL) + if (conn == NULL || key_len > SSL_MAX_MASTER_KEY_LENGTH) return -1; - memcpy(ssl->session->master_key, key, key_len); - ssl->session->master_key_length = key_len; + free(conn->pre_shared_secret); + conn->pre_shared_secret = NULL; + conn->pre_shared_secret_len = 0; + + if (key) { + conn->pre_shared_secret = malloc(key_len); + if (conn->pre_shared_secret) { + memcpy(conn->pre_shared_secret, key, key_len); + conn->pre_shared_secret_len = key_len; + } + if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb, + conn) != 1) + return -1; + } else { + if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1) + return -1; + } return 0; } +#endif /* EAP_FAST */ int tls_connection_set_anon_dh(void *ssl_ctx, struct tls_connection *conn) @@ -802,8 +1986,8 @@ int tls_connection_set_anon_dh(void *ssl_ctx, struct tls_connection *conn) return -1; if (SSL_set_cipher_list(conn->ssl, "ADH-AES128-SHA") != 1) { - wpa_printf(MSG_INFO, "TLS: Anon DH configuration failed - %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Anon DH configuration failed"); return -1; } @@ -844,36 +2028,116 @@ int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, int ext_type, const u8 *data, size_t data_len) { - struct tls_ext_hdr { - u16 extensions_len; - u16 extension_type; - u16 extension_len; - } *hdr; - if (conn == NULL || conn->ssl == NULL) return -1; - OPENSSL_free(conn->ssl->hello_extension); - if (data == NULL) { - conn->ssl->hello_extension = NULL; - conn->ssl->hello_extension_len = 0; - return 0; + + if (SSL_set_hello_extension(conn->ssl, ext_type, (void *) data, + data_len) != 1) + return -1; + + return 0; +} +#endif /* EAP_FAST */ + + +int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->failed; +} + + +int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->read_alerts; +} + + +int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->write_alerts; +} + + +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params) +{ + int ret; + unsigned long err; + + if (conn == NULL) + return -1; + + while ((err = ERR_get_error())) { + wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s", + __func__, ERR_error_string(err, NULL)); } - if (data_len == 0) { - conn->ssl->hello_extension = OPENSSL_malloc(1); - conn->ssl->hello_extension_len = 0; - return 0; + + if (tls_connection_set_subject_match(tls_ctx, conn, + params->subject_match, + params->altsubject_match)) + return -1; + 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 (tls_connection_client_cert(tls_ctx, conn, params->client_cert, + params->client_cert_blob, + params->client_cert_blob_len)) + return -1; + + if (params->engine) { + wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine"); + ret = tls_engine_init(conn, params->engine_id, params->pin, + params->key_id); + if (ret) + return ret; + if (tls_connection_engine_private_key(tls_ctx, conn)) + return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; + } else if (tls_connection_private_key(tls_ctx, conn, + params->private_key, + params->private_key_passwd, + params->private_key_blob, + params->private_key_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to load private key '%s'", + params->private_key); + return -1; } - conn->ssl->hello_extension = OPENSSL_malloc(sizeof(*hdr) + data_len); - if (conn->ssl->hello_extension == NULL) + + if (tls_connection_dh(tls_ctx, conn, params->dh_file)) { + wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'", + params->dh_file); return -1; + } - hdr = (struct tls_ext_hdr *) conn->ssl->hello_extension; - hdr->extensions_len = host_to_be16(sizeof(*hdr) - 2 + data_len); - hdr->extension_type = host_to_be16(ext_type); - hdr->extension_len = host_to_be16(data_len); - memcpy(hdr + 1, data, data_len); - conn->ssl->hello_extension_len = sizeof(*hdr) + data_len; + tls_get_errors(tls_ctx); return 0; } -#endif /* EAP_FAST */ + + +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) + return -1; + + c = conn->ssl->enc_read_ctx->cipher; + h = conn->ssl->read_hash; + + return 2 * (EVP_CIPHER_key_length(c) + + EVP_MD_size(h) + + EVP_CIPHER_iv_length(c)); +} |