From c8ea3d387510e99e20f1bd4523152ab9d8f6bc22 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 30 Oct 2014 09:21:11 +0100 Subject: [PATCH] Update Csocket to 1d7e685 > Extend EDisableProtocol > missing ifdef for non-ssl > added ability to disable compression > added new callbacks for Certificate Verification and Handshake completion > added SNI support for client and server, cleanup some of the certificate verification callback code --- include/znc/Csocket.h | 70 ++++- src/Csocket.cpp | 584 ++++++++++++++++++++++++------------------ 2 files changed, 402 insertions(+), 252 deletions(-) diff --git a/include/znc/Csocket.h b/include/znc/Csocket.h index d87d8645..670d6150 100644 --- a/include/znc/Csocket.h +++ b/include/znc/Csocket.h @@ -285,8 +285,12 @@ private: //! backwards compatible wrapper around CGetAddrInfo and gethostbyname int GetAddrInfo( const CS_STRING & sHostname, Csock * pSock, CSSockAddr & csSockAddr ); -//! used to retrieve the context position of the socket to its associated ssl connection. Setup once in InitSSL() via SSL_get_ex_new_index -int GetCsockClassIdx(); +/** + * This returns the [ex_]data index position for SSL objects only. If you want to tie more data + * to the SSL object, you should generate your own at application start so as to avoid collision + * with Csocket SSL_set_ex_data() + */ +int GetCsockSSLIdx(); #ifdef HAVE_LIBSSL //! returns the sock object associated to the particular context. returns NULL on failure or if not available @@ -606,9 +610,11 @@ public: enum EDisableProtocol { EDP_None = 0, //!< disable nothing - EDP_SSLv2 = 1, //!< disable SSL verion 2 - EDP_SSLv3 = 2, //!< disable SSL verion 3 - EDP_TLSv1 = 4, //!< disable TLS verion 1 + EDP_SSLv2 = 1, //!< disable SSL version 2 + EDP_SSLv3 = 2, //!< disable SSL version 3 + EDP_TLSv1 = 4, //!< disable TLS version 1 + EDP_TLSv1_1 = 8, //!< disable TLS version 1.1 + EDP_TLSv1_2 = 16, //!< disable TLS version 1.2 EDP_SSL = (EDP_SSLv2|EDP_SSLv3) }; @@ -851,6 +857,8 @@ public: #ifdef HAVE_LIBSSL //! bitwise setter, @see EDisableProtocol void DisableSSLProtocols( u_int uDisableOpts ) { m_uDisableProtocols = uDisableOpts; } + //! allow disabling compression + void DisableSSLCompression() { m_bNoSSLCompression = true; } //! Set the cipher type ( openssl cipher [to see ciphers available] ) void SetCipher( const CS_STRING & sCipher ); const CS_STRING & GetCipher() const; @@ -865,10 +873,11 @@ public: void SetSSLMethod( int iMethod ); int GetSSLMethod() const; - void SetSSLObject( SSL *ssl ); - void SetCTXObject( SSL_CTX *sslCtx ); + void SetSSLObject( SSL *ssl, bool bDeleteExisting = false ); + void SetCTXObject( SSL_CTX *sslCtx, bool bDeleteExisting = false ); SSL_SESSION * GetSSLSession() const; + //! setting this to NULL will allow the default openssl verification process kick in void SetCertVerifyCB( FPCertVerifyCB pFP ) { m_pCerVerifyCB = pFP; } #endif /* HAVE_LIBSSL */ @@ -888,6 +897,7 @@ public: //! Get the peer's X509 cert #ifdef HAVE_LIBSSL + //! it is up to you, the caller to call X509_free() on this object X509 *GetX509() const; //! Returns the peer's public key @@ -983,6 +993,49 @@ public: * to this ssl session via SSL_set_ex_data */ virtual void SSLFinishSetup( SSL * pSSL ) {} + /** + * @brief gets called when a SNI request is sent, and used to configure a SNI session + * @param sHostname the hostname sent from the client + * @param sPemFile fill this with the location to the pemfile + * @param sPemPass fill this with the pemfile password if there is one + * @return return true to proceed with the SNI server configuration + */ + virtual bool SNIConfigureServer( const CS_STRING & sHostname, CS_STRING & sPemFile, CS_STRING & sPemPass ) { return( false ); } + /** + * @brief called to configure the SNI client + * @param sHostname, the hostname to configure SNI with, you can fill this with GetHostname() if its a valid hostname and not an OP + * @return returning true causes a call to configure SNI with the hostname returned + */ + virtual bool SNIConfigureClient( CS_STRING & sHostname ) { return( false ); } + //! creates a new SSL_CTX based on the setup of this sock + SSL_CTX * SetupServerCTX(); + + /** + * @brief called once the SSL handshake is complete, this is triggered via SSL_CB_HANDSHAKE_DONE in SSL_set_info_callback() + * + * This is a spot where you can look at the finished peer certifificate ... IE + *
+	 * X509 * pCert = GetX509();
+	 * char szName[256];
+	 * memset( szName, '\0', 256 );
+	 * X509_NAME_get_text_by_NID ( X509_get_subject_name( pCert ), NID_commonName, szName, 255 );
+	 * cerr << "Name! " << szName << endl;
+	 * X509_free( pCert );
+	 * 
+ */ + virtual void SSLHandShakeFinished() {} + /** + * @brief this is hooked in via SSL_set_verify, and be default it just returns 1 meaning success + * @param iPreVerify the pre-verification status as determined by openssl internally + * @param pStoreCTX the X509_STORE_CTX containing the certificate + * @return 1 to continue, 0 to abort + * + * This may get called multiple times, for example with a chain certificate which is fairly typical with + * certificates from godaddy, freessl, etc. Additionally, openssl does not do any host verification, they + * leave that up to the you. One easy way to deal with this is to wait for SSLHandShakeFinished() and examine + * the peer certificate @see SSLHandShakeFinished + */ + virtual int VerifyPeerCertificate( int iPreVerify, X509_STORE_CTX * pStoreCTX ) { return( 1 ); } #endif /* HAVE_LIBSSL */ @@ -1090,7 +1143,6 @@ private: void ShrinkSendBuff(); void IncBuffPos( size_t uBytes ); //! checks for configured protocol disabling - void CheckDisabledProtocols(); // NOTE! if you add any new members, be sure to add them to Copy() uint16_t m_uPort; @@ -1120,11 +1172,13 @@ private: SSL_CTX * m_ssl_ctx; uint32_t m_iRequireClientCertFlags; u_int m_uDisableProtocols; + bool m_bNoSSLCompression; FPCertVerifyCB m_pCerVerifyCB; void FREE_SSL(); void FREE_CTX(); + void ConfigureCTXOptions( SSL_CTX * pCTX ); #endif /* HAVE_LIBSSL */ diff --git a/src/Csocket.cpp b/src/Csocket.cpp index 6443ca87..c483ebf9 100644 --- a/src/Csocket.cpp +++ b/src/Csocket.cpp @@ -70,10 +70,10 @@ namespace Csocket { #endif /* _NO_CSOCKET_NS */ -static int g_iCsockSSLIdx = 0; //!< this get setup once in InitSSL -int GetCsockClassIdx() +static int s_iCsockSSLIdx = 0; //!< this gets setup once in InitSSL +int GetCsockSSLIdx() { - return( g_iCsockSSLIdx ); + return( s_iCsockSSLIdx ); } #ifdef _WIN32 @@ -203,23 +203,30 @@ static int _PemPassCB( char *pBuff, int iBuffLen, int rwflag, void * pcSocket ) static int _CertVerifyCB( int preverify_ok, X509_STORE_CTX *x509_ctx ) { - /* - * A small quick example on how to get ahold of the Csock in the data portion of x509_ctx Csock * pSock = GetCsockFromCTX( x509_ctx ); - assert( pSock ); - cerr << pSock->GetRemoteIP() << endl; - */ + if( pSock ) + return( pSock->VerifyPeerCertificate( preverify_ok, x509_ctx ) ); - /* return 1 always for now, probably want to add some code for cert verification */ - return( 1 ); + return( preverify_ok ); } +static void _InfoCallback( const SSL * pSSL, int where, int ret ) +{ + if( ( where & SSL_CB_HANDSHAKE_DONE ) && ret != 0 ) + { + Csock * pSock = static_cast( SSL_get_ex_data( pSSL, GetCsockSSLIdx() ) ); + if( pSock ) + pSock->SSLHandShakeFinished(); + } +} + + Csock * GetCsockFromCTX( X509_STORE_CTX * pCTX ) { Csock * pSock = NULL; SSL * pSSL = ( SSL * ) X509_STORE_CTX_get_ex_data( pCTX, SSL_get_ex_data_X509_STORE_CTX_idx() ); if( pSSL ) - pSock = ( Csock * ) SSL_get_ex_data( pSSL, GetCsockClassIdx() ); + pSock = ( Csock * ) SSL_get_ex_data( pSSL, GetCsockSSLIdx() ); return( pSock ); } #endif /* HAVE_LIBSSL */ @@ -581,7 +588,7 @@ bool InitSSL( ECompType eCompressionType ) } // setting this up once in the begining - g_iCsockSSLIdx = SSL_get_ex_new_index( 0, ( void * )"CsockGlobalIndex", NULL, NULL, NULL ); + s_iCsockSSLIdx = SSL_get_ex_new_index( 0, NULL, NULL, NULL, NULL ); return( true ); } @@ -895,7 +902,7 @@ void CSockCommon::DelCronByAddr( CCron * pcCron ) Csock::Csock( int iTimeout ) : CSockCommon() { #ifdef HAVE_LIBSSL - m_pCerVerifyCB = NULL; + m_pCerVerifyCB = _CertVerifyCB; #endif /* HAVE_LIBSSL */ Init( "", 0, iTimeout ); } @@ -903,7 +910,7 @@ Csock::Csock( int iTimeout ) : CSockCommon() Csock::Csock( const CS_STRING & sHostname, uint16_t iport, int iTimeout ) : CSockCommon() { #ifdef HAVE_LIBSSL - m_pCerVerifyCB = NULL; + m_pCerVerifyCB = _CertVerifyCB; #endif /* HAVE_LIBSSL */ Init( sHostname, iport, iTimeout ); } @@ -1032,6 +1039,8 @@ void Csock::Copy( const Csock & cCopy ) #endif /* HAVE_C_ARES */ #ifdef HAVE_LIBSSL + m_bNoSSLCompression = cCopy.m_bNoSSLCompression; + m_uDisableProtocols = cCopy.m_uDisableProtocols; m_iRequireClientCertFlags = cCopy.m_iRequireClientCertFlags; m_sSSLBuffer = cCopy.m_sSSLBuffer; @@ -1315,6 +1324,32 @@ cs_sock_t Csock::Accept( CS_STRING & sHost, uint16_t & iRPort ) return( iSock ); } +#ifdef HAVE_LIBSSL +static int __SNICallBack( SSL *pSSL, int *piAD, void *pData ) +{ + if( !pSSL || !pData ) + return( SSL_TLSEXT_ERR_NOACK ); + + const char * pServerName = SSL_get_servername( pSSL, TLSEXT_NAMETYPE_host_name ); + if( !pServerName ) + return( SSL_TLSEXT_ERR_NOACK ); + + Csock * pSock = static_cast( pData ); + + CS_STRING sPemFile, sPemPass; + if( !pSock->SNIConfigureServer( pServerName, sPemFile, sPemPass ) ) + return( SSL_TLSEXT_ERR_NOACK ); + + pSock->SetPemLocation( sPemFile ); + pSock->SetPemPass( sPemPass ); + SSL_CTX * pCTX = pSock->SetupServerCTX(); + SSL_set_SSL_CTX( pSSL, pCTX ); + pSock->SetCTXObject( pCTX, true ); + return( SSL_TLSEXT_ERR_OK ); +} + +#endif /* HAVE_LIBSSL */ + bool Csock::AcceptSSL() { #ifdef HAVE_LIBSSL @@ -1322,6 +1357,11 @@ bool Csock::AcceptSSL() if( !SSLServerSetup() ) return( false ); +#if defined( SSL_CTX_set_tlsext_servername_callback ) + SSL_CTX_set_tlsext_servername_callback( m_ssl_ctx, __SNICallBack ); + SSL_CTX_set_tlsext_servername_arg( m_ssl_ctx, this ); +#endif /* SSL_CTX_set_tlsext_servername_callback */ + int err = SSL_accept( m_ssl ); if( err == 1 ) @@ -1341,23 +1381,42 @@ bool Csock::AcceptSSL() return( false ); } -void Csock::CheckDisabledProtocols() -{ #ifdef HAVE_LIBSSL - if( m_ssl_ctx && m_uDisableProtocols > 0 ) +void Csock::ConfigureCTXOptions( SSL_CTX * pCTX ) +{ + if( pCTX ) { long uCTXOptions = 0; - if( EDP_SSLv2 & m_uDisableProtocols ) - uCTXOptions |= SSL_OP_NO_SSLv2; - if( EDP_SSLv3 & m_uDisableProtocols ) - uCTXOptions |= SSL_OP_NO_SSLv3; - if( EDP_TLSv1 & m_uDisableProtocols ) - uCTXOptions |= SSL_OP_NO_TLSv1; + if( m_uDisableProtocols > 0 ) + { +#ifdef SSL_OP_NO_SSLv2 + if( EDP_SSLv2 & m_uDisableProtocols ) + uCTXOptions |= SSL_OP_NO_SSLv2; +#endif /* SSL_OP_NO_SSLv2 */ +#ifdef SSL_OP_NO_SSLv3 + if( EDP_SSLv3 & m_uDisableProtocols ) + uCTXOptions |= SSL_OP_NO_SSLv3; +#endif /* SSL_OP_NO_SSLv3 */ +#ifdef SSL_OP_NO_TLSv1 + if( EDP_TLSv1 & m_uDisableProtocols ) + uCTXOptions |= SSL_OP_NO_TLSv1; +#endif /* SSL_OP_NO_TLSv1 */ +#ifdef SSL_OP_NO_TLSv1_1 + if( EDP_TLSv1_1 & m_uDisableProtocols ) + uCTXOptions |= SSL_OP_NO_TLSv1_1; +#endif /* SSL_OP_NO_TLSv1 */ +#ifdef SSL_OP_NO_TLSv1_2 + if( EDP_TLSv1_2 & m_uDisableProtocols ) + uCTXOptions |= SSL_OP_NO_TLSv1_2; +#endif /* SSL_OP_NO_TLSv1_2 */ + } + if( m_bNoSSLCompression ) + uCTXOptions |= SSL_OP_NO_COMPRESSION; if( uCTXOptions ) - SSL_CTX_set_options( m_ssl_ctx, uCTXOptions ); + SSL_CTX_set_options( pCTX, uCTXOptions ); } -#endif /* HAVE_LIBSSL */ } +#endif /* HAVE_LIBSSL */ bool Csock::SSLClientSetup() { @@ -1462,7 +1521,7 @@ bool Csock::SSLClientSetup() } } - CheckDisabledProtocols(); + ConfigureCTXOptions( m_ssl_ctx ); m_ssl = SSL_new( m_ssl_ctx ); if( !m_ssl ) @@ -1470,8 +1529,15 @@ bool Csock::SSLClientSetup() SSL_set_rfd( m_ssl, ( int )m_iReadSock ); SSL_set_wfd( m_ssl, ( int )m_iWriteSock ); - SSL_set_verify( m_ssl, SSL_VERIFY_PEER, ( m_pCerVerifyCB ? m_pCerVerifyCB : _CertVerifyCB ) ); - SSL_set_ex_data( m_ssl, GetCsockClassIdx(), this ); + SSL_set_verify( m_ssl, SSL_VERIFY_PEER, m_pCerVerifyCB ); + SSL_set_info_callback( m_ssl, _InfoCallback ); + SSL_set_ex_data( m_ssl, GetCsockSSLIdx(), this ); + +#if defined( SSL_set_tlsext_host_name ) + CS_STRING sSNIHostname; + if( SNIConfigureClient( sSNIHostname ) ) + SSL_set_tlsext_host_name( m_ssl, sSNIHostname.c_str() ); +#endif /* SSL_set_tlsext_host_name */ SSLFinishSetup( m_ssl ); return( true ); @@ -1481,6 +1547,171 @@ bool Csock::SSLClientSetup() #endif /* HAVE_LIBSSL */ } +#ifdef HAVE_LIBSSL +SSL_CTX * Csock::SetupServerCTX() +{ + SSL_CTX * pCTX = NULL; + switch( m_iMethod ) + { + case SSL3: + pCTX = SSL_CTX_new( SSLv3_server_method() ); + if( !pCTX ) + { + CS_DEBUG( "WARNING: MakeConnection .... SSLv3_server_method failed!" ); + return( NULL ); + } + break; + case TLS12: +#ifdef TLS1_2_VERSION + pCTX = SSL_CTX_new( TLSv1_2_server_method() ); + if( !pCTX ) + { + CS_DEBUG( "WARNING: MakeConnection .... TLSv1_2_server_method failed!" ); + return( NULL ); + } + break; +#endif /* TLS1_2_VERSION */ + case TLS11: +#ifdef TLS1_1_VERSION + pCTX = SSL_CTX_new( TLSv1_1_server_method() ); + if( !pCTX ) + { + CS_DEBUG( "WARNING: MakeConnection .... TLSv1_1_server_method failed!" ); + return( NULL ); + } + break; + case TLS1: +#endif /* TLS1_1_VERSION */ + pCTX = SSL_CTX_new( TLSv1_server_method() ); + if( !pCTX ) + { + CS_DEBUG( "WARNING: MakeConnection .... TLSv1_server_method failed!" ); + return( NULL ); + } + break; + case SSL2: +#ifndef OPENSSL_NO_SSL2 + pCTX = SSL_CTX_new( SSLv2_server_method() ); + if( !pCTX ) + { + CS_DEBUG( "WARNING: MakeConnection .... SSLv2_server_method failed!" ); + return( NULL ); + } + break; +#endif /* OPENSSL_NO_SSL2 */ + /* Fall through if SSL2 is disabled */ + case SSL23: + default: + if( m_iMethod != SSL23 ) + { + CS_DEBUG( "WARNING: SSL Server Method other than SSLv23 specified, but has passed through" ); + } + pCTX = SSL_CTX_new( SSLv23_server_method() ); + if( !pCTX ) + { + CS_DEBUG( "WARNING: MakeConnection .... SSLv23_server_method failed!" ); + return( NULL ); + } + break; + } + if( !pCTX ) + { + CS_DEBUG( "ERROR: NULL Ptr where there shouldn't be" ); + return( NULL ); + } + + SSL_CTX_set_default_verify_paths( pCTX ); + + // set the pemfile password + SSL_CTX_set_default_passwd_cb( pCTX, _PemPassCB ); + SSL_CTX_set_default_passwd_cb_userdata( pCTX, ( void * )this ); + + if( m_sPemFile.empty() || access( m_sPemFile.c_str(), R_OK ) != 0 ) + { + CS_DEBUG( "Empty, missing, or bad pemfile ... [" << m_sPemFile << "]" ); + SSL_CTX_free( pCTX ); + return( NULL ); + } + + // + // set up the CTX + if( SSL_CTX_use_certificate_chain_file( pCTX, m_sPemFile.c_str() ) <= 0 ) + { + CS_DEBUG( "Error with PEM file [" << m_sPemFile << "]" ); + SSLErrors( __FILE__, __LINE__ ); + SSL_CTX_free( pCTX ); + return( NULL ); + } + + if( SSL_CTX_use_PrivateKey_file( pCTX, m_sPemFile.c_str(), SSL_FILETYPE_PEM ) <= 0 ) + { + CS_DEBUG( "Error with PEM file [" << m_sPemFile << "]" ); + SSLErrors( __FILE__, __LINE__ ); + SSL_CTX_free( pCTX ); + return( NULL ); + } + + // check to see if this pem file contains a DH structure for use with DH key exchange + // https://github.com/znc/znc/pull/46 + FILE *dhParamsFile = fopen( m_sPemFile.c_str(), "r" ); + if( !dhParamsFile ) + { + CS_DEBUG( "There is a problem with [" << m_sPemFile << "]" ); + SSL_CTX_free( pCTX ); + return( NULL ); + } + + DH * dhParams = PEM_read_DHparams( dhParamsFile, NULL, NULL, NULL ); + fclose( dhParamsFile ); + if( dhParams ) + { + SSL_CTX_set_options( pCTX, SSL_OP_SINGLE_DH_USE ); + if( !SSL_CTX_set_tmp_dh( pCTX, dhParams ) ) + { + CS_DEBUG( "Error setting ephemeral DH parameters from [" << m_sPemFile << "]" ); + SSLErrors( __FILE__, __LINE__ ); + DH_free( dhParams ); + SSL_CTX_free( pCTX ); + return( NULL ); + } + DH_free( dhParams ); + } + else + { + // Presumably PEM_read_DHparams failed, as there was no DH structure. Clearing those errors here so they are removed off the stack + ERR_clear_error(); + } + + // Errors for the following block are non-fatal (ECDHE is nice to have + // but not a requirement) +#if defined( SSL_CTX_set_ecdh_auto ) + // Auto-select sensible curve + if( !SSL_CTX_set_ecdh_auto( pCTX , 1 ) ) + ERR_clear_error(); +#elif defined( SSL_CTX_set_tmp_ecdh ) + // Use a standard, widely-supported curve + EC_KEY * ecdh = EC_KEY_new_by_curve_name( NID_X9_62_prime256v1 ); + if( ecdh ) + { + if( !SSL_CTX_set_tmp_ecdh( pCTX, ecdh ) ) + ERR_clear_error(); + EC_KEY_free( ecdh ); + } + else + ERR_clear_error(); +#endif + + if( SSL_CTX_set_cipher_list( pCTX, m_sCipherType.c_str() ) <= 0 ) + { + CS_DEBUG( "Could not assign cipher [" << m_sCipherType << "]" ); + SSL_CTX_free( pCTX ); + return( NULL ); + } + ConfigureCTXOptions( pCTX ); + return( pCTX ); +} +#endif /* HAVE_LIBSSL */ + bool Csock::SSLServerSetup() { #ifdef HAVE_LIBSSL @@ -1497,154 +1728,7 @@ bool Csock::SSLServerSetup() } #endif /* _WIN64 */ - - switch( m_iMethod ) - { - case SSL3: - m_ssl_ctx = SSL_CTX_new( SSLv3_server_method() ); - if( !m_ssl_ctx ) - { - CS_DEBUG( "WARNING: MakeConnection .... SSLv3_server_method failed!" ); - return( false ); - } - break; - case TLS12: -#ifdef TLS1_2_VERSION - m_ssl_ctx = SSL_CTX_new( TLSv1_2_server_method() ); - if( !m_ssl_ctx ) - { - CS_DEBUG( "WARNING: MakeConnection .... TLSv1_2_server_method failed!" ); - return( false ); - } - break; -#endif /* TLS1_2_VERSION */ - case TLS11: -#ifdef TLS1_1_VERSION - m_ssl_ctx = SSL_CTX_new( TLSv1_1_server_method() ); - if( !m_ssl_ctx ) - { - CS_DEBUG( "WARNING: MakeConnection .... TLSv1_1_server_method failed!" ); - return( false ); - } - break; - case TLS1: -#endif /* TLS1_1_VERSION */ - m_ssl_ctx = SSL_CTX_new( TLSv1_server_method() ); - if( !m_ssl_ctx ) - { - CS_DEBUG( "WARNING: MakeConnection .... TLSv1_server_method failed!" ); - return( false ); - } - break; - case SSL2: -#ifndef OPENSSL_NO_SSL2 - m_ssl_ctx = SSL_CTX_new( SSLv2_server_method() ); - if( !m_ssl_ctx ) - { - CS_DEBUG( "WARNING: MakeConnection .... SSLv2_server_method failed!" ); - return( false ); - } - break; -#endif /* OPENSSL_NO_SSL2 */ - /* Fall through if SSL2 is disabled */ - case SSL23: - default: - if( m_iMethod != SSL23 ) - { - CS_DEBUG( "WARNING: SSL Server Method other than SSLv23 specified, but has passed through" ); - } - m_ssl_ctx = SSL_CTX_new( SSLv23_server_method() ); - if( !m_ssl_ctx ) - { - CS_DEBUG( "WARNING: MakeConnection .... SSLv23_server_method failed!" ); - return( false ); - } - break; - } - - SSL_CTX_set_default_verify_paths( m_ssl_ctx ); - - // set the pemfile password - SSL_CTX_set_default_passwd_cb( m_ssl_ctx, _PemPassCB ); - SSL_CTX_set_default_passwd_cb_userdata( m_ssl_ctx, ( void * )this ); - - if( m_sPemFile.empty() || access( m_sPemFile.c_str(), R_OK ) != 0 ) - { - CS_DEBUG( "There is a problem with [" << m_sPemFile << "]" ); - return( false ); - } - - // - // set up the CTX - if( SSL_CTX_use_certificate_chain_file( m_ssl_ctx, m_sPemFile.c_str() ) <= 0 ) - { - CS_DEBUG( "Error with PEM file [" << m_sPemFile << "]" ); - SSLErrors( __FILE__, __LINE__ ); - return( false ); - } - - if( SSL_CTX_use_PrivateKey_file( m_ssl_ctx, m_sPemFile.c_str(), SSL_FILETYPE_PEM ) <= 0 ) - { - CS_DEBUG( "Error with PEM file [" << m_sPemFile << "]" ); - SSLErrors( __FILE__, __LINE__ ); - return( false ); - } - - // check to see if this pem file contains a DH structure for use with DH key exchange - // https://github.com/znc/znc/pull/46 - FILE *dhParamsFile = fopen( m_sPemFile.c_str(), "r" ); - if( !dhParamsFile ) - { - CS_DEBUG( "There is a problem with [" << m_sPemFile << "]" ); - return( false ); - } - - DH * dhParams = PEM_read_DHparams( dhParamsFile, NULL, NULL, NULL ); - fclose( dhParamsFile ); - if( dhParams ) - { - SSL_CTX_set_options( m_ssl_ctx, SSL_OP_SINGLE_DH_USE ); - if( !SSL_CTX_set_tmp_dh( m_ssl_ctx, dhParams ) ) - { - CS_DEBUG( "Error setting ephemeral DH parameters from [" << m_sPemFile << "]" ); - SSLErrors( __FILE__, __LINE__ ); - DH_free( dhParams ); - return( false ); - } - DH_free( dhParams ); - } - else - { - // Presumably PEM_read_DHparams failed, as there was no DH structure. Clearing those errors here so they are removed off the stack - ERR_clear_error(); - } - - // Errors for the following block are non-fatal (ECDHE is nice to have - // but not a requirement) -#if defined( SSL_CTX_set_ecdh_auto ) - // Auto-select sensible curve - if( !SSL_CTX_set_ecdh_auto( m_ssl_ctx , 1 ) ) - ERR_clear_error(); -#elif defined( SSL_CTX_set_tmp_ecdh ) - // Use a standard, widely-supported curve - EC_KEY * ecdh = EC_KEY_new_by_curve_name( NID_X9_62_prime256v1 ); - if( ecdh ) - { - if( !SSL_CTX_set_tmp_ecdh( m_ssl_ctx, ecdh ) ) - ERR_clear_error(); - EC_KEY_free( ecdh ); - } - else - ERR_clear_error(); -#endif - - if( SSL_CTX_set_cipher_list( m_ssl_ctx, m_sCipherType.c_str() ) <= 0 ) - { - CS_DEBUG( "Could not assign cipher [" << m_sCipherType << "]" ); - return( false ); - } - - CheckDisabledProtocols(); + m_ssl_ctx = SetupServerCTX(); // // setup the SSL @@ -1662,9 +1746,10 @@ bool Csock::SSLServerSetup() SSL_set_accept_state( m_ssl ); if( m_iRequireClientCertFlags ) { - SSL_set_verify( m_ssl, m_iRequireClientCertFlags, ( m_pCerVerifyCB ? m_pCerVerifyCB : _CertVerifyCB ) ); - SSL_set_ex_data( m_ssl, GetCsockClassIdx(), this ); + SSL_set_verify( m_ssl, m_iRequireClientCertFlags, m_pCerVerifyCB ); } + SSL_set_info_callback( m_ssl, _InfoCallback ); + SSL_set_ex_data( m_ssl, GetCsockSSLIdx(), this ); SSLFinishSetup( m_ssl ); return( true ); @@ -1878,30 +1963,30 @@ bool Csock::Write( const char *data, size_t len ) switch( SSL_get_error( m_ssl, iErr ) ) { - case SSL_ERROR_NONE: - m_bsslEstablished = true; - // all ok - break; + case SSL_ERROR_NONE: + m_bsslEstablished = true; + // all ok + break; - case SSL_ERROR_ZERO_RETURN: - { - // weird closer alert - return( false ); - } + case SSL_ERROR_ZERO_RETURN: + { + // weird closer alert + return( false ); + } - case SSL_ERROR_WANT_READ: - // retry - break; + case SSL_ERROR_WANT_READ: + // retry + break; - case SSL_ERROR_WANT_WRITE: - // retry - break; + case SSL_ERROR_WANT_WRITE: + // retry + break; - case SSL_ERROR_SSL: - { - SSLErrors( __FILE__, __LINE__ ); - return( false ); - } + case SSL_ERROR_SSL: + { + SSLErrors( __FILE__, __LINE__ ); + return( false ); + } } if( iErr > 0 ) @@ -2320,8 +2405,18 @@ const CS_STRING & Csock::GetPemPass() const { return( m_sPemPass ); } void Csock::SetSSLMethod( int iMethod ) { m_iMethod = iMethod; } int Csock::GetSSLMethod() const { return( m_iMethod ); } -void Csock::SetSSLObject( SSL *ssl ) { m_ssl = ssl; } -void Csock::SetCTXObject( SSL_CTX *sslCtx ) { m_ssl_ctx = sslCtx; } +void Csock::SetSSLObject( SSL *ssl, bool bDeleteExisting ) +{ + if( bDeleteExisting ) + FREE_SSL(); + m_ssl = ssl; +} +void Csock::SetCTXObject( SSL_CTX *sslCtx, bool bDeleteExisting ) +{ + if( bDeleteExisting ) + FREE_CTX(); + m_ssl_ctx = sslCtx; +} SSL_SESSION * Csock::GetSSLSession() const { @@ -2807,6 +2902,7 @@ void Csock::Init( const CS_STRING & sHostname, uint16_t uPort, int iTimeout ) m_ssl_ctx = NULL; m_iRequireClientCertFlags = 0; m_uDisableProtocols = 0; + m_bNoSSLCompression = false; #endif /* HAVE_LIBSSL */ m_iTcount = 0; m_iReadSock = CS_INVALID_SOCK; @@ -3109,58 +3205,58 @@ void CSocketManager::Loop() switch( bytes ) { - case Csock::READ_EOF: - { - DelSockByAddr( pcSock ); - break; - } - - case Csock::READ_ERR: - { - bool bHandled = false; -#ifdef HAVE_LIBSSL - if( pcSock->GetSSL() ) + case Csock::READ_EOF: { - unsigned long iSSLError = ERR_peek_error(); - if( iSSLError ) - { - char szError[512]; - memset( ( char * ) szError, '\0', 512 ); - ERR_error_string_n( iSSLError, szError, 511 ); - SSLErrors( __FILE__, __LINE__ ); - pcSock->CallSockError( GetSockError(), szError ); - bHandled = true; - } + DelSockByAddr( pcSock ); + break; } + + case Csock::READ_ERR: + { + bool bHandled = false; +#ifdef HAVE_LIBSSL + if( pcSock->GetSSL() ) + { + unsigned long iSSLError = ERR_peek_error(); + if( iSSLError ) + { + char szError[512]; + memset( ( char * ) szError, '\0', 512 ); + ERR_error_string_n( iSSLError, szError, 511 ); + SSLErrors( __FILE__, __LINE__ ); + pcSock->CallSockError( GetSockError(), szError ); + bHandled = true; + } + } #endif - if( !bHandled ) - pcSock->CallSockError( GetSockError() ); - DelSockByAddr( pcSock ); - break; - } + if( !bHandled ) + pcSock->CallSockError( GetSockError() ); + DelSockByAddr( pcSock ); + break; + } - case Csock::READ_EAGAIN: - break; + case Csock::READ_EAGAIN: + break; - case Csock::READ_CONNREFUSED: - pcSock->ConnectionRefused(); - DelSockByAddr( pcSock ); - break; + case Csock::READ_CONNREFUSED: + pcSock->ConnectionRefused(); + DelSockByAddr( pcSock ); + break; - case Csock::READ_TIMEDOUT: - pcSock->Timeout(); - DelSockByAddr( pcSock ); - break; + case Csock::READ_TIMEDOUT: + pcSock->Timeout(); + DelSockByAddr( pcSock ); + break; - default: - { - if( Csock::TMO_READ & pcSock->GetTimeoutType() ) - pcSock->ResetTimer(); // reset the timeout timer + default: + { + if( Csock::TMO_READ & pcSock->GetTimeoutType() ) + pcSock->ResetTimer(); // reset the timeout timer - pcSock->ReadData( cBuff(), bytes ); // Call ReadData() before PushBuff() so that it is called before the ReadLine() event - LD 07/18/05 - pcSock->PushBuff( cBuff(), bytes ); - break; - } + pcSock->ReadData( cBuff(), bytes ); // Call ReadData() before PushBuff() so that it is called before the ReadLine() event - LD 07/18/05 + pcSock->PushBuff( cBuff(), bytes ); + break; + } } } else if( iErrno == SELECT_ERROR )