diff --git a/doc/man3/SSL_CTX_set_num_tickets.pod b/doc/man3/SSL_CTX_set_num_tickets.pod index aa673bd8d0759..7ab62d3ad3205 100644 --- a/doc/man3/SSL_CTX_set_num_tickets.pod +++ b/doc/man3/SSL_CTX_set_num_tickets.pod @@ -45,17 +45,22 @@ sent. To issue tickets after other events (such as application-layer changes), SSL_new_session_ticket() is used by a server application to request that a new ticket be sent when it is safe to do so. New tickets are only allowed to be -sent in this manner after the initial handshake has completed, and only for TLS -1.3 connections. The ticket generation and transmission are delayed until the -server is starting a new write operation, so that it is bundled with other -application data being written and properly aligned to a record boundary. -SSL_new_session_ticket() can be called more than once to request additional -tickets be sent; all such requests are queued and written together when it is -safe to do so. Note that a successful return from SSL_new_session_ticket() -indicates only that the request to send a ticket was processed, not that the -ticket itself was sent. To be notified when the ticket itself is sent, a -new-session callback can be registered with L that -will be invoked as the ticket or tickets are generated. +sent in this manner after the initial handshake has completed, and only for +TLS 1.3 connections. By default, the ticket generation and transmission are +delayed until the server is starting a new write operation, so that it is +bundled with other application data being written and properly aligned to a +record boundary. If the connection was at a record boundary when +SSL_new_session_ticket() was called, the ticket can be sent immediately +(without waiting for the next application write) by calling +SSL_do_handshake(). SSL_new_session_ticket() can be called more than once to +request additional tickets be sent; all such requests are queued and written +together when it is safe to do so and triggered by SSL_write() or +SSL_do_handshake(). Note that a successful return from +SSL_new_session_ticket() indicates only that the request to send a ticket was +processed, not that the ticket itself was sent. To be notified when the +ticket itself is sent, a new-session callback can be registered with +L that will be invoked as the ticket or tickets +are generated. SSL_CTX_get_num_tickets() and SSL_get_num_tickets() return the number of tickets set by a previous call to SSL_CTX_set_num_tickets() or diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index e76f8e0479280..25ea794c28218 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -2360,10 +2360,14 @@ int SSL_renegotiate_pending(const SSL *s) int SSL_new_session_ticket(SSL *s) { - if (SSL_in_init(s) || SSL_IS_FIRST_HANDSHAKE(s) || !s->server + /* If we are in init because we're sending tickets, okay to send more. */ + if ((SSL_in_init(s) && s->ext.extra_tickets_expected == 0) + || SSL_IS_FIRST_HANDSHAKE(s) || !s->server || !SSL_IS_TLS13(s)) return 0; s->ext.extra_tickets_expected++; + if (s->rlayer.wbuf[0].left == 0 && !SSL_in_init(s)) + ossl_statem_set_in_init(s, 1); return 1; } diff --git a/test/sslapitest.c b/test/sslapitest.c index 9433bf48ed1ad..e1d617b89ba0f 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -2483,11 +2483,22 @@ static int test_extra_tickets(int idx) || !TEST_int_eq(4, new_called)) goto end; + /* Once more, but with SSL_do_handshake() to drive the ticket generation */ + c = '4'; + new_called = 0; + if (!TEST_true(SSL_new_session_ticket(serverssl)) + || !TEST_true(SSL_new_session_ticket(serverssl)) + || !TEST_true(SSL_do_handshake(serverssl)) + || !TEST_int_eq(2, new_called) + || !TEST_false(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes)) + || !TEST_int_eq(4, new_called)) + goto end; + /* * Use the always-retry BIO to exercise the logic that forces ticket * generation to wait until a record boundary. */ - c = '4'; + c = '5'; new_called = 0; tmp = SSL_get_wbio(serverssl); if (!TEST_ptr(tmp) || !TEST_true(BIO_up_ref(tmp))) { @@ -2503,9 +2514,14 @@ static int test_extra_tickets(int idx) /* Restore a BIO that will let the write succeed */ SSL_set0_wbio(serverssl, tmp); tmp = NULL; - /* These calls should just queue the request and not send anything. */ + /* + * These calls should just queue the request and not send anything + * even if we explicitly try to hit the state machine. + */ if (!TEST_true(SSL_new_session_ticket(serverssl)) || !TEST_true(SSL_new_session_ticket(serverssl)) + || !TEST_int_eq(0, new_called) + || !TEST_true(SSL_do_handshake(serverssl)) || !TEST_int_eq(0, new_called)) goto end; /* Re-do the write; still no tickets sent */ @@ -2518,8 +2534,12 @@ static int test_extra_tickets(int idx) || !TEST_int_eq(c, buf[0]) || !TEST_false(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes))) goto end; + /* Even trying to hit the state machine now will still not send tickets */ + if (!TEST_true(SSL_do_handshake(serverssl)) + || !TEST_int_eq(0, new_called)) + goto end; /* Now the *next* write should send the tickets */ - c = '5'; + c = '6'; if (!TEST_true(SSL_write_ex(serverssl, &c, 1, &nbytes)) || !TEST_size_t_eq(1, nbytes) || !TEST_int_eq(2, new_called)