diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 1ad4d61806..6ae2a05a10 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -179,7 +179,7 @@ instead. use. Typically, the server chooses a particular protocol version, and the client must adapt to the server's choice. Most of the versions are not interoperable with the other versions. If not specified, the default is - :data:`PROTOCOL_SSLv23`; it provides the most compatibility with other + :data:`PROTOCOL_TLS`; it provides the most compatibility with other versions. Here's a table showing which versions in a client (down the side) can connect @@ -188,11 +188,11 @@ instead. .. table:: ======================== ========= ========= ========== ========= =========== =========== - *client* / **server** **SSLv2** **SSLv3** **SSLv23** **TLSv1** **TLSv1.1** **TLSv1.2** + *client* / **server** **SSLv2** **SSLv3** **TLS** **TLSv1** **TLSv1.1** **TLSv1.2** ------------------------ --------- --------- ---------- --------- ----------- ----------- *SSLv2* yes no yes no no no *SSLv3* no yes yes no no no - *SSLv23* no yes yes yes yes yes + *TLS* (*SSLv23*) no yes yes yes yes yes *TLSv1* no no yes yes no no *TLSv1.1* no no yes no yes no *TLSv1.2* no no yes no no yes @@ -245,7 +245,7 @@ purposes. :const:`None`, this function can choose to trust the system's default CA certificates instead. - The settings are: :data:`PROTOCOL_SSLv23`, :data:`OP_NO_SSLv2`, and + The settings are: :data:`PROTOCOL_TLS`, :data:`OP_NO_SSLv2`, and :data:`OP_NO_SSLv3` with high encryption cipher suites without RC4 and without unauthenticated cipher suites. Passing :data:`~Purpose.SERVER_AUTH` as *purpose* sets :data:`~SSLContext.verify_mode` to :data:`CERT_REQUIRED` @@ -323,6 +323,11 @@ Random generation .. versionadded:: 3.3 + .. deprecated:: 3.5.3 + + OpenSSL has deprecated :func:`ssl.RAND_pseudo_bytes`, use + :func:`ssl.RAND_bytes` instead. + .. function:: RAND_status() Return ``True`` if the SSL pseudo-random number generator has been seeded @@ -341,6 +346,8 @@ Random generation See http://egd.sourceforge.net/ or http://prngd.sourceforge.net/ for sources of entropy-gathering daemons. + Availability: not available with LibreSSL and OpenSSL > 1.1.0 + .. function:: RAND_add(bytes, entropy) Mix the given *bytes* into the SSL pseudo-random number generator. The @@ -396,7 +403,7 @@ Certificate handling >>> time.ctime(ssl.cert_time_to_seconds("May 9 00:00:00 2007 GMT")) 'Wed May 9 00:00:00 2007' -.. function:: get_server_certificate(addr, ssl_version=PROTOCOL_SSLv3, ca_certs=None) +.. function:: get_server_certificate(addr, ssl_version=PROTOCOL_TLS, ca_certs=None) Given the address ``addr`` of an SSL-protected server, as a (*hostname*, *port-number*) pair, fetches the server's certificate, and returns it as a @@ -410,6 +417,10 @@ Certificate handling .. versionchanged:: 3.3 This function is now IPv6-compatible. + .. versionchanged:: 3.5 (backported) + The default *ssl_version* is changed from :data:`PROTOCOL_SSLv3` to + :data:`PROTOCOL_TLS` for maximum compatibility with modern servers. + .. function:: DER_cert_to_PEM_cert(DER_cert_bytes) Given a certificate as a DER-encoded blob of bytes, returns a PEM-encoded @@ -434,6 +445,9 @@ Certificate handling * :attr:`openssl_capath_env` - OpenSSL's environment key that points to a capath, * :attr:`openssl_capath` - hard coded path to a capath directory + Availability: LibreSSL ignores the environment vars + :attr:`openssl_cafile_env` and :attr:`openssl_capath_env` + .. versionadded:: 3.4 .. function:: enum_certificates(store_name) @@ -551,11 +565,21 @@ Constants .. versionadded:: 3.4.4 -.. data:: PROTOCOL_SSLv23 +.. data:: PROTOCOL_TLS Selects the highest protocol version that both the client and server support. Despite the name, this option can select "TLS" protocols as well as "SSL". + .. versionadded:: 3.5.3 (backported) + +.. data:: PROTOCOL_SSLv23 + + Alias for data:`PROTOCOL_TLS`. + + .. deprecated:: 3.5.3 + + Use data:`PROTOCOL_TLS` instead. + .. data:: PROTOCOL_SSLv2 Selects SSL version 2 as the channel encryption protocol. @@ -567,6 +591,10 @@ Constants SSL version 2 is insecure. Its use is highly discouraged. + .. deprecated:: 3.5.3 + + OpenSSL has removed support for SSLv2. + .. data:: PROTOCOL_SSLv3 Selects SSL version 3 as the channel encryption protocol. @@ -578,10 +606,20 @@ Constants SSL version 3 is insecure. Its use is highly discouraged. + .. deprecated:: 3.5.3 + + OpenSSL has deprecated all version specific protocols. Use the default + protocol data:`PROTOCOL_TLS` with flags like data:`OP_NO_SSLv3` instead. + .. data:: PROTOCOL_TLSv1 Selects TLS version 1.0 as the channel encryption protocol. + .. deprecated:: 3.5.3 + + OpenSSL has deprecated all version specific protocols. Use the default + protocol data:`PROTOCOL_TLS` with flags like data:`OP_NO_SSLv3` instead. + .. data:: PROTOCOL_TLSv1_1 Selects TLS version 1.1 as the channel encryption protocol. @@ -589,6 +627,11 @@ Constants .. versionadded:: 3.4 + .. deprecated:: 3.5.3 + + OpenSSL has deprecated all version specific protocols. Use the default + protocol data:`PROTOCOL_TLS` with flags like data:`OP_NO_SSLv3` instead. + .. data:: PROTOCOL_TLSv1_2 Selects TLS version 1.2 as the channel encryption protocol. This is the @@ -597,6 +640,11 @@ Constants .. versionadded:: 3.4 + .. deprecated:: 3.5.3 + + OpenSSL has deprecated all version specific protocols. Use the default + protocol data:`PROTOCOL_TLS` with flags like data:`OP_NO_SSLv3` instead. + .. data:: OP_ALL Enables workarounds for various bugs present in other SSL implementations. @@ -608,23 +656,32 @@ Constants .. data:: OP_NO_SSLv2 Prevents an SSLv2 connection. This option is only applicable in - conjunction with :const:`PROTOCOL_SSLv23`. It prevents the peers from + conjunction with :const:`PROTOCOL_TLS`. It prevents the peers from choosing SSLv2 as the protocol version. .. versionadded:: 3.2 + .. deprecated:: 3.5.3 + + SSLv2 is deprecated + + .. data:: OP_NO_SSLv3 Prevents an SSLv3 connection. This option is only applicable in - conjunction with :const:`PROTOCOL_SSLv23`. It prevents the peers from + conjunction with :const:`PROTOCOL_TLS`. It prevents the peers from choosing SSLv3 as the protocol version. .. versionadded:: 3.2 + .. deprecated:: 3.5.3 + + SSLv3 is deprecated + .. data:: OP_NO_TLSv1 Prevents a TLSv1 connection. This option is only applicable in - conjunction with :const:`PROTOCOL_SSLv23`. It prevents the peers from + conjunction with :const:`PROTOCOL_TLS`. It prevents the peers from choosing TLSv1 as the protocol version. .. versionadded:: 3.2 @@ -632,7 +689,7 @@ Constants .. data:: OP_NO_TLSv1_1 Prevents a TLSv1.1 connection. This option is only applicable in conjunction - with :const:`PROTOCOL_SSLv23`. It prevents the peers from choosing TLSv1.1 as + with :const:`PROTOCOL_TLS`. It prevents the peers from choosing TLSv1.1 as the protocol version. Available only with openssl version 1.0.1+. .. versionadded:: 3.4 @@ -640,7 +697,7 @@ Constants .. data:: OP_NO_TLSv1_2 Prevents a TLSv1.2 connection. This option is only applicable in conjunction - with :const:`PROTOCOL_SSLv23`. It prevents the peers from choosing TLSv1.2 as + with :const:`PROTOCOL_TLS`. It prevents the peers from choosing TLSv1.2 as the protocol version. Available only with openssl version 1.0.1+. .. versionadded:: 3.4 @@ -1002,17 +1059,21 @@ such as SSL configuration options, certificate(s) and private key(s). It also manages a cache of SSL sessions for server-side sockets, in order to speed up repeated connections from the same clients. -.. class:: SSLContext(protocol) +.. class:: SSLContext(protocol=PROTOCOL_TLS) - Create a new SSL context. You must pass *protocol* which must be one + Create a new SSL context. You may pass *protocol* which must be one of the ``PROTOCOL_*`` constants defined in this module. - :data:`PROTOCOL_SSLv23` is currently recommended for maximum - interoperability. + :data:`PROTOCOL_TLS` is currently recommended for maximum + interoperability and default value. .. seealso:: :func:`create_default_context` lets the :mod:`ssl` module choose security settings for a given purpose. + .. versionchanged:: 3.5.3 (backported) + + :data:`PROTOCOL_TLS` is the default value. + :class:`SSLContext` objects have the following methods and attributes: @@ -1141,6 +1202,23 @@ to speed up repeated connections from the same clients. when connected, the :meth:`SSLSocket.cipher` method of SSL sockets will give the currently selected cipher. +.. method:: SSLContext.set_alpn_protocols(protocols) + + Specify which protocols the socket should advertise during the SSL/TLS + handshake. It should be a list of ASCII strings, like ``['http/1.1', + 'spdy/2']``, ordered by preference. The selection of a protocol will happen + during the handshake, and will play out according to :rfc:`7301`. After a + successful handshake, the :meth:`SSLSocket.selected_alpn_protocol` method will + return the agreed-upon protocol. + + This method will raise :exc:`NotImplementedError` if :data:`HAS_ALPN` is + False. + + OpenSSL 1.1.0+ will abort the handshake and raise :exc:`SSLError` when + both sides support ALPN but cannot agree on a protocol. + + .. versionadded:: 3.5 (backported) + .. method:: SSLContext.set_npn_protocols(protocols) Specify which protocols the socket should advertise during the SSL/TLS @@ -1495,7 +1573,7 @@ If you prefer to tune security settings yourself, you might create a context from scratch (but beware that you might not get the settings right):: - >>> context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + >>> context = ssl.SSLContext(ssl.PROTOCOL_TLS) >>> context.verify_mode = ssl.CERT_REQUIRED >>> context.check_hostname = True >>> context.load_verify_locations("/etc/ssl/certs/ca-bundle.crt") @@ -1760,15 +1838,17 @@ Protocol versions SSL versions 2 and 3 are considered insecure and are therefore dangerous to use. If you want maximum compatibility between clients and servers, it is -recommended to use :const:`PROTOCOL_SSLv23` as the protocol version and then +recommended to use :const:`PROTOCOL_TLS` as the protocol version and then disable SSLv2 and SSLv3 explicitly using the :data:`SSLContext.options` attribute:: - context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + context = ssl.SSLContext(ssl.PROTOCOL_TLS) context.options |= ssl.OP_NO_SSLv2 context.options |= ssl.OP_NO_SSLv3 + context.options |= ssl.OP_NO_TLSv1 + context.options |= ssl.OP_NO_TLSv1_1 -The SSL context created above will only allow TLSv1 and later (if +The SSL context created above will only allow TLSv1.2 and later (if supported by your system) connections. Cipher selection diff --git a/Lib/ssl.py b/Lib/ssl.py index 873aa4798b..c1aaec74ec 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -51,6 +51,7 @@ The following constants identify various SSL protocol variants: PROTOCOL_SSLv2 PROTOCOL_SSLv3 PROTOCOL_SSLv23 +PROTOCOL_TLS PROTOCOL_TLSv1 PROTOCOL_TLSv1_1 PROTOCOL_TLSv1_2 @@ -92,7 +93,7 @@ import re import sys import os from collections import namedtuple -from enum import Enum as _Enum +from enum import Enum as _Enum, IntEnum as _IntEnum import _ssl # if we can't import it, let the error propagate @@ -126,23 +127,18 @@ from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN from _ssl import _OPENSSL_API_VERSION +_IntEnum._convert( + '_SSLMethod', __name__, + lambda name: name.startswith('PROTOCOL_') and name != 'PROTOCOL_SSLv23', + source=_ssl) + +PROTOCOL_SSLv23 = _SSLMethod.PROTOCOL_SSLv23 = _SSLMethod.PROTOCOL_TLS +_PROTOCOL_NAMES = {value: name for name, value in _SSLMethod.__members__.items()} -_PROTOCOL_NAMES = {value: name for name, value in globals().items() if name.startswith('PROTOCOL_')} try: - from _ssl import PROTOCOL_SSLv2 _SSLv2_IF_EXISTS = PROTOCOL_SSLv2 -except ImportError: +except NameError: _SSLv2_IF_EXISTS = None -else: - _PROTOCOL_NAMES[PROTOCOL_SSLv2] = "SSLv2" - -try: - from _ssl import PROTOCOL_TLSv1_1, PROTOCOL_TLSv1_2 -except ImportError: - pass -else: - _PROTOCOL_NAMES[PROTOCOL_TLSv1_1] = "TLSv1.1" - _PROTOCOL_NAMES[PROTOCOL_TLSv1_2] = "TLSv1.2" if sys.platform == "win32": from _ssl import enum_certificates, enum_crls @@ -348,13 +344,13 @@ class SSLContext(_SSLContext): __slots__ = ('protocol', '__weakref__') _windows_cert_stores = ("CA", "ROOT") - def __new__(cls, protocol, *args, **kwargs): + def __new__(cls, protocol=PROTOCOL_TLS, *args, **kwargs): self = _SSLContext.__new__(cls, protocol) if protocol != _SSLv2_IF_EXISTS: self.set_ciphers(_DEFAULT_CIPHERS) return self - def __init__(self, protocol): + def __init__(self, protocol=PROTOCOL_TLS): self.protocol = protocol def wrap_socket(self, sock, server_side=False, @@ -408,7 +404,7 @@ def create_default_context(purpose=Purpose.SERVER_AUTH, *, cafile=None, if not isinstance(purpose, _ASN1Object): raise TypeError(purpose) - context = SSLContext(PROTOCOL_SSLv23) + context = SSLContext(PROTOCOL_TLS) # SSLv2 considered harmful. context.options |= OP_NO_SSLv2 @@ -445,7 +441,7 @@ def create_default_context(purpose=Purpose.SERVER_AUTH, *, cafile=None, context.load_default_certs(purpose) return context -def _create_unverified_context(protocol=PROTOCOL_SSLv23, *, cert_reqs=None, +def _create_unverified_context(protocol=PROTOCOL_TLS, *, cert_reqs=None, check_hostname=False, purpose=Purpose.SERVER_AUTH, certfile=None, keyfile=None, cafile=None, capath=None, cadata=None): @@ -501,7 +497,7 @@ class SSLSocket(socket): def __init__(self, sock=None, keyfile=None, certfile=None, server_side=False, cert_reqs=CERT_NONE, - ssl_version=PROTOCOL_SSLv23, ca_certs=None, + ssl_version=PROTOCOL_TLS, ca_certs=None, do_handshake_on_connect=True, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None, suppress_ragged_eofs=True, npn_protocols=None, ciphers=None, @@ -883,7 +879,7 @@ class SSLSocket(socket): def wrap_socket(sock, keyfile=None, certfile=None, server_side=False, cert_reqs=CERT_NONE, - ssl_version=PROTOCOL_SSLv23, ca_certs=None, + ssl_version=PROTOCOL_TLS, ca_certs=None, do_handshake_on_connect=True, suppress_ragged_eofs=True, ciphers=None): @@ -930,7 +926,7 @@ def PEM_cert_to_DER_cert(pem_cert_string): d = pem_cert_string.strip()[len(PEM_HEADER):-len(PEM_FOOTER)] return base64.decodebytes(d.encode('ASCII', 'strict')) -def get_server_certificate(addr, ssl_version=PROTOCOL_SSLv23, ca_certs=None): +def get_server_certificate(addr, ssl_version=PROTOCOL_TLS, ca_certs=None): """Retrieve the certificate from the server at the specified address, and return it as a PEM-encoded string. If 'ca_certs' is specified, validate the server cert against it. diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 19ef354a65..b48003f69f 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -23,6 +23,9 @@ ssl = support.import_module("ssl") PROTOCOLS = sorted(ssl._PROTOCOL_NAMES) HOST = support.HOST +IS_LIBRESSL = ssl.OPENSSL_VERSION.startswith('LibreSSL') +IS_OPENSSL_1_1 = not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (1, 1, 0) + def data_file(*name): return os.path.join(os.path.dirname(__file__), *name) @@ -134,6 +137,14 @@ class BasicSocketTests(unittest.TestCase): self.assertIn(ssl.HAS_SNI, {True, False}) self.assertIn(ssl.HAS_ECDH, {True, False}) + def test_str_for_enums(self): + # Make sure that the PROTOCOL_* constants have enum-like string + # reprs. + proto = ssl.PROTOCOL_TLS + self.assertEqual(str(proto), '_SSLMethod.PROTOCOL_TLS') + ctx = ssl.SSLContext(proto) + self.assertIs(ctx.protocol, proto) + def test_random(self): v = ssl.RAND_status() if support.verbose: @@ -296,9 +307,9 @@ class BasicSocketTests(unittest.TestCase): self.assertGreaterEqual(status, 0) self.assertLessEqual(status, 15) # Version string as returned by {Open,Libre}SSL, the format might change - if "LibreSSL" in s: - self.assertTrue(s.startswith("LibreSSL {:d}.{:d}".format(major, minor)), - (s, t)) + if IS_LIBRESSL: + self.assertTrue(s.startswith("LibreSSL {:d}".format(major)), + (s, t, hex(n))) else: self.assertTrue(s.startswith("OpenSSL {:d}.{:d}.{:d}".format(major, minor, fix)), (s, t)) @@ -655,7 +666,8 @@ class ContextTests(unittest.TestCase): def test_constructor(self): for protocol in PROTOCOLS: ssl.SSLContext(protocol) - self.assertRaises(TypeError, ssl.SSLContext) + ctx = ssl.SSLContext() + self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLS) self.assertRaises(ValueError, ssl.SSLContext, -1) self.assertRaises(ValueError, ssl.SSLContext, 42) @@ -676,15 +688,15 @@ class ContextTests(unittest.TestCase): def test_options(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) # OP_ALL | OP_NO_SSLv2 | OP_NO_SSLv3 is the default value - self.assertEqual(ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3, - ctx.options) + default = (ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3) + if not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (1, 1, 0): + default |= ssl.OP_NO_COMPRESSION + self.assertEqual(default, ctx.options) ctx.options |= ssl.OP_NO_TLSv1 - self.assertEqual(ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1, - ctx.options) + self.assertEqual(default | ssl.OP_NO_TLSv1, ctx.options) if can_clear_options(): - ctx.options = (ctx.options & ~ssl.OP_NO_SSLv2) | ssl.OP_NO_TLSv1 - self.assertEqual(ssl.OP_ALL | ssl.OP_NO_TLSv1 | ssl.OP_NO_SSLv3, - ctx.options) + ctx.options = (ctx.options & ~ssl.OP_NO_TLSv1) + self.assertEqual(default, ctx.options) ctx.options = 0 # Ubuntu has OP_NO_SSLv3 forced on by default self.assertEqual(0, ctx.options & ~ssl.OP_NO_SSLv3) @@ -1020,6 +1032,7 @@ class ContextTests(unittest.TestCase): self.assertRaises(TypeError, ctx.load_default_certs, 'SERVER_AUTH') @unittest.skipIf(sys.platform == "win32", "not-Windows specific") + @unittest.skipIf(IS_LIBRESSL, "LibreSSL doesn't support env vars") def test_load_default_certs_env(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) with support.EnvironmentVarGuard() as env: @@ -1505,6 +1518,94 @@ class NetworkedTests(unittest.TestCase): self.assertIs(ss.context, ctx2) self.assertIs(ss._sslobj.context, ctx2) + +class NetworkedBIOTests(unittest.TestCase): + + def ssl_io_loop(self, sock, incoming, outgoing, func, *args, **kwargs): + # A simple IO loop. Call func(*args) depending on the error we get + # (WANT_READ or WANT_WRITE) move data between the socket and the BIOs. + timeout = kwargs.get('timeout', 10) + count = 0 + while True: + errno = None + count += 1 + try: + ret = func(*args) + except ssl.SSLError as e: + if e.errno not in (ssl.SSL_ERROR_WANT_READ, + ssl.SSL_ERROR_WANT_WRITE): + raise + errno = e.errno + # Get any data from the outgoing BIO irrespective of any error, and + # send it to the socket. + buf = outgoing.read() + sock.sendall(buf) + # If there's no error, we're done. For WANT_READ, we need to get + # data from the socket and put it in the incoming BIO. + if errno is None: + break + elif errno == ssl.SSL_ERROR_WANT_READ: + buf = sock.recv(32768) + if buf: + incoming.write(buf) + else: + incoming.write_eof() + if support.verbose: + sys.stdout.write("Needed %d calls to complete %s().\n" + % (count, func.__name__)) + return ret + + def test_handshake(self): + with support.transient_internet(REMOTE_HOST): + sock = socket.socket(socket.AF_INET) + sock.connect((REMOTE_HOST, 443)) + incoming = ssl.MemoryBIO() + outgoing = ssl.MemoryBIO() + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.verify_mode = ssl.CERT_REQUIRED + ctx.load_verify_locations(REMOTE_ROOT_CERT) + ctx.check_hostname = True + sslobj = ctx.wrap_bio(incoming, outgoing, False, REMOTE_HOST) + self.assertIs(sslobj._sslobj.owner, sslobj) + self.assertIsNone(sslobj.cipher()) + self.assertIsNotNone(sslobj.shared_ciphers()) + self.assertRaises(ValueError, sslobj.getpeercert) + if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES: + self.assertIsNone(sslobj.get_channel_binding('tls-unique')) + self.ssl_io_loop(sock, incoming, outgoing, sslobj.do_handshake) + self.assertTrue(sslobj.cipher()) + self.assertIsNotNone(sslobj.shared_ciphers()) + self.assertTrue(sslobj.getpeercert()) + if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES: + self.assertTrue(sslobj.get_channel_binding('tls-unique')) + try: + self.ssl_io_loop(sock, incoming, outgoing, sslobj.unwrap) + except ssl.SSLSyscallError: + # self-signed.pythontest.net probably shuts down the TCP + # connection without sending a secure shutdown message, and + # this is reported as SSL_ERROR_SYSCALL + pass + self.assertRaises(ssl.SSLError, sslobj.write, b'foo') + sock.close() + + def test_read_write_data(self): + with support.transient_internet(REMOTE_HOST): + sock = socket.socket(socket.AF_INET) + sock.connect((REMOTE_HOST, 443)) + incoming = ssl.MemoryBIO() + outgoing = ssl.MemoryBIO() + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.verify_mode = ssl.CERT_NONE + sslobj = ctx.wrap_bio(incoming, outgoing, False) + self.ssl_io_loop(sock, incoming, outgoing, sslobj.do_handshake) + req = b'GET / HTTP/1.0\r\n\r\n' + self.ssl_io_loop(sock, incoming, outgoing, sslobj.write, req) + buf = self.ssl_io_loop(sock, incoming, outgoing, sslobj.read, 1024) + self.assertEqual(buf[:5], b'HTTP/') + self.ssl_io_loop(sock, incoming, outgoing, sslobj.unwrap) + sock.close() + + try: import threading except ImportError: @@ -2629,6 +2730,21 @@ else: s.connect((HOST, server.port)) self.assertIn("no shared cipher", str(server.conn_errors[0])) + def test_version_basic(self): + """ + Basic tests for SSLSocket.version(). + More tests are done in the test_protocol_*() methods. + """ + context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + with ThreadedEchoServer(CERTFILE, + ssl_version=ssl.PROTOCOL_TLSv1, + chatty=False) as server: + with context.wrap_socket(socket.socket()) as s: + self.assertIs(s.version(), None) + s.connect((HOST, server.port)) + self.assertEqual(s.version(), 'TLSv1') + self.assertIs(s.version(), None) + @unittest.skipUnless(ssl.HAS_ECDH, "test requires ECDH-enabled OpenSSL") def test_default_ecdh_curve(self): # Issue #21015: elliptic curve-based Diffie Hellman key exchange @@ -2738,6 +2854,67 @@ else: if "ADH" not in parts and "EDH" not in parts and "DHE" not in parts: self.fail("Non-DH cipher: " + cipher[0]) + def test_selected_alpn_protocol(self): + # selected_alpn_protocol() is None unless ALPN is used. + context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + context.load_cert_chain(CERTFILE) + stats = server_params_test(context, context, + chatty=True, connectionchatty=True) + self.assertIs(stats['client_alpn_protocol'], None) + + @unittest.skipUnless(ssl.HAS_ALPN, "ALPN support required") + def test_selected_alpn_protocol_if_server_uses_alpn(self): + # selected_alpn_protocol() is None unless ALPN is used by the client. + client_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + client_context.load_verify_locations(CERTFILE) + server_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + server_context.load_cert_chain(CERTFILE) + server_context.set_alpn_protocols(['foo', 'bar']) + stats = server_params_test(client_context, server_context, + chatty=True, connectionchatty=True) + self.assertIs(stats['client_alpn_protocol'], None) + + @unittest.skipUnless(ssl.HAS_ALPN, "ALPN support needed for this test") + def test_alpn_protocols(self): + server_protocols = ['foo', 'bar', 'milkshake'] + protocol_tests = [ + (['foo', 'bar'], 'foo'), + (['bar', 'foo'], 'foo'), + (['milkshake'], 'milkshake'), + (['http/3.0', 'http/4.0'], None) + ] + for client_protocols, expected in protocol_tests: + server_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) + server_context.load_cert_chain(CERTFILE) + server_context.set_alpn_protocols(server_protocols) + client_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) + client_context.load_cert_chain(CERTFILE) + client_context.set_alpn_protocols(client_protocols) + + try: + stats = server_params_test(client_context, + server_context, + chatty=True, + connectionchatty=True) + except ssl.SSLError as e: + stats = e + + if expected is None and IS_OPENSSL_1_1: + # OpenSSL 1.1.0 raises handshake error + self.assertIsInstance(stats, ssl.SSLError) + else: + msg = "failed trying %s (s) and %s (c).\n" \ + "was expecting %s, but got %%s from the %%s" \ + % (str(server_protocols), str(client_protocols), + str(expected)) + client_result = stats['client_alpn_protocol'] + self.assertEqual(client_result, expected, + msg % (client_result, "client")) + server_result = stats['server_alpn_protocols'][-1] \ + if len(stats['server_alpn_protocols']) else 'nothing' + self.assertEqual(server_result, expected, + msg % (server_result, "server")) + def test_selected_npn_protocol(self): # selected_npn_protocol() is None unless NPN is used context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) @@ -2878,6 +3055,30 @@ else: self.assertEqual(cm.exception.reason, 'TLSV1_ALERT_INTERNAL_ERROR') self.assertIn("TypeError", stderr.getvalue()) + def test_shared_ciphers(self): + server_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + server_context.load_cert_chain(SIGNED_CERTFILE) + client_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + client_context.verify_mode = ssl.CERT_REQUIRED + client_context.load_verify_locations(SIGNING_CA) + if ssl.OPENSSL_VERSION_INFO >= (1, 0, 2): + client_context.set_ciphers("AES128:AES256") + server_context.set_ciphers("AES256") + alg1 = "AES256" + alg2 = "AES-256" + else: + client_context.set_ciphers("AES:3DES") + server_context.set_ciphers("3DES") + alg1 = "3DES" + alg2 = "DES-CBC3" + + stats = server_params_test(client_context, server_context) + ciphers = stats['server_shared_ciphers'][0] + self.assertGreater(len(ciphers), 0) + for name, tls_version, bits in ciphers: + if not alg1 in name.split("-") and alg2 not in name: + self.fail(name) + def test_read_write_after_close_raises_valuerror(self): context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) context.verify_mode = ssl.CERT_REQUIRED diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c index 5b0a7be12b..1f353e4bd8 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c @@ -20,7 +20,6 @@ /* EVP is the preferred interface to hashing in OpenSSL */ #include -#include /* We use the object interface to discover what hashes OpenSSL supports. */ #include #include "openssl/err.h" @@ -31,15 +30,21 @@ #define HASH_OBJ_CONSTRUCTOR 0 #endif -/* Minimum OpenSSL version needed to support sha224 and higher. */ -#if defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x00908000) -#define _OPENSSL_SUPPORTS_SHA2 +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) +/* OpenSSL < 1.1.0 */ +#define EVP_MD_CTX_new EVP_MD_CTX_create +#define EVP_MD_CTX_free EVP_MD_CTX_destroy +#define HAS_FAST_PKCS5_PBKDF2_HMAC 0 +#include +#else +/* OpenSSL >= 1.1.0 */ +#define HAS_FAST_PKCS5_PBKDF2_HMAC 1 #endif typedef struct { PyObject_HEAD PyObject *name; /* name of this hash algorithm */ - EVP_MD_CTX ctx; /* OpenSSL message digest context */ + EVP_MD_CTX *ctx; /* OpenSSL message digest context */ #ifdef WITH_THREAD PyThread_type_lock lock; /* OpenSSL context lock */ #endif @@ -51,7 +56,6 @@ static PyTypeObject EVPtype; #define DEFINE_CONSTS_FOR_NEW(Name) \ static PyObject *CONST_ ## Name ## _name_obj = NULL; \ - static EVP_MD_CTX CONST_new_ ## Name ## _ctx; \ static EVP_MD_CTX *CONST_new_ ## Name ## _ctx_p = NULL; DEFINE_CONSTS_FOR_NEW(md5) @@ -64,19 +68,57 @@ DEFINE_CONSTS_FOR_NEW(sha512) #endif +/* LCOV_EXCL_START */ +static PyObject * +_setException(PyObject *exc) +{ + unsigned long errcode; + const char *lib, *func, *reason; + + errcode = ERR_peek_last_error(); + if (!errcode) { + PyErr_SetString(exc, "unknown reasons"); + return NULL; + } + ERR_clear_error(); + + lib = ERR_lib_error_string(errcode); + func = ERR_func_error_string(errcode); + reason = ERR_reason_error_string(errcode); + + if (lib && func) { + PyErr_Format(exc, "[%s: %s] %s", lib, func, reason); + } + else if (lib) { + PyErr_Format(exc, "[%s] %s", lib, reason); + } + else { + PyErr_SetString(exc, reason); + } + return NULL; +} +/* LCOV_EXCL_STOP */ + static EVPobject * newEVPobject(PyObject *name) { EVPobject *retval = (EVPobject *)PyObject_New(EVPobject, &EVPtype); + if (retval == NULL) { + return NULL; + } + + retval->ctx = EVP_MD_CTX_new(); + if (retval->ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } /* save the name for .name to return */ - if (retval != NULL) { - Py_INCREF(name); - retval->name = name; + Py_INCREF(name); + retval->name = name; #ifdef WITH_THREAD - retval->lock = NULL; + retval->lock = NULL; #endif - } return retval; } @@ -91,7 +133,7 @@ EVP_hash(EVPobject *self, const void *vp, Py_ssize_t len) process = MUNCH_SIZE; else process = Py_SAFE_DOWNCAST(len, Py_ssize_t, unsigned int); - EVP_DigestUpdate(&self->ctx, (const void*)cp, process); + EVP_DigestUpdate(self->ctx, (const void*)cp, process); len -= process; cp += process; } @@ -106,16 +148,19 @@ EVP_dealloc(EVPobject *self) if (self->lock != NULL) PyThread_free_lock(self->lock); #endif - EVP_MD_CTX_cleanup(&self->ctx); + EVP_MD_CTX_free(self->ctx); Py_XDECREF(self->name); PyObject_Del(self); } -static void locked_EVP_MD_CTX_copy(EVP_MD_CTX *new_ctx_p, EVPobject *self) +static int +locked_EVP_MD_CTX_copy(EVP_MD_CTX *new_ctx_p, EVPobject *self) { + int result; ENTER_HASHLIB(self); - EVP_MD_CTX_copy(new_ctx_p, &self->ctx); + result = EVP_MD_CTX_copy(new_ctx_p, self->ctx); LEAVE_HASHLIB(self); + return result; } /* External methods for a hash object */ @@ -131,7 +176,9 @@ EVP_copy(EVPobject *self, PyObject *unused) if ( (newobj = newEVPobject(self->name))==NULL) return NULL; - locked_EVP_MD_CTX_copy(&newobj->ctx, self); + if (!locked_EVP_MD_CTX_copy(newobj->ctx, self)) { + return _setException(PyExc_ValueError); + } return (PyObject *)newobj; } @@ -142,16 +189,24 @@ static PyObject * EVP_digest(EVPobject *self, PyObject *unused) { unsigned char digest[EVP_MAX_MD_SIZE]; - EVP_MD_CTX temp_ctx; + EVP_MD_CTX *temp_ctx; PyObject *retval; unsigned int digest_size; - locked_EVP_MD_CTX_copy(&temp_ctx, self); - digest_size = EVP_MD_CTX_size(&temp_ctx); - EVP_DigestFinal(&temp_ctx, digest, NULL); + temp_ctx = EVP_MD_CTX_new(); + if (temp_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } + + if (!locked_EVP_MD_CTX_copy(temp_ctx, self)) { + return _setException(PyExc_ValueError); + } + digest_size = EVP_MD_CTX_size(temp_ctx); + EVP_DigestFinal(temp_ctx, digest, NULL); retval = PyBytes_FromStringAndSize((const char *)digest, digest_size); - EVP_MD_CTX_cleanup(&temp_ctx); + EVP_MD_CTX_free(temp_ctx); return retval; } @@ -162,17 +217,25 @@ static PyObject * EVP_hexdigest(EVPobject *self, PyObject *unused) { unsigned char digest[EVP_MAX_MD_SIZE]; - EVP_MD_CTX temp_ctx; + EVP_MD_CTX *temp_ctx; PyObject *retval; char *hex_digest; unsigned int i, j, digest_size; + temp_ctx = EVP_MD_CTX_new(); + if (temp_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } + /* Get the raw (binary) digest value */ - locked_EVP_MD_CTX_copy(&temp_ctx, self); - digest_size = EVP_MD_CTX_size(&temp_ctx); - EVP_DigestFinal(&temp_ctx, digest, NULL); + if (!locked_EVP_MD_CTX_copy(temp_ctx, self)) { + return _setException(PyExc_ValueError); + } + digest_size = EVP_MD_CTX_size(temp_ctx); + EVP_DigestFinal(temp_ctx, digest, NULL); - EVP_MD_CTX_cleanup(&temp_ctx); + EVP_MD_CTX_free(temp_ctx); /* Allocate a new buffer */ hex_digest = PyMem_Malloc(digest_size * 2 + 1); @@ -241,7 +304,7 @@ static PyObject * EVP_get_block_size(EVPobject *self, void *closure) { long block_size; - block_size = EVP_MD_CTX_block_size(&self->ctx); + block_size = EVP_MD_CTX_block_size(self->ctx); return PyLong_FromLong(block_size); } @@ -249,7 +312,7 @@ static PyObject * EVP_get_digest_size(EVPobject *self, void *closure) { long size; - size = EVP_MD_CTX_size(&self->ctx); + size = EVP_MD_CTX_size(self->ctx); return PyLong_FromLong(size); } @@ -310,7 +373,7 @@ EVP_tp_init(EVPobject *self, PyObject *args, PyObject *kwds) PyBuffer_Release(&view); return -1; } - EVP_DigestInit(&self->ctx, digest); + EVP_DigestInit(self->ctx, digest); self->name = name_obj; Py_INCREF(self->name); @@ -407,9 +470,9 @@ EVPnew(PyObject *name_obj, return NULL; if (initial_ctx) { - EVP_MD_CTX_copy(&self->ctx, initial_ctx); + EVP_MD_CTX_copy(self->ctx, initial_ctx); } else { - EVP_DigestInit(&self->ctx, digest); + EVP_DigestInit(self->ctx, digest); } if (cp && len) { @@ -475,6 +538,7 @@ EVP_new(PyObject *self, PyObject *args, PyObject *kwdict) #define PY_PBKDF2_HMAC 1 +#if !HAS_FAST_PKCS5_PBKDF2_HMAC /* Improved implementation of PKCS5_PBKDF2_HMAC() * * PKCS5_PBKDF2_HMAC_fast() hashes the password exactly one time instead of @@ -556,37 +620,8 @@ PKCS5_PBKDF2_HMAC_fast(const char *pass, int passlen, HMAC_CTX_cleanup(&hctx_tpl); return 1; } +#endif -/* LCOV_EXCL_START */ -static PyObject * -_setException(PyObject *exc) -{ - unsigned long errcode; - const char *lib, *func, *reason; - - errcode = ERR_peek_last_error(); - if (!errcode) { - PyErr_SetString(exc, "unknown reasons"); - return NULL; - } - ERR_clear_error(); - - lib = ERR_lib_error_string(errcode); - func = ERR_func_error_string(errcode); - reason = ERR_reason_error_string(errcode); - - if (lib && func) { - PyErr_Format(exc, "[%s: %s] %s", lib, func, reason); - } - else if (lib) { - PyErr_Format(exc, "[%s] %s", lib, reason); - } - else { - PyErr_SetString(exc, reason); - } - return NULL; -} -/* LCOV_EXCL_STOP */ PyDoc_STRVAR(pbkdf2_hmac__doc__, "pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None) -> key\n\ @@ -668,10 +703,17 @@ pbkdf2_hmac(PyObject *self, PyObject *args, PyObject *kwdict) key = PyBytes_AS_STRING(key_obj); Py_BEGIN_ALLOW_THREADS +#if HAS_FAST_PKCS5_PBKDF2_HMAC + retval = PKCS5_PBKDF2_HMAC((char*)password.buf, (int)password.len, + (unsigned char *)salt.buf, (int)salt.len, + iterations, digest, dklen, + (unsigned char *)key); +#else retval = PKCS5_PBKDF2_HMAC_fast((char*)password.buf, (int)password.len, (unsigned char *)salt.buf, (int)salt.len, iterations, digest, dklen, (unsigned char *)key); +#endif Py_END_ALLOW_THREADS if (!retval) { @@ -790,7 +832,7 @@ generate_hash_name_list(void) if (CONST_ ## NAME ## _name_obj == NULL) { \ CONST_ ## NAME ## _name_obj = PyUnicode_FromString(#NAME); \ if (EVP_get_digestbyname(#NAME)) { \ - CONST_new_ ## NAME ## _ctx_p = &CONST_new_ ## NAME ## _ctx; \ + CONST_new_ ## NAME ## _ctx_p = EVP_MD_CTX_new(); \ EVP_DigestInit(CONST_new_ ## NAME ## _ctx_p, EVP_get_digestbyname(#NAME)); \ } \ } \ diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 02971a7515..d717d0f7b3 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -55,6 +55,14 @@ static PySocketModule_APIObject PySocketModule; #include #endif +/* Don't warn about deprecated functions */ +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + /* Include OpenSSL header files */ #include "openssl/rsa.h" #include "openssl/crypto.h" @@ -90,6 +98,10 @@ struct py_ssl_library_code { /* Include generated data (error codes) */ #include "_ssl_data.h" +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) +# define OPENSSL_VERSION_1_1 1 +#endif + /* Openssl comes with TLSv1.1 and TLSv1.2 between 1.0.0h and 1.0.1 http://www.openssl.org/news/changelog.html */ @@ -108,6 +120,80 @@ struct py_ssl_library_code { # define HAVE_SNI 0 #endif +#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation +# define HAVE_ALPN +#endif + +#ifndef INVALID_SOCKET /* MS defines this */ +#define INVALID_SOCKET (-1) +#endif + +#ifdef OPENSSL_VERSION_1_1 +/* OpenSSL 1.1.0+ */ +#ifndef OPENSSL_NO_SSL2 +#define OPENSSL_NO_SSL2 +#endif +#else /* OpenSSL < 1.1.0 */ +#if defined(WITH_THREAD) +#define HAVE_OPENSSL_CRYPTO_LOCK +#endif + +#define TLS_method SSLv23_method + +static int X509_NAME_ENTRY_set(const X509_NAME_ENTRY *ne) +{ + return ne->set; +} + +#ifndef OPENSSL_NO_COMP +static int COMP_get_type(const COMP_METHOD *meth) +{ + return meth->type; +} + +static const char *COMP_get_name(const COMP_METHOD *meth) +{ + return meth->name; +} +#endif + +static pem_password_cb *SSL_CTX_get_default_passwd_cb(SSL_CTX *ctx) +{ + return ctx->default_passwd_callback; +} + +static void *SSL_CTX_get_default_passwd_cb_userdata(SSL_CTX *ctx) +{ + return ctx->default_passwd_callback_userdata; +} + +static int X509_OBJECT_get_type(X509_OBJECT *x) +{ + return x->type; +} + +static X509 *X509_OBJECT_get0_X509(X509_OBJECT *x) +{ + return x->data.x509; +} + +static int BIO_up_ref(BIO *b) +{ + CRYPTO_add(&b->references, 1, CRYPTO_LOCK_BIO); + return 1; +} + +static STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(X509_STORE *store) { + return store->objs; +} + +static X509_VERIFY_PARAM *X509_STORE_get0_param(X509_STORE *store) +{ + return store->param; +} +#endif /* OpenSSL < 1.1.0 or LibreSSL */ + + enum py_ssl_error { /* these mirror ssl.h */ PY_SSL_ERROR_NONE, @@ -138,7 +224,7 @@ enum py_ssl_cert_requirements { enum py_ssl_version { PY_SSL_VERSION_SSL2, PY_SSL_VERSION_SSL3=1, - PY_SSL_VERSION_SSL23, + PY_SSL_VERSION_TLS, #if HAVE_TLSv1_2 PY_SSL_VERSION_TLS1, PY_SSL_VERSION_TLS1_1, @@ -504,7 +590,7 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, PySSL_BEGIN_ALLOW_THREADS self->ssl = SSL_new(ctx); PySSL_END_ALLOW_THREADS - SSL_set_app_data(self->ssl,self); + SSL_set_app_data(self->ssl, self); SSL_set_fd(self->ssl, Py_SAFE_DOWNCAST(sock->sock_fd, SOCKET_T, int)); mode = SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER; #ifdef SSL_MODE_AUTO_RETRY @@ -691,7 +777,7 @@ _create_tuple_for_X509_NAME (X509_NAME *xname) /* check to see if we've gotten to a new RDN */ if (rdn_level >= 0) { - if (rdn_level != entry->set) { + if (rdn_level != X509_NAME_ENTRY_set(entry)) { /* yes, new RDN */ /* add old RDN to DN */ rdnt = PyList_AsTuple(rdn); @@ -708,7 +794,7 @@ _create_tuple_for_X509_NAME (X509_NAME *xname) goto fail0; } } - rdn_level = entry->set; + rdn_level = X509_NAME_ENTRY_set(entry); /* now add this attribute to the current RDN */ name = X509_NAME_ENTRY_get_object(entry); @@ -811,18 +897,18 @@ _get_peer_alt_names (X509 *certificate) { goto fail; } - p = ext->value->data; + p = X509_EXTENSION_get_data(ext)->data; if (method->it) names = (GENERAL_NAMES*) (ASN1_item_d2i(NULL, &p, - ext->value->length, + X509_EXTENSION_get_data(ext)->length, ASN1_ITEM_ptr(method->it))); else names = (GENERAL_NAMES*) (method->d2i(NULL, &p, - ext->value->length)); + X509_EXTENSION_get_data(ext)->length)); for(j = 0; j < sk_GENERAL_NAME_num(names); j++) { /* get a rendering of each name in the set of names */ @@ -1033,13 +1119,11 @@ _get_crl_dp(X509 *certificate) { int i, j; PyObject *lst, *res = NULL; -#if OPENSSL_VERSION_NUMBER < 0x10001000L - dps = X509_get_ext_d2i(certificate, NID_crl_distribution_points, NULL, NULL); -#else +#if OPENSSL_VERSION_NUMBER >= 0x10001000L /* Calls x509v3_cache_extensions and sets up crldp */ X509_check_ca(certificate); - dps = certificate->crldp; #endif + dps = X509_get_ext_d2i(certificate, NID_crl_distribution_points, NULL, NULL); if (dps == NULL) return Py_None; @@ -1403,6 +1487,71 @@ static PyObject *PySSL_cipher (PySSLSocket *self) { return NULL; } +/*[clinic input] +_ssl._SSLSocket.shared_ciphers +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_shared_ciphers_impl(PySSLSocket *self) +/*[clinic end generated code: output=3d174ead2e42c4fd input=0bfe149da8fe6306]*/ +{ + STACK_OF(SSL_CIPHER) *ciphers; + int i; + PyObject *res; + + ciphers = SSL_get_ciphers(self->ssl); + if (!ciphers) + Py_RETURN_NONE; + res = PyList_New(sk_SSL_CIPHER_num(ciphers)); + if (!res) + return NULL; + for (i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) { + PyObject *tup = cipher_to_tuple(sk_SSL_CIPHER_value(ciphers, i)); + if (!tup) { + Py_DECREF(res); + return NULL; + } + PyList_SET_ITEM(res, i, tup); + } + return res; +} + +/*[clinic input] +_ssl._SSLSocket.cipher +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_cipher_impl(PySSLSocket *self) +/*[clinic end generated code: output=376417c16d0e5815 input=548fb0e27243796d]*/ +{ + const SSL_CIPHER *current; + + if (self->ssl == NULL) + Py_RETURN_NONE; + current = SSL_get_current_cipher(self->ssl); + if (current == NULL) + Py_RETURN_NONE; + return cipher_to_tuple(current); +} + +/*[clinic input] +_ssl._SSLSocket.version +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_version_impl(PySSLSocket *self) +/*[clinic end generated code: output=178aed33193b2cdb input=900186a503436fd6]*/ +{ + const char *version; + + if (self->ssl == NULL) + Py_RETURN_NONE; + version = SSL_get_version(self->ssl); + if (!strcmp(version, "unknown")) + Py_RETURN_NONE; + return PyUnicode_FromString(version); +} + #ifdef OPENSSL_NPN_NEGOTIATED static PyObject *PySSL_selected_npn_protocol(PySSLSocket *self) { const unsigned char *out; @@ -1427,9 +1576,9 @@ static PyObject *PySSL_compression(PySSLSocket *self) { if (self->ssl == NULL) Py_RETURN_NONE; comp_method = SSL_get_current_compression(self->ssl); - if (comp_method == NULL || comp_method->type == NID_undef) + if (comp_method == NULL || COMP_get_type(comp_method) == NID_undef) Py_RETURN_NONE; - short_name = OBJ_nid2sn(comp_method->type); + short_name = COMP_get_name(comp_method); if (short_name == NULL) Py_RETURN_NONE; return PyUnicode_DecodeFSDefault(short_name); @@ -1977,7 +2126,7 @@ context_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { char *kwlist[] = {"protocol", NULL}; PySSLContext *self; - int proto_version = PY_SSL_VERSION_SSL23; + int proto_version = PY_SSL_VERSION_TLS; long options; SSL_CTX *ctx = NULL; @@ -2003,8 +2152,8 @@ context_new(PyTypeObject *type, PyObject *args, PyObject *kwds) else if (proto_version == PY_SSL_VERSION_SSL2) ctx = SSL_CTX_new(SSLv2_method()); #endif - else if (proto_version == PY_SSL_VERSION_SSL23) - ctx = SSL_CTX_new(SSLv23_method()); + else if (proto_version == PY_SSL_VERSION_TLS) + ctx = SSL_CTX_new(TLS_method()); else proto_version = -1; PySSL_END_ALLOW_THREADS @@ -2047,8 +2196,9 @@ context_new(PyTypeObject *type, PyObject *args, PyObject *kwds) #ifndef OPENSSL_NO_ECDH /* Allow automatic ECDH curve selection (on OpenSSL 1.0.2+), or use prime256v1 by default. This is Apache mod_ssl's initialization - policy, so we should be safe. */ -#if defined(SSL_CTX_set_ecdh_auto) + policy, so we should be safe. OpenSSL 1.1 has it enabled by default. + */ +#if defined(SSL_CTX_set_ecdh_auto) && !defined(OPENSSL_VERSION_1_1) SSL_CTX_set_ecdh_auto(self->ctx, 1); #else { @@ -2259,10 +2409,12 @@ static PyObject * get_verify_flags(PySSLContext *self, void *c) { X509_STORE *store; + X509_VERIFY_PARAM *param; unsigned long flags; store = SSL_CTX_get_cert_store(self->ctx); - flags = X509_VERIFY_PARAM_get_flags(store->param); + param = X509_STORE_get0_param(store); + flags = X509_VERIFY_PARAM_get_flags(param); return PyLong_FromUnsignedLong(flags); } @@ -2270,22 +2422,24 @@ static int set_verify_flags(PySSLContext *self, PyObject *arg, void *c) { X509_STORE *store; + X509_VERIFY_PARAM *param; unsigned long new_flags, flags, set, clear; if (!PyArg_Parse(arg, "k", &new_flags)) return -1; store = SSL_CTX_get_cert_store(self->ctx); - flags = X509_VERIFY_PARAM_get_flags(store->param); + param = X509_STORE_get0_param(store); + flags = X509_VERIFY_PARAM_get_flags(param); clear = flags & ~new_flags; set = ~flags & new_flags; if (clear) { - if (!X509_VERIFY_PARAM_clear_flags(store->param, clear)) { + if (!X509_VERIFY_PARAM_clear_flags(param, clear)) { _setSSLError(NULL, 0, __FILE__, __LINE__); return -1; } } if (set) { - if (!X509_VERIFY_PARAM_set_flags(store->param, set)) { + if (!X509_VERIFY_PARAM_set_flags(param, set)) { _setSSLError(NULL, 0, __FILE__, __LINE__); return -1; } @@ -2455,8 +2609,8 @@ load_cert_chain(PySSLContext *self, PyObject *args, PyObject *kwds) char *kwlist[] = {"certfile", "keyfile", "password", NULL}; PyObject *certfile, *keyfile = NULL, *password = NULL; PyObject *certfile_bytes = NULL, *keyfile_bytes = NULL; - pem_password_cb *orig_passwd_cb = self->ctx->default_passwd_callback; - void *orig_passwd_userdata = self->ctx->default_passwd_callback_userdata; + pem_password_cb *orig_passwd_cb = SSL_CTX_get_default_passwd_cb(self->ctx); + void *orig_passwd_userdata = SSL_CTX_get_default_passwd_cb_userdata(self->ctx); _PySSLPasswordInfo pw_info = { NULL, NULL, NULL, 0, 0 }; int r; @@ -2587,8 +2741,9 @@ _add_ca_certs(PySSLContext *self, void *data, Py_ssize_t len, cert = d2i_X509_bio(biobuf, NULL); } else { cert = PEM_read_bio_X509(biobuf, NULL, - self->ctx->default_passwd_callback, - self->ctx->default_passwd_callback_userdata); + SSL_CTX_get_default_passwd_cb(self->ctx), + SSL_CTX_get_default_passwd_cb_userdata(self->ctx) + ); } if (cert == NULL) { break; @@ -3036,25 +3191,24 @@ static PyObject * cert_store_stats(PySSLContext *self) { X509_STORE *store; + STACK_OF(X509_OBJECT) *objs; X509_OBJECT *obj; - int x509 = 0, crl = 0, pkey = 0, ca = 0, i; + int x509 = 0, crl = 0, ca = 0, i; store = SSL_CTX_get_cert_store(self->ctx); - for (i = 0; i < sk_X509_OBJECT_num(store->objs); i++) { - obj = sk_X509_OBJECT_value(store->objs, i); - switch (obj->type) { + objs = X509_STORE_get0_objects(store); + for (i = 0; i < sk_X509_OBJECT_num(objs); i++) { + obj = sk_X509_OBJECT_value(objs, i); + switch (X509_OBJECT_get_type(obj)) { case X509_LU_X509: x509++; - if (X509_check_ca(obj->data.x509)) { + if (X509_check_ca(X509_OBJECT_get0_X509(obj))) { ca++; } break; case X509_LU_CRL: crl++; break; - case X509_LU_PKEY: - pkey++; - break; default: /* Ignore X509_LU_FAIL, X509_LU_RETRY, X509_LU_PKEY. * As far as I can tell they are internal states and never @@ -3079,6 +3233,7 @@ get_ca_certs(PySSLContext *self, PyObject *args, PyObject *kwds) { char *kwlist[] = {"binary_form", NULL}; X509_STORE *store; + STACK_OF(X509_OBJECT) *objs; PyObject *ci = NULL, *rlist = NULL; int i; int binary_mode = 0; @@ -3093,17 +3248,18 @@ get_ca_certs(PySSLContext *self, PyObject *args, PyObject *kwds) } store = SSL_CTX_get_cert_store(self->ctx); - for (i = 0; i < sk_X509_OBJECT_num(store->objs); i++) { + objs = X509_STORE_get0_objects(store); + for (i = 0; i < sk_X509_OBJECT_num(objs); i++) { X509_OBJECT *obj; X509 *cert; - obj = sk_X509_OBJECT_value(store->objs, i); - if (obj->type != X509_LU_X509) { + obj = sk_X509_OBJECT_value(objs, i); + if (X509_OBJECT_get_type(obj) != X509_LU_X509) { /* not a x509 cert */ continue; } /* CA for any purpose */ - cert = obj->data.x509; + cert = X509_OBJECT_get0_X509(obj); if (!X509_check_ca(cert)) { continue; } @@ -3776,10 +3932,12 @@ static PyMethodDef PySSL_methods[] = { }; -#ifdef WITH_THREAD +#ifdef HAVE_OPENSSL_CRYPTO_LOCK /* an implementation of OpenSSL threading operations in terms - of the Python C thread library */ + * of the Python C thread library + * Only used up to 1.0.2. OpenSSL 1.1.0+ has its own locking code. + */ static PyThread_type_lock *_ssl_locks = NULL; @@ -3860,7 +4018,7 @@ static int _setup_ssl_threads(void) { return 1; } -#endif /* def HAVE_THREAD */ +#endif /* HAVE_OPENSSL_CRYPTO_LOCK for WITH_THREAD && OpenSSL < 1.1.0 */ PyDoc_STRVAR(module_doc, "Implementation module for SSL socket operations. See the socket module\n\ @@ -3927,11 +4085,16 @@ PyInit__ssl(void) SSL_load_error_strings(); SSL_library_init(); #ifdef WITH_THREAD +#ifdef HAVE_OPENSSL_CRYPTO_LOCK /* note that this will start threading if not already started */ if (!_setup_ssl_threads()) { return NULL; } +#elif OPENSSL_VERSION_1_1 && defined(OPENSSL_THREADS) + /* OpenSSL 1.1.0 builtin thread support is enabled */ + _ssl_locks_count++; #endif +#endif /* WITH_THREAD */ OpenSSL_add_all_algorithms(); /* Add symbols to module dict */ @@ -4075,7 +4238,9 @@ PyInit__ssl(void) PY_SSL_VERSION_SSL3); #endif PyModule_AddIntConstant(m, "PROTOCOL_SSLv23", - PY_SSL_VERSION_SSL23); + PY_SSL_VERSION_TLS); + PyModule_AddIntConstant(m, "PROTOCOL_TLS", + PY_SSL_VERSION_TLS); PyModule_AddIntConstant(m, "PROTOCOL_TLSv1", PY_SSL_VERSION_TLS1); #if HAVE_TLSv1_2