[openssl-commits] [openssl] master update

Matt Caswell matt at openssl.org
Thu May 17 15:52:35 UTC 2018


The branch master has been updated
       via  c22365b399f62af4a81e9202500cd2cbd9c23a9d (commit)
       via  56548e86ac60fd4ff94fd65389e6807640db00e8 (commit)
       via  36ff232cf2bf5dfcaf9e60a8c492439428a243bb (commit)
       via  394159da608f625b60f07c59e36dc7d01df3a709 (commit)
       via  9d0a8bb71e3e411e9183e635122f17c1429c4116 (commit)
      from  029c11c21fdd018ec51badaafd34118223055274 (commit)


- Log -----------------------------------------------------------------
commit c22365b399f62af4a81e9202500cd2cbd9c23a9d
Author: Matt Caswell <matt at openssl.org>
Date:   Fri May 11 17:47:27 2018 +0100

    Improve testing of tickets with post-handshake auth
    
    Reviewed-by: Viktor Dukhovni <viktor at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/5227)

commit 56548e86ac60fd4ff94fd65389e6807640db00e8
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Mar 16 18:03:52 2018 +0000

    Add documentation for the ability to control the number of tickets
    
    Reviewed-by: Viktor Dukhovni <viktor at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/5227)

commit 36ff232cf2bf5dfcaf9e60a8c492439428a243bb
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Mar 14 19:22:48 2018 +0000

    Change the default number of NewSessionTickets we send to 2
    
    Reviewed-by: Viktor Dukhovni <viktor at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/5227)

commit 394159da608f625b60f07c59e36dc7d01df3a709
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Jan 31 16:40:03 2018 +0000

    Allow configuation of the number of TLSv1.3 session tickets via SSL_CONF
    
    Also allows the apps to set it.
    
    Reviewed-by: Viktor Dukhovni <viktor at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/5227)

commit 9d0a8bb71e3e411e9183e635122f17c1429c4116
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Jan 31 17:26:46 2018 +0000

    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 <viktor at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/5227)

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

Summary of changes:
 apps/apps.h                          |   4 +-
 apps/s_server.c                      |   5 +-
 doc/man3/SSL_CTX_set_num_tickets.pod |  68 ++++++++++++
 doc/man3/SSL_CTX_set_options.pod     |   2 +
 include/openssl/ssl.h                |   5 +
 ssl/ssl_conf.c                       |  18 +++-
 ssl/ssl_lib.c                        |  29 ++++++
 ssl/ssl_locl.h                       |   9 ++
 ssl/statem/statem_clnt.c             |   8 --
 ssl/statem/statem_srvr.c             |  88 +++++++++++-----
 test/handshake_helper.c              |  18 +++-
 test/sslapitest.c                    | 197 ++++++++++++++++++++++++++++++-----
 test/ssltestlib.c                    |  15 +--
 util/libssl.num                      |   4 +
 util/perl/TLSProxy/Proxy.pm          |   6 ++
 15 files changed, 402 insertions(+), 74 deletions(-)
 create mode 100644 doc/man3/SSL_CTX_set_num_tickets.pod

diff --git a/apps/apps.h b/apps/apps.h
index b45a31a..5b98d27 100644
--- a/apps/apps.h
+++ b/apps/apps.h
@@ -281,8 +281,8 @@ int set_cert_times(X509 *x, const char *startdate, const char *enddate,
             "Block size to pad TLS 1.3 records to."}, \
         {"debug_broken_protocol", OPT_S_DEBUGBROKE, '-', \
             "Perform all sorts of protocol violations for testing purposes"}, \
-        {"no_middlebox", OPT_S_NO_MIDDLEBOX, '-', "Disable TLSv1.3 middlebox compat mode" }
-
+        {"no_middlebox", OPT_S_NO_MIDDLEBOX, '-', \
+            "Disable TLSv1.3 middlebox compat mode" }
 
 # define OPT_S_CASES \
         OPT_S__FIRST: case OPT_S__LAST: break; \
