Skip to content

Commit 3ca8921

Browse files
authored
Merge pull request #13041 from sethmlarson/truststore-0.10.0
Upgrade vendored truststore to 0.10.0
2 parents 6958e28 + 0cc7375 commit 3ca8921

File tree

6 files changed

+147
-69
lines changed

6 files changed

+147
-69
lines changed

news/truststore.vendor.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Upgrade truststore to 0.9.2
1+
Upgrade truststore to 0.10.0

src/pip/_vendor/truststore/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,4 @@
3333
del _api, _sys # type: ignore[name-defined] # noqa: F821
3434

3535
__all__ = ["SSLContext", "inject_into_ssl", "extract_from_ssl"]
36-
__version__ = "0.9.2"
36+
__version__ = "0.10.0"

src/pip/_vendor/truststore/_api.py

+3
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,9 @@ def session_stats(self) -> dict[str, int]:
169169
def cert_store_stats(self) -> dict[str, int]:
170170
raise NotImplementedError()
171171

172+
def set_default_verify_paths(self) -> None:
173+
self._ctx.set_default_verify_paths()
174+
172175
@typing.overload
173176
def get_ca_certs(
174177
self, binary_form: typing.Literal[False] = ...

src/pip/_vendor/truststore/_macos.py

+136-64
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
f"Only OS X 10.8 and newer are supported, not {_mac_version_info[0]}.{_mac_version_info[1]}"
2626
)
2727

28+
_is_macos_version_10_14_or_later = _mac_version_info >= (10, 14)
29+
2830

2931
def _load_cdll(name: str, macos10_16_path: str) -> CDLL:
3032
"""Loads a CDLL by name, falling back to known path on 10.16+"""
@@ -115,6 +117,12 @@ def _load_cdll(name: str, macos10_16_path: str) -> CDLL:
115117
]
116118
Security.SecTrustGetTrustResult.restype = OSStatus
117119

120+
Security.SecTrustEvaluate.argtypes = [
121+
SecTrustRef,
122+
POINTER(SecTrustResultType),
123+
]
124+
Security.SecTrustEvaluate.restype = OSStatus
125+
118126
Security.SecTrustRef = SecTrustRef # type: ignore[attr-defined]
119127
Security.SecTrustResultType = SecTrustResultType # type: ignore[attr-defined]
120128
Security.OSStatus = OSStatus # type: ignore[attr-defined]
@@ -197,8 +205,19 @@ def _load_cdll(name: str, macos10_16_path: str) -> CDLL:
197205
CoreFoundation.CFStringRef = CFStringRef # type: ignore[attr-defined]
198206
CoreFoundation.CFErrorRef = CFErrorRef # type: ignore[attr-defined]
199207

200-
except AttributeError:
201-
raise ImportError("Error initializing ctypes") from None
208+
except AttributeError as e:
209+
raise ImportError(f"Error initializing ctypes: {e}") from None
210+
211+
# SecTrustEvaluateWithError is macOS 10.14+
212+
if _is_macos_version_10_14_or_later:
213+
try:
214+
Security.SecTrustEvaluateWithError.argtypes = [
215+
SecTrustRef,
216+
POINTER(CFErrorRef),
217+
]
218+
Security.SecTrustEvaluateWithError.restype = c_bool
219+
except AttributeError as e:
220+
raise ImportError(f"Error initializing ctypes: {e}") from None
202221

203222

204223
def _handle_osstatus(result: OSStatus, _: typing.Any, args: typing.Any) -> typing.Any:
@@ -258,6 +277,7 @@ def _handle_osstatus(result: OSStatus, _: typing.Any, args: typing.Any) -> typin
258277
Security.SecTrustSetAnchorCertificates.errcheck = _handle_osstatus # type: ignore[assignment]
259278
Security.SecTrustSetAnchorCertificatesOnly.errcheck = _handle_osstatus # type: ignore[assignment]
260279
Security.SecTrustGetTrustResult.errcheck = _handle_osstatus # type: ignore[assignment]
280+
Security.SecTrustEvaluate.errcheck = _handle_osstatus # type: ignore[assignment]
261281

262282

