# Patch to support disabling ciphers.
# Closes: #766957

diff -Naur znc-1.4.orig/include/znc/Csocket.h znc-1.4/include/znc/Csocket.h
--- znc-1.4.orig/include/znc/Csocket.h	2014-05-08 22:21:25.000000000 +0200
+++ znc-1.4/include/znc/Csocket.h	2014-12-18 20:22:03.882667166 +0100
@@ -585,6 +585,15 @@
 		TLS1				= 4
 	};
 
+	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_SSL			= (EDP_SSLv2|EDP_SSLv3)
+	};
+
 	enum ECONState
 	{
 		CST_START		= 0,
@@ -815,6 +824,8 @@
 	void SetSSL( bool b );
 
 #ifdef HAVE_LIBSSL
+	//! bitwise setter, @see EDisableProtocol
+	void DisableSSLProtocols( u_int uDisableOpts ) { m_uDisableProtocols = uDisableOpts; }
 	//! Set the cipher type ( openssl cipher [to see ciphers available] )
 	void SetCipher( const CS_STRING & sCipher );
 	const CS_STRING & GetCipher();
@@ -1083,6 +1094,9 @@
 	//! making private for safety
 	Csock( const Csock & cCopy ) : CSockCommon() {}
 
+	//! 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, m_iRemotePort, m_iLocalPort;
 	cs_sock_t	m_iReadSock, m_iWriteSock;
@@ -1106,6 +1120,7 @@
 	SSL	*		m_ssl;
 	SSL_CTX	*	m_ssl_ctx;
 	uint32_t	m_iRequireClientCertFlags;
+	u_int		m_uDisableProtocols;
 
 	FPCertVerifyCB		m_pCerVerifyCB;
 
diff -Naur znc-1.4.orig/include/znc/Socket.h znc-1.4/include/znc/Socket.h
--- znc-1.4.orig/include/znc/Socket.h	2014-05-08 22:21:25.000000000 +0200
+++ znc-1.4/include/znc/Socket.h	2014-12-18 20:22:03.882667166 +0100
@@ -25,8 +25,8 @@
 
 class CZNCSock : public Csock {
 public:
-	CZNCSock(int timeout = 60) : Csock(timeout) {}
-	CZNCSock(const CString& sHost, u_short port, int timeout = 60) : Csock(sHost, port, timeout) {}
+	CZNCSock(int timeout = 60);
+	CZNCSock(const CString& sHost, u_short port, int timeout = 60);
 	~CZNCSock() {}
 
 	virtual int ConvertAddress(const struct sockaddr_storage * pAddr, socklen_t iAddrLen, CS_STRING & sIP, u_short * piPort);
diff -Naur znc-1.4.orig/include/znc/znc.h znc-1.4/include/znc/znc.h
--- znc-1.4.orig/include/znc/znc.h	2014-05-08 22:21:25.000000000 +0200
+++ znc-1.4/include/znc/znc.h	2014-12-18 20:22:03.882667166 +0100
@@ -124,6 +124,7 @@
 	unsigned int GetServerThrottle() const { return m_sConnectThrottle.GetTTL() / 1000; }
 	unsigned int GetConnectDelay() const { return m_uiConnectDelay; }
 	bool GetProtectWebSessions() const { return m_bProtectWebSessions; }
+	CString GetSSLCiphers() const { return m_sSSLCiphers; }
 	// !Getters
 
 	// Static allocator
@@ -203,6 +204,7 @@
 	CString                m_sStatusPrefix;
 	CString                m_sPidFile;
 	CString                m_sSSLCertFile;
+	CString                m_sSSLCiphers;
 	VCString               m_vsBindHosts;
 	VCString               m_vsMotd;
 	CFile*                 m_pLockFile;
diff -Naur znc-1.4.orig/src/Csocket.cpp znc-1.4/src/Csocket.cpp
--- znc-1.4.orig/src/Csocket.cpp	2014-05-08 22:21:25.000000000 +0200
+++ znc-1.4/src/Csocket.cpp	2014-12-18 20:22:03.882667166 +0100
@@ -1321,6 +1321,24 @@
 	return( false );
 }
 
+void Csock::CheckDisabledProtocols()
+{
+#ifdef HAVE_LIBSSL
+	if( m_ssl_ctx && m_uDisableProtocols > 0 )
+	{
+		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( uCTXOptions )
+			SSL_CTX_set_options( m_ssl_ctx, uCTXOptions );
+	}
+#endif /* HAVE_LIBSSL */
+}
+
 bool Csock::SSLClientSetup()
 {
 #ifdef HAVE_LIBSSL
@@ -1399,6 +1417,8 @@
 		}
 	}
 