diff --git a/apps/s_server.c b/apps/s_server.c
index b0e9659..5d53250 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -747,7 +747,7 @@ typedef enum OPTION_choice {
     OPT_ID_PREFIX, OPT_SERVERNAME, OPT_SERVERNAME_FATAL,
     OPT_CERT2, OPT_KEY2, OPT_NEXTPROTONEG, OPT_ALPN,
     OPT_SRTP_PROFILES, OPT_KEYMATEXPORT, OPT_KEYMATEXPORTLEN,
-    OPT_KEYLOG_FILE, OPT_MAX_EARLY, OPT_EARLY_DATA,
+    OPT_KEYLOG_FILE, OPT_MAX_EARLY, OPT_EARLY_DATA, OPT_S_NUM_TICKETS,
     OPT_R_ENUM,
     OPT_S_ENUM,
     OPT_V_ENUM,
@@ -955,6 +955,8 @@ const OPTIONS s_server_options[] = {
     {"max_early_data", OPT_MAX_EARLY, 'n',
      "The maximum number of bytes of early data"},
     {"early_data", OPT_EARLY_DATA, '-', "Attempt to read early data"},
+    {"num_tickets", OPT_S_NUM_TICKETS, 'n',
+     "The number of TLSv1.3 session tickets that a server will automatically  issue" },
     {NULL, OPT_EOF, 0, NULL}
 };
 
@@ -1252,6 +1254,7 @@ int s_server_main(int argc, char *argv[])
                 goto opthelp;
             break;
         case OPT_S_CASES:
+        case OPT_S_NUM_TICKETS:
             if (ssl_args == NULL)
                 ssl_args = sk_OPENSSL_STRING_new_null();
             if (ssl_args == NULL
diff --git a/doc/man3/SSL_CTX_set_num_tickets.pod b/doc/man3/SSL_CTX_set_num_tickets.pod
new file mode 100644
index 0000000..b6b0e3e
--- /dev/null
+++ b/doc/man3/SSL_CTX_set_num_tickets.pod
@@ -0,0 +1,68 @@
+=pod
+
+=head1 NAME
+
+SSL_set_num_tickets,
+SSL_get_num_tickets,
+SSL_CTX_set_num_tickets,
+SSL_CTX_get_num_tickets
+- control the number of TLSv1.3 session tickets that are issued
+
+=head1 SYNOPSIS
+
+ #include <openssl/ssl.h>
+
+ 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);
+
+=head1 DESCRIPTION
+
+SSL_CTX_set_num_tickets() and SSL_set_num_tickets() can be called for a server
+application and set the number of session tickets that will be sent to the
+client after a full handshake. Set the desired value (which could be 0) in the
+B<num_tickets> argument. Typically these functions should be called before the
+start of the handshake.
+
+The default number of tickets is 2; the default number of tickets sent following
+a resumption handshake is 1 but this cannot be changed using these functions.
+The number of tickets following a resumption handshake can be reduced to 0 using
+custom session ticket callbacks (see L<SSL_CTX_set_session_ticket_cb(3)>).
+
+Tickets are also issued on receipt of a post-handshake certificate from the
+client following a request by the server using
+L<SSL_verify_client_post_handshake(3)>. These new tickets will be associated
+with the updated client identity (i.e. including their certificate and
+verification status). The number of tickets issued will normally be the same as
+was used for the initial handshake. If the initial handshake was a full
+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.
+
+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_get_num_tickets() and SSL_get_num_tickets() return the number of tickets
+that have been previously set.
+
+=head1 HISTORY
+
+These functions were added in OpenSSL 1.1.1.
+
+=head1 COPYRIGHT
+
+Copyright 2018 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the OpenSSL license (the "License").  You may not use
+this file except in compliance with the License.  You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man3/SSL_CTX_set_options.pod b/doc/man3/SSL_CTX_set_options.pod
index 0d51077..552de07 100644
--- a/doc/man3/SSL_CTX_set_options.pod
+++ b/doc/man3/SSL_CTX_set_options.pod
@@ -151,6 +151,8 @@ of RFC4507bis tickets for stateless session resumption.
 If this option is set this functionality is disabled and tickets will
 not be used by clients or servers.
 
+This option only applies to TLSv1.2 and below. It is ignored for TLSv1.3.
+
 =item SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
 
 Allow legacy insecure renegotiation between OpenSSL and unpatched clients or
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 1f4f261..db0a2d5 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_conf.c b/ssl/ssl_conf.c
index f1e8200..758f012 100644
--- a/ssl/ssl_conf.c
+++ b/ssl/ssl_conf.c
@@ -570,6 +570,21 @@ static int cmd_RecordPadding(SSL_CONF_CTX *cctx, const char *value)
     return rv;
 }
 
+
+static int cmd_NumTickets(SSL_CONF_CTX *cctx, const char *value)
+{
+    int rv = 0;
+    int num_tickets = atoi(value);
+
+    if (num_tickets >= 0) {
+        if (cctx->ctx)
+            rv = SSL_CTX_set_num_tickets(cctx->ctx, num_tickets);
+        if (cctx->ssl)
+            rv = SSL_set_num_tickets(cctx->ssl, num_tickets);
+    }
+    return rv;
+}
+
 typedef struct {
     int (*cmd) (SSL_CONF_CTX *cctx, const char *value);
     const char *str_file;
@@ -655,7 +670,8 @@ static const ssl_conf_cmd_tbl ssl_conf_cmds[] = {
                  SSL_CONF_FLAG_SERVER | SSL_CONF_FLAG_CERTIFICATE,
                  SSL_CONF_TYPE_FILE),
 #endif
-    SSL_CONF_CMD_STRING(RecordPadding, "record_padding", 0)
+    SSL_CONF_CMD_STRING(RecordPadding, "record_padding", 0),
+    SSL_CONF_CMD_STRING(NumTickets, "num_tickets", SSL_CONF_FLAG_SERVER)
 };
 
 /* Supported switches: must match order of switches in ssl_conf_cmds */
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 41574c4..c38fc58 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -591,6 +591,7 @@ int SSL_clear(SSL *s)
     s->psksession_id = NULL;
     s->psksession_id_len = 0;
     s->hello_retry_request = 0;
+    s->sent_tickets = 0;
 
     s->error = 0;
     s->hit = 0;
@@ -699,6 +700,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 +3035,9 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth)
      */
     ret->max_early_data = 0;
 
+    /* By default we send two session tickets automatically in TLSv1.3 */
+    ret->num_tickets = 2;
+
     ssl_ctx_system_config(ret);
 
     return ret;
@@ -4314,6 +4319,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 e02f5a1..4aec810 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_clnt.c b/ssl/statem/statem_clnt.c
index 60e987a..6c0f8be 100644
--- a/ssl/statem/statem_clnt.c
+++ b/ssl/statem/statem_clnt.c
@@ -2590,7 +2590,6 @@ MSG_PROCESS_RETURN tls_process_new_session_ticket(SSL *s, PACKET *pkt)
      * cache.
      */
     if (SSL_IS_TLS13(s) || s->session->session_id_length > 0) {
-        int i = s->session_ctx->session_cache_mode;
         SSL_SESSION *new_sess;
         /*
          * We reused an existing session, so we need to replace it with a new
@@ -2603,13 +2602,6 @@ MSG_PROCESS_RETURN tls_process_new_session_ticket(SSL *s, PACKET *pkt)
             goto err;
         }
 
-        if (i & SSL_SESS_CACHE_CLIENT) {
-            /*
-             * Remove the old session from the cache. We carry on if this fails
-             */
-            SSL_CTX_remove_session(s->session_ctx, s->session);
-        }
-
         SSL_SESSION_free(s->session);
         s->session = new_sess;
     }
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index 22786be..ce8cec1 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:
@@ -506,9 +507,19 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s)
         /* Fall through */
 
     case TLS_ST_SW_KEY_UPDATE:
-    case TLS_ST_SW_SESSION_TICKET:
         st->hand_state = TLS_ST_OK;
         return WRITE_TRAN_CONTINUE;
+
+    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;
     }
 }
 
