[openssl] master update

kaduk at mit.edu kaduk at mit.edu
Fri May 1 22:38:21 UTC 2020


The branch master has been updated
       via  e908f292de6383c5d4dbece9381b523d4ce41c69 (commit)
       via  f0049b86cc5d745af97c087e54abaeb00de40b6b (commit)
       via  3bfacb5fd4679812a7b9ec61d296b1add64669c0 (commit)
       via  6250282f7fc37c5903d051174a69053a80e1b1bd (commit)
      from  90113096186e185cd07ee1c4c7267d0e68e67793 (commit)


- Log -----------------------------------------------------------------
commit e908f292de6383c5d4dbece9381b523d4ce41c69
Author: Benjamin Kaduk <bkaduk at akamai.com>
Date:   Wed Apr 1 17:08:10 2020 -0700

    make update for SSL_new_session_ticket
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/11416)

commit f0049b86cc5d745af97c087e54abaeb00de40b6b
Author: Benjamin Kaduk <bkaduk at akamai.com>
Date:   Tue Mar 17 10:08:11 2020 -0700

    Add test for SSL_new_session_ticket()
    
    Run a normal handshake and then request some extra tickets,
    checking that the new_session_cb is called the expected number of
    times.  Since the tickets are generated in the same way as other
    tickets, there should not be a need to verify that these specific ones
    can be used to resume.
    
    Run the test with both zero and a non-zero number of tickets issued in the
    initial handshake.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/11416)

commit 3bfacb5fd4679812a7b9ec61d296b1add64669c0
Author: Benjamin Kaduk <bkaduk at akamai.com>
Date:   Mon Mar 16 11:25:58 2020 -0700

    Add SSL_new_session_ticket() API
    
    This API requests that the TLS stack generate a (TLS 1.3)
    NewSessionTicket message the next time it is safe to do so (i.e., we do
    not have other data pending write, which could be mid-record).  For
    efficiency, defer actually generating/writing the ticket until there
    is other data to write, to avoid producing server-to-client traffic when
    not needed.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/11416)

commit 6250282f7fc37c5903d051174a69053a80e1b1bd
Author: Benjamin Kaduk <bkaduk at akamai.com>
Date:   Mon Mar 16 11:25:12 2020 -0700

    Fix whitespace nit in ossl_statem_server_pre_work
    
    An 'if' clause was nestled against a previous closing brace as it if was
    an 'else if', but should properly stand on its own line.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/11416)

-----------------------------------------------------------------------

Summary of changes:
 doc/man3/SSL_CTX_set_num_tickets.pod |  27 ++++++-
 include/openssl/ssl.h                |   1 +
 ssl/record/rec_layer_s3.c            |   8 +-
 ssl/ssl_lib.c                        |   9 +++
 ssl/ssl_local.h                      |   2 +
 ssl/statem/statem_srvr.c             |  19 ++++-
 test/sslapitest.c                    | 143 +++++++++++++++++++++++++++++++++++
 util/libssl.num                      |   1 +
 8 files changed, 199 insertions(+), 11 deletions(-)

diff --git a/doc/man3/SSL_CTX_set_num_tickets.pod b/doc/man3/SSL_CTX_set_num_tickets.pod
index 84afe4ae6a..5d4bd6f5be 100644
--- a/doc/man3/SSL_CTX_set_num_tickets.pod
+++ b/doc/man3/SSL_CTX_set_num_tickets.pod
@@ -5,7 +5,8 @@
 SSL_set_num_tickets,
 SSL_get_num_tickets,
 SSL_CTX_set_num_tickets,
-SSL_CTX_get_num_tickets
+SSL_CTX_get_num_tickets,
+SSL_new_session_ticket
 - control the number of TLSv1.3 session tickets that are issued
 
 =head1 SYNOPSIS
@@ -16,6 +17,7 @@ SSL_CTX_get_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);
+ int SSL_new_session_ticket(SSL *s);
 
 =head1 DESCRIPTION
 
@@ -40,14 +42,29 @@ handshake then SSL_set_num_tickets() can be called again prior to calling
 SSL_verify_client_post_handshake() to update the number of tickets that will be
 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<SSL_CTX_sess_set_new_cb(3)> 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
 SSL_set_num_tickets(), or 2 if no such call has been made.
 
 =head1 RETURN VALUES
 
