You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					922 lines
				
				34 KiB
			
		
		
			
		
	
	
					922 lines
				
				34 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								"""
							 | 
						||
| 
								 | 
							
								SecureTranport support for urllib3 via ctypes.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								This makes platform-native TLS available to urllib3 users on macOS without the
							 | 
						||
| 
								 | 
							
								use of a compiler. This is an important feature because the Python Package
							 | 
						||
| 
								 | 
							
								Index is moving to become a TLSv1.2-or-higher server, and the default OpenSSL
							 | 
						||
| 
								 | 
							
								that ships with macOS is not capable of doing TLSv1.2. The only way to resolve
							 | 
						||
| 
								 | 
							
								this is to give macOS users an alternative solution to the problem, and that
							 | 
						||
| 
								 | 
							
								solution is to use SecureTransport.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								We use ctypes here because this solution must not require a compiler. That's
							 | 
						||
| 
								 | 
							
								because pip is not allowed to require a compiler either.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								This is not intended to be a seriously long-term solution to this problem.
							 | 
						||
| 
								 | 
							
								The hope is that PEP 543 will eventually solve this issue for us, at which
							 | 
						||
| 
								 | 
							
								point we can retire this contrib module. But in the short term, we need to
							 | 
						||
| 
								 | 
							
								solve the impending tire fire that is Python on Mac without this kind of
							 | 
						||
| 
								 | 
							
								contrib module. So...here we are.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								To use this module, simply import and inject it::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    import urllib3.contrib.securetransport
							 | 
						||
| 
								 | 
							
								    urllib3.contrib.securetransport.inject_into_urllib3()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Happy TLSing!
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								This code is a bastardised version of the code found in Will Bond's oscrypto
							 | 
						||
| 
								 | 
							
								library. An enormous debt is owed to him for blazing this trail for us. For
							 | 
						||
| 
								 | 
							
								that reason, this code should be considered to be covered both by urllib3's
							 | 
						||
| 
								 | 
							
								license and by oscrypto's:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. code-block::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Copyright (c) 2015-2016 Will Bond <will@wbond.net>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Permission is hereby granted, free of charge, to any person obtaining a
							 | 
						||
| 
								 | 
							
								    copy of this software and associated documentation files (the "Software"),
							 | 
						||
| 
								 | 
							
								    to deal in the Software without restriction, including without limitation
							 | 
						||
| 
								 | 
							
								    the rights to use, copy, modify, merge, publish, distribute, sublicense,
							 | 
						||
| 
								 | 
							
								    and/or sell copies of the Software, and to permit persons to whom the
							 | 
						||
| 
								 | 
							
								    Software is furnished to do so, subject to the following conditions:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The above copyright notice and this permission notice shall be included in
							 | 
						||
| 
								 | 
							
								    all copies or substantial portions of the Software.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
							 | 
						||
| 
								 | 
							
								    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
							 | 
						||
| 
								 | 
							
								    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
							 | 
						||
| 
								 | 
							
								    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
							 | 
						||
| 
								 | 
							
								    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
							 | 
						||
| 
								 | 
							
								    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
							 | 
						||
| 
								 | 
							
								    DEALINGS IN THE SOFTWARE.
							 | 
						||
| 
								 | 
							
								"""
							 | 
						||
| 
								 | 
							
								from __future__ import absolute_import
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import contextlib
							 | 
						||
| 
								 | 
							
								import ctypes
							 | 
						||
| 
								 | 
							
								import errno
							 | 
						||
| 
								 | 
							
								import os.path
							 | 
						||
| 
								 | 
							
								import shutil
							 | 
						||
| 
								 | 
							
								import socket
							 | 
						||
| 
								 | 
							
								import ssl
							 | 
						||
| 
								 | 
							
								import struct
							 | 
						||
| 
								 | 
							
								import threading
							 | 
						||
| 
								 | 
							
								import weakref
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import six
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from .. import util
							 | 
						||
| 
								 | 
							
								from ..util.ssl_ import PROTOCOL_TLS_CLIENT
							 | 
						||
| 
								 | 
							
								from ._securetransport.bindings import CoreFoundation, Security, SecurityConst
							 | 
						||
