Skip to content
Permalink
Browse files
Don't change the state of the ETM flags until CCS processing
Changing the ciphersuite during a renegotiation can result in a crash
leading to a DoS attack. ETM has not been implemented in 1.1.0 for DTLS
so this is TLS only.

The problem is caused by changing the flag indicating whether to use ETM
or not immediately on negotiation of ETM, rather than at CCS. Therefore,
during a renegotiation, if the ETM state is changing (usually due to a
change of ciphersuite), then an error/crash will occur.

Due to the fact that there are separate CCS messages for read and write
we actually now need two flags to determine whether to use ETM or not.

CVE-2017-3733

Reviewed-by: Richard Levitte <levitte@openssl.org>
  • Loading branch information
mattcaswell committed Feb 16, 2017
1 parent 9c5a691 commit 4ad9361
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 19 deletions.
@@ -264,11 +264,14 @@ extern "C" {
# define TLS1_FLAGS_SKIP_CERT_VERIFY 0x0010

/* Set if we encrypt then mac instead of usual mac then encrypt */
# define TLS1_FLAGS_ENCRYPT_THEN_MAC 0x0100
# define TLS1_FLAGS_ENCRYPT_THEN_MAC_READ 0x0100
# define TLS1_FLAGS_ENCRYPT_THEN_MAC TLS1_FLAGS_ENCRYPT_THEN_MAC_READ

/* Set if extended master secret extension received from peer */
# define TLS1_FLAGS_RECEIVED_EXTMS 0x0200

# define TLS1_FLAGS_ENCRYPT_THEN_MAC_WRITE 0x0400

# define SSL3_MT_HELLO_REQUEST 0
# define SSL3_MT_CLIENT_HELLO 1
# define SSL3_MT_SERVER_HELLO 2
@@ -395,7 +395,7 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, int len)
if (type == SSL3_RT_APPLICATION_DATA &&
u_len >= 4 * (max_send_fragment = s->max_send_fragment) &&
s->compress == NULL && s->msg_callback == NULL &&
!SSL_USE_ETM(s) && SSL_USE_EXPLICIT_IV(s) &&
!SSL_WRITE_ETM(s) && SSL_USE_EXPLICIT_IV(s) &&
EVP_CIPHER_flags(EVP_CIPHER_CTX_cipher(s->enc_write_ctx)) &
EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK) {
unsigned char aad[13];
@@ -791,7 +791,7 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
* wb->buf
*/

if (!SSL_USE_ETM(s) && mac_size != 0) {
if (!SSL_WRITE_ETM(s) && mac_size != 0) {
if (s->method->ssl3_enc->mac(s, &wr[j],
&(outbuf[j][wr[j].length + eivlen]),
1) < 0)
@@ -814,7 +814,7 @@ int do_ssl3_write(SSL *s, int type, const unsigned char *buf,
goto err;

for (j = 0; j < numpipes; j++) {
if (SSL_USE_ETM(s) && mac_size != 0) {
if (SSL_WRITE_ETM(s) && mac_size != 0) {
if (s->method->ssl3_enc->mac(s, &wr[j],
outbuf[j] + wr[j].length, 1) < 0)
goto err;
@@ -346,7 +346,7 @@ int ssl3_get_record(SSL *s)
* If in encrypt-then-mac mode calculate mac from encrypted record. All
* the details below are public so no timing details can leak.
*/
if (SSL_USE_ETM(s) && s->read_hash) {
if (SSL_READ_ETM(s) && s->read_hash) {
unsigned char *mac;
mac_size = EVP_MD_CTX_size(s->read_hash);
OPENSSL_assert(mac_size <= EVP_MAX_MD_SIZE);
@@ -393,7 +393,7 @@ int ssl3_get_record(SSL *s)
/* r->length is now the compressed data plus mac */
if ((sess != NULL) &&
(s->enc_read_ctx != NULL) &&
(EVP_MD_CTX_md(s->read_hash) != NULL) && !SSL_USE_ETM(s)) {
(!SSL_READ_ETM(s) && EVP_MD_CTX_md(s->read_hash) != NULL)) {
/* s->read_hash != NULL => mac_size != -1 */
unsigned char *mac = NULL;
unsigned char mac_tmp[EVP_MAX_MD_SIZE];
@@ -823,7 +823,7 @@ int tls1_enc(SSL *s, SSL3_RECORD *recs, unsigned int n_recs, int send)
}

ret = 1;
if (!SSL_USE_ETM(s) && EVP_MD_CTX_md(s->read_hash) != NULL)
if (!SSL_READ_ETM(s) && EVP_MD_CTX_md(s->read_hash) != NULL)
mac_size = EVP_MD_CTX_size(s->read_hash);
if ((bs != 1) && !send) {
int tmpret;
@@ -997,7 +997,7 @@ int tls1_mac(SSL *ssl, SSL3_RECORD *rec, unsigned char *md, int send)
header[11] = (rec->length) >> 8;
header[12] = (rec->length) & 0xff;

if (!send && !SSL_USE_ETM(ssl) &&
if (!send && !SSL_READ_ETM(ssl) &&
EVP_CIPHER_CTX_mode(ssl->enc_read_ctx) == EVP_CIPH_CBC_MODE &&
ssl3_cbc_record_digest_supported(mac_ctx)) {
/*
@@ -1022,7 +1022,7 @@ int tls1_mac(SSL *ssl, SSL3_RECORD *rec, unsigned char *md, int send)
EVP_MD_CTX_free(hmac);
return -1;
}
if (!send && !SSL_USE_ETM(ssl) && FIPS_mode())
if (!send && !SSL_READ_ETM(ssl) && FIPS_mode())
if (!tls_fips_digest_extra(ssl->enc_read_ctx,
mac_ctx, rec->input,
rec->length, rec->orig_len)) {
@@ -378,7 +378,8 @@
# define SSL_CLIENT_USE_SIGALGS(s) \
SSL_CLIENT_USE_TLS1_2_CIPHERS(s)

# define SSL_USE_ETM(s) (s->s3->flags & TLS1_FLAGS_ENCRYPT_THEN_MAC)
# define SSL_READ_ETM(s) (s->s3->flags & TLS1_FLAGS_ENCRYPT_THEN_MAC_READ)
# define SSL_WRITE_ETM(s) (s->s3->flags & TLS1_FLAGS_ENCRYPT_THEN_MAC_WRITE)

/* Mostly for SSLv3 */
# define SSL_PKEY_RSA_ENC 0
@@ -1110,6 +1111,10 @@ struct ssl_st {
*/
unsigned char *alpn_client_proto_list;
unsigned alpn_client_proto_list_len;

/* Set to one if we have negotiated ETM */
int tlsext_use_etm;

/*-
* 1 if we are renegotiating.
* 2 if we are a server and are inside a handshake
@@ -130,6 +130,11 @@ int tls1_change_cipher_state(SSL *s, int which)
#endif

if (which & SSL3_CC_READ) {
if (s->tlsext_use_etm)
s->s3->flags |= TLS1_FLAGS_ENCRYPT_THEN_MAC_READ;
else
s->s3->flags &= ~TLS1_FLAGS_ENCRYPT_THEN_MAC_READ;

if (s->s3->tmp.new_cipher->algorithm2 & TLS1_STREAM_MAC)
s->mac_flags |= SSL_MAC_FLAG_READ_MAC_STREAM;
else
@@ -168,6 +173,11 @@ int tls1_change_cipher_state(SSL *s, int which)
mac_secret = &(s->s3->read_mac_secret[0]);
mac_secret_size = &(s->s3->read_mac_secret_size);
} else {
if (s->tlsext_use_etm)
s->s3->flags |= TLS1_FLAGS_ENCRYPT_THEN_MAC_WRITE;
else
s->s3->flags &= ~TLS1_FLAGS_ENCRYPT_THEN_MAC_WRITE;

if (s->s3->tmp.new_cipher->algorithm2 & TLS1_STREAM_MAC)
s->mac_flags |= SSL_MAC_FLAG_WRITE_MAC_STREAM;
else
@@ -367,9 +377,8 @@ int tls1_setup_key_block(SSL *s)
if (s->s3->tmp.key_block_length != 0)
return (1);

if (!ssl_cipher_get_evp
(s->session, &c, &hash, &mac_type, &mac_secret_size, &comp,
SSL_USE_ETM(s))) {
if (!ssl_cipher_get_evp(s->session, &c, &hash, &mac_type, &mac_secret_size,
&comp, s->tlsext_use_etm)) {
SSLerr(SSL_F_TLS1_SETUP_KEY_BLOCK, SSL_R_CIPHER_OR_HASH_UNAVAILABLE);
return (0);
}
@@ -1674,7 +1674,7 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *buf,
#endif
if (!custom_ext_add(s, 1, &ret, limit, al))
return NULL;
if (s->s3->flags & TLS1_FLAGS_ENCRYPT_THEN_MAC) {
if (s->tlsext_use_etm) {
/*
* Don't use encrypt_then_mac if AEAD or RC4 might want to disable
* for other cases too.
@@ -1683,7 +1683,7 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *buf,
|| s->s3->tmp.new_cipher->algorithm_enc == SSL_RC4
|| s->s3->tmp.new_cipher->algorithm_enc == SSL_eGOST2814789CNT
|| s->s3->tmp.new_cipher->algorithm_enc == SSL_eGOST2814789CNT12)
s->s3->flags &= ~TLS1_FLAGS_ENCRYPT_THEN_MAC;
s->tlsext_use_etm = 0;
else {
/*-
* check for enough space.
@@ -1916,7 +1916,7 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET *pkt, int *al)
/* Clear any signature algorithms extension received */
OPENSSL_free(s->s3->tmp.peer_sigalgs);
s->s3->tmp.peer_sigalgs = NULL;
s->s3->flags &= ~TLS1_FLAGS_ENCRYPT_THEN_MAC;
s->tlsext_use_etm = 0;

#ifndef OPENSSL_NO_SRP
OPENSSL_free(s->srp_ctx.login);
@@ -2264,7 +2264,7 @@ static int ssl_scan_clienthello_tlsext(SSL *s, PACKET *pkt, int *al)
}
#endif
else if (type == TLSEXT_TYPE_encrypt_then_mac)
s->s3->flags |= TLS1_FLAGS_ENCRYPT_THEN_MAC;
s->tlsext_use_etm = 1;
/*
* Note: extended master secret extension handled in
* tls_check_serverhello_tlsext_early()
@@ -2366,7 +2366,7 @@ static int ssl_scan_serverhello_tlsext(SSL *s, PACKET *pkt, int *al)
SSL_DTLSEXT_HB_DONT_SEND_REQUESTS);
#endif

s->s3->flags &= ~TLS1_FLAGS_ENCRYPT_THEN_MAC;
s->tlsext_use_etm = 0;

s->s3->flags &= ~TLS1_FLAGS_RECEIVED_EXTMS;

@@ -2585,7 +2585,7 @@ static int ssl_scan_serverhello_tlsext(SSL *s, PACKET *pkt, int *al)
/* Ignore if inappropriate ciphersuite */
if (s->s3->tmp.new_cipher->algorithm_mac != SSL_AEAD
&& s->s3->tmp.new_cipher->algorithm_enc != SSL_RC4)
s->s3->flags |= TLS1_FLAGS_ENCRYPT_THEN_MAC;
s->tlsext_use_etm = 1;
} else if (type == TLSEXT_TYPE_extended_master_secret) {
s->s3->flags |= TLS1_FLAGS_RECEIVED_EXTMS;
if (!s->hit)

0 comments on commit 4ad9361

Please sign in to comment.