Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TLSv1.3: Downgrade Protection mechanism #3022

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions Configure
Expand Up @@ -407,6 +407,7 @@ my @disablables = (
"tests",
"threads",
"tls",
"tls13downgrade",
"ts",
"ubsan",
"ui",
Expand Down Expand Up @@ -451,6 +452,7 @@ our %disabled = ( # "what" => "comment"
"ubsan" => "default",
#TODO(TLS1.3): Temporarily disabled while this is a WIP
"tls1_3" => "default",
"tls13downgrade" => "default",
"unit-test" => "default",
"weak-ssl-ciphers" => "default",
"zlib" => "default",
Expand Down
10 changes: 10 additions & 0 deletions INSTALL
Expand Up @@ -427,6 +427,16 @@
require additional system-dependent options! See "Note on
multi-threading" below.

enable-tls13downgrade
TODO(TLS1.3): Make this enabled by default and remove the
option when TLSv1.3 is out of draft
TLSv1.3 offers a downgrade protection mechanism. This is
implemented but disabled by default. It should not typically
be enabled except for testing purposes. Otherwise this could
cause problems if a pre-RFC version of OpenSSL talks to an
RFC implementation (it will erroneously be detected as a
downgrade).

no-ts
Don't build Time Stamping Authority support.

Expand Down
33 changes: 28 additions & 5 deletions ssl/s3_lib.c
Expand Up @@ -48,6 +48,7 @@
*/

#include <stdio.h>
#include <assert.h>
#include <openssl/objects.h>
#include "ssl_locl.h"
#include <openssl/md5.h>
Expand All @@ -57,6 +58,14 @@
#define SSL3_NUM_CIPHERS OSSL_NELEM(ssl3_ciphers)
#define SSL3_NUM_SCSVS OSSL_NELEM(ssl3_scsvs)

/* TLSv1.3 downgrade protection sentinel values */
const unsigned char tls11downgrade[] = {
0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x00
};
const unsigned char tls12downgrade[] = {
0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x01
};

/*
* The list of available ciphers, mostly organized into the following
* groups:
Expand Down Expand Up @@ -4007,9 +4016,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;
Expand All @@ -4022,9 +4032,22 @@ 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) {
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,
Expand Down
17 changes: 14 additions & 3 deletions ssl/ssl_locl.h
Expand Up @@ -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
Expand Down Expand Up @@ -1859,6 +1865,9 @@ typedef enum tlsext_index_en {
/* A dummy signature value not valid for TLSv1.2 signature algs */
#define TLSEXT_signature_rsa_pss 0x0101

/* TLSv1.3 downgrade protection sentinel values */
extern const unsigned char tls11downgrade[8];
extern const unsigned char tls12downgrade[8];

extern SSL3_ENC_METHOD ssl3_undef_enc_method;

Expand Down Expand Up @@ -2101,7 +2110,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);
Expand Down Expand Up @@ -2167,8 +2176,10 @@ __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_client_version(SSL *s, int version);
__owur int ssl_choose_server_version(SSL *s, CLIENTHELLO_MSG *hello,
DOWNGRADE *dgrd);
__owur int ssl_choose_client_version(SSL *s, int version, int checkdgrd,
int *al);
int ssl_get_client_min_max_version(const SSL *s, int *min_version,
int *max_version);

Expand Down
30 changes: 16 additions & 14 deletions ssl/statem/statem_clnt.c
Expand Up @@ -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;

/*-
Expand Down Expand Up @@ -1316,10 +1317,20 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
goto f_err;
}

/* We do this immediately so we know what format the ServerHello is in */
protverr = ssl_choose_client_version(s, sversion);
/* load the server random */
if (!PACKET_copy_bytes(pkt, s->s3->server_random, SSL3_RANDOM_SIZE)) {
al = SSL_AD_DECODE_ERROR;
SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_LENGTH_MISMATCH);
goto f_err;
}

/*
* We do this immediately so we know what format the ServerHello is in.
* Must be done after reading the random data so we can check for the
* TLSv1.3 downgrade sentinels
*/
protverr = ssl_choose_client_version(s, sversion, 1, &al);
if (protverr != 0) {
al = SSL_AD_PROTOCOL_VERSION;
SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, protverr);
goto f_err;
}
Expand All @@ -1334,14 +1345,6 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
goto f_err;
}

