[openssl-commits] [openssl] master update

Matt Caswell matt at openssl.org
Mon Jul 2 14:11:23 UTC 2018


The branch master has been updated
       via  3bb5e5b09e32defefda2b61087c113203005ffa0 (commit)
       via  5a42141565a4074167b006e7a28a822176b40f86 (commit)
       via  dc7a3543e0244bfdb9cbca1408fb7a6aa5da34b5 (commit)
       via  c9598459b6c797bd316e44834f5129bdf28add2b (commit)
       via  5d263fb78b51f96753056f21abc4d992d0219df2 (commit)
      from  b6ff436fcb597663ffcfe6d724d207cf120e7250 (commit)


- Log -----------------------------------------------------------------
commit 3bb5e5b09e32defefda2b61087c113203005ffa0
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Jun 15 14:55:06 2018 +0100

    Add the ability to configure anti-replay via SSL_CONF
    
    This also adds the ability to control this through s_server
    
    Reviewed-by: Viktor Dukhovni <viktor at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/6469)

commit 5a42141565a4074167b006e7a28a822176b40f86
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Jun 8 10:03:19 2018 +0100

    Add a test for the new early data callback
    
    Reviewed-by: Viktor Dukhovni <viktor at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/6469)

commit dc7a3543e0244bfdb9cbca1408fb7a6aa5da34b5
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Jun 7 16:32:19 2018 +0100

    Document the new early data callback and option
    
    Document SSL_OP_NO_ANTI_REPLAY and SSL_CTX_set_allow_early_data_cb()
    
    Reviewed-by: Viktor Dukhovni <viktor at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/6469)

commit c9598459b6c797bd316e44834f5129bdf28add2b
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Jun 7 15:14:36 2018 +0100

    Add setters to set the early_data callback
    
    Reviewed-by: Viktor Dukhovni <viktor at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/6469)

commit 5d263fb78b51f96753056f21abc4d992d0219df2
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Jun 7 09:11:05 2018 +0100

    Make the anti-replay feature optional
    
    Fixes #6389
    
    Reviewed-by: Viktor Dukhovni <viktor at openssl.org>
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/6469)

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

Summary of changes:
 apps/s_server.c                  |   5 ++
 doc/man1/s_server.pod            |  11 ++++
 doc/man3/SSL_CONF_cmd.pod        |  21 +++++++
 doc/man3/SSL_CTX_set_options.pod |  11 ++++
 doc/man3/SSL_read_early_data.pod |  35 ++++++++++-
 include/openssl/ssl.h            |  16 ++++-
 ssl/ssl_conf.c                   |  11 +++-
 ssl/ssl_lib.c                    |  22 ++++++-
 ssl/ssl_locl.h                   |   9 +++
 ssl/statem/extensions.c          |   5 +-
 ssl/statem/extensions_srvr.c     |   4 +-
 ssl/statem/statem_srvr.c         |   6 +-
 test/sslapitest.c                | 131 ++++++++++++++++++++++++++++++++++-----
 util/libssl.num                  |   2 +
 util/private.num                 |   2 +
 15 files changed, 267 insertions(+), 24 deletions(-)

