comparison env/lib/python3.9/site-packages/pip/_vendor/urllib3/contrib/securetransport.py @ 0:4f3585e2f14b draft default tip

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