This shouldn't change any behavior.
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.
*
double m_fFloodRate;
bool m_bFloodProtection;
SCString m_ssSupportedTags;
+ VCString m_vsSSLError;
friend class CIRCFloodTimer;
};
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;
}
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();
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()) {
}
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 "";
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