//============================================================================= // // File : KviSSL.cpp // Creation date : Mon May 27 2002 21:36:12 CEST by Szymon Stefanek // // This file is part of the KVIrc IRC client distribution // Copyright (C) 2002-2010 Szymon Stefanek (pragma at kvirc dot net) // // This program is FREE software. You can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the HOPE that it will be USEFUL, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, write to the Free Software Foundation, // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //============================================================================= #include "KviSSL.h" #include "KviLocale.h" #include "kvi_socket.h" #ifdef COMPILE_SSL_SUPPORT #include "KviThread.h" #include "KviMemory.h" #include #include #include #include #include #if !(defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)) // linux, mac #include #endif static bool g_bSSLInitialized = false; static KviMutex * g_pSSLMutex = nullptr; static inline void my_ssl_lock() { g_pSSLMutex->lock(); } static inline void my_ssl_unlock() { g_pSSLMutex->unlock(); } #if OPENSSL_VERSION_NUMBER < 0x30000000L // THIS PART OF OpenSSL SUCKS static DH * dh_512 = nullptr; static DH * dh_1024 = nullptr; static DH * dh_2048 = nullptr; static DH * dh_4096 = nullptr; static unsigned char dh512_p[] = { 0x90, 0x86, 0xDD, 0x06, 0xE8, 0x0F, 0x10, 0x86, 0xF0, 0x91, 0xC5, 0x55, 0x4D, 0x6B, 0xAF, 0x69, 0x4F, 0x01, 0xED, 0xF9, 0x57, 0x8F, 0x3B, 0xB8, 0x9C, 0x87, 0xAE, 0x85, 0xC1, 0xBF, 0x57, 0xA5, 0xD5, 0xBA, 0x81, 0x24, 0xE7, 0x99, 0xE3, 0xF6, 0xCD, 0xB4, 0x41, 0xB7, 0x7F, 0x6E, 0x7B, 0xB1, 0xD2, 0xF3, 0xE9, 0x0F, 0xB9, 0x0E, 0x4D, 0xEB, 0x9D, 0xD4, 0xA9, 0xE5, 0x03, 0x67, 0xA7, 0x27 }; static unsigned char dh512_g[] = { 0x05 }; static unsigned char dh1024_p[] = { 0xA5, 0x4C, 0xB9, 0xB9, 0xC4, 0x35, 0x88, 0x68, 0x9B, 0x79, 0x48, 0x6C, 0x21, 0xA7, 0x8E, 0xE2, 0x9C, 0xAF, 0x2F, 0x04, 0xBF, 0x45, 0xBC, 0xF5, 0xAB, 0x35, 0x86, 0xC8, 0xBB, 0x9B, 0x75, 0x18, 0x7C, 0x9B, 0xAB, 0xE8, 0x52, 0x7F, 0x57, 0x3E, 0xD8, 0x65, 0x7D, 0x2B, 0xE1, 0x6D, 0x3D, 0xA5, 0x32, 0xE8, 0xA0, 0x2B, 0x7A, 0x58, 0x6B, 0x47, 0x16, 0x4E, 0xB1, 0xFC, 0x09, 0xB7, 0x7C, 0xC6, 0xE9, 0x6E, 0xC7, 0xC7, 0xA1, 0x42, 0x0F, 0x4B, 0x43, 0xFB, 0x58, 0xBA, 0xC7, 0x66, 0xD6, 0xCA, 0x6B, 0xC7, 0x45, 0x7C, 0x99, 0xE4, 0x46, 0x02, 0x93, 0x3F, 0x28, 0xD2, 0xCE, 0x0C, 0x8A, 0xDD, 0x6A, 0x22, 0x2E, 0xA9, 0x9A, 0xCA, 0x16, 0x48, 0x4E, 0x67, 0x4C, 0xE9, 0xC8, 0x54, 0xCD, 0x18, 0xC9, 0xF3, 0x30, 0x3A, 0x74, 0xAB, 0xF9, 0xAF, 0xE4, 0xA4, 0x0D, 0x56, 0x62, 0x28, 0x07, 0xBF }; static unsigned char dh1024_g[] = { 0x05 }; static unsigned char dh2048_p[] = { 0xBF, 0x67, 0x7B, 0x79, 0xA5, 0x22, 0xD3, 0xB5, 0x0C, 0x13, 0xE6, 0x92, 0x54, 0xFD, 0x64, 0xBF, 0x57, 0x25, 0xBD, 0x02, 0x7C, 0xFD, 0x72, 0x97, 0x82, 0xA4, 0xA6, 0x0A, 0xB9, 0xE6, 0x4B, 0xFA, 0xBD, 0xFA, 0x71, 0x8A, 0x2E, 0x36, 0xF9, 0x03, 0x58, 0x1B, 0xB6, 0x3A, 0xFD, 0x15, 0xCC, 0x87, 0x5D, 0x04, 0xF7, 0x45, 0xE0, 0xE2, 0x34, 0x7F, 0x54, 0x5F, 0x5D, 0x14, 0xD3, 0xCA, 0x3E, 0xFD, 0x2A, 0x92, 0x10, 0x89, 0xA0, 0xB0, 0xB4, 0xE5, 0x80, 0x05, 0x13, 0xBE, 0xA3, 0xD0, 0x42, 0x4B, 0x98, 0x44, 0x54, 0xB3, 0xE0, 0x23, 0x26, 0xF5, 0x6B, 0x0E, 0x4D, 0x2A, 0x81, 0xB2, 0x8A, 0x06, 0xC8, 0x00, 0x9E, 0xAB, 0x1B, 0x77, 0xDC, 0x87, 0x9C, 0x6C, 0xD5, 0xEE, 0xB4, 0xB4, 0xDD, 0xDA, 0x3F, 0x40, 0xA3, 0xFA, 0xC1, 0x1E, 0xC0, 0xA2, 0x9E, 0xB8, 0xAC, 0x31, 0xE8, 0x12, 0x93, 0x9C, 0x71, 0xF6, 0xE7, 0xF0, 0x65, 0x7F, 0xA5, 0x20, 0xF7, 0x49, 0x3D, 0xD6, 0xF9, 0xD3, 0xF0, 0x3F, 0xB3, 0xF0, 0xD0, 0x23, 0x22, 0x82, 0xA5, 0xDD, 0xFB, 0xD9, 0x9C, 0x7D, 0xE7, 0xA0, 0x78, 0xE8, 0xF9, 0x02, 0x0C, 0x2F, 0x1D, 0x52, 0xC7, 0x61, 0xED, 0xA0, 0xC9, 0x06, 0x14, 0xDF, 0xE7, 0xB1, 0x1E, 0x50, 0x98, 0x4F, 0x10, 0xB9, 0x87, 0x4C, 0x1C, 0x9C, 0xB3, 0xD2, 0x98, 0x23, 0x7C, 0x47, 0xD2, 0x3C, 0xC5, 0x29, 0x65, 0xC5, 0x67, 0x4E, 0xC0, 0x76, 0x0F, 0x43, 0x27, 0x28, 0x89, 0x69, 0x30, 0x7D, 0x04, 0xFD, 0xF7, 0x89, 0xE5, 0xD6, 0xE6, 0x97, 0x7D, 0xBB, 0x54, 0x5F, 0xB7, 0x94, 0x1D, 0xBC, 0x82, 0xAB, 0x9A, 0xF5, 0x0A, 0x0C, 0x89, 0x68, 0xE7, 0x0A, 0x8C, 0x2D, 0x0D, 0x82, 0x44, 0xA7, 0xB8, 0xF9, 0x0B, 0x8E, 0xCB, 0xA4, 0x6A, 0xA7, 0xEC, 0x5F, 0x0A, 0xF8, 0x5F, 0xE7 }; static unsigned char dh2048_g[] = { 0x05 }; static unsigned char dh4096_p[] = { 0xFA, 0x14, 0x72, 0x52, 0xC1, 0x4D, 0xE1, 0x5A, 0x49, 0xD4, 0xEF, 0x09, 0x2D, 0xC0, 0xA8, 0xFD, 0x55, 0xAB, 0xD7, 0xD9, 0x37, 0x04, 0x28, 0x09, 0xE2, 0xE9, 0x3E, 0x77, 0xE2, 0xA1, 0x7A, 0x18, 0xDD, 0x46, 0xA3, 0x43, 0x37, 0x23, 0x90, 0x97, 0xF3, 0x0E, 0xC9, 0x03, 0x50, 0x7D, 0x65, 0xCF, 0x78, 0x62, 0xA6, 0x3A, 0x62, 0x22, 0x83, 0xA1, 0x2F, 0xFE, 0x79, 0xBA, 0x35, 0xFF, 0x59, 0xD8, 0x1D, 0x61, 0xDD, 0x1E, 0x21, 0x13, 0x17, 0xFE, 0xCD, 0x38, 0x87, 0x9E, 0xF5, 0x4F, 0x79, 0x10, 0x61, 0x8D, 0xD4, 0x22, 0xF3, 0x5A, 0xED, 0x5D, 0xEA, 0x21, 0xE9, 0x33, 0x6B, 0x48, 0x12, 0x0A, 0x20, 0x77, 0xD4, 0x25, 0x60, 0x61, 0xDE, 0xF6, 0xB4, 0x4F, 0x1C, 0x63, 0x40, 0x8B, 0x3A, 0x21, 0x93, 0x8B, 0x79, 0x53, 0x51, 0x2C, 0xCA, 0xB3, 0x7B, 0x29, 0x56, 0xA8, 0xC7, 0xF8, 0xF4, 0x7B, 0x08, 0x5E, 0xA6, 0xDC, 0xA2, 0x45, 0x12, 0x56, 0xDD, 0x41, 0x92, 0xF2, 0xDD, 0x5B, 0x8F, 0x23, 0xF0, 0xF3, 0xEF, 0xE4, 0x3B, 0x0A, 0x44, 0xDD, 0xED, 0x96, 0x84, 0xF1, 0xA8, 0x32, 0x46, 0xA3, 0xDB, 0x4A, 0xBE, 0x3D, 0x45, 0xBA, 0x4E, 0xF8, 0x03, 0xE5, 0xDD, 0x6B, 0x59, 0x0D, 0x84, 0x1E, 0xCA, 0x16, 0x5A, 0x8C, 0xC8, 0xDF, 0x7C, 0x54, 0x44, 0xC4, 0x27, 0xA7, 0x3B, 0x2A, 0x97, 0xCE, 0xA3, 0x7D, 0x26, 0x9C, 0xAD, 0xF4, 0xC2, 0xAC, 0x37, 0x4B, 0xC3, 0xAD, 0x68, 0x84, 0x7F, 0x99, 0xA6, 0x17, 0xEF, 0x6B, 0x46, 0x3A, 0x7A, 0x36, 0x7A, 0x11, 0x43, 0x92, 0xAD, 0xE9, 0x9C, 0xFB, 0x44, 0x6C, 0x3D, 0x82, 0x49, 0xCC, 0x5C, 0x6A, 0x52, 0x42, 0xF8, 0x42, 0xFB, 0x44, 0xF9, 0x39, 0x73, 0xFB, 0x60, 0x79, 0x3B, 0xC2, 0x9E, 0x0B, 0xDC, 0xD4, 0xA6, 0x67, 0xF7, 0x66, 0x3F, 0xFC, 0x42, 0x3B, 0x1B, 0xDB, 0x4F, 0x66, 0xDC, 0xA5, 0x8F, 0x66, 0xF9, 0xEA, 0xC1, 0xED, 0x31, 0xFB, 0x48, 0xA1, 0x82, 0x7D, 0xF8, 0xE0, 0xCC, 0xB1, 0xC7, 0x03, 0xE4, 0xF8, 0xB3, 0xFE, 0xB7, 0xA3, 0x13, 0x73, 0xA6, 0x7B, 0xC1, 0x0E, 0x39, 0xC7, 0x94, 0x48, 0x26, 0x00, 0x85, 0x79, 0xFC, 0x6F, 0x7A, 0xAF, 0xC5, 0x52, 0x35, 0x75, 0xD7, 0x75, 0xA4, 0x40, 0xFA, 0x14, 0x74, 0x61, 0x16, 0xF2, 0xEB, 0x67, 0x11, 0x6F, 0x04, 0x43, 0x3D, 0x11, 0x14, 0x4C, 0xA7, 0x94, 0x2A, 0x39, 0xA1, 0xC9, 0x90, 0xCF, 0x83, 0xC6, 0xFF, 0x02, 0x8F, 0xA3, 0x2A, 0xAC, 0x26, 0xDF, 0x0B, 0x8B, 0xBE, 0x64, 0x4A, 0xF1, 0xA1, 0xDC, 0xEE, 0xBA, 0xC8, 0x03, 0x82, 0xF6, 0x62, 0x2C, 0x5D, 0xB6, 0xBB, 0x13, 0x19, 0x6E, 0x86, 0xC5, 0x5B, 0x2B, 0x5E, 0x3A, 0xF3, 0xB3, 0x28, 0x6B, 0x70, 0x71, 0x3A, 0x8E, 0xFF, 0x5C, 0x15, 0xE6, 0x02, 0xA4, 0xCE, 0xED, 0x59, 0x56, 0xCC, 0x15, 0x51, 0x07, 0x79, 0x1A, 0x0F, 0x25, 0x26, 0x27, 0x30, 0xA9, 0x15, 0xB2, 0xC8, 0xD4, 0x5C, 0xCC, 0x30, 0xE8, 0x1B, 0xD8, 0xD5, 0x0F, 0x19, 0xA8, 0x80, 0xA4, 0xC7, 0x01, 0xAA, 0x8B, 0xBA, 0x53, 0xBB, 0x47, 0xC2, 0x1F, 0x6B, 0x54, 0xB0, 0x17, 0x60, 0xED, 0x79, 0x21, 0x95, 0xB6, 0x05, 0x84, 0x37, 0xC8, 0x03, 0xA4, 0xDD, 0xD1, 0x06, 0x69, 0x8F, 0x4C, 0x39, 0xE0, 0xC8, 0x5D, 0x83, 0x1D, 0xBE, 0x6A, 0x9A, 0x99, 0xF3, 0x9F, 0x0B, 0x45, 0x29, 0xD4, 0xCB, 0x29, 0x66, 0xEE, 0x1E, 0x7E, 0x3D, 0xD7, 0x13, 0x4E, 0xDB, 0x90, 0x90, 0x58, 0xCB, 0x5E, 0x9B, 0xCD, 0x2E, 0x2B, 0x0F, 0xA9, 0x4E, 0x78, 0xAC, 0x05, 0x11, 0x7F, 0xE3, 0x9E, 0x27, 0xD4, 0x99, 0xE1, 0xB9, 0xBD, 0x78, 0xE1, 0x84, 0x41, 0xA0, 0xDF }; static unsigned char dh4096_g[] = { 0x02 }; static DH * my_get_dh(int keylength) { DH * dh = nullptr; unsigned char * p = nullptr; unsigned char * g = nullptr; int sp = 0; int sg = 0; BIGNUM *bp, *bg; switch(keylength) { case 512: dh = dh_512; p = dh512_p; g = dh512_g; sp = sizeof(dh512_p); sg = sizeof(dh512_g); break; case 1024: dh = dh_1024; p = dh1024_p; g = dh1024_g; sp = sizeof(dh1024_p); sg = sizeof(dh1024_g); break; case 2048: dh = dh_2048; p = dh2048_p; g = dh2048_g; sp = sizeof(dh2048_p); sg = sizeof(dh2048_g); break; case 4096: dh = dh_4096; p = dh4096_p; g = dh4096_g; sp = sizeof(dh4096_p); sg = sizeof(dh4096_g); break; default: // What the hell do you want from me ? qDebug("OpenSSL is asking for a DH param with keylen %d: no way :D", keylength); break; } if(dh) return dh; dh = DH_new(); if(!dh) return nullptr; bp = BN_bin2bn(p, sp, nullptr); bg = BN_bin2bn(g, sg, nullptr); if((p == nullptr) || (g == nullptr)) { BN_free(bp); BN_free(bg); DH_free(dh); return nullptr; } #if OPENSSL_VERSION_NUMBER >= 0x10100005L DH_set0_pqg(dh, bp, nullptr, bg); #else dh->p = bp; dh->g = bg; #endif return dh; } DH * my_ugly_dh_callback(SSL *, int, int keylength) { my_ssl_lock(); DH * dh = my_get_dh(keylength); my_ssl_unlock(); return dh; } #endif void KviSSL::globalInit() { if(g_pSSLMutex) return; g_pSSLMutex = new KviMutex(); } void KviSSL::globalDestroy() { if(!g_pSSLMutex) return; #if OPENSSL_VERSION_NUMBER < 0x30000000L if(dh_512) DH_free(dh_512); if(dh_1024) DH_free(dh_1024); if(dh_2048) DH_free(dh_2048); if(dh_4096) DH_free(dh_4096); #endif globalSSLDestroy(); delete g_pSSLMutex; g_pSSLMutex = nullptr; } void KviSSL::globalSSLInit() { my_ssl_lock(); if(!g_bSSLInitialized) { // FIXME: this should be done only if SSL is really needed SSL_library_init(); SSL_load_error_strings(); //needed to support SHA2 in < OpenSSL 0.9.8o and 1.0.0a OpenSSL_add_all_algorithms(); g_bSSLInitialized = true; } my_ssl_unlock(); } void KviSSL::globalSSLDestroy() { my_ssl_lock(); if(g_bSSLInitialized) { EVP_cleanup(); g_bSSLInitialized = false; } my_ssl_unlock(); } KviSSL::KviSSL() { globalSSLInit(); m_pSSL = nullptr; m_pSSLCtx = nullptr; } KviSSL::~KviSSL() { shutdown(); } #ifdef COMPILE_ON_WINDOWS // On windows we need to override new and delete operators // to ensure that always the right new/delete pair is called for an object instance // This bug is present in all the classes exported by a module that // can be instantiated/destroyed from external modules. // (this is a well known bug described in Q122675 of MSDN) void * KviSSL::operator new(size_t tSize) { return KviMemory::allocate(tSize); } void KviSSL::operator delete(void * p) { KviMemory::free(p); } #endif void KviSSL::shutdown() { if(m_pSSL) { //avoid to die on a SIGPIPE if the connection has close (SSL_shutdown can call send()) //see bug #440 #if !(defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)) // ignore SIGPIPE signal(SIGPIPE, SIG_IGN); // At least attempt to shutdown the connection gracefully if(!SSL_in_init(m_pSSL)) SSL_shutdown(m_pSSL); //restore normal SIGPIPE behaviour. signal(SIGPIPE, SIG_DFL); #else // At least attempt to shutdown the connection gracefully if(!SSL_in_init(m_pSSL)) SSL_shutdown(m_pSSL); #endif SSL_free(m_pSSL); m_pSSL = nullptr; } if(m_pSSLCtx) { SSL_CTX_free(m_pSSLCtx); m_pSSLCtx = nullptr; } } /** * Used only during passive (server) mode * Check the client certificate, always valid, even if it not exists */ int verify_clientCallback(int, X509_STORE_CTX *) { //int preverify_ok, X509_STORE_CTX *x509_ctx // From man page: If verify_callback returns 1, the verification process is continued. 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. return 1; } bool KviSSL::initContext(Method m) { if(m_pSSL) return false; m_pSSLCtx = SSL_CTX_new(m == Client ? SSLv23_client_method() : SSLv23_server_method()); if(!m_pSSLCtx) return false; if(m == Server) { // we have to request the peer certificate, else only the client can see the peer identity, not the server SSL_CTX_set_verify(m_pSSLCtx, SSL_VERIFY_PEER, verify_clientCallback); } SSL_CTX_set_options (m_pSSLCtx, // disable old, unsecure protocols SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3 |SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1 // disable unsecure defaults on old openSSL versions #ifdef SSL_OP_NO_COMPRESSION |SSL_OP_NO_COMPRESSION #endif #ifdef SSL_OP_SINGLE_DH_USE |SSL_OP_SINGLE_DH_USE #endif #ifdef SSL_OP_SINGLE_ECDH_USE |SSL_OP_SINGLE_ECDH_USE #endif #ifdef SSL_OP_IGNORE_UNEXPECTED_EOF |SSL_OP_IGNORE_UNEXPECTED_EOF #endif ); // we want all ciphers to be available here, except insecure ones, orderer by strength; SSL_CTX_set_cipher_list(m_pSSLCtx, "ALL:!eNULL:!LOW:!EXP:!SSLv2:!SSLv3:!TLSv1:@STRENGTH"); #if OPENSSL_VERSION_NUMBER >= 0x30000000L SSL_CTX_set_dh_auto(m_pSSLCtx, 1); #else SSL_CTX_set_tmp_dh_callback(m_pSSLCtx, my_ugly_dh_callback); #endif return true; } bool KviSSL::enableADHCiphers() { if(!m_pSSLCtx) return false; if(!m_pSSL) return false; // Add Anonymous DH cipher suites to the list of available ciphers. // ADH don't need a certificate, so we can use secure dcc without a cert) // They are moved to the end since they are considered NOT SECURE since at least 2015's Logjam // NOTE: see bug ticket #155 if(!SSL_set_cipher_list(m_pSSL, "ALL:!eNULL:!LOW:!EXP:!SSLv2:!SSLv3:!TLSv1:+ADH:+AECDH:@STRENGTH:@SECLEVEL=0")) return false; return true; } bool KviSSL::initSocket(kvi_socket_t fd) { if(!m_pSSLCtx) return false; m_pSSL = SSL_new(m_pSSLCtx); if(!m_pSSL) return false; if(!SSL_set_fd(m_pSSL, fd)) return false; return true; } static int cb(char * buf, int size, int, void * u) { KviCString * p = (KviCString *)u; int len = p->len(); if(len >= size) return 0; KviMemory::move(buf, p->ptr(), len + 1); //qDebug("PASS REQYESTED: %s",p->ptr()); return len; } KviSSL::Result KviSSL::useCertificateFile(QString cert, QString pass) { if(!m_pSSLCtx) return NotInitialized; m_szPass = pass.toUtf8().data(); if(m_szPass.len() < 4) m_szPass.append("xxxx"); X509 * x509 = nullptr; #ifdef COMPILE_ON_WINDOWS FILE * f = _wfopen(cert.toStdWString().data(), L"r"); #else FILE * f = fopen(cert.toUtf8().data(), "r"); #endif if(!f) return FileIoError; //qDebug("READING CERTIFICATE %s",cert.Utf8().data()); if(PEM_read_X509(f, &x509, cb, &m_szPass)) { if(!SSL_CTX_use_certificate(m_pSSLCtx, x509)) { X509_free(x509); fclose(f); return SSLError; } X509_free(x509); } fclose(f); return Success; } KviSSL::Result KviSSL::usePrivateKeyFile(QString key, QString pass) { if(!m_pSSLCtx) return NotInitialized; m_szPass = pass.toUtf8().data(); if(m_szPass.len() < 4) m_szPass.append("xxxx"); EVP_PKEY * k = nullptr; #ifdef COMPILE_ON_WINDOWS FILE * f = _wfopen(key.toStdWString().data(), L"r"); #else FILE * f = fopen(key.toUtf8().data(), "r"); #endif if(!f) return FileIoError; //qDebug("READING KEY %s",key.toUtf8().data()); if(PEM_read_PrivateKey(f, &k, cb, &m_szPass)) { if(!SSL_CTX_use_PrivateKey(m_pSSLCtx, k)) { EVP_PKEY_free(k); fclose(f); return SSLError; } EVP_PKEY_free(k); } fclose(f); return Success; } unsigned long KviSSL::getLastError(bool bPeek) { return bPeek ? ERR_peek_error() : ERR_get_error(); } bool KviSSL::getLastErrorString(KviCString & buffer, bool bPeek) { unsigned long uErr = getLastError(bPeek); if(uErr != 0) { const char * err = ERR_reason_error_string(uErr); buffer = err ? err : "Unknown error"; return true; } return false; } bool KviSSL::setTLSHostname(const char * name) { if(!m_pSSL) return false; return SSL_set_tlsext_host_name(m_pSSL, name) ? true : false; } KviSSL::Result KviSSL::connect() { if(!m_pSSL) return NotInitialized; #if !(defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)) // ignore SIGPIPE signal(SIGPIPE, SIG_IGN); int ret = SSL_connect(m_pSSL); //restore normal SIGPIPE behaviour. signal(SIGPIPE, SIG_DFL); #else int ret = SSL_connect(m_pSSL); #endif return connectOrAcceptError(ret); } KviSSL::Result KviSSL::accept() { if(!m_pSSL) return NotInitialized; int ret = SSL_accept(m_pSSL); return connectOrAcceptError(ret); } KviSSL::Result KviSSL::connectOrAcceptError(int ret) { switch(SSL_get_error(m_pSSL, ret)) { case SSL_ERROR_NONE: return Success; break; case SSL_ERROR_WANT_READ: return WantRead; break; case SSL_ERROR_WANT_WRITE: return WantWrite; break; case SSL_ERROR_ZERO_RETURN: return RemoteEndClosedConnection; break; case SSL_ERROR_WANT_X509_LOOKUP: return ObscureError; break; case SSL_ERROR_SYSCALL: { if(getLastError(true) != 0) return SSLError; if(ret == 0) return RemoteEndClosedConnection; return SyscallError; } break; case SSL_ERROR_SSL: return SSLError; break; default: return UnknownError; break; } return UnknownError; } int KviSSL::read(char * buffer, int len) { int iReceived = SSL_read(m_pSSL, buffer, len); g_uIncomingTraffic += iReceived; return iReceived; } int KviSSL::write(const char * buffer, int len) { g_uOutgoingTraffic += len; return SSL_write(m_pSSL, buffer, len); } KviSSL::Result KviSSL::getProtocolError(int ret) { if(!m_pSSL) return NotInitialized; switch(SSL_get_error(m_pSSL, ret)) { case SSL_ERROR_NONE: return Success; break; case SSL_ERROR_WANT_READ: return WantRead; break; case SSL_ERROR_WANT_WRITE: return WantWrite; break; case SSL_ERROR_ZERO_RETURN: return ZeroReturn; break; case SSL_ERROR_WANT_X509_LOOKUP: return ObscureError; break; case SSL_ERROR_SYSCALL: return SyscallError; break; case SSL_ERROR_SSL: return SSLError; break; default: return UnknownError; break; } return UnknownError; } KviSSLCertificate * KviSSL::getPeerCertificate() { if(!m_pSSL) return nullptr; X509 * x509 = SSL_get_peer_certificate(m_pSSL); if(!x509) return nullptr; return new KviSSLCertificate(x509); } KviSSLCertificate * KviSSL::getLocalCertificate() { if(!m_pSSL) return nullptr; X509 * x509 = SSL_get_certificate(m_pSSL); if(!x509) return nullptr; return new KviSSLCertificate(x509); } KviSSLCipherInfo * KviSSL::getCurrentCipherInfo() { if(!m_pSSL) return nullptr; #if OPENSSL_VERSION_NUMBER >= 0x10000000L const SSL_CIPHER * c = SSL_get_current_cipher(m_pSSL); #else SSL_CIPHER * c = SSL_get_current_cipher(m_pSSL); #endif if(!c) return nullptr; return new KviSSLCipherInfo(c, m_pSSL); } KviSSLCertificate::KviSSLCertificate(X509 * x509) { m_pSubject = new KviPointerHashTable(17); m_pSubject->setAutoDelete(true); m_pIssuer = new KviPointerHashTable(17); m_pIssuer->setAutoDelete(true); m_pX509 = nullptr; setX509(x509); } KviSSLCertificate::~KviSSLCertificate() { X509_free(m_pX509); delete m_pSubject; delete m_pIssuer; } char * KviSSLCertificate::getX509Base64() { BUF_MEM * bptr; BIO * mem = BIO_new(BIO_s_mem()); PEM_write_bio_X509(mem, m_pX509); int iLen = BIO_get_mem_data(mem, &bptr); char * szTmp = (char *)KviMemory::allocate(iLen + 1); KviMemory::copy(szTmp, bptr, iLen); *(szTmp + iLen) = '\0'; BIO_free_all(mem); return szTmp; } #ifdef COMPILE_ON_WINDOWS // On windows we need to override new and delete operators // to ensure that always the right new/delete pair is called for an object instance // This bug is present in all the classes exported by a module that // can be instantiated/destroyed from external modules. // (this is a well known bug described in Q122675 of MSDN) void * KviSSLCertificate::operator new(size_t tSize) { return KviMemory::allocate(tSize); } void KviSSLCertificate::operator delete(void * p) { KviMemory::free(p); } #endif void KviSSLCertificate::setX509(X509 * x509) { if(m_pX509) X509_free(m_pX509); m_pX509 = x509; m_iVersion = X509_get_version(x509); extractSubject(); extractIssuer(); extractPubKeyInfo(); extractSerialNumber(); extractSignature(); } bool KviSSLCertificate::fingerprintIsValid() { if(!m_pX509) return false; EVP_PKEY * pkey = X509_get_pubkey(m_pX509); int rv = X509_verify(m_pX509, pkey); // careful: https://support.ntp.org/bugs/show_bug.cgi?id=1127 // quote: X509_verify is a call to ASN1_item_verify which can return both 0 and -1 for error cases. // In particular it can return -1 when the message digest type is not known, or memory allocation failed. return (rv > 0); } int KviSSLCertificate::fingerprintDigestId() { if(!m_pX509) return -1; const X509_ALGOR * alg; #if OPENSSL_VERSION_NUMBER >= 0x10100005L X509_get0_signature(nullptr, &alg, m_pX509); #else alg = m_pX509->sig_alg; #endif int NID = OBJ_obj2nid(alg->algorithm); if(NID == NID_undef) { return 0; // unknown digest function: it means the signature can't be verified: the certificate can't be trusted } const EVP_MD * mdType = nullptr; mdType = EVP_get_digestbyname(OBJ_nid2sn(NID)); if(mdType == nullptr) { return 0; // Unknown digest } return EVP_MD_type(mdType); } const char * KviSSLCertificate::fingerprintDigestStr() { int iDigestType = fingerprintDigestId(); if(iDigestType == 0) return ""; return OBJ_nid2ln(iDigestType); } const char * KviSSLCertificate::fingerprintContents(QString digestName) { unsigned char bufferData[EVP_MAX_MD_SIZE]; unsigned int bufferLen = 0; const char * pDigestName; if(digestName.isEmpty()) { pDigestName = OBJ_nid2sn(fingerprintDigestId()); // use the one used to create the signature } else { pDigestName = digestName.toUtf8().data(); } if(getFingerprint(bufferData, &bufferLen, pDigestName) != 0) return ""; QByteArray digestByteArray = QByteArray::fromRawData((char *)bufferData, bufferLen); return digestByteArray.toHex().data(); } int KviSSLCertificate::getFingerprint(unsigned char * bufferData, unsigned int * bufferLen, const char * digestName) { //TODO if in the future we will want to check the return value, ensure this // doesn't collide with the one from openssl if(!m_pX509) return -99; const EVP_MD * mdType = nullptr; mdType = EVP_get_digestbyname(digestName); if(mdType == nullptr) { return -98; // Unknown digest } if(!X509_digest(m_pX509, mdType, bufferData, bufferLen)) { return -97; } return 0; } void KviSSLCertificate::extractSubject() { char buffer[1024]; char * t = X509_NAME_oneline(X509_get_subject_name(m_pX509), buffer, 1024); if(!t) return; m_pSubject->clear(); splitX509String(m_pSubject, t); } void KviSSLCertificate::extractIssuer() { char buffer[1024]; char * t = X509_NAME_oneline(X509_get_issuer_name(m_pX509), buffer, 1024); if(!t) return; m_pIssuer->clear(); splitX509String(m_pIssuer, t); } void KviSSLCertificate::splitX509String(KviPointerHashTable * dict, const char * t) { KviCString buf = t; int cnt; KviCString ** arr = buf.splitToArray('/', 50, &cnt); if(arr) { if(cnt > 0) { for(int i = 0; i < cnt; i++) { int idx = arr[i]->findFirstIdx('='); if(idx != -1) { KviCString szTok = arr[i]->left(idx); arr[i]->cutLeft(idx + 1); if(szTok.hasData() && arr[i]->hasData()) { dict->replace(szTok.ptr(), new KviCString(arr[i]->ptr())); } } } } KviCString::freeArray(arr); } } const char * KviSSLCertificate::dictEntry(KviPointerHashTable * dict, const char * entry) { KviCString * t = dict->find(entry); if(!t) return __tr("Unknown"); return t->ptr(); } void KviSSLCertificate::extractPubKeyInfo() { EVP_PKEY * p = X509_get_pubkey(m_pX509); if(p) { int type; #if OPENSSL_VERSION_NUMBER >= 0x10100005L type = EVP_PKEY_base_id(p); #else type = EVP_PKEY_type(p->type); #endif m_iPubKeyBits = EVP_PKEY_bits(p); m_szPubKeyType = (type == NID_undef) ? __tr("Unknown") : OBJ_nid2ln(type); // getPKeyType(p->type,m_szPubKeyType); } else { m_iPubKeyBits = 0; m_szPubKeyType = "None"; } } void KviSSLCertificate::extractSerialNumber() { m_szSerialNumber.clear(); ASN1_INTEGER * i = X509_get_serialNumber(m_pX509); if(i) { BIGNUM * bn = ASN1_INTEGER_to_BN(i, nullptr); if(bn) { char * str = BN_bn2dec(bn); if(str) { m_szSerialNumber = KviCString(str); OPENSSL_free(str); } BN_free(bn); } } } void KviSSLCertificate::extractSignature() { static char hexdigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; const ASN1_BIT_STRING * sig; const X509_ALGOR * alg; #if OPENSSL_VERSION_NUMBER >= 0x10100005L X509_get0_signature(&sig, &alg, m_pX509); #else sig = m_pX509->signature; alg = m_pX509->sig_alg; #endif int i = OBJ_obj2nid(alg->algorithm); m_szSignatureType = (i == NID_undef) ? __tr("Unknown") : OBJ_nid2ln(i); m_szSignatureContents = ""; #if OPENSSL_VERSION_NUMBER >= 0x10100005L int length = ASN1_STRING_length(sig); #else int length = sig->length; #endif for(i = 0; i < length; i++) { if(m_szSignatureContents.hasData()) m_szSignatureContents.append(":"); #if OPENSSL_VERSION_NUMBER >= 0x10100005L const unsigned char *data = ASN1_STRING_get0_data(sig); m_szSignatureContents.append(hexdigits[(data[i] & 0xf0) >> 4]); m_szSignatureContents.append(hexdigits[(data[i] & 0x0f)]); #else m_szSignatureContents.append(hexdigits[(sig->data[i] & 0xf0) >> 4]); m_szSignatureContents.append(hexdigits[(sig->data[i] & 0x0f)]); #endif } } #if OPENSSL_VERSION_NUMBER >= 0x10000000L KviSSLCipherInfo::KviSSLCipherInfo(const SSL_CIPHER * c, const SSL * s) #else KviSSLCipherInfo::KviSSLCipherInfo(SSL_CIPHER * c, SSL * s) #endif { m_szVersion = SSL_get_version(s); m_iNumBitsUsed = SSL_CIPHER_get_bits(c, &m_iNumBits); m_szName = SSL_CIPHER_get_name(c); char buf[1024]; m_szDescription = SSL_CIPHER_description(c, buf, 1024); } KviSSLCipherInfo::~KviSSLCipherInfo() = default; #ifdef COMPILE_ON_WINDOWS // On windows we need to override new and delete operators // to ensure that always the right new/delete pair is called for an object instance // This bug is present in all the classes exported by a module that // can be instantiated/destroyed from external modules. // (this is a well known bug described in Q122675 of MSDN) void * KviSSLCipherInfo::operator new(size_t tSize) { return KviMemory::allocate(tSize); } void KviSSLCipherInfo::operator delete(void * p) { KviMemory::free(p); } #endif #endif //COMPILE_SSL_SUPPORT