Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/urllib3/contrib/securetransport.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
| author | guerler |
|---|---|
| date | Fri, 31 Jul 2020 00:32:28 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 0:d30785e31577 | 1:56ad4e20f292 |
|---|---|
| 1 """ | |
| 2 SecureTranport support for urllib3 via ctypes. | |
| 3 | |
| 4 This makes platform-native TLS available to urllib3 users on macOS without the | |
| 5 use of a compiler. This is an important feature because the Python Package | |
| 6 Index is moving to become a TLSv1.2-or-higher server, and the default OpenSSL | |
| 7 that ships with macOS is not capable of doing TLSv1.2. The only way to resolve | |
| 8 this is to give macOS users an alternative solution to the problem, and that | |
| 9 solution is to use SecureTransport. | |
| 10 | |
| 11 We use ctypes here because this solution must not require a compiler. That's | |
| 12 because pip is not allowed to require a compiler either. | |
| 13 | |
| 14 This is not intended to be a seriously long-term solution to this problem. | |
| 15 The hope is that PEP 543 will eventually solve this issue for us, at which | |
| 16 point we can retire this contrib module. But in the short term, we need to | |
| 17 solve the impending tire fire that is Python on Mac without this kind of | |
| 18 contrib module. So...here we are. | |
| 19 | |
| 20 To use this module, simply import and inject it:: | |
| 21 | |
| 22 import urllib3.contrib.securetransport | |
| 23 urllib3.contrib.securetransport.inject_into_urllib3() | |
| 24 | |
| 25 Happy TLSing! | |
| 26 | |
| 27 This code is a bastardised version of the code found in Will Bond's oscrypto | |
| 28 library. An enormous debt is owed to him for blazing this trail for us. For | |
| 29 that reason, this code should be considered to be covered both by urllib3's | |
| 30 license and by oscrypto's: | |
| 31 | |
| 32 Copyright (c) 2015-2016 Will Bond <will@wbond.net> | |
| 33 | |
| 34 Permission is hereby granted, free of charge, to any person obtaining a | |
| 35 copy of this software and associated documentation files (the "Software"), | |
| 36 to deal in the Software without restriction, including without limitation | |
| 37 the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| 38 and/or sell copies of the Software, and to permit persons to whom the | |
| 39 Software is furnished to do so, subject to the following conditions: | |
| 40 | |
| 41 The above copyright notice and this permission notice shall be included in | |
| 42 all copies or substantial portions of the Software. | |
| 43 | |
| 44 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 45 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 46 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 47 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 48 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
| 49 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
| 50 DEALINGS IN THE SOFTWARE. | |
| 51 """ | |
| 52 from __future__ import absolute_import | |
| 53 | |
| 54 import contextlib | |
| 55 import ctypes | |
| 56 import errno | |
| 57 import os.path | |
| 58 import shutil | |
| 59 import socket | |
| 60 import ssl | |
| 61 import threading | |
| 62 import weakref | |
| 63 | |
| 64 from .. import util | |
| 65 from ._securetransport.bindings import Security, SecurityConst, CoreFoundation | |
| 66 from ._securetransport.low_level import ( | |
| 67 _assert_no_error, | |
| 68 _cert_array_from_pem, | |
| 69 _temporary_keychain, | |
| 70 _load_client_cert_chain, | |
| 71 ) | |
| 72 | |
| 73 try: # Platform-specific: Python 2 | |
| 74 from socket import _fileobject | |
| 75 except ImportError: # Platform-specific: Python 3 | |
| 76 _fileobject = None | |
| 77 from ..packages.backports.makefile import backport_makefile | |
| 78 | |
| 79 __all__ = ["inject_into_urllib3", "extract_from_urllib3"] | |
| 80 | |
| 81 # SNI always works | |
| 82 HAS_SNI = True | |
| 83 | |
| 84 orig_util_HAS_SNI = util.HAS_SNI | |
| 85 orig_util_SSLContext = util.ssl_.SSLContext | |
| 86 | |
| 87 # This dictionary is used by the read callback to obtain a handle to the | |
| 88 # calling wrapped socket. This is a pretty silly approach, but for now it'll | |
| 89 # do. I feel like I should be able to smuggle a handle to the wrapped socket | |
| 90 # directly in the SSLConnectionRef, but for now this approach will work I | |
| 91 # guess. | |
| 92 # | |
| 93 # We need to lock around this structure for inserts, but we don't do it for | |
| 94 # reads/writes in the callbacks. The reasoning here goes as follows: | |
| 95 # | |
| 96 # 1. It is not possible to call into the callbacks before the dictionary is | |
| 97 # populated, so once in the callback the id must be in the dictionary. | |
| 98 # 2. The callbacks don't mutate the dictionary, they only read from it, and | |
| 99 # so cannot conflict with any of the insertions. | |
| 100 # | |
| 101 # This is good: if we had to lock in the callbacks we'd drastically slow down | |
| 102 # the performance of this code. | |
| 103 _connection_refs = weakref.WeakValueDictionary() | |
| 104 _connection_ref_lock = threading.Lock() | |
| 105 | |
| 106 # Limit writes to 16kB. This is OpenSSL's limit, but we'll cargo-cult it over | |
| 107 # for no better reason than we need *a* limit, and this one is right there. | |
| 108 SSL_WRITE_BLOCKSIZE = 16384 | |
| 109 | |
| 110 # This is our equivalent of util.ssl_.DEFAULT_CIPHERS, but expanded out to | |
| 111 # individual cipher suites. We need to do this because this is how | |
| 112 # SecureTransport wants them. | |
| 113 CIPHER_SUITES = [ | |
| 114 SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, | |
| 115 SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, | |
| 116 SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, | |
| 117 SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, | |
| 118 SecurityConst.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, | |
| 119 SecurityConst.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, | |
| 120 SecurityConst.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, | |
| 121 SecurityConst.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, | |
| 122 SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, | |
| 123 SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, | |
| 124 SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, | |
| 125 SecurityConst.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, | |
| 126 SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, | |
| 127 SecurityConst.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, | |
| 128 SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, | |
| 129 SecurityConst.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, | |
| 130 SecurityConst.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, | |
| 131 SecurityConst.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, | |
| 132 SecurityConst.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, | |
| 133 SecurityConst.TLS_DHE_RSA_WITH_AES_128_CBC_SHA, | |
| 134 SecurityConst.TLS_AES_256_GCM_SHA384, | |
| 135 SecurityConst.TLS_AES_128_GCM_SHA256, | |
| 136 SecurityConst.TLS_RSA_WITH_AES_256_GCM_SHA384, | |
| 137 SecurityConst.TLS_RSA_WITH_AES_128_GCM_SHA256, | |
| 138 SecurityConst.TLS_AES_128_CCM_8_SHA256, | |
| 139 SecurityConst.TLS_AES_128_CCM_SHA256, | |
| 140 SecurityConst.TLS_RSA_WITH_AES_256_CBC_SHA256, | |
| 141 SecurityConst.TLS_RSA_WITH_AES_128_CBC_SHA256, | |
| 142 SecurityConst.TLS_RSA_WITH_AES_256_CBC_SHA, | |
| 143 SecurityConst.TLS_RSA_WITH_AES_128_CBC_SHA, | |
| 144 ] | |
| 145 | |
| 146 # Basically this is simple: for PROTOCOL_SSLv23 we turn it into a low of | |
| 147 # TLSv1 and a high of TLSv1.2. For everything else, we pin to that version. | |
| 148 # TLSv1 to 1.2 are supported on macOS 10.8+ | |
| 149 _protocol_to_min_max = { | |
| 150 util.PROTOCOL_TLS: (SecurityConst.kTLSProtocol1, SecurityConst.kTLSProtocol12) | |
| 151 } | |
| 152 | |
| 153 if hasattr(ssl, "PROTOCOL_SSLv2"): | |
| 154 _protocol_to_min_max[ssl.PROTOCOL_SSLv2] = ( | |
| 155 SecurityConst.kSSLProtocol2, | |
| 156 SecurityConst.kSSLProtocol2, | |
| 157 ) | |
| 158 if hasattr(ssl, "PROTOCOL_SSLv3"): | |
| 159 _protocol_to_min_max[ssl.PROTOCOL_SSLv3] = ( | |
| 160 SecurityConst.kSSLProtocol3, | |
| 161 SecurityConst.kSSLProtocol3, | |
| 162 ) | |
| 163 if hasattr(ssl, "PROTOCOL_TLSv1"): | |
| 164 _protocol_to_min_max[ssl.PROTOCOL_TLSv1] = ( | |
| 165 SecurityConst.kTLSProtocol1, | |
| 166 SecurityConst.kTLSProtocol1, | |
| 167 ) | |
| 168 if hasattr(ssl, "PROTOCOL_TLSv1_1"): | |
| 169 _protocol_to_min_max[ssl.PROTOCOL_TLSv1_1] = ( | |
| 170 SecurityConst.kTLSProtocol11, | |
| 171 SecurityConst.kTLSProtocol11, | |
| 172 ) | |
| 173 if hasattr(ssl, "PROTOCOL_TLSv1_2"): | |
| 174 _protocol_to_min_max[ssl.PROTOCOL_TLSv1_2] = ( | |
| 175 SecurityConst.kTLSProtocol12, | |
| 176 SecurityConst.kTLSProtocol12, | |
| 177 ) | |
| 178 | |
| 179 | |
| 180 def inject_into_urllib3(): | |
| 181 """ | |
| 182 Monkey-patch urllib3 with SecureTransport-backed SSL-support. | |
| 183 """ | |
| 184 util.SSLContext = SecureTransportContext | |
| 185 util.ssl_.SSLContext = SecureTransportContext | |
| 186 util.HAS_SNI = HAS_SNI | |
| 187 util.ssl_.HAS_SNI = HAS_SNI | |
| 188 util.IS_SECURETRANSPORT = True | |
| 189 util.ssl_.IS_SECURETRANSPORT = True | |
| 190 | |
| 191 | |
| 192 def extract_from_urllib3(): | |
| 193 """ | |
| 194 Undo monkey-patching by :func:`inject_into_urllib3`. | |
| 195 """ | |
| 196 util.SSLContext = orig_util_SSLContext | |
| 197 util.ssl_.SSLContext = orig_util_SSLContext | |
| 198 util.HAS_SNI = orig_util_HAS_SNI | |
| 199 util.ssl_.HAS_SNI = orig_util_HAS_SNI | |
| 200 util.IS_SECURETRANSPORT = False | |
| 201 util.ssl_.IS_SECURETRANSPORT = False | |
| 202 | |
| 203 | |
| 204 def _read_callback(connection_id, data_buffer, data_length_pointer): | |
| 205 """ | |
| 206 SecureTransport read callback. This is called by ST to request that data | |
| 207 be returned from the socket. | |
| 208 """ | |
| 209 wrapped_socket = None | |
| 210 try: | |
| 211 wrapped_socket = _connection_refs.get(connection_id) | |
| 212 if wrapped_socket is None: | |
| 213 return SecurityConst.errSSLInternal | |
| 214 base_socket = wrapped_socket.socket | |
| 215 | |
| 216 requested_length = data_length_pointer[0] | |
| 217 | |
| 218 timeout = wrapped_socket.gettimeout() | |
| 219 error = None | |
| 220 read_count = 0 | |
| 221 | |
| 222 try: | |
| 223 while read_count < requested_length: | |
| 224 if timeout is None or timeout >= 0: | |
| 225 if not util.wait_for_read(base_socket, timeout): | |
| 226 raise socket.error(errno.EAGAIN, "timed out") | |
| 227 | |
| 228 remaining = requested_length - read_count | |
| 229 buffer = (ctypes.c_char * remaining).from_address( | |
| 230 data_buffer + read_count | |
| 231 ) | |
| 232 chunk_size = base_socket.recv_into(buffer, remaining) | |
| 233 read_count += chunk_size | |
| 234 if not chunk_size: | |
| 235 if not read_count: | |
| 236 return SecurityConst.errSSLClosedGraceful | |
| 237 break | |
| 238 except (socket.error) as e: | |
| 239 error = e.errno | |
| 240 | |
| 241 if error is not None and error != errno.EAGAIN: | |
| 242 data_length_pointer[0] = read_count | |
| 243 if error == errno.ECONNRESET or error == errno.EPIPE: | |
| 244 return SecurityConst.errSSLClosedAbort | |
| 245 raise | |
| 246 | |
| 247 data_length_pointer[0] = read_count | |
| 248 | |
| 249 if read_count != requested_length: | |
| 250 return SecurityConst.errSSLWouldBlock | |
| 251 | |
| 252 return 0 | |
| 253 except Exception as e: | |
| 254 if wrapped_socket is not None: | |
| 255 wrapped_socket._exception = e | |
| 256 return SecurityConst.errSSLInternal | |
| 257 | |
| 258 | |
| 259 def _write_callback(connection_id, data_buffer, data_length_pointer): | |
| 260 """ | |
| 261 SecureTransport write callback. This is called by ST to request that data | |
| 262 actually be sent on the network. | |
| 263 """ | |
| 264 wrapped_socket = None | |
| 265 try: | |
| 266 wrapped_socket = _connection_refs.get(connection_id) | |
| 267 if wrapped_socket is None: | |
| 268 return SecurityConst.errSSLInternal | |
| 269 base_socket = wrapped_socket.socket | |
| 270 | |
| 271 bytes_to_write = data_length_pointer[0] | |
| 272 data = ctypes.string_at(data_buffer, bytes_to_write) | |
| 273 | |
| 274 timeout = wrapped_socket.gettimeout() | |
| 275 error = None | |
| 276 sent = 0 | |
| 277 | |
| 278 try: | |
| 279 while sent < bytes_to_write: | |
| 280 if timeout is None or timeout >= 0: | |
| 281 if not util.wait_for_write(base_socket, timeout): | |
| 282 raise socket.error(errno.EAGAIN, "timed out") | |
| 283 chunk_sent = base_socket.send(data) | |
| 284 sent += chunk_sent | |
| 285 | |
| 286 # This has some needless copying here, but I'm not sure there's | |
| 287 # much value in optimising this data path. | |
| 288 data = data[chunk_sent:] | |
| 289 except (socket.error) as e: | |
| 290 error = e.errno | |
| 291 | |
| 292 if error is not None and error != errno.EAGAIN: | |
| 293 data_length_pointer[0] = sent | |
| 294 if error == errno.ECONNRESET or error == errno.EPIPE: | |
| 295 return SecurityConst.errSSLClosedAbort | |
| 296 raise | |
| 297 | |
| 298 data_length_pointer[0] = sent | |
| 299 | |
| 300 if sent != bytes_to_write: | |
| 301 return SecurityConst.errSSLWouldBlock | |
| 302 | |
| 303 return 0 | |
| 304 except Exception as e: | |
| 305 if wrapped_socket is not None: | |
| 306 wrapped_socket._exception = e | |
| 307 return SecurityConst.errSSLInternal | |
| 308 | |
| 309 | |
| 310 # We need to keep these two objects references alive: if they get GC'd while | |
| 311 # in use then SecureTransport could attempt to call a function that is in freed | |
| 312 # memory. That would be...uh...bad. Yeah, that's the word. Bad. | |
| 313 _read_callback_pointer = Security.SSLReadFunc(_read_callback) | |
| 314 _write_callback_pointer = Security.SSLWriteFunc(_write_callback) | |
| 315 | |
| 316 | |
| 317 class WrappedSocket(object): | |
| 318 """ | |
| 319 API-compatibility wrapper for Python's OpenSSL wrapped socket object. | |
| 320 | |
| 321 Note: _makefile_refs, _drop(), and _reuse() are needed for the garbage | |
| 322 collector of PyPy. | |
| 323 """ | |
| 324 | |
| 325 def __init__(self, socket): | |
| 326 self.socket = socket | |
| 327 self.context = None | |
| 328 self._makefile_refs = 0 | |
| 329 self._closed = False | |
| 330 self._exception = None | |
| 331 self._keychain = None | |
| 332 self._keychain_dir = None | |
| 333 self._client_cert_chain = None | |
| 334 | |
| 335 # We save off the previously-configured timeout and then set it to | |
| 336 # zero. This is done because we use select and friends to handle the | |
| 337 # timeouts, but if we leave the timeout set on the lower socket then | |
| 338 # Python will "kindly" call select on that socket again for us. Avoid | |
| 339 # that by forcing the timeout to zero. | |
| 340 self._timeout = self.socket.gettimeout() | |
| 341 self.socket.settimeout(0) | |
| 342 | |
| 343 @contextlib.contextmanager | |
| 344 def _raise_on_error(self): | |
| 345 """ | |
| 346 A context manager that can be used to wrap calls that do I/O from | |
| 347 SecureTransport. If any of the I/O callbacks hit an exception, this | |
| 348 context manager will correctly propagate the exception after the fact. | |
| 349 This avoids silently swallowing those exceptions. | |
| 350 | |
| 351 It also correctly forces the socket closed. | |
| 352 """ | |
| 353 self._exception = None | |
| 354 | |
| 355 # We explicitly don't catch around this yield because in the unlikely | |
| 356 # event that an exception was hit in the block we don't want to swallow | |
| 357 # it. | |
| 358 yield | |
| 359 if self._exception is not None: | |
| 360 exception, self._exception = self._exception, None | |
| 361 self.close() | |
| 362 raise exception | |
| 363 | |
| 364 def _set_ciphers(self): | |
| 365 """ | |
| 366 Sets up the allowed ciphers. By default this matches the set in | |
| 367 util.ssl_.DEFAULT_CIPHERS, at least as supported by macOS. This is done | |
| 368 custom and doesn't allow changing at this time, mostly because parsing | |
| 369 OpenSSL cipher strings is going to be a freaking nightmare. | |
| 370 """ | |
| 371 ciphers = (Security.SSLCipherSuite * len(CIPHER_SUITES))(*CIPHER_SUITES) | |
| 372 result = Security.SSLSetEnabledCiphers( | |
| 373 self.context, ciphers, len(CIPHER_SUITES) | |
| 374 ) | |
| 375 _assert_no_error(result) | |
| 376 | |
| 377 def _custom_validate(self, verify, trust_bundle): | |
| 378 """ | |
| 379 Called when we have set custom validation. We do this in two cases: | |
| 380 first, when cert validation is entirely disabled; and second, when | |
| 381 using a custom trust DB. | |
| 382 """ | |
| 383 # If we disabled cert validation, just say: cool. | |
| 384 if not verify: | |
| 385 return | |
| 386 | |
| 387 # We want data in memory, so load it up. | |
| 388 if os.path.isfile(trust_bundle): | |
| 389 with open(trust_bundle, "rb") as f: | |
| 390 trust_bundle = f.read() | |
| 391 | |
| 392 cert_array = None | |
| 393 trust = Security.SecTrustRef() | |
| 394 | |
| 395 try: | |
| 396 # Get a CFArray that contains the certs we want. | |
| 397 cert_array = _cert_array_from_pem(trust_bundle) | |
| 398 | |
| 399 # Ok, now the hard part. We want to get the SecTrustRef that ST has | |
| 400 # created for this connection, shove our CAs into it, tell ST to | |
| 401 # ignore everything else it knows, and then ask if it can build a | |
| 402 # chain. This is a buuuunch of code. | |
| 403 result = Security.SSLCopyPeerTrust(self.context, ctypes.byref(trust)) | |
| 404 _assert_no_error(result) | |
| 405 if not trust: | |
| 406 raise ssl.SSLError("Failed to copy trust reference") | |
| 407 | |
| 408 result = Security.SecTrustSetAnchorCertificates(trust, cert_array) | |
| 409 _assert_no_error(result) | |
| 410 | |
| 411 result = Security.SecTrustSetAnchorCertificatesOnly(trust, True) | |
| 412 _assert_no_error(result) | |
| 413 | |
| 414 trust_result = Security.SecTrustResultType() | |
| 415 result = Security.SecTrustEvaluate(trust, ctypes.byref(trust_result)) | |
| 416 _assert_no_error(result) | |
| 417 finally: | |
| 418 if trust: | |
| 419 CoreFoundation.CFRelease(trust) | |
| 420 | |
| 421 if cert_array is not None: | |
| 422 CoreFoundation.CFRelease(cert_array) | |
| 423 | |
| 424 # Ok, now we can look at what the result was. | |
| 425 successes = ( | |
| 426 SecurityConst.kSecTrustResultUnspecified, | |
| 427 SecurityConst.kSecTrustResultProceed, | |
| 428 ) | |
| 429 if trust_result.value not in successes: | |
| 430 raise ssl.SSLError( | |
| 431 "certificate verify failed, error code: %d" % trust_result.value | |
| 432 ) | |
| 433 | |
| 434 def handshake( | |
| 435 self, | |
| 436 server_hostname, | |
| 437 verify, | |
| 438 trust_bundle, | |
| 439 min_version, | |
| 440 max_version, | |
| 441 client_cert, | |
| 442 client_key, | |
| 443 client_key_passphrase, | |
| 444 ): | |
| 445 """ | |
| 446 Actually performs the TLS handshake. This is run automatically by | |
| 447 wrapped socket, and shouldn't be needed in user code. | |
| 448 """ | |
| 449 # First, we do the initial bits of connection setup. We need to create | |
| 450 # a context, set its I/O funcs, and set the connection reference. | |
| 451 self.context = Security.SSLCreateContext( | |
| 452 None, SecurityConst.kSSLClientSide, SecurityConst.kSSLStreamType | |
| 453 ) | |
| 454 result = Security.SSLSetIOFuncs( | |
| 455 self.context, _read_callback_pointer, _write_callback_pointer | |
| 456 ) | |
| 457 _assert_no_error(result) | |
| 458 | |
| 459 # Here we need to compute the handle to use. We do this by taking the | |
| 460 # id of self modulo 2**31 - 1. If this is already in the dictionary, we | |
| 461 # just keep incrementing by one until we find a free space. | |
| 462 with _connection_ref_lock: | |
| 463 handle = id(self) % 2147483647 | |
| 464 while handle in _connection_refs: | |
| 465 handle = (handle + 1) % 2147483647 | |
| 466 _connection_refs[handle] = self | |
| 467 | |
| 468 result = Security.SSLSetConnection(self.context, handle) | |
| 469 _assert_no_error(result) | |
| 470 | |
| 471 # If we have a server hostname, we should set that too. | |
| 472 if server_hostname: | |
| 473 if not isinstance(server_hostname, bytes): | |
| 474 server_hostname = server_hostname.encode("utf-8") | |
| 475 | |
| 476 result = Security.SSLSetPeerDomainName( | |
| 477 self.context, server_hostname, len(server_hostname) | |
| 478 ) | |
| 479 _assert_no_error(result) | |
| 480 | |
| 481 # Setup the ciphers. | |
| 482 self._set_ciphers() | |
| 483 | |
| 484 # Set the minimum and maximum TLS versions. | |
| 485 result = Security.SSLSetProtocolVersionMin(self.context, min_version) | |
| 486 _assert_no_error(result) | |
| 487 | |
| 488 result = Security.SSLSetProtocolVersionMax(self.context, max_version) | |
| 489 _assert_no_error(result) | |
| 490 | |
| 491 # If there's a trust DB, we need to use it. We do that by telling | |
| 492 # SecureTransport to break on server auth. We also do that if we don't | |
| 493 # want to validate the certs at all: we just won't actually do any | |
| 494 # authing in that case. | |
| 495 if not verify or trust_bundle is not None: | |
| 496 result = Security.SSLSetSessionOption( | |
| 497 self.context, SecurityConst.kSSLSessionOptionBreakOnServerAuth, True | |
| 498 ) | |
| 499 _assert_no_error(result) | |
| 500 | |
| 501 # If there's a client cert, we need to use it. | |
| 502 if client_cert: | |
| 503 self._keychain, self._keychain_dir = _temporary_keychain() | |
| 504 self._client_cert_chain = _load_client_cert_chain( | |
| 505 self._keychain, client_cert, client_key | |
| 506 ) | |
| 507 result = Security.SSLSetCertificate(self.context, self._client_cert_chain) | |
| 508 _assert_no_error(result) | |
| 509 | |
| 510 while True: | |
| 511 with self._raise_on_error(): | |
| 512 result = Security.SSLHandshake(self.context) | |
| 513 | |
| 514 if result == SecurityConst.errSSLWouldBlock: | |
| 515 raise socket.timeout("handshake timed out") | |
| 516 elif result == SecurityConst.errSSLServerAuthCompleted: | |
| 517 self._custom_validate(verify, trust_bundle) | |
| 518 continue | |
| 519 else: | |
| 520 _assert_no_error(result) | |
| 521 break | |
| 522 | |
| 523 def fileno(self): | |
| 524 return self.socket.fileno() | |
| 525 | |
| 526 # Copy-pasted from Python 3.5 source code | |
| 527 def _decref_socketios(self): | |
| 528 if self._makefile_refs > 0: | |
| 529 self._makefile_refs -= 1 | |
| 530 if self._closed: | |
| 531 self.close() | |
| 532 | |
| 533 def recv(self, bufsiz): | |
| 534 buffer = ctypes.create_string_buffer(bufsiz) | |
| 535 bytes_read = self.recv_into(buffer, bufsiz) | |
| 536 data = buffer[:bytes_read] | |
| 537 return data | |
| 538 | |
| 539 def recv_into(self, buffer, nbytes=None): | |
| 540 # Read short on EOF. | |
| 541 if self._closed: | |
| 542 return 0 | |
| 543 | |
| 544 if nbytes is None: | |
| 545 nbytes = len(buffer) | |
| 546 | |
| 547 buffer = (ctypes.c_char * nbytes).from_buffer(buffer) | |
| 548 processed_bytes = ctypes.c_size_t(0) | |
| 549 | |
| 550 with self._raise_on_error(): | |
| 551 result = Security.SSLRead( | |
| 552 self.context, buffer, nbytes, ctypes.byref(processed_bytes) | |
| 553 ) | |
| 554 | |
| 555 # There are some result codes that we want to treat as "not always | |
| 556 # errors". Specifically, those are errSSLWouldBlock, | |
| 557 # errSSLClosedGraceful, and errSSLClosedNoNotify. | |
| 558 if result == SecurityConst.errSSLWouldBlock: | |
| 559 # If we didn't process any bytes, then this was just a time out. | |
| 560 # However, we can get errSSLWouldBlock in situations when we *did* | |
| 561 # read some data, and in those cases we should just read "short" | |
| 562 # and return. | |
| 563 if processed_bytes.value == 0: | |
| 564 # Timed out, no data read. | |
| 565 raise socket.timeout("recv timed out") | |
| 566 elif result in ( | |
| 567 SecurityConst.errSSLClosedGraceful, | |
| 568 SecurityConst.errSSLClosedNoNotify, | |
| 569 ): | |
| 570 # The remote peer has closed this connection. We should do so as | |
| 571 # well. Note that we don't actually return here because in | |
| 572 # principle this could actually be fired along with return data. | |
| 573 # It's unlikely though. | |
| 574 self.close() | |
| 575 else: | |
| 576 _assert_no_error(result) | |
| 577 | |
| 578 # Ok, we read and probably succeeded. We should return whatever data | |
| 579 # was actually read. | |
| 580 return processed_bytes.value | |
| 581 | |
| 582 def settimeout(self, timeout): | |
| 583 self._timeout = timeout | |
| 584 | |
| 585 def gettimeout(self): | |
| 586 return self._timeout | |
| 587 | |
| 588 def send(self, data): | |
| 589 processed_bytes = ctypes.c_size_t(0) | |
| 590 | |
| 591 with self._raise_on_error(): | |
| 592 result = Security.SSLWrite( | |
| 593 self.context, data, len(data), ctypes.byref(processed_bytes) | |
| 594 ) | |
| 595 | |
| 596 if result == SecurityConst.errSSLWouldBlock and processed_bytes.value == 0: | |
| 597 # Timed out | |
| 598 raise socket.timeout("send timed out") | |
| 599 else: | |
| 600 _assert_no_error(result) | |
| 601 | |
| 602 # We sent, and probably succeeded. Tell them how much we sent. | |
| 603 return processed_bytes.value | |
| 604 | |
| 605 def sendall(self, data): | |
| 606 total_sent = 0 | |
| 607 while total_sent < len(data): | |
| 608 sent = self.send(data[total_sent : total_sent + SSL_WRITE_BLOCKSIZE]) | |
| 609 total_sent += sent | |
| 610 | |
| 611 def shutdown(self): | |
| 612 with self._raise_on_error(): | |
| 613 Security.SSLClose(self.context) | |
| 614 | |
| 615 def close(self): | |
| 616 # TODO: should I do clean shutdown here? Do I have to? | |
| 617 if self._makefile_refs < 1: | |
| 618 self._closed = True | |
| 619 if self.context: | |
| 620 CoreFoundation.CFRelease(self.context) | |
| 621 self.context = None | |
| 622 if self._client_cert_chain: | |
| 623 CoreFoundation.CFRelease(self._client_cert_chain) | |
| 624 self._client_cert_chain = None | |
| 625 if self._keychain: | |
| 626 Security.SecKeychainDelete(self._keychain) | |
| 627 CoreFoundation.CFRelease(self._keychain) | |
| 628 shutil.rmtree(self._keychain_dir) | |
| 629 self._keychain = self._keychain_dir = None | |
| 630 return self.socket.close() | |
| 631 else: | |
| 632 self._makefile_refs -= 1 | |
| 633 | |
| 634 def getpeercert(self, binary_form=False): | |
| 635 # Urgh, annoying. | |
| 636 # | |
| 637 # Here's how we do this: | |
| 638 # | |
| 639 # 1. Call SSLCopyPeerTrust to get hold of the trust object for this | |
| 640 # connection. | |
| 641 # 2. Call SecTrustGetCertificateAtIndex for index 0 to get the leaf. | |
| 642 # 3. To get the CN, call SecCertificateCopyCommonName and process that | |
| 643 # string so that it's of the appropriate type. | |
| 644 # 4. To get the SAN, we need to do something a bit more complex: | |
| 645 # a. Call SecCertificateCopyValues to get the data, requesting | |
| 646 # kSecOIDSubjectAltName. | |
| 647 # b. Mess about with this dictionary to try to get the SANs out. | |
| 648 # | |
| 649 # This is gross. Really gross. It's going to be a few hundred LoC extra | |
| 650 # just to repeat something that SecureTransport can *already do*. So my | |
| 651 # operating assumption at this time is that what we want to do is | |
| 652 # instead to just flag to urllib3 that it shouldn't do its own hostname | |
| 653 # validation when using SecureTransport. | |
| 654 if not binary_form: | |
| 655 raise ValueError("SecureTransport only supports dumping binary certs") | |
| 656 trust = Security.SecTrustRef() | |
| 657 certdata = None | |
| 658 der_bytes = None | |
| 659 | |
| 660 try: | |
| 661 # Grab the trust store. | |
| 662 result = Security.SSLCopyPeerTrust(self.context, ctypes.byref(trust)) | |
| 663 _assert_no_error(result) | |
| 664 if not trust: | |
| 665 # Probably we haven't done the handshake yet. No biggie. | |
| 666 return None | |
| 667 | |
| 668 cert_count = Security.SecTrustGetCertificateCount(trust) | |
| 669 if not cert_count: | |
| 670 # Also a case that might happen if we haven't handshaked. | |
| 671 # Handshook? Handshaken? | |
| 672 return None | |
| 673 | |
| 674 leaf = Security.SecTrustGetCertificateAtIndex(trust, 0) | |
| 675 assert leaf | |
| 676 | |
| 677 # Ok, now we want the DER bytes. | |
| 678 certdata = Security.SecCertificateCopyData(leaf) | |
| 679 assert certdata | |
| 680 | |
| 681 data_length = CoreFoundation.CFDataGetLength(certdata) | |
| 682 data_buffer = CoreFoundation.CFDataGetBytePtr(certdata) | |
| 683 der_bytes = ctypes.string_at(data_buffer, data_length) | |
| 684 finally: | |
| 685 if certdata: | |
| 686 CoreFoundation.CFRelease(certdata) | |
| 687 if trust: | |
| 688 CoreFoundation.CFRelease(trust) | |
| 689 | |
| 690 return der_bytes | |
| 691 | |
| 692 def version(self): | |
| 693 protocol = Security.SSLProtocol() | |
| 694 result = Security.SSLGetNegotiatedProtocolVersion( | |
| 695 self.context, ctypes.byref(protocol) | |
| 696 ) | |
| 697 _assert_no_error(result) | |
| 698 if protocol.value == SecurityConst.kTLSProtocol13: | |
| 699 raise ssl.SSLError("SecureTransport does not support TLS 1.3") | |
| 700 elif protocol.value == SecurityConst.kTLSProtocol12: | |
| 701 return "TLSv1.2" | |
| 702 elif protocol.value == SecurityConst.kTLSProtocol11: | |
| 703 return "TLSv1.1" | |
| 704 elif protocol.value == SecurityConst.kTLSProtocol1: | |
| 705 return "TLSv1" | |
| 706 elif protocol.value == SecurityConst.kSSLProtocol3: | |
| 707 return "SSLv3" | |
| 708 elif protocol.value == SecurityConst.kSSLProtocol2: | |
| 709 return "SSLv2" | |
| 710 else: | |
| 711 raise ssl.SSLError("Unknown TLS version: %r" % protocol) | |
| 712 | |
| 713 def _reuse(self): | |
| 714 self._makefile_refs += 1 | |
| 715 | |
| 716 def _drop(self): | |
| 717 if self._makefile_refs < 1: | |
| 718 self.close() | |
| 719 else: | |
| 720 self._makefile_refs -= 1 | |
| 721 | |
| 722 | |
| 723 if _fileobject: # Platform-specific: Python 2 | |
| 724 | |
| 725 def makefile(self, mode, bufsize=-1): | |
| 726 self._makefile_refs += 1 | |
| 727 return _fileobject(self, mode, bufsize, close=True) | |
| 728 | |
| 729 | |
| 730 else: # Platform-specific: Python 3 | |
| 731 | |
| 732 def makefile(self, mode="r", buffering=None, *args, **kwargs): | |
| 733 # We disable buffering with SecureTransport because it conflicts with | |
| 734 # the buffering that ST does internally (see issue #1153 for more). | |
| 735 buffering = 0 | |
| 736 return backport_makefile(self, mode, buffering, *args, **kwargs) | |
| 737 | |
| 738 | |
| 739 WrappedSocket.makefile = makefile | |
| 740 | |
| 741 | |
| 742 class SecureTransportContext(object): | |
| 743 """ | |
| 744 I am a wrapper class for the SecureTransport library, to translate the | |
| 745 interface of the standard library ``SSLContext`` object to calls into | |
| 746 SecureTransport. | |
| 747 """ | |
| 748 | |
| 749 def __init__(self, protocol): | |
| 750 self._min_version, self._max_version = _protocol_to_min_max[protocol] | |
| 751 self._options = 0 | |
| 752 self._verify = False | |
| 753 self._trust_bundle = None | |
| 754 self._client_cert = None | |
| 755 self._client_key = None | |
| 756 self._client_key_passphrase = None | |
| 757 | |
| 758 @property | |
| 759 def check_hostname(self): | |
| 760 """ | |
| 761 SecureTransport cannot have its hostname checking disabled. For more, | |
| 762 see the comment on getpeercert() in this file. | |
| 763 """ | |
| 764 return True | |
| 765 | |
| 766 @check_hostname.setter | |
| 767 def check_hostname(self, value): | |
| 768 """ | |
| 769 SecureTransport cannot have its hostname checking disabled. For more, | |
| 770 see the comment on getpeercert() in this file. | |
| 771 """ | |
| 772 pass | |
| 773 | |
| 774 @property | |
| 775 def options(self): | |
| 776 # TODO: Well, crap. | |
| 777 # | |
| 778 # So this is the bit of the code that is the most likely to cause us | |
| 779 # trouble. Essentially we need to enumerate all of the SSL options that | |
| 780 # users might want to use and try to see if we can sensibly translate | |
| 781 # them, or whether we should just ignore them. | |
| 782 return self._options | |
| 783 | |
| 784 @options.setter | |
| 785 def options(self, value): | |
| 786 # TODO: Update in line with above. | |
| 787 self._options = value | |
| 788 | |
| 789 @property | |
| 790 def verify_mode(self): | |
| 791 return ssl.CERT_REQUIRED if self._verify else ssl.CERT_NONE | |
| 792 | |
| 793 @verify_mode.setter | |
| 794 def verify_mode(self, value): | |
| 795 self._verify = True if value == ssl.CERT_REQUIRED else False | |
| 796 | |
| 797 def set_default_verify_paths(self): | |
| 798 # So, this has to do something a bit weird. Specifically, what it does | |
| 799 # is nothing. | |
| 800 # | |
| 801 # This means that, if we had previously had load_verify_locations | |
| 802 # called, this does not undo that. We need to do that because it turns | |
| 803 # out that the rest of the urllib3 code will attempt to load the | |
| 804 # default verify paths if it hasn't been told about any paths, even if | |
| 805 # the context itself was sometime earlier. We resolve that by just | |
| 806 # ignoring it. | |
| 807 pass | |
| 808 | |
| 809 def load_default_certs(self): | |
| 810 return self.set_default_verify_paths() | |
| 811 | |
| 812 def set_ciphers(self, ciphers): | |
| 813 # For now, we just require the default cipher string. | |
| 814 if ciphers != util.ssl_.DEFAULT_CIPHERS: | |
| 815 raise ValueError("SecureTransport doesn't support custom cipher strings") | |
| 816 | |
| 817 def load_verify_locations(self, cafile=None, capath=None, cadata=None): | |
| 818 # OK, we only really support cadata and cafile. | |
| 819 if capath is not None: | |
| 820 raise ValueError("SecureTransport does not support cert directories") | |
| 821 | |
| 822 # Raise if cafile does not exist. | |
| 823 if cafile is not None: | |
| 824 with open(cafile): | |
| 825 pass | |
| 826 | |
| 827 self._trust_bundle = cafile or cadata | |
| 828 | |
| 829 def load_cert_chain(self, certfile, keyfile=None, password=None): | |
| 830 self._client_cert = certfile | |
| 831 self._client_key = keyfile | |
| 832 self._client_cert_passphrase = password | |
| 833 | |
| 834 def wrap_socket( | |
| 835 self, | |
| 836 sock, | |
| 837 server_side=False, | |
| 838 do_handshake_on_connect=True, | |
| 839 suppress_ragged_eofs=True, | |
| 840 server_hostname=None, | |
| 841 ): | |
| 842 # So, what do we do here? Firstly, we assert some properties. This is a | |
| 843 # stripped down shim, so there is some functionality we don't support. | |
| 844 # See PEP 543 for the real deal. | |
| 845 assert not server_side | |
| 846 assert do_handshake_on_connect | |
| 847 assert suppress_ragged_eofs | |
| 848 | |
| 849 # Ok, we're good to go. Now we want to create the wrapped socket object | |
| 850 # and store it in the appropriate place. | |
| 851 wrapped_socket = WrappedSocket(sock) | |
| 852 | |
| 853 # Now we can handshake | |
| 854 wrapped_socket.handshake( | |
| 855 server_hostname, | |
| 856 self._verify, | |
| 857 self._trust_bundle, | |
| 858 self._min_version, | |
| 859 self._max_version, | |
| 860 self._client_cert, | |
| 861 self._client_key, | |
| 862 self._client_key_passphrase, | |
| 863 ) | |
| 864 return wrapped_socket |
