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