/* load the server hello data */
/* load the server random */
if (!PACKET_copy_bytes(pkt, s->s3->server_random, SSL3_RANDOM_SIZE)) {
al = SSL_AD_DECODE_ERROR;
SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, SSL_R_LENGTH_MISMATCH);
goto f_err;
}

/* Get the session-id. */
if (!SSL_IS_TLS13(s)) {
if (!PACKET_get_length_prefixed_1(pkt, &session_id)) {
Expand Down Expand Up @@ -1608,9 +1611,8 @@ static MSG_PROCESS_RETURN tls_process_hello_retry_request(SSL *s, PACKET *pkt)
s->hello_retry_request = 1;

/* This will fail if it doesn't choose TLSv1.3+ */
errorcode = ssl_choose_client_version(s, sversion);
errorcode = ssl_choose_client_version(s, sversion, 0, &al);
if (errorcode != 0) {
al = SSL_AD_PROTOCOL_VERSION;
SSLerr(SSL_F_TLS_PROCESS_HELLO_RETRY_REQUEST, errorcode);
goto f_err;
}
Expand Down
86 changes: 76 additions & 10 deletions ssl/statem/statem_lib.c
Expand Up @@ -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},
Expand Down Expand Up @@ -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},
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -1662,22 +1681,32 @@ int ssl_choose_server_version(SSL *s, CLIENTHELLO_MSG *hello)
*
* @s: client SSL handle.
* @version: The proposed version from the server's HELLO.
* @checkdgrd: Whether to check the downgrade sentinels in the server_random
* @al: Where to store any alert value that may be generated
*
* Returns 0 on success or an SSL error reason number on failure.
*/
int ssl_choose_client_version(SSL *s, int version)
int ssl_choose_client_version(SSL *s, int version, int checkdgrd, int *al)
{
const version_info *vent;
const version_info *table;
int highver = 0;

/* TODO(TLS1.3): Remove this before release */
if (version == TLS1_3_VERSION_DRAFT)
version = TLS1_3_VERSION;

if (s->hello_retry_request && version != TLS1_3_VERSION) {
*al = SSL_AD_PROTOCOL_VERSION;
return SSL_R_WRONG_SSL_VERSION;
}

switch (s->method->version) {
default:
if (version != s->version)
if (version != s->version) {
*al = SSL_AD_PROTOCOL_VERSION;
return SSL_R_WRONG_SSL_VERSION;
}
/*
* 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
Expand All @@ -1698,22 +1727,59 @@ int ssl_choose_client_version(SSL *s, int version)
const SSL_METHOD *method;
int err;

if (version != vent->version)
continue;
if (vent->cmeth == NULL)
break;
if (s->hello_retry_request && version != TLS1_3_VERSION)
return SSL_R_WRONG_SSL_VERSION;
continue;

if (highver != 0 && version != vent->version)
continue;

method = vent->cmeth();
err = ssl_method_error(s, method);
if (err != 0)
return err;
if (err != 0) {
if (version == vent->version) {
*al = SSL_AD_PROTOCOL_VERSION;
return err;
}

continue;
}
if (highver == 0)
highver = vent->version;

if (version != vent->version)
continue;

#ifndef OPENSSL_NO_TLS13DOWNGRADE
/* Check for downgrades */
if (checkdgrd) {
if (version == TLS1_2_VERSION && highver > version) {
if (memcmp(tls12downgrade,
s->s3->server_random + SSL3_RANDOM_SIZE
- sizeof(tls12downgrade),
sizeof(tls12downgrade)) == 0) {
*al = SSL_AD_ILLEGAL_PARAMETER;
return SSL_R_INAPPROPRIATE_FALLBACK;
}
} else if (!SSL_IS_DTLS(s)
&& version < TLS1_2_VERSION
&& highver > version) {
if (memcmp(tls11downgrade,
s->s3->server_random + SSL3_RANDOM_SIZE
- sizeof(tls11downgrade),
sizeof(tls11downgrade)) == 0) {
*al = SSL_AD_ILLEGAL_PARAMETER;
return SSL_R_INAPPROPRIATE_FALLBACK;
}
}
}
#endif

s->method = method;
s->version = version;
return 0;
}

*al = SSL_AD_PROTOCOL_VERSION;
return SSL_R_UNSUPPORTED_PROTOCOL;
}

Expand Down