@@ -700,7 +711,7 @@ 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)) {
+        if (SSL_IS_TLS13(s) && s->sent_tickets == 0) {
             /*
              * Actually this is the end of the handshake, but we're going
              * straight into writing the session ticket out. So we finish off
@@ -3679,12 +3690,16 @@ MSG_PROCESS_RETURN tls_process_client_certificate(SSL *s, PACKET *pkt)
     sk = NULL;
 
     /* Save the current hash state for when we receive the CertificateVerify */
-    if (SSL_IS_TLS13(s)
-            && !ssl_handshake_hash(s, s->cert_verify_hash,
-                                   sizeof(s->cert_verify_hash),
-                                   &s->cert_verify_hash_len)) {
-        /* SSLfatal() already called */
-        goto err;
+    if (SSL_IS_TLS13(s)) {
+        if (!ssl_handshake_hash(s, s->cert_verify_hash,
+                                sizeof(s->cert_verify_hash),
+                                &s->cert_verify_hash_len)) {
+            /* SSLfatal() already called */
+            goto err;
+        }
+
+        /* Resend session tickets */
+        s->sent_tickets = 0;
     }
 
     ret = MSG_PROCESS_CONTINUE_READING;
@@ -3743,21 +3758,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)) {
@@ -3961,13 +3996,14 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
         goto err;
     }
     if (SSL_IS_TLS13(s)) {
-        ssl_update_cache(s, SSL_SESS_CACHE_SERVER);
         if (!tls_construct_extensions(s, pkt,
                                       SSL_EXT_TLS1_3_NEW_SESSION_TICKET,
                                       NULL, 0)) {
             /* SSLfatal() already called */
             goto err;
         }
+        s->sent_tickets++;
+        ssl_update_cache(s, SSL_SESS_CACHE_SERVER);
     }
     EVP_CIPHER_CTX_free(ctx);
     HMAC_CTX_free(hctx);
diff --git a/test/handshake_helper.c b/test/handshake_helper.c
index b3d94bb..3ebf64d 100644
--- a/test/handshake_helper.c
+++ b/test/handshake_helper.c
@@ -1403,7 +1403,7 @@ static HANDSHAKE_RESULT *do_handshake_internal(
     HANDSHAKE_EX_DATA server_ex_data, client_ex_data;
     CTX_DATA client_ctx_data, server_ctx_data, server2_ctx_data;
     HANDSHAKE_RESULT *ret = HANDSHAKE_RESULT_new();
-    int client_turn = 1, client_turn_count = 0;
+    int client_turn = 1, client_turn_count = 0, client_wait_count = 0;
     connect_phase_t phase = HANDSHAKE;
     handshake_status_t status = HANDSHAKE_RETRY;
     const unsigned char* tick = NULL;
@@ -1586,9 +1586,19 @@ static HANDSHAKE_RESULT *do_handshake_internal(
                     ret->result = SSL_TEST_INTERNAL_ERROR;
                     goto err;
                 }
-
-                /* Continue. */
-                client_turn ^= 1;
+                if (client_turn && server.status == PEER_SUCCESS) {
+                    /*
+                     * The server may finish before the client because the
+                     * client spends some turns processing NewSessionTickets.
+                     */
+                    if (client_wait_count++ >= 2) {
+                        ret->result = SSL_TEST_INTERNAL_ERROR;
+                        goto err;
+                    }
+                } else {
+                    /* Continue. */
+                    client_turn ^= 1;
+                }
             }
             break;
         }
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 06d6cb2..fe355f2 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -882,10 +882,14 @@ static int execute_test_session(int maxprot, int use_int_cache,
     SSL *serverssl3 = NULL, *clientssl3 = NULL;
 # endif
     SSL_SESSION *sess1 = NULL, *sess2 = NULL;
-    int testresult = 0;
+    int testresult = 0, numnewsesstick = 1;
 
     new_called = remove_called = 0;
 
+    /* TLSv1.3 sends 2 NewSessionTickets */
+    if (maxprot == TLS1_3_VERSION)
+        numnewsesstick = 2;
+
     if (!TEST_true(create_ssl_ctx_pair(TLS_server_method(), TLS_client_method(),
                                        TLS1_VERSION, TLS_MAX_VERSION,
                                        &sctx, &cctx, cert, privkey)))
@@ -923,7 +927,9 @@ static int execute_test_session(int maxprot, int use_int_cache,
     if (use_int_cache && !TEST_false(SSL_CTX_add_session(cctx, sess1)))
         goto end;
     if (use_ext_cache
-            && (!TEST_int_eq(new_called, 1) || !TEST_int_eq(remove_called, 0)))
+            && (!TEST_int_eq(new_called, numnewsesstick)
+
+                || !TEST_int_eq(remove_called, 0)))
         goto end;
 
     new_called = remove_called = 0;
@@ -938,11 +944,11 @@ static int execute_test_session(int maxprot, int use_int_cache,
     if (maxprot == TLS1_3_VERSION) {
         /*
          * In TLSv1.3 we should have created a new session even though we have
-         * resumed. The original session should also have been removed.
+         * resumed.
          */
         if (use_ext_cache
                 && (!TEST_int_eq(new_called, 1)
-                    || !TEST_int_eq(remove_called, 1)))
+                    || !TEST_int_eq(remove_called, 0)))
             goto end;
     } else {
         /*
@@ -972,7 +978,8 @@ static int execute_test_session(int maxprot, int use_int_cache,
         goto end;
 
     if (use_ext_cache
-            && (!TEST_int_eq(new_called, 1) || !TEST_int_eq(remove_called, 0)))
+            && (!TEST_int_eq(new_called, numnewsesstick)
+                || !TEST_int_eq(remove_called, 0)))
         goto end;
 
     new_called = remove_called = 0;
@@ -1072,7 +1079,7 @@ static int execute_test_session(int maxprot, int use_int_cache,
     if (use_ext_cache) {
         SSL_SESSION *tmp = sess2;
 
-        if (!TEST_int_eq(new_called, 1)
+        if (!TEST_int_eq(new_called, numnewsesstick)
                 || !TEST_int_eq(remove_called, 0)
                 || !TEST_int_eq(get_called, 0))
             goto end;
@@ -1105,10 +1112,6 @@ static int execute_test_session(int maxprot, int use_int_cache,
             goto end;
 
         if (maxprot == TLS1_3_VERSION) {
-            /*
-             * Every time we issue a NewSessionTicket we are creating a new
-             * session for next time in TLSv1.3
-             */
             if (!TEST_int_eq(new_called, 1)
                     || !TEST_int_eq(get_called, 0))
                 goto end;
@@ -1181,6 +1184,134 @@ static int test_session_with_both_cache(void)
 #endif
 }
 
