Factor out remaining version-related functions.
Now SSLv23 and DTLS_ANY_VERSION share version-related helper functions.
ssl3_get_method is temporary until the method switch is no longer necessary.
Put them all together so there's one place to refactor them when we add a new
version or implement min_version/max_version controls.
Change-Id: Ic28a145cad22db08a87fdb854480b22886c451c6
Reviewed-on: https://boringssl-review.googlesource.com/2580
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/s23_clnt.c b/ssl/s23_clnt.c
index b0d6b69..ca46d8d 100644
--- a/ssl/s23_clnt.c
+++ b/ssl/s23_clnt.c
@@ -447,37 +447,15 @@
((p[0] == SSL3_RT_HANDSHAKE && p[5] == SSL3_MT_SERVER_HELLO) ||
(p[0] == SSL3_RT_ALERT && p[3] == 0 && p[4] == 2)))
{
- /* we have sslv3 or tls1 (server hello or alert) */
-
- if ((p[2] == SSL3_VERSION_MINOR) &&
- !(s->options & SSL_OP_NO_SSLv3))
- {
- s->version=SSL3_VERSION;
- s->method=SSLv3_client_method();
- }
- else if ((p[2] == TLS1_VERSION_MINOR) &&
- !(s->options & SSL_OP_NO_TLSv1))
- {
- s->version=TLS1_VERSION;
- s->method=TLSv1_client_method();
- }
- else if ((p[2] == TLS1_1_VERSION_MINOR) &&
- !(s->options & SSL_OP_NO_TLSv1_1))
- {
- s->version=TLS1_1_VERSION;
- s->method=TLSv1_1_client_method();
- }
- else if ((p[2] == TLS1_2_VERSION_MINOR) &&
- !(s->options & SSL_OP_NO_TLSv1_2))
- {
- s->version=TLS1_2_VERSION;
- s->method=TLSv1_2_client_method();
- }
- else
+ uint16_t version = (p[1] << 8) | p[2];
+ if (!ssl3_is_version_enabled(s, version))
{
OPENSSL_PUT_ERROR(SSL, ssl23_get_server_hello, SSL_R_UNSUPPORTED_PROTOCOL);
goto err;
}
+ s->version = version;
+ s->method = ssl3_get_method(version);
+ assert(s->method != NULL);
if (p[0] == SSL3_RT_ALERT && p[5] != SSL3_AL_WARNING)
{
diff --git a/ssl/s23_srvr.c b/ssl/s23_srvr.c
index 06916ed..08d063a 100644
--- a/ssl/s23_srvr.c
+++ b/ssl/s23_srvr.c
@@ -121,20 +121,6 @@
static int ssl23_get_client_hello(SSL *s);
static int ssl23_get_v2_client_hello(SSL *s);
-static const SSL_METHOD *ssl23_get_server_method(int ver)
- {
- if (ver == SSL3_VERSION)
- return(SSLv3_server_method());
- else if (ver == TLS1_VERSION)
- return(TLSv1_server_method());
- else if (ver == TLS1_1_VERSION)
- return(TLSv1_1_server_method());
- else if (ver == TLS1_2_VERSION)
- return(TLSv1_2_server_method());
- else
- return(NULL);
- }
-
int ssl23_accept(SSL *s)
{
BUF_MEM *buf;
@@ -213,7 +199,7 @@
}
s->state = SSL3_ST_SR_CLNT_HELLO_A;
- s->method = ssl23_get_server_method(s->version);
+ s->method = ssl3_get_method(s->version);
assert(s->method != NULL);
s->handshake_func = s->method->ssl_accept;
s->init_num = 0;
@@ -246,35 +232,6 @@
return(ret);
}
-/* ssl23_get_mutual_version determines the highest supported version for a
- * client which reports a highest version of |client_version|. On success, it
- * returns 1 and sets |*out_version| to the negotiated version. Otherwise, it
- * returns 0. */
-static int ssl23_get_mutual_version(SSL *s, int *out_version, uint16_t client_version)
- {
- if (client_version >= TLS1_2_VERSION && !(s->options & SSL_OP_NO_TLSv1_2))
- {
- *out_version = TLS1_2_VERSION;
- return 1;
- }
- if (client_version >= TLS1_1_VERSION && !(s->options & SSL_OP_NO_TLSv1_1))
- {
- *out_version = TLS1_1_VERSION;
- return 1;
- }
- if (client_version >= TLS1_VERSION && !(s->options & SSL_OP_NO_TLSv1))
- {
- *out_version = TLS1_VERSION;
- return 1;
- }
- if (client_version >= SSL3_VERSION && !(s->options & SSL_OP_NO_SSLv3))
- {
- *out_version = SSL3_VERSION;
- return 1;
- }
- return 0;
- }
-
static int ssl23_get_client_hello(SSL *s)
{
uint8_t *p;
@@ -336,11 +293,13 @@
/* This is a V2ClientHello. Determine the version to
* use. */
uint16_t client_version = (p[3] << 8) | p[4];
- if (!ssl23_get_mutual_version(s, &s->version, client_version))
+ uint16_t version = ssl3_get_mutual_version(s, client_version);
+ if (version == 0)
{
OPENSSL_PUT_ERROR(SSL, ssl23_get_client_hello, SSL_R_UNSUPPORTED_PROTOCOL);
goto err;
}
+ s->version = version;
/* Parse the entire V2ClientHello. */
s->state = SSL23_ST_SR_V2_CLNT_HELLO;
}
@@ -355,7 +314,7 @@
* this, so we simply reject such connections to avoid
* protocol version downgrade attacks. */
uint16_t record_length = (p[3] << 8) | p[4];
- uint16_t client_version;
+ uint16_t client_version, version;
if (record_length < 6)
{
OPENSSL_PUT_ERROR(SSL, ssl23_get_client_hello, SSL_R_RECORD_TOO_SMALL);
@@ -363,11 +322,13 @@
}
client_version = (p[9] << 8) | p[10];
- if (!ssl23_get_mutual_version(s, &s->version, client_version))
+ version = ssl3_get_mutual_version(s, client_version);
+ if (version == 0)
{
OPENSSL_PUT_ERROR(SSL, ssl23_get_client_hello, SSL_R_UNSUPPORTED_PROTOCOL);
goto err;
}
+ s->version = version;
/* Reset the record-layer state for SSL3. */
assert(s->rstate == SSL_ST_READ_HEADER);
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index 19af3cc..c7ae0ee 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -580,49 +580,6 @@
return(ret);
}
-uint16_t ssl3_get_max_client_version(SSL *s)
- {
- unsigned long options = s->options;
- uint16_t version = 0;
-
- /* OpenSSL's API for controlling versions entails blacklisting
- * individual protocols. This has two problems. First, on the client,
- * the protocol can only express a contiguous range of versions. Second,
- * a library consumer trying to set a maximum version cannot disable
- * protocol versions that get added in a future version of the library.
- *
- * To account for both of these, OpenSSL interprets the client-side
- * bitmask as a min/max range by picking the lowest contiguous non-empty
- * range of enabled protocols. Note that this means it is impossible to
- * set a maximum version of TLS 1.2 in a future-proof way.
- *
- * By this scheme, the maximum version is the lowest version V such that
- * V is enabled and V+1 is disabled or unimplemented.
- *
- * TODO(davidben): Deprecate this API in favor of more sensible
- * min_version/max_version settings. */
- if (SSL_IS_DTLS(s))
- {
- if (!(options & SSL_OP_NO_DTLSv1_2))
- version = DTLS1_2_VERSION;
- if (!(options & SSL_OP_NO_DTLSv1) && (options & SSL_OP_NO_DTLSv1_2))
- version = DTLS1_VERSION;
- }
- else
- {
- if (!(options & SSL_OP_NO_TLSv1_2))
- version = TLS1_2_VERSION;
- if (!(options & SSL_OP_NO_TLSv1_1) && (options & SSL_OP_NO_TLSv1_2))
- version = TLS1_1_VERSION;
- if (!(options & SSL_OP_NO_TLSv1) && (options & SSL_OP_NO_TLSv1_1))
- version = TLS1_VERSION;
- if (!(options & SSL_OP_NO_SSLv3) && (options & SSL_OP_NO_TLSv1))
- version = SSL3_VERSION;
- }
-
- return version;
- }
-
int ssl3_send_client_hello(SSL *s)
{
unsigned char *buf;
@@ -816,22 +773,16 @@
if (s->method->version == DTLS_ANY_VERSION)
{
- /* Work out correct protocol version to use */
- int options = s->options;
- if (server_version == DTLS1_2_VERSION
- && !(options & SSL_OP_NO_DTLSv1_2))
- s->method = DTLSv1_2_client_method();
- else if (server_version == DTLS1_VERSION
- && !(options & SSL_OP_NO_DTLSv1))
- s->method = DTLSv1_client_method();
- else
+ if (!ssl3_is_version_enabled(s, server_version))
{
OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, SSL_R_WRONG_SSL_VERSION);
s->version = server_version;
al = SSL_AD_PROTOCOL_VERSION;
goto f_err;
}
- s->version = s->method->version;
+ s->version = server_version;
+ s->method = ssl3_get_method(server_version);
+ assert(s->method != NULL);
}
if (server_version != s->version)
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index 86e8744..8a204ce 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -854,25 +854,17 @@
if (s->method->version == DTLS_ANY_VERSION)
{
/* Select version to use */
- if (s->client_version <= DTLS1_2_VERSION &&
- !(s->options & SSL_OP_NO_DTLSv1_2))
- {
- s->version = DTLS1_2_VERSION;
- s->method = DTLSv1_2_server_method();
- }
- else if (s->client_version <= DTLS1_VERSION &&
- !(s->options & SSL_OP_NO_DTLSv1))
- {
- s->version = DTLS1_VERSION;
- s->method = DTLSv1_server_method();
- }
- else
+ uint16_t version = ssl3_get_mutual_version(s, client_version);
+ if (version == 0)
{
OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_WRONG_VERSION_NUMBER);
s->version = s->client_version;
al = SSL_AD_PROTOCOL_VERSION;
goto f_err;
}
+ s->version = version;
+ s->method = ssl3_get_method(version);
+ assert(s->method != NULL);
}
}
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 61c8dc9..0456cf6 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -1498,7 +1498,9 @@
/* Check for FALLBACK_SCSV. */
if (s->s3 && cipher_suite == (SSL3_CK_FALLBACK_SCSV & 0xffff))
{
- if (s->version < ssl_get_max_version(s))
+ uint16_t max_version = ssl3_get_max_server_version(s);
+ if (SSL_IS_DTLS(s) ? (uint16_t)s->version > max_version
+ : (uint16_t)s->version < max_version)
{
OPENSSL_PUT_ERROR(SSL, ssl_bytes_to_cipher_list, SSL_R_INAPPROPRIATE_FALLBACK);
ssl3_send_alert(s, SSL3_AL_FATAL, SSL3_AD_INAPPROPRIATE_FALLBACK);
@@ -3046,13 +3048,37 @@
return 1;
}
-/* ssl_get_max_version returns the maximum SSL/TLS version number supported by
- * |s|, or zero if all versions are disabled. */
-int ssl_get_max_version(const SSL *s)
+const SSL_METHOD *ssl3_get_method(uint16_t version)
{
- /* Only one version supported for DTLS. */
- if (s->version == DTLS1_VERSION)
- return DTLS1_VERSION;
+ switch (version)
+ {
+ case SSL3_VERSION:
+ return SSLv3_method();
+ case TLS1_VERSION:
+ return TLSv1_method();
+ case TLS1_1_VERSION:
+ return TLSv1_1_method();
+ case TLS1_2_VERSION:
+ return TLSv1_2_method();
+ case DTLS1_VERSION:
+ return DTLSv1_method();
+ case DTLS1_2_VERSION:
+ return DTLSv1_2_method();
+ default:
+ return NULL;
+ }
+ }
+
+uint16_t ssl3_get_max_server_version(const SSL *s)
+ {
+ if (SSL_IS_DTLS(s))
+ {
+ if (!(s->options & SSL_OP_NO_DTLSv1_2))
+ return DTLS1_2_VERSION;
+ if (!(s->options & SSL_OP_NO_DTLSv1))
+ return DTLS1_VERSION;
+ return 0;
+ }
if (!(s->options & SSL_OP_NO_TLSv1_2))
return TLS1_2_VERSION;
@@ -3065,6 +3091,105 @@
return 0;
}
+uint16_t ssl3_get_mutual_version(SSL *s, uint16_t client_version)
+ {
+ if (SSL_IS_DTLS(s))
+ {
+ if (client_version <= DTLS1_2_VERSION && !(s->options & SSL_OP_NO_DTLSv1_2))
+ return DTLS1_2_VERSION;
+ if (client_version <= DTLS1_VERSION && !(s->options & SSL_OP_NO_DTLSv1))
+ return DTLS1_VERSION;
+ return 0;
+ }
+ else
+ {
+ if (client_version >= TLS1_2_VERSION && !(s->options & SSL_OP_NO_TLSv1_2))
+ return TLS1_2_VERSION;
+ if (client_version >= TLS1_1_VERSION && !(s->options & SSL_OP_NO_TLSv1_1))
+ return TLS1_1_VERSION;
+ if (client_version >= TLS1_VERSION && !(s->options & SSL_OP_NO_TLSv1))
+ return TLS1_VERSION;
+ if (client_version >= SSL3_VERSION && !(s->options & SSL_OP_NO_SSLv3))
+ return SSL3_VERSION;
+ return 0;
+ }
+ }
+
+uint16_t ssl3_get_max_client_version(SSL *s)
+ {
+ unsigned long options = s->options;
+ uint16_t version = 0;
+
+ /* OpenSSL's API for controlling versions entails blacklisting
+ * individual protocols. This has two problems. First, on the client,
+ * the protocol can only express a contiguous range of versions. Second,
+ * a library consumer trying to set a maximum version cannot disable
+ * protocol versions that get added in a future version of the library.
+ *
+ * To account for both of these, OpenSSL interprets the client-side
+ * bitmask as a min/max range by picking the lowest contiguous non-empty
+ * range of enabled protocols. Note that this means it is impossible to
+ * set a maximum version of TLS 1.2 in a future-proof way.
+ *
+ * By this scheme, the maximum version is the lowest version V such that
+ * V is enabled and V+1 is disabled or unimplemented.
+ *
+ * TODO(davidben): Deprecate this API in favor of more sensible
+ * min_version/max_version settings. */
+ if (SSL_IS_DTLS(s))
+ {
+ if (!(options & SSL_OP_NO_DTLSv1_2))
+ version = DTLS1_2_VERSION;
+ if (!(options & SSL_OP_NO_DTLSv1) && (options & SSL_OP_NO_DTLSv1_2))
+ version = DTLS1_VERSION;
+ }
+ else
+ {
+ if (!(options & SSL_OP_NO_TLSv1_2))
+ version = TLS1_2_VERSION;
+ if (!(options & SSL_OP_NO_TLSv1_1) && (options & SSL_OP_NO_TLSv1_2))
+ version = TLS1_1_VERSION;
+ if (!(options & SSL_OP_NO_TLSv1) && (options & SSL_OP_NO_TLSv1_1))
+ version = TLS1_VERSION;
+ if (!(options & SSL_OP_NO_SSLv3) && (options & SSL_OP_NO_TLSv1))
+ version = SSL3_VERSION;
+ }
+
+ return version;
+ }
+
+int ssl3_is_version_enabled(SSL *s, uint16_t version)
+ {
+ if (SSL_IS_DTLS(s))
+ {
+ switch (version)
+ {
+ case DTLS1_VERSION:
+ return !(s->options & SSL_OP_NO_DTLSv1);
+ case DTLS1_2_VERSION:
+ return !(s->options & SSL_OP_NO_DTLSv1_2);
+ default:
+ return 0;
+ }
+ }
+ else
+ {
+ switch (version)
+ {
+ case SSL3_VERSION:
+ return !(s->options & SSL_OP_NO_SSLv3);
+ case TLS1_VERSION:
+ return !(s->options & SSL_OP_NO_TLSv1);
+ case TLS1_1_VERSION:
+ return !(s->options & SSL_OP_NO_TLSv1_1);
+ case TLS1_2_VERSION:
+ return !(s->options & SSL_OP_NO_TLSv1_2);
+ default:
+ return 0;
+ }
+ }
+ }
+
/* Allocates new EVP_MD_CTX and sets pointer to it into given pointer
* vairable, freeing EVP_MD_CTX previously stored in that variable, if
* any. If EVP_MD pointer is passed, initializes ctx with this md
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 1c81370..9940eb0 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -929,13 +929,6 @@
void dtls1_hm_fragment_free(hm_fragment *frag);
/* some client-only functions */
-
-/* ssl3_get_max_client_version returns the maximum protocol version configured
- * for the client. It is guaranteed that the set of allowed versions at or below
- * this maximum version is contiguous. If all versions are disabled, it returns
- * zero. */
-uint16_t ssl3_get_max_client_version(SSL *s);
-
int ssl3_send_client_hello(SSL *s);
int ssl3_get_server_hello(SSL *s);
int ssl3_get_certificate_request(SSL *s);
@@ -1078,7 +1071,30 @@
const uint8_t *master, size_t master_len);
int ssl3_can_cutthrough(const SSL *s);
-int ssl_get_max_version(const SSL *s);
+
+/* ssl3_get_method returns the version-locked SSL_METHOD corresponding
+ * to |version|. */
+const SSL_METHOD *ssl3_get_method(uint16_t version);
+
+/* ssl3_get_max_server_version returns the maximum SSL/TLS version number
+ * supported by |s| as a server, or zero if all versions are disabled. */
+uint16_t ssl3_get_max_server_version(const SSL *s);
+
+/* ssl3_get_mutual_version selects the protocol version on |s| for a client
+ * which advertises |client_version|. If no suitable version exists, it returns
+ * zero. */
+uint16_t ssl3_get_mutual_version(SSL *s, uint16_t client_version);
+
+/* ssl3_get_max_client_version returns the maximum protocol version configured
+ * for the client. It is guaranteed that the set of allowed versions at or below
+ * this maximum version is contiguous. If all versions are disabled, it returns
+ * zero. */
+uint16_t ssl3_get_max_client_version(SSL *s);
+
+/* ssl3_is_version_enabled returns one if |version| is an enabled protocol
+ * version for |s| and zero otherwise. */
+int ssl3_is_version_enabled(SSL *s, uint16_t version);
+
EVP_MD_CTX* ssl_replace_hash(EVP_MD_CTX **hash,const EVP_MD *md) ;
void ssl_clear_hash_ctx(EVP_MD_CTX **hash);
int ssl_add_serverhello_renegotiate_ext(SSL *s, unsigned char *p, int *len,