Allow different protocol version when trying to reuse a session We now send the highest supported version by the client, even if the session uses an older version. This fixes 2 problems: - When you try to reuse a session but the other side doesn't reuse it and uses a different protocol version the connection will fail. - When you're trying to reuse a session with an old version you might be stuck trying to reuse the old version while both sides support a newer version Signed-off-by: Kurt Roeckx <kurt@roeckx.be> Reviewed-by: Viktor Dukhovni <viktor@openssl.org> GH: #852, MR: #2452
diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c index bafb90a..73f54bc 100644 --- a/ssl/statem/statem_clnt.c +++ b/ssl/statem/statem_clnt.c
@@ -817,7 +817,8 @@ goto err; } - if ((sess == NULL) || (sess->ssl_version != s->version) || + if ((sess == NULL) || + !ssl_version_supported(s, sess->ssl_version) || /* * In the case of EAP-FAST, we can have a pre-shared * "ticket" without a session ID. @@ -1126,12 +1127,22 @@ } } + s->session->ssl_version = s->version; s->session->session_id_length = session_id_len; /* session_id_len could be 0 */ memcpy(s->session->session_id, PACKET_data(&session_id), session_id_len); } + /* Session version and negotiated protocol version should match */ + if (s->version != s->session->ssl_version) { + al = SSL_AD_PROTOCOL_VERSION; + + SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, + SSL_R_SSL_SESSION_VERSION_MISMATCH); + goto f_err; + } + c = ssl_get_cipher_by_char(s, cipherchars); if (c == NULL) { /* unknown cipher */
diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c index a24060e..8fcc232 100644 --- a/ssl/statem/statem_lib.c +++ b/ssl/statem/statem_lib.c
@@ -788,6 +788,44 @@ } /* + * ssl_version_supported - Check that the specified `version` is supported by + * `SSL *` instance + * + * @s: The SSL handle for the candidate method + * @version: Protocol version to test against + * + * Returns 1 when supported, otherwise 0 + */ +int ssl_version_supported(const SSL *s, int version) +{ + const version_info *vent; + const version_info *table; + + switch (s->method->version) { + default: + /* Version should match method version for non-ANY method */ + return version_cmp(s, version, s->version) == 0; + case TLS_ANY_VERSION: + table = tls_version_table; + break; + case DTLS_ANY_VERSION: + table = dtls_version_table; + break; + } + + for (vent = table; + vent->version != 0 && version_cmp(s, version, vent->version) <= 0; + ++vent) { + if (vent->cmeth != NULL && + version_cmp(s, version, vent->version) == 0 && + ssl_method_error(s, vent->cmeth()) == 0) { + return 1; + } + } + return 0; +} + +/* * ssl_check_version_downgrade - In response to RFC7507 SCSV version * fallback indication from a client check whether we're using the highest * supported protocol version. @@ -976,7 +1014,6 @@ * versions they don't want. If not, then easy to fix, just return * ssl_method_error(s, s->method) */ - s->session->ssl_version = s->version; return 0; case TLS_ANY_VERSION: table = tls_version_table; @@ -999,7 +1036,7 @@ if (err != 0) return err; s->method = method; - s->session->ssl_version = s->version = version; + s->version = version; return 0; }