diff --git a/apps/s_server.c b/apps/s_server.c
index df2bf02..b050200 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -749,6 +749,7 @@ typedef enum OPTION_choice {
     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_S_NUM_TICKETS,
+    OPT_ANTI_REPLAY, OPT_NO_ANTI_REPLAY,
     OPT_R_ENUM,
     OPT_S_ENUM,
     OPT_V_ENUM,
@@ -958,6 +959,8 @@ const OPTIONS s_server_options[] = {
     {"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" },
+    {"anti_replay", OPT_ANTI_REPLAY, '-', "Switch on anti-replay protection (default)"},
+    {"no_anti_replay", OPT_NO_ANTI_REPLAY, '-', "Switch off anti-replay protection"},
     {NULL, OPT_EOF, 0, NULL}
 };
 
@@ -1258,6 +1261,8 @@ int s_server_main(int argc, char *argv[])
             break;
         case OPT_S_CASES:
         case OPT_S_NUM_TICKETS:
+        case OPT_ANTI_REPLAY:
+        case OPT_NO_ANTI_REPLAY:
             if (ssl_args == NULL)
                 ssl_args = sk_OPENSSL_STRING_new_null();
             if (ssl_args == NULL
diff --git a/doc/man1/s_server.pod b/doc/man1/s_server.pod
index 2b7db63..f601794 100644
--- a/doc/man1/s_server.pod
+++ b/doc/man1/s_server.pod
@@ -180,6 +180,8 @@ B<openssl> B<s_server>
 [B<-keylogfile outfile>]
 [B<-max_early_data int>]
 [B<-early_data>]
+[B<-anti_replay>]
+[B<-no_anti_replay>]
 
 =head1 DESCRIPTION
 
@@ -709,6 +711,15 @@ greater than or equal to 0.
 
 Accept early data where possible.
 
+=item B<-anti_replay>, B<-no_anti_replay>
+
+Switches replay protection on or off, respectively. Replay protection is on by
+default unless overridden by a configuration file. When it is on, OpenSSL will
+automatically detect if a session ticket has been used more than once, TLSv1.3
+has been negotiated, and early data is enabled on the server. A full handshake
+is forced if a session ticket is used a second or subsequent time. Any early
+data that was sent will be rejected.
+
 =back
 
 =head1 CONNECTED COMMANDS
diff --git a/doc/man3/SSL_CONF_cmd.pod b/doc/man3/SSL_CONF_cmd.pod
index 4d3e9c2..4edd49c 100644
--- a/doc/man3/SSL_CONF_cmd.pod
+++ b/doc/man3/SSL_CONF_cmd.pod
@@ -211,6 +211,18 @@ that there will be no forward secrecy for the resumed session.
 enables strict mode protocol handling. Equivalent to setting
 B<SSL_CERT_FLAG_TLS_STRICT>.
 
+=item B<-anti_replay>, B<-no_anti_replay>
+
+Switches replay protection, on or off respectively. With replay protection on,
+OpenSSL will automatically detect if a session ticket has been used more than
+once, TLSv1.3 has been negotiated, and early data is enabled on the server. A
+full handshake is forced if a session ticket is used a second or subsequent
+time. Anti-Replay is on by default unless overridden by a configuration file and
+is only used by servers. Anti-replay measures are required for compliance with
+the TLSv1.3 specification. Some applications may be able to mitigate the replay
+risks in other ways and in such cases the built-in OpenSSL functionality is not
+required. Switching off anti-replay is equivalent to B<SSL_OP_NO_ANTI_REPLAY>.
+
 =back
 
 =head1 SUPPORTED CONFIGURATION FILE COMMANDS
@@ -441,6 +453,15 @@ middleboxes that do not understand TLSv1.3 will not drop the connection. This
 option is set by default. A future version of OpenSSL may not set this by
 default. Equivalent to B<SSL_OP_ENABLE_MIDDLEBOX_COMPAT>.
 
+B<AntiReplay>: If set then OpenSSL will automatically detect if a session ticket
+has been used more than once, TLSv1.3 has been negotiated, and early data is
+enabled on the server. A full handshake is forced if a session ticket is used a
+second or subsequent time. This option is set by default and is only used by
+servers. Anti-replay measures are required to comply with the TLSv1.3
+specification. Some applications may be able to mitigate the replay risks in
+other ways and in such cases the built-in OpenSSL functionality is not required.
+Disabling anti-replay is equivalent to setting B<SSL_OP_NO_ANTI_REPLAY>.
+
 =item B<VerifyMode>
 
 The B<value> argument is a comma separated list of flags to set.
diff --git a/doc/man3/SSL_CTX_set_options.pod b/doc/man3/SSL_CTX_set_options.pod
index f04de32..ae5ca1b 100644
--- a/doc/man3/SSL_CTX_set_options.pod
+++ b/doc/man3/SSL_CTX_set_options.pod
@@ -226,6 +226,17 @@ this option is set or not CCS messages received from the peer will always be
 ignored in TLSv1.3. This option is set by default. To switch it off use
 SSL_clear_options(). A future version of OpenSSL may not set this by default.
 
+=item SSL_OP_NO_ANTI_REPLAY
+
+By default, when a server is configured for early data (i.e., max_early_data > 0),
+OpenSSL will switch on replay protection. See L<SSL_read_early_data(3)> for a
+description of the replay protection feature. Anti-replay measures are required
+to comply with the TLSv1.3 specification. Some applications may be able to
+mitigate the replay risks in other ways and in such cases the built in OpenSSL
+functionality is not required. Those applications can turn this feature off by
+setting this option. This is a server-side opton only. It is ignored by
+clients.
+
 =back
 
 The following options no longer have any effect but their identifiers are
diff --git a/doc/man3/SSL_read_early_data.pod b/doc/man3/SSL_read_early_data.pod
index 6a76ec2..cf6f757 100644
--- a/doc/man3/SSL_read_early_data.pod
+++ b/doc/man3/SSL_read_early_data.pod
@@ -10,7 +10,10 @@ SSL_SESSION_get_max_early_data,
 SSL_SESSION_set_max_early_data,
 SSL_write_early_data,
 SSL_read_early_data,
-SSL_get_early_data_status
+SSL_get_early_data_status,
+SSL_allow_early_data_cb_fn,
+SSL_CTX_set_allow_early_data_cb,
+SSL_set_allow_early_data_cb
 - functions for sending and receiving early data
 
 =head1 SYNOPSIS
@@ -30,6 +33,16 @@ SSL_get_early_data_status
 
  int SSL_get_early_data_status(const SSL *s);
 
+
+ typedef int (*SSL_allow_early_data_cb_fn)(SSL *s, void *arg);
+
+ void SSL_CTX_set_allow_early_data_cb(SSL_CTX *ctx,
+                                      SSL_allow_early_data_cb_fn cb,
+                                      void *arg);
+ void SSL_set_allow_early_data_cb(SSL *s,
+                                  SSL_allow_early_data_cb_fn cb,
+                                  void *arg);
+
 =head1 DESCRIPTION
 
 These functions are used to send and receive early data where TLSv1.3 has been
@@ -186,6 +199,20 @@ In the event that the current maximum early data setting for the server is
 different to that originally specified in a session that a client is resuming
 with then the lower of the two values will apply.
 
+Some server applications may wish to have more control over whether early data
+is accepted or not, for example to mitigate replay risks (see L</REPLAY PROTECTION>
+below) or to decline early_data when the server is heavily loaded. The functions
+SSL_CTX_set_allow_early_data_cb() and SSL_set_allow_early_data_cb() set a
+callback which is called at a point in the handshake immediately before a
+decision is made to accept or reject early data. The callback is provided with a
+pointer to the user data argument that was provided when the callback was first
+set. Returning 1 from the callback will allow early data and returning 0 will
+reject it. Note that the OpenSSL library may reject early data for other reasons
+in which case this callback will not get called. Notably, the built-in replay
+protection feature will still be used even if a callback is present unless it
+has been explicitly disabled using the SSL_OP_NO_ANTI_REPLAY option. See
+L</REPLAY PROTECTION> below.
+
 =head1 NOTES
 
 The whole purpose of early data is to enable a client to start sending data to
@@ -252,6 +279,12 @@ The OpenSSL replay protection does not apply to external Pre Shared Keys (PSKs)
 (e.g. see SSL_CTX_set_psk_find_session_callback(3)). Therefore extreme caution
 should be applied when combining external PSKs with early data.
 
+Some applications may mitigate the replay risks in other ways. For those
+applications it is possible to turn off the built-in replay protection feature
+using the B<SSL_OP_NO_ANTI_REPLAY> option. See L<SSL_CTX_set_options(3)> for
+details. Applications can also set a callback to make decisions about accepting
+early data or not. See SSL_CTX_set_allow_early_data_cb() above for details.
+
 =head1 RETURN VALUES
 
 SSL_write_early_data() returns 1 for success or 0 for failure. In the event of a
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 943a8d6..bbcfb3c 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -368,6 +368,12 @@ typedef int (*SSL_verify_cb)(int preverify_ok, X509_STORE_CTX *x509_ctx);
  */
 # define SSL_OP_TLS_ROLLBACK_BUG                         0x00800000U
 
+/*
+ * Switches off automatic TLSv1.3 anti-replay protection for early data. This
+ * is a server-side option only (no effect on the client).
+ */
+# define SSL_OP_NO_ANTI_REPLAY                           0x01000000U
+
 # define SSL_OP_NO_SSLv3                                 0x02000000U
 # define SSL_OP_NO_TLSv1                                 0x04000000U
 # define SSL_OP_NO_TLSv1_2                               0x08000000U
@@ -2383,13 +2389,19 @@ int SSL_SESSION_get0_ticket_appdata(SSL_SESSION *ss, void **data, size_t *len);
 
 extern const char SSL_version_str[];
 
-
-
 typedef unsigned int (*DTLS_timer_cb)(SSL *s, unsigned int timer_us);
 
 void DTLS_set_timer_cb(SSL *s, DTLS_timer_cb cb);
 
 
+typedef int (*SSL_allow_early_data_cb_fn)(SSL *s, void *arg);
+void SSL_CTX_set_allow_early_data_cb(SSL_CTX *ctx,
+                                     SSL_allow_early_data_cb_fn cb,
+                                     void *arg);
+void SSL_set_allow_early_data_cb(SSL *s,
+                                 SSL_allow_early_data_cb_fn cb,
+                                 void *arg);
+
 # ifdef  __cplusplus
 }
 # endif
diff --git a/ssl/ssl_conf.c b/ssl/ssl_conf.c
index 758f012..9c20270 100644
--- a/ssl/ssl_conf.c
+++ b/ssl/ssl_conf.c
@@ -383,7 +383,8 @@ static int cmd_Options(SSL_CONF_CTX *cctx, const char *value)
         SSL_FLAG_TBL("NoRenegotiation", SSL_OP_NO_RENEGOTIATION),
         SSL_FLAG_TBL("AllowNoDHEKEX", SSL_OP_ALLOW_NO_DHE_KEX),
         SSL_FLAG_TBL("PrioritizeChaCha", SSL_OP_PRIORITIZE_CHACHA),
-        SSL_FLAG_TBL("MiddleboxCompat", SSL_OP_ENABLE_MIDDLEBOX_COMPAT)
+        SSL_FLAG_TBL("MiddleboxCompat", SSL_OP_ENABLE_MIDDLEBOX_COMPAT),
+        SSL_FLAG_TBL_INV("AntiReplay", SSL_OP_NO_ANTI_REPLAY)
     };
     if (value == NULL)
         return -3;
