Skip to content

Commit

Permalink
Revert "Upgrade vendored truststore to 0.9.0" (#12671)
Browse files Browse the repository at this point in the history
This reverts commit 8547b52.
  • Loading branch information
pradyunsg authored May 4, 2024
1 parent 8547b52 commit bc84491
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 52 deletions.
1 change: 0 additions & 1 deletion news/truststore.vendor.rst

This file was deleted.

2 changes: 1 addition & 1 deletion src/pip/_vendor/truststore/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
del _api, _sys # type: ignore[name-defined] # noqa: F821

__all__ = ["SSLContext", "inject_into_ssl", "extract_from_ssl"]
__version__ = "0.9.0"
__version__ = "0.8.0"
39 changes: 16 additions & 23 deletions src/pip/_vendor/truststore/_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
import platform
import socket
import ssl
import sys
import typing

import _ssl # type: ignore[import]

from ._ssl_constants import (
_original_SSLContext,
_original_super_SSLContext,
Expand Down Expand Up @@ -48,7 +49,7 @@ def extract_from_ssl() -> None:
try:
import pip._vendor.urllib3.util.ssl_ as urllib3_ssl

urllib3_ssl.SSLContext = _original_SSLContext # type: ignore[assignment]
urllib3_ssl.SSLContext = _original_SSLContext
except ImportError:
pass

Expand Down Expand Up @@ -170,13 +171,16 @@ def cert_store_stats(self) -> dict[str, int]:
@typing.overload
def get_ca_certs(
self, binary_form: typing.Literal[False] = ...
) -> list[typing.Any]: ...
) -> list[typing.Any]:
...

@typing.overload
def get_ca_certs(self, binary_form: typing.Literal[True] = ...) -> list[bytes]: ...
def get_ca_certs(self, binary_form: typing.Literal[True] = ...) -> list[bytes]:
...

@typing.overload
def get_ca_certs(self, binary_form: bool = ...) -> typing.Any: ...
def get_ca_certs(self, binary_form: bool = ...) -> typing.Any:
...

def get_ca_certs(self, binary_form: bool = False) -> list[typing.Any] | list[bytes]:
raise NotImplementedError()
Expand Down Expand Up @@ -272,23 +276,6 @@ def verify_mode(self, value: ssl.VerifyMode) -> None:
)


# Python 3.13+ makes get_unverified_chain() a public API that only returns DER
# encoded certificates. We detect whether we need to call public_bytes() for 3.10->3.12
# Pre-3.13 returned None instead of an empty list from get_unverified_chain()
if sys.version_info >= (3, 13):

def _get_unverified_chain_bytes(sslobj: ssl.SSLObject) -> list[bytes]:
unverified_chain = sslobj.get_unverified_chain() or () # type: ignore[attr-defined]
return [cert for cert in unverified_chain]

else:
import _ssl # type: ignore[import-not-found]

def _get_unverified_chain_bytes(sslobj: ssl.SSLObject) -> list[bytes]:
unverified_chain = sslobj.get_unverified_chain() or () # type: ignore[attr-defined]
return [cert.public_bytes(_ssl.ENCODING_DER) for cert in unverified_chain]


