Commit 9d0a8bb7 authored by Matt Caswell's avatar Matt Caswell
Browse files

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: default avatarViktor Dukhovni <viktor@openssl.org>
Reviewed-by: default avatarRich Salz <rsalz@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/5227)
parent 029c11c2
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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
+28 −0
Original line number Diff line number Diff line
@@ -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.
+9 −0
Original line number Diff line number Diff line
@@ -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;
};

/*
+47 −18
Original line number Diff line number Diff line
@@ -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:
        /* 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,22 +3751,42 @@ 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;

            /*
             * 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.
             */
        if (s->info_callback != NULL)
            cb = s->info_callback;
        else if (s->ctx->info_callback != NULL)
            cb = s->ctx->info_callback;
            if (cb != NULL)


        if (cb != NULL) {
            /*
             * 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->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)) {
            /* SSLfatal() already called */
@@ -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);
+4 −0
Original line number Diff line number Diff line
@@ -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: