From f7f2a01d6364f10f353652e29555e6c66aec9b6d Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Wed, 22 Mar 2017 08:52:54 +0000 Subject: [PATCH] Add server side support for TLSv1.3 downgrade mechanism Reviewed-by: Rich Salz (Merged from https://github.com/openssl/openssl/pull/3022) --- ssl/s3_lib.c | 32 +++++++++++++++++++++++++++----- ssl/ssl_locl.h | 11 +++++++++-- ssl/statem/statem_clnt.c | 3 ++- ssl/statem/statem_lib.c | 21 ++++++++++++++++++++- ssl/statem/statem_srvr.c | 7 ++++--- 5 files changed, 62 insertions(+), 12 deletions(-) diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index 1669652644..3feb628809 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -48,6 +48,7 @@ */ #include +#include #include #include "ssl_locl.h" #include @@ -4007,9 +4008,10 @@ long ssl_get_algorithm2(SSL *s) * Fill a ClientRandom or ServerRandom field of length len. Returns <= 0 on * failure, 1 on success. */ -int ssl_fill_hello_random(SSL *s, int server, unsigned char *result, size_t len) +int ssl_fill_hello_random(SSL *s, int server, unsigned char *result, size_t len, + DOWNGRADE dgrd) { - int send_time = 0; + int send_time = 0, ret; if (len < 4) return 0; @@ -4022,9 +4024,29 @@ int ssl_fill_hello_random(SSL *s, int server, unsigned char *result, size_t len) unsigned char *p = result; l2n(Time, p); /* TODO(size_t): Convert this */ - return RAND_bytes(p, (int)(len - 4)); - } else - return RAND_bytes(result, (int)len); + ret = RAND_bytes(p, (int)(len - 4)); + } else { + ret = RAND_bytes(result, (int)len); + } +#ifndef OPENSSL_NO_TLS13DOWNGRADE + if (ret) { + static const unsigned char tls11downgrade[] = { + 0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x00 + }; + static const unsigned char tls12downgrade[] = { + 0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x01 + }; + + assert(sizeof(tls11downgrade) < len && sizeof(tls12downgrade) < len); + if (dgrd == DOWNGRADE_TO_1_2) + memcpy(result + len - sizeof(tls12downgrade), tls12downgrade, + sizeof(tls12downgrade)); + else if (dgrd == DOWNGRADE_TO_1_1) + memcpy(result + len - sizeof(tls11downgrade), tls11downgrade, + sizeof(tls11downgrade)); + } +#endif + return ret; } int ssl_generate_master_secret(SSL *s, unsigned char *pms, size_t pmslen, diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index f6eb03f4d9..98f77ceac9 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -1783,6 +1783,12 @@ typedef struct ssl3_comp_st { } SSL3_COMP; # endif +typedef enum downgrade_en { + DOWNGRADE_NONE, + DOWNGRADE_TO_1_2, + DOWNGRADE_TO_1_1 +} DOWNGRADE; + /* * Extension index values NOTE: Any updates to these defines should be mirrored * with equivalent updates to ext_defs in extensions.c @@ -2101,7 +2107,7 @@ __owur int ssl_verify_alarm_type(long type); void ssl_sort_cipher_list(void); void ssl_load_ciphers(void); __owur int ssl_fill_hello_random(SSL *s, int server, unsigned char *field, - size_t len); + size_t len, DOWNGRADE dgrd); __owur int ssl_generate_master_secret(SSL *s, unsigned char *pms, size_t pmslen, int free_pms); __owur EVP_PKEY *ssl_generate_pkey(EVP_PKEY *pm); @@ -2167,7 +2173,8 @@ __owur int ssl_version_supported(const SSL *s, int version); __owur int ssl_set_client_hello_version(SSL *s); __owur int ssl_check_version_downgrade(SSL *s); __owur int ssl_set_version_bound(int method_version, int version, int *bound); -__owur int ssl_choose_server_version(SSL *s, CLIENTHELLO_MSG *hello); +__owur int ssl_choose_server_version(SSL *s, CLIENTHELLO_MSG *hello, + DOWNGRADE *dgrd); __owur int ssl_choose_client_version(SSL *s, int version); int ssl_get_client_min_max_version(const SSL *s, int *min_version, int *max_version); diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c index d584bd72fe..04908130f7 100644 --- a/ssl/statem/statem_clnt.c +++ b/ssl/statem/statem_clnt.c @@ -1094,7 +1094,8 @@ int tls_construct_client_hello(SSL *s, WPACKET *pkt) } else i = 1; - if (i && ssl_fill_hello_random(s, 0, p, sizeof(s->s3->client_random)) <= 0) + if (i && ssl_fill_hello_random(s, 0, p, sizeof(s->s3->client_random), + DOWNGRADE_NONE) <= 0) return 0; /*- diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c index 849310ea75..41a375104d 100644 --- a/ssl/statem/statem_lib.c +++ b/ssl/statem/statem_lib.c @@ -1295,6 +1295,7 @@ typedef struct { # error Code needs update for TLS_method() support beyond TLS1_3_VERSION. #endif +/* Must be in order high to low */ static const version_info tls_version_table[] = { #ifndef OPENSSL_NO_TLS1_3 {TLS1_3_VERSION, tlsv1_3_client_method, tlsv1_3_server_method}, @@ -1328,6 +1329,7 @@ static const version_info tls_version_table[] = { # error Code needs update for DTLS_method() support beyond DTLS1_2_VERSION. #endif +/* Must be in order high to low */ static const version_info dtls_version_table[] = { #ifndef OPENSSL_NO_DTLS1_2 {DTLS1_2_VERSION, dtlsv1_2_client_method, dtlsv1_2_server_method}, @@ -1510,6 +1512,20 @@ int ssl_set_version_bound(int method_version, int version, int *bound) return 1; } +static void check_for_downgrade(SSL *s, int vers, DOWNGRADE *dgrd) +{ + if (vers == TLS1_2_VERSION + && ssl_version_supported(s, TLS1_3_VERSION)) { + *dgrd = DOWNGRADE_TO_1_2; + } else if (!SSL_IS_DTLS(s) && vers < TLS1_2_VERSION + && (ssl_version_supported(s, TLS1_2_VERSION) + || ssl_version_supported(s, TLS1_3_VERSION))) { + *dgrd = DOWNGRADE_TO_1_1; + } else { + *dgrd = DOWNGRADE_NONE; + } +} + /* * ssl_choose_server_version - Choose server (D)TLS version. Called when the * client HELLO is received to select the final server protocol version and @@ -1519,7 +1535,7 @@ int ssl_set_version_bound(int method_version, int version, int *bound) * * Returns 0 on success or an SSL error reason number on failure. */ -int ssl_choose_server_version(SSL *s, CLIENTHELLO_MSG *hello) +int ssl_choose_server_version(SSL *s, CLIENTHELLO_MSG *hello, DOWNGRADE *dgrd) { /*- * With version-flexible methods we have an initial state with: @@ -1544,6 +1560,7 @@ int ssl_choose_server_version(SSL *s, CLIENTHELLO_MSG *hello) if (!SSL_IS_TLS13(s)) { if (version_cmp(s, client_version, s->version) < 0) return SSL_R_WRONG_SSL_VERSION; + *dgrd = DOWNGRADE_NONE; /* * If this SSL handle is not from a version flexible method we don't * (and never did) check min/max FIPS or Suite B constraints. Hope @@ -1620,6 +1637,7 @@ int ssl_choose_server_version(SSL *s, CLIENTHELLO_MSG *hello) return SSL_R_UNSUPPORTED_PROTOCOL; return 0; } + check_for_downgrade(s, best_vers, dgrd); s->version = best_vers; s->method = best_method; return 0; @@ -1646,6 +1664,7 @@ int ssl_choose_server_version(SSL *s, CLIENTHELLO_MSG *hello) continue; method = vent->smeth(); if (ssl_method_error(s, method) == 0) { + check_for_downgrade(s, vent->version, dgrd); s->version = vent->version; s->method = method; return 0; diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index ffb0685b09..e2c47994b8 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -1476,6 +1476,7 @@ static int tls_early_post_process_client_hello(SSL *s, int *al) STACK_OF(SSL_CIPHER) *ciphers = NULL; STACK_OF(SSL_CIPHER) *scsvs = NULL; CLIENTHELLO_MSG *clienthello = s->clienthello; + DOWNGRADE dgrd = DOWNGRADE_NONE; *al = SSL_AD_INTERNAL_ERROR; /* Finished parsing the ClientHello, now we can start processing it */ @@ -1516,7 +1517,7 @@ static int tls_early_post_process_client_hello(SSL *s, int *al) * versions are potentially compatible. Version negotiation comes later. */ if (!SSL_IS_DTLS(s)) { - protverr = ssl_choose_server_version(s, clienthello); + protverr = ssl_choose_server_version(s, clienthello, &dgrd); } else if (s->method->version != DTLS_ANY_VERSION && DTLS_VERSION_LT((int)clienthello->legacy_version, s->version)) { protverr = SSL_R_VERSION_TOO_LOW; @@ -1565,7 +1566,7 @@ static int tls_early_post_process_client_hello(SSL *s, int *al) s->d1->cookie_verified = 1; } if (s->method->version == DTLS_ANY_VERSION) { - protverr = ssl_choose_server_version(s, clienthello); + protverr = ssl_choose_server_version(s, clienthello, &dgrd); if (protverr != 0) { SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, protverr); s->version = s->client_version; @@ -1722,7 +1723,7 @@ static int tls_early_post_process_client_hello(SSL *s, int *al) { unsigned char *pos; pos = s->s3->server_random; - if (ssl_fill_hello_random(s, 1, pos, SSL3_RANDOM_SIZE) <= 0) { + if (ssl_fill_hello_random(s, 1, pos, SSL3_RANDOM_SIZE, dgrd) <= 0) { goto err; } } -- 2.25.1