Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hhhrrrttt222111
GitHub Repository: hhhrrrttt222111/Dorkify
Path: blob/master/venv/Lib/site-packages/urllib3/contrib/pyopenssl.py
811 views
1
"""
2
SSL with SNI_-support for Python 2. Follow these instructions if you would
3
like to verify SSL certificates in Python 2. Note, the default libraries do
4
*not* do certificate checking; you need to do additional work to validate
5
certificates yourself.
6
7
This needs the following packages installed:
8
9
* pyOpenSSL (tested with 16.0.0)
10
* cryptography (minimum 1.3.4, from pyopenssl)
11
* idna (minimum 2.0, from cryptography)
12
13
However, pyopenssl depends on cryptography, which depends on idna, so while we
14
use all three directly here we end up having relatively few packages required.
15
16
You can install them with the following command:
17
18
pip install pyopenssl cryptography idna
19
20
To activate certificate checking, call
21
:func:`~urllib3.contrib.pyopenssl.inject_into_urllib3` from your Python code
22
before you begin making HTTP requests. This can be done in a ``sitecustomize``
23
module, or at any other time before your application begins using ``urllib3``,
24
like this::
25
26
try:
27
import urllib3.contrib.pyopenssl
28
urllib3.contrib.pyopenssl.inject_into_urllib3()
29
except ImportError:
30
pass
31
32
Now you can use :mod:`urllib3` as you normally would, and it will support SNI
33
when the required modules are installed.
34
35
Activating this module also has the positive side effect of disabling SSL/TLS
36
compression in Python 2 (see `CRIME attack`_).
37
38
If you want to configure the default list of supported cipher suites, you can
39
set the ``urllib3.contrib.pyopenssl.DEFAULT_SSL_CIPHER_LIST`` variable.
40
41
.. _sni: https://en.wikipedia.org/wiki/Server_Name_Indication
42
.. _crime attack: https://en.wikipedia.org/wiki/CRIME_(security_exploit)
43
"""
44
from __future__ import absolute_import
45
46
import OpenSSL.SSL
47
from cryptography import x509
48
from cryptography.hazmat.backends.openssl import backend as openssl_backend
49
from cryptography.hazmat.backends.openssl.x509 import _Certificate
50
51
try:
52
from cryptography.x509 import UnsupportedExtension
53
except ImportError:
54
# UnsupportedExtension is gone in cryptography >= 2.1.0
55
class UnsupportedExtension(Exception):
56
pass
57
58
59
from socket import timeout, error as SocketError
60
from io import BytesIO
61
62
try: # Platform-specific: Python 2
63
from socket import _fileobject
64
except ImportError: # Platform-specific: Python 3
65
_fileobject = None
66
from ..packages.backports.makefile import backport_makefile
67
68
import logging
69
import ssl
70
from ..packages import six
71
import sys
72
73
from .. import util
74
75
76
__all__ = ["inject_into_urllib3", "extract_from_urllib3"]
77
78
# SNI always works.
79
HAS_SNI = True
80
81
# Map from urllib3 to PyOpenSSL compatible parameter-values.
82
_openssl_versions = {
83
util.PROTOCOL_TLS: OpenSSL.SSL.SSLv23_METHOD,
84
ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD,
85
}
86
87
if hasattr(ssl, "PROTOCOL_SSLv3") and hasattr(OpenSSL.SSL, "SSLv3_METHOD"):
88
_openssl_versions[ssl.PROTOCOL_SSLv3] = OpenSSL.SSL.SSLv3_METHOD
89
90
if hasattr(ssl, "PROTOCOL_TLSv1_1") and hasattr(OpenSSL.SSL, "TLSv1_1_METHOD"):
91
_openssl_versions[ssl.PROTOCOL_TLSv1_1] = OpenSSL.SSL.TLSv1_1_METHOD
92
93
if hasattr(ssl, "PROTOCOL_TLSv1_2") and hasattr(OpenSSL.SSL, "TLSv1_2_METHOD"):
94
_openssl_versions[ssl.PROTOCOL_TLSv1_2] = OpenSSL.SSL.TLSv1_2_METHOD
95
96
97
_stdlib_to_openssl_verify = {
98
ssl.CERT_NONE: OpenSSL.SSL.VERIFY_NONE,
99
ssl.CERT_OPTIONAL: OpenSSL.SSL.VERIFY_PEER,
100
ssl.CERT_REQUIRED: OpenSSL.SSL.VERIFY_PEER
101
+ OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
102
}
103
_openssl_to_stdlib_verify = dict((v, k) for k, v in _stdlib_to_openssl_verify.items())
104
105
# OpenSSL will only write 16K at a time
106
SSL_WRITE_BLOCKSIZE = 16384
107
108
orig_util_HAS_SNI = util.HAS_SNI
109
orig_util_SSLContext = util.ssl_.SSLContext
110
111
112
log = logging.getLogger(__name__)
113
114
115
def inject_into_urllib3():
116
"Monkey-patch urllib3 with PyOpenSSL-backed SSL-support."
117
118
_validate_dependencies_met()
119
120
util.SSLContext = PyOpenSSLContext
121
util.ssl_.SSLContext = PyOpenSSLContext
122
util.HAS_SNI = HAS_SNI
123
util.ssl_.HAS_SNI = HAS_SNI
124
util.IS_PYOPENSSL = True
125
util.ssl_.IS_PYOPENSSL = True
126
127
128
def extract_from_urllib3():
129
"Undo monkey-patching by :func:`inject_into_urllib3`."
130
131
util.SSLContext = orig_util_SSLContext
132
util.ssl_.SSLContext = orig_util_SSLContext
133
util.HAS_SNI = orig_util_HAS_SNI
134
util.ssl_.HAS_SNI = orig_util_HAS_SNI
135
util.IS_PYOPENSSL = False
136
util.ssl_.IS_PYOPENSSL = False
137
138
139
def _validate_dependencies_met():
140
"""
141
Verifies that PyOpenSSL's package-level dependencies have been met.
142
Throws `ImportError` if they are not met.
143
"""
144
# Method added in `cryptography==1.1`; not available in older versions
145
from cryptography.x509.extensions import Extensions
146
147
if getattr(Extensions, "get_extension_for_class", None) is None:
148
raise ImportError(
149
"'cryptography' module missing required functionality. "
150
"Try upgrading to v1.3.4 or newer."
151
)
152
153
# pyOpenSSL 0.14 and above use cryptography for OpenSSL bindings. The _x509
154
# attribute is only present on those versions.
155
from OpenSSL.crypto import X509
156
157
x509 = X509()
158
if getattr(x509, "_x509", None) is None:
159
raise ImportError(
160
"'pyOpenSSL' module missing required functionality. "
161
"Try upgrading to v0.14 or newer."
162
)
163
164
165
def _dnsname_to_stdlib(name):
166
"""
167
Converts a dNSName SubjectAlternativeName field to the form used by the
168
standard library on the given Python version.
169
170
Cryptography produces a dNSName as a unicode string that was idna-decoded
171
from ASCII bytes. We need to idna-encode that string to get it back, and
172
then on Python 3 we also need to convert to unicode via UTF-8 (the stdlib
173
uses PyUnicode_FromStringAndSize on it, which decodes via UTF-8).
174
175
If the name cannot be idna-encoded then we return None signalling that
176
the name given should be skipped.
177
"""
178
179
def idna_encode(name):
180
"""
181
Borrowed wholesale from the Python Cryptography Project. It turns out
182
that we can't just safely call `idna.encode`: it can explode for
183
wildcard names. This avoids that problem.
184
"""
185
import idna
186
187
try:
188
for prefix in [u"*.", u"."]:
189
if name.startswith(prefix):
190
name = name[len(prefix) :]
191
return prefix.encode("ascii") + idna.encode(name)
192
return idna.encode(name)
193
except idna.core.IDNAError:
194
return None
195
196
# Don't send IPv6 addresses through the IDNA encoder.
197
if ":" in name:
198
return name
199
200
name = idna_encode(name)
201
if name is None:
202
return None
203
elif sys.version_info >= (3, 0):
204
name = name.decode("utf-8")
205
return name
206
207
208
def get_subj_alt_name(peer_cert):
209
"""
210
Given an PyOpenSSL certificate, provides all the subject alternative names.
211
"""
212
# Pass the cert to cryptography, which has much better APIs for this.
213
if hasattr(peer_cert, "to_cryptography"):
214
cert = peer_cert.to_cryptography()
215
else:
216
# This is technically using private APIs, but should work across all
217
# relevant versions before PyOpenSSL got a proper API for this.
218
cert = _Certificate(openssl_backend, peer_cert._x509)
219
220
# We want to find the SAN extension. Ask Cryptography to locate it (it's
221
# faster than looping in Python)
222
try:
223
ext = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName).value
224
except x509.ExtensionNotFound:
225
# No such extension, return the empty list.
226
return []
227
except (
228
x509.DuplicateExtension,
229
UnsupportedExtension,
230
x509.UnsupportedGeneralNameType,
231
UnicodeError,
232
) as e:
233
# A problem has been found with the quality of the certificate. Assume
234
# no SAN field is present.
235
log.warning(
236
"A problem was encountered with the certificate that prevented "
237
"urllib3 from finding the SubjectAlternativeName field. This can "
238
"affect certificate validation. The error was %s",
239
e,
240
)
241
return []
242
243
# We want to return dNSName and iPAddress fields. We need to cast the IPs
244
# back to strings because the match_hostname function wants them as
245
# strings.
246
# Sadly the DNS names need to be idna encoded and then, on Python 3, UTF-8
247
# decoded. This is pretty frustrating, but that's what the standard library
248
# does with certificates, and so we need to attempt to do the same.
249
# We also want to skip over names which cannot be idna encoded.
250
names = [
251
("DNS", name)
252
for name in map(_dnsname_to_stdlib, ext.get_values_for_type(x509.DNSName))
253
if name is not None
254
]
255
names.extend(
256
("IP Address", str(name)) for name in ext.get_values_for_type(x509.IPAddress)
257
)
258
259
return names
260
261
262
class WrappedSocket(object):
263
"""API-compatibility wrapper for Python OpenSSL's Connection-class.
264
265
Note: _makefile_refs, _drop() and _reuse() are needed for the garbage
266
collector of pypy.
267
"""
268
269
def __init__(self, connection, socket, suppress_ragged_eofs=True):
270
self.connection = connection
271
self.socket = socket
272
self.suppress_ragged_eofs = suppress_ragged_eofs
273
self._makefile_refs = 0
274
self._closed = False
275
276
def fileno(self):
277
return self.socket.fileno()
278
279
# Copy-pasted from Python 3.5 source code
280
def _decref_socketios(self):
281
if self._makefile_refs > 0:
282
self._makefile_refs -= 1
283
if self._closed:
284
self.close()
285
286
def recv(self, *args, **kwargs):
287
try:
288
data = self.connection.recv(*args, **kwargs)
289
except OpenSSL.SSL.SysCallError as e:
290
if self.suppress_ragged_eofs and e.args == (-1, "Unexpected EOF"):
291
return b""
292
else:
293
raise SocketError(str(e))
294
except OpenSSL.SSL.ZeroReturnError:
295
if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN:
296
return b""
297
else:
298
raise
299
except OpenSSL.SSL.WantReadError:
300
if not util.wait_for_read(self.socket, self.socket.gettimeout()):
301
raise timeout("The read operation timed out")
302
else:
303
return self.recv(*args, **kwargs)
304
305
# TLS 1.3 post-handshake authentication
306
except OpenSSL.SSL.Error as e:
307
raise ssl.SSLError("read error: %r" % e)
308
else:
309
return data
310
311
def recv_into(self, *args, **kwargs):
312
try:
313
return self.connection.recv_into(*args, **kwargs)
314
except OpenSSL.SSL.SysCallError as e:
315
if self.suppress_ragged_eofs and e.args == (-1, "Unexpected EOF"):
316
return 0
317
else:
318
raise SocketError(str(e))
319
except OpenSSL.SSL.ZeroReturnError:
320
if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN:
321
return 0
322
else:
323
raise
324
except OpenSSL.SSL.WantReadError:
325
if not util.wait_for_read(self.socket, self.socket.gettimeout()):
326
raise timeout("The read operation timed out")
327
else:
328
return self.recv_into(*args, **kwargs)
329
330
# TLS 1.3 post-handshake authentication
331
except OpenSSL.SSL.Error as e:
332
raise ssl.SSLError("read error: %r" % e)
333
334
def settimeout(self, timeout):
335
return self.socket.settimeout(timeout)
336
337
def _send_until_done(self, data):
338
while True:
339
try:
340
return self.connection.send(data)
341
except OpenSSL.SSL.WantWriteError:
342
if not util.wait_for_write(self.socket, self.socket.gettimeout()):
343
raise timeout()
344
continue
345
except OpenSSL.SSL.SysCallError as e:
346
raise SocketError(str(e))
347
348
def sendall(self, data):
349
total_sent = 0
350
while total_sent < len(data):
351
sent = self._send_until_done(
352
data[total_sent : total_sent + SSL_WRITE_BLOCKSIZE]
353
)
354
total_sent += sent
355
356
def shutdown(self):
357
# FIXME rethrow compatible exceptions should we ever use this
358
self.connection.shutdown()
359
360
def close(self):
361
if self._makefile_refs < 1:
362
try:
363
self._closed = True
364
return self.connection.close()
365
except OpenSSL.SSL.Error:
366
return
367
else:
368
self._makefile_refs -= 1
369
370
def getpeercert(self, binary_form=False):
371
x509 = self.connection.get_peer_certificate()
372
373
if not x509:
374
return x509
375
376
if binary_form:
377
return OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, x509)
378
379
return {
380
"subject": ((("commonName", x509.get_subject().CN),),),
381
"subjectAltName": get_subj_alt_name(x509),
382
}
383
384
def version(self):
385
return self.connection.get_protocol_version_name()
386
387
def _reuse(self):
388
self._makefile_refs += 1
389
390
def _drop(self):
391
if self._makefile_refs < 1:
392
self.close()
393
else:
394
self._makefile_refs -= 1
395
396
397
if _fileobject: # Platform-specific: Python 2
398
399
def makefile(self, mode, bufsize=-1):
400
self._makefile_refs += 1
401
return _fileobject(self, mode, bufsize, close=True)
402
403
404
else: # Platform-specific: Python 3
405
makefile = backport_makefile
406
407
WrappedSocket.makefile = makefile
408
409
410
class PyOpenSSLContext(object):
411
"""
412
I am a wrapper class for the PyOpenSSL ``Context`` object. I am responsible
413
for translating the interface of the standard library ``SSLContext`` object
414
to calls into PyOpenSSL.
415
"""
416
417
def __init__(self, protocol):
418
self.protocol = _openssl_versions[protocol]
419
self._ctx = OpenSSL.SSL.Context(self.protocol)
420
self._options = 0
421
self.check_hostname = False
422
423
@property
424
def options(self):
425
return self._options
426
427
@options.setter
428
def options(self, value):
429
self._options = value
430
self._ctx.set_options(value)
431
432
@property
433
def verify_mode(self):
434
return _openssl_to_stdlib_verify[self._ctx.get_verify_mode()]
435
436
@verify_mode.setter
437
def verify_mode(self, value):
438
self._ctx.set_verify(_stdlib_to_openssl_verify[value], _verify_callback)
439
440
def set_default_verify_paths(self):
441
self._ctx.set_default_verify_paths()
442
443
def set_ciphers(self, ciphers):
444
if isinstance(ciphers, six.text_type):
445
ciphers = ciphers.encode("utf-8")
446
self._ctx.set_cipher_list(ciphers)
447
448
def load_verify_locations(self, cafile=None, capath=None, cadata=None):
449
if cafile is not None:
450
cafile = cafile.encode("utf-8")
451
if capath is not None:
452
capath = capath.encode("utf-8")
453
try:
454
self._ctx.load_verify_locations(cafile, capath)
455
if cadata is not None:
456
self._ctx.load_verify_locations(BytesIO(cadata))
457
except OpenSSL.SSL.Error as e:
458
raise ssl.SSLError("unable to load trusted certificates: %r" % e)
459
460
def load_cert_chain(self, certfile, keyfile=None, password=None):
461
self._ctx.use_certificate_chain_file(certfile)
462
if password is not None:
463
if not isinstance(password, six.binary_type):
464
password = password.encode("utf-8")
465
self._ctx.set_passwd_cb(lambda *_: password)
466
self._ctx.use_privatekey_file(keyfile or certfile)
467
468
def wrap_socket(
469
self,
470
sock,
471
server_side=False,
472
do_handshake_on_connect=True,
473
suppress_ragged_eofs=True,
474
server_hostname=None,
475
):
476
cnx = OpenSSL.SSL.Connection(self._ctx, sock)
477
478
if isinstance(server_hostname, six.text_type): # Platform-specific: Python 3
479
server_hostname = server_hostname.encode("utf-8")
480
481
if server_hostname is not None:
482
cnx.set_tlsext_host_name(server_hostname)
483
484
cnx.set_connect_state()
485
486
while True:
487
try:
488
cnx.do_handshake()
489
except OpenSSL.SSL.WantReadError:
490
if not util.wait_for_read(sock, sock.gettimeout()):
491
raise timeout("select timed out")
492
continue
493
except OpenSSL.SSL.Error as e:
494
raise ssl.SSLError("bad handshake: %r" % e)
495
break
496
497
return WrappedSocket(cnx, sock)
498
499
500
def _verify_callback(cnx, x509, err_no, err_depth, return_code):
501
return err_no == 0
502
503