tls rework
This commit is contained in:
@@ -4,20 +4,147 @@
|
||||
#include <openssl/conf.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include "utiltls.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
#include "../util.h"
|
||||
#include "../protocol.h"
|
||||
#ifndef HEADER_DH_H
|
||||
#include <openssl/dh.h>
|
||||
#endif
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include "tlsmanager.h"
|
||||
#include "utiltls.h"
|
||||
|
||||
using namespace std;
|
||||
namespace hush
|
||||
{
|
||||
|
||||
// this is the 'dh crypto environment' to be shared between two peers and it is meant to be public, therefore
|
||||
// it is OK to hard code it (or as an alternative to read it from a file)
|
||||
// ----
|
||||
// generated via: openssl dhparam -C 2048
|
||||
static DH *get_dh2048(void)
|
||||
{
|
||||
static unsigned char dhp_2048[] = {
|
||||
0xFF, 0x4A, 0xA8, 0x6C, 0x68, 0xD4, 0x4C, 0x41, 0x73, 0x8D,
|
||||
0xD8, 0x14, 0x57, 0xF9, 0x1C, 0x35, 0x72, 0x5F, 0xCD, 0x24,
|
||||
0xCB, 0xD1, 0x77, 0x30, 0xC2, 0x9A, 0x69, 0x01, 0xCF, 0x01,
|
||||
0xDE, 0xD4, 0x67, 0xD4, 0xEE, 0x9A, 0x03, 0x1C, 0x27, 0x42,
|
||||
0x06, 0x3D, 0x1D, 0x91, 0x27, 0xCF, 0x1C, 0x17, 0xB3, 0xDC,
|
||||
0x9F, 0x6F, 0x12, 0xC8, 0x03, 0x5C, 0x01, 0xF3, 0x27, 0x7F,
|
||||
0x34, 0x58, 0xAE, 0xB9, 0xA7, 0xA9, 0xCE, 0x5E, 0x25, 0x7D,
|
||||
0x46, 0x84, 0xDD, 0xEE, 0x55, 0xFB, 0xEA, 0x1C, 0xCD, 0x9B,
|
||||
0x96, 0xC4, 0x22, 0x8C, 0x33, 0x8B, 0xC7, 0xE6, 0xCC, 0x4C,
|
||||
0x77, 0x1B, 0x7A, 0x46, 0xDE, 0x33, 0xAD, 0xBB, 0xFD, 0x2D,
|
||||
0xAD, 0x26, 0xE1, 0x27, 0x48, 0x94, 0xA3, 0x59, 0xC5, 0x10,
|
||||
0x5A, 0x86, 0x71, 0x8D, 0xAA, 0x15, 0x8B, 0xB2, 0xCB, 0x70,
|
||||
0xBE, 0x1F, 0x17, 0xBD, 0xEB, 0x51, 0xB1, 0x76, 0x0E, 0x24,
|
||||
0x43, 0xAA, 0x06, 0xC0, 0x97, 0x01, 0x25, 0x52, 0x30, 0x7A,
|
||||
0x56, 0x92, 0x3D, 0x8A, 0x3A, 0xBC, 0xFA, 0x98, 0x51, 0x04,
|
||||
0x1D, 0x9B, 0x05, 0xB8, 0x84, 0x8C, 0x2F, 0x7A, 0x94, 0x1E,
|
||||
0xAA, 0x51, 0xF2, 0x5D, 0x48, 0x50, 0x58, 0x8D, 0x7E, 0xBA,
|
||||
0xD3, 0xCC, 0xF2, 0x92, 0x28, 0xB1, 0x1C, 0x4B, 0x50, 0x10,
|
||||
0xFA, 0x7E, 0xDF, 0x8D, 0x23, 0x1C, 0x8C, 0x65, 0xE3, 0x86,
|
||||
0x16, 0x67, 0x88, 0x9E, 0xFC, 0x8B, 0xC8, 0x55, 0x38, 0x6E,
|
||||
0x79, 0x06, 0x6A, 0x6D, 0x72, 0x75, 0xA6, 0xAC, 0x77, 0x98,
|
||||
0xDD, 0xB2, 0x0B, 0xAA, 0x48, 0x54, 0xA9, 0x07, 0x7E, 0x8C,
|
||||
0x4C, 0x39, 0x08, 0x26, 0x6D, 0x53, 0xC2, 0xDF, 0xE2, 0xF0,
|
||||
0xD6, 0x8A, 0x4F, 0xB5, 0x7A, 0x32, 0xEE, 0x93, 0x0E, 0x2A,
|
||||
0x81, 0x2F, 0x3B, 0x1E, 0xE6, 0x38, 0xF8, 0x3C, 0xF5, 0x84,
|
||||
0xB4, 0xFB, 0x92, 0x12, 0x28, 0xA3
|
||||
};
|
||||
static unsigned char dhg_2048[] = {
|
||||
0x02
|
||||
};
|
||||
DH *dh = DH_new();
|
||||
BIGNUM *p, *g;
|
||||
|
||||
if (dh == NULL)
|
||||
return NULL;
|
||||
p = BN_bin2bn(dhp_2048, sizeof(dhp_2048), NULL);
|
||||
g = BN_bin2bn(dhg_2048, sizeof(dhg_2048), NULL);
|
||||
if (p == NULL || g == NULL
|
||||
|| !DH_set0_pqg(dh, p, NULL, g)) {
|
||||
DH_free(dh);
|
||||
BN_free(p);
|
||||
BN_free(g);
|
||||
return NULL;
|
||||
}
|
||||
return dh;
|
||||
}
|
||||
|
||||
DH *tmp_dh_callback(SSL *ssl, int is_export, int keylength)
|
||||
{
|
||||
LogPrint("tls", "TLS: %s: %s():%d - Using Diffie-Hellman param for PFS: is_export=%d, keylength=%d\n",
|
||||
__FILE__, __func__, __LINE__, is_export, keylength);
|
||||
|
||||
return get_dh2048();
|
||||
}
|
||||
|
||||
/** if 'tls' debug category is enabled, collect info about certificates relevant to the passed context and print them on logs */
|
||||
static void dumpCertificateDebugInfo(int preverify_ok, X509_STORE_CTX* chainContext)
|
||||
{
|
||||
if (!LogAcceptCategory("tls") )
|
||||
return;
|
||||
|
||||
char buf[256] = {};
|
||||
X509 *cert;
|
||||
int err, depth;
|
||||
|
||||
cert = X509_STORE_CTX_get_current_cert(chainContext);
|
||||
err = X509_STORE_CTX_get_error(chainContext);
|
||||
depth = X509_STORE_CTX_get_error_depth(chainContext);
|
||||
|
||||
LogPrintf("TLS: %s: %s():%d - preverify_ok=%d, errCode=%d, depth=%d\n",
|
||||
__FILE__, __func__, __LINE__, preverify_ok, err, depth);
|
||||
|
||||
// is not useful checking preverify_ok because, after the chain root verification, it is set accordingly
|
||||
// to the return value of this callback, and we choose to always return 1
|
||||
if (err != X509_V_OK )
|
||||
{
|
||||
LogPrintf("TLS: %s: %s():%d - Certificate Verification ERROR=%d: [%s] at chain depth=%d\n",
|
||||
__FILE__, __func__, __LINE__, err, X509_verify_cert_error_string(err), depth);
|
||||
|
||||
if (cert && err == X509_V_ERR_CERT_HAS_EXPIRED)
|
||||
{
|
||||
struct tm t = {};
|
||||
const ASN1_TIME * at = X509_get0_notAfter(cert);
|
||||
int ret = ASN1_TIME_to_tm(at, &t);
|
||||
if (ret == 1)
|
||||
{
|
||||
(void)strftime(buf, sizeof (buf), "%c", &t);
|
||||
LogPrintf("TLS: %s: %s():%d - expired on=%s\n",
|
||||
__FILE__, __func__, __LINE__, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cert)
|
||||
{
|
||||
X509_NAME_oneline(X509_get_subject_name(cert), buf, 256);
|
||||
LogPrintf("TLS: %s: %s():%d - subj name=%s\n",
|
||||
__FILE__, __func__, __LINE__, buf);
|
||||
|
||||
X509_NAME_oneline(X509_get_issuer_name(cert), buf, 256);
|
||||
LogPrintf("TLS: %s: %s():%d - issuer name=%s\n",
|
||||
__FILE__, __func__, __LINE__, buf);
|
||||
|
||||
struct tm t = {};
|
||||
const ASN1_TIME * at = X509_get0_notAfter(cert);
|
||||
int ret = ASN1_TIME_to_tm(at, &t);
|
||||
if (ret == 1)
|
||||
{
|
||||
(void)strftime(buf, sizeof (buf), "%c", &t);
|
||||
LogPrintf("TLS: %s: %s():%d - expiring on=%s\n",
|
||||
__FILE__, __func__, __LINE__, buf);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// should never happen
|
||||
LogPrintf("TLS: %s: %s():%d - invalid cert/err\n", __FILE__, __func__, __LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief If verify_callback always returns 1, the TLS/SSL handshake will not be terminated with respect to verification failures and the connection will be established.
|
||||
*
|
||||
@@ -27,6 +154,16 @@ namespace hush
|
||||
*/
|
||||
int tlsCertVerificationCallback(int preverify_ok, X509_STORE_CTX* chainContext)
|
||||
{
|
||||
dumpCertificateDebugInfo(preverify_ok, chainContext);
|
||||
|
||||
/* The return value controls the strategy of the further verification process. If it returns 0
|
||||
* the verification process is immediately stopped with "verification failed" state.
|
||||
* If SSL_VERIFY_PEER has been set in set_verify, a verification failure alert is sent to the peer and the TLS/SSL
|
||||
* handshake is terminated.
|
||||
* If it returns 1, the verification process is continued.
|
||||
* Here we choose to continue the verification process by returning 1 and to leave the optional cert
|
||||
* verification if we call ValidatePeerCertificate().
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
/**
|
||||
@@ -38,42 +175,103 @@ int tlsCertVerificationCallback(int preverify_ok, X509_STORE_CTX* chainContext)
|
||||
* @param timeoutSec timeout in seconds.
|
||||
* @return int returns nError corresponding to the connection event.
|
||||
*/
|
||||
int TLSManager::waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, SSL* ssl, int timeoutSec)
|
||||
int TLSManager::waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, SSL* ssl, int timeoutSec, unsigned long& err_code)
|
||||
{
|
||||
int nErr = 0;
|
||||
ERR_clear_error(); // clear the error queue
|
||||
int retOp = 0;
|
||||
err_code = 0;
|
||||
|
||||
while (true) {
|
||||
// clear the current thread's error queue
|
||||
ERR_clear_error();
|
||||
|
||||
switch (eRoutine) {
|
||||
case SSL_CONNECT:
|
||||
nErr = SSL_connect(ssl);
|
||||
case SSL_CONNECT:
|
||||
{
|
||||
retOp = SSL_connect(ssl);
|
||||
if (retOp == 0)
|
||||
{
|
||||
err_code = ERR_get_error();
|
||||
const char* error_str = ERR_error_string(err_code, NULL);
|
||||
LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_CONNECT err: %s\n",
|
||||
__FILE__, __func__, __LINE__, error_str);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SSL_ACCEPT:
|
||||
nErr = SSL_accept(ssl);
|
||||
|
||||
case SSL_ACCEPT:
|
||||
{
|
||||
retOp = SSL_accept(ssl);
|
||||
if (retOp == 0)
|
||||
{
|
||||
err_code = ERR_get_error();
|
||||
const char* error_str = ERR_error_string(err_code, NULL);
|
||||
LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_ACCEPT err: %s\n",
|
||||
__FILE__, __func__, __LINE__, error_str);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SSL_SHUTDOWN:
|
||||
nErr = SSL_shutdown(ssl);
|
||||
|
||||
case SSL_SHUTDOWN:
|
||||
{
|
||||
if (hSocket != INVALID_SOCKET)
|
||||
{
|
||||
std::string disconnectedPeer("no info");
|
||||
struct sockaddr_in addr;
|
||||
socklen_t serv_len = sizeof(addr);
|
||||
int ret = getpeername(hSocket, (struct sockaddr *)&addr, &serv_len);
|
||||
if (ret == 0)
|
||||
{
|
||||
disconnectedPeer = std::string(inet_ntoa(addr.sin_addr)) + ":" + std::to_string(ntohs(addr.sin_port));
|
||||
}
|
||||
LogPrint("tls", "TLS: shutting down fd=%d, peer=%s\n", hSocket, disconnectedPeer);
|
||||
}
|
||||
retOp = SSL_shutdown(ssl);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (eRoutine == SSL_SHUTDOWN) {
|
||||
if (nErr >= 0)
|
||||
if (retOp == 0)
|
||||
{
|
||||
LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_SHUTDOWN: The close_notify was sent but the peer did not send it back yet.\n",
|
||||
__FILE__, __func__, __LINE__);
|
||||
// do not call SSL_get_error() because it may misleadingly indicate an error even though no error occurred.
|
||||
break;
|
||||
}
|
||||
else
|
||||
if (retOp == 1)
|
||||
{
|
||||
LogPrint("tls", "TLS: %s: %s():%d - SSL_SHUTDOWN completed\n", __FILE__, __func__, __LINE__);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint("tls", "TLS: %s: %s():%d - SSL_SHUTDOWN failed\n", __FILE__, __func__, __LINE__);
|
||||
// the error will be read afterwards
|
||||
}
|
||||
} else {
|
||||
if (nErr == 1)
|
||||
if (retOp == 1)
|
||||
{
|
||||
LogPrint("tls", "TLS: %s: %s():%d - %s completed\n", __FILE__, __func__, __LINE__,
|
||||
eRoutine == SSL_CONNECT ? "SSL_CONNECT" : "SSL_ACCEPT");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int sslErr = SSL_get_error(ssl, nErr);
|
||||
int sslErr = SSL_get_error(ssl, retOp);
|
||||
|
||||
if (sslErr != SSL_ERROR_WANT_READ && sslErr != SSL_ERROR_WANT_WRITE) {
|
||||
LogPrint("net", "TLS: WARNING: %s: %s: ssl_err_code: %s; errno: %s\n", __FILE__, __func__, ERR_error_string(sslErr, NULL), strerror(errno));
|
||||
nErr = -1;
|
||||
err_code = ERR_get_error();
|
||||
const char* error_str = ERR_error_string(err_code, NULL);
|
||||
LogPrint("tls", "TLS: WARNING: %s: %s():%d - routine(%d), sslErr[0x%x], retOp[%d], errno[0x%x], lib[0x%x], func[0x%x], reas[0x%x]-> err: %s\n",
|
||||
__FILE__, __func__, __LINE__,
|
||||
eRoutine, sslErr, retOp, errno, ERR_GET_LIB(err_code), ERR_GET_FUNC(err_code), ERR_GET_REASON(err_code), error_str);
|
||||
retOp = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -86,30 +284,39 @@ int TLSManager::waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, SSL* ssl,
|
||||
if (sslErr == SSL_ERROR_WANT_READ) {
|
||||
int result = select(hSocket + 1, &socketSet, NULL, NULL, &timeout);
|
||||
if (result == 0) {
|
||||
LogPrint("net", "TLS: ERROR: %s: %s: WANT_READ timeout\n", __FILE__, __func__);
|
||||
nErr = -1;
|
||||
LogPrint("tls", "TLS: ERROR: %s: %s():%d - WANT_READ timeout on %s\n", __FILE__, __func__, __LINE__,
|
||||
(eRoutine == SSL_CONNECT ? "SSL_CONNECT" :
|
||||
(eRoutine == SSL_ACCEPT ? "SSL_ACCEPT" : "SSL_SHUTDOWN" )));
|
||||
err_code = SELECT_TIMEDOUT;
|
||||
retOp = -1;
|
||||
break;
|
||||
} else if (result == -1) {
|
||||
LogPrint("net", "TLS: ERROR: %s: %s: WANT_READ ssl_err_code: %s; errno: %s\n", __FILE__, __func__, ERR_error_string(sslErr, NULL), strerror(errno));
|
||||
nErr = -1;
|
||||
LogPrint("tls", "TLS: ERROR: %s: %s: WANT_READ ssl_err_code: 0x%x; errno: %s\n",
|
||||
__FILE__, __func__, sslErr, strerror(errno));
|
||||
retOp = -1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
int result = select(hSocket + 1, NULL, &socketSet, NULL, &timeout);
|
||||
if (result == 0) {
|
||||
LogPrint("net", "TLS: ERROR: %s: %s: WANT_WRITE timeout\n", __FILE__, __func__);
|
||||
nErr = -1;
|
||||
LogPrint("tls", "TLS: ERROR: %s: %s():%d - WANT_WRITE timeout on %s\n", __FILE__, __func__, __LINE__,
|
||||
(eRoutine == SSL_CONNECT ? "SSL_CONNECT" :
|
||||
(eRoutine == SSL_ACCEPT ? "SSL_ACCEPT" : "SSL_SHUTDOWN" )));
|
||||
err_code = SELECT_TIMEDOUT;
|
||||
retOp = -1;
|
||||
break;
|
||||
} else if (result == -1) {
|
||||
LogPrint("net", "TLS: ERROR: %s: %s: WANT_WRITE ssl_err_code: %s; errno: %s\n", __FILE__, __func__, ERR_error_string(sslErr, NULL), strerror(errno));
|
||||
nErr = -1;
|
||||
LogPrint("tls", "TLS: ERROR: %s: %s: WANT_WRITE ssl_err_code: 0x%x; errno: %s\n",
|
||||
__FILE__, __func__, sslErr, strerror(errno));
|
||||
retOp = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nErr;
|
||||
return retOp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief establish TLS connection to an address
|
||||
*
|
||||
@@ -118,25 +325,38 @@ int TLSManager::waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, SSL* ssl,
|
||||
* @param tls_ctx_client TLS Client context
|
||||
* @return SSL* returns a ssl* if successful, otherwise returns NULL.
|
||||
*/
|
||||
SSL* TLSManager::connect(SOCKET hSocket, const CAddress& addrConnect)
|
||||
SSL* TLSManager::connect(SOCKET hSocket, const CAddress& addrConnect, unsigned long& err_code)
|
||||
{
|
||||
LogPrint("net", "TLS: establishing connection tid=%X peerid=%s\n", pthread_self(), addrConnect.ToString());
|
||||
LogPrint("tls", "TLS: establishing connection (tid = %X), (peerid = %s)\n", pthread_self(), addrConnect.ToString());
|
||||
|
||||
err_code = 0;
|
||||
SSL* ssl = NULL;
|
||||
bool bConnectedTLS = false;
|
||||
|
||||
if ((ssl = SSL_new(tls_ctx_client))) {
|
||||
if (SSL_set_fd(ssl, hSocket)) {
|
||||
if (TLSManager::waitFor(SSL_CONNECT, hSocket, ssl, (DEFAULT_CONNECT_TIMEOUT / 1000)) == 1)
|
||||
|
||||
int ret = TLSManager::waitFor(SSL_CONNECT, hSocket, ssl, (DEFAULT_CONNECT_TIMEOUT / 1000), err_code);
|
||||
if (ret == 1)
|
||||
{
|
||||
bConnectedTLS = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
err_code = ERR_get_error();
|
||||
const char* error_str = ERR_error_string(err_code, NULL);
|
||||
LogPrint("tls", "TLS: %s: %s():%d - SSL_new failed err: %s\n",
|
||||
__FILE__, __func__, __LINE__, error_str);
|
||||
}
|
||||
|
||||
|
||||
if (bConnectedTLS) {
|
||||
LogPrintf("TLS: connection to %s has been established. Using cipher: %s\n", addrConnect.ToString(), SSL_get_cipher(ssl));
|
||||
LogPrintf("TLS: connection to %s has been established (tlsv = %s 0x%04x / ssl = %s 0x%x ). Using cipher: %s\n",
|
||||
addrConnect.ToString(), SSL_get_version(ssl), SSL_version(ssl), OpenSSL_version(OPENSSL_VERSION), OpenSSL_version_num(), SSL_get_cipher(ssl));
|
||||
} else {
|
||||
LogPrintf("TLS: %s: TLS connection to %s failed\n", __func__, addrConnect.ToString());
|
||||
LogPrintf("TLS: %s: %s():%d - TLS connection to %s failed (err_code 0x%X)\n",
|
||||
__FILE__, __func__, __LINE__, addrConnect.ToString(), err_code);
|
||||
|
||||
if (ssl) {
|
||||
SSL_free(ssl);
|
||||
@@ -160,6 +380,9 @@ SSL_CTX* TLSManager::initCtx(
|
||||
const boost::filesystem::path& certificateFile,
|
||||
const std::vector<boost::filesystem::path>& trustedDirs)
|
||||
{
|
||||
LogPrintf("TLS: %s: %s():%d - Initializing %s context\n",
|
||||
__FILE__, __func__, __LINE__, ctxType == SERVER_CONTEXT ? "server" : "client");
|
||||
|
||||
if (!boost::filesystem::exists(privateKeyFile) ||
|
||||
!boost::filesystem::exists(certificateFile))
|
||||
return NULL;
|
||||
@@ -170,6 +393,55 @@ SSL_CTX* TLSManager::initCtx(
|
||||
if ((tlsCtx = SSL_CTX_new(ctxType == SERVER_CONTEXT ? TLS_server_method() : TLS_client_method()))) {
|
||||
SSL_CTX_set_mode(tlsCtx, SSL_MODE_AUTO_RETRY);
|
||||
|
||||
// Disable TLS < 1.3
|
||||
int ret = SSL_CTX_set_min_proto_version(tlsCtx, TLS1_3_VERSION);
|
||||
if (ret == 0)
|
||||
{
|
||||
LogPrintf("TLS: WARNING: %s: %s():%d - failed to set min TLS version\n", __FILE__, __func__, __LINE__);
|
||||
}
|
||||
|
||||
LogPrintf("TLS: %s: %s():%d - setting cipher list\n", __FILE__, __func__, __LINE__);
|
||||
|
||||
SSL_CTX_set_cipher_list(tlsCtx, ""); // removes all <= TLS1.2 ciphers
|
||||
// default is "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256"
|
||||
// Nodes will randomly choose to prefer one suite or the other, to create diversity on the network
|
||||
// and not be in the situation where all nodes have the same list so the first is always used
|
||||
if(GetRand(100) > 50) {
|
||||
if (SSL_CTX_set_ciphersuites(tlsCtx, "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256")) {
|
||||
LogPrintf("%s: Preferring TLS_AES256-GCM-SHA384\n", __func__);
|
||||
} else {
|
||||
LogPrintf("%s: Setting preferred cipher failed !!!\n", __func__);
|
||||
}
|
||||
} else {
|
||||
if (SSL_CTX_set_ciphersuites(tlsCtx, "TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384")) {
|
||||
LogPrintf("%s: Preferring TLS_AES256-GCM-SHA384\n", __func__);
|
||||
} else {
|
||||
LogPrintf("%s: Setting preferred cipher failed !!!\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
// TLS 1.3 has ephemeral Diffie-Hellman as the only key exchange mechanism, so that perfect forward
|
||||
// secrecy is ensured.
|
||||
|
||||
if (ctxType == SERVER_CONTEXT)
|
||||
{
|
||||
// amongst the Cl/Srv mutually-acceptable set, pick the one that the server prefers most instead of the one that
|
||||
// the client prefers most
|
||||
SSL_CTX_set_options(tlsCtx, SSL_OP_CIPHER_SERVER_PREFERENCE);
|
||||
|
||||
LogPrintf("TLS: %s: %s():%d - setting dh callback\n", __FILE__, __func__, __LINE__);
|
||||
SSL_CTX_set_tmp_dh_callback(tlsCtx, tmp_dh_callback);
|
||||
}
|
||||
|
||||
// Fix for Secure Client-Initiated Renegotiation DoS threat
|
||||
SSL_CTX_set_options(tlsCtx, SSL_OP_NO_RENEGOTIATION);
|
||||
|
||||
int min_ver = SSL_CTX_get_min_proto_version(tlsCtx);
|
||||
int max_ver = SSL_CTX_get_max_proto_version(tlsCtx); // 0x0 means auto
|
||||
int opt_mask = SSL_CTX_get_options(tlsCtx);
|
||||
|
||||
LogPrintf("TLS: proto version: min/max 0x%04x/0x04%x, opt_mask=0x%x\n", min_ver, max_ver, opt_mask);
|
||||
|
||||
int rootCertsNum = LoadDefaultRootCertificates(tlsCtx);
|
||||
int trustedPathsNum = 0;
|
||||
|
||||
@@ -207,27 +479,6 @@ SSL_CTX* TLSManager::initCtx(
|
||||
}
|
||||
}
|
||||
|
||||
SSL_CTX_set_cipher_list(tlsCtx, ""); // removes all <= TLS1.2 ciphers
|
||||
// default is "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256"
|
||||
// Nodes will randomly choose to prefer one suite or the other, to create diversity on the network
|
||||
// and not be in the situation where all nodes have the same list so the first is always used
|
||||
if(GetRand(100) > 50) {
|
||||
LogPrintf("%s: Preferring TLS_AES256-GCM-SHA384\n", __func__);
|
||||
SSL_CTX_set_ciphersuites(tlsCtx, "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256");
|
||||
} else {
|
||||
LogPrintf("%s: Preferring TLS_CHACHA20-POLY1305\n", __func__);
|
||||
SSL_CTX_set_ciphersuites(tlsCtx, "TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384");
|
||||
}
|
||||
|
||||
/*
|
||||
STACK_OF(SSL_CIPHER) *sk = SSL_CTX_get_ciphers(tlsCtx);
|
||||
for (int i = 0; i < sk_SSL_CIPHER_num(sk); i++)
|
||||
{
|
||||
const SSL_CIPHER *c = sk_SSL_CIPHER_value(sk, i);
|
||||
LogPrintf("%s: AVAILABLE CIPHER %s\n", __func__, SSL_CIPHER_get_name(c));
|
||||
}
|
||||
*/
|
||||
|
||||
return tlsCtx;
|
||||
}
|
||||
/**
|
||||
@@ -275,24 +526,43 @@ bool TLSManager::prepareCredentials()
|
||||
* @param tls_ctx_server TLS server context.
|
||||
* @return SSL* returns pointer to the ssl object if successful, otherwise returns NULL
|
||||
*/
|
||||
SSL* TLSManager::accept(SOCKET hSocket, const CAddress& addr)
|
||||
SSL* TLSManager::accept(SOCKET hSocket, const CAddress& addr, unsigned long& err_code)
|
||||
{
|
||||
LogPrint("net", "TLS: accepting connection from %s (tid = %X)\n", addr.ToString(), pthread_self());
|
||||
LogPrint("tls", "TLS: accepting connection from %s (tid = %X)\n", addr.ToString(), pthread_self());
|
||||
|
||||
err_code = 0;
|
||||
SSL* ssl = NULL;
|
||||
bool bAcceptedTLS = false;
|
||||
|
||||
if ((ssl = SSL_new(tls_ctx_server))) {
|
||||
if (SSL_set_fd(ssl, hSocket)) {
|
||||
if (TLSManager::waitFor(SSL_ACCEPT, hSocket, ssl, (DEFAULT_CONNECT_TIMEOUT / 1000)) == 1)
|
||||
int ret = TLSManager::waitFor(SSL_ACCEPT, hSocket, ssl, (DEFAULT_CONNECT_TIMEOUT / 1000), err_code);
|
||||
if (ret == 1)
|
||||
{
|
||||
bAcceptedTLS = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
err_code = ERR_get_error();
|
||||
const char* error_str = ERR_error_string(err_code, NULL);
|
||||
LogPrint("tls", "TLS: %s: %s():%d - SSL_new failed err: %s\n",
|
||||
__FILE__, __func__, __LINE__, error_str);
|
||||
}
|
||||
|
||||
if (bAcceptedTLS) {
|
||||
LogPrintf("TLS: connection from %s has been accepted. Using cipher: %s\n", addr.ToString(), SSL_get_cipher(ssl));
|
||||
LogPrintf("TLS: connection from %s has been accepted (tlsv = %s 0x%04x / ssl = %s 0x%x ). Using cipher: %s\n",
|
||||
addr.ToString(), SSL_get_version(ssl), SSL_version(ssl), OpenSSL_version(OPENSSL_VERSION), OpenSSL_version_num(), SSL_get_cipher(ssl));
|
||||
|
||||
STACK_OF(SSL_CIPHER) *sk = SSL_get_ciphers(ssl);
|
||||
for (int i = 0; i < sk_SSL_CIPHER_num(sk); i++) {
|
||||
const SSL_CIPHER *c = sk_SSL_CIPHER_value(sk, i);
|
||||
LogPrint("tls", "TLS: supporting cipher: %s\n", SSL_CIPHER_get_name(c));
|
||||
}
|
||||
} else {
|
||||
LogPrintf("TLS: ERROR: %s: %s: TLS connection from %s failed\n", __FILE__, __func__, addr.ToString());
|
||||
LogPrintf("TLS: %s: %s():%d - TLS connection from %s failed (err_code 0x%X)\n",
|
||||
__FILE__, __func__, __LINE__, addr.ToString(), err_code);
|
||||
|
||||
if (ssl) {
|
||||
SSL_free(ssl);
|
||||
@@ -331,7 +601,7 @@ void TLSManager::cleanNonTLSPool(std::vector<NODE_ADDR>& vPool, CCriticalSection
|
||||
BOOST_FOREACH (NODE_ADDR nodeAddr, vPool) {
|
||||
if ((GetTimeMillis() - nodeAddr.time) >= 900000) {
|
||||
vDeleted.push_back(nodeAddr);
|
||||
LogPrint("net", "TLS: Node %s is deleted from the non-TLS pool\n", nodeAddr.ipAddr);
|
||||
LogPrint("tls", "TLS: Node %s is deleted from the non-TLS pool\n", nodeAddr.ipAddr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,7 +656,7 @@ int TLSManager::threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fds
|
||||
LOCK(pnode->cs_hSocket);
|
||||
|
||||
if (pnode->hSocket == INVALID_SOCKET) {
|
||||
LogPrint("net", "Receive: connection with %s is already closed\n", pnode->addr.ToString());
|
||||
LogPrint("tls", "Receive: connection with %s is already closed\n", pnode->addr.ToString());
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -409,11 +679,20 @@ int TLSManager::threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fds
|
||||
pnode->nRecvBytes += nBytes;
|
||||
pnode->RecordBytesRecv(nBytes);
|
||||
} else if (nBytes == 0) {
|
||||
|
||||
if (bIsSSL) {
|
||||
unsigned long error = ERR_get_error();
|
||||
const char* error_str = ERR_error_string(error, NULL);
|
||||
LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_read err: %s\n",
|
||||
__FILE__, __func__, __LINE__, error_str);
|
||||
}
|
||||
// socket closed gracefully (peer disconnected)
|
||||
//
|
||||
if (!pnode->fDisconnect)
|
||||
LogPrint("net", "socket closed (%s)\n", pnode->addr.ToString());
|
||||
LogPrint("tls", "socket closed (%s)\n", pnode->addr.ToString());
|
||||
pnode->CloseSocketDisconnect();
|
||||
|
||||
|
||||
} else if (nBytes < 0) {
|
||||
// error
|
||||
//
|
||||
@@ -421,8 +700,14 @@ int TLSManager::threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fds
|
||||
if (nRet != SSL_ERROR_WANT_READ && nRet != SSL_ERROR_WANT_WRITE) // SSL_read() operation has to be repeated because of SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE (https://wiki.openssl.org/index.php/Manual:SSL_read(3)#NOTES)
|
||||
{
|
||||
if (!pnode->fDisconnect)
|
||||
LogPrintf("ERROR: SSL_read %s\n", ERR_error_string(nRet, NULL));
|
||||
LogPrintf("TSL: ERROR: SSL_read %s\n", ERR_error_string(nRet, NULL));
|
||||
pnode->CloseSocketDisconnect();
|
||||
|
||||
unsigned long error = ERR_get_error();
|
||||
const char* error_str = ERR_error_string(error, NULL);
|
||||
LogPrint("tls", "TLS: WARNING: %s: %s():%d - SSL_read - code[0x%x], err: %s\n",
|
||||
__FILE__, __func__, __LINE__, nRet, error_str);
|
||||
|
||||
} else {
|
||||
// preventive measure from exhausting CPU usage
|
||||
//
|
||||
@@ -431,7 +716,7 @@ int TLSManager::threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fds
|
||||
} else {
|
||||
if (nRet != WSAEWOULDBLOCK && nRet != WSAEMSGSIZE && nRet != WSAEINTR && nRet != WSAEINPROGRESS) {
|
||||
if (!pnode->fDisconnect)
|
||||
LogPrintf("ERROR: socket recv %s\n", NetworkErrorString(nRet));
|
||||
LogPrintf("TSL: ERROR: socket recv %s\n", NetworkErrorString(nRet));
|
||||
pnode->CloseSocketDisconnect();
|
||||
}
|
||||
}
|
||||
@@ -461,6 +746,7 @@ bool TLSManager::initialize()
|
||||
bool bInitializationStatus = false;
|
||||
|
||||
// Initialization routines for the OpenSSL library
|
||||
//
|
||||
SSL_load_error_strings();
|
||||
ERR_load_crypto_strings();
|
||||
OpenSSL_add_ssl_algorithms(); // OpenSSL_add_ssl_algorithms() always returns "1", so it is safe to discard the return value.
|
||||
@@ -468,7 +754,7 @@ bool TLSManager::initialize()
|
||||
namespace fs = boost::filesystem;
|
||||
fs::path certFile = GetArg("-tlscertpath", "");
|
||||
if (!fs::exists(certFile))
|
||||
certFile = (GetDataDir() / TLS_CERT_FILE_NAME);
|
||||
certFile = (GetDataDir() / TLS_CERT_FILE_NAME);
|
||||
|
||||
fs::path privKeyFile = GetArg("-tlskeypath", "");
|
||||
if (!fs::exists(privKeyFile)) {
|
||||
@@ -489,11 +775,12 @@ bool TLSManager::initialize()
|
||||
LogPrintf("TLS: trusted directory '%s' will be used\n", dir.string().c_str());
|
||||
|
||||
// Initialization of the server and client contexts
|
||||
//
|
||||
if ((tls_ctx_server = TLSManager::initCtx(SERVER_CONTEXT, privKeyFile, certFile, trustedDirs)))
|
||||
{
|
||||
if ((tls_ctx_client = TLSManager::initCtx(CLIENT_CONTEXT, privKeyFile, certFile, trustedDirs)))
|
||||
{
|
||||
LogPrint("net", "TLS: contexts are initialized\n");
|
||||
LogPrint("tls", "TLS: contexts are initialized\n");
|
||||
bInitializationStatus = true;
|
||||
} else {
|
||||
LogPrintf("TLS: ERROR: %s: %s: failed to initialize TLS client context\n", __FILE__, __func__);
|
||||
|
||||
Reference in New Issue
Block a user