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