from ._ssl_constants import _set_ssl_context_verify_mode
_mac_version = platform.mac_ver()[0]
_mac_version_info = tuple(map(int, _mac_version.split("."))) if _mac_version_info < (10, 8): raise ImportError(
f"Only OS X 10.8 and newer are supported, not {_mac_version_info[0]}.{_mac_version_info[1]}"
)
def _load_cdll(name: str, macos10_16_path: str) -> CDLL: """Loads a CDLL by name, falling back to known path on 10.16+""" try: # Big Sur is technically 11 but we use 10.16 due to the Big Sur # beta being labeled as 10.16.
path: str | None if _mac_version_info >= (10, 16):
path = macos10_16_path else:
path = find_library(name) ifnot path: raise OSError # Caught and reraised as 'ImportError' return CDLL(path, use_errno=True) except OSError: raise ImportError(f"The library {name} failed to load") fromNone
def _handle_osstatus(result: OSStatus, _: typing.Any, args: typing.Any) -> typing.Any: """
Raises an error if the OSStatus value is non-zero. """ if int(result) == 0: return args
# Returns a CFString which we need to transform # into a UTF-8 Python string.
error_message_cfstring = None try:
error_message_cfstring = Security.SecCopyErrorMessageString(result, None)
# First step is convert the CFString into a C string pointer. # We try the fast no-copy way first.
error_message_cfstring_c_void_p = ctypes.cast(
error_message_cfstring, ctypes.POINTER(ctypes.c_void_p)
)
message = CoreFoundation.CFStringGetCStringPtr(
error_message_cfstring_c_void_p, CFConst.kCFStringEncodingUTF8
)
# Quoting the Apple dev docs: # # "A pointer to a C string or NULL if the internal # storage of theString does not allow this to be # returned efficiently." # # So we need to get our hands dirty. if message isNone:
buffer = ctypes.create_string_buffer(1024)
result = CoreFoundation.CFStringGetCString(
error_message_cfstring_c_void_p,
buffer,
1024,
CFConst.kCFStringEncodingUTF8,
) ifnot result: raise OSError("Error copying C string from CFStringRef")
message = buffer.value
finally: if error_message_cfstring isnotNone:
CoreFoundation.CFRelease(error_message_cfstring)
# If no message can be found for this status we come # up with a generic one that forwards the status code. if message isNoneor message == "":
message = f"SecureTransport operation returned a non-zero OSStatus: {result}"
def _bytes_to_cf_string(value: bytes) -> CFString: """
Given a Python binary data, create a CFString.
The string must be CFReleased by the caller. """
c_str = ctypes.c_char_p(value)
cf_str = CoreFoundation.CFStringCreateWithCString(
CoreFoundation.kCFAllocatorDefault,
c_str,
CFConst.kCFStringEncodingUTF8,
) return cf_str # type: ignore[no-any-return]
def _cf_string_ref_to_str(cf_string_ref: CFStringRef) -> str | None: # type: ignore[valid-type] """
Creates a Unicode string from a CFString object. Used entirely for error
reporting.
Yes, it annoys me quite a lot that this function is this complex. """
string = CoreFoundation.CFStringGetCStringPtr(
cf_string_ref, CFConst.kCFStringEncodingUTF8
) if string isNone:
buffer = ctypes.create_string_buffer(1024)
result = CoreFoundation.CFStringGetCString(
cf_string_ref, buffer, 1024, CFConst.kCFStringEncodingUTF8
) ifnot result: raise OSError("Error copying C string from CFStringRef")
string = buffer.value if string isnotNone:
string = string.decode("utf-8") return string # type: ignore[no-any-return]
def _der_certs_to_cf_cert_array(certs: list[bytes]) -> CFMutableArrayRef: # type: ignore[valid-type] """Builds a CFArray of SecCertificateRefs from a list of DER-encoded certificates.
Responsibility of the caller to call CoreFoundation.CFRelease on the CFArray. """
cf_array = CoreFoundation.CFArrayCreateMutable(
CoreFoundation.kCFAllocatorDefault,
0,
ctypes.byref(CoreFoundation.kCFTypeArrayCallBacks),
) ifnot cf_array: raise MemoryError("Unable to allocate memory!")
for cert_data in certs:
cf_data = None
sec_cert_ref = None try:
cf_data = _bytes_to_cf_data_ref(cert_data)
sec_cert_ref = Security.SecCertificateCreateWithData(
CoreFoundation.kCFAllocatorDefault, cf_data
)
CoreFoundation.CFArrayAppendValue(cf_array, sec_cert_ref) finally: if cf_data:
CoreFoundation.CFRelease(cf_data) if sec_cert_ref:
CoreFoundation.CFRelease(sec_cert_ref)
# Now that we have certificates loaded and a SecPolicy # we can finally create a SecTrust object!
trust = Security.SecTrustRef()
Security.SecTrustCreateWithCertificates(
certs, policies, ctypes.byref(trust)
)
finally: # The certs are now being held by SecTrust so we can # release our handles for the array. if certs:
CoreFoundation.CFRelease(certs)
# If there are additional trust anchors to load we need to transform # 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(cert_chain)
Security.SecTrustSetAnchorCertificates(trust, ctx_ca_certs) finally: if ctx_ca_certs:
CoreFoundation.CFRelease(ctx_ca_certs) else:
Security.SecTrustSetAnchorCertificates(trust, None)
cf_error = CoreFoundation.CFErrorRef()
sec_trust_eval_result = Security.SecTrustEvaluateWithError(
trust, ctypes.byref(cf_error)
) # sec_trust_eval_result is a bool (0 or 1) # where 1 means that the certs are trusted. if sec_trust_eval_result == 1:
is_trusted = True elif sec_trust_eval_result == 0:
is_trusted = False else: raise ssl.SSLError(
f"Unknown result from Security.SecTrustEvaluateWithError: {sec_trust_eval_result!r}"
)
# If the error is a known failure that we're # explicitly okay with from SSLContext configuration # we can set is_trusted accordingly. if ssl_context.verify_mode != ssl.CERT_REQUIRED and (
cf_error_code == CFConst.errSecNotTrusted or cf_error_code == CFConst.errSecCertificateExpired
):
is_trusted = True elif ( not ssl_context.check_hostname and cf_error_code == CFConst.errSecHostNameMismatch
):
is_trusted = True
# If we're still not trusted then we start to # construct and raise the SSLCertVerificationError. ifnot is_trusted:
cf_error_string_ref = None try:
cf_error_string_ref = CoreFoundation.CFErrorCopyDescription(cf_error)
# Can this ever return 'None' if there's a CFError?
cf_error_message = (
_cf_string_ref_to_str(cf_error_string_ref) or"Certificate verification failed"
)
# TODO: Not sure if we need the SecTrustResultType for anything? # We only care whether or not it's a success or failure for now.
sec_trust_result_type = Security.SecTrustResultType()
Security.SecTrustGetTrustResult(
trust, ctypes.byref(sec_trust_result_type)
)
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.