diff options
Diffstat (limited to 'src/kvilib/net/KviSSL.cpp')
| -rw-r--r-- | src/kvilib/net/KviSSL.cpp | 866 |
1 files changed, 866 insertions, 0 deletions
diff --git a/src/kvilib/net/KviSSL.cpp b/src/kvilib/net/KviSSL.cpp new file mode 100644 index 000000000..dedc70632 --- /dev/null +++ b/src/kvilib/net/KviSSL.cpp @@ -0,0 +1,866 @@ +//============================================================================= +// +// 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 opinion) 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" + +#ifdef COMPILE_SSL_SUPPORT + +#include "KviThread.h" +#include "kvi_memmove.h" +#include "KviMemory.h" + +#include <openssl/asn1.h> +#include <openssl/err.h> +#include <openssl/dh.h> + +#include <stdio.h> + +#if !(defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)) + #include <signal.h> +#endif + +static bool g_bSSLInitialized = false; +static KviMutex * g_pSSLMutex = 0; + + +static inline void my_ssl_lock() +{ + g_pSSLMutex->lock(); +} + +static inline void my_ssl_unlock() +{ + g_pSSLMutex->unlock(); +} + + +// THIS PART OF OpenSSL SUCKS + +static DH * dh_512 = 0; +static DH * dh_1024 = 0; +static DH * dh_2048 = 0; +static DH * dh_4096 = 0; + +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 = 0; + unsigned char * p = 0; + unsigned char * g = 0; + int sp = 0; + int sg = 0; + 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 0; + dh->p=BN_bin2bn(p,sp,0); + dh->g=BN_bin2bn(g,sg,0); + if((dh->p == 0) || (dh->g == 0)) + { + DH_free(dh); + return 0; + } + 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; +} + +void KviSSL::globalInit() +{ + if(g_pSSLMutex)return; + g_pSSLMutex = new KviMutex(); +} + +void KviSSL::globalDestroy() +{ + if(!g_pSSLMutex)return; + 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); + globalSSLDestroy(); + delete g_pSSLMutex; + g_pSSLMutex = 0; +} + +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 = 0; + m_pSSLCtx = 0; +} + +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) + { + #if !(defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)) + //avoid to die on a SIGPIPE if the connection has close (SSL_shutdown can call send()) + //see bug #440 + signal( SIGPIPE, SIG_IGN ); + // At least attempt to shutdown the connection gracefully + SSL_shutdown(m_pSSL); + //restore normal SIGPIPE behaviour. + signal( SIGPIPE, SIG_DFL ); + #else + // At least attempt to shutdown the connection gracefully + SSL_shutdown(m_pSSL); + #endif + SSL_free(m_pSSL); + m_pSSL = 0; + } + if(m_pSSLCtx) + { + SSL_CTX_free(m_pSSLCtx); + m_pSSLCtx = 0; + } +} + +/** + * 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); + } + + // we want all ciphers to be available here, except insecure ones, orderer by strength; + // ADH are moved to the end since they are less secure, but they don't need a certificate + // (so we can use secure dcc without a cert) + // NOTE: see bug ticket #155 + SSL_CTX_set_cipher_list(m_pSSLCtx,"ALL:!eNULL:!EXP:!SSLv2:+ADH@STRENGTH"); + SSL_CTX_set_tmp_dh_callback(m_pSSLCtx,my_ugly_dh_callback); + 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; + kvi_memmove(buf,p->ptr(),len + 1); +// qDebug("PASS REQYESTED: %s",p->ptr()); + return len; +} + +KviSSL::Result KviSSL::useCertificateFile(const char * cert,const char * pass) +{ + if(!m_pSSLCtx)return NotInitialized; + m_szPass = pass; + if(m_szPass.len() < 4)m_szPass.append("xxxx"); + X509 * x509 = 0; + + FILE * f = fopen(cert,"r"); + if(!f) + return FileIoError; + +// qDebug("READING CERTIFICATE %s",cert); + 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(const char * key,const char * pass) +{ + if(!m_pSSLCtx)return NotInitialized; + m_szPass = pass; + if(m_szPass.len() < 4)m_szPass.append("xxxx"); + + EVP_PKEY * k = 0; + + FILE * f = fopen(key,"r"); + if(!f) + return FileIoError; + +// qDebug("READING KEY %s",key); + 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; +} + +KviSSL::Result KviSSL::connect() +{ + if(!m_pSSL)return NotInitialized; + int ret = SSL_connect(m_pSSL); + 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) +{ +// if(!m_pSSL)return -1; + return SSL_read(m_pSSL,buffer,len); +} +int KviSSL::write(const char * buffer,int len) +{ +// if(!m_pSSL)return -1; + 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 0; + X509 * x509 = SSL_get_peer_certificate(m_pSSL); + if(!x509)return 0; + return new KviSSLCertificate(x509); +} + +KviSSLCertificate * KviSSL::getLocalCertificate() +{ + if(!m_pSSL)return 0; + X509 * x509 = SSL_get_certificate(m_pSSL); + if(!x509)return 0; + return new KviSSLCertificate(x509); +} + +KviSSLCipherInfo * KviSSL::getCurrentCipherInfo() +{ + if(!m_pSSL)return 0; +#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 0; + return new KviSSLCipherInfo(c); +} + + + +KviSSLCertificate::KviSSLCertificate(X509 * x509) +{ + m_pSubject = new KviPointerHashTable<const char *,KviCString>(17); + m_pSubject->setAutoDelete(true); + m_pIssuer = new KviPointerHashTable<const char *,KviCString>(17); + m_pIssuer->setAutoDelete(true); + m_pX509 = 0; + setX509(x509); +} + +KviSSLCertificate::~KviSSLCertificate() +{ + X509_free(m_pX509); + delete m_pSubject; + delete m_pIssuer; +} + +const 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); + kvi_fastmove(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; + + int NID = OBJ_obj2nid(m_pX509->sig_alg->algorithm); + if (NID == NID_undef) + { + // unknow digest function: it means the signature can't be verified: the certificate can't be trusted + return 0; + } + + const EVP_MD * mdType = NULL; + mdType = EVP_get_digestbyname(OBJ_nid2sn(NID)); + + if (mdType == NULL) + { + // Unknown digest + return 0; + } + + return mdType->type; +} + +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()) + { + // use the one used to create the signature + pDigestName = OBJ_nid2sn(fingerprintDigestId()); + } 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 = NULL; + mdType = EVP_get_digestbyname(digestName); + + if (mdType == NULL) + { + // Unknown digest + return -98; + } + + 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<const char *,KviCString> * 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<const char *,KviCString> * dict,const char * entry) +{ + KviCString * t = dict->find(entry); + if(!t)return __tr("Unknown"); + return t->ptr(); +} + + +/* +void KviSSLCertificate::getPKeyType(int type,KviCString &buffer) +{ + switch(type) + { +#ifndef NO_RSA + case EVP_PKEY_RSA: buffer = "RSA"; break; +#endif +#ifndef NO_DSA + case EVP_PKEY_DSA: buffer = "DSA"; break; +#endif +#ifndef NO_DH + case EVP_PKEY_DH: buffer = "DH"; break; +#endif + case EVP_PKEY_NONE: buffer = "NONE"; break; + } +} +*/ + +void KviSSLCertificate::extractPubKeyInfo() +{ + EVP_PKEY *p = X509_get_pubkey(m_pX509); + if(p) + { + m_iPubKeyBits = EVP_PKEY_bits(p); + m_szPubKeyType = (p->type == NID_undef) ? __tr("Unknown") : OBJ_nid2ln(p->type); +// getPKeyType(p->type,m_szPubKeyType); + } else { + m_iPubKeyBits = 0; + m_szPubKeyType = "None"; + } + +} + +void KviSSLCertificate::extractSerialNumber() +{ + ASN1_INTEGER * i = X509_get_serialNumber(m_pX509); + if(i)m_iSerialNumber = ASN1_INTEGER_get(i); + else m_iSerialNumber = -1; +} + +void KviSSLCertificate::extractSignature() +{ + static char hexdigits[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + + //getPKeyType(X509_get_signature_type(m_pX509),m_szSignatureType); + + int i = OBJ_obj2nid(m_pX509->sig_alg->algorithm); + m_szSignatureType = (i == NID_undef) ? __tr("Unknown") : OBJ_nid2ln(i); + + m_szSignatureContents = ""; + + for(i = 0;i < m_pX509->signature->length;i++) + { + if(m_szSignatureContents.hasData())m_szSignatureContents.append(":"); + m_szSignatureContents.append(hexdigits[(m_pX509->signature->data[i] & 0xf0) >> 4]); + m_szSignatureContents.append(hexdigits[(m_pX509->signature->data[i] & 0x0f)]); + } +} + +/* +const char * KviSSLCertificate::verify() +{ + +} +*/ + +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +KviSSLCipherInfo::KviSSLCipherInfo(const SSL_CIPHER * c) +#else +KviSSLCipherInfo::KviSSLCipherInfo(SSL_CIPHER * c) +#endif +{ + m_szVersion = SSL_CIPHER_get_version(c); + 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() +{ +} + +#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 |
