mirror of
https://github.com/znc/znc.git
synced 2026-03-28 17:42:41 +01:00
Merge pull request #1697 from DarthGandalf/ssl
Backport SSL changes from asio branch (#1639).
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user