Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/boringssl.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteven Valdez <svaldez@google.com>2016-06-27 23:34:59 +0300
committerCQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>2016-07-30 00:22:46 +0300
commit87eab4902d1003983079881d3ee8a66ec2eaccc2 (patch)
treeafaac2e2b48d2bba884059a8572b3cb84a0e630a
parent163f29af076a2fcc5564f7998ab57e3fb9b197b7 (diff)
Splitting SSL session state.
To prevent configuration/established session confusion, the handshake session state is separated into the configured session (ssl->session) and the newly created session (ssl->s3->new_session). Upon conclusion of the handshake, the finalized session is stored in (ssl->s3->established_session). During the handshake, any requests for the session (SSL_get_session) return a non-resumable session, to prevent resumption of a partially filled session. Sessions should only be cached upon the completion of the full handshake, using the resulting established_session. The semantics of accessors on the session are maintained mid-renego. Change-Id: I4358aecb71fce4fe14a6746c5af1416a69935078 Reviewed-on: https://boringssl-review.googlesource.com/8612 Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: David Benjamin <davidben@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
-rw-r--r--include/openssl/ssl.h34
-rw-r--r--ssl/handshake_client.c138
-rw-r--r--ssl/handshake_server.c131
-rw-r--r--ssl/internal.h15
-rw-r--r--ssl/s3_both.c5
-rw-r--r--ssl/s3_enc.c11
-rw-r--r--ssl/s3_lib.c2
-rw-r--r--ssl/ssl_lib.c78
-rw-r--r--ssl/ssl_session.c108
-rw-r--r--ssl/ssl_test.cc44
-rw-r--r--ssl/t1_enc.c23
-rw-r--r--ssl/t1_lib.c47
-rw-r--r--ssl/tls13_both.c22
-rw-r--r--ssl/tls13_client.c4
-rw-r--r--ssl/tls13_enc.c16
-rw-r--r--ssl/tls13_server.c6
16 files changed, 460 insertions, 224 deletions
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 82d503ee..ec8eba21 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -1658,10 +1658,16 @@ OPENSSL_EXPORT int SSL_CTX_get_session_cache_mode(const SSL_CTX *ctx);
* |session|. */
OPENSSL_EXPORT int SSL_set_session(SSL *ssl, SSL_SESSION *session);
-/* SSL_get_session returns a non-owning pointer to |ssl|'s session. Prior to the
- * initial handshake beginning, this is the session to be offered, set by
- * |SSL_set_session|. After a handshake has finished, this is the currently
- * active session. Its behavior is undefined while a handshake is progress. */
+/* SSL_get_session returns a non-owning pointer to |ssl|'s session. For
+ * historical reasons, which session it returns depends on |ssl|'s state.
+ *
+ * Prior to the start of the initial handshake, it returns the session the
+ * caller set with |SSL_set_session|. After the initial handshake has finished
+ * and if no additional handshakes are in progress, it returns the currently
+ * active session. Its behavior is undefined while a handshake is in progress.
+ *
+ * Using this function to add new sessions to an external session cache is
+ * deprecated. Use |SSL_CTX_sess_set_new_cb| instead. */
OPENSSL_EXPORT SSL_SESSION *SSL_get_session(const SSL *ssl);
/* SSL_get0_session is an alias for |SSL_get_session|. */
@@ -2945,6 +2951,7 @@ OPENSSL_EXPORT void SSL_CTX_set_dos_protection_cb(
#define SSL_ST_OK 0x03
#define SSL_ST_RENEGOTIATE (0x04 | SSL_ST_INIT)
#define SSL_ST_TLS13 (0x05 | SSL_ST_INIT)
+#define SSL_ST_ERROR (0x06| SSL_ST_INIT)
/* SSL_CB_* are possible values for the |type| parameter in the info
* callback and the bitmasks that make them up. */
@@ -3681,9 +3688,7 @@ struct ssl_session_st {
/* peer_sha256_valid is non-zero if |peer_sha256| is valid. */
unsigned peer_sha256_valid:1; /* Non-zero if peer_sha256 is valid */
- /* not_resumable is used to indicate that session resumption is not allowed.
- * Applications can also set this bit for a new session via
- * not_resumable_session_cb to disable session caching and tickets. */
+ /* not_resumable is used to indicate that session resumption is disallowed. */
unsigned not_resumable:1;
};
@@ -4069,7 +4074,8 @@ struct ssl_st {
unsigned int sid_ctx_length;
uint8_t sid_ctx[SSL_MAX_SID_CTX_LENGTH];
- /* This can also be in the session once a session is established */
+ /* session is the configured session to be offered by the client. This session
+ * is immutable. */
SSL_SESSION *session;
int (*verify_callback)(int ok,
@@ -4139,9 +4145,6 @@ struct ssl_st {
/* verify_mode is a bitmask of |SSL_VERIFY_*| values. */
uint8_t verify_mode;
- /* hit is true if this connection is resuming a previous session. */
- unsigned hit:1;
-
/* server is true iff the this SSL* is the server half. Note: before the SSL*
* is initialized by either SSL_set_accept_state or SSL_set_connect_state,
* the side is not determined. In this state, server is always false. */
@@ -4397,6 +4400,15 @@ typedef struct ssl3_state_st {
uint32_t server_params_len;
} tmp;
+ /* new_session is the new mutable session being established by the current
+ * handshake. It should not be cached. */
+ SSL_SESSION *new_session;
+
+ /* established_session is the session established by the connection. This
+ * session is only filled upon the completion of the handshake and is
+ * immutable. */
+ SSL_SESSION *established_session;
+
/* Connection binding to prevent renegotiation attacks */
uint8_t previous_client_finished[EVP_MAX_MD_SIZE];
uint8_t previous_client_finished_len;
diff --git a/ssl/handshake_client.c b/ssl/handshake_client.c
index 5fe706ce..d78bc270 100644
--- a/ssl/handshake_client.c
+++ b/ssl/handshake_client.c
@@ -252,7 +252,7 @@ int ssl3_connect(SSL *ssl) {
goto end;
}
- if (ssl->hit) {
+ if (ssl->session != NULL) {
ssl->state = SSL3_ST_CR_SESSION_TICKET_A;
} else {
ssl->state = SSL3_ST_CR_CERT_A;
@@ -411,7 +411,7 @@ int ssl3_connect(SSL *ssl) {
}
ssl->state = SSL3_ST_CW_FLUSH;
- if (ssl->hit) {
+ if (ssl->session != NULL) {
ssl->s3->tmp.next_state = SSL_ST_OK;
} else {
/* This is a non-resumption handshake. If it involves ChannelID, then
@@ -474,7 +474,7 @@ int ssl3_connect(SSL *ssl) {
}
ssl->method->received_flight(ssl);
- if (ssl->hit) {
+ if (ssl->session != NULL) {
ssl->state = SSL3_ST_CW_CHANGE;
} else {
ssl->state = SSL_ST_OK;
@@ -506,6 +506,29 @@ int ssl3_connect(SSL *ssl) {
ssl3_cleanup_key_block(ssl);
ssl->method->release_current_message(ssl, 1 /* free_buffer */);
+ SSL_SESSION_free(ssl->s3->established_session);
+ if (ssl->session != NULL) {
+ ssl->s3->established_session = SSL_SESSION_up_ref(ssl->session);
+ } else {
+ /* We make a copy of the session in order to maintain the immutability
+ * of the new established_session due to False Start. The caller may
+ * have taken a reference to the temporary session. */
+ ssl->s3->established_session =
+ SSL_SESSION_dup(ssl->s3->new_session, 1 /* include ticket */);
+ if (ssl->s3->established_session == NULL) {
+ /* Do not stay in SSL_ST_OK, to avoid confusing |SSL_in_init|
+ * callers. */
+ ssl->state = SSL_ST_ERROR;
+ skip = 1;
+ ret = -1;
+ goto end;
+ }
+ ssl->s3->established_session->not_resumable = 0;
+
+ SSL_SESSION_free(ssl->s3->new_session);
+ ssl->s3->new_session = NULL;
+ }
+
/* Remove write buffering now. */
ssl_free_wbio_buffer(ssl);
@@ -526,6 +549,11 @@ int ssl3_connect(SSL *ssl) {
ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_DONE, 1);
goto end;
+ case SSL_ST_ERROR:
+ OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE);
+ ret = -1;
+ goto end;
+
default:
OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_STATE);
ret = -1;
@@ -869,17 +897,16 @@ static int ssl3_get_server_hello(SSL *ssl) {
SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT);
goto f_err;
}
- ssl->hit = 1;
} else {
/* The session wasn't resumed. Create a fresh SSL_SESSION to
* fill out. */
- ssl->hit = 0;
+ SSL_set_session(ssl, NULL);
if (!ssl_get_new_session(ssl, 0 /* client */)) {
goto f_err;
}
/* Note: session_id could be empty. */
- ssl->session->session_id_length = CBS_len(&session_id);
- memcpy(ssl->session->session_id, CBS_data(&session_id),
+ ssl->s3->new_session->session_id_length = CBS_len(&session_id);
+ memcpy(ssl->s3->new_session->session_id, CBS_data(&session_id),
CBS_len(&session_id));
}
@@ -908,7 +935,7 @@ static int ssl3_get_server_hello(SSL *ssl) {
goto f_err;
}
- if (ssl->hit) {
+ if (ssl->session != NULL) {
if (ssl->session->cipher != c) {
al = SSL_AD_ILLEGAL_PARAMETER;
OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED);
@@ -920,7 +947,7 @@ static int ssl3_get_server_hello(SSL *ssl) {
goto f_err;
}
} else {
- ssl->session->cipher = c;
+ ssl->s3->new_session->cipher = c;
}
ssl->s3->tmp.new_cipher = c;
@@ -932,7 +959,8 @@ static int ssl3_get_server_hello(SSL *ssl) {
/* If doing a full handshake, the server may request a client certificate
* which requires hashing the handshake transcript. Otherwise, the handshake
* buffer may be released. */
- if (ssl->hit || !ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
+ if (ssl->session != NULL ||
+ !ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
ssl3_free_handshake_buffer(ssl);
}
@@ -957,7 +985,7 @@ static int ssl3_get_server_hello(SSL *ssl) {
goto f_err;
}
- if (ssl->hit &&
+ if (ssl->session != NULL &&
ssl->s3->tmp.extended_master_secret !=
ssl->session->extended_master_secret) {
al = SSL_AD_HANDSHAKE_FAILURE;
@@ -1007,13 +1035,13 @@ static int ssl3_get_server_certificate(SSL *ssl) {
/* NOTE: Unlike the server half, the client's copy of |cert_chain| includes
* the leaf. */
- sk_X509_pop_free(ssl->session->cert_chain, X509_free);
- ssl->session->cert_chain = chain;
+ sk_X509_pop_free(ssl->s3->new_session->cert_chain, X509_free);
+ ssl->s3->new_session->cert_chain = chain;
- X509_free(ssl->session->peer);
- ssl->session->peer = X509_up_ref(leaf);
+ X509_free(ssl->s3->new_session->peer);
+ ssl->s3->new_session->peer = X509_up_ref(leaf);
- ssl->session->verify_result = ssl->verify_result;
+ ssl->s3->new_session->verify_result = ssl->verify_result;
return 1;
@@ -1050,8 +1078,8 @@ static int ssl3_get_cert_status(SSL *ssl) {
goto f_err;
}
- if (!CBS_stow(&ocsp_response, &ssl->session->ocsp_response,
- &ssl->session->ocsp_response_length)) {
+ if (!CBS_stow(&ocsp_response, &ssl->s3->new_session->ocsp_response,
+ &ssl->s3->new_session->ocsp_response_length)) {
al = SSL_AD_INTERNAL_ERROR;
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
goto f_err;
@@ -1064,7 +1092,7 @@ f_err:
}
static int ssl3_verify_server_cert(SSL *ssl) {
- int ret = ssl_verify_cert_chain(ssl, ssl->session->cert_chain);
+ int ret = ssl_verify_cert_chain(ssl, ssl->s3->new_session->cert_chain);
if (ssl->verify_mode != SSL_VERIFY_NONE && ret <= 0) {
int al = ssl_verify_alarm_type(ssl->verify_result);
ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
@@ -1173,11 +1201,11 @@ static int ssl3_get_server_key_exchange(SSL *ssl) {
goto err;
}
- ssl->session->key_exchange_info = DH_num_bits(dh);
- if (ssl->session->key_exchange_info < 1024) {
+ ssl->s3->new_session->key_exchange_info = DH_num_bits(dh);
+ if (ssl->s3->new_session->key_exchange_info < 1024) {
OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_DH_P_LENGTH);
goto err;
- } else if (ssl->session->key_exchange_info > 4096) {
+ } else if (ssl->s3->new_session->key_exchange_info > 4096) {
/* Overly large DHE groups are prohibitively expensive, so enforce a limit
* to prevent a server from causing us to perform too expensive of a
* computation. */
@@ -1210,7 +1238,7 @@ static int ssl3_get_server_key_exchange(SSL *ssl) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
goto f_err;
}
- ssl->session->key_exchange_info = group_id;
+ ssl->s3->new_session->key_exchange_info = group_id;
/* Ensure the group is consistent with preferences. */
if (!tls1_check_group_id(ssl, group_id)) {
@@ -1261,7 +1289,7 @@ static int ssl3_get_server_key_exchange(SSL *ssl) {
/* ServerKeyExchange should be signed by the server's public key. */
if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
- pkey = X509_get_pubkey(ssl->session->peer);
+ pkey = X509_get_pubkey(ssl->s3->new_session->peer);
if (pkey == NULL) {
goto err;
}
@@ -1518,9 +1546,9 @@ static int ssl3_send_client_key_exchange(SSL *ssl) {
}
assert(psk_len <= PSK_MAX_PSK_LEN);
- OPENSSL_free(ssl->session->psk_identity);
- ssl->session->psk_identity = BUF_strdup(identity);
- if (ssl->session->psk_identity == NULL) {
+ OPENSSL_free(ssl->s3->new_session->psk_identity);
+ ssl->s3->new_session->psk_identity = BUF_strdup(identity);
+ if (ssl->s3->new_session->psk_identity == NULL) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
goto err;
}
@@ -1544,7 +1572,7 @@ static int ssl3_send_client_key_exchange(SSL *ssl) {
goto err;
}
- EVP_PKEY *pkey = X509_get_pubkey(ssl->session->peer);
+ EVP_PKEY *pkey = X509_get_pubkey(ssl->s3->new_session->peer);
if (pkey == NULL) {
goto err;
}
@@ -1654,12 +1682,14 @@ static int ssl3_send_client_key_exchange(SSL *ssl) {
}
ssl->state = SSL3_ST_CW_KEY_EXCH_B;
- ssl->session->master_key_length =
- tls1_generate_master_secret(ssl, ssl->session->master_key, pms, pms_len);
- if (ssl->session->master_key_length == 0) {
+ ssl->s3->new_session->master_key_length =
+ tls1_generate_master_secret(ssl, ssl->s3->new_session->master_key, pms,
+ pms_len);
+ if (ssl->s3->new_session->master_key_length == 0) {
goto err;
}
- ssl->session->extended_master_secret = ssl->s3->tmp.extended_master_secret;
+ ssl->s3->new_session->extended_master_secret =
+ ssl->s3->tmp.extended_master_secret;
OPENSSL_cleanse(pms, pms_len);
OPENSSL_free(pms);
@@ -1879,7 +1909,6 @@ err:
}
static int ssl3_get_new_session_ticket(SSL *ssl) {
- int al;
int ret = ssl->method->ssl_get_message(ssl, SSL3_MT_NEW_SESSION_TICKET,
ssl_hash_message);
if (ret <= 0) {
@@ -1892,9 +1921,9 @@ static int ssl3_get_new_session_ticket(SSL *ssl) {
if (!CBS_get_u32(&new_session_ticket, &ticket_lifetime_hint) ||
!CBS_get_u16_length_prefixed(&new_session_ticket, &ticket) ||
CBS_len(&new_session_ticket) != 0) {
- al = SSL_AD_DECODE_ERROR;
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
- goto f_err;
+ return -1;
}
if (CBS_len(&ticket) == 0) {
@@ -1906,46 +1935,47 @@ static int ssl3_get_new_session_ticket(SSL *ssl) {
return 1;
}
- if (ssl->hit) {
+ int session_renewed = ssl->session != NULL;
+ SSL_SESSION *session = ssl->s3->new_session;
+ if (session_renewed) {
/* The server is sending a new ticket for an existing session. Sessions are
* immutable once established, so duplicate all but the ticket of the
* existing session. */
- uint8_t *bytes;
- size_t bytes_len;
- if (!SSL_SESSION_to_bytes_for_ticket(ssl->session, &bytes, &bytes_len)) {
- goto err;
- }
- SSL_SESSION *new_session = SSL_SESSION_from_bytes(bytes, bytes_len);
- OPENSSL_free(bytes);
- if (new_session == NULL) {
+ session = SSL_SESSION_dup(ssl->session,
+ 0 /* Don't duplicate session ticket */);
+ if (session == NULL) {
/* This should never happen. */
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
goto err;
}
-
- SSL_SESSION_free(ssl->session);
- ssl->session = new_session;
}
- if (!CBS_stow(&ticket, &ssl->session->tlsext_tick,
- &ssl->session->tlsext_ticklen)) {
+ if (!CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
goto err;
}
- ssl->session->tlsext_tick_lifetime_hint = ticket_lifetime_hint;
+ session->tlsext_tick_lifetime_hint = ticket_lifetime_hint;
/* Generate a session ID for this session based on the session ticket. We use
* the session ID mechanism for detecting ticket resumption. This also fits in
* with assumptions elsewhere in OpenSSL.*/
- if (!EVP_Digest(CBS_data(&ticket), CBS_len(&ticket), ssl->session->session_id,
- &ssl->session->session_id_length, EVP_sha256(), NULL)) {
+ if (!EVP_Digest(CBS_data(&ticket), CBS_len(&ticket),
+ session->session_id, &session->session_id_length,
+ EVP_sha256(), NULL)) {
goto err;
}
+ if (session_renewed) {
+ session->not_resumable = 0;
+ SSL_SESSION_free(ssl->session);
+ ssl->session = session;
+ }
+
return 1;
-f_err:
- ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
err:
+ if (session_renewed) {
+ SSL_SESSION_free(session);
+ }
return -1;
}
diff --git a/ssl/handshake_server.c b/ssl/handshake_server.c
index 9f4aae3c..caa26816 100644
--- a/ssl/handshake_server.c
+++ b/ssl/handshake_server.c
@@ -243,7 +243,7 @@ int ssl3_accept(SSL *ssl) {
if (ret <= 0) {
goto end;
}
- if (ssl->hit) {
+ if (ssl->session != NULL) {
ssl->state = SSL3_ST_SW_SESSION_TICKET_A;
} else {
ssl->state = SSL3_ST_SW_CERT_A;
@@ -391,16 +391,16 @@ int ssl3_accept(SSL *ssl) {
}
ssl->method->received_flight(ssl);
- if (ssl->hit) {
+ if (ssl->session != NULL) {
ssl->state = SSL_ST_OK;
} else {
ssl->state = SSL3_ST_SW_SESSION_TICKET_A;
}
- /* If this is a full handshake with ChannelID then record the hashshake
- * hashes in |ssl->session| in case we need them to verify a ChannelID
- * signature on a resumption of this session in the future. */
- if (!ssl->hit && ssl->s3->tlsext_channel_id_valid) {
+ /* If this is a full handshake with ChannelID then record the handshake
+ * hashes in |ssl->s3->new_session| in case we need them to verify a
+ * ChannelID signature on a resumption of this session in the future. */
+ if (ssl->session == NULL && ssl->s3->tlsext_channel_id_valid) {
ret = tls1_record_handshake_hashes_for_channel_id(ssl);
if (ret <= 0) {
goto end;
@@ -442,7 +442,7 @@ int ssl3_accept(SSL *ssl) {
goto end;
}
ssl->state = SSL3_ST_SW_FLUSH;
- if (ssl->hit) {
+ if (ssl->session != NULL) {
ssl->s3->tmp.next_state = SSL3_ST_SR_CHANGE;
} else {
ssl->s3->tmp.next_state = SSL_ST_OK;
@@ -475,21 +475,31 @@ int ssl3_accept(SSL *ssl) {
ssl3_cleanup_key_block(ssl);
ssl->method->release_current_message(ssl, 1 /* free_buffer */);
+ /* If we aren't retaining peer certificates then we can discard it
+ * now. */
+ if (ssl->s3->new_session != NULL &&
+ ssl->ctx->retain_only_sha256_of_client_certs) {
+ X509_free(ssl->s3->new_session->peer);
+ ssl->s3->new_session->peer = NULL;
+ sk_X509_pop_free(ssl->s3->new_session->cert_chain, X509_free);
+ ssl->s3->new_session->cert_chain = NULL;
+ }
+
+ SSL_SESSION_free(ssl->s3->established_session);
+ if (ssl->session != NULL) {
+ ssl->s3->established_session = SSL_SESSION_up_ref(ssl->session);
+ } else {
+ ssl->s3->established_session = ssl->s3->new_session;
+ ssl->s3->established_session->not_resumable = 0;
+ ssl->s3->new_session = NULL;
+ }
+
/* remove buffering on output */
ssl_free_wbio_buffer(ssl);
ssl_handshake_free(ssl->s3->hs);
ssl->s3->hs = NULL;
- /* If we aren't retaining peer certificates then we can discard it
- * now. */
- if (ssl->ctx->retain_only_sha256_of_client_certs) {
- X509_free(ssl->session->peer);
- ssl->session->peer = NULL;
- sk_X509_pop_free(ssl->session->cert_chain, X509_free);
- ssl->session->cert_chain = NULL;
- }
-
ssl->s3->initial_handshake_complete = 1;
ssl_update_cache(ssl, SSL_SESS_CACHE_SERVER);
@@ -653,7 +663,6 @@ static int ssl3_get_client_hello(SSL *ssl) {
}
}
- ssl->hit = 0;
int send_new_ticket = 0;
switch (ssl_get_prev_session(ssl, &session, &send_new_ticket, &early_ctx)) {
case ssl_session_success:
@@ -679,6 +688,7 @@ static int ssl3_get_client_hello(SSL *ssl) {
&ems_data, &ems_len) &&
ems_len == 0;
+ int has_session = 0;
if (session != NULL) {
if (session->extended_master_secret &&
!have_extended_master_secret) {
@@ -689,7 +699,7 @@ static int ssl3_get_client_hello(SSL *ssl) {
goto f_err;
}
- ssl->hit =
+ has_session =
/* Only resume if the session's version matches the negotiated version:
* most clients do not accept a mismatch. */
ssl->version == session->ssl_version &&
@@ -698,21 +708,20 @@ static int ssl3_get_client_hello(SSL *ssl) {
have_extended_master_secret == session->extended_master_secret;
}
- if (ssl->hit) {
- /* Use the new session. */
- SSL_SESSION_free(ssl->session);
+ if (has_session) {
+ /* Use the old session. */
ssl->session = session;
session = NULL;
-
ssl->verify_result = ssl->session->verify_result;
} else {
+ SSL_set_session(ssl, NULL);
if (!ssl_get_new_session(ssl, 1 /* server */)) {
goto err;
}
/* Clear the session ID if we want the session to be single-use. */
if (!(ssl->ctx->session_cache_mode & SSL_SESS_CACHE_SERVER)) {
- ssl->session->session_id_length = 0;
+ ssl->s3->new_session->session_id_length = 0;
}
}
@@ -740,7 +749,7 @@ static int ssl3_get_client_hello(SSL *ssl) {
}
/* If it is a hit, check that the cipher is in the list. */
- if (ssl->hit) {
+ if (ssl->session != NULL) {
size_t j;
int found_cipher = 0;
uint32_t id = ssl->session->cipher->id;
@@ -792,7 +801,7 @@ static int ssl3_get_client_hello(SSL *ssl) {
}
/* Given ciphers and SSL_get_ciphers, we must pick a cipher */
- if (!ssl->hit) {
+ if (ssl->session == NULL) {
/* Let cert callback update server certificates if required */
if (ssl->cert->cert_cb) {
int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
@@ -813,7 +822,7 @@ static int ssl3_get_client_hello(SSL *ssl) {
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER);
goto f_err;
}
- ssl->session->cipher = c;
+ ssl->s3->new_session->cipher = c;
ssl->s3->tmp.new_cipher = c;
/* Determine whether to request a client certificate. */
@@ -843,16 +852,6 @@ static int ssl3_get_client_hello(SSL *ssl) {
ssl3_free_handshake_buffer(ssl);
}
- /* we now have the following setup;
- * client_random
- * cipher_list - our prefered list of ciphers
- * ciphers - the clients prefered list of ciphers
- * compression - basically ignored right now
- * ssl version is set - sslv3
- * ssl->session - The ssl session has been setup.
- * ssl->hit - session reuse flag
- * ssl->tmp.new_cipher - the new cipher to use. */
-
ret = 1;
if (0) {
@@ -883,7 +882,8 @@ static int ssl3_send_server_hello(SSL *ssl) {
/* If this is a resumption and the original handshake didn't support
* ChannelID then we didn't record the original handshake hashes in the
* session and so cannot resume with ChannelIDs. */
- if (ssl->hit && ssl->session->original_handshake_hash_len == 0) {
+ if (ssl->session != NULL &&
+ ssl->session->original_handshake_hash_len == 0) {
ssl->s3->tlsext_channel_id_valid = 0;
}
@@ -911,13 +911,18 @@ static int ssl3_send_server_hello(SSL *ssl) {
memcpy(ssl->s3->server_random + SSL3_RANDOM_SIZE - 8, kDowngradeTLS12, 8);
}
+ const SSL_SESSION *session = ssl->s3->new_session;
+ if (ssl->session != NULL) {
+ session = ssl->session;
+ }
+
CBB cbb, body, session_id;
if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO) ||
!CBB_add_u16(&body, ssl->version) ||
!CBB_add_bytes(&body, ssl->s3->server_random, SSL3_RANDOM_SIZE) ||
!CBB_add_u8_length_prefixed(&body, &session_id) ||
- !CBB_add_bytes(&session_id, ssl->session->session_id,
- ssl->session->session_id_length) ||
+ !CBB_add_bytes(&session_id, session->session_id,
+ session->session_id_length) ||
!CBB_add_u16(&body, ssl_cipher_get_value(ssl->s3->tmp.new_cipher)) ||
!CBB_add_u8(&body, 0 /* no compression */) ||
!ssl_add_serverhello_tlsext(ssl, &body) ||
@@ -1010,7 +1015,7 @@ static int ssl3_send_server_key_exchange(SSL *ssl) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
goto err;
}
- ssl->session->key_exchange_info = DH_num_bits(params);
+ ssl->s3->new_session->key_exchange_info = DH_num_bits(params);
/* Set up DH, generate a key, and emit the public half. */
DH *dh = DHparams_dup(params);
@@ -1035,7 +1040,7 @@ static int ssl3_send_server_key_exchange(SSL *ssl) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
goto err;
}
- ssl->session->key_exchange_info = group_id;
+ ssl->s3->new_session->key_exchange_info = group_id;
/* Set up ECDH, generate a key, and emit the public half. */
if (!SSL_ECDH_CTX_init(&ssl->s3->tmp.ecdh_ctx, group_id) ||
@@ -1291,9 +1296,9 @@ static int ssl3_get_client_certificate(SSL *ssl) {
CBS_init(&certificate_msg, ssl->init_msg, ssl->init_num);
uint8_t alert;
STACK_OF(X509) *chain = ssl_parse_cert_chain(
- ssl, &alert,
- ssl->ctx->retain_only_sha256_of_client_certs ? ssl->session->peer_sha256
- : NULL,
+ ssl, &alert, ssl->ctx->retain_only_sha256_of_client_certs
+ ? ssl->s3->new_session->peer_sha256
+ : NULL,
&certificate_msg);
if (chain == NULL) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
@@ -1325,7 +1330,7 @@ static int ssl3_get_client_certificate(SSL *ssl) {
} else {
/* The hash would have been filled in. */
if (ssl->ctx->retain_only_sha256_of_client_certs) {
- ssl->session->peer_sha256_valid = 1;
+ ssl->s3->new_session->peer_sha256_valid = 1;
}
if (ssl_verify_cert_chain(ssl, chain) <= 0) {
@@ -1336,12 +1341,12 @@ static int ssl3_get_client_certificate(SSL *ssl) {
}
}
- X509_free(ssl->session->peer);
- ssl->session->peer = sk_X509_shift(chain);
- ssl->session->verify_result = ssl->verify_result;
+ X509_free(ssl->s3->new_session->peer);
+ ssl->s3->new_session->peer = sk_X509_shift(chain);
+ ssl->s3->new_session->verify_result = ssl->verify_result;
- sk_X509_pop_free(ssl->session->cert_chain, X509_free);
- ssl->session->cert_chain = chain;
+ sk_X509_pop_free(ssl->s3->new_session->cert_chain, X509_free);
+ ssl->s3->new_session->cert_chain = chain;
/* Inconsistency alert: cert_chain does *not* include the peer's own
* certificate, while we do include it in s3_clnt.c */
@@ -1402,15 +1407,15 @@ static int ssl3_get_client_key_exchange(SSL *ssl) {
goto f_err;
}
- if (!CBS_strdup(&psk_identity, &ssl->session->psk_identity)) {
+ if (!CBS_strdup(&psk_identity, &ssl->s3->new_session->psk_identity)) {
al = SSL_AD_INTERNAL_ERROR;
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
goto f_err;
}
/* Look up the key for the identity. */
- psk_len = ssl->psk_server_callback(ssl, ssl->session->psk_identity, psk,
- sizeof(psk));
+ psk_len = ssl->psk_server_callback(ssl, ssl->s3->new_session->psk_identity,
+ psk, sizeof(psk));
if (psk_len > PSK_MAX_PSK_LEN) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
al = SSL_AD_INTERNAL_ERROR;
@@ -1597,12 +1602,14 @@ static int ssl3_get_client_key_exchange(SSL *ssl) {
}
/* Compute the master secret */
- ssl->session->master_key_length = tls1_generate_master_secret(
- ssl, ssl->session->master_key, premaster_secret, premaster_secret_len);
- if (ssl->session->master_key_length == 0) {
+ ssl->s3->new_session->master_key_length = tls1_generate_master_secret(
+ ssl, ssl->s3->new_session->master_key, premaster_secret,
+ premaster_secret_len);
+ if (ssl->s3->new_session->master_key_length == 0) {
goto err;
}
- ssl->session->extended_master_secret = ssl->s3->tmp.extended_master_secret;
+ ssl->s3->new_session->extended_master_secret =
+ ssl->s3->tmp.extended_master_secret;
OPENSSL_cleanse(premaster_secret, premaster_secret_len);
OPENSSL_free(premaster_secret);
@@ -1623,7 +1630,7 @@ err:
static int ssl3_get_cert_verify(SSL *ssl) {
int al, ret = 0;
CBS certificate_verify, signature;
- X509 *peer = ssl->session->peer;
+ X509 *peer = ssl->s3->new_session->peer;
EVP_PKEY *pkey = NULL;
/* Only RSA and ECDSA client certificates are supported, so a
@@ -1865,8 +1872,9 @@ static int ssl3_send_new_session_ticket(SSL *ssl) {
/* Serialize the SSL_SESSION to be encoded into the ticket. */
uint8_t *session = NULL;
size_t session_len;
- if (!SSL_SESSION_to_bytes_for_ticket(ssl->session, &session,
- &session_len)) {
+ if (!SSL_SESSION_to_bytes_for_ticket(
+ ssl->session != NULL ? ssl->session : ssl->s3->new_session,
+ &session, &session_len)) {
return -1;
}
@@ -1881,7 +1889,8 @@ static int ssl3_send_new_session_ticket(SSL *ssl) {
/* Ticket lifetime hint (advisory only): We leave this unspecified for
* resumed session (for simplicity), and guess that tickets for new
* sessions will live as long as their sessions. */
- !CBB_add_u32(&body, ssl->hit ? 0 : ssl->session->timeout) ||
+ !CBB_add_u32(&body, ssl->session != NULL ? 0 :
+ ssl->s3->new_session->timeout) ||
!CBB_add_u16_length_prefixed(&body, &ticket)) {
goto err;
}
diff --git a/ssl/internal.h b/ssl/internal.h
index 7a4a9586..e3d04634 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -157,6 +157,10 @@ OPENSSL_MSVC_PRAGMA(warning(pop))
#include <sys/time.h>
#endif
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
/* Cipher suites. */
@@ -1216,6 +1220,12 @@ enum ssl_session_result_t ssl_get_prev_session(
SSL *ssl, SSL_SESSION **out_session, int *out_send_ticket,
const struct ssl_early_callback_ctx *ctx);
+/* SSL_SESSION_dup returns a newly-allocated |SSL_SESSION| with a copy of the
+ * fields in |session| or NULL on error. The new session is non-resumable and
+ * must be explicitly marked resumable once it has been filled in. */
+OPENSSL_EXPORT SSL_SESSION *SSL_SESSION_dup(SSL_SESSION *session,
+ int include_ticket);
+
STACK_OF(SSL_CIPHER) *
ssl_bytes_to_cipher_list(SSL *ssl, const CBS *cbs, uint16_t max_version);
void ssl_cipher_preference_list_free(
@@ -1453,4 +1463,9 @@ int tls12_check_peer_sigalg(SSL *ssl, int *out_alert,
uint16_t signature_algorithm);
void ssl_set_client_disabled(SSL *ssl);
+
+#if defined(__cplusplus)
+} /* extern C */
+#endif
+
#endif /* OPENSSL_HEADER_SSL_INTERNAL_H */
diff --git a/ssl/s3_both.c b/ssl/s3_both.c
index 0dd67bc7..cb5d0da0 100644
--- a/ssl/s3_both.c
+++ b/ssl/s3_both.c
@@ -216,8 +216,9 @@ int ssl3_send_finished(SSL *ssl, int a, int b) {
ssl->s3->tmp.finish_md_len = n;
/* Log the master secret, if logging is enabled. */
- if (!ssl_log_secret(ssl, "CLIENT_RANDOM", ssl->session->master_key,
- ssl->session->master_key_length)) {
+ if (!ssl_log_secret(ssl, "CLIENT_RANDOM",
+ SSL_get_session(ssl)->master_key,
+ SSL_get_session(ssl)->master_key_length)) {
return 0;
}
diff --git a/ssl/s3_enc.c b/ssl/s3_enc.c
index a9edcf57..2209e80a 100644
--- a/ssl/s3_enc.c
+++ b/ssl/s3_enc.c
@@ -331,12 +331,16 @@ static int ssl3_handshake_mac(SSL *ssl, int md_nid, const char *sender,
n = EVP_MD_CTX_size(&ctx);
+ SSL_SESSION *session = ssl->session;
+ if (ssl->s3->new_session != NULL) {
+ session = ssl->s3->new_session;
+ }
+
npad = (48 / n) * n;
if (sender != NULL) {
EVP_DigestUpdate(&ctx, sender, sender_len);
}
- EVP_DigestUpdate(&ctx, ssl->session->master_key,
- ssl->session->master_key_length);
+ EVP_DigestUpdate(&ctx, session->master_key, session->master_key_length);
EVP_DigestUpdate(&ctx, kPad1, npad);
EVP_DigestFinal_ex(&ctx, md_buf, &i);
@@ -345,8 +349,7 @@ static int ssl3_handshake_mac(SSL *ssl, int md_nid, const char *sender,
OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP);
return 0;
}
- EVP_DigestUpdate(&ctx, ssl->session->master_key,
- ssl->session->master_key_length);
+ EVP_DigestUpdate(&ctx, session->master_key, session->master_key_length);
EVP_DigestUpdate(&ctx, kPad2, npad);
EVP_DigestUpdate(&ctx, md_buf, i);
EVP_DigestFinal_ex(&ctx, p, &ret);
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index c93722ae..fa29e691 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -211,6 +211,8 @@ void ssl3_free(SSL *ssl) {
OPENSSL_free(ssl->s3->tmp.certificate_types);
OPENSSL_free(ssl->s3->tmp.peer_supported_group_list);
OPENSSL_free(ssl->s3->tmp.peer_psk_identity_hint);
+ SSL_SESSION_free(ssl->s3->new_session);
+ SSL_SESSION_free(ssl->s3->established_session);
ssl3_free_handshake_buffer(ssl);
ssl3_free_handshake_hash(ssl);
ssl_handshake_free(ssl->s3->hs);
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 4da52332..c1619504 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -999,17 +999,25 @@ uint32_t SSL_clear_mode(SSL *ssl, uint32_t mode) {
uint32_t SSL_get_mode(const SSL *ssl) { return ssl->mode; }
X509 *SSL_get_peer_certificate(const SSL *ssl) {
- if (ssl == NULL || ssl->session == NULL || ssl->session->peer == NULL) {
+ if (ssl == NULL) {
+ return NULL;
+ }
+ SSL_SESSION *session = SSL_get_session(ssl);
+ if (session == NULL || session->peer == NULL) {
return NULL;
}
- return X509_up_ref(ssl->session->peer);
+ return X509_up_ref(session->peer);
}
STACK_OF(X509) *SSL_get_peer_cert_chain(const SSL *ssl) {
- if (ssl == NULL || ssl->session == NULL) {
+ if (ssl == NULL) {
return NULL;
}
- return ssl->session->cert_chain;
+ SSL_SESSION *session = SSL_get_session(ssl);
+ if (session == NULL) {
+ return NULL;
+ }
+ return session->cert_chain;
}
int SSL_get_tls_unique(const SSL *ssl, uint8_t *out, size_t *out_len,
@@ -1019,7 +1027,7 @@ int SSL_get_tls_unique(const SSL *ssl, uint8_t *out, size_t *out_len,
* https://tools.ietf.org/html/rfc5929#section-3.1. */
const uint8_t *finished = ssl->s3->previous_client_finished;
size_t finished_len = ssl->s3->previous_client_finished_len;
- if (ssl->hit) {
+ if (ssl->session != NULL) {
/* tls-unique is broken for resumed sessions unless EMS is used. */
if (!ssl->session->extended_master_secret) {
goto err;
@@ -1447,13 +1455,14 @@ int SSL_set1_curves(SSL *ssl, const int *curves, size_t curves_len) {
uint16_t SSL_get_curve_id(const SSL *ssl) {
/* TODO(davidben): This checks the wrong session if there is a renegotiation in
* progress. */
- if (ssl->session == NULL ||
- ssl->session->cipher == NULL ||
- !SSL_CIPHER_is_ECDHE(ssl->session->cipher)) {
+ SSL_SESSION *session = SSL_get_session(ssl);
+ if (session == NULL ||
+ session->cipher == NULL ||
+ !SSL_CIPHER_is_ECDHE(session->cipher)) {
return 0;
}
- return (uint16_t)ssl->session->key_exchange_info;
+ return (uint16_t)session->key_exchange_info;
}
int SSL_CTX_set_tmp_dh(SSL_CTX *ctx, const DH *dh) {
@@ -1679,18 +1688,19 @@ const char *SSL_get_servername(const SSL *ssl, const int type) {
return ssl->tlsext_hostname;
}
- if (ssl->session == NULL) {
+ SSL_SESSION *session = SSL_get_session(ssl);
+ if (session == NULL) {
return NULL;
}
- return ssl->session->tlsext_hostname;
+ return session->tlsext_hostname;
}
int SSL_get_servername_type(const SSL *ssl) {
- if (ssl->session != NULL && ssl->session->tlsext_hostname != NULL) {
- return TLSEXT_NAMETYPE_host_name;
+ SSL_SESSION *session = SSL_get_session(ssl);
+ if (session == NULL || session->tlsext_hostname == NULL) {
+ return -1;
}
-
- return -1;
+ return TLSEXT_NAMETYPE_host_name;
}
void SSL_CTX_enable_signed_cert_timestamps(SSL_CTX *ctx) {
@@ -1713,7 +1723,7 @@ int SSL_enable_ocsp_stapling(SSL *ssl) {
void SSL_get0_signed_cert_timestamp_list(const SSL *ssl, const uint8_t **out,
size_t *out_len) {
- SSL_SESSION *session = ssl->session;
+ SSL_SESSION *session = SSL_get_session(ssl);
*out_len = 0;
*out = NULL;
@@ -1727,7 +1737,7 @@ void SSL_get0_signed_cert_timestamp_list(const SSL *ssl, const uint8_t **out,
void SSL_get0_ocsp_response(const SSL *ssl, const uint8_t **out,
size_t *out_len) {
- SSL_SESSION *session = ssl->session;
+ SSL_SESSION *session = SSL_get_session(ssl);
*out_len = 0;
*out = NULL;
@@ -2044,7 +2054,7 @@ void ssl_get_compatible_server_ciphers(SSL *ssl, uint32_t *out_mask_k,
void ssl_update_cache(SSL *ssl, int mode) {
SSL_CTX *ctx = ssl->initial_ctx;
/* Never cache sessions with empty session IDs. */
- if (ssl->session->session_id_length == 0 ||
+ if (ssl->s3->established_session->session_id_length == 0 ||
(ctx->session_cache_mode & mode) != mode) {
return;
}
@@ -2056,14 +2066,16 @@ void ssl_update_cache(SSL *ssl, int mode) {
/* A client may see new sessions on abbreviated handshakes if the server
* decides to renew the ticket. Once the handshake is completed, it should be
* inserted into the cache. */
- if (!ssl->hit || (!ssl->server && ssl->tlsext_ticket_expected)) {
+ if (ssl->s3->established_session != ssl->session ||
+ (!ssl->server && ssl->tlsext_ticket_expected)) {
if (use_internal_cache) {
- SSL_CTX_add_session(ctx, ssl->session);
+ SSL_CTX_add_session(ctx, ssl->s3->established_session);
}
if (ctx->new_session_cb != NULL &&
- !ctx->new_session_cb(ssl, SSL_SESSION_up_ref(ssl->session))) {
+ !ctx->new_session_cb(ssl, SSL_SESSION_up_ref(
+ ssl->s3->established_session))) {
/* |new_session_cb|'s return value signals whether it took ownership. */
- SSL_SESSION_free(ssl->session);
+ SSL_SESSION_free(ssl->s3->established_session);
}
}
@@ -2161,7 +2173,7 @@ const SSL_CIPHER *SSL_get_current_cipher(const SSL *ssl) {
}
int SSL_session_reused(const SSL *ssl) {
- return ssl->hit;
+ return ssl->session != NULL;
}
const COMP_METHOD *SSL_get_current_compression(SSL *ssl) { return NULL; }
@@ -2384,13 +2396,14 @@ void SSL_set_tmp_dh_callback(SSL *ssl, DH *(*callback)(SSL *ssl, int is_export,
unsigned SSL_get_dhe_group_size(const SSL *ssl) {
/* TODO(davidben): This checks the wrong session if there is a renegotiation in
* progress. */
- if (ssl->session == NULL ||
- ssl->session->cipher == NULL ||
- !SSL_CIPHER_is_DHE(ssl->session->cipher)) {
+ SSL_SESSION *session = SSL_get_session(ssl);
+ if (session == NULL ||
+ session->cipher == NULL ||
+ !SSL_CIPHER_is_DHE(session->cipher)) {
return 0;
}
- return ssl->session->key_exchange_info;
+ return session->key_exchange_info;
}
int SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *identity_hint) {
@@ -2445,11 +2458,14 @@ const char *SSL_get_psk_identity_hint(const SSL *ssl) {
}
const char *SSL_get_psk_identity(const SSL *ssl) {
- if (ssl == NULL || ssl->session == NULL) {
+ if (ssl == NULL) {
return NULL;
}
-
- return ssl->session->psk_identity;
+ SSL_SESSION *session = SSL_get_session(ssl);
+ if (session == NULL) {
+ return NULL;
+ }
+ return session->psk_identity;
}
void SSL_set_psk_client_callback(
@@ -2871,8 +2887,6 @@ int SSL_clear(SSL *ssl) {
ssl->session = NULL;
}
- ssl->hit = 0;
-
/* SSL_clear may be called before or after the |ssl| is initialized in either
* accept or connect state. In the latter case, SSL_clear should preserve the
* half and reset |ssl->state| accordingly. */
diff --git a/ssl/ssl_session.c b/ssl/ssl_session.c
index 8e51a6a2..0a9ccba5 100644
--- a/ssl/ssl_session.c
+++ b/ssl/ssl_session.c
@@ -175,6 +175,95 @@ SSL_SESSION *SSL_SESSION_new(void) {
return session;
}
+SSL_SESSION *SSL_SESSION_dup(SSL_SESSION *session, int include_ticket) {
+ SSL_SESSION *new_session = SSL_SESSION_new();
+ if (new_session == NULL) {
+ goto err;
+ }
+
+ new_session->ssl_version = session->ssl_version;
+ new_session->key_exchange_info = session->key_exchange_info;
+ new_session->master_key_length = session->master_key_length;
+ memcpy(new_session->master_key, session->master_key,
+ session->master_key_length);
+ new_session->session_id_length = session->session_id_length;
+ memcpy(new_session->session_id, session->session_id,
+ session->session_id_length);
+ new_session->sid_ctx_length = session->sid_ctx_length;
+ memcpy(new_session->sid_ctx, session->sid_ctx, session->sid_ctx_length);
+ if (session->psk_identity != NULL) {
+ new_session->psk_identity = BUF_strdup(session->psk_identity);
+ if (new_session->psk_identity == NULL) {
+ goto err;
+ }
+ }
+ if (session->peer != NULL) {
+ new_session->peer = X509_up_ref(session->peer);
+ }
+ if (session->cert_chain != NULL) {
+ new_session->cert_chain = X509_chain_up_ref(session->cert_chain);
+ if (new_session->cert_chain == NULL) {
+ goto err;
+ }
+ }
+ new_session->verify_result = session->verify_result;
+ new_session->timeout = session->timeout;
+ new_session->time = session->time;
+ new_session->cipher = session->cipher;
+ /* The new_session does not get a copy of the ex_data. */
+ if (session->tlsext_hostname != NULL) {
+ new_session->tlsext_hostname = BUF_strdup(session->tlsext_hostname);
+ if (new_session->tlsext_hostname == NULL) {
+ goto err;
+ }
+ }
+ if (include_ticket) {
+ if (session->tlsext_tick != NULL) {
+ new_session->tlsext_tick = BUF_memdup(session->tlsext_tick,
+ session->tlsext_ticklen);
+ if (new_session->tlsext_tick == NULL) {
+ goto err;
+ }
+ }
+ new_session->tlsext_ticklen = session->tlsext_ticklen;
+ }
+
+ new_session->tlsext_signed_cert_timestamp_list_length =
+ session->tlsext_signed_cert_timestamp_list_length;
+ if (session->tlsext_signed_cert_timestamp_list != NULL) {
+ new_session->tlsext_signed_cert_timestamp_list =
+ BUF_memdup(session->tlsext_signed_cert_timestamp_list,
+ session->tlsext_signed_cert_timestamp_list_length);
+ if (new_session->tlsext_signed_cert_timestamp_list == NULL) {
+ goto err;
+ }
+ }
+ new_session->ocsp_response_length = session->ocsp_response_length;
+ if (session->ocsp_response != NULL) {
+ new_session->ocsp_response = BUF_memdup(session->ocsp_response,
+ session->ocsp_response_length);
+ if (new_session->ocsp_response == NULL) {
+ goto err;
+ }
+ }
+ memcpy(new_session->peer_sha256, session->peer_sha256, SHA256_DIGEST_LENGTH);
+ memcpy(new_session->original_handshake_hash,
+ session->original_handshake_hash,
+ session->original_handshake_hash_len);
+ new_session->original_handshake_hash_len =
+ session->original_handshake_hash_len;
+ new_session->tlsext_tick_lifetime_hint = session->tlsext_tick_lifetime_hint;
+ new_session->extended_master_secret = session->extended_master_secret;
+ new_session->peer_sha256_valid = session->peer_sha256_valid;
+ new_session->not_resumable = 1;
+ return new_session;
+
+err:
+ SSL_SESSION_free(new_session);
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return 0;
+}
+
SSL_SESSION *SSL_SESSION_up_ref(SSL_SESSION *session) {
if (session != NULL) {
CRYPTO_refcount_inc(&session->references);
@@ -281,13 +370,21 @@ SSL_SESSION *SSL_magic_pending_session_ptr(void) {
SSL_SESSION *SSL_get_session(const SSL *ssl)
{
- /* aka SSL_get0_session; gets 0 objects, just returns a copy of the pointer */
+ /* Once the handshake completes we return the established session. Otherwise
+ * we return the intermediate session, either |session| (for resumption) or
+ * |new_session| if doing a full handshake. */
+ if (!SSL_in_init(ssl)) {
+ return ssl->s3->established_session;
+ }
+ if (ssl->s3->new_session != NULL) {
+ return ssl->s3->new_session;
+ }
return ssl->session;
}
SSL_SESSION *SSL_get1_session(SSL *ssl) {
/* variant of SSL_get_session: caller really gets something */
- return SSL_SESSION_up_ref(ssl->session);
+ return SSL_SESSION_up_ref(SSL_get_session(ssl));
}
int SSL_SESSION_get_ex_new_index(long argl, void *argp,
@@ -358,10 +455,13 @@ int ssl_get_new_session(SSL *ssl, int is_server) {
memcpy(session->sid_ctx, ssl->sid_ctx, ssl->sid_ctx_length);
session->sid_ctx_length = ssl->sid_ctx_length;
+ /* The session is marked not resumable until it is completely filled in. */
+ session->not_resumable = 1;
session->verify_result = X509_V_OK;
- SSL_SESSION_free(ssl->session);
- ssl->session = session;
+ SSL_SESSION_free(ssl->s3->new_session);
+ ssl->s3->new_session = session;
+ SSL_set_session(ssl, NULL);
return 1;
err:
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index f26962f1..5dd689b5 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -30,6 +30,7 @@
#include <openssl/ssl.h>
#include <openssl/x509.h>
+#include "internal.h"
#include "test/scoped_types.h"
#include "../crypto/test/test_util.h"
@@ -1253,6 +1254,48 @@ static bool TestOneSidedShutdown() {
return true;
}
+static bool TestSessionDuplication() {
+ ScopedSSL_CTX client_ctx(SSL_CTX_new(TLS_method()));
+ ScopedSSL_CTX server_ctx(SSL_CTX_new(TLS_method()));
+ if (!client_ctx || !server_ctx) {
+ return false;
+ }
+
+ ScopedX509 cert = GetTestCertificate();
+ ScopedEVP_PKEY key = GetTestKey();
+ if (!cert || !key ||
+ !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
+ !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) {
+ return false;
+ }
+
+ ScopedSSL client, server;
+ if (!ConnectClientAndServer(&client, &server, client_ctx.get(),
+ server_ctx.get())) {
+ return false;
+ }
+
+ SSL_SESSION *session0 = SSL_get_session(client.get());
+ ScopedSSL_SESSION session1(SSL_SESSION_dup(session0, 1));
+ if (!session1) {
+ return false;
+ }
+
+ uint8_t *s0_bytes, *s1_bytes;
+ size_t s0_len, s1_len;
+
+ if (!SSL_SESSION_to_bytes(session0, &s0_bytes, &s0_len)) {
+ return false;
+ }
+ ScopedOpenSSLBytes free_s0(s0_bytes);
+
+ if (!SSL_SESSION_to_bytes(session1.get(), &s1_bytes, &s1_len)) {
+ return false;
+ }
+ ScopedOpenSSLBytes free_s1(s1_bytes);
+
+ return s0_len == s1_len && memcmp(s0_bytes, s1_bytes, s0_len) == 0;
+}
static bool ExpectFDs(const SSL *ssl, int rfd, int wfd) {
if (SSL_get_rfd(ssl) != rfd || SSL_get_wfd(ssl) != wfd) {
@@ -1501,6 +1544,7 @@ int main() {
!TestSequenceNumber(false /* TLS */) ||
!TestSequenceNumber(true /* DTLS */) ||
!TestOneSidedShutdown() ||
+ !TestSessionDuplication() ||
!TestSetFD() ||
!TestGetPeerCertificate() ||
!TestRetainOnlySHA256OfCerts()) {
diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c
index 74c5d41b..4b8fe455 100644
--- a/ssl/t1_enc.c
+++ b/ssl/t1_enc.c
@@ -327,8 +327,8 @@ size_t SSL_get_key_block_len(const SSL *ssl) {
int SSL_generate_key_block(const SSL *ssl, uint8_t *out, size_t out_len) {
return ssl->s3->enc_method->prf(
- ssl, out, out_len, ssl->session->master_key,
- ssl->session->master_key_length, TLS_MD_KEY_EXPANSION_CONST,
+ ssl, out, out_len, SSL_get_session(ssl)->master_key,
+ SSL_get_session(ssl)->master_key_length, TLS_MD_KEY_EXPANSION_CONST,
TLS_MD_KEY_EXPANSION_CONST_SIZE, ssl->s3->server_random, SSL3_RANDOM_SIZE,
ssl->s3->client_random, SSL3_RANDOM_SIZE);
}
@@ -338,12 +338,16 @@ int tls1_setup_key_block(SSL *ssl) {
return 1;
}
+ SSL_SESSION *session = ssl->session;
+ if (ssl->s3->new_session != NULL) {
+ session = ssl->s3->new_session;
+ }
+
const EVP_AEAD *aead = NULL;
size_t mac_secret_len, fixed_iv_len;
- if (ssl->session->cipher == NULL ||
+ if (session->cipher == NULL ||
!ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len,
- ssl->session->cipher,
- ssl3_protocol_version(ssl))) {
+ session->cipher, ssl3_protocol_version(ssl))) {
OPENSSL_PUT_ERROR(SSL, SSL_R_CIPHER_OR_HASH_UNAVAILABLE);
return 0;
}
@@ -454,8 +458,8 @@ static int tls1_final_finish_mac(SSL *ssl, int from_server, uint8_t *out) {
static const size_t kFinishedLen = 12;
if (!ssl->s3->enc_method->prf(ssl, out, kFinishedLen,
- ssl->session->master_key,
- ssl->session->master_key_length, label,
+ SSL_get_session(ssl)->master_key,
+ SSL_get_session(ssl)->master_key_length, label,
label_len, buf, digests_len, NULL, 0)) {
return 0;
}
@@ -529,8 +533,9 @@ int SSL_export_keying_material(SSL *ssl, uint8_t *out, size_t out_len,
}
int ret =
- ssl->s3->enc_method->prf(ssl, out, out_len, ssl->session->master_key,
- ssl->session->master_key_length, label,
+ ssl->s3->enc_method->prf(ssl, out, out_len,
+ SSL_get_session(ssl)->master_key,
+ SSL_get_session(ssl)->master_key_length, label,
label_len, seed, seed_len, NULL, 0);
OPENSSL_free(seed);
return ret;
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index b1e3b13c..6b0c16ab 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -713,10 +713,10 @@ static int ext_sni_parse_serverhello(SSL *ssl, uint8_t *out_alert,
assert(ssl->tlsext_hostname != NULL);
- if (!ssl->hit) {
- assert(ssl->session->tlsext_hostname == NULL);
- ssl->session->tlsext_hostname = BUF_strdup(ssl->tlsext_hostname);
- if (!ssl->session->tlsext_hostname) {
+ if (ssl->session == NULL) {
+ assert(ssl->s3->new_session->tlsext_hostname == NULL);
+ ssl->s3->new_session->tlsext_hostname = BUF_strdup(ssl->tlsext_hostname);
+ if (!ssl->s3->new_session->tlsext_hostname) {
*out_alert = SSL_AD_INTERNAL_ERROR;
return 0;
}
@@ -759,11 +759,11 @@ static int ext_sni_parse_clienthello(SSL *ssl, uint8_t *out_alert,
/* TODO(davidben): SNI should be resolved before resumption. We have the
* early callback as a replacement, but we should fix the current callback
* and avoid the need for |SSL_CTX_set_session_id_context|. */
- if (!ssl->hit) {
- assert(ssl->session->tlsext_hostname == NULL);
+ if (ssl->session == NULL) {
+ assert(ssl->s3->new_session->tlsext_hostname == NULL);
/* Copy the hostname as a string. */
- if (!CBS_strdup(&host_name, &ssl->session->tlsext_hostname)) {
+ if (!CBS_strdup(&host_name, &ssl->s3->new_session->tlsext_hostname)) {
*out_alert = SSL_AD_INTERNAL_ERROR;
return 0;
}
@@ -775,9 +775,9 @@ static int ext_sni_parse_clienthello(SSL *ssl, uint8_t *out_alert,
}
static int ext_sni_add_serverhello(SSL *ssl, CBB *out) {
- if (ssl->hit ||
+ if (ssl->session != NULL ||
!ssl->s3->tmp.should_ack_sni ||
- ssl->session->tlsext_hostname == NULL) {
+ ssl->s3->new_session->tlsext_hostname == NULL) {
return 1;
}
@@ -1210,8 +1210,8 @@ static int ext_ocsp_parse_serverhello(SSL *ssl, uint8_t *out_alert,
return 0;
}
- if (!CBS_stow(&ocsp_response, &ssl->session->ocsp_response,
- &ssl->session->ocsp_response_length)) {
+ if (!CBS_stow(&ocsp_response, &ssl->s3->new_session->ocsp_response,
+ &ssl->s3->new_session->ocsp_response_length)) {
*out_alert = SSL_AD_INTERNAL_ERROR;
return 0;
}
@@ -1246,7 +1246,7 @@ static int ext_ocsp_add_serverhello(SSL *ssl, CBB *out) {
if (ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
/* The extension shouldn't be sent when resuming sessions. */
- if (ssl->hit) {
+ if (ssl->session != NULL) {
return 1;
}
@@ -1436,9 +1436,11 @@ static int ext_sct_parse_serverhello(SSL *ssl, uint8_t *out_alert,
}
/* Session resumption uses the original session information. */
- if (!ssl->hit &&
- !CBS_stow(contents, &ssl->session->tlsext_signed_cert_timestamp_list,
- &ssl->session->tlsext_signed_cert_timestamp_list_length)) {
+ if (ssl->session == NULL &&
+ !CBS_stow(
+ contents,
+ &ssl->s3->new_session->tlsext_signed_cert_timestamp_list,
+ &ssl->s3->new_session->tlsext_signed_cert_timestamp_list_length)) {
*out_alert = SSL_AD_INTERNAL_ERROR;
return 0;
}
@@ -1453,7 +1455,7 @@ static int ext_sct_parse_clienthello(SSL *ssl, uint8_t *out_alert,
static int ext_sct_add_serverhello(SSL *ssl, CBB *out) {
/* The extension shouldn't be sent when resuming sessions. */
- if (ssl->hit ||
+ if (ssl->session != NULL ||
ssl->ctx->signed_cert_timestamp_list_length == 0) {
return 1;
}
@@ -2976,7 +2978,7 @@ int tls1_channel_id_hash(SSL *ssl, uint8_t *out, size_t *out_len) {
static const char kClientIDMagic[] = "TLS Channel ID signature";
EVP_DigestUpdate(&ctx, kClientIDMagic, sizeof(kClientIDMagic));
- if (ssl->hit) {
+ if (ssl->session != NULL) {
static const char kResumptionMagic[] = "Resumption";
EVP_DigestUpdate(&ctx, kResumptionMagic, sizeof(kResumptionMagic));
if (ssl->session->original_handshake_hash_len == 0) {
@@ -3006,25 +3008,26 @@ err:
}
/* tls1_record_handshake_hashes_for_channel_id records the current handshake
- * hashes in |ssl->session| so that Channel ID resumptions can sign that
+ * hashes in |ssl->s3->new_session| so that Channel ID resumptions can sign that
* data. */
int tls1_record_handshake_hashes_for_channel_id(SSL *ssl) {
int digest_len;
/* This function should never be called for a resumed session because the
* handshake hashes that we wish to record are for the original, full
* handshake. */
- if (ssl->hit) {
+ if (ssl->session != NULL) {
return -1;
}
digest_len =
- tls1_handshake_digest(ssl, ssl->session->original_handshake_hash,
- sizeof(ssl->session->original_handshake_hash));
+ tls1_handshake_digest(
+ ssl, ssl->s3->new_session->original_handshake_hash,
+ sizeof(ssl->s3->new_session->original_handshake_hash));
if (digest_len < 0) {
return -1;
}
- ssl->session->original_handshake_hash_len = digest_len;
+ ssl->s3->new_session->original_handshake_hash_len = digest_len;
return 1;
}
diff --git a/ssl/tls13_both.c b/ssl/tls13_both.c
index aa11b2b6..7b276dfa 100644
--- a/ssl/tls13_both.c
+++ b/ssl/tls13_both.c
@@ -194,9 +194,9 @@ int tls13_process_certificate(SSL *ssl) {
int ret = 0;
uint8_t alert;
STACK_OF(X509) *chain = ssl_parse_cert_chain(
- ssl, &alert,
- ssl->ctx->retain_only_sha256_of_client_certs ? ssl->session->peer_sha256
- : NULL,
+ ssl, &alert, ssl->ctx->retain_only_sha256_of_client_certs
+ ? ssl->s3->new_session->peer_sha256
+ : NULL,
&cbs);
if (chain == NULL) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
@@ -232,7 +232,7 @@ int tls13_process_certificate(SSL *ssl) {
if (ssl->server && ssl->ctx->retain_only_sha256_of_client_certs) {
/* The hash was filled in by |ssl_parse_cert_chain|. */
- ssl->session->peer_sha256_valid = 1;
+ ssl->s3->new_session->peer_sha256_valid = 1;
}
X509 *leaf = sk_X509_value(chain, 0);
@@ -251,19 +251,19 @@ int tls13_process_certificate(SSL *ssl) {
}
ERR_clear_error();
- ssl->session->verify_result = ssl->verify_result;
+ ssl->s3->new_session->verify_result = ssl->verify_result;
- X509_free(ssl->session->peer);
+ X509_free(ssl->s3->new_session->peer);
/* For historical reasons, the client and server differ on whether the chain
* includes the leaf. */
if (ssl->server) {
- ssl->session->peer = sk_X509_shift(chain);
+ ssl->s3->new_session->peer = sk_X509_shift(chain);
} else {
- ssl->session->peer = X509_up_ref(leaf);
+ ssl->s3->new_session->peer = X509_up_ref(leaf);
}
- sk_X509_pop_free(ssl->session->cert_chain, X509_free);
- ssl->session->cert_chain = chain;
+ sk_X509_pop_free(ssl->s3->new_session->cert_chain, X509_free);
+ ssl->s3->new_session->cert_chain = chain;
chain = NULL;
ret = 1;
@@ -275,7 +275,7 @@ err:
int tls13_process_certificate_verify(SSL *ssl) {
int ret = 0;
- X509 *peer = ssl->session->peer;
+ X509 *peer = ssl->s3->new_session->peer;
EVP_PKEY *pkey = NULL;
uint8_t *msg = NULL;
size_t msg_len;
diff --git a/ssl/tls13_client.c b/ssl/tls13_client.c
index 742dcd8e..c38358db 100644
--- a/ssl/tls13_client.c
+++ b/ssl/tls13_client.c
@@ -183,7 +183,7 @@ static enum ssl_hs_wait_t do_process_server_hello(SSL *ssl, SSL_HANDSHAKE *hs) {
assert(ssl->s3->have_version);
memcpy(ssl->s3->server_random, CBS_data(&server_random), SSL3_RANDOM_SIZE);
- ssl->hit = 0;
+ SSL_set_session(ssl, NULL);
if (!ssl_get_new_session(ssl, 0)) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
@@ -207,7 +207,7 @@ static enum ssl_hs_wait_t do_process_server_hello(SSL *ssl, SSL_HANDSHAKE *hs) {
return ssl_hs_error;
}
- ssl->session->cipher = cipher;
+ ssl->s3->new_session->cipher = cipher;
ssl->s3->tmp.new_cipher = cipher;
/* The PRF hash is now known. Set up the key schedule. */
diff --git a/ssl/tls13_enc.c b/ssl/tls13_enc.c
index e0f7d39e..1f4fe211 100644
--- a/ssl/tls13_enc.c
+++ b/ssl/tls13_enc.c
@@ -176,7 +176,7 @@ int tls13_set_traffic_key(SSL *ssl, enum tls_record_type_t type,
const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
size_t mac_secret_len, fixed_iv_len;
if (!ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len,
- ssl->session->cipher,
+ ssl->s3->new_session->cipher,
ssl3_protocol_version(ssl))) {
return 0;
}
@@ -206,11 +206,9 @@ int tls13_set_traffic_key(SSL *ssl, enum tls_record_type_t type,
return 0;
}
- SSL_AEAD_CTX *traffic_aead = SSL_AEAD_CTX_new(direction,
- ssl3_protocol_version(ssl),
- ssl->session->cipher,
- key, key_len, NULL, 0,
- iv, iv_len);
+ SSL_AEAD_CTX *traffic_aead = SSL_AEAD_CTX_new(
+ direction, ssl3_protocol_version(ssl), ssl->s3->new_session->cipher, key,
+ key_len, NULL, 0, iv, iv_len);
if (traffic_aead == NULL) {
return 0;
}
@@ -276,12 +274,12 @@ int tls13_finalize_keys(SSL *ssl) {
SSL_HANDSHAKE *hs = ssl->s3->hs;
ssl->s3->exporter_secret_len = hs->hash_len;
- ssl->session->master_key_length = hs->hash_len;
+ ssl->s3->new_session->master_key_length = hs->hash_len;
if (!derive_secret(
ssl, ssl->s3->exporter_secret, ssl->s3->exporter_secret_len,
(const uint8_t *)kTLS13LabelExporter, strlen(kTLS13LabelExporter)) ||
- !derive_secret(ssl, ssl->session->master_key,
- ssl->session->master_key_length,
+ !derive_secret(ssl, ssl->s3->new_session->master_key,
+ ssl->s3->new_session->master_key_length,
(const uint8_t *)kTLS13LabelResumption,
strlen(kTLS13LabelResumption))) {
return 0;
diff --git a/ssl/tls13_server.c b/ssl/tls13_server.c
index a3517ba4..cf0aea22 100644
--- a/ssl/tls13_server.c
+++ b/ssl/tls13_server.c
@@ -136,7 +136,7 @@ static enum ssl_hs_wait_t do_process_client_hello(SSL *ssl, SSL_HANDSHAKE *hs) {
/* Load the client random. */
memcpy(ssl->s3->client_random, CBS_data(&client_random), SSL3_RANDOM_SIZE);
- ssl->hit = 0;
+ SSL_set_session(ssl, NULL);
if (!ssl_get_new_session(ssl, 1 /* server */)) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
@@ -212,7 +212,7 @@ static enum ssl_hs_wait_t do_process_client_hello(SSL *ssl, SSL_HANDSHAKE *hs) {
return ssl_hs_error;
}
- ssl->session->cipher = cipher;
+ ssl->s3->new_session->cipher = cipher;
ssl->s3->tmp.new_cipher = cipher;
ssl->method->received_flight(ssl);
@@ -470,7 +470,7 @@ static enum ssl_hs_wait_t do_process_client_certificate(SSL *ssl,
static enum ssl_hs_wait_t do_process_client_certificate_verify(
SSL *ssl, SSL_HANDSHAKE *hs) {
- if (ssl->session->peer == NULL) {
+ if (ssl->s3->new_session->peer == NULL) {
/* Skip this state. */
hs->state = state_process_client_finished;
return ssl_hs_ok;