]> jfr.im git - dlqueue.git/blob - venv/lib/python3.11/site-packages/pip/_vendor/urllib3/contrib/securetransport.py
init: venv aand flask
[dlqueue.git] / venv / lib / python3.11 / site-packages / pip / _vendor / urllib3 / contrib / securetransport.py
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