Commit 67385eea authored by Alexey Sokolov's avatar Alexey Sokolov Committed by GitHub

Merge pull request #1697 from DarthGandalf/ssl

Backport SSL changes from asio branch (#1639).
parents d3f80095 c0e71184
......@@ -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;
};
......
......@@ -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;
}
......
......@@ -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()) {
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment