Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/urllib3/contrib/pyopenssl.py @ 0:26e78fe6e8c4 draft
"planemo upload commit c699937486c35866861690329de38ec1a5d9f783"
author | shellac |
---|---|
date | Sat, 02 May 2020 07:14:21 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:26e78fe6e8c4 |
---|---|
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 |