]> jfr.im git - irc/znc/znc.git/commitdiff
Backport SSL changes from asio branch (#1639).
authorAlexey Sokolov <redacted>
Sun, 22 Dec 2019 23:01:49 +0000 (23:01 +0000)
committerAlexey Sokolov <redacted>
Sun, 29 Dec 2019 13:03:52 +0000 (13:03 +0000)
This shouldn't change any behavior.

include/znc/IRCSock.h
include/znc/Socket.h
src/IRCSock.cpp
src/Socket.cpp

index 1ea1236bbc97342ae2195cefcf8ff5fa1357736e..75d087f938990033412fb3fe7869d73fc317d914 100644 (file)
@@ -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;
 };
index 68f4dc0c3e50f85aacf5c7ad582cb27f4c818ae3..359a7023c6e089649f83cfcc2abc7f9c529867e4 100644 (file)
@@ -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;
     }
index 285a87d9feb3f7859057ee1867622259311eb13f..af1ccaa860a68aeb0fc67d7a12bf7e484de3e9f8 100644 (file)
@@ -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()) {
index ad95eb4d6083394c7cb4a01903c28067c17ea961..ebd2d204ef1bc85f4c70e84053d2438885dc16cb 100644 (file)
@@ -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<const char*>(buf), sizeof buf)
         .Escape_n(CString::EASCII, CString::EHEXCOLON);
-#else
-    return "";
-#endif
 }
+#endif
 
 void CZNCSock::SetEncoding(const CString& sEncoding) {
 #ifdef HAVE_ICU