Path: blob/master/venv/Lib/site-packages/urllib3/contrib/securetransport.py
811 views
"""1SecureTranport support for urllib3 via ctypes.23This makes platform-native TLS available to urllib3 users on macOS without the4use of a compiler. This is an important feature because the Python Package5Index is moving to become a TLSv1.2-or-higher server, and the default OpenSSL6that ships with macOS is not capable of doing TLSv1.2. The only way to resolve7this is to give macOS users an alternative solution to the problem, and that8solution is to use SecureTransport.910We use ctypes here because this solution must not require a compiler. That's11because pip is not allowed to require a compiler either.1213This is not intended to be a seriously long-term solution to this problem.14The hope is that PEP 543 will eventually solve this issue for us, at which15point we can retire this contrib module. But in the short term, we need to16solve the impending tire fire that is Python on Mac without this kind of17contrib module. So...here we are.1819To use this module, simply import and inject it::2021import urllib3.contrib.securetransport22urllib3.contrib.securetransport.inject_into_urllib3()2324Happy TLSing!2526This code is a bastardised version of the code found in Will Bond's oscrypto27library. An enormous debt is owed to him for blazing this trail for us. For28that reason, this code should be considered to be covered both by urllib3's29license and by oscrypto's:3031Copyright (c) 2015-2016 Will Bond <[email protected]>3233Permission is hereby granted, free of charge, to any person obtaining a34copy of this software and associated documentation files (the "Software"),35to deal in the Software without restriction, including without limitation36the rights to use, copy, modify, merge, publish, distribute, sublicense,37and/or sell copies of the Software, and to permit persons to whom the38Software is furnished to do so, subject to the following conditions:3940The above copyright notice and this permission notice shall be included in41all copies or substantial portions of the Software.4243THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR44IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,45FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE46AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER47LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING48FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER49DEALINGS IN THE SOFTWARE.50"""51from __future__ import absolute_import5253import contextlib54import ctypes55import errno56import os.path57import shutil58import socket59import ssl60import threading61import weakref6263from .. import util64from ._securetransport.bindings import Security, SecurityConst, CoreFoundation65from ._securetransport.low_level import (66_assert_no_error,67_cert_array_from_pem,68_temporary_keychain,69_load_client_cert_chain,70)7172try: # Platform-specific: Python 273from socket import _fileobject74except ImportError: # Platform-specific: Python 375_fileobject = None76from ..packages.backports.makefile import backport_makefile7778__all__ = ["inject_into_urllib3", "extract_from_urllib3"]7980# SNI always works81HAS_SNI = True8283orig_util_HAS_SNI = util.HAS_SNI84orig_util_SSLContext = util.ssl_.SSLContext8586# This dictionary is used by the read callback to obtain a handle to the87# calling wrapped socket. This is a pretty silly approach, but for now it'll88# do. I feel like I should be able to smuggle a handle to the wrapped socket89# directly in the SSLConnectionRef, but for now this approach will work I90# guess.91#92# We need to lock around this structure for inserts, but we don't do it for93# reads/writes in the callbacks. The reasoning here goes as follows:94#95# 1. It is not possible to call into the callbacks before the dictionary is96# populated, so once in the callback the id must be in the dictionary.97# 2. The callbacks don't mutate the dictionary, they only read from it, and98# so cannot conflict with any of the insertions.99#100# This is good: if we had to lock in the callbacks we'd drastically slow down101# the performance of this code.102_connection_refs = weakref.WeakValueDictionary()103_connection_ref_lock = threading.Lock()104105# Limit writes to 16kB. This is OpenSSL's limit, but we'll cargo-cult it over106# for no better reason than we need *a* limit, and this one is right there.107SSL_WRITE_BLOCKSIZE = 16384108109# This is our equivalent of util.ssl_.DEFAULT_CIPHERS, but expanded out to110# individual cipher suites. We need to do this because this is how111# SecureTransport wants them.112CIPHER_SUITES = [113SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,114SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,115SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,116SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,117SecurityConst.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,118SecurityConst.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,119SecurityConst.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,120SecurityConst.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,121SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,122SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,123SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,124SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,125SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,126SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,127SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,128SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,129SecurityConst.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,130SecurityConst.TLS_DHE_RSA_WITH_AES_256_CBC_SHA,131SecurityConst.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,132SecurityConst.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,133SecurityConst.TLS_AES_256_GCM_SHA384,134SecurityConst.TLS_AES_128_GCM_SHA256,135SecurityConst.TLS_RSA_WITH_AES_256_GCM_SHA384,136SecurityConst.TLS_RSA_WITH_AES_128_GCM_SHA256,137SecurityConst.TLS_AES_128_CCM_8_SHA256,138SecurityConst.TLS_AES_128_CCM_SHA256,139SecurityConst.TLS_RSA_WITH_AES_256_CBC_SHA256,140SecurityConst.TLS_RSA_WITH_AES_128_CBC_SHA256,141SecurityConst.TLS_RSA_WITH_AES_256_CBC_SHA,142SecurityConst.TLS_RSA_WITH_AES_128_CBC_SHA,143]144145# Basically this is simple: for PROTOCOL_SSLv23 we turn it into a low of146# TLSv1 and a high of TLSv1.2. For everything else, we pin to that version.147# TLSv1 to 1.2 are supported on macOS 10.8+148_protocol_to_min_max = {149util.PROTOCOL_TLS: (SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol12)150}151152if hasattr(ssl, "PROTOCOL_SSLv2"):153_protocol_to_min_max[ssl.PROTOCOL_SSLv2] = (154SecurityConst.kSSLProtocol2,155SecurityConst.kSSLProtocol2,156)157if hasattr(ssl, "PROTOCOL_SSLv3"):158_protocol_to_min_max[ssl.PROTOCOL_SSLv3] = (159SecurityConst.kSSLProtocol3,160SecurityConst.kSSLProtocol3,161)162if hasattr(ssl, "PROTOCOL_TLSv1"):163_protocol_to_min_max[ssl.PROTOCOL_TLSv1] = (164SecurityConst.kTLSProtocol1,165SecurityConst.kTLSProtocol1,166)167if hasattr(ssl, "PROTOCOL_TLSv1_1"):168_protocol_to_min_max[ssl.PROTOCOL_TLSv1_1] = (169SecurityConst.kTLSProtocol11,170SecurityConst.kTLSProtocol11,171)172if hasattr(ssl, "PROTOCOL_TLSv1_2"):173_protocol_to_min_max[ssl.PROTOCOL_TLSv1_2] = (174SecurityConst.kTLSProtocol12,175SecurityConst.kTLSProtocol12,176)177178179def inject_into_urllib3():180"""181Monkey-patch urllib3 with SecureTransport-backed SSL-support.182"""183util.SSLContext = SecureTransportContext184util.ssl_.SSLContext = SecureTransportContext185util.HAS_SNI = HAS_SNI186util.ssl_.HAS_SNI = HAS_SNI187util.IS_SECURETRANSPORT = True188util.ssl_.IS_SECURETRANSPORT = True189190191def extract_from_urllib3():192"""193Undo monkey-patching by :func:`inject_into_urllib3`.194"""195util.SSLContext = orig_util_SSLContext196util.ssl_.SSLContext = orig_util_SSLContext197util.HAS_SNI = orig_util_HAS_SNI198util.ssl_.HAS_SNI = orig_util_HAS_SNI199util.IS_SECURETRANSPORT = False200util.ssl_.IS_SECURETRANSPORT = False201202203def _read_callback(connection_id, data_buffer, data_length_pointer):204"""205SecureTransport read callback. This is called by ST to request that data206be returned from the socket.207"""208wrapped_socket = None209try:210wrapped_socket = _connection_refs.get(connection_id)211if wrapped_socket is None:212return SecurityConst.errSSLInternal213base_socket = wrapped_socket.socket214215requested_length = data_length_pointer[0]216217timeout = wrapped_socket.gettimeout()218error = None219read_count = 0220221try:222while read_count < requested_length:223if timeout is None or timeout >= 0:224if not util.wait_for_read(base_socket, timeout):225raise socket.error(errno.EAGAIN, "timed out")226227remaining = requested_length - read_count228buffer = (ctypes.c_char * remaining).from_address(229data_buffer + read_count230)231chunk_size = base_socket.recv_into(buffer, remaining)232read_count += chunk_size233if not chunk_size:234if not read_count:235return SecurityConst.errSSLClosedGraceful236break237except (socket.error) as e:238error = e.errno239240if error is not None and error != errno.EAGAIN:241data_length_pointer[0] = read_count242if error == errno.ECONNRESET or error == errno.EPIPE:243return SecurityConst.errSSLClosedAbort244raise245246data_length_pointer[0] = read_count247248if read_count != requested_length:249return SecurityConst.errSSLWouldBlock250251return 0252except Exception as e:253if wrapped_socket is not None:254wrapped_socket._exception = e255return SecurityConst.errSSLInternal256257258def _write_callback(connection_id, data_buffer, data_length_pointer):259"""260SecureTransport write callback. This is called by ST to request that data261actually be sent on the network.262"""263wrapped_socket = None264try:265wrapped_socket = _connection_refs.get(connection_id)266if wrapped_socket is None:267return SecurityConst.errSSLInternal268base_socket = wrapped_socket.socket269270bytes_to_write = data_length_pointer[0]271data = ctypes.string_at(data_buffer, bytes_to_write)272273timeout = wrapped_socket.gettimeout()274error = None275sent = 0276277try:278while sent < bytes_to_write:279if timeout is None or timeout >= 0:280if not util.wait_for_write(base_socket, timeout):281raise socket.error(errno.EAGAIN, "timed out")282chunk_sent = base_socket.send(data)283sent += chunk_sent284285# This has some needless copying here, but I'm not sure there's286# much value in optimising this data path.287data = data[chunk_sent:]288except (socket.error) as e:289error = e.errno290291if error is not None and error != errno.EAGAIN:292data_length_pointer[0] = sent293if error == errno.ECONNRESET or error == errno.EPIPE:294return SecurityConst.errSSLClosedAbort295raise296297data_length_pointer[0] = sent298299if sent != bytes_to_write:300return SecurityConst.errSSLWouldBlock301302return 0303except Exception as e:304if wrapped_socket is not None:305wrapped_socket._exception = e306return SecurityConst.errSSLInternal307308309# We need to keep these two objects references alive: if they get GC'd while310# in use then SecureTransport could attempt to call a function that is in freed311# memory. That would be...uh...bad. Yeah, that's the word. Bad.312_read_callback_pointer = Security.SSLReadFunc(_read_callback)313_write_callback_pointer = Security.SSLWriteFunc(_write_callback)314315316class WrappedSocket(object):317"""318API-compatibility wrapper for Python's OpenSSL wrapped socket object.319320Note: _makefile_refs, _drop(), and _reuse() are needed for the garbage321collector of PyPy.322"""323324def __init__(self, socket):325self.socket = socket326self.context = None327self._makefile_refs = 0328self._closed = False329self._exception = None330self._keychain = None331self._keychain_dir = None332self._client_cert_chain = None333334# We save off the previously-configured timeout and then set it to335# zero. This is done because we use select and friends to handle the336# timeouts, but if we leave the timeout set on the lower socket then337# Python will "kindly" call select on that socket again for us. Avoid338# that by forcing the timeout to zero.339self._timeout = self.socket.gettimeout()340self.socket.settimeout(0)341342@contextlib.contextmanager343def _raise_on_error(self):344"""345A context manager that can be used to wrap calls that do I/O from346SecureTransport. If any of the I/O callbacks hit an exception, this347context manager will correctly propagate the exception after the fact.348This avoids silently swallowing those exceptions.349350It also correctly forces the socket closed.351"""352self._exception = None353354# We explicitly don't catch around this yield because in the unlikely355# event that an exception was hit in the block we don't want to swallow356# it.357yield358if self._exception is not None:359exception, self._exception = self._exception, None360self.close()361raise exception362363def _set_ciphers(self):364"""365Sets up the allowed ciphers. By default this matches the set in366util.ssl_.DEFAULT_CIPHERS, at least as supported by macOS. This is done367custom and doesn't allow changing at this time, mostly because parsing368OpenSSL cipher strings is going to be a freaking nightmare.369"""370ciphers = (Security.SSLCipherSuite * len(CIPHER_SUITES))(*CIPHER_SUITES)371result = Security.SSLSetEnabledCiphers(372self.context, ciphers, len(CIPHER_SUITES)373)374_assert_no_error(result)375376def _custom_validate(self, verify, trust_bundle):377"""378Called when we have set custom validation. We do this in two cases:379first, when cert validation is entirely disabled; and second, when380using a custom trust DB.381"""382# If we disabled cert validation, just say: cool.383if not verify:384return385386# We want data in memory, so load it up.387if os.path.isfile(trust_bundle):388with open(trust_bundle, "rb") as f:389trust_bundle = f.read()390391cert_array = None392trust = Security.SecTrustRef()393394try:395# Get a CFArray that contains the certs we want.396cert_array = _cert_array_from_pem(trust_bundle)397398# Ok, now the hard part. We want to get the SecTrustRef that ST has399# created for this connection, shove our CAs into it, tell ST to400# ignore everything else it knows, and then ask if it can build a401# chain. This is a buuuunch of code.402result = Security.SSLCopyPeerTrust(self.context, ctypes.byref(trust))403_assert_no_error(result)404if not trust:405raise ssl.SSLError("Failed to copy trust reference")406407result = Security.SecTrustSetAnchorCertificates(trust, cert_array)408_assert_no_error(result)409410result = Security.SecTrustSetAnchorCertificatesOnly(trust, True)411_assert_no_error(result)412413trust_result = Security.SecTrustResultType()414result = Security.SecTrustEvaluate(trust, ctypes.byref(trust_result))415_assert_no_error(result)416finally:417if trust:418CoreFoundation.CFRelease(trust)419420if cert_array is not None:421CoreFoundation.CFRelease(cert_array)422423# Ok, now we can look at what the result was.424successes = (425SecurityConst.kSecTrustResultUnspecified,426SecurityConst.kSecTrustResultProceed,427)428if trust_result.value not in successes:429raise ssl.SSLError(430"certificate verify failed, error code: %d" % trust_result.value431)432433def handshake(434self,435server_hostname,436verify,437trust_bundle,438min_version,439max_version,440client_cert,441client_key,442client_key_passphrase,443):444"""445Actually performs the TLS handshake. This is run automatically by446wrapped socket, and shouldn't be needed in user code.447"""448# First, we do the initial bits of connection setup. We need to create449# a context, set its I/O funcs, and set the connection reference.450self.context = Security.SSLCreateContext(451None, SecurityConst.kSSLClientSide, SecurityConst.kSSLStreamType452)453result = Security.SSLSetIOFuncs(454self.context, _read_callback_pointer, _write_callback_pointer455)456_assert_no_error(result)457458# Here we need to compute the handle to use. We do this by taking the459# id of self modulo 2**31 - 1. If this is already in the dictionary, we460# just keep incrementing by one until we find a free space.461with _connection_ref_lock:462handle = id(self) % 2147483647463while handle in _connection_refs:464handle = (handle + 1) % 2147483647465_connection_refs[handle] = self466467result = Security.SSLSetConnection(self.context, handle)468_assert_no_error(result)469470# If we have a server hostname, we should set that too.471if server_hostname:472if not isinstance(server_hostname, bytes):473server_hostname = server_hostname.encode("utf-8")474475result = Security.SSLSetPeerDomainName(476self.context, server_hostname, len(server_hostname)477)478_assert_no_error(result)479480# Setup the ciphers.481self._set_ciphers()482483# Set the minimum and maximum TLS versions.484result = Security.SSLSetProtocolVersionMin(self.context, min_version)485_assert_no_error(result)486487result = Security.SSLSetProtocolVersionMax(self.context, max_version)488_assert_no_error(result)489490# If there's a trust DB, we need to use it. We do that by telling491# SecureTransport to break on server auth. We also do that if we don't492# want to validate the certs at all: we just won't actually do any493# authing in that case.494if not verify or trust_bundle is not None:495result = Security.SSLSetSessionOption(496self.context, SecurityConst.kSSLSessionOptionBreakOnServerAuth, True497)498_assert_no_error(result)499500# If there's a client cert, we need to use it.501if client_cert:502self._keychain, self._keychain_dir = _temporary_keychain()503self._client_cert_chain = _load_client_cert_chain(504self._keychain, client_cert, client_key505)506result = Security.SSLSetCertificate(self.context, self._client_cert_chain)507_assert_no_error(result)508509while True:510with self._raise_on_error():511result = Security.SSLHandshake(self.context)512513if result == SecurityConst.errSSLWouldBlock:514raise socket.timeout("handshake timed out")515elif result == SecurityConst.errSSLServerAuthCompleted:516self._custom_validate(verify, trust_bundle)517continue518else:519_assert_no_error(result)520break521522def fileno(self):523return self.socket.fileno()524525# Copy-pasted from Python 3.5 source code526def _decref_socketios(self):527if self._makefile_refs > 0:528self._makefile_refs -= 1529if self._closed:530self.close()531532def recv(self, bufsiz):533buffer = ctypes.create_string_buffer(bufsiz)534bytes_read = self.recv_into(buffer, bufsiz)535data = buffer[:bytes_read]536return data537538def recv_into(self, buffer, nbytes=None):539# Read short on EOF.540if self._closed:541return 0542543if nbytes is None:544nbytes = len(buffer)545546buffer = (ctypes.c_char * nbytes).from_buffer(buffer)547processed_bytes = ctypes.c_size_t(0)548549with self._raise_on_error():550result = Security.SSLRead(551self.context, buffer, nbytes, ctypes.byref(processed_bytes)552)553554# There are some result codes that we want to treat as "not always555# errors". Specifically, those are errSSLWouldBlock,556# errSSLClosedGraceful, and errSSLClosedNoNotify.557if result == SecurityConst.errSSLWouldBlock:558# If we didn't process any bytes, then this was just a time out.559# However, we can get errSSLWouldBlock in situations when we *did*560# read some data, and in those cases we should just read "short"561# and return.562if processed_bytes.value == 0:563# Timed out, no data read.564raise socket.timeout("recv timed out")565elif result in (566SecurityConst.errSSLClosedGraceful,567SecurityConst.errSSLClosedNoNotify,568):569# The remote peer has closed this connection. We should do so as570# well. Note that we don't actually return here because in571# principle this could actually be fired along with return data.572# It's unlikely though.573self.close()574else:575_assert_no_error(result)576577# Ok, we read and probably succeeded. We should return whatever data578# was actually read.579return processed_bytes.value580581def settimeout(self, timeout):582self._timeout = timeout583584def gettimeout(self):585return self._timeout586587def send(self, data):588processed_bytes = ctypes.c_size_t(0)589590with self._raise_on_error():591result = Security.SSLWrite(592self.context, data, len(data), ctypes.byref(processed_bytes)593)594595if result == SecurityConst.errSSLWouldBlock and processed_bytes.value == 0:596# Timed out597raise socket.timeout("send timed out")598else:599_assert_no_error(result)600601# We sent, and probably succeeded. Tell them how much we sent.602return processed_bytes.value603604def sendall(self, data):605total_sent = 0606while total_sent < len(data):607sent = self.send(data[total_sent : total_sent + SSL_WRITE_BLOCKSIZE])608total_sent += sent609610def shutdown(self):611with self._raise_on_error():612Security.SSLClose(self.context)613614def close(self):615# TODO: should I do clean shutdown here? Do I have to?616if self._makefile_refs < 1:617self._closed = True618if self.context:619CoreFoundation.CFRelease(self.context)620self.context = None621if self._client_cert_chain:622CoreFoundation.CFRelease(self._client_cert_chain)623self._client_cert_chain = None624if self._keychain:625Security.SecKeychainDelete(self._keychain)626CoreFoundation.CFRelease(self._keychain)627shutil.rmtree(self._keychain_dir)628self._keychain = self._keychain_dir = None629return self.socket.close()630else:631self._makefile_refs -= 1632633def getpeercert(self, binary_form=False):634# Urgh, annoying.635#636# Here's how we do this:637#638# 1. Call SSLCopyPeerTrust to get hold of the trust object for this639# connection.640# 2. Call SecTrustGetCertificateAtIndex for index 0 to get the leaf.641# 3. To get the CN, call SecCertificateCopyCommonName and process that642# string so that it's of the appropriate type.643# 4. To get the SAN, we need to do something a bit more complex:644# a. Call SecCertificateCopyValues to get the data, requesting645# kSecOIDSubjectAltName.646# b. Mess about with this dictionary to try to get the SANs out.647#648# This is gross. Really gross. It's going to be a few hundred LoC extra649# just to repeat something that SecureTransport can *already do*. So my650# operating assumption at this time is that what we want to do is651# instead to just flag to urllib3 that it shouldn't do its own hostname652# validation when using SecureTransport.653if not binary_form:654raise ValueError("SecureTransport only supports dumping binary certs")655trust = Security.SecTrustRef()656certdata = None657der_bytes = None658659try:660# Grab the trust store.661result = Security.SSLCopyPeerTrust(self.context, ctypes.byref(trust))662_assert_no_error(result)663if not trust:664# Probably we haven't done the handshake yet. No biggie.665return None666667cert_count = Security.SecTrustGetCertificateCount(trust)668if not cert_count:669# Also a case that might happen if we haven't handshaked.670# Handshook? Handshaken?671return None672673leaf = Security.SecTrustGetCertificateAtIndex(trust, 0)674assert leaf675676# Ok, now we want the DER bytes.677certdata = Security.SecCertificateCopyData(leaf)678assert certdata679680data_length = CoreFoundation.CFDataGetLength(certdata)681data_buffer = CoreFoundation.CFDataGetBytePtr(certdata)682der_bytes = ctypes.string_at(data_buffer, data_length)683finally:684if certdata:685CoreFoundation.CFRelease(certdata)686if trust:687CoreFoundation.CFRelease(trust)688689return der_bytes690691def version(self):692protocol = Security.SSLProtocol()693result = Security.SSLGetNegotiatedProtocolVersion(694self.context, ctypes.byref(protocol)695)696_assert_no_error(result)697if protocol.value == SecurityConst.kTLSProtocol13:698raise ssl.SSLError("SecureTransport does not support TLS 1.3")699elif protocol.value == SecurityConst.kTLSProtocol12:700return "TLSv1.2"701elif protocol.value == SecurityConst.kTLSProtocol11:702return "TLSv1.1"703elif protocol.value == SecurityConst.kTLSProtocol1:704return "TLSv1"705elif protocol.value == SecurityConst.kSSLProtocol3:706return "SSLv3"707elif protocol.value == SecurityConst.kSSLProtocol2:708return "SSLv2"709else:710raise ssl.SSLError("Unknown TLS version: %r" % protocol)711712def _reuse(self):713self._makefile_refs += 1714715def _drop(self):716if self._makefile_refs < 1:717self.close()718else:719self._makefile_refs -= 1720721722if _fileobject: # Platform-specific: Python 2723724def makefile(self, mode, bufsize=-1):725self._makefile_refs += 1726return _fileobject(self, mode, bufsize, close=True)727728729else: # Platform-specific: Python 3730731def makefile(self, mode="r", buffering=None, *args, **kwargs):732# We disable buffering with SecureTransport because it conflicts with733# the buffering that ST does internally (see issue #1153 for more).734buffering = 0735return backport_makefile(self, mode, buffering, *args, **kwargs)736737738WrappedSocket.makefile = makefile739740741class SecureTransportContext(object):742"""743I am a wrapper class for the SecureTransport library, to translate the744interface of the standard library ``SSLContext`` object to calls into745SecureTransport.746"""747748def __init__(self, protocol):749self._min_version, self._max_version = _protocol_to_min_max[protocol]750self._options = 0751self._verify = False752self._trust_bundle = None753self._client_cert = None754self._client_key = None755self._client_key_passphrase = None756757@property758def check_hostname(self):759"""760SecureTransport cannot have its hostname checking disabled. For more,761see the comment on getpeercert() in this file.762"""763return True764765@check_hostname.setter766def check_hostname(self, value):767"""768SecureTransport cannot have its hostname checking disabled. For more,769see the comment on getpeercert() in this file.770"""771pass772773@property774def options(self):775# TODO: Well, crap.776#777# So this is the bit of the code that is the most likely to cause us778# trouble. Essentially we need to enumerate all of the SSL options that779# users might want to use and try to see if we can sensibly translate780# them, or whether we should just ignore them.781return self._options782783@options.setter784def options(self, value):785# TODO: Update in line with above.786self._options = value787788@property789def verify_mode(self):790return ssl.CERT_REQUIRED if self._verify else ssl.CERT_NONE791792@verify_mode.setter793def verify_mode(self, value):794self._verify = True if value == ssl.CERT_REQUIRED else False795796def set_default_verify_paths(self):797# So, this has to do something a bit weird. Specifically, what it does798# is nothing.799#800# This means that, if we had previously had load_verify_locations801# called, this does not undo that. We need to do that because it turns802# out that the rest of the urllib3 code will attempt to load the803# default verify paths if it hasn't been told about any paths, even if804# the context itself was sometime earlier. We resolve that by just805# ignoring it.806pass807808def load_default_certs(self):809return self.set_default_verify_paths()810811def set_ciphers(self, ciphers):812# For now, we just require the default cipher string.813if ciphers != util.ssl_.DEFAULT_CIPHERS:814raise ValueError("SecureTransport doesn't support custom cipher strings")815816def load_verify_locations(self, cafile=None, capath=None, cadata=None):817# OK, we only really support cadata and cafile.818if capath is not None:819raise ValueError("SecureTransport does not support cert directories")820821# Raise if cafile does not exist.822if cafile is not None:823with open(cafile):824pass825826self._trust_bundle = cafile or cadata827828def load_cert_chain(self, certfile, keyfile=None, password=None):829self._client_cert = certfile830self._client_key = keyfile831self._client_cert_passphrase = password832833def wrap_socket(834self,835sock,836server_side=False,837do_handshake_on_connect=True,838suppress_ragged_eofs=True,839server_hostname=None,840):841# So, what do we do here? Firstly, we assert some properties. This is a842# stripped down shim, so there is some functionality we don't support.843# See PEP 543 for the real deal.844assert not server_side845assert do_handshake_on_connect846assert suppress_ragged_eofs847848# Ok, we're good to go. Now we want to create the wrapped socket object849# and store it in the appropriate place.850wrapped_socket = WrappedSocket(sock)851852# Now we can handshake853wrapped_socket.handshake(854server_hostname,855self._verify,856self._trust_bundle,857self._min_version,858self._max_version,859self._client_cert,860self._client_key,861self._client_key_passphrase,862)863return wrapped_socket864865866