.. versionadded:: 3.6
+.. data:: PROTOCOL_TLS_CLIENT
+
+ Auto-negotiate the the highest protocol version like :data:`PROTOCOL_SSLv23`,
+ but only support client-side :class:`SSLSocket` connections. The protocol
+ enables :data:`CERT_REQUIRED` and :attr:`~SSLContext.check_hostname` by
+ default.
+
+ .. versionadded:: 3.6
+
+.. data:: PROTOCOL_TLS_SERVER
+
+ Auto-negotiate the the highest protocol version like :data:`PROTOCOL_SSLv23`,
+ but only support server-side :class:`SSLSocket` connections.
+
+ .. versionadded:: 3.6
+
.. data:: PROTOCOL_SSLv23
Alias for data:`PROTOCOL_TLS`.
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_TLS` as the protocol version and then
-disable SSLv2 and SSLv3 explicitly using the :data:`SSLContext.options`
-attribute::
+recommended to use :const:`PROTOCOL_TLS_CLIENT` or
+:const:`PROTOCOL_TLS_SERVER` as the protocol version. SSLv2 and SSLv3 are
+disabled by default.
+
+ client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
+ client_context.options |= ssl.OP_NO_TLSv1
+ client_context.options |= ssl.OP_NO_TLSv1_1
- 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.2 and later (if
-supported by your system) connections.
+supported by your system) connections to a server. :const:`PROTOCOL_TLS_CLIENT`
+implies certificate validation and hostname checks by default. You have to
+load certificates into the context.
+
Cipher selection
''''''''''''''''
ssl module disables certain weak ciphers by default, but you may want
to further restrict the cipher choice. Be sure to read OpenSSL's documentation
about the `cipher list format <https://www.openssl.org/docs/apps/ciphers.html#CIPHER-LIST-FORMAT>`_.
-If you want to check which ciphers are enabled by a given cipher list, use the
-``openssl ciphers`` command on your system.
+If you want to check which ciphers are enabled by a given cipher list, use
+:meth:`SSLContext.get_ciphers` or the ``openssl ciphers`` command on your
+system.
Multi-processing
^^^^^^^^^^^^^^^^
PROTOCOL_SSLv3
PROTOCOL_SSLv23
PROTOCOL_TLS
+PROTOCOL_TLS_CLIENT
+PROTOCOL_TLS_SERVER
PROTOCOL_TLSv1
PROTOCOL_TLSv1_1
PROTOCOL_TLSv1_2
ctx.check_hostname = False
self.assertFalse(ctx.check_hostname)
+ def test_context_client_server(self):
+ # PROTOCOL_TLS_CLIENT has sane defaults
+ ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
+ self.assertTrue(ctx.check_hostname)
+ self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED)
+
+ # PROTOCOL_TLS_SERVER has different but also sane defaults
+ ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
+ self.assertFalse(ctx.check_hostname)
+ self.assertEqual(ctx.verify_mode, ssl.CERT_NONE)
+
class SSLErrorTests(unittest.TestCase):
if support.verbose:
sys.stdout.write("\n")
for protocol in PROTOCOLS:
+ if protocol in {ssl.PROTOCOL_TLS_CLIENT, ssl.PROTOCOL_TLS_SERVER}:
+ continue
with self.subTest(protocol=ssl._PROTOCOL_NAMES[protocol]):
context = ssl.SSLContext(protocol)
context.load_cert_chain(CERTFILE)
server_params_test(context, context,
chatty=True, connectionchatty=True)
+ client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
+ client_context.load_verify_locations(SIGNING_CA)
+ server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
+ # server_context.load_verify_locations(SIGNING_CA)
+ server_context.load_cert_chain(SIGNED_CERTFILE2)
+
+ with self.subTest(client='PROTOCOL_TLS_CLIENT', server='PROTOCOL_TLS_SERVER'):
+ server_params_test(client_context=client_context,
+ server_context=server_context,
+ chatty=True, connectionchatty=True,
+ sni_name='fakehostname')
+
+ with self.subTest(client='PROTOCOL_TLS_SERVER', server='PROTOCOL_TLS_CLIENT'):
+ with self.assertRaises(ssl.SSLError):
+ server_params_test(client_context=server_context,
+ server_context=client_context,
+ chatty=True, connectionchatty=True,
+ sni_name='fakehostname')
+
def test_getpeercert(self):
if support.verbose:
sys.stdout.write("\n")
#endif
#define TLS_method SSLv23_method
+#define TLS_client_method SSLv23_client_method
+#define TLS_server_method SSLv23_server_method
static int X509_NAME_ENTRY_set(const X509_NAME_ENTRY *ne)
{
enum py_ssl_version {
PY_SSL_VERSION_SSL2,
PY_SSL_VERSION_SSL3=1,
- PY_SSL_VERSION_TLS,
+ PY_SSL_VERSION_TLS, /* SSLv23 */
#if HAVE_TLSv1_2
PY_SSL_VERSION_TLS1,
PY_SSL_VERSION_TLS1_1,
- PY_SSL_VERSION_TLS1_2
+ PY_SSL_VERSION_TLS1_2,
#else
- PY_SSL_VERSION_TLS1
+ PY_SSL_VERSION_TLS1,
#endif
+ PY_SSL_VERSION_TLS_CLIENT=0x10,
+ PY_SSL_VERSION_TLS_SERVER,
};
#ifdef WITH_THREAD
* _SSLContext objects
*/
+static int
+_set_verify_mode(SSL_CTX *ctx, enum py_ssl_cert_requirements n)
+{
+ int mode;
+ int (*verify_cb)(int, X509_STORE_CTX *) = NULL;
+
+ switch(n) {
+ case PY_SSL_CERT_NONE:
+ mode = SSL_VERIFY_NONE;
+ break;
+ case PY_SSL_CERT_OPTIONAL:
+ mode = SSL_VERIFY_PEER;
+ break;
+ case PY_SSL_CERT_REQUIRED:
+ mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+ break;
+ default:
+ PyErr_SetString(PyExc_ValueError,
+ "invalid value for verify_mode");
+ return -1;
+ }
+ /* keep current verify cb */
+ verify_cb = SSL_CTX_get_verify_callback(ctx);
+ SSL_CTX_set_verify(ctx, mode, verify_cb);
+ return 0;
+}
+
/*[clinic input]
@classmethod
_ssl._SSLContext.__new__
else if (proto_version == PY_SSL_VERSION_SSL2)
ctx = SSL_CTX_new(SSLv2_method());
#endif
- else if (proto_version == PY_SSL_VERSION_TLS)
+ else if (proto_version == PY_SSL_VERSION_TLS) /* SSLv23 */
ctx = SSL_CTX_new(TLS_method());
+ else if (proto_version == PY_SSL_VERSION_TLS_CLIENT)
+ ctx = SSL_CTX_new(TLS_client_method());
+ else if (proto_version == PY_SSL_VERSION_TLS_SERVER)
+ ctx = SSL_CTX_new(TLS_server_method());
else
proto_version = -1;
PySSL_END_ALLOW_THREADS
self->set_hostname = NULL;
#endif
/* Don't check host name by default */
- self->check_hostname = 0;
+ if (proto_version == PY_SSL_VERSION_TLS_CLIENT) {
+ self->check_hostname = 1;
+ if (_set_verify_mode(self->ctx, PY_SSL_CERT_REQUIRED) == -1) {
+ Py_DECREF(self);
+ return NULL;
+ }
+ } else {
+ self->check_hostname = 0;
+ if (_set_verify_mode(self->ctx, PY_SSL_CERT_NONE) == -1) {
+ Py_DECREF(self);
+ return NULL;
+ }
+ }
/* Defaults */
- SSL_CTX_set_verify(self->ctx, SSL_VERIFY_NONE, NULL);
options = SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
if (proto_version != PY_SSL_VERSION_SSL2)
options |= SSL_OP_NO_SSLv2;
static int
set_verify_mode(PySSLContext *self, PyObject *arg, void *c)
{
- int n, mode;
+ int n;
if (!PyArg_Parse(arg, "i", &n))
return -1;
- if (n == PY_SSL_CERT_NONE)
- mode = SSL_VERIFY_NONE;
- else if (n == PY_SSL_CERT_OPTIONAL)
- mode = SSL_VERIFY_PEER;
- else if (n == PY_SSL_CERT_REQUIRED)
- mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
- else {
- PyErr_SetString(PyExc_ValueError,
- "invalid value for verify_mode");
- return -1;
- }
- if (mode == SSL_VERIFY_NONE && self->check_hostname) {
+ if (n == PY_SSL_CERT_NONE && self->check_hostname) {
PyErr_SetString(PyExc_ValueError,
"Cannot set verify_mode to CERT_NONE when "
"check_hostname is enabled.");
return -1;
}
- SSL_CTX_set_verify(self->ctx, mode, NULL);
- return 0;
+ return _set_verify_mode(self->ctx, n);
}
static PyObject *
PY_SSL_VERSION_TLS);
PyModule_AddIntConstant(m, "PROTOCOL_TLS",
PY_SSL_VERSION_TLS);
+ PyModule_AddIntConstant(m, "PROTOCOL_TLS_CLIENT",
+ PY_SSL_VERSION_TLS_CLIENT);
+ PyModule_AddIntConstant(m, "PROTOCOL_TLS_SERVER",
+ PY_SSL_VERSION_TLS_SERVER);
PyModule_AddIntConstant(m, "PROTOCOL_TLSv1",
PY_SSL_VERSION_TLS1);
#if HAVE_TLSv1_2