Skip to content

Commit c2254d2

Browse files
committed
CVE-2023-40217 Add in bpo-37428 (pythonGH-14421) so that code can compile
1 parent c97c32c commit c2254d2

3 files changed

Lines changed: 96 additions & 1 deletion

File tree

Lib/test/test_ssl.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3728,6 +3728,37 @@ def call_after_accept(conn_to_client):
37283728

37293729
server.join()
37303730

3731+
def test_bpo37428_pha_cert_none(self):
3732+
# verify that post_handshake_auth does not implicitly enable cert
3733+
# validation.
3734+
hostname = SIGNED_CERTFILE_HOSTNAME
3735+
client_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
3736+
client_context.post_handshake_auth = True
3737+
client_context.load_cert_chain(SIGNED_CERTFILE)
3738+
# no cert validation and CA on client side
3739+
client_context.check_hostname = False
3740+
client_context.verify_mode = ssl.CERT_NONE
3741+
3742+
server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
3743+
server_context.load_cert_chain(SIGNED_CERTFILE)
3744+
server_context.load_verify_locations(SIGNING_CA)
3745+
server_context.post_handshake_auth = True
3746+
server_context.verify_mode = ssl.CERT_REQUIRED
3747+
3748+
server = ThreadedEchoServer(context=server_context, chatty=False)
3749+
with server:
3750+
with client_context.wrap_socket(socket.socket(),
3751+
server_hostname=hostname) as s:
3752+
s.connect((HOST, server.port))
3753+
s.write(b'HASCERT')
3754+
self.assertEqual(s.recv(1024), b'FALSE\n')
3755+
s.write(b'PHA')
3756+
self.assertEqual(s.recv(1024), b'OK\n')
3757+
s.write(b'HASCERT')
3758+
self.assertEqual(s.recv(1024), b'TRUE\n')
3759+
# server cert has not been validated
3760+
self.assertEqual(s.getpeercert(), {})
3761+
37313762

37323763
def test_main(verbose=False):
37333764
if support.verbose:
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
SSLContext.post_handshake_auth = True no longer sets
2+
SSL_VERIFY_POST_HANDSHAKE verify flag for client connections. Although the
3+
option is documented as ignored for clients, OpenSSL implicitly enables cert
4+
chain validation when the flag is set.

Modules/_ssl.c

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,9 @@ typedef struct {
324324
PyObject *set_hostname;
325325
#endif
326326
int check_hostname;
327+
#ifdef TLS1_3_VERSION
328+
int post_handshake_auth;
329+
#endif
327330
} PySSLContext;
328331

329332
typedef struct {
@@ -606,6 +609,28 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
606609
#endif
607610
SSL_set_mode(self->ssl, mode);
608611

612+
/* bpo-37428: newPySSLSocket() sets SSL_VERIFY_POST_HANDSHAKE flag for
613+
* server sockets and SSL_set_post_handshake_auth() for client. */
614+
// #ifdef TLS1_3_VERSION
615+
// if (sslctx->post_handshake_auth == 1) {
616+
// if (socket_type == PY_SSL_SERVER) {
617+
// /* bpo-37428: OpenSSL does not ignore SSL_VERIFY_POST_HANDSHAKE.
618+
// * Set SSL_VERIFY_POST_HANDSHAKE flag only for server sockets and
619+
// * only in combination with SSL_VERIFY_PEER flag. */
620+
// int mode = SSL_get_verify_mode(self->ssl);
621+
// if (mode & SSL_VERIFY_PEER) {
622+
// int (*verify_cb)(int, X509_STORE_CTX *) = NULL;
623+
// verify_cb = SSL_get_verify_callback(self->ssl);
624+
// mode |= SSL_VERIFY_POST_HANDSHAKE;
625+
// SSL_set_verify(self->ssl, mode, verify_cb);
626+
// }
627+
// } else {
628+
// /* client socket */
629+
// SSL_set_post_handshake_auth(self->ssl, 1);
630+
// }
631+
// }
632+
// #endif
633+
609634
#if HAVE_SNI
610635
if (server_hostname != NULL) {
611636
/* Don't send SNI for IP addresses. We cannot simply use inet_aton() and
@@ -2308,6 +2333,11 @@ context_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
23082333
}
23092334
#endif
23102335

2336+
#ifdef TLS1_3_VERSION
2337+
self->post_handshake_auth = 0;
2338+
SSL_CTX_set_post_handshake_auth(self->ctx, self->post_handshake_auth);
2339+
#endif
2340+
23112341
return (PyObject *)self;
23122342
}
23132343

@@ -2527,6 +2557,8 @@ static int
25272557
set_verify_mode(PySSLContext *self, PyObject *arg, void *c)
25282558
{
25292559
int n, mode;
2560+
int (*verify_cb)(int, X509_STORE_CTX *) = NULL;
2561+
25302562
if (!PyArg_Parse(arg, "i", &n))
25312563
return -1;
25322564
if (n == PY_SSL_CERT_NONE)
@@ -2540,13 +2572,20 @@ set_verify_mode(PySSLContext *self, PyObject *arg, void *c)
25402572
"invalid value for verify_mode");
25412573
return -1;
25422574
}
2575+
25432576
if (mode == SSL_VERIFY_NONE && self->check_hostname) {
25442577
PyErr_SetString(PyExc_ValueError,
25452578
"Cannot set verify_mode to CERT_NONE when "
25462579
"check_hostname is enabled.");
25472580
return -1;
25482581
}
2549-
SSL_CTX_set_verify(self->ctx, mode, NULL);
2582+
2583+
/* bpo-37428: newPySSLSocket() sets SSL_VERIFY_POST_HANDSHAKE flag for
2584+
* server sockets and SSL_set_post_handshake_auth() for client. */
2585+
2586+
/* keep current verify cb */
2587+
verify_cb = SSL_CTX_get_verify_callback(self->ctx);
2588+
SSL_CTX_set_verify(self->ctx, mode, verify_cb);
25502589
return 0;
25512590
}
25522591

@@ -2651,6 +2690,27 @@ set_check_hostname(PySSLContext *self, PyObject *arg, void *c)
26512690
return 0;
26522691
}
26532692

2693+
#if TLS1_3_VERSION
2694+
static int
2695+
set_post_handshake_auth(PySSLContext *self, PyObject *arg, void *c) {
2696+
if (arg == NULL) {
2697+
PyErr_SetString(PyExc_AttributeError, "cannot delete attribute");
2698+
return -1;
2699+
}
2700+
int pha = PyObject_IsTrue(arg);
2701+
2702+
if (pha == -1) {
2703+
return -1;
2704+
}
2705+
self->post_handshake_auth = pha;
2706+
2707+
/* bpo-37428: newPySSLSocket() sets SSL_VERIFY_POST_HANDSHAKE flag for
2708+
* server sockets and SSL_set_post_handshake_auth() for client. */
2709+
2710+
return 0;
2711+
}
2712+
#endif
2713+
26542714

26552715
typedef struct {
26562716
PyThreadState *thread_state;

0 commit comments

Comments
 (0)