+static SSL_SESSION *sesscache[6];
+static int do_cache;
+
+static int new_cachesession_cb(SSL *ssl, SSL_SESSION *sess)
+{
+    if (do_cache) {
+        sesscache[new_called] = sess;
+    } else {
+        /* We don't need the reference to the session, so free it */
+        SSL_SESSION_free(sess);
+    }
+    new_called++;
+
+    return 1;
+}
+
+static int post_handshake_verify(SSL *sssl, SSL *cssl)
+{
+    SSL_set_verify(sssl, SSL_VERIFY_PEER, NULL);
+    if (!TEST_true(SSL_verify_client_post_handshake(sssl)))
+        return 0;
+
+    /* Start handshake on the server and client */
+    if (!TEST_int_eq(SSL_do_handshake(sssl), 1)
+            || !TEST_int_le(SSL_read(cssl, NULL, 0), 0)
+            || !TEST_int_le(SSL_read(sssl, NULL, 0), 0)
+            || !TEST_true(create_ssl_connection(sssl, cssl,
+                                                SSL_ERROR_NONE)))
+        return 0;
+
+    return 1;
+}
+
+static int test_tickets(int idx)
+{
+    SSL_CTX *sctx = NULL, *cctx = NULL;
+    SSL *serverssl = NULL, *clientssl = NULL;
+    int testresult = 0, i;
+    size_t j;
+
+    /* idx is the test number, but also the number of tickets we want */
+
+    new_called = 0;
+    do_cache = 1;
+
+    if (!TEST_true(create_ssl_ctx_pair(TLS_server_method(), TLS_client_method(),
+                                       TLS1_VERSION, TLS_MAX_VERSION, &sctx,
+                                       &cctx, cert, privkey))
+            || !TEST_true(SSL_CTX_set_num_tickets(sctx, idx)))
+        goto end;
+
+    SSL_CTX_set_session_cache_mode(cctx, SSL_SESS_CACHE_CLIENT
+                                         | SSL_SESS_CACHE_NO_INTERNAL_STORE);
+    SSL_CTX_sess_set_new_cb(cctx, new_cachesession_cb);
+
+    if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl,
+                                          &clientssl, NULL, NULL)))
+        goto end;
+
+    SSL_force_post_handshake_auth(clientssl);
+
+    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, new_called))
+        goto end;
+
+    /* After a post-handshake authentication we should get new tickets issued */
+    if (!post_handshake_verify(serverssl, clientssl)
+            || !TEST_int_eq(idx * 2, new_called))
+        goto end;
+
+    SSL_shutdown(clientssl);
+    SSL_shutdown(serverssl);
+    SSL_free(serverssl);
+    SSL_free(clientssl);
+    serverssl = clientssl = NULL;
+
+    /* Stop caching sessions - just count them */
+    do_cache = 0;
+
+    /* Test that we can resume with all the tickets we got given */
+    for (i = 0; i < idx * 2; i++) {
+        new_called = 0;
+        if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl,
+                                              &clientssl, NULL, NULL))
+                || !TEST_true(SSL_set_session(clientssl, sesscache[i])))
+            goto end;
+
+        SSL_force_post_handshake_auth(clientssl);
+
+        if (!TEST_true(create_ssl_connection(serverssl, clientssl,
+                                                    SSL_ERROR_NONE))
+                || !TEST_true(SSL_session_reused(clientssl))
+                   /* Following a resumption we only get 1 ticket */
+                || !TEST_int_eq(new_called, 1))
+            goto end;
+
+        new_called = 0;
+        /* After a post-handshake authentication we should get 1 new ticket */
+        if (!post_handshake_verify(serverssl, clientssl)
+                || !TEST_int_eq(new_called, 1))
+            goto end;
+
+        SSL_shutdown(clientssl);
+        SSL_shutdown(serverssl);
+        SSL_free(serverssl);
+        SSL_free(clientssl);
+        serverssl = clientssl = NULL;
+        SSL_SESSION_free(sesscache[i]);
+        sesscache[i] = NULL;
+    }
+
+    testresult = 1;
+
+ end:
+    SSL_free(serverssl);
+    SSL_free(clientssl);
+    for (j = 0; j < OSSL_NELEM(sesscache); j++) {
+        SSL_SESSION_free(sesscache[j]);
+        sesscache[j] = NULL;
+    }
+    SSL_CTX_free(sctx);
+    SSL_CTX_free(cctx);
+
+    return testresult;
+}
+
 #define USE_NULL            0
 #define USE_BIO_1           1
 #define USE_BIO_2           2
