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>kris/onging/CECPQ3_patch15
@@ -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; | |||
@@ -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; | |||
} |
@@ -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; | |||
} | |||
@@ -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 */ |
@@ -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; | |||
} | |||
@@ -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); | |||
@@ -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); | |||
@@ -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. */ | |||
@@ -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: | |||
@@ -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()) { | |||
@@ -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; | |||
@@ -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; | |||
} |
@@ -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; | |||
@@ -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. */ | |||
@@ -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; | |||
@@ -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; | |||