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