def _verify_peercerts(
sock_or_sslobj: ssl.SSLSocket | ssl.SSLObject, server_hostname: str | None
) -> None:
Expand All @@ -303,7 +290,13 @@ def _verify_peercerts(
except AttributeError:
pass

cert_bytes = _get_unverified_chain_bytes(sslobj)
# SSLObject.get_unverified_chain() returns 'None'
# if the peer sends no certificates. This is common
# for the server-side scenario.
unverified_chain: typing.Sequence[_ssl.Certificate] = (
sslobj.get_unverified_chain() or () # type: ignore[attr-defined]
)
cert_bytes = [cert.public_bytes(_ssl.ENCODING_DER) for cert in unverified_chain]
_verify_peercerts_impl(
sock_or_sslobj.context, cert_bytes, server_hostname=server_hostname
)
14 changes: 8 additions & 6 deletions src/pip/_vendor/truststore/_macos.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ def _load_cdll(name: str, macos10_16_path: str) -> CDLL:
Security.SecTrustSetAnchorCertificatesOnly.argtypes = [SecTrustRef, Boolean]
Security.SecTrustSetAnchorCertificatesOnly.restype = OSStatus

Security.SecTrustEvaluate.argtypes = [SecTrustRef, POINTER(SecTrustResultType)]
Security.SecTrustEvaluate.restype = OSStatus

Security.SecPolicyCreateRevocation.argtypes = [CFOptionFlags]
Security.SecPolicyCreateRevocation.restype = SecPolicyRef

Expand Down Expand Up @@ -256,7 +259,6 @@ def _handle_osstatus(result: OSStatus, _: typing.Any, args: typing.Any) -> typin

Security.SecTrustCreateWithCertificates.errcheck = _handle_osstatus # type: ignore[assignment]
Security.SecTrustSetAnchorCertificates.errcheck = _handle_osstatus # type: ignore[assignment]
Security.SecTrustSetAnchorCertificatesOnly.errcheck = _handle_osstatus # type: ignore[assignment]
Security.SecTrustGetTrustResult.errcheck = _handle_osstatus # type: ignore[assignment]


Expand Down Expand Up @@ -415,21 +417,21 @@ def _verify_peercerts_impl(
CoreFoundation.CFRelease(certs)

# If there are additional trust anchors to load we need to transform
# the list of DER-encoded certificates into a CFArray.
# the list of DER-encoded certificates into a CFArray. Otherwise
# pass 'None' to signal that we only want system / fetched certificates.
ctx_ca_certs_der: list[bytes] | None = ssl_context.get_ca_certs(
binary_form=True
)
if ctx_ca_certs_der:
ctx_ca_certs = None
try:
ctx_ca_certs = _der_certs_to_cf_cert_array(ctx_ca_certs_der)
ctx_ca_certs = _der_certs_to_cf_cert_array(cert_chain)
Security.SecTrustSetAnchorCertificates(trust, ctx_ca_certs)
finally:
if ctx_ca_certs:
CoreFoundation.CFRelease(ctx_ca_certs)

# We always want system certificates.
Security.SecTrustSetAnchorCertificatesOnly(trust, False)
else:
Security.SecTrustSetAnchorCertificates(trust, None)

cf_error = CoreFoundation.CFErrorRef()
sec_trust_eval_result = Security.SecTrustEvaluateWithError(
Expand Down
30 changes: 10 additions & 20 deletions src/pip/_vendor/truststore/_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,12 +325,6 @@ def _verify_peercerts_impl(
server_hostname: str | None = None,
) -> None:
"""Verify the cert_chain from the server using Windows APIs."""

# If the peer didn't send any certificates then
# we can't do verification. Raise an error.
if not cert_chain:
raise ssl.SSLCertVerificationError("Peer sent no certificates to verify")

pCertContext = None
hIntermediateCertStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, None, 0, None)
try:
Expand Down Expand Up @@ -381,7 +375,7 @@ def _verify_peercerts_impl(
server_hostname,
chain_flags=chain_flags,
)
except ssl.SSLCertVerificationError as e:
except ssl.SSLCertVerificationError:
# If that fails but custom CA certs have been added
# to the SSLContext using load_verify_locations,
# try verifying using a custom chain engine
Expand All @@ -390,19 +384,15 @@ def _verify_peercerts_impl(
binary_form=True
)
if custom_ca_certs:
try:
_verify_using_custom_ca_certs(
ssl_context,
custom_ca_certs,
hIntermediateCertStore,
pCertContext,
pChainPara,
server_hostname,
chain_flags=chain_flags,
)
# Raise the original error, not the new error.
except ssl.SSLCertVerificationError:
raise e from None
_verify_using_custom_ca_certs(
ssl_context,
custom_ca_certs,
hIntermediateCertStore,
pCertContext,
pChainPara,
server_hostname,
chain_flags=chain_flags,
)
else:
raise
finally:
Expand Down
2 changes: 1 addition & 1 deletion src/pip/_vendor/vendor.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ setuptools==69.1.1
six==1.16.0
tenacity==8.2.3
tomli==2.0.1
truststore==0.9.0
truststore==0.8.0
webencodings==0.5.1

0 comments on commit bc84491

Please sign in to comment.