@@ -1198,7 +1329,6 @@ static int test_session_with_both_cache(void)
 # define TOTAL_CONN_FAIL_SSL_SET_BIO_TESTS       0
 #endif
 
-
 #define TOTAL_SSL_SET_BIO_TESTS TOTAL_NO_CONN_SSL_SET_BIO_TESTS \
                                 + TOTAL_CONN_SUCCESS_SSL_SET_BIO_TESTS \
                                 + TOTAL_CONN_FAIL_SSL_SET_BIO_TESTS
@@ -1933,10 +2063,13 @@ static int test_early_data_read_write(int idx)
         goto end;
 
     /*
-     * Make sure we process the NewSessionTicket. This arrives post-handshake.
-     * We attempt a read which we do not expect to return any data.
+     * Make sure we process the two NewSessionTickets. These arrive
+     * post-handshake. We attempt reads which we do not expect to return any
+     * data.
      */
-    if (!TEST_false(SSL_read_ex(clientssl, buf, sizeof(buf), &readbytes)))
+    if (!TEST_false(SSL_read_ex(clientssl, buf, sizeof(buf), &readbytes))
+            || !TEST_false(SSL_read_ex(clientssl, buf, sizeof(buf),
+                           &readbytes)))
         goto end;
 
     /* Server should be able to write normal data */
@@ -3392,9 +3525,10 @@ static int test_custom_exts(int tst)
                 || (tst == 2 && snicb != 1))
             goto end;
     } else {
+        /* In this case there 2 NewSessionTicket messages created */
         if (clntaddnewcb != 1
-                || clntparsenewcb != 4
-                || srvaddnewcb != 4
+                || clntparsenewcb != 5
+                || srvaddnewcb != 5
                 || srvparsenewcb != 1)
             goto end;
     }
