Implement session id TLSv1.3 middlebox compatibility mode Clients will send a "fake" session id and servers must echo it back. Reviewed-by: Ben Kaduk <kaduk@mit.edu> (Merged from https://github.com/openssl/openssl/pull/4701)
diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c index 3c628bd..473da7a 100644 --- a/ssl/statem/statem_clnt.c +++ b/ssl/statem/statem_clnt.c
@@ -1028,6 +1028,7 @@ SSL_COMP *comp; #endif SSL_SESSION *sess = s->session; + unsigned char *session_id; if (!WPACKET_set_max_size(pkt, SSL3_RT_MAX_PLAIN_LENGTH)) { /* Should not happen */ @@ -1047,7 +1048,7 @@ if (sess == NULL || !ssl_version_supported(s, sess->ssl_version) || !SSL_SESSION_is_resumable(sess)) { - if (!ssl_get_new_session(s, 0)) { + if (!s->hello_retry_request && !ssl_get_new_session(s, 0)) { /* SSLfatal() already called */ return 0; } @@ -1121,13 +1122,34 @@ } /* Session ID */ - if (s->new_session || s->session->ssl_version == TLS1_3_VERSION) - sess_id_len = 0; - else + session_id = s->session->session_id; + if (s->new_session || s->session->ssl_version == TLS1_3_VERSION) { + if (s->version == TLS1_3_VERSION + && (s->options & SSL_OP_ENABLE_MIDDLEBOX_COMPAT) != 0) { + sess_id_len = sizeof(s->tmp_session_id); + s->tmp_session_id_len = sess_id_len; + session_id = s->tmp_session_id; + if (!s->hello_retry_request + && ssl_randbytes(s, s->tmp_session_id, + sess_id_len) <= 0) { + SSLfatal(s, SSL_AD_INTERNAL_ERROR, + SSL_F_TLS_CONSTRUCT_CLIENT_HELLO, + ERR_R_INTERNAL_ERROR); + return 0; + } + } else { + sess_id_len = 0; + } + } else { sess_id_len = s->session->session_id_length; + if (s->version == TLS1_3_VERSION) { + s->tmp_session_id_len = sess_id_len; + memcpy(s->tmp_session_id, s->session->session_id, sess_id_len); + } + } if (sess_id_len > sizeof(s->session->session_id) || !WPACKET_start_sub_packet_u8(pkt) - || (sess_id_len != 0 && !WPACKET_memcpy(pkt, s->session->session_id, + || (sess_id_len != 0 && !WPACKET_memcpy(pkt, session_id, sess_id_len)) || !WPACKET_close(pkt)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_CLIENT_HELLO, @@ -1393,25 +1415,35 @@ goto err; } - /* - * In TLSv1.3 a ServerHello message signals a key change so the end of the - * message must be on a record boundary. - */ - if (SSL_IS_TLS13(s) && RECORD_LAYER_processed_read_pending(&s->rlayer)) { - SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, SSL_F_TLS_PROCESS_SERVER_HELLO, - SSL_R_NOT_ON_RECORD_BOUNDARY); - goto err; - } - - if (SSL_IS_TLS13(s) && compression != 0) { - SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, SSL_F_TLS_PROCESS_SERVER_HELLO, - SSL_R_INVALID_COMPRESSION_ALGORITHM); - goto err; - } - s->hit = 0; if (SSL_IS_TLS13(s)) { + /* + * In TLSv1.3 a ServerHello message signals a key change so the end of + * the message must be on a record boundary. + */ + if (RECORD_LAYER_processed_read_pending(&s->rlayer)) { + SSLfatal(s, SSL_AD_UNEXPECTED_MESSAGE, + SSL_F_TLS_PROCESS_SERVER_HELLO, + SSL_R_NOT_ON_RECORD_BOUNDARY); + goto err; + } + + if (compression != 0) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, + SSL_F_TLS_PROCESS_SERVER_HELLO, + SSL_R_INVALID_COMPRESSION_ALGORITHM); + goto err; + } + + if (session_id_len != s->tmp_session_id_len + || memcmp(PACKET_data(&session_id), s->tmp_session_id, + session_id_len) != 0) { + SSLfatal(s, SSL_AD_ILLEGAL_PARAMETER, + SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_INVALID_SESSION_ID); + goto err; + } + /* This will set s->hit if we are resuming */ if (!tls_parse_extension(s, TLSEXT_IDX_psk, SSL_EXT_TLS1_3_SERVER_HELLO, @@ -1493,11 +1525,19 @@ } s->session->ssl_version = s->version; - s->session->session_id_length = session_id_len; - /* session_id_len could be 0 */ - if (session_id_len > 0) - memcpy(s->session->session_id, PACKET_data(&session_id), - session_id_len); + /* + * In TLSv1.2 and below we save the session id we were sent so we can + * resume it later. In TLSv1.3 the session id we were sent is just an + * echo of what we originally sent in the ClientHello and should not be + * used for resumption. + */ + if (!SSL_IS_TLS13(s)) { + s->session->session_id_length = session_id_len; + /* session_id_len could be 0 */ + if (session_id_len > 0) + memcpy(s->session->session_id, PACKET_data(&session_id), + session_id_len); + } } /* Session version and negotiated protocol version should match */
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index e91bba4..3608309 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c
@@ -1686,6 +1686,12 @@ } } + if (SSL_IS_TLS13(s)) { + memcpy(s->tmp_session_id, s->clienthello->session_id, + s->clienthello->session_id_len); + s->tmp_session_id_len = s->clienthello->session_id_len; + } + /* * If it is a hit, check that the cipher is in the list. In TLSv1.3 we check * ciphersuite compatibility with the session as part of resumption. @@ -2192,6 +2198,7 @@ int compm; size_t sl, len; int version; + unsigned char *session_id; version = SSL_IS_TLS13(s) ? TLS1_2_VERSION : s->version; if (!WPACKET_put_bytes_u16(pkt, version) @@ -2217,6 +2224,8 @@ * session ID. * - However, if we want the new session to be single-use, * we send back a 0-length session ID. + * - In TLSv1.3 we echo back the session id sent to us by the client + * regardless * s->hit is non-zero in either case of session reuse, * so the following won't overwrite an ID that we're supposed * to send back. @@ -2226,17 +2235,20 @@ && !s->hit)) s->session->session_id_length = 0; - sl = s->session->session_id_length; + if (SSL_IS_TLS13(s)) { + sl = s->tmp_session_id_len; + session_id = s->tmp_session_id; + } else { + sl = s->session->session_id_length; + session_id = s->session->session_id; + } + if (sl > sizeof(s->session->session_id)) { SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_TLS_CONSTRUCT_SERVER_HELLO, ERR_R_INTERNAL_ERROR); return 0; } - /* Never send a session_id back in the ServerHello */ - if (SSL_IS_TLS13(s)) - sl = 0; - /* set up the compression method */ #ifdef OPENSSL_NO_COMP compm = 0; @@ -2247,7 +2259,7 @@ compm = s->s3->tmp.new_compression->id; #endif - if (!WPACKET_sub_memcpy_u8(pkt, s->session->session_id, sl) + if (!WPACKET_sub_memcpy_u8(pkt, session_id, sl) || !s->method->put_cipher_by_char(s->s3->tmp.new_cipher, pkt, &len) || !WPACKET_put_bytes_u8(pkt, compm) || !tls_construct_extensions(s, pkt,