From eaed7355c6e373d2bbed7c2972d0a2e3b4ba9952 Mon Sep 17 00:00:00 2001 From: miodragpop Date: Fri, 23 Oct 2020 21:17:20 +0200 Subject: [PATCH 01/19] tls rework --- src/hush/tlsmanager.cpp | 431 +++++++++++++++++++++++++++++++++------- src/hush/tlsmanager.h | 14 +- src/hush/utiltls.cpp | 79 +++++++- src/init.cpp | 6 +- src/net.cpp | 380 +++++++++++++++++++++++------------ src/net.h | 20 +- 6 files changed, 708 insertions(+), 222 deletions(-) diff --git a/src/hush/tlsmanager.cpp b/src/hush/tlsmanager.cpp index 55c0d0d54..dcdca6418 100644 --- a/src/hush/tlsmanager.cpp +++ b/src/hush/tlsmanager.cpp @@ -4,20 +4,147 @@ #include #include #include -#include "utiltls.h" -#include -#include -#include "../util.h" -#include "../protocol.h" +#ifndef HEADER_DH_H +#include +#endif #include #include #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& 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& 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__); diff --git a/src/hush/tlsmanager.h b/src/hush/tlsmanager.h index ef677631e..5ad9f3c11 100644 --- a/src/hush/tlsmanager.h +++ b/src/hush/tlsmanager.h @@ -4,12 +4,10 @@ #include #include #include -#include "utiltls.h" #include "tlsenums.h" #include #include #include "../util.h" -#include "../protocol.h" #include "../net.h" #include "sync.h" #include @@ -43,8 +41,14 @@ bool operator==(const _NODE_ADDR b) const class TLSManager { public: - int waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, SSL* ssl, int timeoutSec); - SSL* connect(SOCKET hSocket, const CAddress& addrConnect); + /* This is set as a custom error number which is not an error in OpenSSL protocol. + A true (not null) OpenSSL error returned by ERR_get_error() consists of a library number, + function code and reason code. */ + static const long SELECT_TIMEDOUT = 0xFFFFFFFF; + + int waitFor(SSLConnectionRoutine eRoutine, SOCKET hSocket, SSL* ssl, int timeoutSec, unsigned long& err_code); + + SSL* connect(SOCKET hSocket, const CAddress& addrConnect, unsigned long& err_code); SSL_CTX* initCtx( TLSContextType ctxType, const boost::filesystem::path& privateKeyFile, @@ -52,7 +56,7 @@ public: const std::vector& trustedDirs); bool prepareCredentials(); - SSL* accept(SOCKET hSocket, const CAddress& addr); + SSL* accept(SOCKET hSocket, const CAddress& addr, unsigned long& err_code); bool isNonTLSAddr(const string& strAddr, const vector& vPool, CCriticalSection& cs); void cleanNonTLSPool(std::vector& vPool, CCriticalSection& cs); int threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fdsetSend, fd_set& fdsetError); diff --git a/src/hush/utiltls.cpp b/src/hush/utiltls.cpp index 48fb9cc50..8a86cbe61 100644 --- a/src/hush/utiltls.cpp +++ b/src/hush/utiltls.cpp @@ -14,7 +14,7 @@ #include #include -#include "util.h" +#include "../util.h" #include "utiltls.h" namespace hush { @@ -103,6 +103,42 @@ static EVP_PKEY* GenerateRsaKey(int bits, BN_ULONG uPublicKey) return evpPrivKey; } +// Generates EC keypair +// +static EVP_PKEY* GenerateEcKey(int bits, BN_ULONG uPublicKey) +{ + EVP_PKEY *evpPrivKey = NULL; + + BIGNUM *pubKey = BN_new(); + if (pubKey) + { + if (BN_set_word(pubKey, uPublicKey)) + { + EC_KEY *privKey = EC_KEY_new_by_curve_name(NID_secp256k1); + if (privKey) + { + if (EC_KEY_generate_key(privKey)) + { + if ((evpPrivKey = EVP_PKEY_new())) + { + if (!EVP_PKEY_assign_EC_KEY(evpPrivKey, privKey)) + { + EVP_PKEY_free(evpPrivKey); + evpPrivKey = NULL; + } + } + } + + if(!evpPrivKey) + EC_KEY_free(privKey); + } + } + BN_free(pubKey); + } + + return evpPrivKey; +} + // Generates certificate for a specified public key using a corresponding private key (both of them should be specified in the 'keypair'). // static X509* GenerateCertificate(EVP_PKEY *keypair) @@ -305,8 +341,16 @@ static bool CheckCredentials(EVP_PKEY *key, X509 *cert) } break; } - - // Currently only RSA keys are supported. + case EVP_PKEY_EC: + { + EC_KEY *eccKey = EVP_PKEY_get1_EC_KEY(key); + if (eccKey) + { + bIsOk = (EC_KEY_check_key(eccKey) == 1); + EC_KEY_free(eccKey); + } + } + // Currently only RSA & EC keys are supported. // Other key types can be added here in further. default: @@ -400,15 +444,34 @@ bool ValidatePeerCertificate(SSL *ssl) X509 *cert = SSL_get_peer_certificate (ssl); if (cert) { - // NOTE: SSL_get_verify_result() is only useful in connection with SSL_get_peer_certificate (https://www.openssl.org/docs/man1.0.2/ssl/SSL_get_verify_result.html) - // - bIsOk = (SSL_get_verify_result(ssl) == X509_V_OK); + // NOTE: SSL_get_verify_result() is only useful in connection with SSL_get_peer_certificate + // (https://www.openssl.org/docs/man1.1.1/ssl/SSL_get_verify_result.html) + long errCode = SSL_get_verify_result(ssl); + if (errCode != X509_V_OK) + { + LogPrint("tls", "TLS: %s: %s():%d - Certificate Verification ERROR=%d: [%s]\n", + __FILE__, __func__, __LINE__, errCode, X509_verify_cert_error_string(errCode)); + } + else + { + bIsOk = true; + + char buf[256]; + X509_NAME_oneline(X509_get_subject_name(cert), buf, 256); + LogPrint("tls", "TLS: %s: %s():%d - subj name=%s\n", + __FILE__, __func__, __LINE__, buf); + + X509_NAME_oneline(X509_get_issuer_name(cert), buf, 256); + LogPrint("tls", "TLS: %s: %s():%d - issuer name=%s\n", + __FILE__, __func__, __LINE__, buf); + } + X509_free(cert); } else { - LogPrint("net", "TLS: Peer does not have certificate\n"); - bIsOk = false; + LogPrint("tls", "TLS: %s: %s():%d - WARNING: Peer does not have certificate\n", + __FILE__, __func__, __LINE__); } return bIsOk; } diff --git a/src/init.cpp b/src/init.cpp index 1dde8781e..9b1f9c142 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -296,6 +296,7 @@ void Shutdown() //pzcashParams = NULL; globalVerifyHandle.reset(); ECC_Stop(); + CNode::NetCleanup(); LogPrintf("%s: done\n", __func__); } @@ -431,6 +432,8 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-torcontrol=:", strprintf(_("Tor control port to use if onion listening enabled (default: %s)"), DEFAULT_TOR_CONTROL)); strUsage += HelpMessageOpt("-torpassword=", _("Tor control port password (default: empty)")); strUsage += HelpMessageOpt("-tls=