Skip to content

Commit 3491641

Browse files
authored
Change method for disabling hostname checking for Windows and macOS
Previously we'd ignore hostname errors if configured to do so, now we change the certificate policy to not check hostname
1 parent 23318e2 commit 3491641

File tree

3 files changed

+39
-23
lines changed

3 files changed

+39
-23
lines changed

src/truststore/_macos.py

+3-6
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,9 @@ def _verify_peercerts_impl(
386386
policies = None
387387
trust = None
388388
try:
389-
if server_hostname is not None:
389+
# Only set a hostname on the policy if we're verifying the hostname
390+
# on the leaf certificate.
391+
if server_hostname is not None and ssl_context.check_hostname:
390392
cf_str_hostname = None
391393
try:
392394
cf_str_hostname = _bytes_to_cf_string(server_hostname.encode("ascii"))
@@ -539,11 +541,6 @@ def _verify_peercerts_impl_macos_10_14(
539541
or cf_error_code == CFConst.errSecCertificateExpired
540542
):
541543
is_trusted = True
542-
elif (
543-
not ssl_context.check_hostname
544-
and cf_error_code == CFConst.errSecHostNameMismatch
545-
):
546-
is_trusted = True
547544

548545
# If we're still not trusted then we start to
549546
# construct and raise the SSLCertVerificationError.

src/truststore/_windows.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ class CERT_CHAIN_ENGINE_CONFIG(Structure):
212212
CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS = 0x00000F00
213213
CERT_CHAIN_POLICY_ALLOW_TESTROOT_FLAG = 0x00008000
214214
CERT_CHAIN_POLICY_TRUST_TESTROOT_FLAG = 0x00004000
215+
SECURITY_FLAG_IGNORE_CERT_CN_INVALID = 0x00001000
215216
AUTHTYPE_SERVER = 2
216217
CERT_CHAIN_POLICY_SSL = 4
217218
FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
@@ -443,6 +444,10 @@ def _get_and_verify_cert_chain(
443444
)
444445
ssl_extra_cert_chain_policy_para.dwAuthType = AUTHTYPE_SERVER
445446
ssl_extra_cert_chain_policy_para.fdwChecks = 0
447+
if ssl_context.check_hostname is False:
448+
ssl_extra_cert_chain_policy_para.fdwChecks = (
449+
SECURITY_FLAG_IGNORE_CERT_CN_INVALID
450+
)
446451
if server_hostname:
447452
ssl_extra_cert_chain_policy_para.pwszServerName = c_wchar_p(server_hostname)
448453

@@ -452,8 +457,6 @@ def _get_and_verify_cert_chain(
452457
)
453458
if ssl_context.verify_mode == ssl.CERT_NONE:
454459
chain_policy.dwFlags |= CERT_CHAIN_POLICY_VERIFY_MODE_NONE_FLAGS
455-
if not ssl_context.check_hostname:
456-
chain_policy.dwFlags |= CERT_CHAIN_POLICY_IGNORE_INVALID_NAME_FLAG
457460
chain_policy.cbSize = sizeof(chain_policy)
458461

459462
pPolicyPara = pointer(chain_policy)

tests/test_api.py

+31-15
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,23 @@ class FailureHost:
3939
error_messages: list[str]
4040

4141

42+
wrong_host_failure_host = FailureHost(
43+
host="wrong.host.badssl.com",
44+
error_messages=[
45+
# OpenSSL
46+
"Hostname mismatch, certificate is not valid for 'wrong.host.badssl.com'",
47+
# macOS
48+
"certificate name does not match",
49+
# macOS with revocation checks
50+
"certificates do not meet pinning requirements",
51+
# macOS 10.13
52+
"Recoverable trust failure occurred",
53+
# Windows
54+
"The certificate's CN name does not match the passed value.",
55+
],
56+
)
4257
failure_hosts_list = [
43-
FailureHost(
44-
host="wrong.host.badssl.com",
45-
error_messages=[
46-
# OpenSSL
47-
"Hostname mismatch, certificate is not valid for 'wrong.host.badssl.com'",
48-
# macOS
49-
"certificate name does not match",
50-
# macOS with revocation checks
51-
"certificates do not meet pinning requirements",
52-
# macOS 10.13
53-
"Recoverable trust failure occurred",
54-
# Windows
55-
"The certificate's CN name does not match the passed value.",
56-
],
57-
),
58+
wrong_host_failure_host,
5859
FailureHost(
5960
host="expired.badssl.com",
6061
error_messages=[
@@ -371,6 +372,21 @@ def test_requests_sslcontext_api_failures(failure):
371372
assert "cert" in repr(e.value).lower() and "verif" in repr(e.value).lower()
372373

373374

375+
def test_wrong_host_succeeds_with_hostname_verification_disabled() -> None:
376+
global wrong_host_failure_host
377+
378+
ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
379+
ctx.check_hostname = False
380+
assert ctx.check_hostname is False
381+
382+
with urllib3.PoolManager(ssl_context=ctx, retries=5, assert_hostname=False) as http:
383+
resp = http.request("GET", f"https://{wrong_host_failure_host.host}")
384+
385+
assert resp.status == 200
386+
assert len(resp.data) > 0
387+
assert ctx.check_hostname is False
388+
389+
374390
def test_trustme_cert(trustme_ca, httpserver):
375391
ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
376392
trustme_ca.configure_trust(ctx)

0 commit comments

Comments
 (0)