@@ -3438,10 +3572,13 @@ static int test_custom_exts(int tst)
                 || srvparsenewcb != 2)
             goto end;
     } else {
-        /* No Certificate message extensions in the resumption handshake */
+        /*
+         * No Certificate message extensions in the resumption handshake,
+         * 2 NewSessionTickets in the initial handshake, 1 in the resumption
+         */
         if (clntaddnewcb != 2
-                || clntparsenewcb != 7
-                || srvaddnewcb != 7
+                || clntparsenewcb != 8
+                || srvaddnewcb != 8
                 || srvparsenewcb != 2)
             goto end;
     }
@@ -4205,14 +4342,16 @@ static struct info_cb_states_st {
         {SSL_CB_EXIT, NULL}, {SSL_CB_LOOP, "TED"}, {SSL_CB_LOOP, "TRFIN"},
         {SSL_CB_HANDSHAKE_DONE, NULL}, {SSL_CB_HANDSHAKE_START, NULL},
         {SSL_CB_LOOP, "TWST"}, {SSL_CB_HANDSHAKE_DONE, NULL},
-        {SSL_CB_EXIT, NULL}, {SSL_CB_ALERT, NULL},
-        {SSL_CB_HANDSHAKE_START, NULL}, {SSL_CB_LOOP, "PINIT "},
-        {SSL_CB_LOOP, "PINIT "}, {SSL_CB_LOOP, "TRCH"}, {SSL_CB_LOOP, "TWSH"},
-        {SSL_CB_LOOP, "TWCCS"}, {SSL_CB_LOOP, "TWEE"}, {SSL_CB_LOOP, "TWFIN"},
-        {SSL_CB_LOOP, "TED"}, {SSL_CB_EXIT, NULL}, {SSL_CB_LOOP, "TED"},
-        {SSL_CB_LOOP, "TRFIN"}, {SSL_CB_HANDSHAKE_DONE, NULL},
         {SSL_CB_HANDSHAKE_START, NULL}, {SSL_CB_LOOP, "TWST"},
-        {SSL_CB_HANDSHAKE_DONE, NULL}, {SSL_CB_EXIT, NULL}, {0, NULL},
+        {SSL_CB_HANDSHAKE_DONE, NULL}, {SSL_CB_EXIT, NULL},
+        {SSL_CB_ALERT, NULL}, {SSL_CB_HANDSHAKE_START, NULL},
+        {SSL_CB_LOOP, "PINIT "}, {SSL_CB_LOOP, "PINIT "}, {SSL_CB_LOOP, "TRCH"},
+        {SSL_CB_LOOP, "TWSH"}, {SSL_CB_LOOP, "TWCCS"}, {SSL_CB_LOOP, "TWEE"},
+        {SSL_CB_LOOP, "TWFIN"}, {SSL_CB_LOOP, "TED"}, {SSL_CB_EXIT, NULL},
+        {SSL_CB_LOOP, "TED"}, {SSL_CB_LOOP, "TRFIN"},
+        {SSL_CB_HANDSHAKE_DONE, NULL}, {SSL_CB_HANDSHAKE_START, NULL},
+        {SSL_CB_LOOP, "TWST"}, {SSL_CB_HANDSHAKE_DONE, NULL},
+        {SSL_CB_EXIT, NULL}, {0, NULL},
     }, {
         /* TLSv1.3 client followed by resumption */
         {SSL_CB_HANDSHAKE_START, NULL}, {SSL_CB_LOOP, "PINIT "},
@@ -4223,6 +4362,9 @@ static struct info_cb_states_st {
         {SSL_CB_EXIT, NULL}, {SSL_CB_HANDSHAKE_START, NULL},
         {SSL_CB_LOOP, "SSLOK "}, {SSL_CB_LOOP, "SSLOK "}, {SSL_CB_LOOP, "TRST"},
         {SSL_CB_HANDSHAKE_DONE, NULL},  {SSL_CB_EXIT, NULL},
+        {SSL_CB_HANDSHAKE_START, NULL}, {SSL_CB_LOOP, "SSLOK "},
+        {SSL_CB_LOOP, "SSLOK "}, {SSL_CB_LOOP, "TRST"},
+        {SSL_CB_HANDSHAKE_DONE, NULL},  {SSL_CB_EXIT, NULL},
         {SSL_CB_ALERT, NULL}, {SSL_CB_HANDSHAKE_START, NULL},
         {SSL_CB_LOOP, "PINIT "}, {SSL_CB_LOOP, "TWCH"}, {SSL_CB_EXIT, NULL},
         {SSL_CB_LOOP, "TWCH"}, {SSL_CB_LOOP, "TRSH"},  {SSL_CB_LOOP, "TREE"},
@@ -4856,6 +4998,9 @@ int setup_tests(void)
     ADD_TEST(test_session_with_only_int_cache);
     ADD_TEST(test_session_with_only_ext_cache);
     ADD_TEST(test_session_with_both_cache);
+#ifndef OPENSSL_NO_TLS1_3
+    ADD_ALL_TESTS(test_tickets, 3);
+#endif
     ADD_ALL_TESTS(test_ssl_set_bio, TOTAL_SSL_SET_BIO_TESTS);
     ADD_TEST(test_ssl_bio_pop_next_bio);
     ADD_TEST(test_ssl_bio_pop_ssl_bio);
diff --git a/test/ssltestlib.c b/test/ssltestlib.c
index c768963..2ef4b5d 100644
--- a/test/ssltestlib.c
+++ b/test/ssltestlib.c
@@ -682,7 +682,7 @@ int create_ssl_objects(SSL_CTX *serverctx, SSL_CTX *clientctx, SSL **sssl,
 
 int create_ssl_connection(SSL *serverssl, SSL *clientssl, int want)
 {
-    int retc = -1, rets = -1, err, abortctr = 0;
+    int retc = -1, rets = -1, err, abortctr = 0, i;
     int clienterr = 0, servererr = 0;
     unsigned char buf;
     size_t readbytes;
@@ -741,13 +741,16 @@ int create_ssl_connection(SSL *serverssl, SSL *clientssl, int want)
     /*
      * We attempt to read some data on the client side which we expect to fail.
      * This will ensure we have received the NewSessionTicket in TLSv1.3 where
-     * appropriate.
+     * appropriate. We do this twice because there are 2 NewSesionTickets.
      */
-    if (SSL_read_ex(clientssl, &buf, sizeof(buf), &readbytes) > 0) {
-        if (!TEST_ulong_eq(readbytes, 0))
+    for (i = 0; i < 2; i++) {
+        if (SSL_read_ex(clientssl, &buf, sizeof(buf), &readbytes) > 0) {
+            if (!TEST_ulong_eq(readbytes, 0))
+                return 0;
+        } else if (!TEST_int_eq(SSL_get_error(clientssl, 0),
+                                SSL_ERROR_WANT_READ)) {
             return 0;
-    } else if (!TEST_int_eq(SSL_get_error(clientssl, 0), SSL_ERROR_WANT_READ)) {
-        return 0;
+        }
     }
 
     return 1;
diff --git a/util/libssl.num b/util/libssl.num
index 344d684..3495903 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:
diff --git a/util/perl/TLSProxy/Proxy.pm b/util/perl/TLSProxy/Proxy.pm
index 8df0153..8c13520 100644
--- a/util/perl/TLSProxy/Proxy.pm
+++ b/util/perl/TLSProxy/Proxy.pm
@@ -220,6 +220,12 @@ sub start
 
     my $execcmd = $self->execute
         ." s_server -max_protocol TLSv1.3 -no_comp -rev -engine ossltest"
+        #In TLSv1.3 we issue two session tickets. The default session id
+        #callback gets confused because the ossltest engine causes the same
+        #session id to be created twice due to the changed random number
+        #generation. Using "-ext_cache" replaces the default callback with a
+        #different one that doesn't get confused.
+        ." -ext_cache"
         ." -accept $self->{server_addr}:0"
         ." -cert ".$self->cert." -cert2 ".$self->cert
         ." -naccept ".$self->serverconnects;


More information about the openssl-commits mailing list