summaryrefslogtreecommitdiffstats
path: root/ssl
diff options
context:
space:
mode:
authorMatt Caswell <matt@openssl.org>2015-05-18 16:27:48 +0100
committerMatt Caswell <matt@openssl.org>2015-06-02 12:44:40 +0100
commit939b4960276b040fc0ed52232238fcc9e2e9ec21 (patch)
treecefe73e09d05363aa05e229b9ee40090fda12f93 /ssl
parentcce3e4adb78a8d3eeb6e0e4efe332fcc5d75f615 (diff)
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: https://github.com/akamai/openssl/commit/c0bf69a791239ceec64509f9f19fcafb2461b0d3 Reviewed-by: Rich Salz <rsalz@openssl.org> (cherry picked from commit 27c76b9b8010b536687318739c6f631ce4194688) Conflicts: ssl/ssl.h ssl/ssl_err.c
Diffstat (limited to 'ssl')
-rw-r--r--ssl/s3_clnt.c32
-rw-r--r--ssl/ssl.h1
-rw-r--r--ssl/ssl_err.c1
-rw-r--r--ssl/ssl_locl.h1
-rw-r--r--ssl/ssl_sess.c123
5 files changed, 158 insertions, 0 deletions
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index 012905b046..eb7b94eac9 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -2139,6 +2139,38 @@ int ssl3_get_new_session_ticket(SSL *s)
}
p = d = (unsigned char *)s->init_msg;
+
+ if (s->session->session_id_length > 0) {
+ int i = s->session_ctx->session_cache_mode;
+ SSL_SESSION *new_sess;
+ /*
+ * We reused an existing session, so we need to replace it with a new
+ * one
+ */
+ if (i & SSL_SESS_CACHE_CLIENT) {
+ /*
+ * Remove the old session from the cache
+ */
+ if (i & SSL_SESS_CACHE_NO_INTERNAL_STORE) {
+ if (s->session_ctx->remove_session_cb != NULL)
+ s->session_ctx->remove_session_cb(s->session_ctx,
+ s->session);
+ } else {
+ /* We carry on if this fails */
+ SSL_CTX_remove_session(s->session_ctx, s->session);
+ }
+ }
+
+ if ((new_sess = ssl_session_dup(s->session, 0)) == 0) {
+ al = SSL_AD_INTERNAL_ERROR;
+ SSLerr(SSL_F_SSL3_GET_NEW_SESSION_TICKET, ERR_R_MALLOC_FAILURE);
+ goto f_err;
+ }
+
+ SSL_SESSION_free(s->session);
+ s->session = new_sess;
+ }
+
n2l(p, s->session->tlsext_tick_lifetime_hint);
n2s(p, ticklen);
/* ticket_lifetime_hint + ticket_length + ticket */
diff --git a/ssl/ssl.h b/ssl/ssl.h
index 32e27c6fbf..d2ab0c074c 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -2410,6 +2410,7 @@ void ERR_load_SSL_strings(void);
# define SSL_F_SSL_READ 223
# define SSL_F_SSL_RSA_PRIVATE_DECRYPT 187
# define SSL_F_SSL_RSA_PUBLIC_ENCRYPT 188
+# define SSL_F_SSL_SESSION_DUP 348
# define SSL_F_SSL_SESSION_NEW 189
# define SSL_F_SSL_SESSION_PRINT_FP 190
# define SSL_F_SSL_SESSION_SET1_ID_CONTEXT 312
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index fef324d462..88621b7250 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -299,6 +299,7 @@ static ERR_STRING_DATA SSL_str_functs[] = {
{ERR_FUNC(SSL_F_SSL_READ), "SSL_read"},
{ERR_FUNC(SSL_F_SSL_RSA_PRIVATE_DECRYPT), "SSL_RSA_PRIVATE_DECRYPT"},
{ERR_FUNC(SSL_F_SSL_RSA_PUBLIC_ENCRYPT), "SSL_RSA_PUBLIC_ENCRYPT"},
+ {ERR_FUNC(SSL_F_SSL_SESSION_DUP), "ssl_session_dup"},
{ERR_FUNC(SSL_F_SSL_SESSION_NEW), "SSL_SESSION_new"},
{ERR_FUNC(SSL_F_SSL_SESSION_PRINT_FP), "SSL_SESSION_print_fp"},
{ERR_FUNC(SSL_F_SSL_SESSION_SET1_ID_CONTEXT),
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index aff3b65d17..a7f3f8dad4 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -865,6 +865,7 @@ int ssl_set_peer_cert_type(SESS_CERT *c, int type);
int ssl_get_new_session(SSL *s, int session);
int ssl_get_prev_session(SSL *s, unsigned char *session, int len,
const unsigned char *limit);
+SSL_SESSION *ssl_session_dup(SSL_SESSION *src, int ticket);
int ssl_cipher_id_cmp(const SSL_CIPHER *a, const SSL_CIPHER *b);
DECLARE_OBJ_BSEARCH_GLOBAL_CMP_FN(SSL_CIPHER, SSL_CIPHER, ssl_cipher_id);
int ssl_cipher_ptr_id_cmp(const SSL_CIPHER *const *ap,
diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c
index eb7936b4e1..e673f9c621 100644
--- a/ssl/ssl_sess.c
+++ b/ssl/ssl_sess.c
@@ -227,6 +227,129 @@ 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_KRB5
+ dest->krb5_client_princ_len = dest->krb5_client_princ_len;
+ if (src->krb5_client_princ_len > 0)
+ memcpy(dest->krb5_client_princ, src->krb5_client_princ,
+ src->krb5_client_princ_len);
+#endif
+
+#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)
{