Skip to content
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
31 changes: 31 additions & 0 deletions Lib/test/test_ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -4434,6 +4434,37 @@ def test_pha_not_tls13(self):
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(
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
43 changes: 26 additions & 17 deletions Modules/_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down