From 9d0a8bb71e3e411e9183e635122f17c1429c4116 Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Wed, 31 Jan 2018 17:26:46 +0000 Subject: [PATCH] Enable the ability to set the number of TLSv1.3 session tickets sent We send a session ticket automatically in TLSv1.3 at the end of the handshake. This commit provides the ability to set how many tickets should be sent. By default this is one. Fixes #4978 Reviewed-by: Viktor Dukhovni Reviewed-by: Rich Salz (Merged from https://github.com/openssl/openssl/pull/5227) --- include/openssl/ssl.h | 5 ++++ ssl/ssl_lib.c | 28 +++++++++++++++++ ssl/ssl_locl.h | 9 ++++++ ssl/statem/statem_srvr.c | 65 +++++++++++++++++++++++++++++----------- util/libssl.num | 4 +++ 5 files changed, 93 insertions(+), 18 deletions(-) diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 1f4f2616b6..db0a2d5d82 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -2095,6 +2095,11 @@ void SSL_set_record_padding_callback_arg(SSL *ssl, void *arg); void *SSL_get_record_padding_callback_arg(SSL *ssl); int SSL_set_block_padding(SSL *ssl, size_t block_size); +int SSL_set_num_tickets(SSL *s, size_t num_tickets); +size_t SSL_get_num_tickets(SSL *s); +int SSL_CTX_set_num_tickets(SSL_CTX *ctx, size_t num_tickets); +size_t SSL_CTX_get_num_tickets(SSL_CTX *ctx); + # if OPENSSL_API_COMPAT < 0x10100000L # define SSL_cache_hit(s) SSL_session_reused(s) # endif diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 41574c4bf2..2c29d7f61c 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -699,6 +699,7 @@ SSL *SSL_new(SSL_CTX *ctx) s->mode = ctx->mode; s->max_cert_list = ctx->max_cert_list; s->max_early_data = ctx->max_early_data; + s->num_tickets = ctx->num_tickets; /* Shallow copy of the ciphersuites stack */ s->tls13_ciphersuites = sk_SSL_CIPHER_dup(ctx->tls13_ciphersuites); @@ -3033,6 +3034,9 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth) */ ret->max_early_data = 0; + /* By default we send one session ticket automatically in TLSv1.3 */ + ret->num_tickets = 1; + ssl_ctx_system_config(ret); return ret; @@ -4314,6 +4318,30 @@ int SSL_set_block_padding(SSL *ssl, size_t block_size) return 1; } +int SSL_set_num_tickets(SSL *s, size_t num_tickets) +{ + s->num_tickets = num_tickets; + + return 1; +} + +size_t SSL_get_num_tickets(SSL *s) +{ + return s->num_tickets; +} + +int SSL_CTX_set_num_tickets(SSL_CTX *ctx, size_t num_tickets) +{ + ctx->num_tickets = num_tickets; + + return 1; +} + +size_t SSL_CTX_get_num_tickets(SSL_CTX *ctx) +{ + return ctx->num_tickets; +} + /* * Allocates new EVP_MD_CTX and sets pointer to it into given pointer * variable, freeing EVP_MD_CTX previously stored in that variable, if any. diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index e02f5a1839..4aec810179 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -1049,6 +1049,9 @@ struct ssl_ctx_st { SSL_CTX_generate_session_ticket_fn generate_ticket_cb; SSL_CTX_decrypt_session_ticket_fn decrypt_ticket_cb; void *ticket_cb_data; + + /* The number of TLS1.3 tickets to automatically send */ + size_t num_tickets; }; struct ssl_st { @@ -1418,6 +1421,12 @@ struct ssl_st { size_t block_padding; CRYPTO_RWLOCK *lock; + RAND_DRBG *drbg; + + /* The number of TLS1.3 tickets to automatically send */ + size_t num_tickets; + /* The number of TLS1.3 tickets actually sent so far */ + size_t sent_tickets; }; /* diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index 22786bed13..dfeba173a7 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -480,13 +480,9 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s) case TLS_ST_SR_FINISHED: /* * Technically we have finished the handshake at this point, but we're - * going to remain "in_init" for now and write out the session ticket + * going to remain "in_init" for now and write out any session tickets * immediately. - * TODO(TLS1.3): Perhaps we need to be able to control this behaviour - * and give the application the opportunity to delay sending the - * session ticket? */ - st->hand_state = TLS_ST_SW_SESSION_TICKET; if (s->post_handshake_auth == SSL_PHA_REQUESTED) { s->post_handshake_auth = SSL_PHA_EXT_RECEIVED; } else if (!s->ext.ticket_expected) { @@ -495,7 +491,12 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s) * handshake at this point. */ st->hand_state = TLS_ST_OK; + return WRITE_TRAN_CONTINUE; } + if (s->num_tickets > s->sent_tickets) + st->hand_state = TLS_ST_SW_SESSION_TICKET; + else + st->hand_state = TLS_ST_OK; return WRITE_TRAN_CONTINUE; case TLS_ST_SR_KEY_UPDATE: @@ -507,7 +508,14 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s) case TLS_ST_SW_KEY_UPDATE: case TLS_ST_SW_SESSION_TICKET: - st->hand_state = TLS_ST_OK; + /* In a resumption we only ever send a maximum of one new ticket. + * Following an initial handshake we send the number of tickets we have + * been configured for. + */ + if (s->hit || s->num_tickets <= s->sent_tickets) { + /* We've written enough tickets out. */ + st->hand_state = TLS_ST_OK; + } return WRITE_TRAN_CONTINUE; } } @@ -3743,21 +3751,41 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt) } age_add_u; if (SSL_IS_TLS13(s)) { - if (s->post_handshake_auth != SSL_PHA_EXT_RECEIVED) { - void (*cb) (const SSL *ssl, int type, int val) = NULL; + void (*cb) (const SSL *ssl, int type, int val) = NULL; + + if (s->info_callback != NULL) + cb = s->info_callback; + else if (s->ctx->info_callback != NULL) + cb = s->ctx->info_callback; + + if (cb != NULL) { /* - * This is the first session ticket we've sent. In the state - * machine we "cheated" and tacked this onto the end of the first - * handshake. From an info callback perspective this should appear - * like the start of a new handshake. + * We don't start and stop the handshake in between each ticket when + * sending more than one - but it should appear that way to the info + * callback. */ - if (s->info_callback != NULL) - cb = s->info_callback; - else if (s->ctx->info_callback != NULL) - cb = s->ctx->info_callback; - if (cb != NULL) - cb(s, SSL_CB_HANDSHAKE_START, 1); + if (s->sent_tickets != 0) { + ossl_statem_set_in_init(s, 0); + cb(s, SSL_CB_HANDSHAKE_DONE, 1); + ossl_statem_set_in_init(s, 1); + } + 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 (s->sent_tickets != 0) { + SSL_SESSION *new_sess = ssl_session_dup(s->session, 0); + + if (new_sess == NULL) { + /* SSLfatal already called */ + goto err; + } + + SSL_SESSION_free(s->session); + s->session = new_sess; } if (!ssl_generate_session_id(s, s->session)) { @@ -3968,6 +3996,7 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt) /* SSLfatal() already called */ goto err; } + s->sent_tickets++; } EVP_CIPHER_CTX_free(ctx); HMAC_CTX_free(hctx); diff --git a/util/libssl.num b/util/libssl.num index 344d684a94..3495903e87 100644 --- a/util/libssl.num +++ b/util/libssl.num @@ -486,3 +486,7 @@ SSL_CTX_set_stateless_cookie_generate_cb 486 1_1_1 EXIST::FUNCTION: SSL_CTX_set_stateless_cookie_verify_cb 487 1_1_1 EXIST::FUNCTION: SSL_CTX_set_ciphersuites 488 1_1_1 EXIST::FUNCTION: SSL_set_ciphersuites 489 1_1_1 EXIST::FUNCTION: +SSL_set_num_tickets 490 1_1_1 EXIST::FUNCTION: +SSL_CTX_get_num_tickets 491 1_1_1 EXIST::FUNCTION: +SSL_get_num_tickets 492 1_1_1 EXIST::FUNCTION: +SSL_CTX_set_num_tickets 493 1_1_1 EXIST::FUNCTION: -- 2.25.1