Mercurial > repos > shellac > sam_consensus_v3
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 |