2019-06-02 02:41:48 +02:00
|
|
|
/*
|
|
|
|
* IXSocketMbedTLS.cpp
|
2020-04-25 00:50:39 +02:00
|
|
|
* Author: Benjamin Sergeant, Max Weisel
|
2020-04-24 21:47:47 +02:00
|
|
|
* Copyright (c) 2019-2020 Machine Zone, Inc. All rights reserved.
|
2019-06-02 02:41:48 +02:00
|
|
|
*
|
|
|
|
* Some code taken from
|
|
|
|
* https://github.com/rottor12/WsClientLib/blob/master/lib/src/WsClientLib.cpp
|
|
|
|
* and mini_client.c example from mbedtls
|
|
|
|
*/
|
2020-04-24 21:47:47 +02:00
|
|
|
#ifdef IXWEBSOCKET_USE_MBED_TLS
|
2019-06-02 02:41:48 +02:00
|
|
|
|
|
|
|
#include "IXSocketMbedTLS.h"
|
2019-09-23 19:25:23 +02:00
|
|
|
|
2019-06-02 02:41:48 +02:00
|
|
|
#include "IXNetSystem.h"
|
|
|
|
#include "IXSocket.h"
|
2019-09-23 19:25:23 +02:00
|
|
|
#include "IXSocketConnect.h"
|
2023-02-25 22:50:35 +01:00
|
|
|
#include <cstdint>
|
2019-06-02 02:41:48 +02:00
|
|
|
#include <string.h>
|
|
|
|
|
2021-03-24 16:03:56 +01:00
|
|
|
#ifdef _WIN32
|
|
|
|
// For manipulating the certificate store
|
|
|
|
#include <wincrypt.h>
|
|
|
|
#endif
|
|
|
|
|
2019-06-02 02:41:48 +02:00
|
|
|
namespace ix
|
|
|
|
{
|
2019-09-30 06:13:11 +02:00
|
|
|
SocketMbedTLS::SocketMbedTLS(const SocketTLSOptions& tlsOptions, int fd)
|
|
|
|
: Socket(fd)
|
|
|
|
, _tlsOptions(tlsOptions)
|
2019-09-23 03:43:57 +02:00
|
|
|
{
|
2019-09-27 23:07:01 +02:00
|
|
|
initMBedTLS();
|
2019-09-23 03:43:57 +02:00
|
|
|
}
|
|
|
|
|
2019-06-02 02:41:48 +02:00
|
|
|
SocketMbedTLS::~SocketMbedTLS()
|
|
|
|
{
|
2019-09-27 23:07:01 +02:00
|
|
|
SocketMbedTLS::close();
|
2019-06-02 02:41:48 +02:00
|
|
|
}
|
|
|
|
|
2019-09-27 23:07:01 +02:00
|
|
|
void SocketMbedTLS::initMBedTLS()
|
2019-06-02 02:41:48 +02:00
|
|
|
{
|
2019-06-06 23:59:22 +02:00
|
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
|
|
|
2019-06-02 02:41:48 +02:00
|
|
|
mbedtls_ssl_init(&_ssl);
|
|
|
|
mbedtls_ssl_config_init(&_conf);
|
|
|
|
mbedtls_ctr_drbg_init(&_ctr_drbg);
|
2019-09-27 23:07:01 +02:00
|
|
|
mbedtls_entropy_init(&_entropy);
|
2019-09-30 06:13:11 +02:00
|
|
|
mbedtls_x509_crt_init(&_cacert);
|
2019-12-18 20:51:02 +01:00
|
|
|
mbedtls_x509_crt_init(&_cert);
|
2019-12-30 23:38:25 +01:00
|
|
|
mbedtls_pk_init(&_pkey);
|
2024-05-08 20:02:00 +02:00
|
|
|
// Initialize the PSA Crypto API if required by the version of Mbed TLS (3.6.0).
|
2024-04-30 06:12:56 +02:00
|
|
|
// This allows the X.509/TLS libraries to use PSA for crypto operations.
|
|
|
|
// See: https://github.com/Mbed-TLS/mbedtls/blob/development/docs/use-psa-crypto.md
|
2024-05-08 20:02:00 +02:00
|
|
|
if (MBEDTLS_VERSION_MAJOR >= 3 && MBEDTLS_VERSION_MINOR >= 6 && MBEDTLS_VERSION_PATCH >= 0)
|
|
|
|
{
|
|
|
|
psa_crypto_init();
|
|
|
|
}
|
2019-09-27 23:07:01 +02:00
|
|
|
}
|
|
|
|
|
2023-06-22 23:12:51 +02:00
|
|
|
bool SocketMbedTLS::loadSystemCertificates(std::string& errorMsg)
|
2020-05-17 19:32:30 +02:00
|
|
|
{
|
|
|
|
#ifdef _WIN32
|
|
|
|
DWORD flags = CERT_STORE_READONLY_FLAG | CERT_STORE_OPEN_EXISTING_FLAG |
|
|
|
|
CERT_SYSTEM_STORE_CURRENT_USER;
|
|
|
|
HCERTSTORE systemStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, flags, L"Root");
|
|
|
|
|
|
|
|
if (!systemStore)
|
|
|
|
{
|
|
|
|
errorMsg = "CertOpenStore failed with ";
|
|
|
|
errorMsg += std::to_string(GetLastError());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
PCCERT_CONTEXT certificateIterator = NULL;
|
|
|
|
|
|
|
|
int certificateCount = 0;
|
|
|
|
while (certificateIterator = CertEnumCertificatesInStore(systemStore, certificateIterator))
|
|
|
|
{
|
|
|
|
if (certificateIterator->dwCertEncodingType & X509_ASN_ENCODING)
|
|
|
|
{
|
|
|
|
int ret = mbedtls_x509_crt_parse(&_cacert,
|
|
|
|
certificateIterator->pbCertEncoded,
|
|
|
|
certificateIterator->cbCertEncoded);
|
|
|
|
if (ret == 0)
|
|
|
|
{
|
|
|
|
++certificateCount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CertFreeCertificateContext(certificateIterator);
|
|
|
|
CertCloseStore(systemStore, 0);
|
|
|
|
|
|
|
|
if (certificateCount == 0)
|
|
|
|
{
|
|
|
|
errorMsg = "No certificates found";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
#else
|
|
|
|
// On macOS we can query the system cert location from the keychain
|
|
|
|
// On Linux we could try to fetch some local files based on the distribution
|
|
|
|
// On Android we could use JNI to get to the system certs
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-12-18 20:51:02 +01:00
|
|
|
bool SocketMbedTLS::init(const std::string& host, bool isClient, std::string& errMsg)
|
2019-09-27 23:07:01 +02:00
|
|
|
{
|
|
|
|
initMBedTLS();
|
|
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
2019-06-02 02:41:48 +02:00
|
|
|
|
2019-09-23 19:25:23 +02:00
|
|
|
const char* pers = "IXSocketMbedTLS";
|
2019-06-02 02:41:48 +02:00
|
|
|
|
|
|
|
if (mbedtls_ctr_drbg_seed(&_ctr_drbg,
|
|
|
|
mbedtls_entropy_func,
|
|
|
|
&_entropy,
|
2019-09-23 19:25:23 +02:00
|
|
|
(const unsigned char*) pers,
|
2019-06-02 20:03:44 +02:00
|
|
|
strlen(pers)) != 0)
|
2019-06-02 02:41:48 +02:00
|
|
|
{
|
|
|
|
errMsg = "Setting entropy seed failed";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mbedtls_ssl_config_defaults(&_conf,
|
2019-12-20 04:13:55 +01:00
|
|
|
(isClient) ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER,
|
2019-06-02 02:41:48 +02:00
|
|
|
MBEDTLS_SSL_TRANSPORT_STREAM,
|
2019-09-23 19:25:23 +02:00
|
|
|
MBEDTLS_SSL_PRESET_DEFAULT) != 0)
|
2019-06-02 02:41:48 +02:00
|
|
|
{
|
|
|
|
errMsg = "Setting config default failed";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
mbedtls_ssl_conf_rng(&_conf, mbedtls_ctr_drbg_random, &_ctr_drbg);
|
|
|
|
|
2019-12-18 20:51:02 +01:00
|
|
|
if (_tlsOptions.hasCertAndKey())
|
|
|
|
{
|
2019-12-21 00:13:26 +01:00
|
|
|
if (mbedtls_x509_crt_parse_file(&_cert, _tlsOptions.certFile.c_str()) < 0)
|
2019-12-18 20:51:02 +01:00
|
|
|
{
|
|
|
|
errMsg = "Cannot parse cert file '" + _tlsOptions.certFile + "'";
|
|
|
|
return false;
|
|
|
|
}
|
2021-10-22 20:10:58 +02:00
|
|
|
#ifdef IXWEBSOCKET_USE_MBED_TLS_MIN_VERSION_3
|
|
|
|
if (mbedtls_pk_parse_keyfile(&_pkey, _tlsOptions.keyFile.c_str(), "", mbedtls_ctr_drbg_random, &_ctr_drbg) < 0)
|
|
|
|
#else
|
2019-12-21 00:13:26 +01:00
|
|
|
if (mbedtls_pk_parse_keyfile(&_pkey, _tlsOptions.keyFile.c_str(), "") < 0)
|
2021-10-22 20:10:58 +02:00
|
|
|
#endif
|
2019-12-21 00:13:26 +01:00
|
|
|
{
|
|
|
|
errMsg = "Cannot parse key file '" + _tlsOptions.keyFile + "'";
|
|
|
|
return false;
|
|
|
|
}
|
2019-12-31 01:11:34 +01:00
|
|
|
if (mbedtls_ssl_conf_own_cert(&_conf, &_cert, &_pkey) < 0)
|
|
|
|
{
|
|
|
|
errMsg = "Problem configuring cert '" + _tlsOptions.certFile + "'";
|
|
|
|
return false;
|
|
|
|
}
|
2019-12-18 20:51:02 +01:00
|
|
|
}
|
|
|
|
|
2019-09-30 06:13:11 +02:00
|
|
|
if (_tlsOptions.isPeerVerifyDisabled())
|
|
|
|
{
|
|
|
|
mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_NONE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// FIXME: should we call mbedtls_ssl_conf_verify ?
|
2020-05-17 19:32:30 +02:00
|
|
|
mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_REQUIRED);
|
2019-09-30 06:13:11 +02:00
|
|
|
|
|
|
|
if (_tlsOptions.isUsingSystemDefaults())
|
|
|
|
{
|
2020-05-17 19:32:30 +02:00
|
|
|
if (!loadSystemCertificates(errMsg))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2019-09-30 06:13:11 +02:00
|
|
|
}
|
2020-04-25 00:34:00 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (_tlsOptions.isUsingInMemoryCAs())
|
|
|
|
{
|
|
|
|
const char* buffer = _tlsOptions.caFile.c_str();
|
|
|
|
size_t bufferSize =
|
|
|
|
_tlsOptions.caFile.size() + 1; // Needs to include null terminating
|
|
|
|
// character otherwise mbedtls will fail.
|
|
|
|
if (mbedtls_x509_crt_parse(
|
|
|
|
&_cacert, (const unsigned char*) buffer, bufferSize) < 0)
|
|
|
|
{
|
2020-04-25 00:32:11 +02:00
|
|
|
errMsg = "Cannot parse CA from memory.";
|
|
|
|
return false;
|
|
|
|
}
|
2020-04-25 00:34:00 +02:00
|
|
|
}
|
|
|
|
else if (mbedtls_x509_crt_parse_file(&_cacert, _tlsOptions.caFile.c_str()) < 0)
|
|
|
|
{
|
2020-04-25 00:32:11 +02:00
|
|
|
errMsg = "Cannot parse CA file '" + _tlsOptions.caFile + "'";
|
|
|
|
return false;
|
|
|
|
}
|
2019-09-30 06:13:11 +02:00
|
|
|
}
|
2019-12-21 00:13:26 +01:00
|
|
|
|
|
|
|
mbedtls_ssl_conf_ca_chain(&_conf, &_cacert, NULL);
|
2019-09-30 06:13:11 +02:00
|
|
|
}
|
2019-06-02 02:41:48 +02:00
|
|
|
|
2019-06-04 07:12:52 +02:00
|
|
|
if (mbedtls_ssl_setup(&_ssl, &_conf) != 0)
|
2019-06-02 02:41:48 +02:00
|
|
|
{
|
|
|
|
errMsg = "SSL setup failed";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-10-12 15:41:32 +02:00
|
|
|
if (!_tlsOptions.disable_hostname_validation)
|
2019-06-02 02:41:48 +02:00
|
|
|
{
|
2022-10-12 15:41:32 +02:00
|
|
|
if (!host.empty() && mbedtls_ssl_set_hostname(&_ssl, host.c_str()) != 0)
|
|
|
|
{
|
|
|
|
errMsg = "SNI setup failed";
|
|
|
|
return false;
|
|
|
|
}
|
2019-06-02 02:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-12-18 19:43:05 +01:00
|
|
|
bool SocketMbedTLS::accept(std::string& errMsg)
|
|
|
|
{
|
2019-12-18 20:51:02 +01:00
|
|
|
bool isClient = false;
|
|
|
|
bool initialized = init(std::string(), isClient, errMsg);
|
|
|
|
if (!initialized)
|
|
|
|
{
|
|
|
|
close();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
mbedtls_ssl_set_bio(&_ssl, &_sockfd, mbedtls_net_send, mbedtls_net_recv, NULL);
|
|
|
|
|
|
|
|
int res;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
|
|
res = mbedtls_ssl_handshake(&_ssl);
|
|
|
|
} while (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE);
|
|
|
|
|
|
|
|
if (res != 0)
|
|
|
|
{
|
|
|
|
char buf[256];
|
|
|
|
mbedtls_strerror(res, buf, sizeof(buf));
|
|
|
|
|
|
|
|
errMsg = "error in handshake : ";
|
|
|
|
errMsg += buf;
|
|
|
|
|
|
|
|
if (res == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED)
|
|
|
|
{
|
|
|
|
char verifyBuf[512];
|
|
|
|
uint32_t flags = mbedtls_ssl_get_verify_result(&_ssl);
|
|
|
|
|
|
|
|
mbedtls_x509_crt_verify_info(verifyBuf, sizeof(verifyBuf), " ! ", flags);
|
|
|
|
errMsg += " : ";
|
|
|
|
errMsg += verifyBuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
close();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2019-12-18 19:43:05 +01:00
|
|
|
}
|
|
|
|
|
2019-06-02 02:41:48 +02:00
|
|
|
bool SocketMbedTLS::connect(const std::string& host,
|
|
|
|
int port,
|
|
|
|
std::string& errMsg,
|
|
|
|
const CancellationRequest& isCancellationRequested)
|
|
|
|
{
|
2019-06-06 23:59:22 +02:00
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
|
|
_sockfd = SocketConnect::connect(host, port, errMsg, isCancellationRequested);
|
|
|
|
if (_sockfd == -1) return false;
|
|
|
|
}
|
|
|
|
|
2019-12-18 20:51:02 +01:00
|
|
|
bool isClient = true;
|
|
|
|
bool initialized = init(host, isClient, errMsg);
|
2019-09-27 23:07:01 +02:00
|
|
|
if (!initialized)
|
2019-06-06 23:59:22 +02:00
|
|
|
{
|
|
|
|
close();
|
|
|
|
return false;
|
|
|
|
}
|
2019-06-02 02:41:48 +02:00
|
|
|
|
|
|
|
mbedtls_ssl_set_bio(&_ssl, &_sockfd, mbedtls_net_send, mbedtls_net_recv, NULL);
|
|
|
|
|
|
|
|
int res;
|
2019-06-06 02:04:24 +02:00
|
|
|
do
|
2019-06-02 02:41:48 +02:00
|
|
|
{
|
2020-03-21 00:57:27 +01:00
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
|
|
res = mbedtls_ssl_handshake(&_ssl);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isCancellationRequested())
|
|
|
|
{
|
|
|
|
errMsg = "Cancellation requested";
|
|
|
|
close();
|
|
|
|
return false;
|
|
|
|
}
|
2019-09-23 19:25:23 +02:00
|
|
|
} while (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE);
|
2019-06-02 02:41:48 +02:00
|
|
|
|
|
|
|
if (res != 0)
|
|
|
|
{
|
|
|
|
char buf[256];
|
|
|
|
mbedtls_strerror(res, buf, sizeof(buf));
|
|
|
|
|
|
|
|
errMsg = "error in handshake : ";
|
|
|
|
errMsg += buf;
|
2019-06-06 23:59:22 +02:00
|
|
|
|
|
|
|
close();
|
2019-06-02 02:41:48 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SocketMbedTLS::close()
|
|
|
|
{
|
2019-06-06 23:59:22 +02:00
|
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
|
|
|
2019-06-02 02:41:48 +02:00
|
|
|
mbedtls_ssl_free(&_ssl);
|
|
|
|
mbedtls_ssl_config_free(&_conf);
|
|
|
|
mbedtls_ctr_drbg_free(&_ctr_drbg);
|
|
|
|
mbedtls_entropy_free(&_entropy);
|
2019-09-30 06:13:11 +02:00
|
|
|
mbedtls_x509_crt_free(&_cacert);
|
2019-12-18 20:51:02 +01:00
|
|
|
mbedtls_x509_crt_free(&_cert);
|
2019-06-06 23:59:22 +02:00
|
|
|
|
|
|
|
Socket::close();
|
2019-06-02 02:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t SocketMbedTLS::send(char* buf, size_t nbyte)
|
|
|
|
{
|
2020-01-12 20:08:44 +01:00
|
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
2019-06-02 02:41:48 +02:00
|
|
|
|
2020-01-12 20:08:44 +01:00
|
|
|
ssize_t res = mbedtls_ssl_write(&_ssl, (unsigned char*) buf, nbyte);
|
2019-06-02 02:41:48 +02:00
|
|
|
|
2020-01-12 20:08:44 +01:00
|
|
|
if (res > 0)
|
|
|
|
{
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
else if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE)
|
|
|
|
{
|
|
|
|
errno = EWOULDBLOCK;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return -1;
|
2019-06-02 02:41:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t SocketMbedTLS::recv(void* buf, size_t nbyte)
|
|
|
|
{
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
|
|
|
|
|
|
ssize_t res = mbedtls_ssl_read(&_ssl, (unsigned char*) buf, (int) nbyte);
|
|
|
|
|
|
|
|
if (res > 0)
|
|
|
|
{
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2024-03-19 06:23:14 +01:00
|
|
|
if (res == 0)
|
|
|
|
{
|
|
|
|
errno = ECONNRESET;
|
|
|
|
}
|
|
|
|
|
2019-06-02 02:41:48 +02:00
|
|
|
if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE)
|
|
|
|
{
|
|
|
|
errno = EWOULDBLOCK;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-23 19:25:23 +02:00
|
|
|
} // namespace ix
|
2020-04-24 21:47:47 +02:00
|
|
|
|
|
|
|
#endif // IXWEBSOCKET_USE_MBED_TLS
|