263283
class CFConst:
@@ -365,9 +385,10 @@ def _verify_peercerts_impl(
365385
certs = None
366386
policies = None
367387
trust = None
368-
cf_error = None
369388
try:
370-
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:
371392
cf_str_hostname = None
372393
try:
373394
cf_str_hostname = _bytes_to_cf_string(server_hostname.encode("ascii"))
@@ -431,69 +452,120 @@ def _verify_peercerts_impl(
431452
# We always want system certificates.
432453
Security.SecTrustSetAnchorCertificatesOnly(trust, False)
433454

434-
cf_error = CoreFoundation.CFErrorRef()
435-
sec_trust_eval_result = Security.SecTrustEvaluateWithError(
436-
trust, ctypes.byref(cf_error)
437-
)
438-
# sec_trust_eval_result is a bool (0 or 1)
439-
# where 1 means that the certs are trusted.
440-
if sec_trust_eval_result == 1:
441-
is_trusted = True
442-
elif sec_trust_eval_result == 0:
443-
is_trusted = False
455+
# macOS 10.13 and earlier don't support SecTrustEvaluateWithError()
456+
# so we use SecTrustEvaluate() which means we need to construct error
457+
# messages ourselves.
458+
if _is_macos_version_10_14_or_later:
459+
_verify_peercerts_impl_macos_10_14(ssl_context, trust)
444460
else:
445-
raise ssl.SSLError(
446-
f"Unknown result from Security.SecTrustEvaluateWithError: {sec_trust_eval_result!r}"
447-
)
448-
449-
cf_error_code = 0
450-
if not is_trusted:
451-
cf_error_code = CoreFoundation.CFErrorGetCode(cf_error)
452-
453-
# If the error is a known failure that we're
454-
# explicitly okay with from SSLContext configuration
455-
# we can set is_trusted accordingly.
456-
if ssl_context.verify_mode != ssl.CERT_REQUIRED and (
457-
cf_error_code == CFConst.errSecNotTrusted
458-
or cf_error_code == CFConst.errSecCertificateExpired
459-
):
460-
is_trusted = True
461-
elif (
462-
not ssl_context.check_hostname
463-
and cf_error_code == CFConst.errSecHostNameMismatch
464-
):
465-
is_trusted = True
466-
467-
# If we're still not trusted then we start to
468-
# construct and raise the SSLCertVerificationError.
469-
if not is_trusted:
470-
cf_error_string_ref = None
471-
try:
472-
cf_error_string_ref = CoreFoundation.CFErrorCopyDescription(cf_error)
473-
474-
# Can this ever return 'None' if there's a CFError?
475-
cf_error_message = (
476-
_cf_string_ref_to_str(cf_error_string_ref)
477-
or "Certificate verification failed"
478-
)
479-
480-
# TODO: Not sure if we need the SecTrustResultType for anything?
481-
# We only care whether or not it's a success or failure for now.
482-
sec_trust_result_type = Security.SecTrustResultType()
483-
Security.SecTrustGetTrustResult(
484-
trust, ctypes.byref(sec_trust_result_type)
485-
)
486-
487-
err = ssl.SSLCertVerificationError(cf_error_message)
488-
err.verify_message = cf_error_message
489-
err.verify_code = cf_error_code
490-
raise err
491-
finally:
492-
if cf_error_string_ref:
493-
CoreFoundation.CFRelease(cf_error_string_ref)
494-
461+
_verify_peercerts_impl_macos_10_13(ssl_context, trust)
495462
finally:
496463
if policies:
497464
CoreFoundation.CFRelease(policies)
498465
if trust:
499466
CoreFoundation.CFRelease(trust)
467+
468+
469+
def _verify_peercerts_impl_macos_10_13(
470+
ssl_context: ssl.SSLContext, sec_trust_ref: typing.Any
471+
) -> None:
472+
"""Verify using 'SecTrustEvaluate' API for macOS 10.13 and earlier.
473+
macOS 10.14 added the 'SecTrustEvaluateWithError' API.
474+
"""
475+
sec_trust_result_type = Security.SecTrustResultType()
476+
Security.SecTrustEvaluate(sec_trust_ref, ctypes.byref(sec_trust_result_type))
477+
478+
try:
479+
sec_trust_result_type_as_int = int(sec_trust_result_type.value)
480+
except (ValueError, TypeError):
481+
sec_trust_result_type_as_int = -1
482+
483+
# Apple doesn't document these values in their own API docs.
484+
# See: https://github.com/xybp888/iOS-SDKs/blob/master/iPhoneOS13.0.sdk/System/Library/Frameworks/Security.framework/Headers/SecTrust.h#L84
485+
if (
486+
ssl_context.verify_mode == ssl.CERT_REQUIRED
487+
and sec_trust_result_type_as_int not in (1, 4)
488+
):
489+
# Note that we're not able to ignore only hostname errors
490+
# for macOS 10.13 and earlier, so check_hostname=False will
491+
# still return an error.
492+
sec_trust_result_type_to_message = {
493+
0: "Invalid trust result type",
494+
# 1: "Trust evaluation succeeded",
495+
2: "User confirmation required",
496+
3: "User specified that certificate is not trusted",
497+
# 4: "Trust result is unspecified",
498+
5: "Recoverable trust failure occurred",
499+
6: "Fatal trust failure occurred",
500+
7: "Other error occurred, certificate may be revoked",
501+
}
502+
error_message = sec_trust_result_type_to_message.get(
503+
sec_trust_result_type_as_int,
504+
f"Unknown trust result: {sec_trust_result_type_as_int}",
505+
)
506+
507+
err = ssl.SSLCertVerificationError(error_message)
508+
err.verify_message = error_message
509+
err.verify_code = sec_trust_result_type_as_int
510+
raise err
511+
512+
513+
def _verify_peercerts_impl_macos_10_14(
514+
ssl_context: ssl.SSLContext, sec_trust_ref: typing.Any
515+
) -> None:
516+
"""Verify using 'SecTrustEvaluateWithError' API for macOS 10.14+."""
517+
cf_error = CoreFoundation.CFErrorRef()
518+
sec_trust_eval_result = Security.SecTrustEvaluateWithError(
519+
sec_trust_ref, ctypes.byref(cf_error)
520+
)
521+
# sec_trust_eval_result is a bool (0 or 1)
522+
# where 1 means that the certs are trusted.
523+
if sec_trust_eval_result == 1:
524+
is_trusted = True
525+
elif sec_trust_eval_result == 0:
526+
is_trusted = False
527+
else:
528+
raise ssl.SSLError(
529+
f"Unknown result from Security.SecTrustEvaluateWithError: {sec_trust_eval_result!r}"
530+
)
531+
532+
cf_error_code = 0
533+
if not is_trusted:
534+
cf_error_code = CoreFoundation.CFErrorGetCode(cf_error)
535+
536+
# If the error is a known failure that we're
537+
# explicitly okay with from SSLContext configuration
538+
# we can set is_trusted accordingly.
539+
if ssl_context.verify_mode != ssl.CERT_REQUIRED and (
540+
cf_error_code == CFConst.errSecNotTrusted
541+
or cf_error_code == CFConst.errSecCertificateExpired
542+
):
543+
is_trusted = True
544+
545+
# If we're still not trusted then we start to
546+
# construct and raise the SSLCertVerificationError.
547+
if not is_trusted:
548+
cf_error_string_ref = None
549+
try:
550+
cf_error_string_ref = CoreFoundation.CFErrorCopyDescription(cf_error)
551+
552+
# Can this ever return 'None' if there's a CFError?
553+
cf_error_message = (
554+
_cf_string_ref_to_str(cf_error_string_ref)
555+
or "Certificate verification failed"
556+
)
557+
558+
# TODO: Not sure if we need the SecTrustResultType for anything?
559+
# We only care whether or not it's a success or failure for now.
560+
sec_trust_result_type = Security.SecTrustResultType()
561+
Security.SecTrustGetTrustResult(
562+
sec_trust_ref, ctypes.byref(sec_trust_result_type)
563+
)
564+
565+
err = ssl.SSLCertVerificationError(cf_error_message)
566+
err.verify_message = cf_error_message
567+
err.verify_code = cf_error_code
568+
raise err
569+
finally:
570+
if cf_error_string_ref:
571+
CoreFoundation.CFRelease(cf_error_string_ref)

src/pip/_vendor/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)

src/pip/_vendor/vendor.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ rich==13.7.1
1515
resolvelib==1.0.1
1616
setuptools==70.3.0
1717
tomli==2.0.1
18-
truststore==0.9.2
18+
truststore==0.10.0

0 commit comments

Comments
 (0)