@@ -626,6 +627,8 @@ static const ssl_conf_cmd_tbl ssl_conf_cmds[] = {
     SSL_CONF_CMD_SWITCH("prioritize_chacha", SSL_CONF_FLAG_SERVER),
     SSL_CONF_CMD_SWITCH("strict", 0),
     SSL_CONF_CMD_SWITCH("no_middlebox", 0),
+    SSL_CONF_CMD_SWITCH("anti_replay", SSL_CONF_FLAG_SERVER),
+    SSL_CONF_CMD_SWITCH("no_anti_replay", SSL_CONF_FLAG_SERVER),
     SSL_CONF_CMD_STRING(SignatureAlgorithms, "sigalgs", 0),
     SSL_CONF_CMD_STRING(ClientSignatureAlgorithms, "client_sigalgs", 0),
     SSL_CONF_CMD_STRING(Curves, "curves", 0),
@@ -671,7 +674,7 @@ static const ssl_conf_cmd_tbl ssl_conf_cmds[] = {
                  SSL_CONF_TYPE_FILE),
 #endif
     SSL_CONF_CMD_STRING(RecordPadding, "record_padding", 0),
-    SSL_CONF_CMD_STRING(NumTickets, "num_tickets", SSL_CONF_FLAG_SERVER)
+    SSL_CONF_CMD_STRING(NumTickets, "num_tickets", SSL_CONF_FLAG_SERVER),
 };
 
 /* Supported switches: must match order of switches in ssl_conf_cmds */
