2008-02-28 02:34:43 +01:00
|
|
|
/*
|
2009-12-20 17:17:55 +01:00
|
|
|
* SSL/TLS interface functions for OpenSSL
|
2015-07-28 10:40:17 +02:00
|
|
|
* Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
|
2008-02-28 02:34:43 +01:00
|
|
|
*
|
2012-02-11 15:46:35 +01:00
|
|
|
* This software may be distributed under the terms of the BSD license.
|
|
|
|
* See README for more details.
|
2008-02-28 02:34:43 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "includes.h"
|
|
|
|
|
|
|
|
#ifndef CONFIG_SMARTCARD
|
|
|
|
#ifndef OPENSSL_NO_ENGINE
|
2012-03-21 01:00:47 +01:00
|
|
|
#ifndef ANDROID
|
2008-02-28 02:34:43 +01:00
|
|
|
#define OPENSSL_NO_ENGINE
|
|
|
|
#endif
|
|
|
|
#endif
|
2012-03-21 01:00:47 +01:00
|
|
|
#endif
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
#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 */
|
2015-07-27 23:57:36 +02:00
|
|
|
#ifndef OPENSSL_NO_DSA
|
|
|
|
#include <openssl/dsa.h>
|
|
|
|
#endif
|
|
|
|
#ifndef OPENSSL_NO_DH
|
|
|
|
#include <openssl/dh.h>
|
|
|
|
#endif
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
#include "common.h"
|
2010-02-13 10:14:23 +01:00
|
|
|
#include "crypto.h"
|
2015-03-31 14:47:32 +02:00
|
|
|
#include "sha1.h"
|
2015-07-28 10:40:17 +02:00
|
|
|
#include "sha256.h"
|
2008-02-28 02:34:43 +01:00
|
|
|
#include "tls.h"
|
2015-12-04 13:04:31 +01:00
|
|
|
#include "tls_openssl.h"
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2014-09-19 03:40:03 +02:00
|
|
|
#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
|
|
|
|
typedef int stack_index_t;
|
2008-11-16 20:29:12 +01:00
|
|
|
#endif
|
|
|
|
|
2013-06-29 23:54:24 +02:00
|
|
|
#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 */
|
|
|
|
|
2013-11-08 00:06:59 +01:00
|
|
|
#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;
|
|
|
|
}
|
|
|
|
#endif /* ANDROID */
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
static int tls_openssl_ref_count = 0;
|
2015-08-23 21:08:27 +02:00
|
|
|
static int tls_ex_idx_session = -1;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2013-05-09 23:22:08 +02:00
|
|
|
struct tls_context {
|
2010-02-13 10:14:23 +01:00
|
|
|
void (*event_cb)(void *ctx, enum tls_event ev,
|
|
|
|
union tls_event_data *data);
|
|
|
|
void *cb_ctx;
|
2011-09-17 21:42:54 +02:00
|
|
|
int cert_in_cb;
|
2013-06-29 23:54:24 +02:00
|
|
|
char *ocsp_stapling_response;
|
2010-02-13 10:14:23 +01:00
|
|
|
};
|
|
|
|
|
2013-05-09 23:22:08 +02:00
|
|
|
static struct tls_context *tls_global = NULL;
|
2010-02-13 10:14:23 +01:00
|
|
|
|
|
|
|
|
2015-08-23 18:22:13 +02:00
|
|
|
struct tls_data {
|
|
|
|
SSL_CTX *ssl;
|
2015-08-23 21:08:27 +02:00
|
|
|
unsigned int tls_session_lifetime;
|
2015-08-23 18:22:13 +02:00
|
|
|
};
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
struct tls_connection {
|
2013-05-09 23:22:08 +02:00
|
|
|
struct tls_context *context;
|
2015-02-04 00:58:37 +01:00
|
|
|
SSL_CTX *ssl_ctx;
|
2008-02-28 02:34:43 +01:00
|
|
|
SSL *ssl;
|
|
|
|
BIO *ssl_in, *ssl_out;
|
2015-10-06 20:05:53 +02:00
|
|
|
#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
|
2008-02-28 02:34:43 +01:00
|
|
|
ENGINE *engine; /* functional reference to the engine */
|
|
|
|
EVP_PKEY *private_key; /* the private key if using engine */
|
|
|
|
#endif /* OPENSSL_NO_ENGINE */
|
2015-01-14 14:31:28 +01:00
|
|
|
char *subject_match, *altsubject_match, *suffix_match, *domain_match;
|
2008-02-28 02:34:43 +01:00
|
|
|
int read_alerts, write_alerts, failed;
|
|
|
|
|
|
|
|
tls_session_ticket_cb session_ticket_cb;
|
|
|
|
void *session_ticket_cb_ctx;
|
|
|
|
|
|
|
|
/* SessionTicket received from OpenSSL hello_extension_cb (server) */
|
|
|
|
u8 *session_ticket;
|
|
|
|
size_t session_ticket_len;
|
2010-02-13 10:14:23 +01:00
|
|
|
|
2010-04-11 11:27:13 +02:00
|
|
|
unsigned int ca_cert_verify:1;
|
|
|
|
unsigned int cert_probe:1;
|
|
|
|
unsigned int server_cert_only:1;
|
2014-04-09 12:02:53 +02:00
|
|
|
unsigned int invalid_hb_used:1;
|
2015-08-23 21:08:27 +02:00
|
|
|
unsigned int success_data:1;
|
2010-02-13 10:14:23 +01:00
|
|
|
|
|
|
|
u8 srv_cert_hash[32];
|
2011-07-05 10:29:42 +02:00
|
|
|
|
|
|
|
unsigned int flags;
|
2013-06-29 23:54:24 +02:00
|
|
|
|
|
|
|
X509 *peer_cert;
|
|
|
|
X509 *peer_issuer;
|
2013-12-09 03:35:11 +01:00
|
|
|
X509 *peer_issuer_issuer;
|
2015-07-27 23:58:39 +02:00
|
|
|
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
|
|
unsigned char client_random[SSL3_RANDOM_SIZE];
|
|
|
|
unsigned char server_random[SSL3_RANDOM_SIZE];
|
|
|
|
#endif
|
2008-02-28 02:34:43 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2013-05-09 23:22:08 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
#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 CERT_SYSTEM_STORE_CURRENT_USER (1 << 16)
|
|
|
|
#define CERT_STORE_READONLY_FLAG 0x00008000
|
|
|
|
#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000
|
|
|
|
|
|
|
|
#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 ((int) 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 = os_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:
|
|
|
|
os_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);
|
|
|
|
os_free(priv);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int cryptoapi_finish(RSA *rsa)
|
|
|
|
{
|
|
|
|
cryptoapi_free_data((struct cryptoapi_rsa_data *) rsa->meth->app_data);
|
|
|
|
os_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, 255);
|
|
|
|
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 = os_strlen(hash) / 2;
|
|
|
|
buf = os_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);
|
|
|
|
}
|
|
|
|
os_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 = os_zalloc(sizeof(*priv));
|
|
|
|
rsa_meth = os_zalloc(sizeof(*rsa_meth));
|
|
|
|
if (priv == NULL || rsa_meth == NULL) {
|
|
|
|
wpa_printf(MSG_WARNING, "CryptoAPI: Failed to allocate memory "
|
|
|
|
"for CryptoAPI RSA method");
|
|
|
|
os_free(priv);
|
|
|
|
os_free(rsa_meth);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-01-27 12:37:49 +01:00
|
|
|
cert = d2i_X509(NULL,
|
|
|
|
(const unsigned char **) &priv->cert->pbCertEncoded,
|
2008-02-28 02:34:43 +01:00
|
|
|
priv->cert->cbCertEncoded);
|
|
|
|
if (cert == NULL) {
|
|
|
|
wpa_printf(MSG_INFO, "CryptoAPI: Could not process X509 DER "
|
|
|
|
"encoding");
|
|
|
|
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)) {
|
|
|
|
RSA_free(rsa);
|
|
|
|
rsa = NULL;
|
|
|
|
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 {
|
|
|
|
os_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];
|
|
|
|
const char *store;
|
|
|
|
#ifdef UNICODE
|
|
|
|
WCHAR *wstore;
|
|
|
|
#endif /* UNICODE */
|
|
|
|
|
|
|
|
if (name == NULL || strncmp(name, "cert_store://", 13) != 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
store = name + 13;
|
|
|
|
#ifdef UNICODE
|
|
|
|
wstore = os_malloc((os_strlen(store) + 1) * sizeof(WCHAR));
|
|
|
|
if (wstore == NULL)
|
|
|
|
return -1;
|
|
|
|
wsprintf(wstore, L"%S", store);
|
|
|
|
cs = CertOpenSystemStore(0, wstore);
|
|
|
|
os_free(wstore);
|
|
|
|
#else /* UNICODE */
|
|
|
|
cs = CertOpenSystemStore(0, store);
|
|
|
|
#endif /* UNICODE */
|
|
|
|
if (cs == NULL) {
|
|
|
|
wpa_printf(MSG_DEBUG, "%s: failed to open system cert store "
|
|
|
|
"'%s': error=%d", __func__, store,
|
|
|
|
(int) GetLastError());
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((ctx = CertEnumCertificatesInStore(cs, ctx))) {
|
2015-01-27 12:37:49 +01:00
|
|
|
cert = d2i_X509(NULL,
|
|
|
|
(const unsigned char **) &ctx->pbCertEncoded,
|
2008-02-28 02:34:43 +01:00
|
|
|
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;
|
|
|
|
int w;
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "SSL: (where=0x%x ret=0x%x)", where, ret);
|
|
|
|
w = where & ~SSL_ST_MASK;
|
|
|
|
if (w & SSL_ST_CONNECT)
|
|
|
|
str = "SSL_connect";
|
|
|
|
else if (w & SSL_ST_ACCEPT)
|
|
|
|
str = "SSL_accept";
|
|
|
|
else
|
|
|
|
str = "undefined";
|
|
|
|
|
|
|
|
if (where & SSL_CB_LOOP) {
|
|
|
|
wpa_printf(MSG_DEBUG, "SSL: %s:%s",
|
|
|
|
str, SSL_state_string_long(ssl));
|
|
|
|
} else if (where & SSL_CB_ALERT) {
|
2013-05-09 23:22:08 +02:00
|
|
|
struct tls_connection *conn = SSL_get_app_data((SSL *) ssl);
|
2008-02-28 02:34:43 +01:00
|
|
|
wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s",
|
|
|
|
where & SSL_CB_READ ?
|
|
|
|
"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) {
|
|
|
|
if (where & SSL_CB_READ)
|
|
|
|
conn->read_alerts++;
|
|
|
|
else
|
|
|
|
conn->write_alerts++;
|
|
|
|
}
|
2013-05-09 23:22:08 +02:00
|
|
|
if (conn->context->event_cb != NULL) {
|
2012-06-04 20:10:01 +02:00
|
|
|
union tls_event_data ev;
|
2013-05-09 23:22:08 +02:00
|
|
|
struct tls_context *context = conn->context;
|
2012-06-04 20:10:01 +02:00
|
|
|
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);
|
2013-05-09 23:22:08 +02:00
|
|
|
context->event_cb(context->cb_ctx, TLS_ALERT, &ev);
|
2012-06-04 20:10:01 +02:00
|
|
|
}
|
2008-02-28 02:34:43 +01:00
|
|
|
} else if (where & SSL_CB_EXIT && ret <= 0) {
|
|
|
|
wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s",
|
|
|
|
str, ret == 0 ? "failed" : "error",
|
|
|
|
SSL_state_string_long(ssl));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#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", NULL /* pkcs11_so_path */,
|
|
|
|
"ID", NULL /* engine_id */,
|
|
|
|
"LIST_ADD", "1",
|
|
|
|
/* "NO_VCHECK", "1", */
|
|
|
|
"LOAD", NULL,
|
|
|
|
NULL, NULL
|
|
|
|
};
|
|
|
|
const char *post_cmd[] = {
|
|
|
|
"MODULE_PATH", NULL /* pkcs11_module_path */,
|
|
|
|
NULL, NULL
|
|
|
|
};
|
|
|
|
|
2014-12-18 16:09:16 +01:00
|
|
|
if (!pkcs11_so_path)
|
2008-02-28 02:34:43 +01:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
pre_cmd[1] = pkcs11_so_path;
|
|
|
|
pre_cmd[3] = engine_id;
|
2014-12-18 16:09:16 +01:00
|
|
|
if (pkcs11_module_path)
|
|
|
|
post_cmd[1] = pkcs11_module_path;
|
|
|
|
else
|
|
|
|
post_cmd[0] = NULL;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
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", NULL /* opensc_so_path */,
|
|
|
|
"ID", NULL /* engine_id */,
|
|
|
|
"LIST_ADD", "1",
|
|
|
|
"LOAD", NULL,
|
|
|
|
NULL, NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!opensc_so_path)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
pre_cmd[1] = opensc_so_path;
|
|
|
|
pre_cmd[3] = engine_id;
|
|
|
|
|
|
|
|
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 */
|
|
|
|
|
|
|
|
|
2015-08-23 21:08:27 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
void * tls_init(const struct tls_config *conf)
|
|
|
|
{
|
2015-08-23 18:22:13 +02:00
|
|
|
struct tls_data *data;
|
2008-02-28 02:34:43 +01:00
|
|
|
SSL_CTX *ssl;
|
2013-05-09 23:22:08 +02:00
|
|
|
struct tls_context *context;
|
2014-10-12 10:45:21 +02:00
|
|
|
const char *ciphers;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
if (tls_openssl_ref_count == 0) {
|
2013-05-09 23:22:08 +02:00
|
|
|
tls_global = context = tls_context_new(conf);
|
|
|
|
if (context == NULL)
|
2010-02-13 10:14:23 +01:00
|
|
|
return NULL;
|
2009-08-16 13:24:22 +02:00
|
|
|
#ifdef CONFIG_FIPS
|
|
|
|
#ifdef OPENSSL_FIPS
|
2010-02-12 19:51:10 +01:00
|
|
|
if (conf && conf->fips_mode) {
|
2015-08-01 21:39:21 +02:00
|
|
|
static int fips_enabled = 0;
|
|
|
|
|
|
|
|
if (!fips_enabled && !FIPS_mode_set(1)) {
|
2009-08-16 13:24:22 +02:00
|
|
|
wpa_printf(MSG_ERROR, "Failed to enable FIPS "
|
|
|
|
"mode");
|
|
|
|
ERR_load_crypto_strings();
|
|
|
|
ERR_print_errors_fp(stderr);
|
2012-08-16 16:38:46 +02:00
|
|
|
os_free(tls_global);
|
|
|
|
tls_global = NULL;
|
2009-08-16 13:24:22 +02:00
|
|
|
return NULL;
|
2015-08-01 21:39:21 +02:00
|
|
|
} else {
|
2009-08-16 13:24:22 +02:00
|
|
|
wpa_printf(MSG_INFO, "Running in FIPS mode");
|
2015-08-01 21:39:21 +02:00
|
|
|
fips_enabled = 1;
|
|
|
|
}
|
2009-08-16 13:24:22 +02:00
|
|
|
}
|
|
|
|
#else /* OPENSSL_FIPS */
|
2010-02-12 19:51:10 +01:00
|
|
|
if (conf && conf->fips_mode) {
|
2009-08-16 13:24:22 +02:00
|
|
|
wpa_printf(MSG_ERROR, "FIPS mode requested, but not "
|
|
|
|
"supported");
|
2012-08-16 16:38:46 +02:00
|
|
|
os_free(tls_global);
|
|
|
|
tls_global = NULL;
|
2009-08-16 13:24:22 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif /* OPENSSL_FIPS */
|
|
|
|
#endif /* CONFIG_FIPS */
|
2008-02-28 02:34:43 +01:00
|
|
|
SSL_load_error_strings();
|
|
|
|
SSL_library_init();
|
2015-01-27 12:37:49 +01:00
|
|
|
#ifndef OPENSSL_NO_SHA256
|
2009-08-16 09:25:13 +02:00
|
|
|
EVP_add_digest(EVP_sha256());
|
|
|
|
#endif /* OPENSSL_NO_SHA256 */
|
2008-02-28 02:34:43 +01:00
|
|
|
/* 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
|
2010-01-08 23:38:09 +01:00
|
|
|
#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 */
|
2008-02-28 02:34:43 +01:00
|
|
|
PKCS12_PBE_add();
|
|
|
|
#endif /* PKCS12_FUNCS */
|
2013-05-09 23:22:08 +02:00
|
|
|
} else {
|
|
|
|
context = tls_context_new(conf);
|
|
|
|
if (context == NULL)
|
|
|
|
return NULL;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
tls_openssl_ref_count++;
|
|
|
|
|
2015-08-23 18:22:13 +02:00
|
|
|
data = os_zalloc(sizeof(*data));
|
|
|
|
if (data)
|
|
|
|
ssl = SSL_CTX_new(SSLv23_method());
|
|
|
|
else
|
|
|
|
ssl = NULL;
|
2013-05-09 23:22:08 +02:00
|
|
|
if (ssl == NULL) {
|
|
|
|
tls_openssl_ref_count--;
|
2013-10-26 11:02:50 +02:00
|
|
|
if (context != tls_global)
|
|
|
|
os_free(context);
|
2013-05-09 23:22:08 +02:00
|
|
|
if (tls_openssl_ref_count == 0) {
|
|
|
|
os_free(tls_global);
|
|
|
|
tls_global = NULL;
|
|
|
|
}
|
2008-02-28 02:34:43 +01:00
|
|
|
return NULL;
|
2013-05-09 23:22:08 +02:00
|
|
|
}
|
2015-08-23 18:22:13 +02:00
|
|
|
data->ssl = ssl;
|
2015-08-23 21:08:27 +02:00
|
|
|
if (conf)
|
|
|
|
data->tls_session_lifetime = conf->tls_session_lifetime;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2014-11-15 11:35:10 +01:00
|
|
|
SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2);
|
|
|
|
SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3);
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
SSL_CTX_set_info_callback(ssl, ssl_info_cb);
|
2013-05-09 23:22:08 +02:00
|
|
|
SSL_CTX_set_app_data(ssl, context);
|
2015-08-23 21:08:27 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
#ifndef OPENSSL_NO_ENGINE
|
2014-12-18 16:09:40 +01:00
|
|
|
wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine");
|
|
|
|
ERR_load_ENGINE_strings();
|
|
|
|
ENGINE_load_dynamic();
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
if (conf &&
|
|
|
|
(conf->opensc_engine_path || conf->pkcs11_engine_path ||
|
|
|
|
conf->pkcs11_module_path)) {
|
|
|
|
if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) ||
|
|
|
|
tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path,
|
|
|
|
conf->pkcs11_module_path)) {
|
2015-08-23 18:22:13 +02:00
|
|
|
tls_deinit(data);
|
2008-02-28 02:34:43 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* OPENSSL_NO_ENGINE */
|
|
|
|
|
2014-10-12 10:45:21 +02:00
|
|
|
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);
|
2015-08-23 18:22:13 +02:00
|
|
|
tls_deinit(data);
|
2014-10-12 10:45:21 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-08-23 18:22:13 +02:00
|
|
|
return data;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void tls_deinit(void *ssl_ctx)
|
|
|
|
{
|
2015-08-23 18:22:13 +02:00
|
|
|
struct tls_data *data = ssl_ctx;
|
|
|
|
SSL_CTX *ssl = data->ssl;
|
2013-05-09 23:22:08 +02:00
|
|
|
struct tls_context *context = SSL_CTX_get_app_data(ssl);
|
|
|
|
if (context != tls_global)
|
|
|
|
os_free(context);
|
2015-08-23 21:08:27 +02:00
|
|
|
if (data->tls_session_lifetime > 0)
|
|
|
|
SSL_CTX_flush_sessions(ssl, 0);
|
2008-02-28 02:34:43 +01:00
|
|
|
SSL_CTX_free(ssl);
|
|
|
|
|
|
|
|
tls_openssl_ref_count--;
|
|
|
|
if (tls_openssl_ref_count == 0) {
|
|
|
|
#ifndef OPENSSL_NO_ENGINE
|
|
|
|
ENGINE_cleanup();
|
|
|
|
#endif /* OPENSSL_NO_ENGINE */
|
|
|
|
CRYPTO_cleanup_all_ex_data();
|
2014-09-19 03:40:03 +02:00
|
|
|
ERR_remove_thread_state(NULL);
|
2008-02-28 02:34:43 +01:00
|
|
|
ERR_free_strings();
|
|
|
|
EVP_cleanup();
|
2013-06-29 23:54:24 +02:00
|
|
|
os_free(tls_global->ocsp_stapling_response);
|
|
|
|
tls_global->ocsp_stapling_response = NULL;
|
2010-02-13 10:14:23 +01:00
|
|
|
os_free(tls_global);
|
|
|
|
tls_global = NULL;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
2015-08-23 18:22:13 +02:00
|
|
|
|
|
|
|
os_free(data);
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-04-16 02:57:51 +02:00
|
|
|
#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 */
|
|
|
|
|
|
|
|
|
2015-10-06 20:05:53 +02:00
|
|
|
#ifdef ANDROID
|
|
|
|
/* EVP_PKEY_from_keystore comes from system/security/keystore-engine. */
|
|
|
|
EVP_PKEY * EVP_PKEY_from_keystore(const char *key_id);
|
|
|
|
#endif /* ANDROID */
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
static int tls_engine_init(struct tls_connection *conn, const char *engine_id,
|
2008-05-23 09:49:59 +02:00
|
|
|
const char *pin, const char *key_id,
|
|
|
|
const char *cert_id, const char *ca_cert_id)
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
2015-10-06 20:05:53 +02:00
|
|
|
#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 */
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
#ifndef OPENSSL_NO_ENGINE
|
|
|
|
int ret = -1;
|
|
|
|
if (engine_id == NULL) {
|
|
|
|
wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ERR_clear_error();
|
2012-03-21 01:00:47 +01:00
|
|
|
#ifdef ANDROID
|
|
|
|
ENGINE_load_dynamic();
|
|
|
|
#endif
|
2008-02-28 02:34:43 +01:00
|
|
|
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");
|
|
|
|
|
2012-03-21 01:00:47 +01:00
|
|
|
#ifndef ANDROID
|
2014-12-18 16:09:55 +01:00
|
|
|
if (pin && ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) {
|
2008-02-28 02:34:43 +01:00
|
|
|
wpa_printf(MSG_ERROR, "ENGINE: cannot set pin [%s]",
|
|
|
|
ERR_error_string(ERR_get_error(), NULL));
|
|
|
|
goto err;
|
|
|
|
}
|
2012-03-21 01:00:47 +01:00
|
|
|
#endif
|
2014-12-18 16:09:23 +01:00
|
|
|
if (key_id) {
|
2014-12-18 16:09:55 +01:00
|
|
|
/*
|
|
|
|
* 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 };
|
|
|
|
|
2014-12-18 16:09:23 +01:00
|
|
|
/* load private key first in-case PIN is required for cert */
|
|
|
|
conn->private_key = ENGINE_load_private_key(conn->engine,
|
2014-12-18 16:09:55 +01:00
|
|
|
key_id, NULL,
|
|
|
|
&key_cb);
|
2014-12-18 16:09:23 +01:00
|
|
|
if (!conn->private_key) {
|
2015-04-16 02:57:51 +02:00
|
|
|
unsigned long err = ERR_get_error();
|
|
|
|
|
2014-12-18 16:09:23 +01:00
|
|
|
wpa_printf(MSG_ERROR,
|
|
|
|
"ENGINE: cannot load private key with id '%s' [%s]",
|
|
|
|
key_id,
|
2015-04-16 02:57:51 +02:00
|
|
|
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;
|
2014-12-18 16:09:23 +01:00
|
|
|
goto err;
|
|
|
|
}
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
2008-05-23 09:49:59 +02:00
|
|
|
|
|
|
|
/* handle a certificate and/or CA certificate */
|
|
|
|
if (cert_id || ca_cert_id) {
|
|
|
|
const char *cmd_name = "LOAD_CERT_CTRL";
|
|
|
|
|
|
|
|
/* test if the engine supports a LOAD_CERT_CTRL */
|
|
|
|
if (!ENGINE_ctrl(conn->engine, ENGINE_CTRL_GET_CMD_FROM_NAME,
|
|
|
|
0, (void *)cmd_name, NULL)) {
|
|
|
|
wpa_printf(MSG_ERROR, "ENGINE: engine does not support"
|
|
|
|
" loading certificates");
|
|
|
|
ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
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)
|
|
|
|
{
|
2015-10-06 20:05:53 +02:00
|
|
|
#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
|
2008-02-28 02:34:43 +01:00
|
|
|
wpa_printf(MSG_DEBUG, "ENGINE: engine deinit");
|
|
|
|
if (conn->private_key) {
|
|
|
|
EVP_PKEY_free(conn->private_key);
|
|
|
|
conn->private_key = NULL;
|
|
|
|
}
|
|
|
|
if (conn->engine) {
|
2015-10-06 20:05:53 +02:00
|
|
|
#if !defined(OPENSSL_IS_BORINGSSL)
|
2008-02-28 02:34:43 +01:00
|
|
|
ENGINE_finish(conn->engine);
|
2015-10-06 20:05:53 +02:00
|
|
|
#endif /* !OPENSSL_IS_BORINGSSL */
|
2008-02-28 02:34:43 +01:00
|
|
|
conn->engine = NULL;
|
|
|
|
}
|
2015-10-06 20:05:53 +02:00
|
|
|
#endif /* ANDROID || !OPENSSL_NO_ENGINE */
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int tls_get_errors(void *ssl_ctx)
|
|
|
|
{
|
|
|
|
int count = 0;
|
|
|
|
unsigned long err;
|
|
|
|
|
|
|
|
while ((err = ERR_get_error())) {
|
|
|
|
wpa_printf(MSG_INFO, "TLS - SSL error: %s",
|
|
|
|
ERR_error_string(err, NULL));
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2014-04-09 12:02:53 +02:00
|
|
|
|
2015-10-11 10:35:35 +02:00
|
|
|
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 "?";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-04-09 12:02:53 +02:00
|
|
|
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;
|
|
|
|
|
2015-10-11 10:14:00 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-10-11 10:35:35 +02:00
|
|
|
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));
|
2014-04-09 12:02:53 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-09 22:55:41 +01:00
|
|
|
struct tls_connection * tls_connection_init(void *ssl_ctx)
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
2015-08-23 18:22:13 +02:00
|
|
|
struct tls_data *data = ssl_ctx;
|
|
|
|
SSL_CTX *ssl = data->ssl;
|
2014-12-09 22:55:41 +01:00
|
|
|
struct tls_connection *conn;
|
2008-05-21 09:10:10 +02:00
|
|
|
long options;
|
2014-12-09 22:55:41 +01:00
|
|
|
struct tls_context *context = SSL_CTX_get_app_data(ssl);
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2014-12-09 22:55:41 +01:00
|
|
|
conn = os_zalloc(sizeof(*conn));
|
|
|
|
if (conn == NULL)
|
|
|
|
return NULL;
|
2015-08-23 18:22:13 +02:00
|
|
|
conn->ssl_ctx = ssl;
|
2014-12-09 22:55:41 +01:00
|
|
|
conn->ssl = SSL_new(ssl);
|
|
|
|
if (conn->ssl == NULL) {
|
2008-02-28 02:34:43 +01:00
|
|
|
tls_show_errors(MSG_INFO, __func__,
|
|
|
|
"Failed to initialize new SSL connection");
|
2014-12-09 22:55:41 +01:00
|
|
|
os_free(conn);
|
|
|
|
return NULL;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
2014-12-09 22:55:41 +01:00
|
|
|
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);
|
2008-05-21 09:10:10 +02:00
|
|
|
options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
|
|
|
SSL_OP_SINGLE_DH_USE;
|
|
|
|
#ifdef SSL_OP_NO_COMPRESSION
|
|
|
|
options |= SSL_OP_NO_COMPRESSION;
|
|
|
|
#endif /* SSL_OP_NO_COMPRESSION */
|
2014-12-09 22:55:41 +01:00
|
|
|
SSL_set_options(conn->ssl, options);
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2014-12-09 22:55:41 +01:00
|
|
|
conn->ssl_in = BIO_new(BIO_s_mem());
|
|
|
|
if (!conn->ssl_in) {
|
2008-02-28 02:34:43 +01:00
|
|
|
tls_show_errors(MSG_INFO, __func__,
|
|
|
|
"Failed to create a new BIO for ssl_in");
|
2014-12-09 22:55:41 +01:00
|
|
|
SSL_free(conn->ssl);
|
|
|
|
os_free(conn);
|
|
|
|
return NULL;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
2014-12-09 22:55:41 +01:00
|
|
|
conn->ssl_out = BIO_new(BIO_s_mem());
|
|
|
|
if (!conn->ssl_out) {
|
2008-02-28 02:34:43 +01:00
|
|
|
tls_show_errors(MSG_INFO, __func__,
|
|
|
|
"Failed to create a new BIO for ssl_out");
|
|
|
|
SSL_free(conn->ssl);
|
2014-12-09 22:55:41 +01:00
|
|
|
BIO_free(conn->ssl_in);
|
2008-02-28 02:34:43 +01:00
|
|
|
os_free(conn);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-12-09 22:55:41 +01:00
|
|
|
SSL_set_bio(conn->ssl, conn->ssl_in, conn->ssl_out);
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
return conn;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
|
|
|
|
{
|
|
|
|
if (conn == NULL)
|
|
|
|
return;
|
2015-08-23 21:08:27 +02:00
|
|
|
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);
|
|
|
|
}
|
2008-02-28 02:34:43 +01:00
|
|
|
SSL_free(conn->ssl);
|
|
|
|
tls_engine_deinit(conn);
|
|
|
|
os_free(conn->subject_match);
|
|
|
|
os_free(conn->altsubject_match);
|
2013-10-07 03:02:16 +02:00
|
|
|
os_free(conn->suffix_match);
|
2015-01-14 14:31:28 +01:00
|
|
|
os_free(conn->domain_match);
|
2008-02-28 02:34:43 +01:00
|
|
|
os_free(conn->session_ticket);
|
|
|
|
os_free(conn);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
|
|
|
|
{
|
|
|
|
return conn ? SSL_is_init_finished(conn->ssl) : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
|
|
|
|
{
|
|
|
|
if (conn == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Shutdown previous TLS connection without notifying the peer
|
|
|
|
* because the connection was already terminated in practice
|
|
|
|
* and "close notify" shutdown alert would confuse AS. */
|
|
|
|
SSL_set_quiet_shutdown(conn->ssl, 1);
|
|
|
|
SSL_shutdown(conn->ssl);
|
2015-08-17 21:50:41 +02:00
|
|
|
return SSL_clear(conn->ssl) == 1 ? 0 : -1;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int tls_match_altsubject_component(X509 *cert, int type,
|
|
|
|
const char *value, size_t len)
|
|
|
|
{
|
|
|
|
GENERAL_NAME *gen;
|
|
|
|
void *ext;
|
2014-09-19 03:40:03 +02:00
|
|
|
int found = 0;
|
|
|
|
stack_index_t i;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
if (gen->type != type)
|
|
|
|
continue;
|
|
|
|
if (os_strlen((char *) gen->d.ia5->data) == len &&
|
|
|
|
os_memcmp(value, gen->d.ia5->data, len) == 0)
|
|
|
|
found++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int tls_match_altsubject(X509 *cert, const char *match)
|
|
|
|
{
|
|
|
|
int type;
|
|
|
|
const char *pos, *end;
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
pos = match;
|
|
|
|
do {
|
|
|
|
if (os_strncmp(pos, "EMAIL:", 6) == 0) {
|
|
|
|
type = GEN_EMAIL;
|
|
|
|
pos += 6;
|
|
|
|
} else if (os_strncmp(pos, "DNS:", 4) == 0) {
|
|
|
|
type = GEN_DNS;
|
|
|
|
pos += 4;
|
|
|
|
} else if (os_strncmp(pos, "URI:", 4) == 0) {
|
|
|
|
type = GEN_URI;
|
|
|
|
pos += 4;
|
|
|
|
} else {
|
|
|
|
wpa_printf(MSG_INFO, "TLS: Invalid altSubjectName "
|
|
|
|
"match '%s'", pos);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
end = os_strchr(pos, ';');
|
|
|
|
while (end) {
|
|
|
|
if (os_strncmp(end + 1, "EMAIL:", 6) == 0 ||
|
|
|
|
os_strncmp(end + 1, "DNS:", 4) == 0 ||
|
|
|
|
os_strncmp(end + 1, "URI:", 4) == 0)
|
|
|
|
break;
|
|
|
|
end = os_strchr(end + 1, ';');
|
|
|
|
}
|
|
|
|
if (end)
|
|
|
|
len = end - pos;
|
|
|
|
else
|
|
|
|
len = os_strlen(pos);
|
|
|
|
if (tls_match_altsubject_component(cert, type, pos, len) > 0)
|
|
|
|
return 1;
|
|
|
|
pos = end + 1;
|
|
|
|
} while (end);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-11-20 22:12:03 +01:00
|
|
|
#ifndef CONFIG_NATIVE_WINDOWS
|
2015-01-14 14:31:28 +01:00
|
|
|
static int domain_suffix_match(const u8 *val, size_t len, const char *match,
|
|
|
|
int full)
|
2013-10-07 03:02:16 +02:00
|
|
|
{
|
|
|
|
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);
|
2015-01-14 14:31:28 +01:00
|
|
|
if (match_len > len || (full && match_len != len))
|
2013-10-07 03:02:16 +02:00
|
|
|
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;
|
|
|
|
}
|
2013-11-20 22:12:03 +01:00
|
|
|
#endif /* CONFIG_NATIVE_WINDOWS */
|
2013-10-07 03:02:16 +02:00
|
|
|
|
|
|
|
|
2015-01-14 14:31:28 +01:00
|
|
|
static int tls_match_suffix(X509 *cert, const char *match, int full)
|
2013-10-07 03:02:16 +02:00
|
|
|
{
|
2013-11-20 22:12:03 +01:00
|
|
|
#ifdef CONFIG_NATIVE_WINDOWS
|
|
|
|
/* wincrypt.h has conflicting X509_NAME definition */
|
|
|
|
return -1;
|
|
|
|
#else /* CONFIG_NATIVE_WINDOWS */
|
2013-10-07 03:02:16 +02:00
|
|
|
GENERAL_NAME *gen;
|
|
|
|
void *ext;
|
|
|
|
int i;
|
2014-10-07 10:44:56 +02:00
|
|
|
stack_index_t j;
|
2013-10-07 03:02:16 +02:00
|
|
|
int dns_name = 0;
|
|
|
|
X509_NAME *name;
|
|
|
|
|
2015-01-14 14:31:28 +01:00
|
|
|
wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s",
|
|
|
|
full ? "": "suffix ", match);
|
2013-10-07 03:02:16 +02:00
|
|
|
|
|
|
|
ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
|
|
|
|
|
2014-10-07 10:44:56 +02:00
|
|
|
for (j = 0; ext && j < sk_GENERAL_NAME_num(ext); j++) {
|
|
|
|
gen = sk_GENERAL_NAME_value(ext, j);
|
2013-10-07 03:02:16 +02:00
|
|
|
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,
|
2015-01-14 14:31:28 +01:00
|
|
|
gen->d.dNSName->length, match, full) ==
|
|
|
|
1) {
|
|
|
|
wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
|
|
|
|
full ? "Match" : "Suffix match");
|
2013-10-07 03:02:16 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2015-01-14 14:31:28 +01:00
|
|
|
if (domain_suffix_match(cn->data, cn->length, match, full) == 1)
|
|
|
|
{
|
|
|
|
wpa_printf(MSG_DEBUG, "TLS: %s in commonName found",
|
|
|
|
full ? "Match" : "Suffix match");
|
2013-10-07 03:02:16 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-14 14:31:28 +01:00
|
|
|
wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found",
|
|
|
|
full ? "": "suffix ");
|
2013-10-07 03:02:16 +02:00
|
|
|
return 0;
|
2013-11-20 22:12:03 +01:00
|
|
|
#endif /* CONFIG_NATIVE_WINDOWS */
|
2013-10-07 03:02:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-02-13 10:14:23 +01:00
|
|
|
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;
|
2013-05-09 23:22:08 +02:00
|
|
|
struct tls_context *context = conn->context;
|
2010-02-13 10:14:23 +01:00
|
|
|
|
2013-05-09 23:22:08 +02:00
|
|
|
if (context->event_cb == NULL)
|
2010-02-13 10:14:23 +01:00
|
|
|
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;
|
2013-05-09 23:22:08 +02:00
|
|
|
context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
|
2010-02-13 10:14:23 +01:00
|
|
|
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;
|
2013-05-09 23:22:08 +02:00
|
|
|
struct tls_context *context = conn->context;
|
2015-01-14 12:29:40 +01:00
|
|
|
char *altsubject[TLS_MAX_ALT_SUBJECT];
|
|
|
|
int alt, num_altsubject = 0;
|
|
|
|
GENERAL_NAME *gen;
|
|
|
|
void *ext;
|
|
|
|
stack_index_t i;
|
2010-02-13 10:14:23 +01:00
|
|
|
#ifdef CONFIG_SHA256
|
|
|
|
u8 hash[32];
|
|
|
|
#endif /* CONFIG_SHA256 */
|
|
|
|
|
2013-05-09 23:22:08 +02:00
|
|
|
if (context->event_cb == NULL)
|
2010-02-13 10:14:23 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
os_memset(&ev, 0, sizeof(ev));
|
EAP peer: External server certificate chain validation
This adds support for optional functionality to validate server
certificate chain in TLS-based EAP methods in an external program.
wpa_supplicant control interface is used to indicate when such
validation is needed and what the result of the external validation is.
This external validation can extend or replace the internal validation.
When ca_cert or ca_path parameter is set, the internal validation is
used. If these parameters are omitted, only the external validation is
used. It needs to be understood that leaving those parameters out will
disable most of the validation steps done with the TLS library and that
configuration is not really recommend.
By default, the external validation is not used. It can be enabled by
addingtls_ext_cert_check=1 into the network profile phase1 parameter.
When enabled, external validation is required through the CTRL-REQ/RSP
mechanism similarly to other EAP authentication parameters through the
control interface.
The request to perform external validation is indicated by the following
event:
CTRL-REQ-EXT_CERT_CHECK-<id>:External server certificate validation needed for SSID <ssid>
Before that event, the server certificate chain is provided with the
CTRL-EVENT-EAP-PEER-CERT events that include the cert=<hexdump>
parameter. depth=# indicates which certificate is in question (0 for the
server certificate, 1 for its issues, and so on).
The result of the external validation is provided with the following
command:
CTRL-RSP-EXT_CERT_CHECK-<id>:<good|bad>
It should be noted that this is currently enabled only for OpenSSL (and
BoringSSL/LibreSSL). Due to the constraints in the library API, the
validation result from external processing cannot be reported cleanly
with TLS alert. In other words, if the external validation reject the
server certificate chain, the pending TLS handshake is terminated
without sending more messages to the server.
Signed-off-by: Jouni Malinen <j@w1.fi>
2015-12-12 17:16:54 +01:00
|
|
|
if (conn->cert_probe || (conn->flags & TLS_CONN_EXT_CERT_CHECK) ||
|
|
|
|
context->cert_in_cb) {
|
2010-02-13 10:14:23 +01:00
|
|
|
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;
|
2015-01-14 12:29:40 +01:00
|
|
|
|
|
|
|
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';
|
|
|
|
}
|
|
|
|
|
|
|
|
for (alt = 0; alt < num_altsubject; alt++)
|
|
|
|
ev.peer_cert.altsubject[alt] = altsubject[alt];
|
|
|
|
ev.peer_cert.num_altsubject = num_altsubject;
|
|
|
|
|
2013-05-09 23:22:08 +02:00
|
|
|
context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
|
2010-02-13 10:14:23 +01:00
|
|
|
wpabuf_free(cert);
|
2015-01-14 12:29:40 +01:00
|
|
|
for (alt = 0; alt < num_altsubject; alt++)
|
|
|
|
os_free(altsubject[alt]);
|
2010-02-13 10:14:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
|
|
|
|
{
|
|
|
|
char buf[256];
|
|
|
|
X509 *err_cert;
|
|
|
|
int err, depth;
|
|
|
|
SSL *ssl;
|
|
|
|
struct tls_connection *conn;
|
2013-05-09 23:22:08 +02:00
|
|
|
struct tls_context *context;
|
2015-01-14 14:31:28 +01:00
|
|
|
char *match, *altmatch, *suffix_match, *domain_match;
|
2010-02-13 10:14:23 +01:00
|
|
|
const char *err_str;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
|
2014-02-10 11:55:08 +01:00
|
|
|
if (!err_cert)
|
|
|
|
return 0;
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
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, sizeof(buf));
|
|
|
|
|
|
|
|
conn = SSL_get_app_data(ssl);
|
2011-04-14 01:50:52 +02:00
|
|
|
if (conn == NULL)
|
|
|
|
return 0;
|
2013-06-29 23:54:24 +02:00
|
|
|
|
|
|
|
if (depth == 0)
|
|
|
|
conn->peer_cert = err_cert;
|
|
|
|
else if (depth == 1)
|
|
|
|
conn->peer_issuer = err_cert;
|
2013-12-09 03:35:11 +01:00
|
|
|
else if (depth == 2)
|
|
|
|
conn->peer_issuer_issuer = err_cert;
|
2013-06-29 23:54:24 +02:00
|
|
|
|
2013-05-09 23:22:08 +02:00
|
|
|
context = conn->context;
|
2011-04-14 01:50:52 +02:00
|
|
|
match = conn->subject_match;
|
|
|
|
altmatch = conn->altsubject_match;
|
2013-10-07 03:02:16 +02:00
|
|
|
suffix_match = conn->suffix_match;
|
2015-01-14 14:31:28 +01:00
|
|
|
domain_match = conn->domain_match;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2010-02-13 10:14:23 +01:00
|
|
|
if (!preverify_ok && !conn->ca_cert_verify)
|
|
|
|
preverify_ok = 1;
|
|
|
|
if (!preverify_ok && depth > 0 && conn->server_cert_only)
|
|
|
|
preverify_ok = 1;
|
2011-07-05 10:29:42 +02:00
|
|
|
if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) &&
|
|
|
|
(err == X509_V_ERR_CERT_HAS_EXPIRED ||
|
|
|
|
err == X509_V_ERR_CERT_NOT_YET_VALID)) {
|
|
|
|
wpa_printf(MSG_DEBUG, "OpenSSL: Ignore certificate validity "
|
|
|
|
"time mismatch");
|
|
|
|
preverify_ok = 1;
|
|
|
|
}
|
2010-02-13 10:14:23 +01:00
|
|
|
|
|
|
|
err_str = X509_verify_cert_error_string(err);
|
|
|
|
|
|
|
|
#ifdef CONFIG_SHA256
|
2015-03-04 16:24:18 +01:00
|
|
|
/*
|
|
|
|
* Do not require preverify_ok so we can explicity allow otherwise
|
|
|
|
* invalid pinned server certificates.
|
|
|
|
*/
|
|
|
|
if (depth == 0 && conn->server_cert_only) {
|
2010-02-13 10:14:23 +01:00
|
|
|
struct wpabuf *cert;
|
|
|
|
cert = get_x509_cert(err_cert);
|
|
|
|
if (!cert) {
|
|
|
|
wpa_printf(MSG_DEBUG, "OpenSSL: Could not fetch "
|
|
|
|
"server certificate data");
|
2008-02-28 02:34:43 +01:00
|
|
|
preverify_ok = 0;
|
2010-02-13 10:14:23 +01:00
|
|
|
} 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;
|
2015-03-04 16:24:18 +01:00
|
|
|
} else if (!preverify_ok) {
|
|
|
|
/*
|
|
|
|
* Certificate matches pinned certificate, allow
|
|
|
|
* regardless of other problems.
|
|
|
|
*/
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"OpenSSL: Ignore validation issues for a pinned server certificate");
|
|
|
|
preverify_ok = 1;
|
2010-02-13 10:14:23 +01:00
|
|
|
}
|
|
|
|
wpabuf_free(cert);
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
}
|
2010-02-13 10:14:23 +01:00
|
|
|
#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);
|
2013-10-07 03:02:16 +02:00
|
|
|
} else if (depth == 0 && suffix_match &&
|
2015-01-14 14:31:28 +01:00
|
|
|
!tls_match_suffix(err_cert, suffix_match, 0)) {
|
2013-10-07 03:02:16 +02:00
|
|
|
wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found",
|
|
|
|
suffix_match);
|
|
|
|
preverify_ok = 0;
|
|
|
|
openssl_tls_fail_event(conn, err_cert, err, depth, buf,
|
|
|
|
"Domain suffix mismatch",
|
|
|
|
TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
|
2015-01-14 14:31:28 +01:00
|
|
|
} else if (depth == 0 && domain_match &&
|
|
|
|
!tls_match_suffix(err_cert, domain_match, 1)) {
|
|
|
|
wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found",
|
|
|
|
domain_match);
|
|
|
|
preverify_ok = 0;
|
|
|
|
openssl_tls_fail_event(conn, err_cert, err, depth, buf,
|
|
|
|
"Domain mismatch",
|
|
|
|
TLS_FAIL_DOMAIN_MISMATCH);
|
2010-02-13 10:14:23 +01:00
|
|
|
} 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);
|
|
|
|
}
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2015-10-08 15:03:06 +02:00
|
|
|
#ifdef OPENSSL_IS_BORINGSSL
|
2015-12-04 13:04:31 +01:00
|
|
|
if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) &&
|
|
|
|
preverify_ok) {
|
2015-10-08 15:03:06 +02:00
|
|
|
enum ocsp_result res;
|
|
|
|
|
2015-12-04 13:04:31 +01:00
|
|
|
res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert,
|
|
|
|
conn->peer_issuer,
|
|
|
|
conn->peer_issuer_issuer);
|
2015-10-08 15:03:06 +02:00
|
|
|
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 */
|
|
|
|
|
EAP peer: External server certificate chain validation
This adds support for optional functionality to validate server
certificate chain in TLS-based EAP methods in an external program.
wpa_supplicant control interface is used to indicate when such
validation is needed and what the result of the external validation is.
This external validation can extend or replace the internal validation.
When ca_cert or ca_path parameter is set, the internal validation is
used. If these parameters are omitted, only the external validation is
used. It needs to be understood that leaving those parameters out will
disable most of the validation steps done with the TLS library and that
configuration is not really recommend.
By default, the external validation is not used. It can be enabled by
addingtls_ext_cert_check=1 into the network profile phase1 parameter.
When enabled, external validation is required through the CTRL-REQ/RSP
mechanism similarly to other EAP authentication parameters through the
control interface.
The request to perform external validation is indicated by the following
event:
CTRL-REQ-EXT_CERT_CHECK-<id>:External server certificate validation needed for SSID <ssid>
Before that event, the server certificate chain is provided with the
CTRL-EVENT-EAP-PEER-CERT events that include the cert=<hexdump>
parameter. depth=# indicates which certificate is in question (0 for the
server certificate, 1 for its issues, and so on).
The result of the external validation is provided with the following
command:
CTRL-RSP-EXT_CERT_CHECK-<id>:<good|bad>
It should be noted that this is currently enabled only for OpenSSL (and
BoringSSL/LibreSSL). Due to the constraints in the library API, the
validation result from external processing cannot be reported cleanly
with TLS alert. In other words, if the external validation reject the
server certificate chain, the pending TLS handshake is terminated
without sending more messages to the server.
Signed-off-by: Jouni Malinen <j@w1.fi>
2015-12-12 17:16:54 +01:00
|
|
|
if (depth == 0 && preverify_ok && context->event_cb != NULL)
|
2013-05-09 23:22:08 +02:00
|
|
|
context->event_cb(context->cb_ctx,
|
|
|
|
TLS_CERT_CHAIN_SUCCESS, NULL);
|
2012-06-04 20:10:01 +02:00
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
return preverify_ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef OPENSSL_NO_STDIO
|
2015-08-23 18:22:13 +02:00
|
|
|
static int tls_load_ca_der(struct tls_data *data, const char *ca_cert)
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
2015-08-23 18:22:13 +02:00
|
|
|
SSL_CTX *ssl_ctx = data->ssl;
|
2008-02-28 02:34:43 +01:00
|
|
|
X509_LOOKUP *lookup;
|
|
|
|
int ret = 0;
|
|
|
|
|
2015-02-04 00:58:37 +01:00
|
|
|
lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(ssl_ctx),
|
2008-02-28 02:34:43 +01:00
|
|
|
X509_LOOKUP_file());
|
|
|
|
if (lookup == NULL) {
|
|
|
|
tls_show_errors(MSG_WARNING, __func__,
|
|
|
|
"Failed add lookup for X509 store");
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif /* OPENSSL_NO_STDIO */
|
|
|
|
|
|
|
|
|
2015-08-23 18:22:13 +02:00
|
|
|
static int tls_connection_ca_cert(struct tls_data *data,
|
|
|
|
struct tls_connection *conn,
|
2008-02-28 02:34:43 +01:00
|
|
|
const char *ca_cert, const u8 *ca_cert_blob,
|
|
|
|
size_t ca_cert_blob_len, const char *ca_path)
|
|
|
|
{
|
2015-08-23 18:22:13 +02:00
|
|
|
SSL_CTX *ssl_ctx = data->ssl;
|
2015-02-04 00:58:37 +01:00
|
|
|
X509_STORE *store;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove previously configured trusted CA certificates before adding
|
|
|
|
* new ones.
|
|
|
|
*/
|
2015-02-04 00:58:37 +01:00
|
|
|
store = X509_STORE_new();
|
|
|
|
if (store == NULL) {
|
2008-02-28 02:34:43 +01:00
|
|
|
wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new "
|
|
|
|
"certificate store", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
2015-02-04 00:58:37 +01:00
|
|
|
SSL_CTX_set_cert_store(ssl_ctx, store);
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2010-02-13 10:14:23 +01:00
|
|
|
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 */
|
|
|
|
}
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
if (ca_cert_blob) {
|
2015-01-27 12:37:49 +01:00
|
|
|
X509 *cert = d2i_X509(NULL,
|
|
|
|
(const unsigned char **) &ca_cert_blob,
|
2008-02-28 02:34:43 +01:00
|
|
|
ca_cert_blob_len);
|
|
|
|
if (cert == NULL) {
|
|
|
|
tls_show_errors(MSG_WARNING, __func__,
|
|
|
|
"Failed to parse ca_cert_blob");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-02-04 00:58:37 +01:00
|
|
|
if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx),
|
|
|
|
cert)) {
|
2008-02-28 02:34:43 +01:00
|
|
|
unsigned long err = ERR_peek_error();
|
|
|
|
tls_show_errors(MSG_WARNING, __func__,
|
|
|
|
"Failed to add ca_cert_blob to "
|
|
|
|
"certificate store");
|
|
|
|
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 {
|
|
|
|
X509_free(cert);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
X509_free(cert);
|
|
|
|
wpa_printf(MSG_DEBUG, "OpenSSL: %s - added ca_cert_blob "
|
|
|
|
"to certificate store", __func__);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-26 11:08:20 +01:00
|
|
|
#ifdef ANDROID
|
|
|
|
if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) {
|
|
|
|
BIO *bio = BIO_from_keystore(&ca_cert[11]);
|
|
|
|
STACK_OF(X509_INFO) *stack = NULL;
|
2014-09-19 03:40:03 +02:00
|
|
|
stack_index_t i;
|
2011-02-26 11:08:20 +01:00
|
|
|
|
|
|
|
if (bio) {
|
|
|
|
stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
|
|
|
|
BIO_free(bio);
|
|
|
|
}
|
|
|
|
if (!stack)
|
|
|
|
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(ssl_ctx->cert_store,
|
|
|
|
info->x509);
|
|
|
|
}
|
|
|
|
if (info->crl) {
|
|
|
|
X509_STORE_add_crl(ssl_ctx->cert_store,
|
|
|
|
info->crl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sk_X509_INFO_pop_free(stack, X509_INFO_free);
|
|
|
|
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* ANDROID */
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
#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");
|
|
|
|
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 &&
|
2015-08-23 18:22:13 +02:00
|
|
|
tls_load_ca_der(data, ca_cert) == 0) {
|
2008-02-28 02:34:43 +01:00
|
|
|
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");
|
2015-08-23 18:22:13 +02:00
|
|
|
tls_get_errors(data);
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
#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 */
|
2010-02-13 10:14:23 +01:00
|
|
|
conn->ca_cert_verify = 0;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-23 18:22:13 +02:00
|
|
|
static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert)
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
2015-08-23 18:22:13 +02:00
|
|
|
SSL_CTX *ssl_ctx = data->ssl;
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
if (ca_cert) {
|
|
|
|
if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1)
|
|
|
|
{
|
|
|
|
tls_show_errors(MSG_WARNING, __func__,
|
|
|
|
"Failed to load root certificates");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
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_global_set_verify(void *ssl_ctx, int check_crl)
|
|
|
|
{
|
|
|
|
int flags;
|
|
|
|
|
|
|
|
if (check_crl) {
|
2015-08-23 18:22:13 +02:00
|
|
|
struct tls_data *data = ssl_ctx;
|
|
|
|
X509_STORE *cs = SSL_CTX_get_cert_store(data->ssl);
|
2008-02-28 02:34:43 +01:00
|
|
|
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(struct tls_connection *conn,
|
|
|
|
const char *subject_match,
|
2013-10-07 03:02:16 +02:00
|
|
|
const char *altsubject_match,
|
2015-01-14 14:31:28 +01:00
|
|
|
const char *suffix_match,
|
|
|
|
const char *domain_match)
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
|
|
|
os_free(conn->subject_match);
|
|
|
|
conn->subject_match = NULL;
|
|
|
|
if (subject_match) {
|
|
|
|
conn->subject_match = os_strdup(subject_match);
|
|
|
|
if (conn->subject_match == NULL)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
os_free(conn->altsubject_match);
|
|
|
|
conn->altsubject_match = NULL;
|
|
|
|
if (altsubject_match) {
|
|
|
|
conn->altsubject_match = os_strdup(altsubject_match);
|
|
|
|
if (conn->altsubject_match == NULL)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-10-07 03:02:16 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-01-14 14:31:28 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-23 20:14:16 +02:00
|
|
|
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 */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
|
2015-08-23 20:11:01 +02:00
|
|
|
int verify_peer, unsigned int flags,
|
|
|
|
const u8 *session_ctx, size_t session_ctx_len)
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
2008-05-29 19:36:18 +02:00
|
|
|
static int counter = 0;
|
2015-08-23 21:08:27 +02:00
|
|
|
struct tls_data *data = ssl_ctx;
|
2008-05-29 19:36:18 +02:00
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
if (conn == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (verify_peer) {
|
2010-02-13 10:14:23 +01:00
|
|
|
conn->ca_cert_verify = 1;
|
2008-02-28 02:34:43 +01:00
|
|
|
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
|
|
|
|
SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
|
|
|
|
SSL_VERIFY_CLIENT_ONCE, tls_verify_cb);
|
|
|
|
} else {
|
2010-02-13 10:14:23 +01:00
|
|
|
conn->ca_cert_verify = 0;
|
2008-02-28 02:34:43 +01:00
|
|
|
SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
|
|
|
|
}
|
|
|
|
|
2015-08-23 20:14:16 +02:00
|
|
|
tls_set_conn_flags(conn->ssl, flags);
|
|
|
|
conn->flags = flags;
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
SSL_set_accept_state(conn->ssl);
|
|
|
|
|
2015-08-23 21:08:27 +02:00
|
|
|
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);
|
|
|
|
}
|
2008-05-29 19:36:18 +02:00
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int tls_connection_client_cert(struct tls_connection *conn,
|
|
|
|
const char *client_cert,
|
|
|
|
const u8 *client_cert_blob,
|
|
|
|
size_t client_cert_blob_len)
|
|
|
|
{
|
|
|
|
if (client_cert == NULL && client_cert_blob == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2011-02-26 11:08:20 +01:00
|
|
|
#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 */
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
#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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SSL_use_certificate_file(conn->ssl, client_cert,
|
|
|
|
SSL_FILETYPE_PEM) == 1) {
|
2011-02-22 21:19:55 +01:00
|
|
|
ERR_clear_error();
|
2008-02-28 02:34:43 +01:00
|
|
|
wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (PEM)"
|
|
|
|
" --> OK");
|
|
|
|
return 0;
|
|
|
|
}
|
2011-02-22 21:19:55 +01:00
|
|
|
|
|
|
|
tls_show_errors(MSG_DEBUG, __func__,
|
|
|
|
"SSL_use_certificate_file failed");
|
2008-02-28 02:34:43 +01:00
|
|
|
#else /* OPENSSL_NO_STDIO */
|
|
|
|
wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
|
|
|
|
#endif /* OPENSSL_NO_STDIO */
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-23 18:22:13 +02:00
|
|
|
static int tls_global_client_cert(struct tls_data *data,
|
|
|
|
const char *client_cert)
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
|
|
|
#ifndef OPENSSL_NO_STDIO
|
2015-08-23 18:22:13 +02:00
|
|
|
SSL_CTX *ssl_ctx = data->ssl;
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
if (client_cert == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (SSL_CTX_use_certificate_file(ssl_ctx, client_cert,
|
|
|
|
SSL_FILETYPE_ASN1) != 1 &&
|
2011-11-19 11:06:59 +01:00
|
|
|
SSL_CTX_use_certificate_chain_file(ssl_ctx, client_cert) != 1 &&
|
2008-02-28 02:34:43 +01:00
|
|
|
SSL_CTX_use_certificate_file(ssl_ctx, client_cert,
|
|
|
|
SSL_FILETYPE_PEM) != 1) {
|
|
|
|
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 */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
|
|
|
|
{
|
|
|
|
if (password == NULL) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
os_strlcpy(buf, (char *) password, size);
|
|
|
|
return os_strlen(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef PKCS12_FUNCS
|
2015-08-23 18:22:13 +02:00
|
|
|
static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12,
|
2008-02-28 02:34:43 +01:00
|
|
|
const char *passwd)
|
|
|
|
{
|
|
|
|
EVP_PKEY *pkey;
|
|
|
|
X509 *cert;
|
|
|
|
STACK_OF(X509) *certs;
|
|
|
|
int res = 0;
|
|
|
|
char buf[256];
|
|
|
|
|
|
|
|
pkey = NULL;
|
|
|
|
cert = NULL;
|
|
|
|
certs = NULL;
|
2015-08-18 01:06:02 +02:00
|
|
|
if (!passwd)
|
|
|
|
passwd = "";
|
2008-02-28 02:34:43 +01:00
|
|
|
if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) {
|
|
|
|
tls_show_errors(MSG_DEBUG, __func__,
|
|
|
|
"Failed to parse PKCS12 file");
|
|
|
|
PKCS12_free(p12);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 data");
|
|
|
|
|
|
|
|
if (cert) {
|
|
|
|
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;
|
|
|
|
} else {
|
2015-08-23 18:22:13 +02:00
|
|
|
if (SSL_CTX_use_certificate(data->ssl, cert) != 1)
|
2008-02-28 02:34:43 +01:00
|
|
|
res = -1;
|
|
|
|
}
|
|
|
|
X509_free(cert);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pkey) {
|
|
|
|
wpa_printf(MSG_DEBUG, "TLS: Got private key from PKCS12");
|
|
|
|
if (ssl) {
|
|
|
|
if (SSL_use_PrivateKey(ssl, pkey) != 1)
|
|
|
|
res = -1;
|
|
|
|
} else {
|
2015-08-23 18:22:13 +02:00
|
|
|
if (SSL_CTX_use_PrivateKey(data->ssl, pkey) != 1)
|
2008-02-28 02:34:43 +01:00
|
|
|
res = -1;
|
|
|
|
}
|
|
|
|
EVP_PKEY_free(pkey);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (certs) {
|
2015-11-17 16:21:02 +01:00
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER)
|
2015-08-11 00:02:27 +02:00
|
|
|
SSL_clear_chain_certs(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_add1_chain_cert(ssl, cert) != 1) {
|
2015-08-24 18:36:34 +02:00
|
|
|
tls_show_errors(MSG_DEBUG, __func__,
|
|
|
|
"Failed to add additional certificate");
|
2015-08-11 00:02:27 +02:00
|
|
|
res = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2015-08-24 18:36:34 +02:00
|
|
|
if (!res) {
|
|
|
|
/* Try to continue anyway */
|
|
|
|
}
|
2015-08-11 00:02:27 +02:00
|
|
|
sk_X509_free(certs);
|
2015-08-17 20:34:11 +02:00
|
|
|
#ifndef OPENSSL_IS_BORINGSSL
|
2015-08-11 00:02:27 +02:00
|
|
|
res = SSL_build_cert_chain(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");
|
|
|
|
}
|
2015-08-17 20:34:11 +02:00
|
|
|
#endif /* OPENSSL_IS_BORINGSSL */
|
2015-08-11 00:02:27 +02:00
|
|
|
/*
|
|
|
|
* 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 */
|
2015-08-10 23:40:27 +02:00
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
|
2015-08-23 18:22:13 +02:00
|
|
|
SSL_CTX_clear_extra_chain_certs(data->ssl);
|
2015-08-10 23:40:27 +02:00
|
|
|
#endif /* OPENSSL_VERSION_NUMBER >= 0x10001000L */
|
2008-02-28 02:34:43 +01:00
|
|
|
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...
|
|
|
|
*/
|
2015-08-23 18:22:13 +02:00
|
|
|
if (SSL_CTX_add_extra_chain_cert(data->ssl, cert) != 1)
|
|
|
|
{
|
2008-02-28 02:34:43 +01:00
|
|
|
res = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sk_X509_free(certs);
|
2015-08-11 00:02:27 +02:00
|
|
|
#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
PKCS12_free(p12);
|
|
|
|
|
|
|
|
if (res < 0)
|
2015-08-23 18:22:13 +02:00
|
|
|
tls_get_errors(data);
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
#endif /* PKCS12_FUNCS */
|
|
|
|
|
|
|
|
|
2015-08-23 18:22:13 +02:00
|
|
|
static int tls_read_pkcs12(struct tls_data *data, SSL *ssl,
|
|
|
|
const char *private_key, const char *passwd)
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
|
|
|
#ifdef PKCS12_FUNCS
|
|
|
|
FILE *f;
|
|
|
|
PKCS12 *p12;
|
|
|
|
|
|
|
|
f = fopen(private_key, "rb");
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-08-23 18:22:13 +02:00
|
|
|
return tls_parse_pkcs12(data, ssl, p12, passwd);
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
#else /* PKCS12_FUNCS */
|
|
|
|
wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read "
|
|
|
|
"p12/pfx files");
|
|
|
|
return -1;
|
|
|
|
#endif /* PKCS12_FUNCS */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-23 18:22:13 +02:00
|
|
|
static int tls_read_pkcs12_blob(struct tls_data *data, SSL *ssl,
|
2008-02-28 02:34:43 +01:00
|
|
|
const u8 *blob, size_t len, const char *passwd)
|
|
|
|
{
|
|
|
|
#ifdef PKCS12_FUNCS
|
|
|
|
PKCS12 *p12;
|
|
|
|
|
2015-01-27 12:37:49 +01:00
|
|
|
p12 = d2i_PKCS12(NULL, (const unsigned char **) &blob, len);
|
2008-02-28 02:34:43 +01:00
|
|
|
if (p12 == NULL) {
|
|
|
|
tls_show_errors(MSG_INFO, __func__,
|
|
|
|
"Failed to use PKCS#12 blob");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-08-23 18:22:13 +02:00
|
|
|
return tls_parse_pkcs12(data, ssl, p12, passwd);
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
#else /* PKCS12_FUNCS */
|
|
|
|
wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse "
|
|
|
|
"p12/pfx blobs");
|
|
|
|
return -1;
|
|
|
|
#endif /* PKCS12_FUNCS */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-05-26 11:04:35 +02:00
|
|
|
#ifndef OPENSSL_NO_ENGINE
|
2008-05-23 09:49:59 +02:00
|
|
|
static int tls_engine_get_cert(struct tls_connection *conn,
|
|
|
|
const char *cert_id,
|
|
|
|
X509 **cert)
|
|
|
|
{
|
|
|
|
/* this runs after the private key is loaded so no PIN is required */
|
|
|
|
struct {
|
|
|
|
const char *cert_id;
|
|
|
|
X509 *cert;
|
|
|
|
} params;
|
|
|
|
params.cert_id = cert_id;
|
|
|
|
params.cert = NULL;
|
|
|
|
|
|
|
|
if (!ENGINE_ctrl_cmd(conn->engine, "LOAD_CERT_CTRL",
|
|
|
|
0, ¶ms, NULL, 1)) {
|
2015-04-16 02:57:51 +02:00
|
|
|
unsigned long err = ERR_get_error();
|
|
|
|
|
2008-05-23 09:49:59 +02:00
|
|
|
wpa_printf(MSG_ERROR, "ENGINE: cannot load client cert with id"
|
|
|
|
" '%s' [%s]", cert_id,
|
2015-04-16 02:57:51 +02:00
|
|
|
ERR_error_string(err, NULL));
|
|
|
|
if (tls_is_pin_error(err))
|
|
|
|
return TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN;
|
2008-05-23 09:49:59 +02:00
|
|
|
return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
|
|
|
|
}
|
|
|
|
if (!params.cert) {
|
|
|
|
wpa_printf(MSG_ERROR, "ENGINE: did not properly cert with id"
|
|
|
|
" '%s'", cert_id);
|
|
|
|
return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
|
|
|
|
}
|
|
|
|
*cert = params.cert;
|
|
|
|
return 0;
|
|
|
|
}
|
2008-05-26 11:04:35 +02:00
|
|
|
#endif /* OPENSSL_NO_ENGINE */
|
2008-05-23 09:49:59 +02:00
|
|
|
|
|
|
|
|
|
|
|
static int tls_connection_engine_client_cert(struct tls_connection *conn,
|
|
|
|
const char *cert_id)
|
|
|
|
{
|
|
|
|
#ifndef OPENSSL_NO_ENGINE
|
|
|
|
X509 *cert;
|
|
|
|
|
|
|
|
if (tls_engine_get_cert(conn, cert_id, &cert))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!SSL_use_certificate(conn->ssl, cert)) {
|
|
|
|
tls_show_errors(MSG_ERROR, __func__,
|
|
|
|
"SSL_use_certificate failed");
|
|
|
|
X509_free(cert);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
X509_free(cert);
|
|
|
|
wpa_printf(MSG_DEBUG, "ENGINE: SSL_use_certificate --> "
|
|
|
|
"OK");
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
#else /* OPENSSL_NO_ENGINE */
|
|
|
|
return -1;
|
|
|
|
#endif /* OPENSSL_NO_ENGINE */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-23 18:22:13 +02:00
|
|
|
static int tls_connection_engine_ca_cert(struct tls_data *data,
|
2008-05-23 09:49:59 +02:00
|
|
|
struct tls_connection *conn,
|
|
|
|
const char *ca_cert_id)
|
|
|
|
{
|
|
|
|
#ifndef OPENSSL_NO_ENGINE
|
|
|
|
X509 *cert;
|
2015-08-23 18:22:13 +02:00
|
|
|
SSL_CTX *ssl_ctx = data->ssl;
|
2015-02-04 00:58:37 +01:00
|
|
|
X509_STORE *store;
|
2008-05-23 09:49:59 +02:00
|
|
|
|
|
|
|
if (tls_engine_get_cert(conn, ca_cert_id, &cert))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* start off the same as tls_connection_ca_cert */
|
2015-02-04 00:58:37 +01:00
|
|
|
store = X509_STORE_new();
|
|
|
|
if (store == NULL) {
|
2008-05-23 09:49:59 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new "
|
|
|
|
"certificate store", __func__);
|
|
|
|
X509_free(cert);
|
|
|
|
return -1;
|
|
|
|
}
|
2015-02-04 00:58:37 +01:00
|
|
|
SSL_CTX_set_cert_store(ssl_ctx, store);
|
|
|
|
if (!X509_STORE_add_cert(store, cert)) {
|
2008-05-23 09:49:59 +02:00
|
|
|
unsigned long err = ERR_peek_error();
|
|
|
|
tls_show_errors(MSG_WARNING, __func__,
|
|
|
|
"Failed to add CA certificate from engine "
|
|
|
|
"to certificate store");
|
|
|
|
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 {
|
|
|
|
X509_free(cert);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
X509_free(cert);
|
|
|
|
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);
|
2012-08-04 23:36:08 +02:00
|
|
|
conn->ca_cert_verify = 1;
|
|
|
|
|
2008-05-23 09:49:59 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
#else /* OPENSSL_NO_ENGINE */
|
|
|
|
return -1;
|
|
|
|
#endif /* OPENSSL_NO_ENGINE */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
static int tls_connection_engine_private_key(struct tls_connection *conn)
|
|
|
|
{
|
2015-10-06 20:05:53 +02:00
|
|
|
#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
|
2008-02-28 02:34:43 +01:00
|
|
|
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 */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-23 18:22:13 +02:00
|
|
|
static int tls_connection_private_key(struct tls_data *data,
|
2008-02-28 02:34:43 +01:00
|
|
|
struct tls_connection *conn,
|
|
|
|
const char *private_key,
|
|
|
|
const char *private_key_passwd,
|
|
|
|
const u8 *private_key_blob,
|
|
|
|
size_t private_key_blob_len)
|
|
|
|
{
|
2015-08-23 18:22:13 +02:00
|
|
|
SSL_CTX *ssl_ctx = data->ssl;
|
2008-02-28 02:34:43 +01:00
|
|
|
char *passwd;
|
|
|
|
int ok;
|
|
|
|
|
|
|
|
if (private_key == NULL && private_key_blob == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (private_key_passwd) {
|
|
|
|
passwd = os_strdup(private_key_passwd);
|
|
|
|
if (passwd == NULL)
|
|
|
|
return -1;
|
|
|
|
} else
|
|
|
|
passwd = NULL;
|
|
|
|
|
|
|
|
SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
|
|
|
|
SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-08-23 18:22:13 +02:00
|
|
|
if (tls_read_pkcs12_blob(data, conn->ssl, private_key_blob,
|
2008-02-28 02:34:43 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 /* OPENSSL_NO_STDIO */
|
|
|
|
wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
|
|
|
|
__func__);
|
|
|
|
#endif /* OPENSSL_NO_STDIO */
|
|
|
|
|
2015-08-23 18:22:13 +02:00
|
|
|
if (tls_read_pkcs12(data, conn->ssl, private_key, passwd)
|
2008-02-28 02:34:43 +01:00
|
|
|
== 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) {
|
2011-02-22 21:19:55 +01:00
|
|
|
tls_show_errors(MSG_INFO, __func__,
|
|
|
|
"Failed to load private key");
|
2008-02-28 02:34:43 +01:00
|
|
|
os_free(passwd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ERR_clear_error();
|
|
|
|
SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
|
|
|
|
os_free(passwd);
|
2012-08-04 23:38:08 +02:00
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
if (!SSL_check_private_key(conn->ssl)) {
|
|
|
|
tls_show_errors(MSG_INFO, __func__, "Private key failed "
|
|
|
|
"verification");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "SSL: Private key loaded successfully");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-23 18:22:13 +02:00
|
|
|
static int tls_global_private_key(struct tls_data *data,
|
|
|
|
const char *private_key,
|
2008-02-28 02:34:43 +01:00
|
|
|
const char *private_key_passwd)
|
|
|
|
{
|
2015-08-23 18:22:13 +02:00
|
|
|
SSL_CTX *ssl_ctx = data->ssl;
|
2008-02-28 02:34:43 +01:00
|
|
|
char *passwd;
|
|
|
|
|
|
|
|
if (private_key == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (private_key_passwd) {
|
|
|
|
passwd = os_strdup(private_key_passwd);
|
|
|
|
if (passwd == NULL)
|
|
|
|
return -1;
|
|
|
|
} else
|
|
|
|
passwd = NULL;
|
|
|
|
|
|
|
|
SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
|
|
|
|
SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
|
|
|
|
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 */
|
2015-08-23 18:22:13 +02:00
|
|
|
tls_read_pkcs12(data, NULL, private_key, passwd)) {
|
2008-02-28 02:34:43 +01:00
|
|
|
tls_show_errors(MSG_INFO, __func__,
|
|
|
|
"Failed to load private key");
|
|
|
|
os_free(passwd);
|
|
|
|
ERR_clear_error();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
os_free(passwd);
|
|
|
|
ERR_clear_error();
|
|
|
|
SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
|
2012-08-04 23:38:08 +02:00
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
if (!SSL_CTX_check_private_key(ssl_ctx)) {
|
|
|
|
tls_show_errors(MSG_INFO, __func__,
|
|
|
|
"Private key failed verification");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int tls_connection_dh(struct tls_connection *conn, const char *dh_file)
|
|
|
|
{
|
|
|
|
#ifdef OPENSSL_NO_DH
|
|
|
|
if (dh_file == NULL)
|
|
|
|
return 0;
|
|
|
|
wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but "
|
|
|
|
"dh_file specified");
|
|
|
|
return -1;
|
|
|
|
#else /* OPENSSL_NO_DH */
|
|
|
|
DH *dh;
|
|
|
|
BIO *bio;
|
|
|
|
|
|
|
|
/* TODO: add support for dh_blob */
|
|
|
|
if (dh_file == NULL)
|
|
|
|
return 0;
|
|
|
|
if (conn == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
bio = BIO_new_file(dh_file, "r");
|
|
|
|
if (bio == NULL) {
|
|
|
|
wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s",
|
|
|
|
dh_file, ERR_error_string(ERR_get_error(), NULL));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
|
|
|
|
BIO_free(bio);
|
|
|
|
#ifndef OPENSSL_NO_DSA
|
|
|
|
while (dh == NULL) {
|
|
|
|
DSA *dsa;
|
|
|
|
wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -"
|
|
|
|
" trying to parse as DSA params", dh_file,
|
|
|
|
ERR_error_string(ERR_get_error(), NULL));
|
|
|
|
bio = BIO_new_file(dh_file, "r");
|
|
|
|
if (bio == NULL)
|
|
|
|
break;
|
|
|
|
dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL);
|
|
|
|
BIO_free(bio);
|
|
|
|
if (!dsa) {
|
|
|
|
wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file "
|
|
|
|
"'%s': %s", dh_file,
|
|
|
|
ERR_error_string(ERR_get_error(), NULL));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format");
|
|
|
|
dh = DSA_dup_DH(dsa);
|
|
|
|
DSA_free(dsa);
|
|
|
|
if (dh == NULL) {
|
|
|
|
wpa_printf(MSG_INFO, "TLS: Failed to convert DSA "
|
|
|
|
"params into DH params");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_DSA */
|
|
|
|
if (dh == NULL) {
|
|
|
|
wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file "
|
|
|
|
"'%s'", dh_file);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SSL_set_tmp_dh(conn->ssl, dh) != 1) {
|
|
|
|
wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': "
|
|
|
|
"%s", dh_file,
|
|
|
|
ERR_error_string(ERR_get_error(), NULL));
|
|
|
|
DH_free(dh);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
DH_free(dh);
|
|
|
|
return 0;
|
|
|
|
#endif /* OPENSSL_NO_DH */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-23 18:22:13 +02:00
|
|
|
static int tls_global_dh(struct tls_data *data, const char *dh_file)
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
|
|
|
#ifdef OPENSSL_NO_DH
|
|
|
|
if (dh_file == NULL)
|
|
|
|
return 0;
|
|
|
|
wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but "
|
|
|
|
"dh_file specified");
|
|
|
|
return -1;
|
|
|
|
#else /* OPENSSL_NO_DH */
|
2015-08-23 18:22:13 +02:00
|
|
|
SSL_CTX *ssl_ctx = data->ssl;
|
2008-02-28 02:34:43 +01:00
|
|
|
DH *dh;
|
|
|
|
BIO *bio;
|
|
|
|
|
|
|
|
/* TODO: add support for dh_blob */
|
|
|
|
if (dh_file == NULL)
|
|
|
|
return 0;
|
|
|
|
if (ssl_ctx == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
bio = BIO_new_file(dh_file, "r");
|
|
|
|
if (bio == NULL) {
|
|
|
|
wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s",
|
|
|
|
dh_file, ERR_error_string(ERR_get_error(), NULL));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
|
|
|
|
BIO_free(bio);
|
|
|
|
#ifndef OPENSSL_NO_DSA
|
|
|
|
while (dh == NULL) {
|
|
|
|
DSA *dsa;
|
|
|
|
wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -"
|
|
|
|
" trying to parse as DSA params", dh_file,
|
|
|
|
ERR_error_string(ERR_get_error(), NULL));
|
|
|
|
bio = BIO_new_file(dh_file, "r");
|
|
|
|
if (bio == NULL)
|
|
|
|
break;
|
|
|
|
dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL);
|
|
|
|
BIO_free(bio);
|
|
|
|
if (!dsa) {
|
|
|
|
wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file "
|
|
|
|
"'%s': %s", dh_file,
|
|
|
|
ERR_error_string(ERR_get_error(), NULL));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format");
|
|
|
|
dh = DSA_dup_DH(dsa);
|
|
|
|
DSA_free(dsa);
|
|
|
|
if (dh == NULL) {
|
|
|
|
wpa_printf(MSG_INFO, "TLS: Failed to convert DSA "
|
|
|
|
"params into DH params");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_DSA */
|
|
|
|
if (dh == NULL) {
|
|
|
|
wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file "
|
|
|
|
"'%s'", dh_file);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SSL_CTX_set_tmp_dh(ssl_ctx, dh) != 1) {
|
|
|
|
wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': "
|
|
|
|
"%s", dh_file,
|
|
|
|
ERR_error_string(ERR_get_error(), NULL));
|
|
|
|
DH_free(dh);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
DH_free(dh);
|
|
|
|
return 0;
|
|
|
|
#endif /* OPENSSL_NO_DH */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-01 17:17:14 +02:00
|
|
|
int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
|
|
|
|
struct tls_random *keys)
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
|
|
|
SSL *ssl;
|
|
|
|
|
|
|
|
if (conn == NULL || keys == NULL)
|
|
|
|
return -1;
|
|
|
|
ssl = conn->ssl;
|
2015-11-17 16:21:02 +01:00
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
|
2008-02-28 02:34:43 +01:00
|
|
|
if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
os_memset(keys, 0, sizeof(*keys));
|
|
|
|
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;
|
2015-07-27 23:58:39 +02:00
|
|
|
#else
|
|
|
|
if (ssl == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
os_memset(keys, 0, sizeof(*keys));
|
|
|
|
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));
|
|
|
|
#endif
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-01 14:52:35 +02:00
|
|
|
#ifndef CONFIG_FIPS
|
2015-03-31 15:15:39 +02:00
|
|
|
static int openssl_get_keyblock_size(SSL *ssl)
|
|
|
|
{
|
2015-11-17 16:21:02 +01:00
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
|
2015-03-31 15:15:39 +02:00
|
|
|
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));
|
2015-07-28 09:48:05 +02:00
|
|
|
#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
|
2015-03-31 15:15:39 +02:00
|
|
|
}
|
2015-08-01 14:52:35 +02:00
|
|
|
#endif /* CONFIG_FIPS */
|
2015-03-31 15:15:39 +02:00
|
|
|
|
|
|
|
|
2015-08-23 18:22:13 +02:00
|
|
|
static int openssl_tls_prf(struct tls_connection *conn,
|
2015-03-31 14:47:32 +02:00
|
|
|
const char *label, int server_random_first,
|
2015-03-31 15:15:39 +02:00
|
|
|
int skip_keyblock, u8 *out, size_t out_len)
|
2015-03-31 14:47:32 +02:00
|
|
|
{
|
|
|
|
#ifdef CONFIG_FIPS
|
|
|
|
wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS "
|
|
|
|
"mode");
|
|
|
|
return -1;
|
|
|
|
#else /* CONFIG_FIPS */
|
2015-11-17 16:21:02 +01:00
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
|
2015-03-31 14:47:32 +02:00
|
|
|
SSL *ssl;
|
|
|
|
u8 *rnd;
|
|
|
|
int ret = -1;
|
2015-03-31 15:15:39 +02:00
|
|
|
int skip = 0;
|
|
|
|
u8 *tmp_out = NULL;
|
|
|
|
u8 *_out = out;
|
2015-07-28 10:40:17 +02:00
|
|
|
const char *ver;
|
2015-03-31 14:47:32 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* TLS library did not support 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 || ssl->s3 == NULL || ssl->session == NULL ||
|
2015-04-19 19:34:12 +02:00
|
|
|
ssl->session->master_key_length <= 0)
|
2015-03-31 14:47:32 +02:00
|
|
|
return -1;
|
2015-07-28 10:40:17 +02:00
|
|
|
ver = SSL_get_version(ssl);
|
2015-03-31 14:47:32 +02:00
|
|
|
|
2015-03-31 15:15:39 +02:00
|
|
|
if (skip_keyblock) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-03-31 14:47:32 +02:00
|
|
|
rnd = os_malloc(2 * SSL3_RANDOM_SIZE);
|
2015-06-17 15:16:34 +02:00
|
|
|
if (!rnd) {
|
|
|
|
os_free(tmp_out);
|
2015-03-31 14:47:32 +02:00
|
|
|
return -1;
|
2015-06-17 15:16:34 +02:00
|
|
|
}
|
|
|
|
|
2015-03-31 14:47:32 +02:00
|
|
|
if (server_random_first) {
|
|
|
|
os_memcpy(rnd, ssl->s3->server_random, SSL3_RANDOM_SIZE);
|
|
|
|
os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->client_random,
|
|
|
|
SSL3_RANDOM_SIZE);
|
|
|
|
} else {
|
|
|
|
os_memcpy(rnd, ssl->s3->client_random, SSL3_RANDOM_SIZE);
|
|
|
|
os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->server_random,
|
|
|
|
SSL3_RANDOM_SIZE);
|
|
|
|
}
|
|
|
|
|
2015-07-28 10:40:17 +02:00
|
|
|
if (os_strcmp(ver, "TLSv1.2") == 0) {
|
|
|
|
tls_prf_sha256(ssl->session->master_key,
|
|
|
|
ssl->session->master_key_length,
|
|
|
|
label, rnd, 2 * SSL3_RANDOM_SIZE,
|
|
|
|
_out, skip + out_len);
|
2015-03-31 14:47:32 +02:00
|
|
|
ret = 0;
|
2015-07-28 10:40:17 +02:00
|
|
|
} else if (tls_prf_sha1_md5(ssl->session->master_key,
|
|
|
|
ssl->session->master_key_length,
|
|
|
|
label, rnd, 2 * SSL3_RANDOM_SIZE,
|
|
|
|
_out, skip + out_len) == 0) {
|
|
|
|
ret = 0;
|
|
|
|
}
|
2015-03-31 14:47:32 +02:00
|
|
|
os_free(rnd);
|
2015-03-31 15:15:39 +02:00
|
|
|
if (ret == 0 && skip_keyblock)
|
|
|
|
os_memcpy(out, _out + skip, out_len);
|
|
|
|
bin_clear_free(tmp_out, skip);
|
2015-03-31 14:47:32 +02:00
|
|
|
|
|
|
|
return ret;
|
2015-07-28 09:48:05 +02:00
|
|
|
#else
|
|
|
|
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;
|
2015-07-28 10:40:17 +02:00
|
|
|
const char *ver;
|
2015-07-28 09:48:05 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* TLS library did not support 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;
|
2015-07-28 10:40:17 +02:00
|
|
|
ver = SSL_get_version(ssl);
|
2015-07-28 09:48:05 +02:00
|
|
|
sess = SSL_get_session(ssl);
|
2015-07-28 10:40:17 +02:00
|
|
|
if (!ver || !sess)
|
2015-07-28 09:48:05 +02:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (skip_keyblock) {
|
|
|
|
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));
|
|
|
|
|
|
|
|
if (server_random_first) {
|
|
|
|
os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE);
|
|
|
|
os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random,
|
|
|
|
SSL3_RANDOM_SIZE);
|
|
|
|
} else {
|
|
|
|
os_memcpy(rnd, client_random, SSL3_RANDOM_SIZE);
|
|
|
|
os_memcpy(rnd + SSL3_RANDOM_SIZE, server_random,
|
|
|
|
SSL3_RANDOM_SIZE);
|
|
|
|
}
|
|
|
|
|
2015-07-28 10:40:17 +02:00
|
|
|
if (os_strcmp(ver, "TLSv1.2") == 0) {
|
|
|
|
tls_prf_sha256(master_key, master_key_len,
|
|
|
|
label, rnd, 2 * SSL3_RANDOM_SIZE,
|
|
|
|
_out, skip + out_len);
|
2015-07-28 09:48:05 +02:00
|
|
|
ret = 0;
|
2015-07-28 10:40:17 +02:00
|
|
|
} else if (tls_prf_sha1_md5(master_key, master_key_len,
|
|
|
|
label, rnd, 2 * SSL3_RANDOM_SIZE,
|
|
|
|
_out, skip + out_len) == 0) {
|
|
|
|
ret = 0;
|
|
|
|
}
|
2015-07-28 09:48:05 +02:00
|
|
|
os_memset(master_key, 0, sizeof(master_key));
|
|
|
|
os_free(rnd);
|
|
|
|
if (ret == 0 && skip_keyblock)
|
|
|
|
os_memcpy(out, _out + skip, out_len);
|
|
|
|
bin_clear_free(tmp_out, skip);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
#endif
|
2015-03-31 14:47:32 +02:00
|
|
|
#endif /* CONFIG_FIPS */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
|
|
|
|
const char *label, int server_random_first,
|
2015-03-31 15:15:39 +02:00
|
|
|
int skip_keyblock, u8 *out, size_t out_len)
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
2012-08-16 18:29:34 +02:00
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
|
|
|
|
SSL *ssl;
|
|
|
|
if (conn == NULL)
|
|
|
|
return -1;
|
2015-03-31 15:15:39 +02:00
|
|
|
if (server_random_first || skip_keyblock)
|
2015-08-23 18:22:13 +02:00
|
|
|
return openssl_tls_prf(conn, label,
|
2015-03-31 15:15:39 +02:00
|
|
|
server_random_first, skip_keyblock,
|
|
|
|
out, out_len);
|
2012-08-16 18:29:34 +02:00
|
|
|
ssl = conn->ssl;
|
|
|
|
if (SSL_export_keying_material(ssl, out, out_len, label,
|
|
|
|
os_strlen(label), NULL, 0, 0) == 1) {
|
|
|
|
wpa_printf(MSG_DEBUG, "OpenSSL: Using internal PRF");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
2015-08-23 18:22:13 +02:00
|
|
|
return openssl_tls_prf(conn, label, server_random_first,
|
2015-03-31 15:15:39 +02:00
|
|
|
skip_keyblock, out, out_len);
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-20 17:17:55 +01:00
|
|
|
static struct wpabuf *
|
|
|
|
openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data,
|
|
|
|
int server)
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
|
|
|
int res;
|
2009-12-20 17:17:55 +01:00
|
|
|
struct wpabuf *out_data;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Give TLS handshake data from the server (if available) to OpenSSL
|
|
|
|
* for processing.
|
|
|
|
*/
|
2015-07-27 23:54:08 +02:00
|
|
|
if (in_data && wpabuf_len(in_data) > 0 &&
|
2009-12-20 17:17:55 +01:00
|
|
|
BIO_write(conn->ssl_in, wpabuf_head(in_data), wpabuf_len(in_data))
|
|
|
|
< 0) {
|
2008-02-28 02:34:43 +01:00
|
|
|
tls_show_errors(MSG_INFO, __func__,
|
|
|
|
"Handshake failed - BIO_write");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initiate TLS handshake or continue the existing handshake */
|
2009-12-20 17:17:55 +01:00
|
|
|
if (server)
|
|
|
|
res = SSL_accept(conn->ssl);
|
|
|
|
else
|
|
|
|
res = SSL_connect(conn->ssl);
|
2008-02-28 02:34:43 +01:00
|
|
|
if (res != 1) {
|
|
|
|
int err = SSL_get_error(conn->ssl, res);
|
|
|
|
if (err == SSL_ERROR_WANT_READ)
|
|
|
|
wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want "
|
|
|
|
"more data");
|
|
|
|
else if (err == SSL_ERROR_WANT_WRITE)
|
|
|
|
wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want to "
|
|
|
|
"write");
|
|
|
|
else {
|
|
|
|
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);
|
2009-12-20 17:17:55 +01:00
|
|
|
out_data = wpabuf_alloc(res);
|
2008-02-28 02:34:43 +01:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2009-12-20 17:17:55 +01:00
|
|
|
res = res == 0 ? 0 : BIO_read(conn->ssl_out, wpabuf_mhead(out_data),
|
|
|
|
res);
|
2008-02-28 02:34:43 +01:00
|
|
|
if (res < 0) {
|
|
|
|
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");
|
|
|
|
}
|
2009-12-20 17:17:55 +01:00
|
|
|
wpabuf_free(out_data);
|
2008-02-28 02:34:43 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
2009-12-20 17:17:55 +01:00
|
|
|
wpabuf_put(out_data, res);
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
return out_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-20 17:17:55 +01:00
|
|
|
static struct wpabuf *
|
|
|
|
openssl_get_appl_data(struct tls_connection *conn, size_t max_len)
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
2009-12-20 17:17:55 +01:00
|
|
|
struct wpabuf *appl_data;
|
2008-02-28 02:34:43 +01:00
|
|
|
int res;
|
|
|
|
|
2009-12-20 17:17:55 +01:00
|
|
|
appl_data = wpabuf_alloc(max_len + 100);
|
|
|
|
if (appl_data == NULL)
|
2008-02-28 02:34:43 +01:00
|
|
|
return NULL;
|
|
|
|
|
2009-12-20 17:17:55 +01:00
|
|
|
res = SSL_read(conn->ssl, wpabuf_mhead(appl_data),
|
|
|
|
wpabuf_size(appl_data));
|
|
|
|
if (res < 0) {
|
2008-05-29 19:36:18 +02:00
|
|
|
int err = SSL_get_error(conn->ssl, res);
|
2009-12-20 17:17:55 +01:00
|
|
|
if (err == SSL_ERROR_WANT_READ ||
|
|
|
|
err == SSL_ERROR_WANT_WRITE) {
|
|
|
|
wpa_printf(MSG_DEBUG, "SSL: No Application Data "
|
|
|
|
"included");
|
|
|
|
} else {
|
2008-02-28 02:34:43 +01:00
|
|
|
tls_show_errors(MSG_INFO, __func__,
|
2009-12-20 17:17:55 +01:00
|
|
|
"Failed to read possible "
|
|
|
|
"Application Data");
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
2009-12-20 17:17:55 +01:00
|
|
|
wpabuf_free(appl_data);
|
2008-02-28 02:34:43 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
2009-12-20 17:17:55 +01:00
|
|
|
|
|
|
|
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)
|
2008-02-28 02:34:43 +01:00
|
|
|
return NULL;
|
2014-04-09 12:02:53 +02:00
|
|
|
if (conn->invalid_hb_used) {
|
|
|
|
wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
|
|
|
|
wpabuf_free(out_data);
|
|
|
|
return NULL;
|
|
|
|
}
|
2009-12-20 17:17:55 +01:00
|
|
|
|
2015-08-18 00:53:17 +02:00
|
|
|
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));
|
|
|
|
}
|
2009-12-20 17:17:55 +01:00
|
|
|
|
2014-04-09 12:02:53 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
return out_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-20 17:17:55 +01:00
|
|
|
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)
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
|
|
|
int res;
|
2009-12-20 17:17:55 +01:00
|
|
|
struct wpabuf *buf;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
if (conn == NULL)
|
2009-12-20 17:17:55 +01:00
|
|
|
return NULL;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
/* 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");
|
2009-12-20 17:17:55 +01:00
|
|
|
return NULL;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
2009-12-20 17:17:55 +01:00
|
|
|
res = SSL_write(conn->ssl, wpabuf_head(in_data), wpabuf_len(in_data));
|
2008-02-28 02:34:43 +01:00
|
|
|
if (res < 0) {
|
|
|
|
tls_show_errors(MSG_INFO, __func__,
|
|
|
|
"Encryption failed - SSL_write");
|
2009-12-20 17:17:55 +01:00
|
|
|
return NULL;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Read encrypted data to be sent to the server */
|
2009-12-20 17:17:55 +01:00
|
|
|
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));
|
2008-02-28 02:34:43 +01:00
|
|
|
if (res < 0) {
|
|
|
|
tls_show_errors(MSG_INFO, __func__,
|
|
|
|
"Encryption failed - BIO_read");
|
2009-12-20 17:17:55 +01:00
|
|
|
wpabuf_free(buf);
|
|
|
|
return NULL;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
2009-12-20 17:17:55 +01:00
|
|
|
wpabuf_put(buf, res);
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2009-12-20 17:17:55 +01:00
|
|
|
return buf;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-20 17:17:55 +01:00
|
|
|
struct wpabuf * tls_connection_decrypt(void *tls_ctx,
|
|
|
|
struct tls_connection *conn,
|
|
|
|
const struct wpabuf *in_data)
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
|
|
|
int res;
|
2009-12-20 17:17:55 +01:00
|
|
|
struct wpabuf *buf;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
/* Give encrypted data from TLS tunnel for OpenSSL to decrypt. */
|
2009-12-20 17:17:55 +01:00
|
|
|
res = BIO_write(conn->ssl_in, wpabuf_head(in_data),
|
|
|
|
wpabuf_len(in_data));
|
2008-02-28 02:34:43 +01:00
|
|
|
if (res < 0) {
|
|
|
|
tls_show_errors(MSG_INFO, __func__,
|
|
|
|
"Decryption failed - BIO_write");
|
2009-12-20 17:17:55 +01:00
|
|
|
return NULL;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
if (BIO_reset(conn->ssl_out) < 0) {
|
|
|
|
tls_show_errors(MSG_INFO, __func__, "BIO_reset failed");
|
2009-12-20 17:17:55 +01:00
|
|
|
return NULL;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Read decrypted data for further processing */
|
2009-12-20 17:17:55 +01:00
|
|
|
/*
|
|
|
|
* 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));
|
2008-02-28 02:34:43 +01:00
|
|
|
if (res < 0) {
|
|
|
|
tls_show_errors(MSG_INFO, __func__,
|
|
|
|
"Decryption failed - SSL_read");
|
2009-12-20 18:12:59 +01:00
|
|
|
wpabuf_free(buf);
|
2009-12-20 17:17:55 +01:00
|
|
|
return NULL;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
2009-12-20 17:17:55 +01:00
|
|
|
wpabuf_put(buf, res);
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2014-04-09 12:02:53 +02:00
|
|
|
if (conn->invalid_hb_used) {
|
|
|
|
wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
|
|
|
|
wpabuf_free(buf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-12-20 17:17:55 +01:00
|
|
|
return buf;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
|
|
|
|
{
|
2015-02-04 01:04:35 +01:00
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
|
|
|
|
return conn ? SSL_cache_hit(conn->ssl) : 0;
|
|
|
|
#else
|
2008-02-28 02:34:43 +01:00
|
|
|
return conn ? conn->ssl->hit : 0;
|
2015-02-04 01:04:35 +01:00
|
|
|
#endif
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
|
|
|
|
u8 *ciphers)
|
|
|
|
{
|
|
|
|
char buf[100], *pos, *end;
|
|
|
|
u8 *c;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (conn == NULL || conn->ssl == NULL || ciphers == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
buf[0] = '\0';
|
|
|
|
pos = buf;
|
|
|
|
end = pos + sizeof(buf);
|
|
|
|
|
|
|
|
c = ciphers;
|
|
|
|
while (*c != TLS_CIPHER_NONE) {
|
|
|
|
const char *suite;
|
|
|
|
|
|
|
|
switch (*c) {
|
|
|
|
case TLS_CIPHER_RC4_SHA:
|
|
|
|
suite = "RC4-SHA";
|
|
|
|
break;
|
|
|
|
case TLS_CIPHER_AES128_SHA:
|
|
|
|
suite = "AES128-SHA";
|
|
|
|
break;
|
|
|
|
case TLS_CIPHER_RSA_DHE_AES128_SHA:
|
|
|
|
suite = "DHE-RSA-AES128-SHA";
|
|
|
|
break;
|
|
|
|
case TLS_CIPHER_ANON_DH_AES128_SHA:
|
|
|
|
suite = "ADH-AES128-SHA";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
wpa_printf(MSG_DEBUG, "TLS: Unsupported "
|
|
|
|
"cipher selection: %d", *c);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ret = os_snprintf(pos, end - pos, ":%s", suite);
|
Check os_snprintf() result more consistently - automatic 1
This converts os_snprintf() result validation cases to use
os_snprintf_error() where the exact rule used in os_snprintf_error() was
used. These changes were done automatically with spatch using the
following semantic patch:
@@
identifier E1;
expression E2,E3,E4,E5,E6;
statement S1;
@@
(
E1 = os_snprintf(E2, E3, ...);
|
int E1 = os_snprintf(E2, E3, ...);
|
if (E5)
E1 = os_snprintf(E2, E3, ...);
else
E1 = os_snprintf(E2, E3, ...);
|
if (E5)
E1 = os_snprintf(E2, E3, ...);
else if (E6)
E1 = os_snprintf(E2, E3, ...);
else
E1 = 0;
|
if (E5) {
...
E1 = os_snprintf(E2, E3, ...);
} else {
...
return -1;
}
|
if (E5) {
...
E1 = os_snprintf(E2, E3, ...);
} else if (E6) {
...
E1 = os_snprintf(E2, E3, ...);
} else {
...
return -1;
}
|
if (E5) {
...
E1 = os_snprintf(E2, E3, ...);
} else {
...
E1 = os_snprintf(E2, E3, ...);
}
)
? os_free(E4);
- if (E1 < 0 || \( E1 >= E3 \| (size_t) E1 >= E3 \| (unsigned int) E1 >= E3 \| E1 >= (int) E3 \))
+ if (os_snprintf_error(E3, E1))
(
S1
|
{ ... }
)
Signed-off-by: Jouni Malinen <j@w1.fi>
2014-12-08 10:15:51 +01:00
|
|
|
if (os_snprintf_error(end - pos, ret))
|
2008-02-28 02:34:43 +01:00
|
|
|
break;
|
|
|
|
pos += ret;
|
|
|
|
|
|
|
|
c++;
|
|
|
|
}
|
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1);
|
|
|
|
|
2015-11-17 16:21:02 +01:00
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
|
2015-07-28 10:51:55 +02:00
|
|
|
#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
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) {
|
|
|
|
tls_show_errors(MSG_INFO, __func__,
|
|
|
|
"Cipher suite configuration failed");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-08 18:51:03 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
int tls_get_cipher(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_cipher(conn->ssl);
|
|
|
|
if (name == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
os_strlcpy(buf, name, buflen);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int tls_connection_enable_workaround(void *ssl_ctx,
|
|
|
|
struct tls_connection *conn)
|
|
|
|
{
|
|
|
|
SSL_set_options(conn->ssl, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-25 11:06:19 +01:00
|
|
|
#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
|
2008-02-28 02:34:43 +01:00
|
|
|
/* 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 tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
|
|
|
|
int ext_type, const u8 *data,
|
|
|
|
size_t data_len)
|
|
|
|
{
|
2008-11-16 20:29:12 +01:00
|
|
|
if (conn == NULL || conn->ssl == NULL || ext_type != 35)
|
2008-02-28 02:34:43 +01:00
|
|
|
return -1;
|
|
|
|
|
2008-11-16 20:29:12 +01:00
|
|
|
if (SSL_set_session_ticket_ext(conn->ssl, (void *) data,
|
|
|
|
data_len) != 1)
|
|
|
|
return -1;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2009-03-25 11:06:19 +01:00
|
|
|
#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-29 23:54:24 +02:00
|
|
|
#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 */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-05-29 12:51:19 +02:00
|
|
|
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 */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-29 23:54:24 +02:00
|
|
|
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;
|
2013-12-09 03:35:11 +01:00
|
|
|
X509_STORE *store;
|
|
|
|
STACK_OF(X509) *certs = NULL;
|
2013-06-29 23:54:24 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-02-04 00:58:37 +01:00
|
|
|
store = SSL_CTX_get_cert_store(conn->ssl_ctx);
|
2013-12-09 03:35:11 +01:00
|
|
|
if (conn->peer_issuer) {
|
2014-05-29 12:51:19 +02:00
|
|
|
debug_print_cert(conn->peer_issuer, "Add OCSP issuer");
|
2013-12-09 03:35:11 +01:00
|
|
|
|
|
|
|
if (X509_STORE_add_cert(store, conn->peer_issuer) != 1) {
|
|
|
|
tls_show_errors(MSG_INFO, __func__,
|
2015-02-19 12:33:33 +01:00
|
|
|
"OpenSSL: Could not add issuer to certificate store");
|
2013-12-09 03:35:11 +01:00
|
|
|
}
|
|
|
|
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__,
|
2015-02-19 12:33:33 +01:00
|
|
|
"OpenSSL: Could not add issuer to OCSP responder trust store");
|
2013-12-09 03:35:11 +01:00
|
|
|
X509_free(cert);
|
|
|
|
sk_X509_free(certs);
|
|
|
|
certs = NULL;
|
|
|
|
}
|
2015-02-19 12:32:05 +01:00
|
|
|
if (certs && conn->peer_issuer_issuer) {
|
2013-12-09 03:35:11 +01:00
|
|
|
cert = X509_dup(conn->peer_issuer_issuer);
|
|
|
|
if (cert && !sk_X509_push(certs, cert)) {
|
|
|
|
tls_show_errors(
|
|
|
|
MSG_INFO, __func__,
|
2015-02-19 12:33:33 +01:00
|
|
|
"OpenSSL: Could not add issuer's issuer to OCSP responder trust store");
|
2013-12-09 03:35:11 +01:00
|
|
|
X509_free(cert);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
status = OCSP_basic_verify(basic, certs, store, OCSP_TRUSTOTHER);
|
|
|
|
sk_X509_pop_free(certs, X509_free);
|
2013-06-29 23:54:24 +02:00
|
|
|
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");
|
|
|
|
|
2013-09-24 15:36:06 +02:00
|
|
|
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");
|
2013-06-29 23:54:24 +02:00
|
|
|
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_BASICRESP_free(basic);
|
|
|
|
OCSP_RESPONSE_free(rsp);
|
|
|
|
return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2013-09-30 09:56:32 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue");
|
2013-06-29 23:54:24 +02:00
|
|
|
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 */
|
|
|
|
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
|
|
|
|
const struct tls_connection_params *params)
|
|
|
|
{
|
2015-08-23 18:22:13 +02:00
|
|
|
struct tls_data *data = tls_ctx;
|
2008-02-28 02:34:43 +01:00
|
|
|
int ret;
|
|
|
|
unsigned long err;
|
2014-12-18 16:09:47 +01:00
|
|
|
int can_pkcs11 = 0;
|
2014-12-18 16:09:32 +01:00
|
|
|
const char *key_id = params->key_id;
|
|
|
|
const char *cert_id = params->cert_id;
|
|
|
|
const char *ca_cert_id = params->ca_cert_id;
|
2014-12-18 16:09:47 +01:00
|
|
|
const char *engine_id = params->engine ? params->engine_id : NULL;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
if (conn == NULL)
|
|
|
|
return -1;
|
|
|
|
|
2015-12-22 23:28:13 +01:00
|
|
|
if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
|
|
|
|
wpa_printf(MSG_INFO,
|
|
|
|
"OpenSSL: ocsp=3 not supported");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-12-18 16:09:32 +01:00
|
|
|
/*
|
2014-12-18 16:09:47 +01:00
|
|
|
* 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.
|
2014-12-18 16:09:32 +01:00
|
|
|
*/
|
2014-12-18 16:09:47 +01:00
|
|
|
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;
|
2014-12-18 16:09:32 +01:00
|
|
|
key_id = params->private_key;
|
2014-12-18 16:09:47 +01:00
|
|
|
}
|
2014-12-18 16:09:32 +01:00
|
|
|
|
2014-12-18 16:09:47 +01:00
|
|
|
if (!cert_id && params->client_cert && can_pkcs11 &&
|
|
|
|
os_strncmp(params->client_cert, "pkcs11:", 7) == 0) {
|
|
|
|
can_pkcs11 = 2;
|
2014-12-18 16:09:32 +01:00
|
|
|
cert_id = params->client_cert;
|
2014-12-18 16:09:47 +01:00
|
|
|
}
|
2014-12-18 16:09:32 +01:00
|
|
|
|
2014-12-18 16:09:47 +01:00
|
|
|
if (!ca_cert_id && params->ca_cert && can_pkcs11 &&
|
|
|
|
os_strncmp(params->ca_cert, "pkcs11:", 7) == 0) {
|
|
|
|
can_pkcs11 = 2;
|
2014-12-18 16:09:32 +01:00
|
|
|
ca_cert_id = params->ca_cert;
|
2014-12-18 16:09:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If we need to automatically enable the PKCS#11 ENGINE, do so. */
|
|
|
|
if (can_pkcs11 == 2 && !engine_id)
|
|
|
|
engine_id = "pkcs11";
|
2014-12-18 16:09:32 +01:00
|
|
|
|
2015-08-17 20:35:44 +02:00
|
|
|
#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
|
2015-07-28 10:53:13 +02:00
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
2014-12-09 22:55:41 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2015-07-28 10:53:13 +02:00
|
|
|
#endif
|
2015-08-17 20:35:44 +02:00
|
|
|
#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
|
2014-12-09 14:57:03 +01:00
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
while ((err = ERR_get_error())) {
|
|
|
|
wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s",
|
|
|
|
__func__, ERR_error_string(err, NULL));
|
|
|
|
}
|
|
|
|
|
2014-12-18 16:09:47 +01:00
|
|
|
if (engine_id) {
|
2008-05-23 09:49:59 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine");
|
2014-12-18 16:09:47 +01:00
|
|
|
ret = tls_engine_init(conn, engine_id, params->pin,
|
2014-12-18 16:09:32 +01:00
|
|
|
key_id, cert_id, ca_cert_id);
|
2008-05-23 09:49:59 +02:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
2008-02-28 02:34:43 +01:00
|
|
|
if (tls_connection_set_subject_match(conn,
|
|
|
|
params->subject_match,
|
2013-10-07 03:02:16 +02:00
|
|
|
params->altsubject_match,
|
2015-01-14 14:31:28 +01:00
|
|
|
params->suffix_match,
|
|
|
|
params->domain_match))
|
2008-02-28 02:34:43 +01:00
|
|
|
return -1;
|
2008-05-23 09:49:59 +02:00
|
|
|
|
2014-12-18 16:09:47 +01:00
|
|
|
if (engine_id && ca_cert_id) {
|
2015-08-23 18:22:13 +02:00
|
|
|
if (tls_connection_engine_ca_cert(data, conn, ca_cert_id))
|
2008-05-23 09:49:59 +02:00
|
|
|
return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
|
2015-08-23 18:22:13 +02:00
|
|
|
} else if (tls_connection_ca_cert(data, conn, params->ca_cert,
|
2008-05-23 09:49:59 +02:00
|
|
|
params->ca_cert_blob,
|
|
|
|
params->ca_cert_blob_len,
|
|
|
|
params->ca_path))
|
2008-02-28 02:34:43 +01:00
|
|
|
return -1;
|
2008-05-23 09:49:59 +02:00
|
|
|
|
2014-12-18 16:09:47 +01:00
|
|
|
if (engine_id && cert_id) {
|
2014-12-18 16:09:32 +01:00
|
|
|
if (tls_connection_engine_client_cert(conn, cert_id))
|
2008-05-23 09:49:59 +02:00
|
|
|
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))
|
2008-02-28 02:34:43 +01:00
|
|
|
return -1;
|
|
|
|
|
2014-12-18 16:09:47 +01:00
|
|
|
if (engine_id && key_id) {
|
2008-05-23 09:49:59 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "TLS: Using private key from engine");
|
2008-02-28 02:34:43 +01:00
|
|
|
if (tls_connection_engine_private_key(conn))
|
|
|
|
return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
|
2015-08-23 18:22:13 +02:00
|
|
|
} else if (tls_connection_private_key(data, conn,
|
2008-02-28 02:34:43 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tls_connection_dh(conn, params->dh_file)) {
|
|
|
|
wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'",
|
|
|
|
params->dh_file);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-10-12 10:45:21 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-08-23 20:14:16 +02:00
|
|
|
tls_set_conn_flags(conn->ssl, params->flags);
|
2014-02-19 22:21:58 +01:00
|
|
|
|
2015-10-08 15:03:06 +02:00
|
|
|
#ifdef OPENSSL_IS_BORINGSSL
|
|
|
|
if (params->flags & TLS_CONN_REQUEST_OCSP) {
|
|
|
|
SSL_enable_ocsp_stapling(conn->ssl);
|
|
|
|
}
|
|
|
|
#else /* OPENSSL_IS_BORINGSSL */
|
2013-06-29 23:54:24 +02:00
|
|
|
#ifdef HAVE_OCSP
|
|
|
|
if (params->flags & TLS_CONN_REQUEST_OCSP) {
|
2015-08-23 18:22:13 +02:00
|
|
|
SSL_CTX *ssl_ctx = data->ssl;
|
2013-06-29 23:54:24 +02:00
|
|
|
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);
|
|
|
|
}
|
2015-08-18 01:24:06 +02:00
|
|
|
#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");
|
|
|
|
}
|
2013-06-29 23:54:24 +02:00
|
|
|
#endif /* HAVE_OCSP */
|
2015-10-08 15:03:06 +02:00
|
|
|
#endif /* OPENSSL_IS_BORINGSSL */
|
2013-06-29 23:54:24 +02:00
|
|
|
|
2011-07-05 10:29:42 +02:00
|
|
|
conn->flags = params->flags;
|
|
|
|
|
2015-08-23 18:22:13 +02:00
|
|
|
tls_get_errors(data);
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int tls_global_set_params(void *tls_ctx,
|
|
|
|
const struct tls_connection_params *params)
|
|
|
|
{
|
2015-08-23 18:22:13 +02:00
|
|
|
struct tls_data *data = tls_ctx;
|
|
|
|
SSL_CTX *ssl_ctx = data->ssl;
|
2008-02-28 02:34:43 +01:00
|
|
|
unsigned long err;
|
|
|
|
|
|
|
|
while ((err = ERR_get_error())) {
|
|
|
|
wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s",
|
|
|
|
__func__, ERR_error_string(err, NULL));
|
|
|
|
}
|
|
|
|
|
2015-08-23 18:22:13 +02:00
|
|
|
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,
|
2015-06-30 20:48:22 +02:00
|
|
|
params->private_key_passwd) ||
|
2015-08-23 18:22:13 +02:00
|
|
|
tls_global_dh(data, params->dh_file)) {
|
2015-06-30 20:48:22 +02:00
|
|
|
wpa_printf(MSG_INFO, "TLS: Failed to set global parameters");
|
2008-02-28 02:34:43 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-10-12 10:45:21 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-08-17 21:18:54 +02:00
|
|
|
#ifdef SSL_OP_NO_TICKET
|
|
|
|
if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
|
|
|
|
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET);
|
2013-06-30 11:55:52 +02:00
|
|
|
#ifdef SSL_CTX_clear_options
|
2012-08-17 21:18:54 +02:00
|
|
|
else
|
|
|
|
SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET);
|
2013-06-30 11:55:52 +02:00
|
|
|
#endif /* SSL_clear_options */
|
2012-08-17 21:18:54 +02:00
|
|
|
#endif /* SSL_OP_NO_TICKET */
|
|
|
|
|
2013-06-29 23:54:24 +02:00
|
|
|
#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 */
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-25 11:06:19 +01:00
|
|
|
#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
|
2008-02-28 02:34:43 +01:00
|
|
|
/* 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. */
|
|
|
|
|
2014-09-19 03:40:03 +02:00
|
|
|
#ifdef OPENSSL_IS_BORINGSSL
|
|
|
|
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 */
|
2008-02-28 02:34:43 +01:00
|
|
|
static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len,
|
|
|
|
STACK_OF(SSL_CIPHER) *peer_ciphers,
|
|
|
|
SSL_CIPHER **cipher, void *arg)
|
2014-09-19 03:40:03 +02:00
|
|
|
#endif /* OPENSSL_IS_BORINGSSL */
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
|
|
|
struct tls_connection *conn = arg;
|
|
|
|
int ret;
|
|
|
|
|
2015-11-17 16:21:02 +01:00
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
|
2008-02-28 02:34:43 +01:00
|
|
|
if (conn == NULL || conn->session_ticket_cb == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx,
|
|
|
|
conn->session_ticket,
|
|
|
|
conn->session_ticket_len,
|
|
|
|
s->s3->client_random,
|
|
|
|
s->s3->server_random, secret);
|
2015-07-28 00:00:06 +02:00
|
|
|
#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
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
os_free(conn->session_ticket);
|
|
|
|
conn->session_ticket = NULL;
|
|
|
|
|
|
|
|
if (ret <= 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
*secret_len = SSL_MAX_MASTER_KEY_LENGTH;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-16 20:29:12 +01:00
|
|
|
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;
|
|
|
|
}
|
2009-03-25 11:06:19 +01:00
|
|
|
#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
|
|
|
|
int tls_connection_set_session_ticket_cb(void *tls_ctx,
|
|
|
|
struct tls_connection *conn,
|
|
|
|
tls_session_ticket_cb cb,
|
|
|
|
void *ctx)
|
|
|
|
{
|
2009-03-25 11:06:19 +01:00
|
|
|
#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
|
2008-02-28 02:34:43 +01:00
|
|
|
conn->session_ticket_cb = cb;
|
|
|
|
conn->session_ticket_cb_ctx = ctx;
|
|
|
|
|
|
|
|
if (cb) {
|
|
|
|
if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb,
|
|
|
|
conn) != 1)
|
|
|
|
return -1;
|
2008-11-16 20:29:12 +01:00
|
|
|
SSL_set_session_ticket_ext_cb(conn->ssl,
|
|
|
|
tls_session_ticket_ext_cb, conn);
|
2008-02-28 02:34:43 +01:00
|
|
|
} else {
|
|
|
|
if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1)
|
|
|
|
return -1;
|
2008-11-16 20:29:12 +01:00
|
|
|
SSL_set_session_ticket_ext_cb(conn->ssl, NULL, NULL);
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2009-03-25 11:06:19 +01:00
|
|
|
#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
|
2008-02-28 02:34:43 +01:00
|
|
|
return -1;
|
2009-03-25 11:06:19 +01:00
|
|
|
#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
2015-01-11 14:37:38 +01:00
|
|
|
|
|
|
|
|
|
|
|
int tls_get_library_version(char *buf, size_t buf_len)
|
|
|
|
{
|
2015-12-03 22:53:35 +01:00
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
|
|
return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
|
|
|
|
OPENSSL_VERSION_TEXT,
|
|
|
|
OpenSSL_version(OPENSSL_VERSION));
|
|
|
|
#else
|
2015-01-11 14:37:38 +01:00
|
|
|
return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
|
|
|
|
OPENSSL_VERSION_TEXT,
|
|
|
|
SSLeay_version(SSLEAY_VERSION));
|
2015-12-03 22:53:35 +01:00
|
|
|
#endif
|
2015-01-11 14:37:38 +01:00
|
|
|
}
|
2015-08-23 21:01:37 +02:00
|
|
|
|
|
|
|
|
|
|
|
void tls_connection_set_success_data(struct tls_connection *conn,
|
|
|
|
struct wpabuf *data)
|
|
|
|
{
|
2015-08-23 21:08:27 +02:00
|
|
|
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);
|
2015-08-23 21:01:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void tls_connection_set_success_data_resumed(struct tls_connection *conn)
|
|
|
|
{
|
2015-08-23 21:08:27 +02:00
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"OpenSSL: Success data accepted for resumed session");
|
|
|
|
conn->success_data = 1;
|
2015-08-23 21:01:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const struct wpabuf *
|
|
|
|
tls_connection_get_success_data(struct tls_connection *conn)
|
|
|
|
{
|
2015-08-23 21:08:27 +02:00
|
|
|
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);
|
2015-08-23 21:01:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void tls_connection_remove_session(struct tls_connection *conn)
|
|
|
|
{
|
2015-08-23 21:08:27 +02:00
|
|
|
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");
|
2015-08-23 21:01:37 +02:00
|
|
|
}
|