]>
Commit | Line | Data |
---|---|---|
e0df8241 JR |
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 pip._vendor.urllib3.contrib.securetransport as securetransport | |
23 | 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 ..util.ssl_ import PROTOCOL_TLS_CLIENT | |
71 | from ._securetransport.bindings import CoreFoundation, Security, SecurityConst | |
72 | from ._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 | ||
81 | try: # Platform-specific: Python 2 | |
82 | from socket import _fileobject | |
83 | except 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 | |
90 | HAS_SNI = True | |
91 | ||
92 | orig_util_HAS_SNI = util.HAS_SNI | |
93 | orig_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. | |
116 | SSL_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. | |
121 | CIPHER_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 | ||
162 | if hasattr(ssl, "PROTOCOL_SSLv2"): | |
163 | _protocol_to_min_max[ssl.PROTOCOL_SSLv2] = ( | |
164 | SecurityConst.kSSLProtocol2, | |
165 | SecurityConst.kSSLProtocol2, | |
166 | ) | |
167 | if hasattr(ssl, "PROTOCOL_SSLv3"): | |
168 | _protocol_to_min_max[ssl.PROTOCOL_SSLv3] = ( | |
169 | SecurityConst.kSSLProtocol3, | |
170 | SecurityConst.kSSLProtocol3, | |
171 | ) | |
172 | if hasattr(ssl, "PROTOCOL_TLSv1"): | |
173 | _protocol_to_min_max[ssl.PROTOCOL_TLSv1] = ( | |
174 | SecurityConst.kTLSProtocol1, | |
175 | SecurityConst.kTLSProtocol1, | |
176 | ) | |
177 | if hasattr(ssl, "PROTOCOL_TLSv1_1"): | |
178 | _protocol_to_min_max[ssl.PROTOCOL_TLSv1_1] = ( | |
179 | SecurityConst.kTLSProtocol11, | |
180 | SecurityConst.kTLSProtocol11, | |
181 | ) | |
182 | if hasattr(ssl, "PROTOCOL_TLSv1_2"): | |
183 | _protocol_to_min_max[ssl.PROTOCOL_TLSv1_2] = ( | |
184 | SecurityConst.kTLSProtocol12, | |
185 | SecurityConst.kTLSProtocol12, | |
186 | ) | |
187 | ||
188 | ||
189 | def 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 | ||
201 | def 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 | ||
213 | def _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 | ||
268 | def _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 | ||
326 | class 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 | ||
767 | if _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 | else: # Platform-specific: Python 3 | |
774 | ||
775 | def makefile(self, mode="r", buffering=None, *args, **kwargs): | |
776 | # We disable buffering with SecureTransport because it conflicts with | |
777 | # the buffering that ST does internally (see issue #1153 for more). | |
778 | buffering = 0 | |
779 | return backport_makefile(self, mode, buffering, *args, **kwargs) | |
780 | ||
781 | ||
782 | WrappedSocket.makefile = makefile | |
783 | ||
784 | ||
785 | class SecureTransportContext(object): | |
786 | """ | |
787 | I am a wrapper class for the SecureTransport library, to translate the | |
788 | interface of the standard library ``SSLContext`` object to calls into | |
789 | SecureTransport. | |
790 | """ | |
791 | ||
792 | def __init__(self, protocol): | |
793 | self._min_version, self._max_version = _protocol_to_min_max[protocol] | |
794 | self._options = 0 | |
795 | self._verify = False | |
796 | self._trust_bundle = None | |
797 | self._client_cert = None | |
798 | self._client_key = None | |
799 | self._client_key_passphrase = None | |
800 | self._alpn_protocols = None | |
801 | ||
802 | @property | |
803 | def check_hostname(self): | |
804 | """ | |
805 | SecureTransport cannot have its hostname checking disabled. For more, | |
806 | see the comment on getpeercert() in this file. | |
807 | """ | |
808 | return True | |
809 | ||
810 | @check_hostname.setter | |
811 | def check_hostname(self, value): | |
812 | """ | |
813 | SecureTransport cannot have its hostname checking disabled. For more, | |
814 | see the comment on getpeercert() in this file. | |
815 | """ | |
816 | pass | |
817 | ||
818 | @property | |
819 | def options(self): | |
820 | # TODO: Well, crap. | |
821 | # | |
822 | # So this is the bit of the code that is the most likely to cause us | |
823 | # trouble. Essentially we need to enumerate all of the SSL options that | |
824 | # users might want to use and try to see if we can sensibly translate | |
825 | # them, or whether we should just ignore them. | |
826 | return self._options | |
827 | ||
828 | @options.setter | |
829 | def options(self, value): | |
830 | # TODO: Update in line with above. | |
831 | self._options = value | |
832 | ||
833 | @property | |
834 | def verify_mode(self): | |
835 | return ssl.CERT_REQUIRED if self._verify else ssl.CERT_NONE | |
836 | ||
837 | @verify_mode.setter | |
838 | def verify_mode(self, value): | |
839 | self._verify = True if value == ssl.CERT_REQUIRED else False | |
840 | ||
841 | def set_default_verify_paths(self): | |
842 | # So, this has to do something a bit weird. Specifically, what it does | |
843 | # is nothing. | |
844 | # | |
845 | # This means that, if we had previously had load_verify_locations | |
846 | # called, this does not undo that. We need to do that because it turns | |
847 | # out that the rest of the urllib3 code will attempt to load the | |
848 | # default verify paths if it hasn't been told about any paths, even if | |
849 | # the context itself was sometime earlier. We resolve that by just | |
850 | # ignoring it. | |
851 | pass | |
852 | ||
853 | def load_default_certs(self): | |
854 | return self.set_default_verify_paths() | |
855 | ||
856 | def set_ciphers(self, ciphers): | |
857 | # For now, we just require the default cipher string. | |
858 | if ciphers != util.ssl_.DEFAULT_CIPHERS: | |
859 | raise ValueError("SecureTransport doesn't support custom cipher strings") | |
860 | ||
861 | def load_verify_locations(self, cafile=None, capath=None, cadata=None): | |
862 | # OK, we only really support cadata and cafile. | |
863 | if capath is not None: | |
864 | raise ValueError("SecureTransport does not support cert directories") | |
865 | ||
866 | # Raise if cafile does not exist. | |
867 | if cafile is not None: | |
868 | with open(cafile): | |
869 | pass | |
870 | ||
871 | self._trust_bundle = cafile or cadata | |
872 | ||
873 | def load_cert_chain(self, certfile, keyfile=None, password=None): | |
874 | self._client_cert = certfile | |
875 | self._client_key = keyfile | |
876 | self._client_cert_passphrase = password | |
877 | ||
878 | def set_alpn_protocols(self, protocols): | |
879 | """ | |
880 | Sets the ALPN protocols that will later be set on the context. | |
881 | ||
882 | Raises a NotImplementedError if ALPN is not supported. | |
883 | """ | |
884 | if not hasattr(Security, "SSLSetALPNProtocols"): | |
885 | raise NotImplementedError( | |
886 | "SecureTransport supports ALPN only in macOS 10.12+" | |
887 | ) | |
888 | self._alpn_protocols = [six.ensure_binary(p) for p in protocols] | |
889 | ||
890 | def wrap_socket( | |
891 | self, | |
892 | sock, | |
893 | server_side=False, | |
894 | do_handshake_on_connect=True, | |
895 | suppress_ragged_eofs=True, | |
896 | server_hostname=None, | |
897 | ): | |
898 | # So, what do we do here? Firstly, we assert some properties. This is a | |
899 | # stripped down shim, so there is some functionality we don't support. | |
900 | # See PEP 543 for the real deal. | |
901 | assert not server_side | |
902 | assert do_handshake_on_connect | |
903 | assert suppress_ragged_eofs | |
904 | ||
905 | # Ok, we're good to go. Now we want to create the wrapped socket object | |
906 | # and store it in the appropriate place. | |
907 | wrapped_socket = WrappedSocket(sock) | |
908 | ||
909 | # Now we can handshake | |
910 | wrapped_socket.handshake( | |
911 | server_hostname, | |
912 | self._verify, | |
913 | self._trust_bundle, | |
914 | self._min_version, | |
915 | self._max_version, | |
916 | self._client_cert, | |
917 | self._client_key, | |
918 | self._client_key_passphrase, | |
919 | self._alpn_protocols, | |
920 | ) | |
921 | return wrapped_socket |