@@ -704,6 +707,10 @@ static const ssl_switch_tbl ssl_cmd_switches[] = {
     {SSL_CERT_FLAG_TLS_STRICT, SSL_TFLAG_CERT}, /* strict */
     /* no_middlebox */
     {SSL_OP_ENABLE_MIDDLEBOX_COMPAT, SSL_TFLAG_INV},
+    /* anti_replay */
+    {SSL_OP_NO_ANTI_REPLAY, SSL_TFLAG_INV},
+    /* no_anti_replay */
+    {SSL_OP_NO_ANTI_REPLAY, 0},
 };
 
 static int ssl_conf_cmd_skip_prefix(SSL_CONF_CTX *cctx, const char **pcmd)
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 6ced147..1387067 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -805,6 +805,9 @@ SSL *SSL_new(SSL_CTX *ctx)
 
     s->key_update = SSL_KEY_UPDATE_NONE;
 
+    s->allow_early_data_cb = ctx->allow_early_data_cb;
+    s->allow_early_data_cb_data = ctx->allow_early_data_cb_data;
+
     if (!s->method->ssl_new(s))
         goto err;
 
@@ -3381,7 +3384,8 @@ void ssl_update_cache(SSL *s, int mode)
         if ((i & SSL_SESS_CACHE_NO_INTERNAL_STORE) == 0
                 && (!SSL_IS_TLS13(s)
                     || !s->server
-                    || s->max_early_data > 0
+                    || (s->max_early_data > 0
+                        && (s->options & SSL_OP_NO_ANTI_REPLAY) == 0)
                     || s->session_ctx->remove_session_cb != NULL
                     || (s->options & SSL_OP_NO_TICKET) != 0))
             SSL_CTX_add_session(s->session_ctx, s->session);
