diff options
author | Matt Caswell <matt@openssl.org> | 2017-11-07 10:45:43 +0000 |
---|---|---|
committer | Matt Caswell <matt@openssl.org> | 2017-12-14 15:06:37 +0000 |
commit | a5816a5ab99610201dcec57a0e02b883d9d32891 (patch) | |
tree | dbcdbc8473d14ad1204c345725e6193d299b22d3 /ssl | |
parent | 88050dd1960bfaba7ede12a3ce1afe40f5deb124 (diff) |
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)
Diffstat (limited to 'ssl')
-rw-r--r-- | ssl/ssl_conf.c | 6 | ||||
-rw-r--r-- | ssl/ssl_err.c | 1 | ||||
-rw-r--r-- | ssl/ssl_lib.c | 6 | ||||
-rw-r--r-- | ssl/ssl_locl.h | 6 | ||||
-rw-r--r-- | ssl/statem/statem_clnt.c | 92 | ||||
-rw-r--r-- | ssl/statem/statem_srvr.c | 24 |
6 files changed, 100 insertions, 35 deletions
diff --git a/ssl/ssl_conf.c b/ssl/ssl_conf.c index fe090ae40d..0f53a47043 100644 --- a/ssl/ssl_conf.c +++ b/ssl/ssl_conf.c @@ -369,7 +369,8 @@ static int cmd_Options(SSL_CONF_CTX *cctx, const char *value) SSL_FLAG_TBL_INV("EncryptThenMac", SSL_OP_NO_ENCRYPT_THEN_MAC), SSL_FLAG_TBL("NoRenegotiation", SSL_OP_NO_RENEGOTIATION), SSL_FLAG_TBL("AllowNoDHEKEX", SSL_OP_ALLOW_NO_DHE_KEX), - SSL_FLAG_TBL("PrioritizeChaCha", SSL_OP_PRIORITIZE_CHACHA) + SSL_FLAG_TBL("PrioritizeChaCha", SSL_OP_PRIORITIZE_CHACHA), + SSL_FLAG_TBL("MiddleboxCompat", SSL_OP_ENABLE_MIDDLEBOX_COMPAT) }; if (value == NULL) return -3; @@ -591,6 +592,7 @@ static const ssl_conf_cmd_tbl ssl_conf_cmds[] = { SSL_CONF_CMD_SWITCH("allow_no_dhe_kex", 0), SSL_CONF_CMD_SWITCH("prioritize_chacha", SSL_CONF_FLAG_SERVER), SSL_CONF_CMD_SWITCH("strict", 0), + SSL_CONF_CMD_SWITCH("no_middlebox", SSL_CONF_FLAG_CLIENT), SSL_CONF_CMD_STRING(SignatureAlgorithms, "sigalgs", 0), SSL_CONF_CMD_STRING(ClientSignatureAlgorithms, "client_sigalgs", 0), SSL_CONF_CMD_STRING(Curves, "curves", 0), @@ -665,6 +667,8 @@ static const ssl_switch_tbl ssl_cmd_switches[] = { /* chacha reprioritization */ {SSL_OP_PRIORITIZE_CHACHA, 0}, {SSL_CERT_FLAG_TLS_STRICT, SSL_TFLAG_CERT}, /* strict */ + /* no_middlebox */ + {SSL_OP_ENABLE_MIDDLEBOX_COMPAT, SSL_TFLAG_INV}, }; static int ssl_conf_cmd_skip_prefix(SSL_CONF_CTX *cctx, const char **pcmd) diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index d608daff07..fa02b21b9d 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -871,6 +871,7 @@ static const ERR_STRING_DATA SSL_str_reasons[] = { "invalid sequence number"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_SERVERINFO_DATA), "invalid serverinfo data"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_SESSION_ID), "invalid session id"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_SRP_USERNAME), "invalid srp username"}, {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_STATUS_RESPONSE), diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 20073184eb..bba0291421 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -2894,9 +2894,11 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth) * Disable compression by default to prevent CRIME. Applications can * re-enable compression by configuring * SSL_CTX_clear_options(ctx, SSL_OP_NO_COMPRESSION); - * or by using the SSL_CONF library. + * or by using the SSL_CONF library. Similarly we also enable TLSv1.3 + * middlebox compatibility by default. This may be disabled by default in + * a later OpenSSL version. */ - ret->options |= SSL_OP_NO_COMPRESSION; + ret->options |= SSL_OP_NO_COMPRESSION | SSL_OP_ENABLE_MIDDLEBOX_COMPAT; ret->ext.status_type = TLSEXT_STATUSTYPE_nothing; diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index ed6b9a8fd4..c2d63fa100 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -1132,6 +1132,12 @@ struct ssl_st { size_t psksession_id_len; /* Default generate session ID callback. */ GEN_SESSION_CB generate_session_id; + /* + * The temporary TLSv1.3 session id. This isn't really a session id at all + * but is a random value sent in the legacy session id field. + */ + unsigned char tmp_session_id[SSL_MAX_SSL_SESSION_ID_LENGTH]; + size_t tmp_session_id_len; /* Used in SSL3 */ /* * 0 don't care about verify failure. diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c index 3c628bdd99..473da7a730 100644 --- a/ssl/statem/statem_clnt.c +++ b/ssl/statem/statem_clnt.c @@ -1028,6 +1028,7 @@ int tls_construct_client_hello(SSL *s, WPACKET *pkt) 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 @@ int tls_construct_client_hello(SSL *s, WPACKET *pkt) 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 @@ int tls_construct_client_hello(SSL *s, WPACKET *pkt) } /* 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 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt) 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 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt) } 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 e91bba4c0f..36083096b9 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -1686,6 +1686,12 @@ static int tls_early_post_process_client_hello(SSL *s) } } + 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 tls_construct_server_hello(SSL *s, WPACKET *pkt) 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 @@ int tls_construct_server_hello(SSL *s, WPACKET *pkt) * 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 @@ int tls_construct_server_hello(SSL *s, WPACKET *pkt) && !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 @@ int tls_construct_server_hello(SSL *s, WPACKET *pkt) 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, |