Fix race condition in NewSessionTicket

If a NewSessionTicket is received by a multi-threaded client when
attempting to reuse a previous ticket then a race condition can occur
potentially leading to a double free of the ticket data.

CVE-2015-1791

This also fixes RT#3808 where a session ID is changed for a session already
in the client session cache. Since the session ID is the key to the cache
this breaks the cache access.

Parts of this patch were inspired by this Akamai change:
c0bf69a791

Reviewed-by: Rich Salz <rsalz@openssl.org>
This commit is contained in:
Matt Caswell
2015-05-18 16:27:48 +01:00
parent 8c2b1d872b
commit 98ece4eebf
5 changed files with 151 additions and 0 deletions

View File

@@ -225,6 +225,122 @@ SSL_SESSION *SSL_SESSION_new(void)
return (ss);
}
/*
* Create a new SSL_SESSION and duplicate the contents of |src| into it. If
* ticket == 0 then no ticket information is duplicated, otherwise it is.
*/
SSL_SESSION *ssl_session_dup(SSL_SESSION *src, int ticket)
{
SSL_SESSION *dest;
dest = OPENSSL_malloc(sizeof(*src));
if (dest == NULL) {
goto err;
}
memcpy(dest, src, sizeof(*dest));
#ifndef OPENSSL_NO_PSK
if (src->psk_identity_hint) {
dest->psk_identity_hint = BUF_strdup(src->psk_identity_hint);
if (dest->psk_identity_hint == NULL) {
goto err;
}
} else {
dest->psk_identity_hint = NULL;
}
if (src->psk_identity) {
dest->psk_identity = BUF_strdup(src->psk_identity);
if (dest->psk_identity == NULL) {
goto err;
}
} else {
dest->psk_identity = NULL;
}
#endif
if (src->sess_cert != NULL)
CRYPTO_add(&src->sess_cert->references, 1, CRYPTO_LOCK_SSL_SESS_CERT);
if (src->peer != NULL)
CRYPTO_add(&src->peer->references, 1, CRYPTO_LOCK_X509);
dest->references = 1;
if(src->ciphers != NULL) {
dest->ciphers = sk_SSL_CIPHER_dup(src->ciphers);
if (dest->ciphers == NULL)
goto err;
} else {
dest->ciphers = NULL;
}
if (!CRYPTO_dup_ex_data(CRYPTO_EX_INDEX_SSL_SESSION,
&dest->ex_data, &src->ex_data)) {
goto err;
}
/* We deliberately don't copy the prev and next pointers */
dest->prev = NULL;
dest->next = NULL;
#ifndef OPENSSL_NO_TLSEXT
if (src->tlsext_hostname) {
dest->tlsext_hostname = BUF_strdup(src->tlsext_hostname);
if (dest->tlsext_hostname == NULL) {
goto err;
}
} else {
dest->tlsext_hostname = NULL;
}
# ifndef OPENSSL_NO_EC
if (src->tlsext_ecpointformatlist) {
dest->tlsext_ecpointformatlist =
BUF_memdup(src->tlsext_ecpointformatlist,
src->tlsext_ecpointformatlist_length);
if (dest->tlsext_ecpointformatlist == NULL)
goto err;
dest->tlsext_ecpointformatlist_length =
src->tlsext_ecpointformatlist_length;
}
if (src->tlsext_ellipticcurvelist) {
dest->tlsext_ellipticcurvelist =
BUF_memdup(src->tlsext_ellipticcurvelist,
src->tlsext_ellipticcurvelist_length);
if (dest->tlsext_ellipticcurvelist == NULL)
goto err;
dest->tlsext_ellipticcurvelist_length =
src->tlsext_ellipticcurvelist_length;
}
# endif
#endif
if (ticket != 0) {
dest->tlsext_tick_lifetime_hint = src->tlsext_tick_lifetime_hint;
dest->tlsext_ticklen = src->tlsext_ticklen;
if((dest->tlsext_tick = OPENSSL_malloc(src->tlsext_ticklen)) == NULL) {
goto err;
}
}
#ifndef OPENSSL_NO_SRP
dest->srp_username = NULL;
if (src->srp_username) {
dest->srp_username = BUF_strdup(src->srp_username);
if (dest->srp_username == NULL) {
goto err;
}
} else {
dest->srp_username = NULL;
}
#endif
return dest;
err:
SSLerr(SSL_F_SSL_SESSION_DUP, ERR_R_MALLOC_FAILURE);
SSL_SESSION_free(dest);
return NULL;
}
const unsigned char *SSL_SESSION_get_id(const SSL_SESSION *s,
unsigned int *len)
{