(websocket+tls) fix hang in tls handshake which could lead to ANR, discovered through unittesting.

This commit is contained in:
Benjamin Sergeant 2020-03-20 16:57:27 -07:00
parent 829751b7af
commit 1d6373335c
8 changed files with 58 additions and 19 deletions

View File

@ -1,6 +1,10 @@
# Changelog # Changelog
All changes to this project will be documented in this file. All changes to this project will be documented in this file.
## [8.3.2] - 2020-03-20
(websocket+tls) fix hang in tls handshake which could lead to ANR, discovered through unittesting.
## [8.3.1] - 2020-03-20 ## [8.3.1] - 2020-03-20
(cobra) CobraMetricsPublisher can be configure with an ix::CobraConfig + more unittest use SSL in server + client (cobra) CobraMetricsPublisher can be configure with an ix::CobraConfig + more unittest use SSL in server + client

View File

@ -164,6 +164,26 @@ namespace ix
return false; return false;
} }
OSStatus SocketAppleSSL::tlsHandShake(std::string& errMsg,
const CancellationRequest& isCancellationRequested)
{
OSStatus status;
do
{
status = SSLHandshake(_sslContext);
// Interrupt the handshake
if (isCancellationRequested())
{
errMsg = "Cancellation requested";
return errSSLInternal;
}
} while (status == errSSLWouldBlock || status == errSSLServerAuthCompleted);
return status;
}
// No wait support // No wait support
bool SocketAppleSSL::connect(const std::string& host, bool SocketAppleSSL::connect(const std::string& host,
int port, int port,
@ -190,26 +210,17 @@ namespace ix
Boolean option(1); Boolean option(1);
SSLSetSessionOption(_sslContext, kSSLSessionOptionBreakOnServerAuth, option); SSLSetSessionOption(_sslContext, kSSLSessionOptionBreakOnServerAuth, option);
do status = tlsHandShake(errMsg, isCancellationRequested);
{
status = SSLHandshake(_sslContext);
} while (status == errSSLWouldBlock || status == errSSLServerAuthCompleted);
if (status == errSSLServerAuthCompleted) if (status == errSSLServerAuthCompleted)
{ {
// proceed with the handshake // proceed with the handshake
do status = tlsHandShake(errMsg, isCancellationRequested);
{
status = SSLHandshake(_sslContext);
} while (status == errSSLWouldBlock || status == errSSLServerAuthCompleted);
} }
} }
else else
{ {
do status = tlsHandShake(errMsg, isCancellationRequested);
{
status = SSLHandshake(_sslContext);
} while (status == errSSLWouldBlock || status == errSSLServerAuthCompleted);
} }
} }

View File

@ -37,6 +37,10 @@ namespace ix
static OSStatus writeToSocket(SSLConnectionRef connection, const void* data, size_t* len); static OSStatus writeToSocket(SSLConnectionRef connection, const void* data, size_t* len);
static OSStatus readFromSocket(SSLConnectionRef connection, void* data, size_t* len); static OSStatus readFromSocket(SSLConnectionRef connection, void* data, size_t* len);
OSStatus tlsHandShake(
std::string& errMsg,
const CancellationRequest& isCancellationRequested);
SSLContextRef _sslContext; SSLContextRef _sslContext;
mutable std::mutex _mutex; // AppleSSL routines are not thread-safe mutable std::mutex _mutex; // AppleSSL routines are not thread-safe

View File

@ -194,9 +194,18 @@ namespace ix
int res; int res;
do do
{
{ {
std::lock_guard<std::mutex> lock(_mutex); std::lock_guard<std::mutex> lock(_mutex);
res = mbedtls_ssl_handshake(&_ssl); res = mbedtls_ssl_handshake(&_ssl);
}
if (isCancellationRequested())
{
errMsg = "Cancellation requested";
close();
return false;
}
} while (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE); } while (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE);
if (res != 0) if (res != 0)

View File

@ -224,7 +224,10 @@ namespace ix
return true; return true;
} }
bool SocketOpenSSL::openSSLClientHandshake(const std::string& host, std::string& errMsg) bool SocketOpenSSL::openSSLClientHandshake(
const std::string& host,
std::string& errMsg,
const CancellationRequest& isCancellationRequested)
{ {
while (true) while (true)
{ {
@ -233,6 +236,12 @@ namespace ix
return false; return false;
} }
if (isCancellationRequested())
{
errMsg = "Cancellation requested";
return false;
}
ERR_clear_error(); ERR_clear_error();
int connect_result = SSL_connect(_ssl_connection); int connect_result = SSL_connect(_ssl_connection);
if (connect_result == 1) if (connect_result == 1)
@ -577,7 +586,7 @@ namespace ix
X509_VERIFY_PARAM* param = SSL_get0_param(_ssl_connection); X509_VERIFY_PARAM* param = SSL_get0_param(_ssl_connection);
X509_VERIFY_PARAM_set1_host(param, host.c_str(), 0); X509_VERIFY_PARAM_set1_host(param, host.c_str(), 0);
#endif #endif
handshakeSuccessful = openSSLClientHandshake(host, errMsg); handshakeSuccessful = openSSLClientHandshake(host, errMsg, isCancellationRequested);
} }
if (!handshakeSuccessful) if (!handshakeSuccessful)

View File

@ -39,7 +39,9 @@ namespace ix
void openSSLInitialize(); void openSSLInitialize();
std::string getSSLError(int ret); std::string getSSLError(int ret);
SSL_CTX* openSSLCreateContext(std::string& errMsg); SSL_CTX* openSSLCreateContext(std::string& errMsg);
bool openSSLClientHandshake(const std::string& hostname, std::string& errMsg); bool openSSLClientHandshake(const std::string& hostname,
std::string& errMsg,
const CancellationRequest& isCancellationRequested);
bool openSSLCheckServerCert(SSL* ssl, const std::string& hostname, std::string& errMsg); bool openSSLCheckServerCert(SSL* ssl, const std::string& hostname, std::string& errMsg);
bool checkHost(const std::string& host, const char* pattern); bool checkHost(const std::string& host, const char* pattern);
bool handleTLSOptions(std::string& errMsg); bool handleTLSOptions(std::string& errMsg);

View File

@ -6,4 +6,4 @@
#pragma once #pragma once
#define IX_WEBSOCKET_VERSION "8.3.1" #define IX_WEBSOCKET_VERSION "8.3.2"

View File

@ -147,7 +147,7 @@ namespace
TEST_CASE("Cobra_Metrics_Publisher", "[cobra]") TEST_CASE("Cobra_Metrics_Publisher", "[cobra]")
{ {
int port = getFreePort(); int port = getFreePort();
bool preferTLS = false; bool preferTLS = true;
snake::AppConfig appConfig = makeSnakeServerConfig(port, preferTLS); snake::AppConfig appConfig = makeSnakeServerConfig(port, preferTLS);
// Start a redis server // Start a redis server