From 02a9ab80be819468352f773bd7120421868d38f0 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Fri, 7 Jun 2024 14:56:03 +0000 Subject: [PATCH] interface: nicer error for CA-signed "Hostname mismatch" certs Previously when encountering a CA-signed cert that failed verification with "Hostname mismatch", we would 1. erroneously mark it as self-signed 2. save its cert to pin it 3. when connecting to it later, and being served a CA-signed cert, we would reject the connection - I think this is because we use the saved cert (the peer cert, just the last cert in the chain) as if it was a root CA, and then during the connection we try to verify against that root. This fails as we are served a different root then. Error logged in step(3): ``` 3.85 | W | i/interface.[wirg2tsto7rme7n26lkd3ivbvxmjyy2pktlozwjuep22jcsfsghfqbqd.onion:50002] | Cannot connect to main server due to SSL error (maybe cert changed compared to "/home/user/.electrum/testnet/certs/wirg2tsto7rme7n26lkd3ivbvxmjyy2pktlozwjuep22jcsfsghfqbqd.onion"). Exc: ConnectError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1007)')) ``` This commit fixes step(1), we won't mark the cert as self-signed, instead the error is propagated out and the connection closed. ``` 35.05 | I | i/interface.[wirg2tsto7rme7n26lkd3ivbvxmjyy2pktlozwjuep22jcsfsghfqbqd.onion:50002] | disconnecting due to: ErrorGettingSSLCertFromServer(ConnectError(SSLCertVerificationError(1, "[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch, certificate is not valid for 'wirg2tsto7rme7n26lkd3ivbvxmjyy2pktlozwjuep22jcsfsghfqbqd.onion'. (_ssl.c:1007)"))) ``` Compare: - SSLCertVerificationError(1, "[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch, certificate is not valid for 'wirg2tsto7rme7n26lkd3ivbvxmjyy2pktlozwjuep22jcsfsghfqbqd.onion'. (_ssl.c:1007)") - verify_code=62 - SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self signed certificate (_ssl.c:1007)') - verify_code=18 Note: the verify_code constants look stable, though they might be openssl-specific. I guess that's ok(?) https://github.com/openssl/openssl/blob/140540189c67ba94188165b1144fdfb5b248bc02/include/openssl/x509_vfy.h.in#L224 --- electrum/interface.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/electrum/interface.py b/electrum/interface.py index 0398d3f9c..d4b1c1021 100644 --- a/electrum/interface.py +++ b/electrum/interface.py @@ -443,10 +443,14 @@ class Interface(Logger): await self.open_session(ca_ssl_context, exit_early=True) except ConnectError as e: cause = e.__cause__ - if isinstance(cause, ssl.SSLError) and cause.reason == 'CERTIFICATE_VERIFY_FAILED': - # failures due to self-signed certs are normal + if (isinstance(cause, ssl.SSLCertVerificationError) + and cause.reason == 'CERTIFICATE_VERIFY_FAILED' + and cause.verify_code == 18): # "self signed certificate" + # Good. We will use this server as self-signed. return False + # Not good. Cannot use this server. raise + # Good. We will use this server as CA-signed. return True async def _try_saving_ssl_cert_for_first_time(self, ca_ssl_context):