Update pre9
This commit is contained in:
+62
-38
@@ -1457,40 +1457,6 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* In case of record types for which we have 'fragment' storage, fill
|
||||
* that so that we can process the data at a fixed place.
|
||||
*/
|
||||
{
|
||||
size_t dest_maxlen = 0;
|
||||
unsigned char *dest = NULL;
|
||||
size_t *dest_len = NULL;
|
||||
|
||||
if (SSL3_RECORD_get_type(rr) == SSL3_RT_HANDSHAKE) {
|
||||
dest_maxlen = sizeof(s->rlayer.handshake_fragment);
|
||||
dest = s->rlayer.handshake_fragment;
|
||||
dest_len = &s->rlayer.handshake_fragment_len;
|
||||
}
|
||||
|
||||
if (dest_maxlen > 0) {
|
||||
n = dest_maxlen - *dest_len; /* available space in 'dest' */
|
||||
if (SSL3_RECORD_get_length(rr) < n)
|
||||
n = SSL3_RECORD_get_length(rr); /* available bytes */
|
||||
|
||||
/* now move 'n' bytes: */
|
||||
memcpy(dest + *dest_len,
|
||||
SSL3_RECORD_get_data(rr) + SSL3_RECORD_get_off(rr), n);
|
||||
SSL3_RECORD_add_off(rr, n);
|
||||
SSL3_RECORD_sub_length(rr, n);
|
||||
*dest_len += n;
|
||||
if (SSL3_RECORD_get_length(rr) == 0)
|
||||
SSL3_RECORD_set_read(rr);
|
||||
|
||||
if (*dest_len < dest_maxlen)
|
||||
goto start; /* fragment was too small */
|
||||
}
|
||||
}
|
||||
|
||||
/*-
|
||||
* s->rlayer.handshake_fragment_len == 4 iff rr->type == SSL3_RT_HANDSHAKE;
|
||||
* (Possibly rr is 'empty' now, i.e. rr->length may be 0.)
|
||||
@@ -1583,12 +1549,70 @@ int ssl3_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (s->shutdown & SSL_SENT_SHUTDOWN) { /* but we have not received a
|
||||
* shutdown */
|
||||
s->rwstate = SSL_NOTHING;
|
||||
/*
|
||||
* If we've sent a close_notify but not yet received one back then ditch
|
||||
* anything we read.
|
||||
*/
|
||||
if ((s->shutdown & SSL_SENT_SHUTDOWN) != 0) {
|
||||
/*
|
||||
* In TLSv1.3 this could get problematic if we receive a KeyUpdate
|
||||
* message after we sent a close_notify because we're about to ditch it,
|
||||
* so we won't be able to read a close_notify sent afterwards! We don't
|
||||
* support that.
|
||||
*/
|
||||
SSL3_RECORD_set_length(rr, 0);
|
||||
SSL3_RECORD_set_read(rr);
|
||||
return 0;
|
||||
|
||||
if (SSL3_RECORD_get_type(rr) == SSL3_RT_HANDSHAKE) {
|
||||
BIO *rbio;
|
||||
|
||||
if ((s->mode & SSL_MODE_AUTO_RETRY) != 0)
|
||||
goto start;
|
||||
|
||||
s->rwstate = SSL_READING;
|
||||
rbio = SSL_get_rbio(s);
|
||||
BIO_clear_retry_flags(rbio);
|
||||
BIO_set_retry_read(rbio);
|
||||
} else {
|
||||
/*
|
||||
* The peer is continuing to send application data, but we have
|
||||
* already sent close_notify. If this was expected we should have
|
||||
* been called via SSL_read() and this would have been handled
|
||||
* above.
|
||||
* No alert sent because we already sent close_notify
|
||||
*/
|
||||
SSLfatal(s, SSL_AD_NO_ALERT, SSL_F_SSL3_READ_BYTES,
|
||||
SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* For handshake data we have 'fragment' storage, so fill that so that we
|
||||
* can process the header at a fixed place. This is done after the
|
||||
* "SHUTDOWN" code above to avoid filling the fragment storage with data
|
||||
* that we're just going to discard.
|
||||
*/
|
||||
if (SSL3_RECORD_get_type(rr) == SSL3_RT_HANDSHAKE) {
|
||||
size_t dest_maxlen = sizeof(s->rlayer.handshake_fragment);
|
||||
unsigned char *dest = s->rlayer.handshake_fragment;
|
||||
size_t *dest_len = &s->rlayer.handshake_fragment_len;
|
||||
|
||||
n = dest_maxlen - *dest_len; /* available space in 'dest' */
|
||||
if (SSL3_RECORD_get_length(rr) < n)
|
||||
n = SSL3_RECORD_get_length(rr); /* available bytes */
|
||||
|
||||
/* now move 'n' bytes: */
|
||||
memcpy(dest + *dest_len,
|
||||
SSL3_RECORD_get_data(rr) + SSL3_RECORD_get_off(rr), n);
|
||||
SSL3_RECORD_add_off(rr, n);
|
||||
SSL3_RECORD_sub_length(rr, n);
|
||||
*dest_len += n;
|
||||
if (SSL3_RECORD_get_length(rr) == 0)
|
||||
SSL3_RECORD_set_read(rr);
|
||||
|
||||
if (*dest_len < dest_maxlen)
|
||||
goto start; /* fragment was too small */
|
||||
}
|
||||
|
||||
if (SSL3_RECORD_get_type(rr) == SSL3_RT_CHANGE_CIPHER_SPEC) {
|
||||
|
||||
@@ -24,8 +24,14 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
|
||||
{ERR_PACK(ERR_LIB_SSL, SSL_F_CONSTRUCT_CA_NAMES, 0), "construct_ca_names"},
|
||||
{ERR_PACK(ERR_LIB_SSL, SSL_F_CONSTRUCT_KEY_EXCHANGE_TBS, 0),
|
||||
"construct_key_exchange_tbs"},
|
||||
{ERR_PACK(ERR_LIB_SSL, SSL_F_CONSTRUCT_STATEFUL_TICKET, 0),
|
||||
"construct_stateful_ticket"},
|
||||
{ERR_PACK(ERR_LIB_SSL, SSL_F_CONSTRUCT_STATELESS_TICKET, 0),
|
||||
"construct_stateless_ticket"},
|
||||
{ERR_PACK(ERR_LIB_SSL, SSL_F_CREATE_SYNTHETIC_MESSAGE_HASH, 0),
|
||||
"create_synthetic_message_hash"},
|
||||
{ERR_PACK(ERR_LIB_SSL, SSL_F_CREATE_TICKET_PREQUEL, 0),
|
||||
"create_ticket_prequel"},
|
||||
{ERR_PACK(ERR_LIB_SSL, SSL_F_CT_MOVE_SCTS, 0), "ct_move_scts"},
|
||||
{ERR_PACK(ERR_LIB_SSL, SSL_F_CT_STRICT, 0), "ct_strict"},
|
||||
{ERR_PACK(ERR_LIB_SSL, SSL_F_CUSTOM_EXT_ADD, 0), "custom_ext_add"},
|
||||
@@ -720,6 +726,8 @@ static const ERR_STRING_DATA SSL_str_functs[] = {
|
||||
};
|
||||
|
||||
static const ERR_STRING_DATA SSL_str_reasons[] = {
|
||||
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY),
|
||||
"application data after close notify"},
|
||||
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_APP_DATA_IN_HANDSHAKE),
|
||||
"app data in handshake"},
|
||||
{ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT),
|
||||
|
||||
+6
-3
@@ -3435,18 +3435,21 @@ void ssl_update_cache(SSL *s, int mode)
|
||||
&& (!s->hit || SSL_IS_TLS13(s))) {
|
||||
/*
|
||||
* Add the session to the internal cache. In server side TLSv1.3 we
|
||||
* normally don't do this because its a full stateless ticket with only
|
||||
* a dummy session id so there is no reason to cache it, unless:
|
||||
* normally don't do this because by default it's a full stateless ticket
|
||||
* with only a dummy session id so there is no reason to cache it,
|
||||
* unless:
|
||||
* - we are doing early_data, in which case we cache so that we can
|
||||
* detect replays
|
||||
* - the application has set a remove_session_cb so needs to know about
|
||||
* session timeout events
|
||||
* - SSL_OP_NO_TICKET is set in which case it is a stateful ticket
|
||||
*/
|
||||
if ((i & SSL_SESS_CACHE_NO_INTERNAL_STORE) == 0
|
||||
&& (!SSL_IS_TLS13(s)
|
||||
|| !s->server
|
||||
|| s->max_early_data > 0
|
||||
|| s->session_ctx->remove_session_cb != NULL))
|
||||
|| s->session_ctx->remove_session_cb != NULL
|
||||
|| (s->options & SSL_OP_NO_TICKET) != 0))
|
||||
SSL_CTX_add_session(s->session_ctx, s->session);
|
||||
|
||||
/*
|
||||
|
||||
@@ -2249,6 +2249,8 @@ void ssl_cert_clear_certs(CERT *c);
|
||||
void ssl_cert_free(CERT *c);
|
||||
__owur int ssl_generate_session_id(SSL *s, SSL_SESSION *ss);
|
||||
__owur int ssl_get_new_session(SSL *s, int session);
|
||||
__owur SSL_SESSION *lookup_sess_in_cache(SSL *s, const unsigned char *sess_id,
|
||||
size_t sess_id_len);
|
||||
__owur int ssl_get_prev_session(SSL *s, CLIENTHELLO_MSG *hello);
|
||||
__owur SSL_SESSION *ssl_session_dup(SSL_SESSION *src, int ticket);
|
||||
__owur int ssl_cipher_id_cmp(const SSL_CIPHER *a, const SSL_CIPHER *b);
|
||||
|
||||
+72
-62
@@ -12,6 +12,7 @@
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/engine.h>
|
||||
#include "internal/refcount.h"
|
||||
#include "internal/cryptlib.h"
|
||||
#include "ssl_locl.h"
|
||||
#include "statem/statem_locl.h"
|
||||
|
||||
@@ -452,6 +453,73 @@ int ssl_get_new_session(SSL *s, int session)
|
||||
return 1;
|
||||
}
|
||||
|
||||
SSL_SESSION *lookup_sess_in_cache(SSL *s, const unsigned char *sess_id,
|
||||
size_t sess_id_len)
|
||||
{
|
||||
SSL_SESSION *ret = NULL;
|
||||
int discard;
|
||||
|
||||
if ((s->session_ctx->session_cache_mode
|
||||
& SSL_SESS_CACHE_NO_INTERNAL_LOOKUP) == 0) {
|
||||
SSL_SESSION data;
|
||||
|
||||
data.ssl_version = s->version;
|
||||
if (!ossl_assert(sess_id_len <= SSL_MAX_SSL_SESSION_ID_LENGTH))
|
||||
return NULL;
|
||||
|
||||
memcpy(data.session_id, sess_id, sess_id_len);
|
||||
data.session_id_length = sess_id_len;
|
||||
|
||||
CRYPTO_THREAD_read_lock(s->session_ctx->lock);
|
||||
ret = lh_SSL_SESSION_retrieve(s->session_ctx->sessions, &data);
|
||||
if (ret != NULL) {
|
||||
/* don't allow other threads to steal it: */
|
||||
SSL_SESSION_up_ref(ret);
|
||||
}
|
||||
CRYPTO_THREAD_unlock(s->session_ctx->lock);
|
||||
if (ret == NULL)
|
||||
CRYPTO_atomic_add(&s->session_ctx->stats.sess_miss, 1, &discard,
|
||||
s->session_ctx->lock);
|
||||
}
|
||||
|
||||
if (ret == NULL && s->session_ctx->get_session_cb != NULL) {
|
||||
int copy = 1;
|
||||
|
||||
ret = s->session_ctx->get_session_cb(s, sess_id, sess_id_len, ©);
|
||||
|
||||
if (ret != NULL) {
|
||||
CRYPTO_atomic_add(&s->session_ctx->stats.sess_cb_hit, 1, &discard,
|
||||
s->session_ctx->lock);
|
||||
|
||||
/*
|
||||
* Increment reference count now if the session callback asks us
|
||||
* to do so (note that if the session structures returned by the
|
||||
* callback are shared between threads, it must handle the
|
||||
* reference count itself [i.e. copy == 0], or things won't be
|
||||
* thread-safe).
|
||||
*/
|
||||
if (copy)
|
||||
SSL_SESSION_up_ref(ret);
|
||||
|
||||
/*
|
||||
* Add the externally cached session to the internal cache as
|
||||
* well if and only if we are supposed to.
|
||||
*/
|
||||
if ((s->session_ctx->session_cache_mode &
|
||||
SSL_SESS_CACHE_NO_INTERNAL_STORE) == 0) {
|
||||
/*
|
||||
* Either return value of SSL_CTX_add_session should not
|
||||
* interrupt the session resumption process. The return
|
||||
* value is intentionally ignored.
|
||||
*/
|
||||
(void)SSL_CTX_add_session(s->session_ctx, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*-
|
||||
* ssl_get_prev attempts to find an SSL_SESSION to be used to resume this
|
||||
* connection. It is only called by servers.
|
||||
@@ -504,8 +572,11 @@ int ssl_get_prev_session(SSL *s, CLIENTHELLO_MSG *hello)
|
||||
goto err;
|
||||
case SSL_TICKET_NONE:
|
||||
case SSL_TICKET_EMPTY:
|
||||
if (hello->session_id_len > 0)
|
||||
if (hello->session_id_len > 0) {
|
||||
try_session_cache = 1;
|
||||
ret = lookup_sess_in_cache(s, hello->session_id,
|
||||
hello->session_id_len);
|
||||
}
|
||||
break;
|
||||
case SSL_TICKET_NO_DECRYPT:
|
||||
case SSL_TICKET_SUCCESS:
|
||||
@@ -514,67 +585,6 @@ int ssl_get_prev_session(SSL *s, CLIENTHELLO_MSG *hello)
|
||||
}
|
||||
}
|
||||
|
||||
if (try_session_cache &&
|
||||
ret == NULL &&
|
||||
!(s->session_ctx->session_cache_mode &
|
||||
SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)) {
|
||||
SSL_SESSION data;
|
||||
|
||||
data.ssl_version = s->version;
|
||||
memcpy(data.session_id, hello->session_id, hello->session_id_len);
|
||||
data.session_id_length = hello->session_id_len;
|
||||
|
||||
CRYPTO_THREAD_read_lock(s->session_ctx->lock);
|
||||
ret = lh_SSL_SESSION_retrieve(s->session_ctx->sessions, &data);
|
||||
if (ret != NULL) {
|
||||
/* don't allow other threads to steal it: */
|
||||
SSL_SESSION_up_ref(ret);
|
||||
}
|
||||
CRYPTO_THREAD_unlock(s->session_ctx->lock);
|
||||
if (ret == NULL)
|
||||
CRYPTO_atomic_add(&s->session_ctx->stats.sess_miss, 1, &discard,
|
||||
s->session_ctx->lock);
|
||||
}
|
||||
|
||||
if (try_session_cache &&
|
||||
ret == NULL && s->session_ctx->get_session_cb != NULL) {
|
||||
int copy = 1;
|
||||
|
||||
ret = s->session_ctx->get_session_cb(s, hello->session_id,
|
||||
hello->session_id_len,
|
||||
©);
|
||||
|
||||
if (ret != NULL) {
|
||||
CRYPTO_atomic_add(&s->session_ctx->stats.sess_cb_hit, 1, &discard,
|
||||
s->session_ctx->lock);
|
||||
|
||||
/*
|
||||
* Increment reference count now if the session callback asks us
|
||||
* to do so (note that if the session structures returned by the
|
||||
* callback are shared between threads, it must handle the
|
||||
* reference count itself [i.e. copy == 0], or things won't be
|
||||
* thread-safe).
|
||||
*/
|
||||
if (copy)
|
||||
SSL_SESSION_up_ref(ret);
|
||||
|
||||
/*
|
||||
* Add the externally cached session to the internal cache as
|
||||
* well if and only if we are supposed to.
|
||||
*/
|
||||
if (!
|
||||
(s->session_ctx->session_cache_mode &
|
||||
SSL_SESS_CACHE_NO_INTERNAL_STORE)) {
|
||||
/*
|
||||
* Either return value of SSL_CTX_add_session should not
|
||||
* interrupt the session resumption process. The return
|
||||
* value is intentionally ignored.
|
||||
*/
|
||||
SSL_CTX_add_session(s->session_ctx, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == NULL)
|
||||
goto err;
|
||||
|
||||
|
||||
+11
-1
@@ -33,6 +33,7 @@ int SSL_SESSION_print(BIO *bp, const SSL_SESSION *x)
|
||||
{
|
||||
size_t i;
|
||||
const char *s;
|
||||
int istls13 = (x->ssl_version == TLS1_3_VERSION);
|
||||
|
||||
if (x == NULL)
|
||||
goto err;
|
||||
@@ -70,7 +71,10 @@ int SSL_SESSION_print(BIO *bp, const SSL_SESSION *x)
|
||||
if (BIO_printf(bp, "%02X", x->sid_ctx[i]) <= 0)
|
||||
goto err;
|
||||
}
|
||||
if (BIO_puts(bp, "\n Master-Key: ") <= 0)
|
||||
if (istls13) {
|
||||
if (BIO_puts(bp, "\n Resumption PSK: ") <= 0)
|
||||
goto err;
|
||||
} else if (BIO_puts(bp, "\n Master-Key: ") <= 0)
|
||||
goto err;
|
||||
for (i = 0; i < x->master_key_length; i++) {
|
||||
if (BIO_printf(bp, "%02X", x->master_key[i]) <= 0)
|
||||
@@ -145,6 +149,12 @@ int SSL_SESSION_print(BIO *bp, const SSL_SESSION *x)
|
||||
x->flags & SSL_SESS_FLAG_EXTMS ? "yes" : "no") <= 0)
|
||||
goto err;
|
||||
|
||||
if (istls13) {
|
||||
if (BIO_printf(bp, " Max Early Data: %u\n",
|
||||
x->ext.max_early_data) <= 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 1;
|
||||
err:
|
||||
return 0;
|
||||
|
||||
@@ -1009,6 +1009,33 @@ int tls_parse_ctos_early_data(SSL *s, PACKET *pkt, unsigned int context,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static SSL_TICKET_STATUS tls_get_stateful_ticket(SSL *s, PACKET *tick,
|
||||
SSL_SESSION **sess)
|
||||
{
|
||||
SSL_SESSION *tmpsess = NULL;
|
||||
|
||||
switch (PACKET_remaining(tick)) {
|
||||
case 0:
|
||||
return SSL_TICKET_EMPTY;
|
||||
|
||||
case SSL_MAX_SSL_SESSION_ID_LENGTH:
|
||||
break;
|
||||
|
||||
default:
|
||||
return SSL_TICKET_NO_DECRYPT;
|
||||
}
|
||||
|
||||
tmpsess = lookup_sess_in_cache(s, PACKET_data(tick),
|
||||
SSL_MAX_SSL_SESSION_ID_LENGTH);
|
||||
|
||||
if (tmpsess == NULL)
|
||||
return SSL_TICKET_NO_DECRYPT;
|
||||
|
||||
s->ext.ticket_expected = 1;
|
||||
*sess = tmpsess;
|
||||
return SSL_TICKET_SUCCESS;
|
||||
}
|
||||
|
||||
int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
|
||||
size_t chainidx)
|
||||
{
|
||||
@@ -1132,9 +1159,18 @@ int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
|
||||
uint32_t ticket_age = 0, now, agesec, agems;
|
||||
int ret;
|
||||
|
||||
ret = tls_decrypt_ticket(s, PACKET_data(&identity),
|
||||
PACKET_remaining(&identity), NULL, 0,
|
||||
&sess);
|
||||
/*
|
||||
* If we are using anti-replay protection then we behave as if
|
||||
* SSL_OP_NO_TICKET is set - we are caching tickets anyway so there
|
||||
* is no point in using full stateless tickets.
|
||||
*/
|
||||
if ((s->options & SSL_OP_NO_TICKET) != 0
|
||||
|| s->max_early_data > 0)
|
||||
ret = tls_get_stateful_ticket(s, &identity, &sess);
|
||||
else
|
||||
ret = tls_decrypt_ticket(s, PACKET_data(&identity),
|
||||
PACKET_remaining(&identity), NULL, 0,
|
||||
&sess);
|
||||
|
||||
if (ret == SSL_TICKET_EMPTY) {
|
||||
SSLfatal(s, SSL_AD_DECODE_ERROR, SSL_F_TLS_PARSE_CTOS_PSK,
|
||||
|
||||
+238
-161
@@ -3740,7 +3740,44 @@ int tls_construct_server_certificate(SSL *s, WPACKET *pkt)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
|
||||
static int create_ticket_prequel(SSL *s, WPACKET *pkt, uint32_t age_add,
|
||||
unsigned char *tick_nonce)
|
||||
{
|
||||
/*
|
||||
* Ticket lifetime hint: For TLSv1.2 this is advisory only and we leave this
|
||||
* unspecified for resumed session (for simplicity).
|
||||
* In TLSv1.3 we reset the "time" field above, and always specify the
|
||||
* timeout.
|
||||
*/
|
||||
if (!WPACKET_put_bytes_u32(pkt,
|
||||
(s->hit && !SSL_IS_TLS13(s))
|
||||
? 0 : s->session->timeout)) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CREATE_TICKET_PREQUEL,
|
||||
ERR_R_INTERNAL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (SSL_IS_TLS13(s)) {
|
||||
if (!WPACKET_put_bytes_u32(pkt, age_add)
|
||||
|| !WPACKET_sub_memcpy_u8(pkt, tick_nonce, TICKET_NONCE_SIZE)) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CREATE_TICKET_PREQUEL,
|
||||
ERR_R_INTERNAL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Start the sub-packet for the actual ticket data */
|
||||
if (!WPACKET_start_sub_packet_u16(pkt)) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CREATE_TICKET_PREQUEL,
|
||||
ERR_R_INTERNAL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int construct_stateless_ticket(SSL *s, WPACKET *pkt, uint32_t age_add,
|
||||
unsigned char *tick_nonce)
|
||||
{
|
||||
unsigned char *senc = NULL;
|
||||
EVP_CIPHER_CTX *ctx = NULL;
|
||||
@@ -3753,14 +3790,197 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
|
||||
SSL_CTX *tctx = s->session_ctx;
|
||||
unsigned char iv[EVP_MAX_IV_LENGTH];
|
||||
unsigned char key_name[TLSEXT_KEYNAME_LENGTH];
|
||||
int iv_len;
|
||||
unsigned char tick_nonce[TICKET_NONCE_SIZE];
|
||||
int iv_len, ok = 0;
|
||||
size_t macoffset, macendoffset;
|
||||
|
||||
/* get session encoding length */
|
||||
slen_full = i2d_SSL_SESSION(s->session, NULL);
|
||||
/*
|
||||
* Some length values are 16 bits, so forget it if session is too
|
||||
* long
|
||||
*/
|
||||
if (slen_full == 0 || slen_full > 0xFF00) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
|
||||
ERR_R_INTERNAL_ERROR);
|
||||
goto err;
|
||||
}
|
||||
senc = OPENSSL_malloc(slen_full);
|
||||
if (senc == NULL) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
|
||||
SSL_F_CONSTRUCT_STATELESS_TICKET, ERR_R_MALLOC_FAILURE);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ctx = EVP_CIPHER_CTX_new();
|
||||
hctx = HMAC_CTX_new();
|
||||
if (ctx == NULL || hctx == NULL) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
|
||||
ERR_R_MALLOC_FAILURE);
|
||||
goto err;
|
||||
}
|
||||
|
||||
p = senc;
|
||||
if (!i2d_SSL_SESSION(s->session, &p)) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
|
||||
ERR_R_INTERNAL_ERROR);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* create a fresh copy (not shared with other threads) to clean up
|
||||
*/
|
||||
const_p = senc;
|
||||
sess = d2i_SSL_SESSION(NULL, &const_p, slen_full);
|
||||
if (sess == NULL) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
|
||||
ERR_R_INTERNAL_ERROR);
|
||||
goto err;
|
||||
}
|
||||
|
||||
slen = i2d_SSL_SESSION(sess, NULL);
|
||||
if (slen == 0 || slen > slen_full) {
|
||||
/* shouldn't ever happen */
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
|
||||
ERR_R_INTERNAL_ERROR);
|
||||
SSL_SESSION_free(sess);
|
||||
goto err;
|
||||
}
|
||||
p = senc;
|
||||
if (!i2d_SSL_SESSION(sess, &p)) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
|
||||
ERR_R_INTERNAL_ERROR);
|
||||
SSL_SESSION_free(sess);
|
||||
goto err;
|
||||
}
|
||||
SSL_SESSION_free(sess);
|
||||
|
||||
/*
|
||||
* Initialize HMAC and cipher contexts. If callback present it does
|
||||
* all the work otherwise use generated values from parent ctx.
|
||||
*/
|
||||
if (tctx->ext.ticket_key_cb) {
|
||||
/* if 0 is returned, write an empty ticket */
|
||||
int ret = tctx->ext.ticket_key_cb(s, key_name, iv, ctx,
|
||||
hctx, 1);
|
||||
|
||||
if (ret == 0) {
|
||||
|
||||
/* Put timeout and length */
|
||||
if (!WPACKET_put_bytes_u32(pkt, 0)
|
||||
|| !WPACKET_put_bytes_u16(pkt, 0)) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
|
||||
SSL_F_CONSTRUCT_STATELESS_TICKET,
|
||||
ERR_R_INTERNAL_ERROR);
|
||||
goto err;
|
||||
}
|
||||
OPENSSL_free(senc);
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
HMAC_CTX_free(hctx);
|
||||
return 1;
|
||||
}
|
||||
if (ret < 0) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
|
||||
SSL_R_CALLBACK_FAILED);
|
||||
goto err;
|
||||
}
|
||||
iv_len = EVP_CIPHER_CTX_iv_length(ctx);
|
||||
} else {
|
||||
const EVP_CIPHER *cipher = EVP_aes_256_cbc();
|
||||
|
||||
iv_len = EVP_CIPHER_iv_length(cipher);
|
||||
if (RAND_bytes(iv, iv_len) <= 0
|
||||
|| !EVP_EncryptInit_ex(ctx, cipher, NULL,
|
||||
tctx->ext.secure->tick_aes_key, iv)
|
||||
|| !HMAC_Init_ex(hctx, tctx->ext.secure->tick_hmac_key,
|
||||
sizeof(tctx->ext.secure->tick_hmac_key),
|
||||
EVP_sha256(), NULL)) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
|
||||
ERR_R_INTERNAL_ERROR);
|
||||
goto err;
|
||||
}
|
||||
memcpy(key_name, tctx->ext.tick_key_name,
|
||||
sizeof(tctx->ext.tick_key_name));
|
||||
}
|
||||
|
||||
if (!create_ticket_prequel(s, pkt, age_add, tick_nonce)) {
|
||||
/* SSLfatal() already called */
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!WPACKET_get_total_written(pkt, &macoffset)
|
||||
/* Output key name */
|
||||
|| !WPACKET_memcpy(pkt, key_name, sizeof(key_name))
|
||||
/* output IV */
|
||||
|| !WPACKET_memcpy(pkt, iv, iv_len)
|
||||
|| !WPACKET_reserve_bytes(pkt, slen + EVP_MAX_BLOCK_LENGTH,
|
||||
&encdata1)
|
||||
/* Encrypt session data */
|
||||
|| !EVP_EncryptUpdate(ctx, encdata1, &len, senc, slen)
|
||||
|| !WPACKET_allocate_bytes(pkt, len, &encdata2)
|
||||
|| encdata1 != encdata2
|
||||
|| !EVP_EncryptFinal(ctx, encdata1 + len, &lenfinal)
|
||||
|| !WPACKET_allocate_bytes(pkt, lenfinal, &encdata2)
|
||||
|| encdata1 + len != encdata2
|
||||
|| len + lenfinal > slen + EVP_MAX_BLOCK_LENGTH
|
||||
|| !WPACKET_get_total_written(pkt, &macendoffset)
|
||||
|| !HMAC_Update(hctx,
|
||||
(unsigned char *)s->init_buf->data + macoffset,
|
||||
macendoffset - macoffset)
|
||||
|| !WPACKET_reserve_bytes(pkt, EVP_MAX_MD_SIZE, &macdata1)
|
||||
|| !HMAC_Final(hctx, macdata1, &hlen)
|
||||
|| hlen > EVP_MAX_MD_SIZE
|
||||
|| !WPACKET_allocate_bytes(pkt, hlen, &macdata2)
|
||||
|| macdata1 != macdata2) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
|
||||
SSL_F_CONSTRUCT_STATELESS_TICKET, ERR_R_INTERNAL_ERROR);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Close the sub-packet created by create_ticket_prequel() */
|
||||
if (!WPACKET_close(pkt)) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATELESS_TICKET,
|
||||
ERR_R_INTERNAL_ERROR);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ok = 1;
|
||||
err:
|
||||
OPENSSL_free(senc);
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
HMAC_CTX_free(hctx);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int construct_stateful_ticket(SSL *s, WPACKET *pkt, uint32_t age_add,
|
||||
unsigned char *tick_nonce)
|
||||
{
|
||||
if (!create_ticket_prequel(s, pkt, age_add, tick_nonce)) {
|
||||
/* SSLfatal() already called */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!WPACKET_memcpy(pkt, s->session->session_id,
|
||||
s->session->session_id_length)
|
||||
|| !WPACKET_close(pkt)) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR, SSL_F_CONSTRUCT_STATEFUL_TICKET,
|
||||
ERR_R_INTERNAL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
|
||||
{
|
||||
SSL_CTX *tctx = s->session_ctx;
|
||||
unsigned char tick_nonce[TICKET_NONCE_SIZE];
|
||||
union {
|
||||
unsigned char age_add_c[sizeof(uint32_t)];
|
||||
uint32_t age_add;
|
||||
} age_add_u;
|
||||
|
||||
age_add_u.age_add = 0;
|
||||
|
||||
if (SSL_IS_TLS13(s)) {
|
||||
size_t i, hashlen;
|
||||
uint64_t nonce;
|
||||
@@ -3797,10 +4017,11 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
|
||||
cb(s, SSL_CB_HANDSHAKE_START, 1);
|
||||
}
|
||||
/*
|
||||
* If we already sent one NewSessionTicket then we need to take a copy
|
||||
* of it and create a new session from it.
|
||||
* If we already sent one NewSessionTicket, or we resumed then
|
||||
* s->session may already be in a cache and so we must not modify it.
|
||||
* Instead we need to take a copy of it and modify that.
|
||||
*/
|
||||
if (s->sent_tickets != 0) {
|
||||
if (s->sent_tickets != 0 || s->hit) {
|
||||
SSL_SESSION *new_sess = ssl_session_dup(s->session, 0);
|
||||
|
||||
if (new_sess == NULL) {
|
||||
@@ -3862,161 +4083,23 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
|
||||
tctx->generate_ticket_cb(s, tctx->ticket_cb_data) == 0)
|
||||
goto err;
|
||||
|
||||
/* get session encoding length */
|
||||
slen_full = i2d_SSL_SESSION(s->session, NULL);
|
||||
/*
|
||||
* Some length values are 16 bits, so forget it if session is too
|
||||
* long
|
||||
* If we are using anti-replay protection then we behave as if
|
||||
* SSL_OP_NO_TICKET is set - we are caching tickets anyway so there
|
||||
* is no point in using full stateless tickets.
|
||||
*/
|
||||
if (slen_full == 0 || slen_full > 0xFF00) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
|
||||
SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR);
|
||||
goto err;
|
||||
}
|
||||
senc = OPENSSL_malloc(slen_full);
|
||||
if (senc == NULL) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
|
||||
SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_MALLOC_FAILURE);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ctx = EVP_CIPHER_CTX_new();
|
||||
hctx = HMAC_CTX_new();
|
||||
if (ctx == NULL || hctx == NULL) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
|
||||
SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_MALLOC_FAILURE);
|
||||
goto err;
|
||||
}
|
||||
|
||||
p = senc;
|
||||
if (!i2d_SSL_SESSION(s->session, &p)) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
|
||||
SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* create a fresh copy (not shared with other threads) to clean up
|
||||
*/
|
||||
const_p = senc;
|
||||
sess = d2i_SSL_SESSION(NULL, &const_p, slen_full);
|
||||
if (sess == NULL) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
|
||||
SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR);
|
||||
goto err;
|
||||
}
|
||||
|
||||
slen = i2d_SSL_SESSION(sess, NULL);
|
||||
if (slen == 0 || slen > slen_full) {
|
||||
/* shouldn't ever happen */
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
|
||||
SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR);
|
||||
SSL_SESSION_free(sess);
|
||||
goto err;
|
||||
}
|
||||
p = senc;
|
||||
if (!i2d_SSL_SESSION(sess, &p)) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
|
||||
SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR);
|
||||
SSL_SESSION_free(sess);
|
||||
goto err;
|
||||
}
|
||||
SSL_SESSION_free(sess);
|
||||
|
||||
/*
|
||||
* Initialize HMAC and cipher contexts. If callback present it does
|
||||
* all the work otherwise use generated values from parent ctx.
|
||||
*/
|
||||
if (tctx->ext.ticket_key_cb) {
|
||||
/* if 0 is returned, write an empty ticket */
|
||||
int ret = tctx->ext.ticket_key_cb(s, key_name, iv, ctx,
|
||||
hctx, 1);
|
||||
|
||||
if (ret == 0) {
|
||||
|
||||
/* Put timeout and length */
|
||||
if (!WPACKET_put_bytes_u32(pkt, 0)
|
||||
|| !WPACKET_put_bytes_u16(pkt, 0)) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
|
||||
SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
|
||||
ERR_R_INTERNAL_ERROR);
|
||||
goto err;
|
||||
}
|
||||
OPENSSL_free(senc);
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
HMAC_CTX_free(hctx);
|
||||
return 1;
|
||||
}
|
||||
if (ret < 0) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
|
||||
SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
|
||||
SSL_R_CALLBACK_FAILED);
|
||||
if (((s->options & SSL_OP_NO_TICKET) != 0 || s->max_early_data > 0)
|
||||
&& SSL_IS_TLS13(s)) {
|
||||
if (!construct_stateful_ticket(s, pkt, age_add_u.age_add, tick_nonce)) {
|
||||
/* SSLfatal() already called */
|
||||
goto err;
|
||||
}
|
||||
iv_len = EVP_CIPHER_CTX_iv_length(ctx);
|
||||
} else {
|
||||
const EVP_CIPHER *cipher = EVP_aes_256_cbc();
|
||||
|
||||
iv_len = EVP_CIPHER_iv_length(cipher);
|
||||
if (RAND_bytes(iv, iv_len) <= 0
|
||||
|| !EVP_EncryptInit_ex(ctx, cipher, NULL,
|
||||
tctx->ext.secure->tick_aes_key, iv)
|
||||
|| !HMAC_Init_ex(hctx, tctx->ext.secure->tick_hmac_key,
|
||||
sizeof(tctx->ext.secure->tick_hmac_key),
|
||||
EVP_sha256(), NULL)) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
|
||||
SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET,
|
||||
ERR_R_INTERNAL_ERROR);
|
||||
goto err;
|
||||
}
|
||||
memcpy(key_name, tctx->ext.tick_key_name,
|
||||
sizeof(tctx->ext.tick_key_name));
|
||||
}
|
||||
|
||||
/*
|
||||
* Ticket lifetime hint: For TLSv1.2 this is advisory only and we leave this
|
||||
* unspecified for resumed session (for simplicity).
|
||||
* In TLSv1.3 we reset the "time" field above, and always specify the
|
||||
* timeout.
|
||||
*/
|
||||
if (!WPACKET_put_bytes_u32(pkt,
|
||||
(s->hit && !SSL_IS_TLS13(s))
|
||||
? 0 : s->session->timeout)
|
||||
|| (SSL_IS_TLS13(s)
|
||||
&& (!WPACKET_put_bytes_u32(pkt, age_add_u.age_add)
|
||||
|| !WPACKET_sub_memcpy_u8(pkt, tick_nonce,
|
||||
TICKET_NONCE_SIZE)))
|
||||
/* Now the actual ticket data */
|
||||
|| !WPACKET_start_sub_packet_u16(pkt)
|
||||
|| !WPACKET_get_total_written(pkt, &macoffset)
|
||||
/* Output key name */
|
||||
|| !WPACKET_memcpy(pkt, key_name, sizeof(key_name))
|
||||
/* output IV */
|
||||
|| !WPACKET_memcpy(pkt, iv, iv_len)
|
||||
|| !WPACKET_reserve_bytes(pkt, slen + EVP_MAX_BLOCK_LENGTH,
|
||||
&encdata1)
|
||||
/* Encrypt session data */
|
||||
|| !EVP_EncryptUpdate(ctx, encdata1, &len, senc, slen)
|
||||
|| !WPACKET_allocate_bytes(pkt, len, &encdata2)
|
||||
|| encdata1 != encdata2
|
||||
|| !EVP_EncryptFinal(ctx, encdata1 + len, &lenfinal)
|
||||
|| !WPACKET_allocate_bytes(pkt, lenfinal, &encdata2)
|
||||
|| encdata1 + len != encdata2
|
||||
|| len + lenfinal > slen + EVP_MAX_BLOCK_LENGTH
|
||||
|| !WPACKET_get_total_written(pkt, &macendoffset)
|
||||
|| !HMAC_Update(hctx,
|
||||
(unsigned char *)s->init_buf->data + macoffset,
|
||||
macendoffset - macoffset)
|
||||
|| !WPACKET_reserve_bytes(pkt, EVP_MAX_MD_SIZE, &macdata1)
|
||||
|| !HMAC_Final(hctx, macdata1, &hlen)
|
||||
|| hlen > EVP_MAX_MD_SIZE
|
||||
|| !WPACKET_allocate_bytes(pkt, hlen, &macdata2)
|
||||
|| macdata1 != macdata2
|
||||
|| !WPACKET_close(pkt)) {
|
||||
SSLfatal(s, SSL_AD_INTERNAL_ERROR,
|
||||
SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR);
|
||||
} else if (!construct_stateless_ticket(s, pkt, age_add_u.age_add,
|
||||
tick_nonce)) {
|
||||
/* SSLfatal() already called */
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (SSL_IS_TLS13(s)) {
|
||||
if (!tls_construct_extensions(s, pkt,
|
||||
SSL_EXT_TLS1_3_NEW_SESSION_TICKET,
|
||||
@@ -4033,15 +4116,9 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
|
||||
s->next_ticket_nonce++;
|
||||
ssl_update_cache(s, SSL_SESS_CACHE_SERVER);
|
||||
}
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
HMAC_CTX_free(hctx);
|
||||
OPENSSL_free(senc);
|
||||
|
||||
return 1;
|
||||
err:
|
||||
OPENSSL_free(senc);
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
HMAC_CTX_free(hctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user