From c0e71184be433a2fcd8c5849e30e1e158aea6391 Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Sun, 22 Dec 2019 23:01:49 +0000 Subject: [PATCH] Backport SSL changes from asio branch (#1639). This shouldn't change any behavior. --- include/znc/IRCSock.h | 4 +++ include/znc/Socket.h | 6 +++- src/IRCSock.cpp | 64 +++++++++++++++++++++---------------------- src/Socket.cpp | 44 +++++++++++++++++------------ 4 files changed, 66 insertions(+), 52 deletions(-) diff --git a/include/znc/IRCSock.h b/include/znc/IRCSock.h index 1ea1236b..75d087f9 100644 --- a/include/znc/IRCSock.h +++ b/include/znc/IRCSock.h @@ -56,6 +56,9 @@ class CIRCSock : public CIRCSocket { void SockError(int iErrno, const CString& sDescription) override; void Timeout() override; void ReachedMaxBuffer() override; +#ifdef HAVE_LIBSSL + void SSLCertError(X509* pCert) override; +#endif /** Sends a raw data line to the server. * @param sLine The line to be sent. * @@ -229,6 +232,7 @@ class CIRCSock : public CIRCSocket { double m_fFloodRate; bool m_bFloodProtection; SCString m_ssSupportedTags; + VCString m_vsSSLError; friend class CIRCFloodTimer; }; diff --git a/include/znc/Socket.h b/include/znc/Socket.h index 68f4dc0c..359a7023 100644 --- a/include/znc/Socket.h +++ b/include/znc/Socket.h @@ -36,12 +36,16 @@ class CZNCSock : public Csock, protected CCoreTranslationMixin { int VerifyPeerCertificate(int iPreVerify, X509_STORE_CTX* pStoreCTX) override; void SSLHandShakeFinished() override; + bool CheckSSLCert(X509* pCert); + virtual void SSLCertError(X509* pCert) {} bool SNIConfigureClient(CString& sHostname) override; + CString GetSSLPeerFingerprint(X509* pCert = nullptr) const; +#else + CString GetSSLPeerFingerprint() const { return ""; } #endif void SetHostToVerifySSL(const CString& sHost) { m_sHostToVerifySSL = sHost; } - CString GetSSLPeerFingerprint() const; void SetSSLTrustedPeerFingerprints(const SCString& ssFPs) { m_ssTrustedFingerprints = ssFPs; } diff --git a/src/IRCSock.cpp b/src/IRCSock.cpp index 285a87d9..af1ccaa8 100644 --- a/src/IRCSock.cpp +++ b/src/IRCSock.cpp @@ -1277,39 +1277,9 @@ void CIRCSock::SockError(int iErrno, const CString& sDescription) { m_pNetwork->PutStatus( t_f("Disconnected from IRC ({1}). Reconnecting...")(sError)); } -#ifdef HAVE_LIBSSL - if (iErrno == errnoBadSSLCert) { - // Stringify bad cert - X509* pCert = GetX509(); - if (pCert) { - BIO* mem = BIO_new(BIO_s_mem()); - X509_print(mem, pCert); - X509_free(pCert); - char* pCertStr = nullptr; - long iLen = BIO_get_mem_data(mem, &pCertStr); - CString sCert(pCertStr, iLen); - BIO_free(mem); - - VCString vsCert; - sCert.Split("\n", vsCert); - for (const CString& s : vsCert) { - // It shouldn't contain any bad characters, but let's be - // safe... - m_pNetwork->PutStatus("|" + s.Escape_n(CString::EDEBUG)); - } - CString sSHA1; - if (GetPeerFingerprint(sSHA1)) - m_pNetwork->PutStatus( - "SHA1: " + - sSHA1.Escape_n(CString::EHEXCOLON, CString::EHEXCOLON)); - CString sSHA256 = GetSSLPeerFingerprint(); - m_pNetwork->PutStatus("SHA-256: " + sSHA256); - m_pNetwork->PutStatus( - t_f("If you trust this certificate, do /znc " - "AddTrustedServerFingerprint {1}")(sSHA256)); - } - } -#endif + } + for (const CString& s : m_vsSSLError) { + m_pNetwork->PutStatus(s); } m_pNetwork->ClearRawBuffer(); m_pNetwork->ClearMotdBuffer(); @@ -1318,6 +1288,34 @@ void CIRCSock::SockError(int iErrno, const CString& sDescription) { m_scUserModes.clear(); } +#ifdef HAVE_LIBSSL +void CIRCSock::SSLCertError(X509* pCert) { + BIO* mem = BIO_new(BIO_s_mem()); + X509_print(mem, pCert); + char* pCertStr = nullptr; + long iLen = BIO_get_mem_data(mem, &pCertStr); + CString sCert(pCertStr, iLen); + BIO_free(mem); + + VCString vsCert; + sCert.Split("\n", vsCert); + for (const CString& s : vsCert) { + // It shouldn't contain any bad characters, but let's be + // safe... + m_vsSSLError.push_back("|" + s.Escape_n(CString::EDEBUG)); + } + CString sSHA1; + if (GetPeerFingerprint(sSHA1)) + m_vsSSLError.push_back( + "SHA1: " + sSHA1.Escape_n(CString::EHEXCOLON, CString::EHEXCOLON)); + CString sSHA256 = GetSSLPeerFingerprint(pCert); + m_vsSSLError.push_back("SHA-256: " + sSHA256); + m_vsSSLError.push_back( + t_f("If you trust this certificate, do /znc " + "AddTrustedServerFingerprint {1}")(sSHA256)); +} +#endif + void CIRCSock::Timeout() { DEBUG(GetSockName() << " == Timeout()"); if (!m_pNetwork->GetUser()->IsBeingDeleted()) { diff --git a/src/Socket.cpp b/src/Socket.cpp index ad95eb4d..ebd2d204 100644 --- a/src/Socket.cpp +++ b/src/Socket.cpp @@ -109,55 +109,63 @@ int CZNCSock::VerifyPeerCertificate(int iPreVerify, X509_STORE_CTX* pStoreCTX) { } void CZNCSock::SSLHandShakeFinished() { + X509* pCert = GetX509(); + if (!CheckSSLCert(pCert)) { + Close(); + } + X509_free(pCert); +} + +bool CZNCSock::CheckSSLCert(X509* pCert) { if (GetType() != ETConn::OUTBOUND) { - return; + return true; } - X509* pCert = GetX509(); if (!pCert) { DEBUG(GetSockName() + ": No cert"); CallSockError(errnoBadSSLCert, "Anonymous SSL cert is not allowed"); - Close(); - return; + return false; } if (GetTrustAllCerts()) { DEBUG(GetSockName() + ": Verification disabled, trusting all."); - return; + return true; } CString sHostVerifyError; if (!ZNC_SSLVerifyHost(m_sHostToVerifySSL, pCert, sHostVerifyError)) { m_ssCertVerificationErrors.insert(sHostVerifyError); } - X509_free(pCert); if (GetTrustPKI() && m_ssCertVerificationErrors.empty()) { DEBUG(GetSockName() + ": Good cert (PKI valid)"); - return; + return true; } - CString sFP = GetSSLPeerFingerprint(); + CString sFP = GetSSLPeerFingerprint(pCert); if (m_ssTrustedFingerprints.count(sFP) != 0) { DEBUG(GetSockName() + ": Cert explicitly trusted by user: " << sFP); - return; + return true; } DEBUG(GetSockName() + ": Bad cert"); CString sErrorMsg = "Invalid SSL certificate: "; sErrorMsg += CString(", ").Join(begin(m_ssCertVerificationErrors), end(m_ssCertVerificationErrors)); + SSLCertError(pCert); CallSockError(errnoBadSSLCert, sErrorMsg); - Close(); + return false; } bool CZNCSock::SNIConfigureClient(CString& sHostname) { sHostname = m_sHostToVerifySSL; return true; } -#endif -CString CZNCSock::GetSSLPeerFingerprint() const { -#ifdef HAVE_LIBSSL +CString CZNCSock::GetSSLPeerFingerprint(X509* pCert) const { // Csocket's version returns insecure SHA-1 // This one is SHA-256 const EVP_MD* evp = EVP_sha256(); - X509* pCert = GetX509(); + bool bOwned = false; + if (!pCert) { + pCert = GetX509(); + bOwned = true; + } if (!pCert) { DEBUG(GetSockName() + ": GetSSLPeerFingerprint: Anonymous cert"); return ""; @@ -165,17 +173,17 @@ CString CZNCSock::GetSSLPeerFingerprint() const { unsigned char buf[256 / 8]; unsigned int _32 = 256 / 8; int iSuccess = X509_digest(pCert, evp, buf, &_32); - X509_free(pCert); + if (bOwned) { + X509_free(pCert); + } if (!iSuccess) { DEBUG(GetSockName() + ": GetSSLPeerFingerprint: Couldn't find digest"); return ""; } return CString(reinterpret_cast(buf), sizeof buf) .Escape_n(CString::EASCII, CString::EHEXCOLON); -#else - return ""; -#endif } +#endif void CZNCSock::SetEncoding(const CString& sEncoding) { #ifdef HAVE_ICU