From: Stefan Eissing Date: Tue, 19 Nov 2024 13:44:02 +0000 (+0100) Subject: OpenSSL: improvde error message on expired certificate X-Git-Tag: curl-8_11_1~31 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fd4528a8d8e1c065945615da38c43c61c10cd3d0;p=thirdparty%2Fcurl.git OpenSSL: improvde error message on expired certificate Fix regression that no longer printed the error messages about expired certificates in openssl. Add test case for openssl/gnutls/wolfssl. Fixes #15612 Reported-by: hiimmat on github Closes #15613 --- diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 0643ba046e..f2987ea944 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -4220,14 +4220,11 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, lerr = SSL_get_verify_result(octx->ssl); if(lerr != X509_V_OK) { ssl_config->certverifyresult = lerr; - msnprintf(error_buffer, sizeof(error_buffer), - "SSL certificate problem: %s", - X509_verify_cert_error_string(lerr)); + failf(data, "SSL certificate problem: %s", + X509_verify_cert_error_string(lerr)); } - else { + else failf(data, "%s", "SSL certificate verification failed"); - return result; - } } #if defined(SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED) /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on @@ -4278,7 +4275,6 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ", extramsg[0] ? extramsg : SSL_ERROR_to_str(detail), connssl->peer.hostname, connssl->peer.port); - return result; } return result; diff --git a/tests/http/test_17_ssl_use.py b/tests/http/test_17_ssl_use.py index f129b11018..883d4a5f6f 100644 --- a/tests/http/test_17_ssl_use.py +++ b/tests/http/test_17_ssl_use.py @@ -366,3 +366,27 @@ class TestSSLUse: ]) assert r.exit_code == 0, f'{r}' assert r.json, f'{r}' + + # connect to an expired certificate + @pytest.mark.parametrize("proto", ['http/1.1', 'h2']) + def test_17_14_expired_cert(self, env: Env, proto): + if proto == 'h3' and not env.have_h3(): + pytest.skip("h3 not supported") + curl = CurlClient(env=env) + url = f'https://{env.expired_domain}:{env.port_for(proto)}/' + r = curl.http_get(url=url, alpn_proto=proto) + assert r.exit_code == 60, f'{r}' # peer failed verification + exp_trace = None + match_trace = None + if env.curl_uses_lib('openssl') or env.curl_uses_lib('quictls'): + exp_trace = r'.*SSL certificate problem: certificate has expired$' + elif env.curl_uses_lib('gnutls'): + exp_trace = r'.*server verification failed: certificate has expired\..*' + elif env.curl_uses_lib('wolfssl'): + exp_trace = r'.*server verification failed: certificate has expired\.$' + if exp_trace is not None: + for line in r.trace_lines: + if re.match(exp_trace, line): + match_trace = line + break + assert match_trace, f'Did not find "{exp_trace}" in trace\n{r.dump_logs()}' diff --git a/tests/http/testenv/env.py b/tests/http/testenv/env.py index 4eb0eb7653..5360c46055 100644 --- a/tests/http/testenv/env.py +++ b/tests/http/testenv/env.py @@ -32,6 +32,7 @@ import socket import subprocess import tempfile from configparser import ConfigParser, ExtendedInterpolation +from datetime import timedelta from typing import Optional from .certs import CertificateSpec, Credentials, TestCA @@ -143,11 +144,14 @@ class EnvConfig: self.domain2 = f"two.{self.tld}" self.ftp_domain = f"ftp.{self.tld}" self.proxy_domain = f"proxy.{self.tld}" + self.expired_domain = f"expired.{self.tld}" self.cert_specs = [ CertificateSpec(domains=[self.domain1, self.domain1brotli, 'localhost', '127.0.0.1'], key_type='rsa2048'), CertificateSpec(domains=[self.domain2], key_type='rsa2048'), CertificateSpec(domains=[self.ftp_domain], key_type='rsa2048'), CertificateSpec(domains=[self.proxy_domain, '127.0.0.1'], key_type='rsa2048'), + CertificateSpec(domains=[self.expired_domain], key_type='rsa2048', + valid_from=timedelta(days=-100), valid_to=timedelta(days=-10)), CertificateSpec(name="clientsX", sub_specs=[ CertificateSpec(name="user1", client=True), ]), @@ -502,6 +506,10 @@ class Env: def proxy_domain(self) -> str: return self.CONFIG.proxy_domain + @property + def expired_domain(self) -> str: + return self.CONFIG.expired_domain + @property def http_port(self) -> int: return self.CONFIG.ports['http'] diff --git a/tests/http/testenv/httpd.py b/tests/http/testenv/httpd.py index f3ca46f133..414f358ee2 100644 --- a/tests/http/testenv/httpd.py +++ b/tests/http/testenv/httpd.py @@ -219,6 +219,9 @@ class Httpd: domain2 = self.env.domain2 creds2 = self.env.get_credentials(domain2) assert creds2 # convince pytype this isn't None + exp_domain = self.env.expired_domain + exp_creds = self.env.get_credentials(exp_domain) + assert exp_creds # convince pytype this isn't None proxy_domain = self.env.proxy_domain proxy_creds = self.env.get_credentials(proxy_domain) assert proxy_creds # convince pytype this isn't None @@ -346,6 +349,22 @@ class Httpd: '', '', ]) + conf.extend([ # https host for expired domain + f'', + f' ServerName {exp_domain}', + ' Protocols h2 http/1.1', + ' SSLEngine on', + f' SSLCertificateFile {exp_creds.cert_file}', + f' SSLCertificateKeyFile {exp_creds.pkey_file}', + f' DocumentRoot "{self._docs_dir}/expired"', + ]) + conf.extend(self._curltest_conf(exp_domain)) + if exp_domain in self._extra_configs: + conf.extend(self._extra_configs[exp_domain]) + conf.extend([ + '', + '', + ]) conf.extend([ # http forward proxy f'', f' ServerName {proxy_domain}',