| 
								 | 
							
								from ._securetransport.low_level import (
							 | 
						||
| 
								 | 
							
								    _assert_no_error,
							 | 
						||
| 
								 | 
							
								    _build_tls_unknown_ca_alert,
							 | 
						||
| 
								 | 
							
								    _cert_array_from_pem,
							 | 
						||
| 
								 | 
							
								    _create_cfstring_array,
							 | 
						||
| 
								 | 
							
								    _load_client_cert_chain,
							 | 
						||
| 
								 | 
							
								    _temporary_keychain,
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								try:  # Platform-specific: Python 2
							 | 
						||
| 
								 | 
							
								    from socket import _fileobject
							 | 
						||
| 
								 | 
							
								except ImportError:  # Platform-specific: Python 3
							 | 
						||
| 
								 | 
							
								    _fileobject = None
							 | 
						||
| 
								 | 
							
								    from ..packages.backports.makefile import backport_makefile
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								__all__ = ["inject_into_urllib3", "extract_from_urllib3"]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# SNI always works
							 | 
						||
| 
								 | 
							
								HAS_SNI = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								orig_util_HAS_SNI = util.HAS_SNI
							 | 
						||
| 
								 | 
							
								orig_util_SSLContext = util.ssl_.SSLContext
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# This dictionary is used by the read callback to obtain a handle to the
							 | 
						||
| 
								 | 
							
								# calling wrapped socket. This is a pretty silly approach, but for now it'll
							 | 
						||
| 
								 | 
							
								# do. I feel like I should be able to smuggle a handle to the wrapped socket
							 | 
						||
| 
								 | 
							
								# directly in the SSLConnectionRef, but for now this approach will work I
							 | 
						||
| 
								 | 
							
								# guess.
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# We need to lock around this structure for inserts, but we don't do it for
							 | 
						||
| 
								 | 
							
								# reads/writes in the callbacks. The reasoning here goes as follows:
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								#    1. It is not possible to call into the callbacks before the dictionary is
							 | 
						||
| 
								 | 
							
								#       populated, so once in the callback the id must be in the dictionary.
							 | 
						||
| 
								 | 
							
								#    2. The callbacks don't mutate the dictionary, they only read from it, and
							 | 
						||
| 
								 | 
							
								#       so cannot conflict with any of the insertions.
							 | 
						||
| 
								 | 
							
								#
							 | 
						||
| 
								 | 
							
								# This is good: if we had to lock in the callbacks we'd drastically slow down
							 | 
						||
| 
								 | 
							
								# the performance of this code.
							 | 
						||
| 
								 | 
							
								_connection_refs = weakref.WeakValueDictionary()
							 | 
						||
| 
								 | 
							
								_connection_ref_lock = threading.Lock()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# Limit writes to 16kB. This is OpenSSL's limit, but we'll cargo-cult it over
							 | 
						||
| 
								 | 
							
								# for no better reason than we need *a* limit, and this one is right there.
							 | 
						||
| 
								 | 
							
								SSL_WRITE_BLOCKSIZE = 16384
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# This is our equivalent of util.ssl_.DEFAULT_CIPHERS, but expanded out to
							 | 
						||
| 
								 | 
							
								# individual cipher suites. We need to do this because this is how
							 | 
						||
| 
								 | 
							
								# SecureTransport wants them.
							 | 
						||
| 
								 | 
							
								CIPHER_SUITES = [
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_AES_256_GCM_SHA384,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_AES_128_GCM_SHA256,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_RSA_WITH_AES_256_GCM_SHA384,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_RSA_WITH_AES_128_GCM_SHA256,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_AES_128_CCM_8_SHA256,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_AES_128_CCM_SHA256,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_RSA_WITH_AES_256_CBC_SHA256,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_RSA_WITH_AES_128_CBC_SHA256,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_RSA_WITH_AES_256_CBC_SHA,
							 | 
						||
| 
								 | 
							
								    SecurityConst.TLS_RSA_WITH_AES_128_CBC_SHA,
							 | 
						||
| 
								 | 
							
								]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# Basically this is simple: for PROTOCOL_SSLv23 we turn it into a low of
							 | 
						||
| 
								 | 
							
								# TLSv1 and a high of TLSv1.2. For everything else, we pin to that version.
							 | 
						||
| 
								 | 
							
								# TLSv1 to 1.2 are supported on macOS 10.8+
							 | 
						||
| 
								 | 
							
								_protocol_to_min_max = {
							 | 
						||
| 
								 | 
							
								    util.PROTOCOL_TLS: (SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol12),
							 | 
						||
| 
								 | 
							
								    PROTOCOL_TLS_CLIENT: (SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol12),
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if hasattr(ssl, "PROTOCOL_SSLv2"):
							 | 
						||
| 
								 | 
							
								    _protocol_to_min_max[ssl.PROTOCOL_SSLv2] = (
							 | 
						||
| 
								 | 
							
								        SecurityConst.kSSLProtocol2,
							 | 
						||
| 
								 | 
							
								        SecurityConst.kSSLProtocol2,
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								if hasattr(ssl, "PROTOCOL_SSLv3"):
							 | 
						||
| 
								 | 
							
								    _protocol_to_min_max[ssl.PROTOCOL_SSLv3] = (
							 | 
						||
| 
								 | 
							
								        SecurityConst.kSSLProtocol3,
							 | 
						||
| 
								 | 
							
								        SecurityConst.kSSLProtocol3,
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								if hasattr(ssl, "PROTOCOL_TLSv1"):
							 | 
						||
| 
								 | 
							
								    _protocol_to_min_max[ssl.PROTOCOL_TLSv1] = (
							 | 
						||
| 
								 | 
							
								        SecurityConst.kTLSProtocol1,
							 | 
						||
| 
								 | 
							
								        SecurityConst.kTLSProtocol1,
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								if hasattr(ssl, "PROTOCOL_TLSv1_1"):
							 | 
						||
| 
								 | 
							
								    _protocol_to_min_max[ssl.PROTOCOL_TLSv1_1] = (
							 | 
						||
| 
								 | 
							
								        SecurityConst.kTLSProtocol11,
							 | 
						||
| 
								 | 
							
								        SecurityConst.kTLSProtocol11,
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								if hasattr(ssl, "PROTOCOL_TLSv1_2"):
							 | 
						||
| 
								 | 
							
								    _protocol_to_min_max[ssl.PROTOCOL_TLSv1_2] = (
							 | 
						||
| 
								 | 
							
								        SecurityConst.kTLSProtocol12,
							 | 
						||
| 
								 | 
							
								        SecurityConst.kTLSProtocol12,
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def inject_into_urllib3():
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    Monkey-patch urllib3 with SecureTransport-backed SSL-support.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    util.SSLContext = SecureTransportContext
							 | 
						||
| 
								 | 
							
								    util.ssl_.SSLContext = SecureTransportContext
							 | 
						||
| 
								 | 
							
								    util.HAS_SNI = HAS_SNI
							 | 
						||
| 
								 | 
							
								    util.ssl_.HAS_SNI = HAS_SNI
							 | 
						||
| 
								 | 
							
								    util.IS_SECURETRANSPORT = True
							 | 
						||
| 
								 | 
							
								    util.ssl_.IS_SECURETRANSPORT = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def extract_from_urllib3():
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    Undo monkey-patching by :func:`inject_into_urllib3`.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    util.SSLContext = orig_util_SSLContext
							 | 
						||
| 
								 | 
							
								    util.ssl_.SSLContext = orig_util_SSLContext
							 | 
						||
| 
								 | 
							
								    util.HAS_SNI = orig_util_HAS_SNI
							 | 
						||
| 
								 | 
							
								    util.ssl_.HAS_SNI = orig_util_HAS_SNI
							 | 
						||
| 
								 | 
							
								    util.IS_SECURETRANSPORT = False
							 | 
						||
| 
								 | 
							
								    util.ssl_.IS_SECURETRANSPORT = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _read_callback(connection_id, data_buffer, data_length_pointer):
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    SecureTransport read callback. This is called by ST to request that data
							 | 
						||
| 
								 | 
							
								    be returned from the socket.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    wrapped_socket = None
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								        wrapped_socket = _connection_refs.get(connection_id)
							 | 
						||
| 
								 | 
							
								        if wrapped_socket is None:
							 | 
						||
| 
								 | 
							
								            return SecurityConst.errSSLInternal
							 | 
						||
| 
								 | 
							
								        base_socket = wrapped_socket.socket
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        requested_length = data_length_pointer[0]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        timeout = wrapped_socket.gettimeout()
							 | 
						||
| 
								 | 
							
								        error = None
							 | 
						||
| 
								 | 
							
								        read_count = 0
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            while read_count < requested_length:
							 | 
						||
| 
								 | 
							
								                if timeout is None or timeout >= 0:
							 | 
						||
| 
								 | 
							
								                    if not util.wait_for_read(base_socket, timeout):
							 | 
						||
| 
								 | 
							
								                        raise socket.error(errno.EAGAIN, "timed out")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                remaining = requested_length - read_count
							 | 
						||
| 
								 | 
							
								                buffer = (ctypes.c_char * remaining).from_address(
							 | 
						||
| 
								 | 
							
								                    data_buffer + read_count
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								                chunk_size = base_socket.recv_into(buffer, remaining)
							 | 
						||
| 
								 | 
							
								                read_count += chunk_size
							 | 
						||
| 
								 | 
							
								                if not chunk_size:
							 | 
						||
| 
								 | 
							
								                    if not read_count:
							 | 
						||
| 
								 | 
							
								                        return SecurityConst.errSSLClosedGraceful
							 | 
						||
| 
								 | 
							
								                    break
							 | 
						||
| 
								 | 
							
								        except (socket.error) as e:
							 | 
						||
| 
								 | 
							
								            error = e.errno
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if error is not None and error != errno.EAGAIN:
							 | 
						||
| 
								 | 
							
								                data_length_pointer[0] = read_count
							 | 
						||
| 
								 | 
							
								                if error == errno.ECONNRESET or error == errno.EPIPE:
							 | 
						||
| 
								 | 
							
								                    return SecurityConst.errSSLClosedAbort
							 | 
						||
| 
								 | 
							
								                raise
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        data_length_pointer[0] = read_count
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if read_count != requested_length:
							 | 
						||
| 
								 | 
							
								            return SecurityConst.errSSLWouldBlock
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return 0
							 | 
						||
| 
								 | 
							
								    except Exception as e:
							 | 
						||
| 
								 | 
							
								        if wrapped_socket is not None:
							 | 
						||
| 
								 | 
							
								            wrapped_socket._exception = e
							 | 
						||
| 
								 | 
							
								        return SecurityConst.errSSLInternal
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								def _write_callback(connection_id, data_buffer, data_length_pointer):
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    SecureTransport write callback. This is called by ST to request that data
							 | 
						||
| 
								 | 
							
								    actually be sent on the network.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    wrapped_socket = None
							 | 
						||
| 
								 | 
							
								    try:
							 | 
						||
| 
								 | 
							
								        wrapped_socket = _connection_refs.get(connection_id)
							 | 
						||
| 
								 | 
							
								        if wrapped_socket is None:
							 | 
						||
| 
								 | 
							
								            return SecurityConst.errSSLInternal
							 | 
						||
| 
								 | 
							
								        base_socket = wrapped_socket.socket
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        bytes_to_write = data_length_pointer[0]
							 | 
						||
| 
								 | 
							
								        data = ctypes.string_at(data_buffer, bytes_to_write)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        timeout = wrapped_socket.gettimeout()
							 | 
						||
| 
								 | 
							
								        error = None
							 | 
						||
| 
								 | 
							
								        sent = 0
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            while sent < bytes_to_write:
							 | 
						||
| 
								 | 
							
								                if timeout is None or timeout >= 0:
							 | 
						||
| 
								 | 
							
								                    if not util.wait_for_write(base_socket, timeout):
							 | 
						||
| 
								 | 
							
								                        raise socket.error(errno.EAGAIN, "timed out")
							 | 
						||
| 
								 | 
							
								                chunk_sent = base_socket.send(data)
							 | 
						||
| 
								 | 
							
								                sent += chunk_sent
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                # This has some needless copying here, but I'm not sure there's
							 | 
						||
| 
								 | 
							
								                # much value in optimising this data path.
							 | 
						||
| 
								 | 
							
								                data = data[chunk_sent:]
							 | 
						||
| 
								 | 
							
								        except (socket.error) as e:
							 | 
						||
| 
								 | 
							
								            error = e.errno
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if error is not None and error != errno.EAGAIN:
							 | 
						||
| 
								 | 
							
								                data_length_pointer[0] = sent
							 | 
						||
| 
								 | 
							
								                if error == errno.ECONNRESET or error == errno.EPIPE:
							 | 
						||
| 
								 | 
							
								                    return SecurityConst.errSSLClosedAbort
							 | 
						||
| 
								 | 
							
								                raise
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        data_length_pointer[0] = sent
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if sent != bytes_to_write:
							 | 
						||
| 
								 | 
							
								            return SecurityConst.errSSLWouldBlock
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return 0
							 | 
						||
| 
								 | 
							
								    except Exception as e:
							 | 
						||
| 
								 | 
							
								        if wrapped_socket is not None:
							 | 
						||
| 
								 | 
							
								            wrapped_socket._exception = e
							 | 
						||
| 
								 | 
							
								        return SecurityConst.errSSLInternal
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# We need to keep these two objects references alive: if they get GC'd while
							 | 
						||
| 
								 | 
							
								# in use then SecureTransport could attempt to call a function that is in freed
							 | 
						||
| 
								 | 
							
								# memory. That would be...uh...bad. Yeah, that's the word. Bad.
							 | 
						||
| 
								 | 
							
								_read_callback_pointer = Security.SSLReadFunc(_read_callback)
							 | 
						||
| 
								 | 
							
								_write_callback_pointer = Security.SSLWriteFunc(_write_callback)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class WrappedSocket(object):
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    API-compatibility wrapper for Python's OpenSSL wrapped socket object.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Note: _makefile_refs, _drop(), and _reuse() are needed for the garbage
							 | 
						||
| 
								 | 
							
								    collector of PyPy.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, socket):
							 | 
						||
| 
								 | 
							
								        self.socket = socket
							 | 
						||
| 
								 | 
							
								        self.context = None
							 | 
						||
| 
								 | 
							
								        self._makefile_refs = 0
							 | 
						||
| 
								 | 
							
								        self._closed = False
							 | 
						||
| 
								 | 
							
								        self._exception = None
							 | 
						||
| 
								 | 
							
								        self._keychain = None
							 | 
						||
| 
								 | 
							
								        self._keychain_dir = None
							 | 
						||
| 
								 | 
							
								        self._client_cert_chain = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # We save off the previously-configured timeout and then set it to
							 | 
						||
| 
								 | 
							
								        # zero. This is done because we use select and friends to handle the
							 | 
						||
| 
								 | 
							
								        # timeouts, but if we leave the timeout set on the lower socket then
							 | 
						||
| 
								 | 
							
								        # Python will "kindly" call select on that socket again for us. Avoid
							 | 
						||
| 
								 | 
							
								        # that by forcing the timeout to zero.
							 | 
						||
| 
								 | 
							
								        self._timeout = self.socket.gettimeout()
							 | 
						||
| 
								 | 
							
								        self.socket.settimeout(0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @contextlib.contextmanager
							 | 
						||
| 
								 | 
							
								    def _raise_on_error(self):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        A context manager that can be used to wrap calls that do I/O from
							 | 
						||
| 
								 | 
							
								        SecureTransport. If any of the I/O callbacks hit an exception, this
							 | 
						||
| 
								 | 
							
								        context manager will correctly propagate the exception after the fact.
							 | 
						||
| 
								 | 
							
								        This avoids silently swallowing those exceptions.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        It also correctly forces the socket closed.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        self._exception = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # We explicitly don't catch around this yield because in the unlikely
							 | 
						||
| 
								 | 
							
								        # event that an exception was hit in the block we don't want to swallow
							 | 
						||
| 
								 | 
							
								        # it.
							 | 
						||
| 
								 | 
							
								        yield
							 | 
						||
| 
								 | 
							
								        if self._exception is not None:
							 | 
						||
| 
								 | 
							
								            exception, self._exception = self._exception, None
							 | 
						||
| 
								 | 
							
								            self.close()
							 | 
						||
| 
								 | 
							
								            raise exception
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _set_ciphers(self):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Sets up the allowed ciphers. By default this matches the set in
							 | 
						||
| 
								 | 
							
								        util.ssl_.DEFAULT_CIPHERS, at least as supported by macOS. This is done
							 | 
						||
| 
								 | 
							
								        custom and doesn't allow changing at this time, mostly because parsing
							 | 
						||
| 
								 | 
							
								        OpenSSL cipher strings is going to be a freaking nightmare.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        ciphers = (Security.SSLCipherSuite * len(CIPHER_SUITES))(*CIPHER_SUITES)
							 | 
						||
| 
								 | 
							
								        result = Security.SSLSetEnabledCiphers(
							 | 
						||
| 
								 | 
							
								            self.context, ciphers, len(CIPHER_SUITES)
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        _assert_no_error(result)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _set_alpn_protocols(self, protocols):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Sets up the ALPN protocols on the context.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        if not protocols:
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								        protocols_arr = _create_cfstring_array(protocols)
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            result = Security.SSLSetALPNProtocols(self.context, protocols_arr)
							 | 
						||
| 
								 | 
							
								            _assert_no_error(result)
							 | 
						||
| 
								 | 
							
								        finally:
							 | 
						||
| 
								 | 
							
								            CoreFoundation.CFRelease(protocols_arr)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _custom_validate(self, verify, trust_bundle):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Called when we have set custom validation. We do this in two cases:
							 | 
						||
| 
								 | 
							
								        first, when cert validation is entirely disabled; and second, when
							 | 
						||
| 
								 | 
							
								        using a custom trust DB.
							 | 
						||
| 
								 | 
							
								        Raises an SSLError if the connection is not trusted.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        # If we disabled cert validation, just say: cool.
							 | 
						||
| 
								 | 
							
								        if not verify:
							 | 
						||
| 
								 | 
							
								            return
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        successes = (
							 | 
						||
| 
								 | 
							
								            SecurityConst.kSecTrustResultUnspecified,
							 | 
						||
| 
								 | 
							
								            SecurityConst.kSecTrustResultProceed,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            trust_result = self._evaluate_trust(trust_bundle)
							 | 
						||
| 
								 | 
							
								            if trust_result in successes:
							 | 
						||
| 
								 | 
							
								                return
							 | 
						||
| 
								 | 
							
								            reason = "error code: %d" % (trust_result,)
							 | 
						||
| 
								 | 
							
								        except Exception as e:
							 | 
						||
| 
								 | 
							
								            # Do not trust on error
							 | 
						||
| 
								 | 
							
								            reason = "exception: %r" % (e,)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # SecureTransport does not send an alert nor shuts down the connection.
							 | 
						||
| 
								 | 
							
								        rec = _build_tls_unknown_ca_alert(self.version())
							 | 
						||
| 
								 | 
							
								        self.socket.sendall(rec)
							 | 
						||
| 
								 | 
							
								        # close the connection immediately
							 | 
						||
| 
								 | 
							
								        # l_onoff = 1, activate linger
							 | 
						||
| 
								 | 
							
								        # l_linger = 0, linger for 0 seoncds
							 | 
						||
| 
								 | 
							
								        opts = struct.pack("ii", 1, 0)
							 | 
						||
| 
								 | 
							
								        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, opts)
							 | 
						||
| 
								 | 
							
								        self.close()
							 | 
						||
| 
								 | 
							
								        raise ssl.SSLError("certificate verify failed, %s" % reason)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _evaluate_trust(self, trust_bundle):
							 | 
						||
| 
								 | 
							
								        # We want data in memory, so load it up.
							 | 
						||
| 
								 | 
							
								        if os.path.isfile(trust_bundle):
							 | 
						||
| 
								 | 
							
								            with open(trust_bundle, "rb") as f:
							 | 
						||
| 
								 | 
							
								                trust_bundle = f.read()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        cert_array = None
							 | 
						||
| 
								 | 
							
								        trust = Security.SecTrustRef()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            # Get a CFArray that contains the certs we want.
							 | 
						||
| 
								 | 
							
								            cert_array = _cert_array_from_pem(trust_bundle)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # Ok, now the hard part. We want to get the SecTrustRef that ST has
							 | 
						||
| 
								 | 
							
								            # created for this connection, shove our CAs into it, tell ST to
							 | 
						||
| 
								 | 
							
								            # ignore everything else it knows, and then ask if it can build a
							 | 
						||
| 
								 | 
							
								            # chain. This is a buuuunch of code.
							 | 
						||
| 
								 | 
							
								            result = Security.SSLCopyPeerTrust(self.context, ctypes.byref(trust))
							 | 
						||
| 
								 | 
							
								            _assert_no_error(result)
							 | 
						||
| 
								 | 
							
								            if not trust:
							 | 
						||
| 
								 | 
							
								                raise ssl.SSLError("Failed to copy trust reference")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            result = Security.SecTrustSetAnchorCertificates(trust, cert_array)
							 | 
						||
| 
								 | 
							
								            _assert_no_error(result)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            result = Security.SecTrustSetAnchorCertificatesOnly(trust, True)
							 | 
						||
| 
								 | 
							
								            _assert_no_error(result)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            trust_result = Security.SecTrustResultType()
							 | 
						||
| 
								 | 
							
								            result = Security.SecTrustEvaluate(trust, ctypes.byref(trust_result))
							 | 
						||
| 
								 | 
							
								            _assert_no_error(result)
							 | 
						||
| 
								 | 
							
								        finally:
							 | 
						||
| 
								 | 
							
								            if trust:
							 | 
						||
| 
								 | 
							
								                CoreFoundation.CFRelease(trust)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if cert_array is not None:
							 | 
						||
| 
								 | 
							
								                CoreFoundation.CFRelease(cert_array)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return trust_result.value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def handshake(
							 | 
						||
| 
								 | 
							
								        self,
							 | 
						||
| 
								 | 
							
								        server_hostname,
							 | 
						||
| 
								 | 
							
								        verify,
							 | 
						||
| 
								 | 
							
								        trust_bundle,
							 | 
						||
| 
								 | 
							
								        min_version,
							 | 
						||
| 
								 | 
							
								        max_version,
							 | 
						||
| 
								 | 
							
								        client_cert,
							 | 
						||
| 
								 | 
							
								        client_key,
							 | 
						||
| 
								 | 
							
								        client_key_passphrase,
							 | 
						||
| 
								 | 
							
								        alpn_protocols,
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Actually performs the TLS handshake. This is run automatically by
							 | 
						||
| 
								 | 
							
								        wrapped socket, and shouldn't be needed in user code.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        # First, we do the initial bits of connection setup. We need to create
							 | 
						||
| 
								 | 
							
								        # a context, set its I/O funcs, and set the connection reference.
							 | 
						||
| 
								 | 
							
								        self.context = Security.SSLCreateContext(
							 | 
						||
| 
								 | 
							
								            None, SecurityConst.kSSLClientSide, SecurityConst.kSSLStreamType
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        result = Security.SSLSetIOFuncs(
							 | 
						||
| 
								 | 
							
								            self.context, _read_callback_pointer, _write_callback_pointer
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        _assert_no_error(result)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Here we need to compute the handle to use. We do this by taking the
							 | 
						||
| 
								 | 
							
								        # id of self modulo 2**31 - 1. If this is already in the dictionary, we
							 | 
						||
| 
								 | 
							
								        # just keep incrementing by one until we find a free space.
							 | 
						||
| 
								 | 
							
								        with _connection_ref_lock:
							 | 
						||
| 
								 | 
							
								            handle = id(self) % 2147483647
							 | 
						||
| 
								 | 
							
								            while handle in _connection_refs:
							 | 
						||
| 
								 | 
							
								                handle = (handle + 1) % 2147483647
							 | 
						||
| 
								 | 
							
								            _connection_refs[handle] = self
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        result = Security.SSLSetConnection(self.context, handle)
							 | 
						||
| 
								 | 
							
								        _assert_no_error(result)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # If we have a server hostname, we should set that too.
							 | 
						||
| 
								 | 
							
								        if server_hostname:
							 | 
						||
| 
								 | 
							
								            if not isinstance(server_hostname, bytes):
							 | 
						||
| 
								 | 
							
								                server_hostname = server_hostname.encode("utf-8")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            result = Security.SSLSetPeerDomainName(
							 | 
						||
| 
								 | 
							
								                self.context, server_hostname, len(server_hostname)
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            _assert_no_error(result)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Setup the ciphers.
							 | 
						||
| 
								 | 
							
								        self._set_ciphers()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Setup the ALPN protocols.
							 | 
						||
| 
								 | 
							
								        self._set_alpn_protocols(alpn_protocols)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Set the minimum and maximum TLS versions.
							 | 
						||
| 
								 | 
							
								        result = Security.SSLSetProtocolVersionMin(self.context, min_version)
							 | 
						||
| 
								 | 
							
								        _assert_no_error(result)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        result = Security.SSLSetProtocolVersionMax(self.context, max_version)
							 | 
						||
| 
								 | 
							
								        _assert_no_error(result)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # If there's a trust DB, we need to use it. We do that by telling
							 | 
						||
| 
								 | 
							
								        # SecureTransport to break on server auth. We also do that if we don't
							 | 
						||
| 
								 | 
							
								        # want to validate the certs at all: we just won't actually do any
							 | 
						||
| 
								 | 
							
								        # authing in that case.
							 | 
						||
| 
								 | 
							
								        if not verify or trust_bundle is not None:
							 | 
						||
| 
								 | 
							
								            result = Security.SSLSetSessionOption(
							 | 
						||
| 
								 | 
							
								                self.context, SecurityConst.kSSLSessionOptionBreakOnServerAuth, True
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            _assert_no_error(result)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # If there's a client cert, we need to use it.
							 | 
						||
| 
								 | 
							
								        if client_cert:
							 | 
						||
| 
								 | 
							
								            self._keychain, self._keychain_dir = _temporary_keychain()
							 | 
						||
| 
								 | 
							
								            self._client_cert_chain = _load_client_cert_chain(
							 | 
						||
| 
								 | 
							
								                self._keychain, client_cert, client_key
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            result = Security.SSLSetCertificate(self.context, self._client_cert_chain)
							 | 
						||
| 
								 | 
							
								            _assert_no_error(result)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        while True:
							 | 
						||
| 
								 | 
							
								            with self._raise_on_error():
							 | 
						||
| 
								 | 
							
								                result = Security.SSLHandshake(self.context)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if result == SecurityConst.errSSLWouldBlock:
							 | 
						||
| 
								 | 
							
								                    raise socket.timeout("handshake timed out")
							 | 
						||
| 
								 | 
							
								                elif result == SecurityConst.errSSLServerAuthCompleted:
							 | 
						||
| 
								 | 
							
								                    self._custom_validate(verify, trust_bundle)
							 | 
						||
| 
								 | 
							
								                    continue
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    _assert_no_error(result)
							 | 
						||
| 
								 | 
							
								                    break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def fileno(self):
							 | 
						||
| 
								 | 
							
								        return self.socket.fileno()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # Copy-pasted from Python 3.5 source code
							 | 
						||
| 
								 | 
							
								    def _decref_socketios(self):
							 | 
						||
| 
								 | 
							
								        if self._makefile_refs > 0:
							 | 
						||
| 
								 | 
							
								            self._makefile_refs -= 1
							 | 
						||
| 
								 | 
							
								        if self._closed:
							 | 
						||
| 
								 | 
							
								            self.close()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def recv(self, bufsiz):
							 | 
						||
| 
								 | 
							
								        buffer = ctypes.create_string_buffer(bufsiz)
							 | 
						||
| 
								 | 
							
								        bytes_read = self.recv_into(buffer, bufsiz)
							 | 
						||
| 
								 | 
							
								        data = buffer[:bytes_read]
							 | 
						||
| 
								 | 
							
								        return data
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def recv_into(self, buffer, nbytes=None):
							 | 
						||
| 
								 | 
							
								        # Read short on EOF.
							 | 
						||
| 
								 | 
							
								        if self._closed:
							 | 
						||
| 
								 | 
							
								            return 0
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if nbytes is None:
							 | 
						||
| 
								 | 
							
								            nbytes = len(buffer)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        buffer = (ctypes.c_char * nbytes).from_buffer(buffer)
							 | 
						||
| 
								 | 
							
								        processed_bytes = ctypes.c_size_t(0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        with self._raise_on_error():
							 | 
						||
| 
								 | 
							
								            result = Security.SSLRead(
							 | 
						||
| 
								 | 
							
								                self.context, buffer, nbytes, ctypes.byref(processed_bytes)
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # There are some result codes that we want to treat as "not always
							 | 
						||
| 
								 | 
							
								        # errors". Specifically, those are errSSLWouldBlock,
							 | 
						||
| 
								 | 
							
								        # errSSLClosedGraceful, and errSSLClosedNoNotify.
							 | 
						||
| 
								 | 
							
								        if result == SecurityConst.errSSLWouldBlock:
							 | 
						||
| 
								 | 
							
								            # If we didn't process any bytes, then this was just a time out.
							 | 
						||
| 
								 | 
							
								            # However, we can get errSSLWouldBlock in situations when we *did*
							 | 
						||
| 
								 | 
							
								            # read some data, and in those cases we should just read "short"
							 | 
						||
| 
								 | 
							
								            # and return.
							 | 
						||
| 
								 | 
							
								            if processed_bytes.value == 0:
							 | 
						||
| 
								 | 
							
								                # Timed out, no data read.
							 | 
						||
| 
								 | 
							
								                raise socket.timeout("recv timed out")
							 | 
						||
| 
								 | 
							
								        elif result in (
							 | 
						||
| 
								 | 
							
								            SecurityConst.errSSLClosedGraceful,
							 | 
						||
| 
								 | 
							
								            SecurityConst.errSSLClosedNoNotify,
							 | 
						||
| 
								 | 
							
								        ):
							 | 
						||
| 
								 | 
							
								            # The remote peer has closed this connection. We should do so as
							 | 
						||
| 
								 | 
							
								            # well. Note that we don't actually return here because in
							 | 
						||
| 
								 | 
							
								            # principle this could actually be fired along with return data.
							 | 
						||
| 
								 | 
							
								            # It's unlikely though.
							 | 
						||
| 
								 | 
							
								            self.close()
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            _assert_no_error(result)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Ok, we read and probably succeeded. We should return whatever data
							 | 
						||
| 
								 | 
							
								        # was actually read.
							 | 
						||
| 
								 | 
							
								        return processed_bytes.value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def settimeout(self, timeout):
							 | 
						||
| 
								 | 
							
								        self._timeout = timeout
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def gettimeout(self):
							 | 
						||
| 
								 | 
							
								        return self._timeout
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def send(self, data):
							 | 
						||
| 
								 | 
							
								        processed_bytes = ctypes.c_size_t(0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        with self._raise_on_error():
							 | 
						||
| 
								 | 
							
								            result = Security.SSLWrite(
							 | 
						||
| 
								 | 
							
								                self.context, data, len(data), ctypes.byref(processed_bytes)
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if result == SecurityConst.errSSLWouldBlock and processed_bytes.value == 0:
							 | 
						||
| 
								 | 
							
								            # Timed out
							 | 
						||
| 
								 | 
							
								            raise socket.timeout("send timed out")
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            _assert_no_error(result)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # We sent, and probably succeeded. Tell them how much we sent.
							 | 
						||
| 
								 | 
							
								        return processed_bytes.value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def sendall(self, data):
							 | 
						||
| 
								 | 
							
								        total_sent = 0
							 | 
						||
| 
								 | 
							
								        while total_sent < len(data):
							 | 
						||
| 
								 | 
							
								            sent = self.send(data[total_sent : total_sent + SSL_WRITE_BLOCKSIZE])
							 | 
						||
| 
								 | 
							
								            total_sent += sent
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def shutdown(self):
							 | 
						||
| 
								 | 
							
								        with self._raise_on_error():
							 | 
						||
| 
								 | 
							
								            Security.SSLClose(self.context)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def close(self):
							 | 
						||
| 
								 | 
							
								        # TODO: should I do clean shutdown here? Do I have to?
							 | 
						||
| 
								 | 
							
								        if self._makefile_refs < 1:
							 | 
						||
| 
								 | 
							
								            self._closed = True
							 | 
						||
| 
								 | 
							
								            if self.context:
							 | 
						||
| 
								 | 
							
								                CoreFoundation.CFRelease(self.context)
							 | 
						||
| 
								 | 
							
								                self.context = None
							 | 
						||
| 
								 | 
							
								            if self._client_cert_chain:
							 | 
						||
| 
								 | 
							
								                CoreFoundation.CFRelease(self._client_cert_chain)
							 | 
						||
| 
								 | 
							
								                self._client_cert_chain = None
							 | 
						||
| 
								 | 
							
								            if self._keychain:
							 | 
						||
| 
								 | 
							
								                Security.SecKeychainDelete(self._keychain)
							 | 
						||
| 
								 | 
							
								                CoreFoundation.CFRelease(self._keychain)
							 | 
						||
| 
								 | 
							
								                shutil.rmtree(self._keychain_dir)
							 | 
						||
| 
								 | 
							
								                self._keychain = self._keychain_dir = None
							 | 
						||
| 
								 | 
							
								            return self.socket.close()
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            self._makefile_refs -= 1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def getpeercert(self, binary_form=False):
							 | 
						||
| 
								 | 
							
								        # Urgh, annoying.
							 | 
						||
| 
								 | 
							
								        #
							 | 
						||
| 
								 | 
							
								        # Here's how we do this:
							 | 
						||
| 
								 | 
							
								        #
							 | 
						||
| 
								 | 
							
								        # 1. Call SSLCopyPeerTrust to get hold of the trust object for this
							 | 
						||
| 
								 | 
							
								        #    connection.
							 | 
						||
| 
								 | 
							
								        # 2. Call SecTrustGetCertificateAtIndex for index 0 to get the leaf.
							 | 
						||
| 
								 | 
							
								        # 3. To get the CN, call SecCertificateCopyCommonName and process that
							 | 
						||
| 
								 | 
							
								        #    string so that it's of the appropriate type.
							 | 
						||
| 
								 | 
							
								        # 4. To get the SAN, we need to do something a bit more complex:
							 | 
						||
| 
								 | 
							
								        #    a. Call SecCertificateCopyValues to get the data, requesting
							 | 
						||
| 
								 | 
							
								        #       kSecOIDSubjectAltName.
							 | 
						||
| 
								 | 
							
								        #    b. Mess about with this dictionary to try to get the SANs out.
							 | 
						||
| 
								 | 
							
								        #
							 | 
						||
| 
								 | 
							
								        # This is gross. Really gross. It's going to be a few hundred LoC extra
							 | 
						||
| 
								 | 
							
								        # just to repeat something that SecureTransport can *already do*. So my
							 | 
						||
| 
								 | 
							
								        # operating assumption at this time is that what we want to do is
							 | 
						||
| 
								 | 
							
								        # instead to just flag to urllib3 that it shouldn't do its own hostname
							 | 
						||
| 
								 | 
							
								        # validation when using SecureTransport.
							 | 
						||
| 
								 | 
							
								        if not binary_form:
							 | 
						||
| 
								 | 
							
								            raise ValueError("SecureTransport only supports dumping binary certs")
							 | 
						||
| 
								 | 
							
								        trust = Security.SecTrustRef()
							 | 
						||
| 
								 | 
							
								        certdata = None
							 | 
						||
| 
								 | 
							
								        der_bytes = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            # Grab the trust store.
							 | 
						||
| 
								 | 
							
								            result = Security.SSLCopyPeerTrust(self.context, ctypes.byref(trust))
							 | 
						||
| 
								 | 
							
								            _assert_no_error(result)
							 | 
						||
| 
								 | 
							
								            if not trust:
							 | 
						||
| 
								 | 
							
								                # Probably we haven't done the handshake yet. No biggie.
							 | 
						||
| 
								 | 
							
								                return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            cert_count = Security.SecTrustGetCertificateCount(trust)
							 | 
						||
| 
								 | 
							
								            if not cert_count:
							 | 
						||
| 
								 | 
							
								                # Also a case that might happen if we haven't handshaked.
							 | 
						||
| 
								 | 
							
								                # Handshook? Handshaken?
							 | 
						||
| 
								 | 
							
								                return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            leaf = Security.SecTrustGetCertificateAtIndex(trust, 0)
							 | 
						||
| 
								 | 
							
								            assert leaf
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # Ok, now we want the DER bytes.
							 | 
						||
| 
								 | 
							
								            certdata = Security.SecCertificateCopyData(leaf)
							 | 
						||
| 
								 | 
							
								            assert certdata
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            data_length = CoreFoundation.CFDataGetLength(certdata)
							 | 
						||
| 
								 | 
							
								            data_buffer = CoreFoundation.CFDataGetBytePtr(certdata)
							 | 
						||
| 
								 | 
							
								            der_bytes = ctypes.string_at(data_buffer, data_length)
							 | 
						||
| 
								 | 
							
								        finally:
							 | 
						||
| 
								 | 
							
								            if certdata:
							 | 
						||
| 
								 | 
							
								                CoreFoundation.CFRelease(certdata)
							 | 
						||
| 
								 | 
							
								            if trust:
							 | 
						||
| 
								 | 
							
								                CoreFoundation.CFRelease(trust)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return der_bytes
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def version(self):
							 | 
						||
| 
								 | 
							
								        protocol = Security.SSLProtocol()
							 | 
						||
| 
								 | 
							
								        result = Security.SSLGetNegotiatedProtocolVersion(
							 | 
						||
| 
								 | 
							
								            self.context, ctypes.byref(protocol)
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        _assert_no_error(result)
							 | 
						||
| 
								 | 
							
								        if protocol.value == SecurityConst.kTLSProtocol13:
							 | 
						||
| 
								 | 
							
								            raise ssl.SSLError("SecureTransport does not support TLS 1.3")
							 | 
						||
| 
								 | 
							
								        elif protocol.value == SecurityConst.kTLSProtocol12:
							 | 
						||
| 
								 | 
							
								            return "TLSv1.2"
							 | 
						||
| 
								 | 
							
								        elif protocol.value == SecurityConst.kTLSProtocol11:
							 | 
						||
| 
								 | 
							
								            return "TLSv1.1"
							 | 
						||
| 
								 | 
							
								        elif protocol.value == SecurityConst.kTLSProtocol1:
							 | 
						||
| 
								 | 
							
								            return "TLSv1"
							 | 
						||
| 
								 | 
							
								        elif protocol.value == SecurityConst.kSSLProtocol3:
							 | 
						||
| 
								 | 
							
								            return "SSLv3"
							 | 
						||
| 
								 | 
							
								        elif protocol.value == SecurityConst.kSSLProtocol2:
							 | 
						||
| 
								 | 
							
								            return "SSLv2"
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            raise ssl.SSLError("Unknown TLS version: %r" % protocol)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _reuse(self):
							 | 
						||
| 
								 | 
							
								        self._makefile_refs += 1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def _drop(self):
							 | 
						||
| 
								 | 
							
								        if self._makefile_refs < 1:
							 | 
						||
| 
								 | 
							
								            self.close()
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            self._makefile_refs -= 1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if _fileobject:  # Platform-specific: Python 2
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def makefile(self, mode, bufsize=-1):
							 | 
						||
| 
								 | 
							
								        self._makefile_refs += 1
							 | 
						||
| 
								 | 
							
								        return _fileobject(self, mode, bufsize, close=True)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								else:  # Platform-specific: Python 3
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def makefile(self, mode="r", buffering=None, *args, **kwargs):
							 | 
						||
| 
								 | 
							
								        # We disable buffering with SecureTransport because it conflicts with
							 | 
						||
| 
								 | 
							
								        # the buffering that ST does internally (see issue #1153 for more).
							 | 
						||
| 
								 | 
							
								        buffering = 0
							 | 
						||
| 
								 | 
							
								        return backport_makefile(self, mode, buffering, *args, **kwargs)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								WrappedSocket.makefile = makefile
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class SecureTransportContext(object):
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								    I am a wrapper class for the SecureTransport library, to translate the
							 | 
						||
| 
								 | 
							
								    interface of the standard library ``SSLContext`` object to calls into
							 | 
						||
| 
								 | 
							
								    SecureTransport.
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, protocol):
							 | 
						||
| 
								 | 
							
								        self._min_version, self._max_version = _protocol_to_min_max[protocol]
							 | 
						||
| 
								 | 
							
								        self._options = 0
							 | 
						||
| 
								 | 
							
								        self._verify = False
							 | 
						||
| 
								 | 
							
								        self._trust_bundle = None
							 | 
						||
| 
								 | 
							
								        self._client_cert = None
							 | 
						||
| 
								 | 
							
								        self._client_key = None
							 | 
						||
| 
								 | 
							
								        self._client_key_passphrase = None
							 | 
						||
| 
								 | 
							
								        self._alpn_protocols = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def check_hostname(self):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        SecureTransport cannot have its hostname checking disabled. For more,
							 | 
						||
| 
								 | 
							
								        see the comment on getpeercert() in this file.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        return True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @check_hostname.setter
							 | 
						||
| 
								 | 
							
								    def check_hostname(self, value):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        SecureTransport cannot have its hostname checking disabled. For more,
							 | 
						||
| 
								 | 
							
								        see the comment on getpeercert() in this file.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        pass
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def options(self):
							 | 
						||
| 
								 | 
							
								        # TODO: Well, crap.
							 | 
						||
| 
								 | 
							
								        #
							 | 
						||
| 
								 | 
							
								        # So this is the bit of the code that is the most likely to cause us
							 | 
						||
| 
								 | 
							
								        # trouble. Essentially we need to enumerate all of the SSL options that
							 | 
						||
| 
								 | 
							
								        # users might want to use and try to see if we can sensibly translate
							 | 
						||
| 
								 | 
							
								        # them, or whether we should just ignore them.
							 | 
						||
| 
								 | 
							
								        return self._options
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @options.setter
							 | 
						||
| 
								 | 
							
								    def options(self, value):
							 | 
						||
| 
								 | 
							
								        # TODO: Update in line with above.
							 | 
						||
| 
								 | 
							
								        self._options = value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @property
							 | 
						||
| 
								 | 
							
								    def verify_mode(self):
							 | 
						||
| 
								 | 
							
								        return ssl.CERT_REQUIRED if self._verify else ssl.CERT_NONE
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @verify_mode.setter
							 | 
						||
| 
								 | 
							
								    def verify_mode(self, value):
							 | 
						||
| 
								 | 
							
								        self._verify = True if value == ssl.CERT_REQUIRED else False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def set_default_verify_paths(self):
							 | 
						||
| 
								 | 
							
								        # So, this has to do something a bit weird. Specifically, what it does
							 | 
						||
| 
								 | 
							
								        # is nothing.
							 | 
						||
| 
								 | 
							
								        #
							 | 
						||
| 
								 | 
							
								        # This means that, if we had previously had load_verify_locations
							 | 
						||
| 
								 | 
							
								        # called, this does not undo that. We need to do that because it turns
							 | 
						||
| 
								 | 
							
								        # out that the rest of the urllib3 code will attempt to load the
							 | 
						||
| 
								 | 
							
								        # default verify paths if it hasn't been told about any paths, even if
							 | 
						||
| 
								 | 
							
								        # the context itself was sometime earlier. We resolve that by just
							 | 
						||
| 
								 | 
							
								        # ignoring it.
							 | 
						||
| 
								 | 
							
								        pass
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def load_default_certs(self):
							 | 
						||
| 
								 | 
							
								        return self.set_default_verify_paths()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def set_ciphers(self, ciphers):
							 | 
						||
| 
								 | 
							
								        # For now, we just require the default cipher string.
							 | 
						||
| 
								 | 
							
								        if ciphers != util.ssl_.DEFAULT_CIPHERS:
							 | 
						||
| 
								 | 
							
								            raise ValueError("SecureTransport doesn't support custom cipher strings")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def load_verify_locations(self, cafile=None, capath=None, cadata=None):
							 | 
						||
| 
								 | 
							
								        # OK, we only really support cadata and cafile.
							 | 
						||
| 
								 | 
							
								        if capath is not None:
							 | 
						||
| 
								 | 
							
								            raise ValueError("SecureTransport does not support cert directories")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Raise if cafile does not exist.
							 | 
						||
| 
								 | 
							
								        if cafile is not None:
							 | 
						||
| 
								 | 
							
								            with open(cafile):
							 | 
						||
| 
								 | 
							
								                pass
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self._trust_bundle = cafile or cadata
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def load_cert_chain(self, certfile, keyfile=None, password=None):
							 | 
						||
| 
								 | 
							
								        self._client_cert = certfile
							 | 
						||
| 
								 | 
							
								        self._client_key = keyfile
							 | 
						||
| 
								 | 
							
								        self._client_cert_passphrase = password
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def set_alpn_protocols(self, protocols):
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        Sets the ALPN protocols that will later be set on the context.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Raises a NotImplementedError if ALPN is not supported.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        if not hasattr(Security, "SSLSetALPNProtocols"):
							 | 
						||
| 
								 | 
							
								            raise NotImplementedError(
							 | 
						||
| 
								 | 
							
								                "SecureTransport supports ALPN only in macOS 10.12+"
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        self._alpn_protocols = [six.ensure_binary(p) for p in protocols]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def wrap_socket(
							 | 
						||
| 
								 | 
							
								        self,
							 | 
						||
| 
								 | 
							
								        sock,
							 | 
						||
| 
								 | 
							
								        server_side=False,
							 | 
						||
| 
								 | 
							
								        do_handshake_on_connect=True,
							 | 
						||
| 
								 | 
							
								        suppress_ragged_eofs=True,
							 | 
						||
| 
								 | 
							
								        server_hostname=None,
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								        # So, what do we do here? Firstly, we assert some properties. This is a
							 | 
						||
| 
								 | 
							
								        # stripped down shim, so there is some functionality we don't support.
							 | 
						||
| 
								 | 
							
								        # See PEP 543 for the real deal.
							 | 
						||
| 
								 | 
							
								        assert not server_side
							 | 
						||
| 
								 | 
							
								        assert do_handshake_on_connect
							 | 
						||
| 
								 | 
							
								        assert suppress_ragged_eofs
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Ok, we're good to go. Now we want to create the wrapped socket object
							 | 
						||
| 
								 | 
							
								        # and store it in the appropriate place.
							 | 
						||
| 
								 | 
							
								        wrapped_socket = WrappedSocket(sock)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Now we can handshake
							 | 
						||
| 
								 | 
							
								        wrapped_socket.handshake(
							 | 
						||
| 
								 | 
							
								            server_hostname,
							 | 
						||
| 
								 | 
							
								            self._verify,
							 | 
						||
| 
								 | 
							
								            self._trust_bundle,
							 | 
						||
| 
								 | 
							
								            self._min_version,
							 | 
						||
| 
								 | 
							
								            self._max_version,
							 | 
						||
| 
								 | 
							
								            self._client_cert,
							 | 
						||
| 
								 | 
							
								            self._client_key,
							 | 
						||
| 
								 | 
							
								            self._client_key_passphrase,
							 | 
						||
| 
								 | 
							
								            self._alpn_protocols,
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								        return wrapped_socket
							 |