1"""
2SecureTranport support for urllib3 via ctypes.
3
4This makes platform-native TLS available to urllib3 users on macOS without the
5use of a compiler. This is an important feature because the Python Package
6Index is moving to become a TLSv1.2-or-higher server, and the default OpenSSL
7that ships with macOS is not capable of doing TLSv1.2. The only way to resolve
8this is to give macOS users an alternative solution to the problem, and that
9solution is to use SecureTransport.
10
11We use ctypes here because this solution must not require a compiler. That's
12because pip is not allowed to require a compiler either.
13
14This is not intended to be a seriously long-term solution to this problem.
15The hope is that PEP 543 will eventually solve this issue for us, at which
16point we can retire this contrib module. But in the short term, we need to
17solve the impending tire fire that is Python on Mac without this kind of
18contrib module. So...here we are.
19
20To use this module, simply import and inject it::
21
22    import urllib3.contrib.securetransport
23    urllib3.contrib.securetransport.inject_into_urllib3()
24
25Happy TLSing!
26
27This code is a bastardised version of the code found in Will Bond's oscrypto
28library. An enormous debt is owed to him for blazing this trail for us. For
29that reason, this code should be considered to be covered both by urllib3's
30license 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"""
54from __future__ import absolute_import
55
56import contextlib
57import ctypes
58import errno
59import os.path
60import shutil
61import socket
62import ssl
63import struct
64import threading
65import weakref
66
67import six
68
69from .. import util
70from ..util.ssl_ import PROTOCOL_TLS_CLIENT
71from ._securetransport.bindings import CoreFoundation, Security, SecurityConst
72from ._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
81try:  # Platform-specific: Python 2
82    from socket import _fileobject
83except 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
90HAS_SNI = True
91
92orig_util_HAS_SNI = util.HAS_SNI
93orig_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.
116SSL_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.
121CIPHER_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
162if hasattr(ssl, "PROTOCOL_SSLv2"):
163    _protocol_to_min_max[ssl.PROTOCOL_SSLv2] = (
164        SecurityConst.kSSLProtocol2,
165        SecurityConst.kSSLProtocol2,
166    )
167if hasattr(ssl, "PROTOCOL_SSLv3"):
168    _protocol_to_min_max[ssl.PROTOCOL_SSLv3] = (
169        SecurityConst.kSSLProtocol3,
170        SecurityConst.kSSLProtocol3,
171    )
172if hasattr(ssl, "PROTOCOL_TLSv1"):
173    _protocol_to_min_max[ssl.PROTOCOL_TLSv1] = (
174        SecurityConst.kTLSProtocol1,
175        SecurityConst.kTLSProtocol1,
176    )
177if hasattr(ssl, "PROTOCOL_TLSv1_1"):
178    _protocol_to_min_max[ssl.PROTOCOL_TLSv1_1] = (
179        SecurityConst.kTLSProtocol11,
180        SecurityConst.kTLSProtocol11,
181    )
182if hasattr(ssl, "PROTOCOL_TLSv1_2"):
183    _protocol_to_min_max[ssl.PROTOCOL_TLSv1_2] = (
184        SecurityConst.kTLSProtocol12,
185        SecurityConst.kTLSProtocol12,
186    )
187
188
189def 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
201def 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
213def _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
268def _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
326class 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
767if _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
774else:  # Platform-specific: Python 3
775
776    def makefile(self, mode="r", buffering=None, *args, **kwargs):
777        # We disable buffering with SecureTransport because it conflicts with
778        # the buffering that ST does internally (see issue #1153 for more).
779        buffering = 0
780        return backport_makefile(self, mode, buffering, *args, **kwargs)
781
782
783WrappedSocket.makefile = makefile
784
785
786class SecureTransportContext(object):
787    """
788    I am a wrapper class for the SecureTransport library, to translate the
789    interface of the standard library ``SSLContext`` object to calls into
790    SecureTransport.
791    """
792
793    def __init__(self, protocol):
794        self._min_version, self._max_version = _protocol_to_min_max[protocol]
795        self._options = 0
796        self._verify = False
797        self._trust_bundle = None
798        self._client_cert = None
799        self._client_key = None
800        self._client_key_passphrase = None
801        self._alpn_protocols = None
802
803    @property
804    def check_hostname(self):
805        """
806        SecureTransport cannot have its hostname checking disabled. For more,
807        see the comment on getpeercert() in this file.
808        """
809        return True
810
811    @check_hostname.setter
812    def check_hostname(self, value):
813        """
814        SecureTransport cannot have its hostname checking disabled. For more,
815        see the comment on getpeercert() in this file.
816        """
817        pass
818
819    @property
820    def options(self):
821        # TODO: Well, crap.
822        #
823        # So this is the bit of the code that is the most likely to cause us
824        # trouble. Essentially we need to enumerate all of the SSL options that
825        # users might want to use and try to see if we can sensibly translate
826        # them, or whether we should just ignore them.
827        return self._options
828
829    @options.setter
830    def options(self, value):
831        # TODO: Update in line with above.
832        self._options = value
833
834    @property
835    def verify_mode(self):
836        return ssl.CERT_REQUIRED if self._verify else ssl.CERT_NONE
837
838    @verify_mode.setter
839    def verify_mode(self, value):
840        self._verify = True if value == ssl.CERT_REQUIRED else False
841
842    def set_default_verify_paths(self):
843        # So, this has to do something a bit weird. Specifically, what it does
844        # is nothing.
845        #
846        # This means that, if we had previously had load_verify_locations
847        # called, this does not undo that. We need to do that because it turns
848        # out that the rest of the urllib3 code will attempt to load the
849        # default verify paths if it hasn't been told about any paths, even if
850        # the context itself was sometime earlier. We resolve that by just
851        # ignoring it.
852        pass
853
854    def load_default_certs(self):
855        return self.set_default_verify_paths()
856
857    def set_ciphers(self, ciphers):
858        # For now, we just require the default cipher string.
859        if ciphers != util.ssl_.DEFAULT_CIPHERS:
860            raise ValueError("SecureTransport doesn't support custom cipher strings")
861
862    def load_verify_locations(self, cafile=None, capath=None, cadata=None):
863        # OK, we only really support cadata and cafile.
864        if capath is not None:
865            raise ValueError("SecureTransport does not support cert directories")
866
867        # Raise if cafile does not exist.
868        if cafile is not None:
869            with open(cafile):
870                pass
871
872        self._trust_bundle = cafile or cadata
873
874    def load_cert_chain(self, certfile, keyfile=None, password=None):
875        self._client_cert = certfile
876        self._client_key = keyfile
877        self._client_cert_passphrase = password
878
879    def set_alpn_protocols(self, protocols):
880        """
881        Sets the ALPN protocols that will later be set on the context.
882
883        Raises a NotImplementedError if ALPN is not supported.
884        """
885        if not hasattr(Security, "SSLSetALPNProtocols"):
886            raise NotImplementedError(
887                "SecureTransport supports ALPN only in macOS 10.12+"
888            )
889        self._alpn_protocols = [six.ensure_binary(p) for p in protocols]
890
891    def wrap_socket(
892        self,
893        sock,
894        server_side=False,
895        do_handshake_on_connect=True,
896        suppress_ragged_eofs=True,
897        server_hostname=None,
898    ):
899        # So, what do we do here? Firstly, we assert some properties. This is a
900        # stripped down shim, so there is some functionality we don't support.
901        # See PEP 543 for the real deal.
902        assert not server_side
903        assert do_handshake_on_connect
904        assert suppress_ragged_eofs
905
906        # Ok, we're good to go. Now we want to create the wrapped socket object
907        # and store it in the appropriate place.
908        wrapped_socket = WrappedSocket(sock)
909
910        # Now we can handshake
911        wrapped_socket.handshake(
912            server_hostname,
913            self._verify,
914            self._trust_bundle,
915            self._min_version,
916            self._max_version,
917            self._client_cert,
918            self._client_key,
919            self._client_key_passphrase,
920            self._alpn_protocols,
921        )
922        return wrapped_socket
923