-SSL_CTX_set_num_tickets() and SSL_set_num_tickets() return 1 on success or 0 on
-failure.
+SSL_CTX_set_num_tickets(), SSL_set_num_tickets(), and
+SSL_new_session_ticket() return 1 on success or 0 on failure.
 
 SSL_CTX_get_num_tickets() and SSL_get_num_tickets() return the number of tickets
 that have been previously set.
@@ -58,7 +75,9 @@ L<ssl(7)>
 
 =head1 HISTORY
 
-These functions were added in OpenSSL 1.1.1.
+SSL_new_session_ticket() was added in OpenSSL 3.0.0.
+SSL_set_num_tickets(), SSL_get_num_tickets(), SSL_CTX_set_num_tickets(), and
+SSL_CTX_get_num_tickets() were added in OpenSSL 1.1.1.
 
 =head1 COPYRIGHT
 
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 5c2affb690..f293b035e3 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -1959,6 +1959,7 @@ int SSL_get_key_update_type(const SSL *s);
 int SSL_renegotiate(SSL *s);
 int SSL_renegotiate_abbreviated(SSL *s);
 __owur int SSL_renegotiate_pending(const SSL *s);
+int SSL_new_session_ticket(SSL *s);
 int SSL_shutdown(SSL *s);
 __owur int SSL_verify_client_post_handshake(SSL *s);
 void SSL_CTX_set_post_handshake_auth(SSL_CTX *ctx, int val);
diff --git a/ssl/record/rec_layer_s3.c b/ssl/record/rec_layer_s3.c
index d4198917d0..bceac72051 100644
--- a/ssl/record/rec_layer_s3.c
+++ b/ssl/record/rec_layer_s3.c
@@ -384,10 +384,12 @@ int ssl3_write_bytes(SSL *s, int type, const void *buf_, size_t len,
     s->rlayer.wnum = 0;
 
     /*
-     * If we are supposed to be sending a KeyUpdate then go into init unless we
-     * have writes pending - in which case we should finish doing that first.
+     * If we are supposed to be sending a KeyUpdate or NewSessionTicket then go
+     * into init unless we have writes pending - in which case we should finish
+     * doing that first.
      */
-    if (wb->left == 0 && s->key_update != SSL_KEY_UPDATE_NONE)
+    if (wb->left == 0 && (s->key_update != SSL_KEY_UPDATE_NONE
+                          || s->ext.extra_tickets_expected > 0))
         ossl_statem_set_in_init(s, 1);
 
     /*
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 63cbb3d904..fde726e0ba 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -2303,6 +2303,15 @@ int SSL_renegotiate_pending(const SSL *s)
     return (s->renegotiate != 0);
 }
 
+int SSL_new_session_ticket(SSL *s)
+{
+        if (SSL_in_init(s) || SSL_IS_FIRST_HANDSHAKE(s) || !s->server
+                || !SSL_IS_TLS13(s))
+            return 0;
+        s->ext.extra_tickets_expected++;
+        return 1;
+}
+
 long SSL_ctrl(SSL *s, int cmd, long larg, void *parg)
 {
     long l;
diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h
index 21bf482bef..de7e9fde48 100644
--- a/ssl/ssl_local.h
+++ b/ssl/ssl_local.h
@@ -1539,6 +1539,8 @@ struct ssl_st {
 
         /* RFC4507 session ticket expected to be received or sent */
         int ticket_expected;
+	/* TLS 1.3 tickets requested by the application. */
+	int extra_tickets_expected;
 # ifndef OPENSSL_NO_EC
         size_t ecpointformats_len;
         /* our list */
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index c463f22ce2..d1d86ea5e6 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -441,6 +441,10 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s)
             st->hand_state = TLS_ST_SW_CERT_REQ;
             return WRITE_TRAN_CONTINUE;
         }
+        if (s->ext.extra_tickets_expected > 0) {
+            st->hand_state = TLS_ST_SW_SESSION_TICKET;
+            return WRITE_TRAN_CONTINUE;
+        }
         /* Try to read from the client instead */
         return WRITE_TRAN_FINISHED;
 
@@ -531,7 +535,9 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s)
          * Following an initial handshake we send the number of tickets we have
          * been configured for.
          */
