Skip to content

bpo-31432: Clarify CERT_NONE/OPTIONAL/REQUIRED doc #3530

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 25 additions & 13 deletions Doc/library/ssl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -543,20 +543,28 @@ Constants
.. data:: CERT_NONE

Possible value for :attr:`SSLContext.verify_mode`, or the ``cert_reqs``
parameter to :func:`wrap_socket`. In this mode (the default), no
certificates will be required from the other side of the socket connection.
If a certificate is received from the other end, no attempt to validate it
is made.
parameter to :func:`wrap_socket`. Except for :const:`PROTOCOL_TLS_CLIENT`,
it is the default mode. With client-side sockets, just about any
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does cert_reqs default to if I call wrap_socket(..., ssl_version=PROTOCOL_TLS_CLIENT)? Judging by the signature and documentation for wrap_socket, CERT_NONE seems to be the default regardless of the protocol setting. If you are only talking about the default value for verify_mode after an SSLContext is constructed, I suggest to mention that under the entry for SSLContext.verify_mode, or the SSLContext constructor, instead of here. (Otherwise, maybe adjust the description of the top-level wrap_socket function.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a moment I feared that you have discovered a security bug.

Thankfully the code is robust and doesn't allow you to use PROTOCOL_TLS_CLIENT with default arguments of wrap_socket and SSLSocket. Both ssl.wrap_socket(ssl_version=ssl.PROTOCOL_TLS_CLIENT) and ssl.SSLSocket(ssl_version=ssl.PROTOCOL_TLS_CLIENT) will both fail with error Cannot set verify_mode to CERT_NONE when check_hostname is enabled..

So the insecure default doesn't work here. I consider it a feature. :) I'm going to deprecate ssl.wrap_socket and ssl.SSLSocket() default constructor because they have more fundamental issues.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, now I realize that the PROTOCOL_TLS_CLIENT mode is newer than the old wrap_socket function. It was not clear that wrap_socket doesn’t support this new mode, but that is separate to what you are trying to do here.

cert is accepted. Validation errors, such as untrusted or expired cert,
are ignored and do not abort the TLS/SSL handshake.

In server mode, no certificate is requested from the client, so the client
does not send any for client cert authentication.

See the discussion of :ref:`ssl-security` below.

.. data:: CERT_OPTIONAL

Possible value for :attr:`SSLContext.verify_mode`, or the ``cert_reqs``
parameter to :func:`wrap_socket`. In this mode no certificates will be
required from the other side of the socket connection; but if they
are provided, validation will be attempted and an :class:`SSLError`
will be raised on failure.
parameter to :func:`wrap_socket`. In client mode, :const:`CERT_OPTIONAL`
has the same meaning as :const:`CERT_REQUIRED`. It is recommended to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if “anonymous ciphers are enabled”, mentioned under https://docs.python.org/dev/library/ssl.html#verifying-certificates?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather not explain anonymous cipher suites and just remove them from the documentation completely. Anonymous cipher suites is a misleading term. They don't provide anonymity for the user but allow the server to stay anonymous by not providing any authentication. It's totally insecure and no longer supported in TLS 1.3 for a very good reason.

use :const:`CERT_REQUIRED` for client-side sockets instead.

In server mode, a client certificate request is sent to the client. The
client may either ignore the request or send a certificate in order
perform TLS client cert authentication. If the client chooses to send
a certificate, it is verified. Any verification error immediately aborts
the TLS handshake.

Use of this setting requires a valid set of CA certificates to
be passed, either to :meth:`SSLContext.load_verify_locations` or as a
Expand All @@ -568,6 +576,15 @@ Constants
parameter to :func:`wrap_socket`. In this mode, certificates are
required from the other side of the socket connection; an :class:`SSLError`
will be raised if no certificate is provided, or if its validation fails.
This mode is **not** sufficient to verify a certificate in client mode as
it does not match hostnames. :attr:`~SSLContext.check_hostname` must be
enabled as well to verify the authenticity of a cert.
:const:`PROTOCOL_TLS_CLIENT` uses :const:`CERT_REQUIRED` and
enables :attr:`~SSLContext.check_hostname` by default.

With server socket, this mode provides mandatory TLS client cert
authentication. A client certificate request is sent to the client and
the client must provide a valid and trusted certificate.

Use of this setting requires a valid set of CA certificates to
be passed, either to :meth:`SSLContext.load_verify_locations` or as a
Expand Down Expand Up @@ -2536,11 +2553,6 @@ In server mode, if you want to authenticate your clients using the SSL layer
(rather than using a higher-level authentication mechanism), you'll also have
to specify :const:`CERT_REQUIRED` and similarly check the client certificate.

.. note::

In client mode, :const:`CERT_OPTIONAL` and :const:`CERT_REQUIRED` are
equivalent unless anonymous ciphers are enabled (they are disabled
by default).

Protocol versions
'''''''''''''''''
Expand Down
4 changes: 3 additions & 1 deletion Lib/test/test_ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -4025,7 +4025,9 @@ def test_session_handling(self):
self.assertTrue(session)
with self.assertRaises(TypeError) as e:
s.session = object
self.assertEqual(str(e.exception), 'Value is not a SSLSession.')
self.assertEqual(
str(e.exception), 'Value is not an SSLSession.'
)

with client_context.wrap_socket(socket.socket(),
server_hostname=hostname) as s:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Clarify meaning of CERT_NONE, CERT_OPTIONAL, and CERT_REQUIRED flags for
ssl.SSLContext.verify_mode.
4 changes: 2 additions & 2 deletions Modules/_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -2066,7 +2066,7 @@ static int PySSL_set_context(PySSLSocket *self, PyObject *value,
SSL_set_SSL_CTX(self->ssl, self->ctx->ctx);
#endif
} else {
PyErr_SetString(PyExc_TypeError, "The value must be a SSLContext");
PyErr_SetString(PyExc_TypeError, "The value must be an SSLContext.");
return -1;
}

Expand Down Expand Up @@ -2725,7 +2725,7 @@ static int PySSL_set_session(PySSLSocket *self, PyObject *value,
int result;

if (!PySSLSession_Check(value)) {
PyErr_SetString(PyExc_TypeError, "Value is not a SSLSession.");
PyErr_SetString(PyExc_TypeError, "Value is not an SSLSession.");
return -1;
}
pysess = (PySSLSession *)value;
Expand Down