@@ -5482,3 +5486,19 @@ int SSL_CTX_set_session_ticket_cb(SSL_CTX *ctx,
     ctx->ticket_cb_data = arg;
     return 1;
 }
+
+void SSL_CTX_set_allow_early_data_cb(SSL_CTX *ctx,
+                                     SSL_allow_early_data_cb_fn cb,
+                                     void *arg)
+{
+    ctx->allow_early_data_cb = cb;
+    ctx->allow_early_data_cb_data = arg;
+}
+
+void SSL_set_allow_early_data_cb(SSL *s,
+                                 SSL_allow_early_data_cb_fn cb,
+                                 void *arg)
+{
+    s->allow_early_data_cb = cb;
+    s->allow_early_data_cb_data = arg;
+}
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index a4d1376..6a2edeb 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -1047,6 +1047,10 @@ struct ssl_ctx_st {
 
     /* The number of TLS1.3 tickets to automatically send */
     size_t num_tickets;
+
+    /* Callback to determine if early_data is acceptable or not */
+    SSL_allow_early_data_cb_fn allow_early_data_cb;
+    void *allow_early_data_cb_data;
 };
 
 struct ssl_st {
@@ -1205,6 +1209,7 @@ struct ssl_st {
 # endif
     SSL_psk_find_session_cb_func psk_find_session_cb;
     SSL_psk_use_session_cb_func psk_use_session_cb;
+
     SSL_CTX *ctx;
     /* Verified chain of peer */
     STACK_OF(X509) *verified_chain;
@@ -1424,6 +1429,10 @@ struct ssl_st {
     size_t sent_tickets;
     /* The next nonce value to use when we send a ticket on this connection */
     uint64_t next_ticket_nonce;
+
+    /* Callback to determine if early_data is acceptable or not */
+    SSL_allow_early_data_cb_fn allow_early_data_cb;
+    void *allow_early_data_cb_data;
 };
 
 /*
diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c
index 496039e..5309b12 100644
--- a/ssl/statem/extensions.c
+++ b/ssl/statem/extensions.c
@@ -1622,7 +1622,10 @@ static int final_early_data(SSL *s, unsigned int context, int sent)
             || s->session->ext.tick_identity != 0
             || s->early_data_state != SSL_EARLY_DATA_ACCEPTING
             || !s->ext.early_data_ok
-            || s->hello_retry_request != SSL_HRR_NONE) {
+            || s->hello_retry_request != SSL_HRR_NONE
+            || (s->ctx->allow_early_data_cb != NULL
+                && !s->ctx->allow_early_data_cb(s,
+                                         s->ctx->allow_early_data_cb_data))) {
         s->ext.early_data = SSL_EARLY_DATA_REJECTED;
     } else {
         s->ext.early_data = SSL_EARLY_DATA_ACCEPTED;
diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c
index f58ed0b..ab38a4f 100644
--- a/ssl/statem/extensions_srvr.c
+++ b/ssl/statem/extensions_srvr.c
@@ -1165,7 +1165,8 @@ int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
              * is no point in using full stateless tickets.
              */
             if ((s->options & SSL_OP_NO_TICKET) != 0
-                    || s->max_early_data > 0)
+                    || (s->max_early_data > 0
+                        && (s->options & SSL_OP_NO_ANTI_REPLAY) == 0))
                 ret = tls_get_stateful_ticket(s, &identity, &sess);
             else
                 ret = tls_decrypt_ticket(s, PACKET_data(&identity),
@@ -1189,6 +1190,7 @@ int tls_parse_ctos_psk(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
 
             /* Check for replay */
             if (s->max_early_data > 0
+                    && (s->options & SSL_OP_NO_ANTI_REPLAY) == 0
                     && !SSL_CTX_remove_session(s->session_ctx, sess)) {
                 SSL_SESSION_free(sess);
                 sess = NULL;
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index 26cd850..5c59eb8 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -4086,8 +4086,10 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
      * SSL_OP_NO_TICKET is set - we are caching tickets anyway so there
      * is no point in using full stateless tickets.
      */
-    if (((s->options & SSL_OP_NO_TICKET) != 0 || s->max_early_data > 0)
-            && SSL_IS_TLS13(s)) {
+    if (SSL_IS_TLS13(s)
+            && ((s->options & SSL_OP_NO_TICKET) != 0
+                || (s->max_early_data > 0
+                    && (s->options & SSL_OP_NO_ANTI_REPLAY) == 0))) {
         if (!construct_stateful_ticket(s, pkt, age_add_u.age_add, tick_nonce)) {
             /* SSLfatal() already called */
             goto err;
diff --git a/test/sslapitest.c b/test/sslapitest.c
index baf0881..6e08795 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -1847,11 +1847,14 @@ static unsigned int psk_server_cb(SSL *ssl, const char *identity,
 static int setupearly_data_test(SSL_CTX **cctx, SSL_CTX **sctx, SSL **clientssl,
                                 SSL **serverssl, SSL_SESSION **sess, int idx)
 {
-    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_max_early_data(*sctx,
-                                                 SSL3_RT_MAX_PLAIN_LENGTH)))
+    if (*sctx == NULL
+            && !TEST_true(create_ssl_ctx_pair(TLS_server_method(),
+                                              TLS_client_method(),
+                                              TLS1_VERSION, TLS_MAX_VERSION,
+                                              sctx, cctx, cert, privkey)))
+        return 0;
+
+    if (!TEST_true(SSL_CTX_set_max_early_data(*sctx, SSL3_RT_MAX_PLAIN_LENGTH)))
         return 0;
 
     if (idx == 1) {
@@ -2156,12 +2159,65 @@ static int test_early_data_read_write(int idx)
     return testresult;
 }
 
-static int test_early_data_replay(int idx)
+static int allow_ed_cb_called = 0;
+
+static int allow_early_data_cb(SSL *s, void *arg)
+{
+    int *usecb = (int *)arg;
+
+    allow_ed_cb_called++;
+
+    if (*usecb == 1)
+        return 0;
+
+    return 1;
+}
+
+/*
+ * idx == 0: Standard early_data setup
+ * idx == 1: early_data setup using read_ahead
+ * usecb == 0: Don't use a custom early data callback
+ * usecb == 1: Use a custom early data callback and reject the early data
+ * usecb == 2: Use a custom early data callback and accept the early data
+ * confopt == 0: Configure anti-replay directly
+ * confopt == 1: Configure anti-replay using SSL_CONF
+ */
+static int test_early_data_replay_int(int idx, int usecb, int confopt)
 {
     SSL_CTX *cctx = NULL, *sctx = NULL;
     SSL *clientssl = NULL, *serverssl = NULL;
     int testresult = 0;
     SSL_SESSION *sess = NULL;
+    size_t readbytes, written;
+    unsigned char buf[20];
+
+    allow_ed_cb_called = 0;
+
+    if (!TEST_true(create_ssl_ctx_pair(TLS_server_method(), TLS_client_method(),
+                                       TLS1_VERSION, TLS_MAX_VERSION, &sctx,
+                                       &cctx, cert, privkey)))
+        return 0;
+
+    if (usecb > 0) {
+        if (confopt == 0) {
+            SSL_CTX_set_options(sctx, SSL_OP_NO_ANTI_REPLAY);
+        } else {
+            SSL_CONF_CTX *confctx = SSL_CONF_CTX_new();
+
+            if (!TEST_ptr(confctx))
+                goto end;
+            SSL_CONF_CTX_set_flags(confctx, SSL_CONF_FLAG_FILE
+                                            | SSL_CONF_FLAG_SERVER);
+            SSL_CONF_CTX_set_ssl_ctx(confctx, sctx);
+            if (!TEST_int_eq(SSL_CONF_cmd(confctx, "Options", "-AntiReplay"),
+                             2)) {
+                SSL_CONF_CTX_free(confctx);
+                goto end;
+            }
+            SSL_CONF_CTX_free(confctx);
+        }
+        SSL_CTX_set_allow_early_data_cb(sctx, allow_early_data_cb, &usecb);
+    }
 
     if (!TEST_true(setupearly_data_test(&cctx, &sctx, &clientssl,
                                         &serverssl, &sess, idx)))
@@ -2183,14 +2239,49 @@ static int test_early_data_replay(int idx)
 
     if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl,
                                       &clientssl, NULL, NULL))
-            || !TEST_true(SSL_set_session(clientssl, sess))
-            || !TEST_true(create_ssl_connection(serverssl, clientssl,
-                          SSL_ERROR_NONE))
-               /*
-                * This time we should not have resumed the session because we
-                * already used it once.
-                */
-            || !TEST_false(SSL_session_reused(clientssl)))
+            || !TEST_true(SSL_set_session(clientssl, sess)))
+        goto end;
+
+    /* Write and read some early data */
+    if (!TEST_true(SSL_write_early_data(clientssl, MSG1, strlen(MSG1),
+                                        &written))
+            || !TEST_size_t_eq(written, strlen(MSG1)))
+        goto end;
+
+    if (usecb <= 1) {
+        if (!TEST_int_eq(SSL_read_early_data(serverssl, buf, sizeof(buf),
+                                             &readbytes),
+                         SSL_READ_EARLY_DATA_FINISH)
+                   /*
+                    * The ticket was reused, so the we should have rejected the
+                    * early data
+                    */
+                || !TEST_int_eq(SSL_get_early_data_status(serverssl),
+                                SSL_EARLY_DATA_REJECTED))
+            goto end;
+    } else {
+        /* In this case the callback decides to accept the early data */
+        if (!TEST_int_eq(SSL_read_early_data(serverssl, buf, sizeof(buf),
+                                             &readbytes),
+                         SSL_READ_EARLY_DATA_SUCCESS)
+                || !TEST_mem_eq(MSG1, strlen(MSG1), buf, readbytes)
+                   /*
+                    * Server will have sent its flight so client can now send
+                    * end of early data and complete its half of the handshake
+                    */
+                || !TEST_int_gt(SSL_connect(clientssl), 0)
+                || !TEST_int_eq(SSL_read_early_data(serverssl, buf, sizeof(buf),
+                                             &readbytes),
+                                SSL_READ_EARLY_DATA_FINISH)
+                || !TEST_int_eq(SSL_get_early_data_status(serverssl),
+                                SSL_EARLY_DATA_ACCEPTED))
+            goto end;
+    }
+
+    /* Complete the connection */
+    if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE))
+            || !TEST_int_eq(SSL_session_reused(clientssl), (usecb > 0) ? 1 : 0)
+            || !TEST_int_eq(allow_ed_cb_called, usecb > 0 ? 1 : 0))
         goto end;
 
     testresult = 1;
@@ -2207,6 +2298,18 @@ static int test_early_data_replay(int idx)
     return testresult;
 }
 