-        if (s->hit || s->num_tickets <= s->sent_tickets) {
+        if (!SSL_IS_FIRST_HANDSHAKE(s) && s->ext.extra_tickets_expected > 0) {
+            return WRITE_TRAN_CONTINUE;
+        } else if (s->hit || s->num_tickets <= s->sent_tickets) {
             /* We've written enough tickets out. */
             st->hand_state = TLS_ST_OK;
         }
@@ -727,7 +733,8 @@ WORK_STATE ossl_statem_server_pre_work(SSL *s, WORK_STATE wst)
         return WORK_FINISHED_CONTINUE;
 
     case TLS_ST_SW_SESSION_TICKET:
-        if (SSL_IS_TLS13(s) && s->sent_tickets == 0) {
+        if (SSL_IS_TLS13(s) && s->sent_tickets == 0
+                && s->ext.extra_tickets_expected == 0) {
             /*
              * Actually this is the end of the handshake, but we're going
              * straight into writing the session ticket out. So we finish off
@@ -736,7 +743,8 @@ WORK_STATE ossl_statem_server_pre_work(SSL *s, WORK_STATE wst)
              * Calls SSLfatal as required.
              */
             return tls_finish_handshake(s, wst, 0, 0);
-        } if (SSL_IS_DTLS(s)) {
+        }
+        if (SSL_IS_DTLS(s)) {
             /*
              * We're into the last flight. We don't retransmit the last flight
              * unless we need to, so we don't use the timer
@@ -4160,10 +4168,13 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
         /*
          * Increment both |sent_tickets| and |next_ticket_nonce|. |sent_tickets|
          * gets reset to 0 if we send more tickets following a post-handshake
-         * auth, but |next_ticket_nonce| does not.
+         * auth, but |next_ticket_nonce| does not.  If we're sending extra
+         * tickets, decrement the count of pending extra tickets.
          */
         s->sent_tickets++;
         s->next_ticket_nonce++;
+        if (s->ext.extra_tickets_expected > 0)
+            s->ext.extra_tickets_expected--;
         ssl_update_cache(s, SSL_SESS_CACHE_SERVER);
     }
 
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 779176a0f1..b8bad61fd2 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -2093,6 +2093,148 @@ static int test_psk_tickets(void)
 
     return testresult;
 }
+
+static int test_extra_tickets(int idx)
+{
+    SSL_CTX *sctx = NULL, *cctx = NULL;
+    SSL *serverssl = NULL, *clientssl = NULL;
+    BIO *bretry = BIO_new(bio_s_always_retry());
+    BIO *tmp = NULL;
+    int testresult = 0;
+    int stateful = 0;
+    size_t nbytes;
+    unsigned char c, buf[1];
+
+    new_called = 0;
+    do_cache = 1;
+
+    if (idx >= 3) {
+        idx -= 3;
+        stateful = 1;
+    }
+
+    if (!TEST_ptr(bretry) || !setup_ticket_test(stateful, idx, &sctx, &cctx))
+        goto end;
+    SSL_CTX_sess_set_new_cb(sctx, new_session_cb);
+    /* setup_ticket_test() uses new_cachesession_cb which we don't need. */
+    SSL_CTX_sess_set_new_cb(cctx, new_session_cb);
+
+    if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl,
+                                          &clientssl, NULL, NULL)))
+        goto end;
+
+    /*
+     * Note that we have new_session_cb on both sctx and cctx, so new_called is
+     * incremented by both client and server.
+     */
+    if (!TEST_true(create_ssl_connection(serverssl, clientssl,
+                                                SSL_ERROR_NONE))
+               /* Check we got the number of tickets we were expecting */
+            || !TEST_int_eq(idx * 2, new_called)
+            || !TEST_true(SSL_new_session_ticket(serverssl))
+            || !TEST_true(SSL_new_session_ticket(serverssl))
+            || !TEST_int_eq(idx * 2, new_called))
+        goto end;
+
+    /* Now try a (real) write to actually send the tickets */
+    c = '1';
+    if (!TEST_true(SSL_write_ex(serverssl, &c, 1, &nbytes))
+            || !TEST_size_t_eq(1, nbytes)
+            || !TEST_int_eq(idx * 2 + 2, new_called)
+            || !TEST_true(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes))
+            || !TEST_int_eq(idx * 2 + 4, new_called)
+            || !TEST_int_eq(sizeof(buf), nbytes)
+            || !TEST_int_eq(c, buf[0])
+            || !TEST_false(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes)))
+        goto end;
+
+    /* Try with only requesting one new ticket, too */
+    c = '2';
+    new_called = 0;
+    if (!TEST_true(SSL_new_session_ticket(serverssl))
+            || !TEST_true(SSL_write_ex(serverssl, &c, sizeof(c), &nbytes))
+            || !TEST_size_t_eq(sizeof(c), nbytes)
+            || !TEST_int_eq(1, new_called)
+            || !TEST_true(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes))
+            || !TEST_int_eq(2, new_called)
+            || !TEST_size_t_eq(sizeof(buf), nbytes)
+            || !TEST_int_eq(c, buf[0]))
+        goto end;
+
+    /* Do it again but use dummy writes to drive the ticket generation */
+    c = '3';
+    new_called = 0;
+    if (!TEST_true(SSL_new_session_ticket(serverssl))
+            || !TEST_true(SSL_new_session_ticket(serverssl))
+            || !TEST_true(SSL_write_ex(serverssl, &c, 0, &nbytes))
+            || !TEST_size_t_eq(0, nbytes)
+            || !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';
+    new_called = 0;
+    tmp = SSL_get_wbio(serverssl);
+    if (!TEST_ptr(tmp) || !TEST_true(BIO_up_ref(tmp))) {
+        tmp = NULL;
+        goto end;
+    }
+    SSL_set0_wbio(serverssl, bretry);
+    bretry = NULL;
+    if (!TEST_false(SSL_write_ex(serverssl, &c, 1, &nbytes))
+            || !TEST_int_eq(SSL_get_error(serverssl, 0), SSL_ERROR_WANT_WRITE)
+            || !TEST_size_t_eq(nbytes, 0))
+        goto end;
+    /* 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. */
+    if (!TEST_true(SSL_new_session_ticket(serverssl))
+            || !TEST_true(SSL_new_session_ticket(serverssl))
+            || !TEST_int_eq(0, new_called))
+        goto end;
+    /* Re-do the write; still no tickets sent */
+    if (!TEST_true(SSL_write_ex(serverssl, &c, 1, &nbytes))
+            || !TEST_size_t_eq(1, nbytes)
+            || !TEST_int_eq(0, new_called)
+            || !TEST_true(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes))
+            || !TEST_int_eq(0, new_called)
+            || !TEST_int_eq(sizeof(buf), nbytes)
+            || !TEST_int_eq(c, buf[0])
+            || !TEST_false(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes)))
+        goto end;
+    /* Now the *next* write should send the tickets */
+    c = '5';
+    if (!TEST_true(SSL_write_ex(serverssl, &c, 1, &nbytes))
+            || !TEST_size_t_eq(1, nbytes)
+            || !TEST_int_eq(2, new_called)
+            || !TEST_true(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes))
+            || !TEST_int_eq(4, new_called)
+            || !TEST_int_eq(sizeof(buf), nbytes)
+            || !TEST_int_eq(c, buf[0])
+            || !TEST_false(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes)))
+        goto end;
+
+    SSL_shutdown(clientssl);
+    SSL_shutdown(serverssl);
+    testresult = 1;
+
+ end:
+    BIO_free(bretry);
+    BIO_free(tmp);
+    SSL_free(serverssl);
+    SSL_free(clientssl);
+    SSL_CTX_free(sctx);
+    SSL_CTX_free(cctx);
+    clientssl = serverssl = NULL;
+    sctx = cctx = NULL;
+    return testresult;
+}
 #endif
 
 #define USE_NULL            0
