diff options
author | Steven Valdez <svaldez@google.com> | 2016-06-27 23:34:59 +0300 |
---|---|---|
committer | CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org> | 2016-07-30 00:22:46 +0300 |
commit | 87eab4902d1003983079881d3ee8a66ec2eaccc2 (patch) | |
tree | afaac2e2b48d2bba884059a8572b3cb84a0e630a | |
parent | 163f29af076a2fcc5564f7998ab57e3fb9b197b7 (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.h | 34 | ||||
-rw-r--r-- | ssl/handshake_client.c | 138 | ||||
-rw-r--r-- | ssl/handshake_server.c | 131 | ||||
-rw-r--r-- | ssl/internal.h | 15 | ||||
-rw-r--r-- | ssl/s3_both.c | 5 | ||||
-rw-r--r-- | ssl/s3_enc.c | 11 | ||||
-rw-r--r-- | ssl/s3_lib.c | 2 | ||||
-rw-r--r-- | ssl/ssl_lib.c | 78 | ||||
-rw-r--r-- | ssl/ssl_session.c | 108 | ||||
-rw-r--r-- | ssl/ssl_test.cc | 44 | ||||
-rw-r--r-- | ssl/t1_enc.c | 23 | ||||
-rw-r--r-- | ssl/t1_lib.c | 47 | ||||
-rw-r--r-- | ssl/tls13_both.c | 22 | ||||
-rw-r--r-- | ssl/tls13_client.c | 4 | ||||
-rw-r--r-- | ssl/tls13_enc.c | 16 | ||||
-rw-r--r-- | ssl/tls13_server.c | 6 |
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; |