From 698dde16f60729d9e3f53c23a4ddb8e5ffe818bf Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 27 Feb 2018 11:54:43 +0100 Subject: [PATCH] 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 --- Doc/library/ssl.rst | 101 +++++++- Doc/whatsnew/3.7.rst | 5 + Lib/ssl.py | 37 ++- Lib/test/test_ssl.py | 117 +++++++++ .../2018-01-21-15-01-50.bpo-31453.cZiZBe.rst | 4 + Modules/_ssl.c | 240 ++++++++++++++++-- 6 files changed, 474 insertions(+), 30 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-01-21-15-01-50.bpo-31453.cZiZBe.rst diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index d18a505937a..2b4bed41398 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -762,6 +762,11 @@ Constants .. 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 Prevents a TLSv1.1 connection. This option is only applicable in conjunction @@ -770,6 +775,9 @@ Constants .. versionadded:: 3.4 + .. deprecated:: 3.7 + The option is deprecated since OpenSSL 1.1.0. + .. data:: OP_NO_TLSv1_2 Prevents a TLSv1.2 connection. This option is only applicable in conjunction @@ -778,6 +786,9 @@ Constants .. versionadded:: 3.4 + .. deprecated:: 3.7 + The option is deprecated since OpenSSL 1.1.0. + .. data:: OP_NO_TLSv1_3 Prevents a TLSv1.3 connection. This option is only applicable in conjunction @@ -788,6 +799,10 @@ Constants .. 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 Use the server's cipher ordering preference, rather than the client's. @@ -856,7 +871,7 @@ Constants .. 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 explicitly disabled by the distributor. @@ -871,7 +886,7 @@ Constants .. 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 `_. When true, you can use the :meth:`SSLContext.set_npn_protocols` method to advertise @@ -879,6 +894,36 @@ Constants .. 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 Whether the OpenSSL library has built-in support for the TLS 1.3 protocol. @@ -965,6 +1010,27 @@ Constants .. 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 ----------- @@ -1788,6 +1854,37 @@ to speed up repeated connections from the same clients. 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 An integer representing the set of SSL options enabled on this context. diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index 2d62ffa5004..fa2d472820c 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -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`. (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 ------ diff --git a/Lib/ssl.py b/Lib/ssl.py index 75ebcc165a1..2db88735471 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -112,9 +112,11 @@ except ImportError: pass -from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN, HAS_TLSv1_3 -from _ssl import _DEFAULT_CIPHERS -from _ssl import _OPENSSL_API_VERSION +from _ssl import ( + HAS_SNI, HAS_ECDH, HAS_NPN, HAS_ALPN, HAS_SSLv2, HAS_SSLv3, HAS_TLSv1, + HAS_TLSv1_1, HAS_TLSv1_2, HAS_TLSv1_3 +) +from _ssl import _DEFAULT_CIPHERS, _OPENSSL_API_VERSION _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) +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": from _ssl import enum_certificates, enum_crls @@ -467,6 +479,25 @@ class SSLContext(_SSLContext): self._load_windows_store_certs(storename, purpose) 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 def options(self): return Options(super().options) diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index ca2357e98e3..8d98b805b49 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -1077,6 +1077,69 @@ class ContextTests(unittest.TestCase): with self.assertRaises(AttributeError): 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(), "verify_flags need OpenSSL > 0.9.8") def test_verify_flags(self): @@ -3457,6 +3520,60 @@ class ThreadedTests(unittest.TestCase): }) 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") def test_default_ecdh_curve(self): # Issue #21015: elliptic curve-based Diffie Hellman key exchange diff --git a/Misc/NEWS.d/next/Library/2018-01-21-15-01-50.bpo-31453.cZiZBe.rst b/Misc/NEWS.d/next/Library/2018-01-21-15-01-50.bpo-31453.cZiZBe.rst new file mode 100644 index 00000000000..6d43dfd8207 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-01-21-15-01-50.bpo-31453.cZiZBe.rst @@ -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. diff --git a/Modules/_ssl.c b/Modules/_ssl.c index f50823e6947..f9e061dfe01 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -320,6 +320,53 @@ enum py_ssl_version { 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. */ /* 0 means no, greater than 0 means yes */ @@ -3323,6 +3370,106 @@ set_verify_flags(PySSLContext *self, PyObject *arg, void *c) 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 * get_options(PySSLContext *self, void *c) { @@ -4289,8 +4436,14 @@ static PyGetSetDef context_getsetlist[] = { (setter) set_check_hostname, NULL}, {"_host_flags", (getter) get_host_flags, (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, - (setter) set_sni_callback, PySSLContext_sni_callback_doc}, + (setter) set_sni_callback, PySSLContext_sni_callback_doc}, {"options", (getter) get_options, (setter) set_options, NULL}, {"protocol", (getter) get_protocol, @@ -5711,45 +5864,82 @@ PyInit__ssl(void) X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS); #endif -#if HAVE_SNI - r = Py_True; -#else - r = Py_False; -#endif - Py_INCREF(r); - PyModule_AddObject(m, "HAS_SNI", r); + /* protocol versions */ + PyModule_AddIntConstant(m, "PROTO_MINIMUM_SUPPORTED", + PY_PROTO_MINIMUM_SUPPORTED); + PyModule_AddIntConstant(m, "PROTO_MAXIMUM_SUPPORTED", + PY_PROTO_MAXIMUM_SUPPORTED); + PyModule_AddIntConstant(m, "PROTO_SSLv3", PY_PROTO_SSLv3); + 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 - r = Py_False; +#define addbool(m, v, b) \ + 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 - 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 - Py_INCREF(r); - PyModule_AddObject(m, "HAS_ECDH", r); #if HAVE_NPN - r = Py_True; + addbool(m, "HAS_NPN", 1); #else - r = Py_False; + addbool(m, "HAS_NPN", 0); #endif - Py_INCREF(r); - PyModule_AddObject(m, "HAS_NPN", r); #if HAVE_ALPN - r = Py_True; + addbool(m, "HAS_ALPN", 1); #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 - Py_INCREF(r); - PyModule_AddObject(m, "HAS_ALPN", r); #if defined(TLS1_3_VERSION) && !defined(OPENSSL_NO_TLS1_3) - r = Py_True; + addbool(m, "HAS_TLSv1_3", 1); #else - r = Py_False; + addbool(m, "HAS_TLSv1_3", 0); #endif - Py_INCREF(r); - PyModule_AddObject(m, "HAS_TLSv1_3", r); /* Mappings for error codes */ err_codes_to_names = PyDict_New();