@@ -7395,6 +7537,7 @@ int setup_tests(void)
     ADD_ALL_TESTS(test_stateful_tickets, 3);
     ADD_ALL_TESTS(test_stateless_tickets, 3);
     ADD_TEST(test_psk_tickets);
+    ADD_ALL_TESTS(test_extra_tickets, 6);
 #endif
     ADD_ALL_TESTS(test_ssl_set_bio, TOTAL_SSL_SET_BIO_TESTS);
     ADD_TEST(test_ssl_bio_pop_next_bio);
diff --git a/util/libssl.num b/util/libssl.num
index c2b162f3bd..b8e0982daa 100644
--- a/util/libssl.num
+++ b/util/libssl.num
@@ -513,3 +513,4 @@ SSL_CTX_load_verify_dir                 ?	3_0_0	EXIST::FUNCTION:
 SSL_CTX_load_verify_store               ?	3_0_0	EXIST::FUNCTION:
 SSL_CTX_set_tlsext_ticket_key_evp_cb    ?	3_0_0	EXIST::FUNCTION:
 SSL_CTX_new_with_libctx                 ?	3_0_0	EXIST::FUNCTION:
+SSL_new_session_ticket                  ?	3_0_0	EXIST::FUNCTION:


More information about the openssl-commits mailing list