bpo-37428: Don't set PHA verify flag on client side (GH-14421)

SSLContext.post_handshake_auth = True no longer sets
SSL_VERIFY_POST_HANDSHAKE verify flag for client connections. Although the
option is documented as ignored for clients, OpenSSL implicitly enables cert
chain validation when the flag is set.

Signed-off-by: Christian Heimes <christian@python.org>



https://bugs.python.org/issue37428
This commit is contained in:
Christian Heimes 2019-07-01 08:29:17 +02:00 committed by Miss Islington (bot)
parent 12b436e3b0
commit f0f5930ac8
3 changed files with 61 additions and 17 deletions

View File

@ -4434,6 +4434,37 @@ class TestPostHandshakeAuth(unittest.TestCase):
s.write(b'PHA')
self.assertIn(b'WRONG_SSL_VERSION', s.recv(1024))
def test_bpo37428_pha_cert_none(self):
# verify that post_handshake_auth does not implicitly enable cert
# validation.
hostname = SIGNED_CERTFILE_HOSTNAME
client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
client_context.post_handshake_auth = True
client_context.load_cert_chain(SIGNED_CERTFILE)
# no cert validation and CA on client side
client_context.check_hostname = False
client_context.verify_mode = ssl.CERT_NONE
server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
server_context.load_cert_chain(SIGNED_CERTFILE)
server_context.load_verify_locations(SIGNING_CA)
server_context.post_handshake_auth = True
server_context.verify_mode = ssl.CERT_REQUIRED
server = ThreadedEchoServer(context=server_context, chatty=False)
with server:
with client_context.wrap_socket(socket.socket(),
server_hostname=hostname) as s:
s.connect((HOST, server.port))
s.write(b'HASCERT')
self.assertEqual(s.recv(1024), b'FALSE\n')
s.write(b'PHA')
self.assertEqual(s.recv(1024), b'OK\n')
s.write(b'HASCERT')
self.assertEqual(s.recv(1024), b'TRUE\n')
# server cert has not been validated
self.assertEqual(s.getpeercert(), {})
HAS_KEYLOG = hasattr(ssl.SSLContext, 'keylog_filename')
requires_keylog = unittest.skipUnless(

View File

@ -0,0 +1,4 @@
SSLContext.post_handshake_auth = True no longer sets
SSL_VERIFY_POST_HANDSHAKE verify flag for client connections. Although the
option is documented as ignored for clients, OpenSSL implicitly enables cert
chain validation when the flag is set.

View File

@ -963,6 +963,26 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
SSL_set_mode(self->ssl,
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_AUTO_RETRY);
#ifdef TLS1_3_VERSION
if (sslctx->post_handshake_auth == 1) {
if (socket_type == PY_SSL_SERVER) {
/* bpo-37428: OpenSSL does not ignore SSL_VERIFY_POST_HANDSHAKE.
* Set SSL_VERIFY_POST_HANDSHAKE flag only for server sockets and
* only in combination with SSL_VERIFY_PEER flag. */
int mode = SSL_get_verify_mode(self->ssl);
if (mode & SSL_VERIFY_PEER) {
int (*verify_cb)(int, X509_STORE_CTX *) = NULL;
verify_cb = SSL_get_verify_callback(self->ssl);
mode |= SSL_VERIFY_POST_HANDSHAKE;
SSL_set_verify(self->ssl, mode, verify_cb);
}
} else {
/* client socket */
SSL_set_post_handshake_auth(self->ssl, 1);
}
}
#endif
if (server_hostname != NULL) {
if (_ssl_configure_hostname(self, server_hostname) < 0) {
Py_DECREF(self);
@ -2986,10 +3006,10 @@ _set_verify_mode(PySSLContext *self, enum py_ssl_cert_requirements n)
"invalid value for verify_mode");
return -1;
}
#ifdef TLS1_3_VERSION
if (self->post_handshake_auth)
mode |= SSL_VERIFY_POST_HANDSHAKE;
#endif
/* bpo-37428: newPySSLSocket() sets SSL_VERIFY_POST_HANDSHAKE flag for
* server sockets and SSL_set_post_handshake_auth() for client. */
/* keep current verify cb */
verify_cb = SSL_CTX_get_verify_callback(self->ctx);
SSL_CTX_set_verify(self->ctx, mode, verify_cb);
@ -3735,8 +3755,6 @@ get_post_handshake_auth(PySSLContext *self, void *c) {
#if TLS1_3_VERSION
static int
set_post_handshake_auth(PySSLContext *self, PyObject *arg, void *c) {
int (*verify_cb)(int, X509_STORE_CTX *) = NULL;
int mode = SSL_CTX_get_verify_mode(self->ctx);
if (arg == NULL) {
PyErr_SetString(PyExc_AttributeError, "cannot delete attribute");
return -1;
@ -3748,17 +3766,8 @@ set_post_handshake_auth(PySSLContext *self, PyObject *arg, void *c) {
}
self->post_handshake_auth = pha;
/* client-side socket setting, ignored by server-side */
SSL_CTX_set_post_handshake_auth(self->ctx, pha);
/* server-side socket setting, ignored by client-side */
verify_cb = SSL_CTX_get_verify_callback(self->ctx);
if (pha) {
mode |= SSL_VERIFY_POST_HANDSHAKE;
} else {
mode ^= SSL_VERIFY_POST_HANDSHAKE;
}
SSL_CTX_set_verify(self->ctx, mode, verify_cb);
/* bpo-37428: newPySSLSocket() sets SSL_VERIFY_POST_HANDSHAKE flag for
* server sockets and SSL_set_post_handshake_auth() for client. */
return 0;
}