summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Heimes <christian@python.org>2018-02-27 10:54:43 (GMT)
committerGitHub <noreply@github.com>2018-02-27 10:54:43 (GMT)
commit698dde16f60729d9e3f53c23a4ddb8e5ffe818bf (patch)
tree5b63122f0f74257a629f45649ed3511612b6e5b0
parent9d50ab563df6307cabbcc9883cb8c52c614b0f22 (diff)
downloadcpython-698dde16f60729d9e3f53c23a4ddb8e5ffe818bf.zip
cpython-698dde16f60729d9e3f53c23a4ddb8e5ffe818bf.tar.gz
cpython-698dde16f60729d9e3f53c23a4ddb8e5ffe818bf.tar.bz2
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>
-rw-r--r--Doc/library/ssl.rst101
-rw-r--r--Doc/whatsnew/3.7.rst5
-rw-r--r--Lib/ssl.py37
-rw-r--r--Lib/test/test_ssl.py117
-rw-r--r--Misc/NEWS.d/next/Library/2018-01-21-15-01-50.bpo-31453.cZiZBe.rst4
-rw-r--r--Modules/_ssl.c234
6 files changed, 471 insertions, 27 deletions
diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
index d18a505..2b4bed4 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 <https://en.wikipedia.org/wiki/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 2d62ffa..fa2d472 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 75ebcc1..2db8873 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 ca2357e..8d98b80 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 0000000..6d43dfd
--- /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 f50823e..f9e061d 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
+ /* 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);
+
+#define addbool(m, v, b) \
+ Py_INCREF((b) ? Py_True : Py_False); \
+ PyModule_AddObject((m), (v), (b) ? Py_True : Py_False);
+
#if HAVE_SNI
- r = Py_True;
+ addbool(m, "HAS_SNI", 1);
#else
- r = Py_False;
+ addbool(m, "HAS_SNI", 0);
#endif
- Py_INCREF(r);
- PyModule_AddObject(m, "HAS_SNI", r);
-#ifdef OPENSSL_NO_ECDH
- r = Py_False;
+ addbool(m, "HAS_TLS_UNIQUE", 1);
+
+#ifndef OPENSSL_NO_ECDH
+ addbool(m, "HAS_ECDH", 1);
#else
- r = Py_True;
+ 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
+ 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
- r = Py_False;
+ 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();