+static int test_early_data_replay(int idx)
+{
+    int ret = 1, usecb, confopt;
+
+    for (usecb = 0; usecb < 3; usecb++) {
+        for (confopt = 0; confopt < 2; confopt++)
+            ret &= test_early_data_replay_int(idx, usecb, confopt);
+    }
+
+    return ret;
+}
+
 /*
  * Helper function to test that a server attempting to read early data can
  * handle a connection from a client where the early data should be skipped.
diff --git a/util/libssl.num b/util/libssl.num
index 3495903..df6a71e 100644
--- a/util/libssl.num
+++ b/util/libssl.num
@@ -490,3 +490,5 @@ 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:
+SSL_CTX_set_allow_early_data_cb         494	1_1_1	EXIST::FUNCTION:
+SSL_set_allow_early_data_cb             495	1_1_1	EXIST::FUNCTION:
diff --git a/util/private.num b/util/private.num
index ac536a5..b90e33d 100644
--- a/util/private.num
+++ b/util/private.num
@@ -48,7 +48,9 @@ RAND_DRBG_cleanup_nonce_fn              datatype
 RAND_DRBG_get_entropy_fn                datatype
 RAND_DRBG_get_nonce_fn                  datatype
 RAND_poll_cb                            datatype
+SSL_CTX_allow_early_data_cb_fn          datatype
 SSL_CTX_keylog_cb_func                  datatype
+SSL_allow_early_data_cb_fn              datatype
 SSL_client_hello_cb_fn                  datatype
 SSL_psk_client_cb_func                  datatype
 SSL_psk_find_session_cb_func            datatype


More information about the openssl-commits mailing list