+	CheckDisabledProtocols();
+
 	m_ssl = SSL_new( m_ssl_ctx );
 	if( !m_ssl )
 		return( false );
@@ -1530,18 +1550,43 @@
 		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();
+
 	//
 	// setup the SSL
 	m_ssl = SSL_new( m_ssl_ctx );
 	if( !m_ssl )
 		return( false );
 
+#if defined( SSL_MODE_SEND_FALLBACK_SCSV )
+    SSL_set_mode( m_ssl, SSL_MODE_SEND_FALLBACK_SCSV );
+#endif /* SSL_MODE_SEND_FALLBACK_SCSV */
+
 	// Call for client Verification
 	SSL_set_rfd( m_ssl, ( int )m_iReadSock );
 	SSL_set_wfd( m_ssl, ( int )m_iWriteSock );
@@ -2560,6 +2605,7 @@
 	m_ssl = NULL;
 	m_ssl_ctx = NULL;
 	m_iRequireClientCertFlags = 0;
+	m_uDisableProtocols = 0;
 #endif /* HAVE_LIBSSL */
 	m_iTcount = 0;
 	m_iReadSock = CS_INVALID_SOCK;
diff -Naur znc-1.4.orig/src/Socket.cpp znc-1.4/src/Socket.cpp
--- znc-1.4.orig/src/Socket.cpp	2014-05-08 22:21:25.000000000 +0200
+++ znc-1.4/src/Socket.cpp	2014-12-18 20:22:03.882667166 +0100
@@ -16,8 +16,29 @@
 
 #include <znc/User.h>
 #include <znc/IRCNetwork.h>
+#include <znc/znc.h>
 #include <signal.h>
 
+CZNCSock::CZNCSock(int timeout) : Csock(timeout) {
+	DisableSSLProtocols(EDP_SSL);
+#ifdef HAVE_LIBSSL
+	CString sCipher = CZNC::Get().GetSSLCiphers();
+	if (!sCipher.empty()) {
+		SetCipher(sCipher);
+	}
+#endif
+}
+
+CZNCSock::CZNCSock(const CString& sHost, u_short port, int timeout) : Csock(sHost, port, timeout) {
+	DisableSSLProtocols(EDP_SSL);
+#ifdef HAVE_LIBSSL
+	CString sCipher = CZNC::Get().GetSSLCiphers();
+	if (!sCipher.empty()) {
+		SetCipher(sCipher);
+	}
+#endif
+}
+
 unsigned int CSockManager::GetAnonConnectionCount(const CString &sIP) const {
 	const_iterator it;
 	unsigned int ret = 0;
diff -Naur znc-1.4.orig/src/znc.cpp znc-1.4/src/znc.cpp
--- znc-1.4.orig/src/znc.cpp	2014-05-08 22:21:25.000000000 +0200
+++ znc-1.4/src/znc.cpp	2014-12-18 20:22:03.886667144 +0100
@@ -472,6 +472,10 @@
 		config.AddKeyValuePair("StatusPrefix", m_sStatusPrefix.FirstLine());
 	}
 
+	if (!m_sSSLCiphers.empty()) {
+		config.AddKeyValuePair("SSLCiphers", CString(m_sSSLCiphers));
+	}
+
 	for (unsigned int m = 0; m < m_vsMotd.size(); m++) {
 		config.AddKeyValuePair("Motd", m_vsMotd[m].FirstLine());
 	}
@@ -1216,6 +1220,8 @@
 		m_sStatusPrefix = sVal;
 	if (config.FindStringEntry("sslcertfile", sVal))
 		m_sSSLCertFile = sVal;
+	if (config.FindStringEntry("sslciphers", sVal))
+		m_sSSLCiphers = sVal;
 	if (config.FindStringEntry("skin", sVal))
 		SetSkinName(sVal);
 	if (config.FindStringEntry("connectdelay", sVal))
