bpo-31453: Add setter for min/max protocol version (#5259)
OpenSSL 1.1 has introduced a new API to set the minimum and maximum supported protocol version. The API is easier to use than the old OP_NO_TLS1 option flags, too. Since OpenSSL has no call to set minimum version to highest supported, the implementation emulate maximum_version = MINIMUM_SUPPORTED and minimum_version = MAXIMUM_SUPPORTED by figuring out the minumum and maximum supported version at compile time. Signed-off-by: Christian Heimes <christian@python.org>
This commit is contained in:
parent
9d50ab563d
commit
698dde16f6
|
@ -762,6 +762,11 @@ Constants
|
||||||
|
|
||||||
.. versionadded:: 3.2
|
.. versionadded:: 3.2
|
||||||
|
|
||||||
|
.. deprecated:: 3.7
|
||||||
|
The option is deprecated since OpenSSL 1.1.0, use the new
|
||||||
|
:attr:`SSLContext.minimum_version` and
|
||||||
|
:attr:`SSLContext.maximum_version` instead.
|
||||||
|
|
||||||
.. data:: OP_NO_TLSv1_1
|
.. data:: OP_NO_TLSv1_1
|
||||||
|
|
||||||
Prevents a TLSv1.1 connection. This option is only applicable in conjunction
|
Prevents a TLSv1.1 connection. This option is only applicable in conjunction
|
||||||
|
@ -770,6 +775,9 @@ Constants
|
||||||
|
|
||||||
.. versionadded:: 3.4
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
|
.. deprecated:: 3.7
|
||||||
|
The option is deprecated since OpenSSL 1.1.0.
|
||||||
|
|
||||||
.. data:: OP_NO_TLSv1_2
|
.. data:: OP_NO_TLSv1_2
|
||||||
|
|
||||||
Prevents a TLSv1.2 connection. This option is only applicable in conjunction
|
Prevents a TLSv1.2 connection. This option is only applicable in conjunction
|
||||||
|
@ -778,6 +786,9 @@ Constants
|
||||||
|
|
||||||
.. versionadded:: 3.4
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
|
.. deprecated:: 3.7
|
||||||
|
The option is deprecated since OpenSSL 1.1.0.
|
||||||
|
|
||||||
.. data:: OP_NO_TLSv1_3
|
.. data:: OP_NO_TLSv1_3
|
||||||
|
|
||||||
Prevents a TLSv1.3 connection. This option is only applicable in conjunction
|
Prevents a TLSv1.3 connection. This option is only applicable in conjunction
|
||||||
|
@ -788,6 +799,10 @@ Constants
|
||||||
|
|
||||||
.. versionadded:: 3.7
|
.. versionadded:: 3.7
|
||||||
|
|
||||||
|
.. deprecated:: 3.7
|
||||||
|
The option is deprecated since OpenSSL 1.1.0. It was added to 2.7.15,
|
||||||
|
3.6.3 and 3.7.0 for backwards compatibility with OpenSSL 1.0.2.
|
||||||
|
|
||||||
.. data:: OP_CIPHER_SERVER_PREFERENCE
|
.. data:: OP_CIPHER_SERVER_PREFERENCE
|
||||||
|
|
||||||
Use the server's cipher ordering preference, rather than the client's.
|
Use the server's cipher ordering preference, rather than the client's.
|
||||||
|
@ -856,7 +871,7 @@ Constants
|
||||||
|
|
||||||
.. data:: HAS_ECDH
|
.. data:: HAS_ECDH
|
||||||
|
|
||||||
Whether the OpenSSL library has built-in support for Elliptic Curve-based
|
Whether the OpenSSL library has built-in support for the Elliptic Curve-based
|
||||||
Diffie-Hellman key exchange. This should be true unless the feature was
|
Diffie-Hellman key exchange. This should be true unless the feature was
|
||||||
explicitly disabled by the distributor.
|
explicitly disabled by the distributor.
|
||||||
|
|
||||||
|
@ -871,7 +886,7 @@ Constants
|
||||||
|
|
||||||
.. data:: HAS_NPN
|
.. data:: HAS_NPN
|
||||||
|
|
||||||
Whether the OpenSSL library has built-in support for *Next Protocol
|
Whether the OpenSSL library has built-in support for the *Next Protocol
|
||||||
Negotiation* as described in the `Application Layer Protocol
|
Negotiation* as described in the `Application Layer Protocol
|
||||||
Negotiation <https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation>`_.
|
Negotiation <https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation>`_.
|
||||||
When true, you can use the :meth:`SSLContext.set_npn_protocols` method to advertise
|
When true, you can use the :meth:`SSLContext.set_npn_protocols` method to advertise
|
||||||
|
@ -879,6 +894,36 @@ Constants
|
||||||
|
|
||||||
.. versionadded:: 3.3
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
.. data:: HAS_SSLv2
|
||||||
|
|
||||||
|
Whether the OpenSSL library has built-in support for the SSL 2.0 protocol.
|
||||||
|
|
||||||
|
.. versionadded:: 3.7
|
||||||
|
|
||||||
|
.. data:: HAS_SSLv3
|
||||||
|
|
||||||
|
Whether the OpenSSL library has built-in support for the SSL 3.0 protocol.
|
||||||
|
|
||||||
|
.. versionadded:: 3.7
|
||||||
|
|
||||||
|
.. data:: HAS_TLSv1
|
||||||
|
|
||||||
|
Whether the OpenSSL library has built-in support for the TLS 1.0 protocol.
|
||||||
|
|
||||||
|
.. versionadded:: 3.7
|
||||||
|
|
||||||
|
.. data:: HAS_TLSv1_1
|
||||||
|
|
||||||
|
Whether the OpenSSL library has built-in support for the TLS 1.1 protocol.
|
||||||
|
|
||||||
|
.. versionadded:: 3.7
|
||||||
|
|
||||||
|
.. data:: HAS_TLSv1_2
|
||||||
|
|
||||||
|
Whether the OpenSSL library has built-in support for the TLS 1.2 protocol.
|
||||||
|
|
||||||
|
.. versionadded:: 3.7
|
||||||
|
|
||||||
.. data:: HAS_TLSv1_3
|
.. data:: HAS_TLSv1_3
|
||||||
|
|
||||||
Whether the OpenSSL library has built-in support for the TLS 1.3 protocol.
|
Whether the OpenSSL library has built-in support for the TLS 1.3 protocol.
|
||||||
|
@ -965,6 +1010,27 @@ Constants
|
||||||
|
|
||||||
.. versionadded:: 3.6
|
.. versionadded:: 3.6
|
||||||
|
|
||||||
|
.. class:: TLSVersion
|
||||||
|
|
||||||
|
:class:`enum.IntEnum` collection of SSL and TLS versions for
|
||||||
|
:attr:`SSLContext.maximum_version` and :attr:`SSLContext.minimum_version`.
|
||||||
|
|
||||||
|
.. versionadded:: 3.7
|
||||||
|
|
||||||
|
.. attribute:: TLSVersion.MINIMUM_SUPPORTED
|
||||||
|
.. attribute:: TLSVersion.MAXIMUM_SUPPORTED
|
||||||
|
|
||||||
|
The minimum or maximum supported SSL or TLS version. These are magic
|
||||||
|
constants. Their values don't reflect the lowest and highest available
|
||||||
|
TLS/SSL versions.
|
||||||
|
|
||||||
|
.. attribute:: TLSVersion.SSLv3
|
||||||
|
.. attribute:: TLSVersion.TLSv1
|
||||||
|
.. attribute:: TLSVersion.TLSv1_1
|
||||||
|
.. attribute:: TLSVersion.TLSv1_2
|
||||||
|
.. attribute:: TLSVersion.TLSv1_3
|
||||||
|
|
||||||
|
SSL 3.0 to TLS 1.3.
|
||||||
|
|
||||||
SSL Sockets
|
SSL Sockets
|
||||||
-----------
|
-----------
|
||||||
|
@ -1788,6 +1854,37 @@ to speed up repeated connections from the same clients.
|
||||||
|
|
||||||
This features requires OpenSSL 0.9.8f or newer.
|
This features requires OpenSSL 0.9.8f or newer.
|
||||||
|
|
||||||
|
.. attribute:: SSLContext.maximum_version
|
||||||
|
|
||||||
|
A :class:`TLSVersion` enum member representing the highest supported
|
||||||
|
TLS version. The value defaults to :attr:`TLSVersion.MAXIMUM_SUPPORTED`.
|
||||||
|
The attribute is read-only for protocols other than :attr:`PROTOCOL_TLS`,
|
||||||
|
:attr:`PROTOCOL_TLS_CLIENT`, and :attr:`PROTOCOL_TLS_SERVER`.
|
||||||
|
|
||||||
|
The attributes :attr:`~SSLContext.maximum_version`,
|
||||||
|
:attr:`~SSLContext.minimum_version` and
|
||||||
|
:attr:`SSLContext.options` all affect the supported SSL
|
||||||
|
and TLS versions of the context. The implementation does not prevent
|
||||||
|
invalid combination. For example a context with
|
||||||
|
:attr:`OP_NO_TLSv1_2` in :attr:`~SSLContext.options` and
|
||||||
|
:attr:`~SSLContext.maximum_version` set to :attr:`TLSVersion.TLSv1_2`
|
||||||
|
will not be able to establish a TLS 1.2 connection.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This attribute is not available unless the ssl module is compiled
|
||||||
|
with OpenSSL 1.1.0g or newer.
|
||||||
|
|
||||||
|
.. attribute:: SSLContext.minimum_version
|
||||||
|
|
||||||
|
Like :attr:`SSLContext.maximum_version` except it is the lowest
|
||||||
|
supported version or :attr:`TLSVersion.MINIMUM_SUPPORTED`.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This attribute is not available unless the ssl module is compiled
|
||||||
|
with OpenSSL 1.1.0g or newer.
|
||||||
|
|
||||||
.. attribute:: SSLContext.options
|
.. attribute:: SSLContext.options
|
||||||
|
|
||||||
An integer representing the set of SSL options enabled on this context.
|
An integer representing the set of SSL options enabled on this context.
|
||||||
|
|
|
@ -683,6 +683,11 @@ feature. Instances must be created with :class:`~ssl.SSLContext` methods
|
||||||
:meth:`~ssl.SSLContext.wrap_socket` and :meth:`~ssl.SSLContext.wrap_bio`.
|
:meth:`~ssl.SSLContext.wrap_socket` and :meth:`~ssl.SSLContext.wrap_bio`.
|
||||||
(Contributed by Christian Heimes in :issue:`32951`)
|
(Contributed by Christian Heimes in :issue:`32951`)
|
||||||
|
|
||||||
|
OpenSSL 1.1 APIs for setting the minimum and maximum TLS protocol version are
|
||||||
|
available as as :attr:`~ssl.SSLContext.minimum_version` and
|
||||||
|
:attr:`~ssl.SSLContext.maximum_version`. Supported protocols are indicated
|
||||||
|
by new flags like :data:`~ssl.HAS_TLSv1_1`.
|
||||||
|
(Contributed by Christian Heimes in :issue:`32609`.)
|
||||||
|
|
||||||
string
|
string
|
||||||
------
|
------
|
||||||
|
|
37
Lib/ssl.py
37
Lib/ssl.py
|
@ -112,9 +112,11 @@ except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN, HAS_TLSv1_3
|
from _ssl import (
|
||||||
from _ssl import _DEFAULT_CIPHERS
|
HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN, HAS_SSLv2, HAS_SSLv3, HAS_TLSv1,
|
||||||
from _ssl import _OPENSSL_API_VERSION
|
HAS_TLSv1_1, HAS_TLSv1_2, HAS_TLSv1_3
|
||||||
|
)
|
||||||
|
from _ssl import _DEFAULT_CIPHERS, _OPENSSL_API_VERSION
|
||||||
|
|
||||||
|
|
||||||
_IntEnum._convert(
|
_IntEnum._convert(
|
||||||
|
@ -153,6 +155,16 @@ _PROTOCOL_NAMES = {value: name for name, value in _SSLMethod.__members__.items()
|
||||||
_SSLv2_IF_EXISTS = getattr(_SSLMethod, 'PROTOCOL_SSLv2', None)
|
_SSLv2_IF_EXISTS = getattr(_SSLMethod, 'PROTOCOL_SSLv2', None)
|
||||||
|
|
||||||
|
|
||||||
|
class TLSVersion(_IntEnum):
|
||||||
|
MINIMUM_SUPPORTED = _ssl.PROTO_MINIMUM_SUPPORTED
|
||||||
|
SSLv3 = _ssl.PROTO_SSLv3
|
||||||
|
TLSv1 = _ssl.PROTO_TLSv1
|
||||||
|
TLSv1_1 = _ssl.PROTO_TLSv1_1
|
||||||
|
TLSv1_2 = _ssl.PROTO_TLSv1_2
|
||||||
|
TLSv1_3 = _ssl.PROTO_TLSv1_3
|
||||||
|
MAXIMUM_SUPPORTED = _ssl.PROTO_MAXIMUM_SUPPORTED
|
||||||
|
|
||||||
|
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
from _ssl import enum_certificates, enum_crls
|
from _ssl import enum_certificates, enum_crls
|
||||||
|
|
||||||
|
@ -467,6 +479,25 @@ class SSLContext(_SSLContext):
|
||||||
self._load_windows_store_certs(storename, purpose)
|
self._load_windows_store_certs(storename, purpose)
|
||||||
self.set_default_verify_paths()
|
self.set_default_verify_paths()
|
||||||
|
|
||||||
|
if hasattr(_SSLContext, 'minimum_version'):
|
||||||
|
@property
|
||||||
|
def minimum_version(self):
|
||||||
|
return TLSVersion(super().minimum_version)
|
||||||
|
|
||||||
|
@minimum_version.setter
|
||||||
|
def minimum_version(self, value):
|
||||||
|
if value == TLSVersion.SSLv3:
|
||||||
|
self.options &= ~Options.OP_NO_SSLv3
|
||||||
|
super(SSLContext, SSLContext).minimum_version.__set__(self, value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def maximum_version(self):
|
||||||
|
return TLSVersion(super().maximum_version)
|
||||||
|
|
||||||
|
@maximum_version.setter
|
||||||
|
def maximum_version(self, value):
|
||||||
|
super(SSLContext, SSLContext).maximum_version.__set__(self, value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def options(self):
|
def options(self):
|
||||||
return Options(super().options)
|
return Options(super().options)
|
||||||
|
|
|
@ -1077,6 +1077,69 @@ class ContextTests(unittest.TestCase):
|
||||||
with self.assertRaises(AttributeError):
|
with self.assertRaises(AttributeError):
|
||||||
ctx.hostname_checks_common_name = True
|
ctx.hostname_checks_common_name = True
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(ssl.SSLContext, 'minimum_version'),
|
||||||
|
"required OpenSSL 1.1.0g")
|
||||||
|
def test_min_max_version(self):
|
||||||
|
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
||||||
|
self.assertEqual(
|
||||||
|
ctx.minimum_version, ssl.TLSVersion.MINIMUM_SUPPORTED
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
ctx.maximum_version, ssl.TLSVersion.MAXIMUM_SUPPORTED
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx.minimum_version = ssl.TLSVersion.TLSv1_1
|
||||||
|
ctx.maximum_version = ssl.TLSVersion.TLSv1_2
|
||||||
|
self.assertEqual(
|
||||||
|
ctx.minimum_version, ssl.TLSVersion.TLSv1_1
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
ctx.maximum_version, ssl.TLSVersion.TLSv1_2
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx.minimum_version = ssl.TLSVersion.MINIMUM_SUPPORTED
|
||||||
|
ctx.maximum_version = ssl.TLSVersion.TLSv1
|
||||||
|
self.assertEqual(
|
||||||
|
ctx.minimum_version, ssl.TLSVersion.MINIMUM_SUPPORTED
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
ctx.maximum_version, ssl.TLSVersion.TLSv1
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx.maximum_version = ssl.TLSVersion.MAXIMUM_SUPPORTED
|
||||||
|
self.assertEqual(
|
||||||
|
ctx.maximum_version, ssl.TLSVersion.MAXIMUM_SUPPORTED
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx.maximum_version = ssl.TLSVersion.MINIMUM_SUPPORTED
|
||||||
|
self.assertIn(
|
||||||
|
ctx.maximum_version,
|
||||||
|
{ssl.TLSVersion.TLSv1, ssl.TLSVersion.SSLv3}
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx.minimum_version = ssl.TLSVersion.MAXIMUM_SUPPORTED
|
||||||
|
self.assertIn(
|
||||||
|
ctx.minimum_version,
|
||||||
|
{ssl.TLSVersion.TLSv1_2, ssl.TLSVersion.TLSv1_3}
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
ctx.minimum_version = 42
|
||||||
|
|
||||||
|
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_1)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
ctx.minimum_version, ssl.TLSVersion.MINIMUM_SUPPORTED
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
ctx.maximum_version, ssl.TLSVersion.MAXIMUM_SUPPORTED
|
||||||
|
)
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
ctx.minimum_version = ssl.TLSVersion.MINIMUM_SUPPORTED
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
ctx.maximum_version = ssl.TLSVersion.TLSv1
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(have_verify_flags(),
|
@unittest.skipUnless(have_verify_flags(),
|
||||||
"verify_flags need OpenSSL > 0.9.8")
|
"verify_flags need OpenSSL > 0.9.8")
|
||||||
def test_verify_flags(self):
|
def test_verify_flags(self):
|
||||||
|
@ -3457,6 +3520,60 @@ class ThreadedTests(unittest.TestCase):
|
||||||
})
|
})
|
||||||
self.assertEqual(s.version(), 'TLSv1.3')
|
self.assertEqual(s.version(), 'TLSv1.3')
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(ssl.SSLContext, 'minimum_version'),
|
||||||
|
"required OpenSSL 1.1.0g")
|
||||||
|
def test_min_max_version(self):
|
||||||
|
client_context, server_context, hostname = testing_context()
|
||||||
|
# client TLSv1.0 to 1.2
|
||||||
|
client_context.minimum_version = ssl.TLSVersion.TLSv1
|
||||||
|
client_context.maximum_version = ssl.TLSVersion.TLSv1_2
|
||||||
|
# server only TLSv1.2
|
||||||
|
server_context.minimum_version = ssl.TLSVersion.TLSv1_2
|
||||||
|
server_context.maximum_version = ssl.TLSVersion.TLSv1_2
|
||||||
|
|
||||||
|
with ThreadedEchoServer(context=server_context) as server:
|
||||||
|
with client_context.wrap_socket(socket.socket(),
|
||||||
|
server_hostname=hostname) as s:
|
||||||
|
s.connect((HOST, server.port))
|
||||||
|
self.assertEqual(s.version(), 'TLSv1.2')
|
||||||
|
|
||||||
|
# client 1.0 to 1.2, server 1.0 to 1.1
|
||||||
|
server_context.minimum_version = ssl.TLSVersion.TLSv1
|
||||||
|
server_context.maximum_version = ssl.TLSVersion.TLSv1_1
|
||||||
|
|
||||||
|
with ThreadedEchoServer(context=server_context) as server:
|
||||||
|
with client_context.wrap_socket(socket.socket(),
|
||||||
|
server_hostname=hostname) as s:
|
||||||
|
s.connect((HOST, server.port))
|
||||||
|
self.assertEqual(s.version(), 'TLSv1.1')
|
||||||
|
|
||||||
|
# client 1.0, server 1.2 (mismatch)
|
||||||
|
server_context.minimum_version = ssl.TLSVersion.TLSv1_2
|
||||||
|
server_context.maximum_version = ssl.TLSVersion.TLSv1_2
|
||||||
|
client_context.minimum_version = ssl.TLSVersion.TLSv1
|
||||||
|
client_context.maximum_version = ssl.TLSVersion.TLSv1
|
||||||
|
with ThreadedEchoServer(context=server_context) as server:
|
||||||
|
with client_context.wrap_socket(socket.socket(),
|
||||||
|
server_hostname=hostname) as s:
|
||||||
|
with self.assertRaises(ssl.SSLError) as e:
|
||||||
|
s.connect((HOST, server.port))
|
||||||
|
self.assertIn("alert", str(e.exception))
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(ssl.SSLContext, 'minimum_version'),
|
||||||
|
"required OpenSSL 1.1.0g")
|
||||||
|
@unittest.skipUnless(ssl.HAS_SSLv3, "requires SSLv3 support")
|
||||||
|
def test_min_max_version_sslv3(self):
|
||||||
|
client_context, server_context, hostname = testing_context()
|
||||||
|
server_context.minimum_version = ssl.TLSVersion.SSLv3
|
||||||
|
client_context.minimum_version = ssl.TLSVersion.SSLv3
|
||||||
|
client_context.maximum_version = ssl.TLSVersion.SSLv3
|
||||||
|
with ThreadedEchoServer(context=server_context) as server:
|
||||||
|
with client_context.wrap_socket(socket.socket(),
|
||||||
|
server_hostname=hostname) as s:
|
||||||
|
s.connect((HOST, server.port))
|
||||||
|
self.assertEqual(s.version(), 'SSLv3')
|
||||||
|
|
||||||
@unittest.skipUnless(ssl.HAS_ECDH, "test requires ECDH-enabled OpenSSL")
|
@unittest.skipUnless(ssl.HAS_ECDH, "test requires ECDH-enabled OpenSSL")
|
||||||
def test_default_ecdh_curve(self):
|
def test_default_ecdh_curve(self):
|
||||||
# Issue #21015: elliptic curve-based Diffie Hellman key exchange
|
# Issue #21015: elliptic curve-based Diffie Hellman key exchange
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Add TLSVersion constants and SSLContext.maximum_version / minimum_version
|
||||||
|
attributes. The new API wraps OpenSSL 1.1
|
||||||
|
https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_min_proto_version.html
|
||||||
|
feature.
|
240
Modules/_ssl.c
240
Modules/_ssl.c
|
@ -320,6 +320,53 @@ enum py_ssl_version {
|
||||||
PY_SSL_VERSION_TLS_SERVER,
|
PY_SSL_VERSION_TLS_SERVER,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum py_proto_version {
|
||||||
|
PY_PROTO_MINIMUM_SUPPORTED = -2,
|
||||||
|
PY_PROTO_SSLv3 = SSL3_VERSION,
|
||||||
|
PY_PROTO_TLSv1 = TLS1_VERSION,
|
||||||
|
PY_PROTO_TLSv1_1 = TLS1_1_VERSION,
|
||||||
|
PY_PROTO_TLSv1_2 = TLS1_2_VERSION,
|
||||||
|
#ifdef TLS1_3_VERSION
|
||||||
|
PY_PROTO_TLSv1_3 = TLS1_3_VERSION,
|
||||||
|
#else
|
||||||
|
PY_PROTO_TLSv1_3 = 0x304,
|
||||||
|
#endif
|
||||||
|
PY_PROTO_MAXIMUM_SUPPORTED = -1,
|
||||||
|
|
||||||
|
/* OpenSSL has no dedicated API to set the minimum version to the maximum
|
||||||
|
* available version, and the other way around. We have to figure out the
|
||||||
|
* minimum and maximum available version on our own and hope for the best.
|
||||||
|
*/
|
||||||
|
#if defined(SSL3_VERSION) && !defined(OPENSSL_NO_SSL3)
|
||||||
|
PY_PROTO_MINIMUM_AVAILABLE = PY_PROTO_SSLv3,
|
||||||
|
#elif defined(TLS1_VERSION) && !defined(OPENSSL_NO_TLS1)
|
||||||
|
PY_PROTO_MINIMUM_AVAILABLE = PY_PROTO_TLSv1,
|
||||||
|
#elif defined(TLS1_1_VERSION) && !defined(OPENSSL_NO_TLS1_1)
|
||||||
|
PY_PROTO_MINIMUM_AVAILABLE = PY_PROTO_TLSv1_1,
|
||||||
|
#elif defined(TLS1_2_VERSION) && !defined(OPENSSL_NO_TLS1_2)
|
||||||
|
PY_PROTO_MINIMUM_AVAILABLE = PY_PROTO_TLSv1_2,
|
||||||
|
#elif defined(TLS1_3_VERSION) && !defined(OPENSSL_NO_TLS1_3)
|
||||||
|
PY_PROTO_MINIMUM_AVAILABLE = PY_PROTO_TLSv1_3,
|
||||||
|
#else
|
||||||
|
#error "PY_PROTO_MINIMUM_AVAILABLE not found"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(TLS1_3_VERSION) && !defined(OPENSSL_NO_TLS1_3)
|
||||||
|
PY_PROTO_MAXIMUM_AVAILABLE = PY_PROTO_TLSv1_3,
|
||||||
|
#elif defined(TLS1_2_VERSION) && !defined(OPENSSL_NO_TLS1_2)
|
||||||
|
PY_PROTO_MAXIMUM_AVAILABLE = PY_PROTO_TLSv1_2,
|
||||||
|
#elif defined(TLS1_1_VERSION) && !defined(OPENSSL_NO_TLS1_1)
|
||||||
|
PY_PROTO_MAXIMUM_AVAILABLE = PY_PROTO_TLSv1_1,
|
||||||
|
#elif defined(TLS1_VERSION) && !defined(OPENSSL_NO_TLS1)
|
||||||
|
PY_PROTO_MAXIMUM_AVAILABLE = PY_PROTO_TLSv1,
|
||||||
|
#elif defined(SSL3_VERSION) && !defined(OPENSSL_NO_SSL3)
|
||||||
|
PY_PROTO_MAXIMUM_AVAILABLE = PY_PROTO_SSLv3,
|
||||||
|
#else
|
||||||
|
#error "PY_PROTO_MAXIMUM_AVAILABLE not found"
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* serves as a flag to see whether we've initialized the SSL thread support. */
|
/* serves as a flag to see whether we've initialized the SSL thread support. */
|
||||||
/* 0 means no, greater than 0 means yes */
|
/* 0 means no, greater than 0 means yes */
|
||||||
|
|
||||||
|
@ -3323,6 +3370,106 @@ set_verify_flags(PySSLContext *self, PyObject *arg, void *c)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Getter and setter for protocol version */
|
||||||
|
#if defined(SSL_CTRL_GET_MAX_PROTO_VERSION)
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
set_min_max_proto_version(PySSLContext *self, PyObject *arg, int what)
|
||||||
|
{
|
||||||
|
long v;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (!PyArg_Parse(arg, "l", &v))
|
||||||
|
return -1;
|
||||||
|
if (v > INT_MAX) {
|
||||||
|
PyErr_SetString(PyExc_OverflowError, "Option is too long");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(self->protocol) {
|
||||||
|
case PY_SSL_VERSION_TLS_CLIENT: /* fall through */
|
||||||
|
case PY_SSL_VERSION_TLS_SERVER: /* fall through */
|
||||||
|
case PY_SSL_VERSION_TLS:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
PyErr_SetString(
|
||||||
|
PyExc_ValueError,
|
||||||
|
"The context's protocol doesn't support modification of "
|
||||||
|
"highest and lowest version."
|
||||||
|
);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (what == 0) {
|
||||||
|
switch(v) {
|
||||||
|
case PY_PROTO_MINIMUM_SUPPORTED:
|
||||||
|
v = 0;
|
||||||
|
break;
|
||||||
|
case PY_PROTO_MAXIMUM_SUPPORTED:
|
||||||
|
/* Emulate max for set_min_proto_version */
|
||||||
|
v = PY_PROTO_MAXIMUM_AVAILABLE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result = SSL_CTX_set_min_proto_version(self->ctx, v);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch(v) {
|
||||||
|
case PY_PROTO_MAXIMUM_SUPPORTED:
|
||||||
|
v = 0;
|
||||||
|
break;
|
||||||
|
case PY_PROTO_MINIMUM_SUPPORTED:
|
||||||
|
/* Emulate max for set_min_proto_version */
|
||||||
|
v = PY_PROTO_MINIMUM_AVAILABLE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result = SSL_CTX_set_max_proto_version(self->ctx, v);
|
||||||
|
}
|
||||||
|
if (result == 0) {
|
||||||
|
PyErr_Format(PyExc_ValueError,
|
||||||
|
"Unsupported protocol version 0x%x", v);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
get_minimum_version(PySSLContext *self, void *c)
|
||||||
|
{
|
||||||
|
int v = SSL_CTX_ctrl(self->ctx, SSL_CTRL_GET_MIN_PROTO_VERSION, 0, NULL);
|
||||||
|
if (v == 0) {
|
||||||
|
v = PY_PROTO_MINIMUM_SUPPORTED;
|
||||||
|
}
|
||||||
|
return PyLong_FromLong(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
set_minimum_version(PySSLContext *self, PyObject *arg, void *c)
|
||||||
|
{
|
||||||
|
return set_min_max_proto_version(self, arg, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
get_maximum_version(PySSLContext *self, void *c)
|
||||||
|
{
|
||||||
|
int v = SSL_CTX_ctrl(self->ctx, SSL_CTRL_GET_MAX_PROTO_VERSION, 0, NULL);
|
||||||
|
if (v == 0) {
|
||||||
|
v = PY_PROTO_MAXIMUM_SUPPORTED;
|
||||||
|
}
|
||||||
|
return PyLong_FromLong(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
set_maximum_version(PySSLContext *self, PyObject *arg, void *c)
|
||||||
|
{
|
||||||
|
return set_min_max_proto_version(self, arg, 1);
|
||||||
|
}
|
||||||
|
#endif /* SSL_CTRL_GET_MAX_PROTO_VERSION */
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
get_options(PySSLContext *self, void *c)
|
get_options(PySSLContext *self, void *c)
|
||||||
{
|
{
|
||||||
|
@ -4289,8 +4436,14 @@ static PyGetSetDef context_getsetlist[] = {
|
||||||
(setter) set_check_hostname, NULL},
|
(setter) set_check_hostname, NULL},
|
||||||
{"_host_flags", (getter) get_host_flags,
|
{"_host_flags", (getter) get_host_flags,
|
||||||
(setter) set_host_flags, NULL},
|
(setter) set_host_flags, NULL},
|
||||||
|
#if SSL_CTRL_GET_MAX_PROTO_VERSION
|
||||||
|
{"minimum_version", (getter) get_minimum_version,
|
||||||
|
(setter) set_minimum_version, NULL},
|
||||||
|
{"maximum_version", (getter) get_maximum_version,
|
||||||
|
(setter) set_maximum_version, NULL},
|
||||||
|
#endif
|
||||||
{"sni_callback", (getter) get_sni_callback,
|
{"sni_callback", (getter) get_sni_callback,
|
||||||
(setter) set_sni_callback, PySSLContext_sni_callback_doc},
|
(setter) set_sni_callback, PySSLContext_sni_callback_doc},
|
||||||
{"options", (getter) get_options,
|
{"options", (getter) get_options,
|
||||||
(setter) set_options, NULL},
|
(setter) set_options, NULL},
|
||||||
{"protocol", (getter) get_protocol,
|
{"protocol", (getter) get_protocol,
|
||||||
|
@ -5711,45 +5864,82 @@ PyInit__ssl(void)
|
||||||
X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS);
|
X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if HAVE_SNI
|
/* protocol versions */
|
||||||
r = Py_True;
|
PyModule_AddIntConstant(m, "PROTO_MINIMUM_SUPPORTED",
|
||||||
#else
|
PY_PROTO_MINIMUM_SUPPORTED);
|
||||||
r = Py_False;
|
PyModule_AddIntConstant(m, "PROTO_MAXIMUM_SUPPORTED",
|
||||||
#endif
|
PY_PROTO_MAXIMUM_SUPPORTED);
|
||||||
Py_INCREF(r);
|
PyModule_AddIntConstant(m, "PROTO_SSLv3", PY_PROTO_SSLv3);
|
||||||
PyModule_AddObject(m, "HAS_SNI", r);
|
PyModule_AddIntConstant(m, "PROTO_TLSv1", PY_PROTO_TLSv1);
|
||||||
|
PyModule_AddIntConstant(m, "PROTO_TLSv1_1", PY_PROTO_TLSv1_1);
|
||||||
|
PyModule_AddIntConstant(m, "PROTO_TLSv1_2", PY_PROTO_TLSv1_2);
|
||||||
|
PyModule_AddIntConstant(m, "PROTO_TLSv1_3", PY_PROTO_TLSv1_3);
|
||||||
|
|
||||||
#ifdef OPENSSL_NO_ECDH
|
#define addbool(m, v, b) \
|
||||||
r = Py_False;
|
Py_INCREF((b) ? Py_True : Py_False); \
|
||||||
|
PyModule_AddObject((m), (v), (b) ? Py_True : Py_False);
|
||||||
|
|
||||||
|
#if HAVE_SNI
|
||||||
|
addbool(m, "HAS_SNI", 1);
|
||||||
#else
|
#else
|
||||||
r = Py_True;
|
addbool(m, "HAS_SNI", 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
addbool(m, "HAS_TLS_UNIQUE", 1);
|
||||||
|
|
||||||
|
#ifndef OPENSSL_NO_ECDH
|
||||||
|
addbool(m, "HAS_ECDH", 1);
|
||||||
|
#else
|
||||||
|
addbool(m, "HAS_ECDH", 0);
|
||||||
#endif
|
#endif
|
||||||
Py_INCREF(r);
|
|
||||||
PyModule_AddObject(m, "HAS_ECDH", r);
|
|
||||||
|
|
||||||
#if HAVE_NPN
|
#if HAVE_NPN
|
||||||
r = Py_True;
|
addbool(m, "HAS_NPN", 1);
|
||||||
#else
|
#else
|
||||||
r = Py_False;
|
addbool(m, "HAS_NPN", 0);
|
||||||
#endif
|
#endif
|
||||||
Py_INCREF(r);
|
|
||||||
PyModule_AddObject(m, "HAS_NPN", r);
|
|
||||||
|
|
||||||
#if HAVE_ALPN
|
#if HAVE_ALPN
|
||||||
r = Py_True;
|
addbool(m, "HAS_ALPN", 1);
|
||||||
#else
|
#else
|
||||||
r = Py_False;
|
addbool(m, "HAS_ALPN", 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(SSL2_VERSION) && !defined(OPENSSL_NO_SSL2)
|
||||||
|
addbool(m, "HAS_SSLv2", 1);
|
||||||
|
#else
|
||||||
|
addbool(m, "HAS_SSLv2", 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(SSL3_VERSION) && !defined(OPENSSL_NO_SSL3)
|
||||||
|
addbool(m, "HAS_SSLv3", 1);
|
||||||
|
#else
|
||||||
|
addbool(m, "HAS_SSLv3", 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(TLS1_VERSION) && !defined(OPENSSL_NO_TLS1)
|
||||||
|
addbool(m, "HAS_TLSv1", 1);
|
||||||
|
#else
|
||||||
|
addbool(m, "HAS_TLSv1", 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(TLS1_1_VERSION) && !defined(OPENSSL_NO_TLS1_1)
|
||||||
|
addbool(m, "HAS_TLSv1_1", 1);
|
||||||
|
#else
|
||||||
|
addbool(m, "HAS_TLSv1_1", 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(TLS1_2_VERSION) && !defined(OPENSSL_NO_TLS1_2)
|
||||||
|
addbool(m, "HAS_TLSv1_2", 1);
|
||||||
|
#else
|
||||||
|
addbool(m, "HAS_TLSv1_2", 0);
|
||||||
#endif
|
#endif
|
||||||
Py_INCREF(r);
|
|
||||||
PyModule_AddObject(m, "HAS_ALPN", r);
|
|
||||||
|
|
||||||
#if defined(TLS1_3_VERSION) && !defined(OPENSSL_NO_TLS1_3)
|
#if defined(TLS1_3_VERSION) && !defined(OPENSSL_NO_TLS1_3)
|
||||||
r = Py_True;
|
addbool(m, "HAS_TLSv1_3", 1);
|
||||||
#else
|
#else
|
||||||
r = Py_False;
|
addbool(m, "HAS_TLSv1_3", 0);
|
||||||
#endif
|
#endif
|
||||||
Py_INCREF(r);
|
|
||||||
PyModule_AddObject(m, "HAS_TLSv1_3", r);
|
|
||||||
|
|
||||||
/* Mappings for error codes */
|
/* Mappings for error codes */
|
||||||
err_codes_to_names = PyDict_New();
|
err_codes_to_names = PyDict_New();
|
||||||
|
|
Loading…
Reference in New Issue