[openssl-commits] [openssl] master update

Richard Levitte levitte at openssl.org
Thu Feb 23 19:21:50 UTC 2017


The branch master has been updated
       via  2afaee5193c3f567ea00ad613f3ee82f985b7141 (commit)
       via  8e2236eff8e38109a57347c8ad795040b380c936 (commit)
       via  694c9180d7f082b896692048052413fc5dc4e467 (commit)
       via  0f82d2f5841fd1c0883f1570632f74c33ce2034c (commit)
       via  80de0c5947cf29ef04227714f3cae5c615012449 (commit)
       via  6b1bb98fad044a6f6b1aec9daee95d6cb450210e (commit)
       via  ddf972583439346cbaa99031fe8d3116018ba711 (commit)
       via  6e3dac1995eb6e34ed2649aca78b5aa88b1cbfeb (commit)
       via  90134d9806f0191bc0eb0cde2750f0cd68667a6d (commit)
       via  ccb8e6e0b1c536430290a87ba5c87dc072cc5a12 (commit)
       via  60d685d196e8d594d754751e4852f01d80d8c0cc (commit)
       via  650c6e41d60905fa1396dff2c7fe4d6fbb7239ba (commit)
       via  cb7a1f5fca4da49dbad1b3f453e7446baa23c1fe (commit)
       via  26f426846ec3798a27f19600f05e4e41446d5b4f (commit)
       via  fc5ece2ee4c7ef41759c2267eccbe08df3245721 (commit)
      from  46958a043d51633ed36bcfb13ff048a3381366a6 (commit)


- Log -----------------------------------------------------------------
commit 2afaee5193c3f567ea00ad613f3ee82f985b7141
Author: Benjamin Kaduk <bkaduk at akamai.com>
Date:   Mon Feb 13 12:42:43 2017 -0600

    Add an sslapitest for early callback
    
    Make sure that we can stop handshake processing and resume it later.
    Also check that the cipher list and compression methods are sane.
    Unfortunately, we don't have the client-side APIs needed to force
    a specific (known) session ID to be sent in the ClientHello, so
    that accessor cannot be tested here.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2279)

commit 8e2236eff8e38109a57347c8ad795040b380c936
Author: Benjamin Kaduk <bkaduk at akamai.com>
Date:   Mon Feb 13 15:10:54 2017 -0600

    Let test handshakes stop on certain errors
    
    Certain callback APIs allow the callback to request async processing
    by trickling a particular error value up the stack to the application
    as an error return from the handshake function.  In those cases,
    SSL_want() returns a code specific to the type of async processing
    needed.
    
    The create_ssl_connection() helper function for the tests is very
    helpful for several things, including creating API tests.  However,
    it does not currently let us test the async processing functionality
    of these callback interfaces, because the special SSL error codes
    are treated as generic errors and the helper continues to loop until
    it reaches its maximum iteration count.
    
    Add a new parameter, 'want', that indicates an expected/desired
    special SSL error code, so that the helper will terminate when
    either side reports that error, giving control back to the calling
    function and allowing the test to proceed.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2279)

commit 694c9180d7f082b896692048052413fc5dc4e467
Author: Benjamin Kaduk <bkaduk at akamai.com>
Date:   Mon Feb 13 14:14:06 2017 -0600

    Use correct variable in test diagnostic
    
    create_ssl_connection() prints out the results if SSL_accept() and/or
    SSL_connect() fail, but was reusing the client return value when printing
    about SSL_accept() failures.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2279)

commit 0f82d2f5841fd1c0883f1570632f74c33ce2034c
Author: Benjamin Kaduk <bkaduk at akamai.com>
Date:   Thu Feb 9 17:21:42 2017 -0600

    Adopt test to changed behavior
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2279)

commit 80de0c5947cf29ef04227714f3cae5c615012449
Author: Benjamin Kaduk <bkaduk at akamai.com>
Date:   Tue Jan 31 16:06:30 2017 -0600

    Tests for SSL early callback
    
    Plumb things through in the same place as the SNI callback, since
    we recommend that the early callback replace (and supplement) the
    SNI callback, and add a few test cases.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2279)

commit 6b1bb98fad044a6f6b1aec9daee95d6cb450210e
Author: Benjamin Kaduk <bkaduk at akamai.com>
Date:   Mon Jan 23 17:03:16 2017 -0600

    Add SSL_CTX early callback
    
    Provide a callback interface that gives the application the ability
    to adjust the nascent SSL object at the earliest stage of ClientHello
    processing, immediately after extensions have been collected but
    before they have been processed.
    
    This is akin to BoringSSL's "select_certificate_cb" (though it is not
    API compatible), and as the name indicates, one major use is to examine
    the supplied server name indication and select what certificate to
    present to the client.  However, it can also be used to make more
    sweeping configuration changes to the SSL object according to the
    selected server identity and configuration.  That may include adjusting
    the permitted TLS versions, swapping out the SSL_CTX object (as is
    traditionally done in a tlsext_servername_callback), changing the
    server's cipher list, and more.
    
    We also wish to allow an early callback to indicate that it needs to perform
    additional work asynchronously and resume processing later.  To that effect,
    refactor the second half of tls_process_client_hello() into a subroutine to be
    called at the post-processing stage (including the early callback itself), to
    allow the callback to result in remaining in the same work stage for a later
    call to succeed.  This requires allocating for and storing the CLIENTHELLO_MSG
    in the SSL object to be preserved across such calls, but the storage is
    reclaimed after ClientHello processing finishes.
    
    Information about the CliehtHello is available to the callback by means of
    accessor functions that can only be used from the early callback.  This allows
    extensions to make use of the existing internal parsing machinery without
    exposing structure internals (e.g., of PACKET), so that applications do not
    have to write fragile parsing code.
    
    Applications are encouraged to utilize an early callback and not use
    a servername_callback, in order to avoid unexpected behavior that
    occurs due to the relative order of processing between things like
    session resumption and the historical servername callback.
    
    Also tidy up nearby style by removing unnecessary braces around one-line
    conditional bodies.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2279)

commit ddf972583439346cbaa99031fe8d3116018ba711
Author: Benjamin Kaduk <bkaduk at akamai.com>
Date:   Mon Feb 6 15:33:28 2017 -0600

    Prepare for WORK_MORE_C
    
    Add the new enum value and case statements as appropriate.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2279)

commit 6e3dac1995eb6e34ed2649aca78b5aa88b1cbfeb
Author: Benjamin Kaduk <bkaduk at akamai.com>
Date:   Mon Jan 30 12:59:59 2017 -0600

    Tests for SSL_bytes_to_cipher_list()
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2279)

commit 90134d9806f0191bc0eb0cde2750f0cd68667a6d
Author: Benjamin Kaduk <bkaduk at akamai.com>
Date:   Mon Jan 30 19:20:14 2017 -0600

    Refactor SSL_bytes_to_cipher_list()
    
    Split off the portions that mutate the SSL object into a separate
    function that the state machine calls, so that the public API can
    be a pure function.  (It still needs the SSL parameter in order
    to determine what SSL_METHOD's get_cipher_by_char() routine to use,
    though.)
    
    Instead of returning the stack of ciphers (functionality that was
    not used internally), require using the output parameter, and add
    a separate output parameter for the SCSVs contained in the supplied
    octets, if desired.  This lets us move to the standard return value
    convention.  Also make both output stacks optional parameters.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2279)

commit ccb8e6e0b1c536430290a87ba5c87dc072cc5a12
Author: Benjamin Kaduk <bkaduk at akamai.com>
Date:   Mon Jan 30 11:24:17 2017 -0600

    Export SSL_bytes_to_cipher_list()
    
    Move ssl_bytes_to_cipher_list() to ssl_lib.c and create a public
    wrapper around it.  This lets application early callbacks easily get
    SSL_CIPHER objects from the raw ciphers bytes without having to
    reimplement the parsing code.  In particular, they do not need to
    know the details of the sslv2 format ClientHello's ciphersuite
    specifications.
    
    Document the new public function, including the arguably buggy behavior
    of modifying the supplied SSL object.  On the face of it, such a function
    should be able to be pure, just a direct translation of wire octets to
    internal data structures.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2279)

commit 60d685d196e8d594d754751e4852f01d80d8c0cc
Author: Benjamin Kaduk <bkaduk at akamai.com>
Date:   Mon Feb 6 11:30:16 2017 -0600

    Let ssl_get_cipher_by_char yield not-valid ciphers
    
    Now that we have made SCSVs into more of a first-class object, provide
    a way for the bytes-to-SSL_CIPHER conversion to actually return them.
    Add a flag 'all' to ssl_get_cipher_by_char to indicate that we want
    all the known ciphers, not just the ones valid for encryption.  This will,
    in practice, let the caller retrieve the SCSVs.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2279)

commit 650c6e41d60905fa1396dff2c7fe4d6fbb7239ba
Author: Benjamin Kaduk <bkaduk at akamai.com>
Date:   Fri Feb 3 17:17:21 2017 -0600

    Add more first-class support for SCSVS
    
    Just as we have a table of ssl3_ciphers, add a table of ssl3_scsvs, to contain
    SSL_CIPHER objects for these non-valid ciphers.  This will allow for unified
    handling of such indicators, especially as we are preparing to pass them around
    between functions.
    
    Since the 'valid' field is not set for the SCSVs, they should not be used
    for anything requiring a cryptographic cipher (as opposed to something
    being stuck in a cipher-shaped hole in the TLS wire protocol).
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2279)

commit cb7a1f5fca4da49dbad1b3f453e7446baa23c1fe
Author: Benjamin Kaduk <bkaduk at akamai.com>
Date:   Mon Jan 23 17:00:47 2017 -0600

    Move CLIENTHELLO_MSG up in the header
    
    We'll be adding a field of this type to struct ssl_st in a subsequent
    commit, and need the type definition to be in scope already.
    Also move up the RAW_EXTENSION definition that it depends on.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2279)

commit 26f426846ec3798a27f19600f05e4e41446d5b4f
Author: Benjamin Kaduk <bkaduk at akamai.com>
Date:   Mon Jan 23 16:59:23 2017 -0600

    Store the number of extensions in CLIENTHELLO_MSG
    
    Keep track of the length of the pre_proc_exts array.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2279)

commit fc5ece2ee4c7ef41759c2267eccbe08df3245721
Author: Benjamin Kaduk <bkaduk at akamai.com>
Date:   Mon Jan 23 16:56:43 2017 -0600

    output number of exts from tls_collect_extensions()
    
    Modify the API of tls_collect_extensions() to be able to output the number of
    extensions that are known (i.e., the length of its 'res' output).  This number
    can never be zero on a successful return due to the builtin extensions list,
    but use a separate output variable so as to not overload the return value
    semantics.
    
    Having this value easily available will give consumers a way to avoid repeating
    the calculation.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/2279)

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

Summary of changes:
 doc/man3/SSL_CTX_set_early_cb.pod                  | 110 +++++
 doc/man3/SSL_get_ciphers.pod                       |  17 +-
 doc/man3/SSL_get_error.pod                         |  10 +-
 doc/man3/SSL_want.pod                              |  22 +-
 include/openssl/ssl.h                              |  23 +
 ssl/s3_lib.c                                       |  30 +-
 ssl/ssl_ciph.c                                     |   5 +-
 ssl/ssl_err.c                                      |   6 +-
 ssl/ssl_lib.c                                      | 251 ++++++++++-
 ssl/ssl_locl.h                                     |  73 ++--
 ssl/statem/extensions.c                            |  10 +-
 ssl/statem/statem.c                                |   3 +
 ssl/statem/statem.h                                |   4 +-
 ssl/statem/statem_clnt.c                           |  14 +-
 ssl/statem/statem_locl.h                           |   2 +-
 ssl/statem/statem_srvr.c                           | 472 +++++++++------------
 test/asynciotest.c                                 |   2 +-
 test/build.info                                    |   6 +-
 test/cipherbytes_test.c                            | 130 ++++++
 test/dtls_mtu_test.c                               |   2 +-
 test/dtlstest.c                                    |   2 +-
 test/handshake_helper.c                            | 118 +++++-
 test/recipes/70-test_sslrecords.t                  |   2 +-
 ...{80-test_cipherlist.t => 80-test_cipherbytes.t} |   6 +-
 test/ssl-tests/05-sni.conf                         | 107 ++++-
 test/ssl-tests/05-sni.conf.in                      |  57 +++
 test/ssl_test_ctx.c                                |   3 +
 test/ssl_test_ctx.h                                |   5 +-
 test/sslapitest.c                                  | 104 ++++-
 test/sslcorrupttest.c                              |   2 +-
 test/ssltestlib.c                                  |   8 +-
 test/ssltestlib.h                                  |   2 +-
 util/libssl.num                                    |   9 +
 33 files changed, 1261 insertions(+), 356 deletions(-)
 create mode 100644 doc/man3/SSL_CTX_set_early_cb.pod
 create mode 100644 test/cipherbytes_test.c
 copy test/recipes/{80-test_cipherlist.t => 80-test_cipherbytes.t} (78%)

diff --git a/doc/man3/SSL_CTX_set_early_cb.pod b/doc/man3/SSL_CTX_set_early_cb.pod
new file mode 100644
index 0000000..b007292
--- /dev/null
+++ b/doc/man3/SSL_CTX_set_early_cb.pod
@@ -0,0 +1,110 @@
+=pod
+
+=head1 NAME
+
+SSL_CTX_set_early_cb, SSL_early_cb_fn, SSL_early_isv2, SSL_early_get0_legacy_version, SSL_early_get0_random, SSL_early_get0_session_id, SSL_early_get0_ciphers, SSL_early_get0_compression_methods, SSL_early_get0_ext - callback functions for early server-side ClientHello processing
+
+=head1 SYNOPSIS
+
+ typedef int (*SSL_early_cb_fn)(SSL *s, int *al, void *arg);
+ void SSL_CTX_set_early_cb(SSL_CTX *c, SSL_early_cb_fn *f, void *arg);
+ int SSL_early_isv2(SSL *s);
+ unsigned int SSL_early_get0_legacy_version(SSL *s);
+ size_t SSL_early_get0_random(SSL *s, const unsigned char **out);
+ size_t SSL_early_get0_session_id(SSL *s, const unsigned char **out);
+ size_t SSL_early_get0_ciphers(SSL *s, const unsigned char **out);
+ size_t SSL_early_get0_compression_methods(SSL *s, const unsigned char **out);
+ int SSL_early_get0_ext(SSL *s, int type, const unsigned char **out,
+                        size_t *outlen);
+
+=head1 DESCRIPTION
+
+SSL_CTX_set_early_cb() sets the callback function, which is automatically
+called during the early stages of ClientHello processing on the server.
+The argument supplied when setting the callback is passed back to the
+callback at runtime.  A callback that returns failure (0) will cause the
+connection to terminate, and callbacks returning failure should indicate
+what alert value is to be sent in the B<al> parameter.  A callback may
+also return a negative value to suspend the handshake, and the handshake
+function will return immediately.  L<SSL_get_error(3)> will return
+SSL_ERROR_WANT_EARLY to indicate that the handshake was suspended.
+It is the job of the early callback to store information about the state
+of the last call if needed to continue.  On the next call into the handshake
+function, the early callback will be called again, and, if it returns
+success, normal handshake processing will continue from that point.
+
+SSL_early_isv2() indicates whether the ClientHello was carried in a
+SSLv2 record and is in the SSLv2 format.  The SSLv2 format has substantial
+differences from the normal SSLv3 format, including using three bytes per
+cipher suite, and not allowing extensions.  Additionally, the SSLv2 format
+'challenge' field is exposed via SSL_early_get0_random(), padded to
+SSL3_RANDOM_SIZE bytes with zeros if needed.  For SSLv2 format ClientHellos,
+SSL_early_get0_compression_methods() returns a dummy list that only includes
+the null compression method, since the SSLv2 format does not include a
+mechanism by which to negotiate compression.
+
+SSL_early_get0_random(), SSL_early_get0_session_id(), SSL_early_get0_ciphers(),
+and SSL_early_get0_compression_methods() provide access to the corresponding
+ClientHello fields, returning the field length and optionally setting an
+out pointer to the octets of that field.
+
+Similarly, SSL_early_get0_ext() provides access to individual extensions
+from the ClientHello on a per-extension basis.  For the provided wire
+protocol extension type value, the extension value and length are returned
+in the output parameters (if present).
+
+=head1 NOTES
+
+The early callback provides a vast window of possibilities for application
+code to affect the TLS handshake.  A primary use of the callback is to
+allow the server to examine the server name indication extension provided
+by the client in order to select an appropriate certificate to present,
+and make other configuration adjustments relevant to that server name
+and its configuration.  Such configuration changes can include swapping out
+the associated SSL_CTX pointer, modifying the server's list of permitted TLS
+versions, changing the server's cipher list, etc.
+
+It is also recommended that applications utilize an early callback and
+not use a servername callback, in order to avoid unexpected behavior that
+occurs due to the relative order of processing between things like session
+resumption and the historical servername callback.
+
+The SSL_early_* family of functions may only be called from code executing
+within an early callback.
+
+=head1 RETURN VALUES
+
+The application's supplied early callback returns 1 on success, 0 on failure,
+and a negative value to suspend processing.
+
+SSL_early_isv2() returns 1 for SSLv2-format ClientHellos and 0 otherwise.
+
+SSL_early_get0_random(), SSL_early_get0_session_id(), SSL_early_get0_ciphers(),
+and SSL_early_get0_compression_methods() return the length of the corresponding
+ClientHello fields.  If zero is returned, the ouput pointer should not be
+assumed to be valid.
+
+SSL_early_get0_ext() returns 1 if the extension of type 'type' is present, and
+0 otherwise.
+
+=head1 SEE ALSO
+
+L<ssl(7)>, L<SSL_CTX_set_tlsext_servername_callback(3)>
+
+=head1 HISTORY
+
+The SSL early callback, SSL_early_isv2(), SSL_early_get0_random(),
+SSL_early_get0_session_id(), SSL_early_get0_ciphers(),
+SSL_early_get0_compression_methods(), and SSL_early_get0_ext() were
+added in OpenSSL 1.1.1.
+
+=head1 COPYRIGHT
+
+Copyright 2017 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_get_ciphers.pod b/doc/man3/SSL_get_ciphers.pod
index ce0be6e..d1baafe 100644
--- a/doc/man3/SSL_get_ciphers.pod
+++ b/doc/man3/SSL_get_ciphers.pod
@@ -3,7 +3,8 @@
 =head1 NAME
 
 SSL_get1_supported_ciphers, SSL_get_client_ciphers,
-SSL_get_ciphers, SSL_CTX_get_ciphers, SSL_get_cipher_list
+SSL_get_ciphers, SSL_CTX_get_ciphers,
+SSL_bytes_to_cipher_list, SSL_get_cipher_list
 - get list of available SSL_CIPHERs
 
 =head1 SYNOPSIS
@@ -14,6 +15,9 @@ SSL_get_ciphers, SSL_CTX_get_ciphers, SSL_get_cipher_list
  STACK_OF(SSL_CIPHER) *SSL_CTX_get_ciphers(const SSL_CTX *ctx);
  STACK_OF(SSL_CIPHER) *SSL_get1_supported_ciphers(SSL *s);
  STACK_OF(SSL_CIPHER) *SSL_get_client_ciphers(const SSL *ssl);
+ int SSL_bytes_to_cipher_list(SSL *s, const unsigned char *bytes, size_t len,
+                              int isv2format, STACK_OF(SSL_CIPHER) **sk,
+                              STACK_OF(SSL_CIPHER) **scsvs);
  const char *SSL_get_cipher_list(const SSL *ssl, int priority);
 
 =head1 DESCRIPTION
@@ -41,6 +45,14 @@ SSL_get_client_ciphers() returns the stack of available SSL_CIPHERs matching the
 list received from the client on B<ssl>. If B<ssl> is NULL, no ciphers are
 available, or B<ssl> is not operating in server mode, NULL is returned.
 
+SSL_bytes_to_cipher_list() treats the supplied B<len> octets in B<bytes>
+as a wire-protocol cipher suite specification (in the three-octet-per-cipher
+SSLv2 wire format if B<isv2format> is nonzero; otherwise the two-octet
+SSLv3/TLS wire format), and parses the cipher suites supported by the library
+into the returned stacks of SSL_CIPHER objects sk and Signalling Cipher-Suite
+Values scsvs.  Unsupported cipher suites are ignored.  Returns 1 on success
+and 0 on failure.
+
 SSL_get_cipher_list() returns a pointer to the name of the SSL_CIPHER
 listed for B<ssl> with B<priority>. If B<ssl> is NULL, no ciphers are
 available, or there are less ciphers than B<priority> available, NULL
@@ -63,6 +75,9 @@ free the return value itself.
 The stack returned by SSL_get1_supported_ciphers() should be freed using
 sk_SSL_CIPHER_free().
 
+The stacks returned by SSL_bytes_to_cipher_list() should be freed using
+sk_SSL_CIPHER_free().
+
 =head1 RETURN VALUES
 
 See DESCRIPTION
diff --git a/doc/man3/SSL_get_error.pod b/doc/man3/SSL_get_error.pod
index db8f85c..e318de8 100644
--- a/doc/man3/SSL_get_error.pod
+++ b/doc/man3/SSL_get_error.pod
@@ -110,6 +110,13 @@ through a call to L<ASYNC_init_thread(3)>. The application should retry the
 operation after a currently executing asynchronous operation for the current
 thread has completed.
 
+=item SSL_ERROR_WANT_EARLY
+
+The operation did not complete because an application callback set by
+SSL_CTX_set_early_cb() has asked to be called again.
+The TLS/SSL I/O function should be called again later.
+Details depend on the application.
+
 =item SSL_ERROR_SYSCALL
 
 Some non-recoverable I/O error occurred.
@@ -130,10 +137,11 @@ L<ssl(7)>, L<err(7)>
 =head1 HISTORY
 
 SSL_ERROR_WANT_ASYNC was added in OpenSSL 1.1.0.
+SSL_ERROR_WANT_EARLY was added in OpenSSL 1.1.1.
 
 =head1 COPYRIGHT
 
-Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2000-2017 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
diff --git a/doc/man3/SSL_want.pod b/doc/man3/SSL_want.pod
index c86344e..8efe50b 100644
--- a/doc/man3/SSL_want.pod
+++ b/doc/man3/SSL_want.pod
@@ -3,8 +3,8 @@
 =head1 NAME
 
 SSL_want, SSL_want_nothing, SSL_want_read, SSL_want_write, SSL_want_x509_lookup,
-SSL_want_async, SSL_want_async_job - obtain state information TLS/SSL I/O
-operation
+SSL_want_async, SSL_want_async_job, SSL_want_early - obtain state information
+TLS/SSL I/O operation
 
 =head1 SYNOPSIS
 
@@ -17,6 +17,7 @@ operation
  int SSL_want_x509_lookup(const SSL *ssl);
  int SSL_want_async(const SSL *ssl);
  int SSL_want_async_job(const SSL *ssl);
+ int SSL_want_early(const SSL *ssl);
 
 =head1 DESCRIPTION
 
@@ -81,19 +82,30 @@ The asynchronous job could not be started because there were no async jobs
 available in the pool (see ASYNC_init_thread(3)). A call to L<SSL_get_error(3)>
 should return SSL_ERROR_WANT_ASYNC_JOB.
 
+=item SSL_EARLY_WORK
+
+The operation did not complete because an application callback set by
+SSL_CTX_set_early_cb() has asked to be called again.
+A call to L<SSL_get_error(3)> should return
+SSL_ERROR_WANT_EARLY.
+
 =back
 
 SSL_want_nothing(), SSL_want_read(), SSL_want_write(), SSL_want_x509_lookup(),
-SSL_want_async() and SSL_want_async_job() return 1, when the corresponding
-condition is true or 0 otherwise.
+SSL_want_async(), SSL_want_async_job(), and SSL_want_early() return 1, when
+the corresponding condition is true or 0 otherwise.
 
 =head1 SEE ALSO
 
 L<ssl(7)>, L<err(7)>, L<SSL_get_error(3)>
 
+=head1 HISTORY
+
+SSL_want_early() and SSL_EARLY_WORK were added in OpenSSL 1.1.1.
+
 =head1 COPYRIGHT
 
-Copyright 2001-2016 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2001-2017 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
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 1c58dcc..d1614d3 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -764,6 +764,7 @@ __owur int SSL_extension_supported(unsigned int ext_type);
 # define SSL_X509_LOOKUP        4
 # define SSL_ASYNC_PAUSED       5
 # define SSL_ASYNC_NO_JOBS      6
+# define SSL_EARLY_WORK         7
 
 /* These will only be used when doing non-blocking IO */
 # define SSL_want_nothing(s)     (SSL_want(s) == SSL_NOTHING)
@@ -772,6 +773,7 @@ __owur int SSL_extension_supported(unsigned int ext_type);
 # define SSL_want_x509_lookup(s) (SSL_want(s) == SSL_X509_LOOKUP)
 # define SSL_want_async(s)       (SSL_want(s) == SSL_ASYNC_PAUSED)
 # define SSL_want_async_job(s)   (SSL_want(s) == SSL_ASYNC_NO_JOBS)
+# define SSL_want_early(s)       (SSL_want(s) == SSL_EARLY_WORK)
 
 # define SSL_MAC_FLAG_READ_MAC_STREAM 1
 # define SSL_MAC_FLAG_WRITE_MAC_STREAM 2
@@ -1041,6 +1043,7 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
 # define SSL_ERROR_WANT_ACCEPT           8
 # define SSL_ERROR_WANT_ASYNC            9
 # define SSL_ERROR_WANT_ASYNC_JOB       10
+# define SSL_ERROR_WANT_EARLY           11
 # define SSL_CTRL_SET_TMP_DH                     3
 # define SSL_CTRL_SET_TMP_ECDH                   4
 # define SSL_CTRL_SET_TMP_DH_CB                  6
@@ -1572,6 +1575,20 @@ __owur char *SSL_get_srp_username(SSL *s);
 __owur char *SSL_get_srp_userinfo(SSL *s);
 # endif
 
+/*
+ * Early callback and helpers.
+ */
+typedef int (*SSL_early_cb_fn) (SSL *s, int *al, void *arg);
+void SSL_CTX_set_early_cb(SSL_CTX *c, SSL_early_cb_fn cb, void *arg);
+int SSL_early_isv2(SSL *s);
+unsigned int SSL_early_get0_legacy_version(SSL *s);
+size_t SSL_early_get0_random(SSL *s, const unsigned char **out);
+size_t SSL_early_get0_session_id(SSL *s, const unsigned char **out);
+size_t SSL_early_get0_ciphers(SSL *s, const unsigned char **out);
+size_t SSL_early_get0_compression_methods(SSL *s, const unsigned char **out);
+int SSL_early_get0_ext(SSL *s, unsigned int type, const unsigned char **out,
+                       size_t *outlen);
+
 void SSL_certs_clear(SSL *s);
 void SSL_free(SSL *ssl);
 # ifdef OSSL_ASYNC_FD
@@ -1820,6 +1837,9 @@ __owur int SSL_COMP_add_compression_method(int id, COMP_METHOD *cm);
 const SSL_CIPHER *SSL_CIPHER_find(SSL *ssl, const unsigned char *ptr);
 int SSL_CIPHER_get_cipher_nid(const SSL_CIPHER *c);
 int SSL_CIPHER_get_digest_nid(const SSL_CIPHER *c);
+int SSL_bytes_to_cipher_list(SSL *s, const unsigned char *bytes, size_t len,
+                             int isv2format, STACK_OF(SSL_CIPHER) **sk,
+                             STACK_OF(SSL_CIPHER) **scsvs);
 
 /* TLS extensions functions */
 __owur int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len);
@@ -2087,6 +2107,7 @@ int ERR_load_SSL_strings(void);
 /* Function codes. */
 # define SSL_F_ADD_CLIENT_KEY_SHARE_EXT                   438
 # define SSL_F_ADD_KEY_SHARE                              512
+# define SSL_F_BYTES_TO_CIPHER_LIST                       519
 # define SSL_F_CHECK_SUITEB_CIPHER_LIST                   331
 # define SSL_F_CT_MOVE_SCTS                               345
 # define SSL_F_CT_STRICT                                  349
@@ -2160,6 +2181,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_SSL_BAD_METHOD                             160
 # define SSL_F_SSL_BUILD_CERT_CHAIN                       332
 # define SSL_F_SSL_BYTES_TO_CIPHER_LIST                   161
+# define SSL_F_SSL_CACHE_CIPHERLIST                       520
 # define SSL_F_SSL_CERT_ADD0_CHAIN_CERT                   346
 # define SSL_F_SSL_CERT_DUP                               221
 # define SSL_F_SSL_CERT_NEW                               162
@@ -2343,6 +2365,7 @@ int ERR_load_SSL_strings(void);
 # define SSL_F_TLS_CONSTRUCT_STOC_SESSION_TICKET          460
 # define SSL_F_TLS_CONSTRUCT_STOC_STATUS_REQUEST          461
 # define SSL_F_TLS_CONSTRUCT_STOC_USE_SRTP                462
+# define SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO        521
 # define SSL_F_TLS_GET_MESSAGE_BODY                       351
 # define SSL_F_TLS_GET_MESSAGE_HEADER                     387
 # define SSL_F_TLS_PARSE_CLIENTHELLO_TLSEXT               449
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index 6932311..6449f8c 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -55,6 +55,7 @@
 #include <openssl/rand.h>
 
 #define SSL3_NUM_CIPHERS        OSSL_NELEM(ssl3_ciphers)
+#define SSL3_NUM_SCSVS          OSSL_NELEM(ssl3_scsvs)
 
 /*
  * The list of available ciphers, mostly organized into the following
@@ -2797,6 +2798,26 @@ static SSL_CIPHER ssl3_ciphers[] = {
 
 };
 
+/*
+ * The list of known Signalling Cipher-Suite Value "ciphers", non-valid
+ * values stuffed into the ciphers field of the wire protocol for signalling
+ * purposes.
+ */
+static SSL_CIPHER ssl3_scsvs[] = {
+    {
+     0,
+     "TLS_EMPTY_RENEGOTIATION_INFO_SCSV",
+     SSL3_CK_SCSV,
+     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    },
+    {
+     0,
+     "TLS_FALLBACK_SCSV",
+     SSL3_CK_FALLBACK_SCSV,
+     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    },
+};
+
 static int cipher_compare(const void *a, const void *b)
 {
     const SSL_CIPHER *ap = (const SSL_CIPHER *)a;
@@ -2807,8 +2828,9 @@ static int cipher_compare(const void *a, const void *b)
 
 void ssl_sort_cipher_list(void)
 {
-    qsort(ssl3_ciphers, OSSL_NELEM(ssl3_ciphers), sizeof ssl3_ciphers[0],
+    qsort(ssl3_ciphers, SSL3_NUM_CIPHERS, sizeof ssl3_ciphers[0],
           cipher_compare);
+    qsort(ssl3_scsvs, SSL3_NUM_SCSVS, sizeof ssl3_scsvs[0], cipher_compare);
 }
 
 const SSL3_ENC_METHOD SSLv3_enc_data = {
@@ -3598,9 +3620,13 @@ long ssl3_ctx_callback_ctrl(SSL_CTX *ctx, int cmd, void (*fp) (void))
 const SSL_CIPHER *ssl3_get_cipher_by_id(uint32_t id)
 {
     SSL_CIPHER c;
+    const SSL_CIPHER *cp;
 
     c.id = id;
-    return OBJ_bsearch_ssl_cipher_id(&c, ssl3_ciphers, SSL3_NUM_CIPHERS);
+    cp = OBJ_bsearch_ssl_cipher_id(&c, ssl3_ciphers, SSL3_NUM_CIPHERS);
+    if (cp != NULL)
+        return cp;
+    return OBJ_bsearch_ssl_cipher_id(&c, ssl3_scsvs, SSL3_NUM_SCSVS);
 }
 
 /*
diff --git a/ssl/ssl_ciph.c b/ssl/ssl_ciph.c
index 2d2395c..e64e3da 100644
--- a/ssl/ssl_ciph.c
+++ b/ssl/ssl_ciph.c
@@ -1915,11 +1915,12 @@ int ssl_cipher_get_cert_index(const SSL_CIPHER *c)
     return -1;
 }
 
-const SSL_CIPHER *ssl_get_cipher_by_char(SSL *ssl, const unsigned char *ptr)
+const SSL_CIPHER *ssl_get_cipher_by_char(SSL *ssl, const unsigned char *ptr,
+                                         int all)
 {
     const SSL_CIPHER *c = ssl->method->get_cipher_by_char(ptr);
 
-    if (c == NULL || c->valid == 0)
+    if (c == NULL || (!all && c->valid == 0))
         return NULL;
     return c;
 }
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index 341712c..bcb9ddb 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -21,6 +21,7 @@
 static ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_FUNC(SSL_F_ADD_CLIENT_KEY_SHARE_EXT), "add_client_key_share_ext"},
     {ERR_FUNC(SSL_F_ADD_KEY_SHARE), "add_key_share"},
+    {ERR_FUNC(SSL_F_BYTES_TO_CIPHER_LIST), "bytes_to_cipher_list"},
     {ERR_FUNC(SSL_F_CHECK_SUITEB_CIPHER_LIST), "check_suiteb_cipher_list"},
     {ERR_FUNC(SSL_F_CT_MOVE_SCTS), "ct_move_scts"},
     {ERR_FUNC(SSL_F_CT_STRICT), "ct_strict"},
@@ -116,7 +117,8 @@ static ERR_STRING_DATA SSL_str_functs[] = {
      "ssl_add_serverhello_use_srtp_ext"},
     {ERR_FUNC(SSL_F_SSL_BAD_METHOD), "ssl_bad_method"},
     {ERR_FUNC(SSL_F_SSL_BUILD_CERT_CHAIN), "ssl_build_cert_chain"},
-    {ERR_FUNC(SSL_F_SSL_BYTES_TO_CIPHER_LIST), "ssl_bytes_to_cipher_list"},
+    {ERR_FUNC(SSL_F_SSL_BYTES_TO_CIPHER_LIST), "SSL_bytes_to_cipher_list"},
+    {ERR_FUNC(SSL_F_SSL_CACHE_CIPHERLIST), "ssl_cache_cipherlist"},
     {ERR_FUNC(SSL_F_SSL_CERT_ADD0_CHAIN_CERT), "ssl_cert_add0_chain_cert"},
     {ERR_FUNC(SSL_F_SSL_CERT_DUP), "ssl_cert_dup"},
     {ERR_FUNC(SSL_F_SSL_CERT_NEW), "ssl_cert_new"},
@@ -374,6 +376,8 @@ static ERR_STRING_DATA SSL_str_functs[] = {
      "tls_construct_stoc_status_request"},
     {ERR_FUNC(SSL_F_TLS_CONSTRUCT_STOC_USE_SRTP),
      "tls_construct_stoc_use_srtp"},
+    {ERR_FUNC(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO),
+     "tls_early_post_process_client_hello"},
     {ERR_FUNC(SSL_F_TLS_GET_MESSAGE_BODY), "tls_get_message_body"},
     {ERR_FUNC(SSL_F_TLS_GET_MESSAGE_HEADER), "tls_get_message_header"},
     {ERR_FUNC(SSL_F_TLS_PARSE_CLIENTHELLO_TLSEXT),
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index dea2dac..8304c73 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -1014,6 +1014,7 @@ void SSL_free(SSL *s)
 #endif
     OPENSSL_free(s->ext.ocsp.resp);
     OPENSSL_free(s->ext.alpn);
+    OPENSSL_free(s->clienthello);
 
     sk_X509_NAME_pop_free(s->client_CA, X509_NAME_free);
 
@@ -3012,15 +3013,14 @@ int SSL_get_error(const SSL *s, int i)
                 return (SSL_ERROR_SYSCALL);
         }
     }
-    if (SSL_want_x509_lookup(s)) {
+    if (SSL_want_x509_lookup(s))
         return (SSL_ERROR_WANT_X509_LOOKUP);
-    }
-    if (SSL_want_async(s)) {
+    if (SSL_want_async(s))
         return SSL_ERROR_WANT_ASYNC;
-    }
-    if (SSL_want_async_job(s)) {
+    if (SSL_want_async_job(s))
         return SSL_ERROR_WANT_ASYNC_JOB;
-    }
+    if (SSL_want_early(s))
+        return SSL_ERROR_WANT_EARLY;
 
     if ((s->shutdown & SSL_RECEIVED_SHUTDOWN) &&
         (s->s3->warn_alert == SSL_AD_CLOSE_NOTIFY))
@@ -4305,7 +4305,84 @@ const CTLOG_STORE *SSL_CTX_get0_ctlog_store(const SSL_CTX *ctx)
     return ctx->ctlog_store;
 }
 
-#endif
+#endif  /* OPENSSL_NO_CT */
+
+void SSL_CTX_set_early_cb(SSL_CTX *c, SSL_early_cb_fn cb, void *arg)
+{
+    c->early_cb = cb;
+    c->early_cb_arg = arg;
+}
+
+int SSL_early_isv2(SSL *s)
+{
+    if (s->clienthello == NULL)
+        return 0;
+    return s->clienthello->isv2;
+}
+
+unsigned int SSL_early_get0_legacy_version(SSL *s)
+{
+    if (s->clienthello == NULL)
+        return 0;
+    return s->clienthello->legacy_version;
+}
+
+size_t SSL_early_get0_random(SSL *s, const unsigned char **out)
+{
+    if (s->clienthello == NULL)
+        return 0;
+    if (out != NULL)
+        *out = s->clienthello->random;
+    return SSL3_RANDOM_SIZE;
+}
+
+size_t SSL_early_get0_session_id(SSL *s, const unsigned char **out)
+{
+    if (s->clienthello == NULL)
+        return 0;
+    if (out != NULL)
+        *out = s->clienthello->session_id;
+    return s->clienthello->session_id_len;
+}
+
+size_t SSL_early_get0_ciphers(SSL *s, const unsigned char **out)
+{
+    if (s->clienthello == NULL)
+        return 0;
+    if (out != NULL)
+        *out = PACKET_data(&s->clienthello->ciphersuites);
+    return PACKET_remaining(&s->clienthello->ciphersuites);
+}
+
+size_t SSL_early_get0_compression_methods(SSL *s, const unsigned char **out)
+{
+    if (s->clienthello == NULL)
+        return 0;
+    if (out != NULL)
+        *out = s->clienthello->compressions;
+    return s->clienthello->compressions_len;
+}
+
+int SSL_early_get0_ext(SSL *s, unsigned int type, const unsigned char **out,
+                       size_t *outlen)
+{
+    size_t i;
+    RAW_EXTENSION *r;
+
+    if (s->clienthello == NULL)
+        return 0;
+    for (i = 0; i < s->clienthello->pre_proc_exts_len; ++i) {
+        r = s->clienthello->pre_proc_exts + i;
+        if (r->present && r->type == type) {
+            if (out != NULL)
+                *out = PACKET_data(&r->data);
+            if (outlen != NULL)
+                *outlen = PACKET_remaining(&r->data);
+            return 1;
+        }
+    }
+    return 0;
+}
 
 void SSL_CTX_set_keylog_callback(SSL_CTX *ctx, SSL_CTX_keylog_cb_func cb)
 {
@@ -4402,3 +4479,163 @@ int ssl_log_secret(SSL *ssl,
                           secret_len);
 }
 
+#define SSLV2_CIPHER_LEN    3
+
+int ssl_cache_cipherlist(SSL *s, PACKET *cipher_suites, int sslv2format,
+                         int *al)
+{
+    int n;
+
+    n = sslv2format ? SSLV2_CIPHER_LEN : TLS_CIPHER_LEN;
+
+    if (PACKET_remaining(cipher_suites) == 0) {
+        SSLerr(SSL_F_SSL_CACHE_CIPHERLIST, SSL_R_NO_CIPHERS_SPECIFIED);
+        *al = SSL_AD_ILLEGAL_PARAMETER;
+        return 0;
+    }
+
+    if (PACKET_remaining(cipher_suites) % n != 0) {
+        SSLerr(SSL_F_SSL_CACHE_CIPHERLIST,
+               SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST);
+        *al = SSL_AD_DECODE_ERROR;
+        return 0;
+    }
+
+    OPENSSL_free(s->s3->tmp.ciphers_raw);
+    s->s3->tmp.ciphers_raw = NULL;
+    s->s3->tmp.ciphers_rawlen = 0;
+
+    if (sslv2format) {
+        size_t numciphers = PACKET_remaining(cipher_suites) / n;
+        PACKET sslv2ciphers = *cipher_suites;
+        unsigned int leadbyte;
+        unsigned char *raw;
+
+        /*
+         * We store the raw ciphers list in SSLv3+ format so we need to do some
+         * preprocessing to convert the list first. If there are any SSLv2 only
+         * ciphersuites with a non-zero leading byte then we are going to
+         * slightly over allocate because we won't store those. But that isn't a
+         * problem.
+         */
+        raw = OPENSSL_malloc(numciphers * TLS_CIPHER_LEN);
+        s->s3->tmp.ciphers_raw = raw;
+        if (raw == NULL) {
+            *al = SSL_AD_INTERNAL_ERROR;
+            goto err;
+        }
+        for (s->s3->tmp.ciphers_rawlen = 0;
+             PACKET_remaining(&sslv2ciphers) > 0;
+             raw += TLS_CIPHER_LEN) {
+            if (!PACKET_get_1(&sslv2ciphers, &leadbyte)
+                    || (leadbyte == 0
+                        && !PACKET_copy_bytes(&sslv2ciphers, raw,
+                                              TLS_CIPHER_LEN))
+                    || (leadbyte != 0
+                        && !PACKET_forward(&sslv2ciphers, TLS_CIPHER_LEN))) {
+                *al = SSL_AD_INTERNAL_ERROR;
+                OPENSSL_free(s->s3->tmp.ciphers_raw);
+                s->s3->tmp.ciphers_raw = NULL;
+                s->s3->tmp.ciphers_rawlen = 0;
+                goto err;
+            }
+            if (leadbyte == 0)
+                s->s3->tmp.ciphers_rawlen += TLS_CIPHER_LEN;
+        }
+    } else if (!PACKET_memdup(cipher_suites, &s->s3->tmp.ciphers_raw,
+                           &s->s3->tmp.ciphers_rawlen)) {
+        *al = SSL_AD_INTERNAL_ERROR;
+        goto err;
+    }
+    return 1;
+ err:
+    return 0;
+}
+
+int SSL_bytes_to_cipher_list(SSL *s, const unsigned char *bytes, size_t len,
+                             int isv2format, STACK_OF(SSL_CIPHER) **sk,
+                             STACK_OF(SSL_CIPHER) **scsvs)
+{
+    int alert;
+    PACKET pkt;
+
+    if (!PACKET_buf_init(&pkt, bytes, len))
+        return 0;
+    return bytes_to_cipher_list(s, &pkt, sk, scsvs, isv2format, &alert);
+}
+
+int bytes_to_cipher_list(SSL *s, PACKET *cipher_suites,
+                         STACK_OF(SSL_CIPHER) **skp,
+                         STACK_OF(SSL_CIPHER) **scsvs_out,
+                         int sslv2format, int *al)
+{
+    const SSL_CIPHER *c;
+    STACK_OF(SSL_CIPHER) *sk = NULL;
+    STACK_OF(SSL_CIPHER) *scsvs = NULL;
+    int n;
+    /* 3 = SSLV2_CIPHER_LEN > TLS_CIPHER_LEN = 2. */
+    unsigned char cipher[SSLV2_CIPHER_LEN];
+
+    n = sslv2format ? SSLV2_CIPHER_LEN : TLS_CIPHER_LEN;
+
+    if (PACKET_remaining(cipher_suites) == 0) {
+        SSLerr(SSL_F_BYTES_TO_CIPHER_LIST, SSL_R_NO_CIPHERS_SPECIFIED);
+        *al = SSL_AD_ILLEGAL_PARAMETER;
+        return 0;
+    }
+
+    if (PACKET_remaining(cipher_suites) % n != 0) {
+        SSLerr(SSL_F_BYTES_TO_CIPHER_LIST,
+               SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST);
+        *al = SSL_AD_DECODE_ERROR;
+        return 0;
+    }
+
+    sk = sk_SSL_CIPHER_new_null();
+    scsvs = sk_SSL_CIPHER_new_null();
+    if (sk == NULL || scsvs == NULL) {
+        SSLerr(SSL_F_BYTES_TO_CIPHER_LIST, ERR_R_MALLOC_FAILURE);
+        *al = SSL_AD_INTERNAL_ERROR;
+        goto err;
+    }
+
+    while (PACKET_copy_bytes(cipher_suites, cipher, n)) {
+        /*
+         * SSLv3 ciphers wrapped in an SSLv2-compatible ClientHello have the
+         * first byte set to zero, while true SSLv2 ciphers have a non-zero
+         * first byte. We don't support any true SSLv2 ciphers, so skip them.
+         */
+        if (sslv2format && cipher[0] != '\0')
+            continue;
+
+        /* For SSLv2-compat, ignore leading 0-byte. */
+        c = ssl_get_cipher_by_char(s, sslv2format ? &cipher[1] : cipher, 1);
+        if (c != NULL) {
+            if ((c->valid && !sk_SSL_CIPHER_push(sk, c)) ||
+                (!c->valid && !sk_SSL_CIPHER_push(scsvs, c))) {
+                SSLerr(SSL_F_BYTES_TO_CIPHER_LIST, ERR_R_MALLOC_FAILURE);
+                *al = SSL_AD_INTERNAL_ERROR;
+                goto err;
+            }
+        }
+    }
+    if (PACKET_remaining(cipher_suites) > 0) {
+        *al = SSL_AD_INTERNAL_ERROR;
+        SSLerr(SSL_F_BYTES_TO_CIPHER_LIST, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+
+    if (skp != NULL)
+        *skp = sk;
+    else
+        sk_SSL_CIPHER_free(sk);
+    if (scsvs_out != NULL)
+        *scsvs_out = scsvs;
+    else
+        sk_SSL_CIPHER_free(scsvs);
+    return 1;
+ err:
+    sk_SSL_CIPHER_free(sk);
+    sk_SSL_CIPHER_free(scsvs);
+    return 0;
+}
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 6557834..89eeb45 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -622,12 +622,41 @@ typedef struct srp_ctx_st {
 
 # endif
 
+#define MAX_COMPRESSIONS_SIZE   255
+
 struct ssl_comp_st {
     int id;
     const char *name;
     COMP_METHOD *method;
 };
 
+typedef struct raw_extension_st {
+    /* Raw packet data for the extension */
+    PACKET data;
+    /* Set to 1 if the extension is present or 0 otherwise */
+    int present;
+    /* Set to 1 if we have already parsed the extension or 0 otherwise */
+    int parsed;
+    /* The type of this extension, i.e. a TLSEXT_TYPE_* value */
+    unsigned int type;
+} RAW_EXTENSION;
+
+typedef struct {
+    unsigned int isv2;
+    unsigned int legacy_version;
+    unsigned char random[SSL3_RANDOM_SIZE];
+    size_t session_id_len;
+    unsigned char session_id[SSL_MAX_SSL_SESSION_ID_LENGTH];
+    size_t dtls_cookie_len;
+    unsigned char dtls_cookie[DTLS1_COOKIE_LENGTH];
+    PACKET ciphersuites;
+    size_t compressions_len;
+    unsigned char compressions[MAX_COMPRESSIONS_SIZE];
+    PACKET extensions;
+    size_t pre_proc_exts_len;
+    RAW_EXTENSION *pre_proc_exts;
+} CLIENTHELLO_MSG;
+
 DEFINE_LHASH_OF(SSL_SESSION);
 /* Needed in ssl_cert.c */
 DEFINE_LHASH_OF(X509_NAME);
@@ -801,6 +830,10 @@ struct ssl_ctx_st {
     ENGINE *client_cert_engine;
 # endif
 
+    /* Early callback.  Mostly for extensions, but not entirely. */
+    SSL_early_cb_fn early_cb;
+    void *early_cb_arg;
+
     /* TLS extensions. */
     struct {
         /* TLS extensions servername callback */
@@ -1142,6 +1175,9 @@ struct ssl_st {
         int use_etm;
     } ext;
 
+    /* Parsed form of the ClientHello, kept around across early_cb calls. */
+    CLIENTHELLO_MSG *clienthello;
+
     /*-
      * no further mod of servername
      * 0 : call the servername extension callback.
@@ -1698,17 +1734,6 @@ typedef struct ssl3_comp_st {
 } SSL3_COMP;
 # endif
 
-typedef struct raw_extension_st {
-    /* Raw packet data for the extension */
-    PACKET data;
-    /* Set to 1 if the extension is present or 0 otherwise */
-    int present;
-    /* Set to 1 if we have already parsed the extension or 0 otherwise */
-    int parsed;
-    /* The type of this extension, i.e. a TLSEXT_TYPE_* value */
-    unsigned int type;
-} RAW_EXTENSION;
-
 /*
  * Extension index values NOTE: Any updates to these defines should be mirrored
  * with equivalent updates to ext_defs in extensions.c
@@ -1783,23 +1808,6 @@ typedef enum tlsext_index_en {
 #define TLSEXT_signature_rsa_pss                                0x0101
 
 
-#define MAX_COMPRESSIONS_SIZE   255
-
-typedef struct {
-    unsigned int isv2;
-    unsigned int legacy_version;
-    unsigned char random[SSL3_RANDOM_SIZE];
-    size_t session_id_len;
-    unsigned char session_id[SSL_MAX_SSL_SESSION_ID_LENGTH];
-    size_t dtls_cookie_len;
-    unsigned char dtls_cookie[DTLS1_COOKIE_LENGTH];
-    PACKET ciphersuites;
-    size_t compressions_len;
-    unsigned char compressions[MAX_COMPRESSIONS_SIZE];
-    PACKET extensions;
-    RAW_EXTENSION *pre_proc_exts;
-} CLIENTHELLO_MSG;
-
 extern SSL3_ENC_METHOD ssl3_undef_enc_method;
 
 __owur const SSL_METHOD *ssl_bad_method(int ver);
@@ -1990,6 +1998,12 @@ __owur STACK_OF(SSL_CIPHER) *ssl_create_cipher_list(const SSL_METHOD *meth,
                                                     **sorted,
                                                     const char *rule_str,
                                                     CERT *c);
+__owur int ssl_cache_cipherlist(SSL *s, PACKET *cipher_suites,
+                                int sslv2format, int *al);
+__owur int bytes_to_cipher_list(SSL *s, PACKET *cipher_suites,
+                                STACK_OF(SSL_CIPHER) **skp,
+                                STACK_OF(SSL_CIPHER) **scsvs, int sslv2format,
+                                int *al);
 void ssl_update_cache(SSL *s, int mode);
 __owur int ssl_cipher_get_evp(const SSL_SESSION *s, const EVP_CIPHER **enc,
                               const EVP_MD **md, int *mac_pkey_type,
@@ -2000,7 +2014,8 @@ __owur int ssl_cipher_get_overhead(const SSL_CIPHER *c, size_t *mac_overhead,
                                    size_t *ext_overhead);
 __owur int ssl_cipher_get_cert_index(const SSL_CIPHER *c);
 __owur const SSL_CIPHER *ssl_get_cipher_by_char(SSL *ssl,
-                                                const unsigned char *ptr);
+                                                const unsigned char *ptr,
+                                                int all);
 __owur int ssl_cert_set0_chain(SSL *s, SSL_CTX *ctx, STACK_OF(X509) *chain);
 __owur int ssl_cert_set1_chain(SSL *s, SSL_CTX *ctx, STACK_OF(X509) *chain);
 __owur int ssl_cert_add0_chain_cert(SSL *s, SSL_CTX *ctx, X509 *x);
diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c
index 8e1b502..c011445 100644
--- a/ssl/statem/extensions.c
+++ b/ssl/statem/extensions.c
@@ -382,10 +382,11 @@ static int extension_is_relevant(SSL *s, unsigned int extctx,
  * extensions that we know about. We ignore others.
  */
 int tls_collect_extensions(SSL *s, PACKET *packet, unsigned int context,
-                           RAW_EXTENSION **res, int *al)
+                           RAW_EXTENSION **res, int *al, size_t *len)
 {
     PACKET extensions = *packet;
     size_t i = 0;
+    size_t num_exts;
     custom_ext_methods *exts = NULL;
     RAW_EXTENSION *raw_extensions = NULL;
     const EXTENSION_DEFINITION *thisexd;
@@ -403,9 +404,8 @@ int tls_collect_extensions(SSL *s, PACKET *packet, unsigned int context,
         exts = &s->cert->cli_ext;
     }
 
-    raw_extensions = OPENSSL_zalloc((OSSL_NELEM(ext_defs)
-                                     + (exts != NULL ? exts->meths_count : 0))
-                                     * sizeof(*raw_extensions));
+    num_exts = OSSL_NELEM(ext_defs) + (exts != NULL ? exts->meths_count : 0);
+    raw_extensions = OPENSSL_zalloc(num_exts * sizeof(*raw_extensions));
     if (raw_extensions == NULL) {
         *al = SSL_AD_INTERNAL_ERROR;
         SSLerr(SSL_F_TLS_COLLECT_EXTENSIONS, ERR_R_MALLOC_FAILURE);
@@ -454,6 +454,8 @@ int tls_collect_extensions(SSL *s, PACKET *packet, unsigned int context,
     }
 
     *res = raw_extensions;
+    if (len != NULL)
+        *len = num_exts;
     return 1;
 
  err:
diff --git a/ssl/statem/statem.c b/ssl/statem/statem.c
index bd7d89a..a1c5a21 100644
--- a/ssl/statem/statem.c
+++ b/ssl/statem/statem.c
@@ -564,6 +564,7 @@ static SUB_STATE_RETURN read_state_machine(SSL *s)
             case WORK_ERROR:
             case WORK_MORE_A:
             case WORK_MORE_B:
+            case WORK_MORE_C:
                 return SUB_STATE_ERROR;
 
             case WORK_FINISHED_CONTINUE:
@@ -706,6 +707,7 @@ static SUB_STATE_RETURN write_state_machine(SSL *s)
             case WORK_ERROR:
             case WORK_MORE_A:
             case WORK_MORE_B:
+            case WORK_MORE_C:
                 return SUB_STATE_ERROR;
 
             case WORK_FINISHED_CONTINUE:
@@ -745,6 +747,7 @@ static SUB_STATE_RETURN write_state_machine(SSL *s)
             case WORK_ERROR:
             case WORK_MORE_A:
             case WORK_MORE_B:
+            case WORK_MORE_C:
                 return SUB_STATE_ERROR;
 
             case WORK_FINISHED_CONTINUE:
diff --git a/ssl/statem/statem.h b/ssl/statem/statem.h
index 021d2d0..906f2ec 100644
--- a/ssl/statem/statem.h
+++ b/ssl/statem/statem.h
@@ -27,7 +27,9 @@ typedef enum {
     /* We're working on phase A */
     WORK_MORE_A,
     /* We're working on phase B */
-    WORK_MORE_B
+    WORK_MORE_B,
+    /* We're working on phase C */
+    WORK_MORE_C
 } WORK_STATE;
 
 /* Write transition return codes */
diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c
index 3957a73..bc35a3e 100644
--- a/ssl/statem/statem_clnt.c
+++ b/ssl/statem/statem_clnt.c
@@ -1254,7 +1254,7 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
 
     context = SSL_IS_TLS13(s) ? EXT_TLS1_3_SERVER_HELLO
                               : EXT_TLS1_2_SERVER_HELLO;
-    if (!tls_collect_extensions(s, &extpkt, context, &extensions, &al))
+    if (!tls_collect_extensions(s, &extpkt, context, &extensions, &al, NULL))
         goto f_err;
 
     s->hit = 0;
@@ -1294,7 +1294,7 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
                      && master_key_length > 0) {
                 s->session->master_key_length = master_key_length;
                 s->session->cipher = pref_cipher ?
-                    pref_cipher : ssl_get_cipher_by_char(s, cipherchars);
+                    pref_cipher : ssl_get_cipher_by_char(s, cipherchars, 0);
             } else {
                 SSLerr(SSL_F_TLS_PROCESS_SERVER_HELLO, ERR_R_INTERNAL_ERROR);
                 al = SSL_AD_INTERNAL_ERROR;
@@ -1353,7 +1353,7 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
         goto f_err;
     }
 
-    c = ssl_get_cipher_by_char(s, cipherchars);
+    c = ssl_get_cipher_by_char(s, cipherchars, 0);
     if (c == NULL) {
         /* unknown cipher */
         al = SSL_AD_ILLEGAL_PARAMETER;
@@ -1524,7 +1524,7 @@ static MSG_PROCESS_RETURN tls_process_hello_retry_request(SSL *s, PACKET *pkt)
     }
 
     if (!tls_collect_extensions(s, &extpkt, EXT_TLS1_3_HELLO_RETRY_REQUEST,
-                                &extensions, &al)
+                                &extensions, &al, NULL)
             || !tls_parse_all_extensions(s, EXT_TLS1_3_HELLO_RETRY_REQUEST,
                                          extensions, NULL, 0, &al))
         goto f_err;
@@ -1596,7 +1596,7 @@ MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s, PACKET *pkt)
                 goto f_err;
             }
             if (!tls_collect_extensions(s, &extensions, EXT_TLS1_3_CERTIFICATE,
-                                        &rawexts, &al)
+                                        &rawexts, &al, NULL)
                     || !tls_parse_all_extensions(s, EXT_TLS1_3_CERTIFICATE,
                                                  rawexts, x, chainidx, &al)) {
                 OPENSSL_free(rawexts);
@@ -2399,7 +2399,7 @@ MSG_PROCESS_RETURN tls_process_new_session_ticket(SSL *s, PACKET *pkt)
         if (!PACKET_as_length_prefixed_2(pkt, &extpkt)
                 || !tls_collect_extensions(s, &extpkt,
                                            EXT_TLS1_3_NEW_SESSION_TICKET,
-                                           &exts, &al)
+                                           &exts, &al, NULL)
                 || !tls_parse_all_extensions(s, EXT_TLS1_3_NEW_SESSION_TICKET,
                                              exts, NULL, 0, &al)) {
             SSLerr(SSL_F_TLS_PROCESS_NEW_SESSION_TICKET, SSL_R_BAD_EXTENSION);
@@ -3362,7 +3362,7 @@ static MSG_PROCESS_RETURN tls_process_encrypted_extensions(SSL *s, PACKET *pkt)
     }
 
     if (!tls_collect_extensions(s, &extensions, EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
-                                &rawexts, &al)
+                                &rawexts, &al, NULL)
             || !tls_parse_all_extensions(s, EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
                                          rawexts, NULL, 0, &al))
         goto err;
diff --git a/ssl/statem/statem_locl.h b/ssl/statem/statem_locl.h
index 595a803..9230332 100644
--- a/ssl/statem/statem_locl.h
+++ b/ssl/statem/statem_locl.h
@@ -167,7 +167,7 @@ __owur int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt);
 /* Extension processing */
 
 __owur int tls_collect_extensions(SSL *s, PACKET *packet, unsigned int context,
-                                  RAW_EXTENSION **res, int *al);
+                                  RAW_EXTENSION **res, int *al, size_t *len);
 __owur int tls_parse_extension(SSL *s, TLSEXT_INDEX idx, int context,
                                RAW_EXTENSION *exts,  X509 *x, size_t chainidx,
                                int *al);
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index 0037e79..9c422e4 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -63,11 +63,6 @@
 
 static int tls_construct_encrypted_extensions(SSL *s, WPACKET *pkt);
 static int tls_construct_hello_retry_request(SSL *s, WPACKET *pkt);
-static STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s,
-                                                      PACKET *cipher_suites,
-                                                      STACK_OF(SSL_CIPHER)
-                                                      **skp, int sslv2format,
-                                                      int *al);
 
 /*
  * ossl_statem_server13_read_transition() encapsulates the logic for the allowed
@@ -1222,21 +1217,17 @@ static void ssl_check_for_safari(SSL *s, const CLIENTHELLO_MSG *hello)
 
 MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
 {
-    int i, al = SSL_AD_INTERNAL_ERROR;
-    unsigned int j;
-    size_t loop;
-    unsigned long id;
-    const SSL_CIPHER *c;
-#ifndef OPENSSL_NO_COMP
-    SSL_COMP *comp = NULL;
-#endif
-    STACK_OF(SSL_CIPHER) *ciphers = NULL;
-    int protverr;
+    int al = SSL_AD_INTERNAL_ERROR;
     /* |cookie| will only be initialized for DTLS. */
     PACKET session_id, compression, extensions, cookie;
     static const unsigned char null_compression = 0;
-    CLIENTHELLO_MSG clienthello;
+    CLIENTHELLO_MSG *clienthello;
 
+    clienthello = OPENSSL_zalloc(sizeof(*clienthello));
+    if (clienthello == NULL) {
+        SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
     /* Check if this is actually an unexpected renegotiation ClientHello */
     if (s->renegotiate == 0 && !SSL_IS_FIRST_HANDSHAKE(s)) {
         s->renegotiate = 1;
@@ -1249,11 +1240,10 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
     /*
      * First, parse the raw ClientHello data into the CLIENTHELLO_MSG structure.
      */
-    memset(&clienthello, 0, sizeof(clienthello));
-    clienthello.isv2 = RECORD_LAYER_is_sslv2_record(&s->rlayer);
+    clienthello->isv2 = RECORD_LAYER_is_sslv2_record(&s->rlayer);
     PACKET_null_init(&cookie);
 
-    if (clienthello.isv2) {
+    if (clienthello->isv2) {
         unsigned int mt;
 
         if (!SSL_IS_FIRST_HANDSHAKE(s) || s->hello_retry_request) {
@@ -1289,14 +1279,14 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
         }
     }
 
-    if (!PACKET_get_net_2(pkt, &clienthello.legacy_version)) {
+    if (!PACKET_get_net_2(pkt, &clienthello->legacy_version)) {
         al = SSL_AD_DECODE_ERROR;
         SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_LENGTH_TOO_SHORT);
         goto err;
     }
 
     /* Parse the message and load client random. */
-    if (clienthello.isv2) {
+    if (clienthello->isv2) {
         /*
          * Handle an SSLv2 backwards compatible ClientHello
          * Note, this is only for SSLv3+ using the backward compatible format.
@@ -1320,9 +1310,9 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
             goto f_err;
         }
 
-        if (!PACKET_get_sub_packet(pkt, &clienthello.ciphersuites,
+        if (!PACKET_get_sub_packet(pkt, &clienthello->ciphersuites,
                                    ciphersuite_len)
-            || !PACKET_copy_bytes(pkt, clienthello.session_id, session_id_len)
+            || !PACKET_copy_bytes(pkt, clienthello->session_id, session_id_len)
             || !PACKET_get_sub_packet(pkt, &challenge, challenge_len)
             /* No extensions. */
             || PACKET_remaining(pkt) != 0) {
@@ -1331,18 +1321,18 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
             al = SSL_AD_DECODE_ERROR;
             goto f_err;
         }
-        clienthello.session_id_len = session_id_len;
+        clienthello->session_id_len = session_id_len;
 
         /* Load the client random and compression list. We use SSL3_RANDOM_SIZE
-         * here rather than sizeof(clienthello.random) because that is the limit
+         * here rather than sizeof(clienthello->random) because that is the limit
          * for SSLv3 and it is fixed. It won't change even if
-         * sizeof(clienthello.random) does.
+         * sizeof(clienthello->random) does.
          */
         challenge_len = challenge_len > SSL3_RANDOM_SIZE
                         ? SSL3_RANDOM_SIZE : challenge_len;
-        memset(clienthello.random, 0, SSL3_RANDOM_SIZE);
+        memset(clienthello->random, 0, SSL3_RANDOM_SIZE);
         if (!PACKET_copy_bytes(&challenge,
-                               clienthello.random + SSL3_RANDOM_SIZE -
+                               clienthello->random + SSL3_RANDOM_SIZE -
                                challenge_len, challenge_len)
             /* Advertise only null compression. */
             || !PACKET_buf_init(&compression, &null_compression, 1)) {
@@ -1351,14 +1341,14 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
             goto f_err;
         }
 
-        PACKET_null_init(&clienthello.extensions);
+        PACKET_null_init(&clienthello->extensions);
     } else {
         /* Regular ClientHello. */
-        if (!PACKET_copy_bytes(pkt, clienthello.random, SSL3_RANDOM_SIZE)
+        if (!PACKET_copy_bytes(pkt, clienthello->random, SSL3_RANDOM_SIZE)
             || !PACKET_get_length_prefixed_1(pkt, &session_id)
-            || !PACKET_copy_all(&session_id, clienthello.session_id,
+            || !PACKET_copy_all(&session_id, clienthello->session_id,
                     SSL_MAX_SSL_SESSION_ID_LENGTH,
-                    &clienthello.session_id_len)) {
+                    &clienthello->session_id_len)) {
             al = SSL_AD_DECODE_ERROR;
             SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH);
             goto f_err;
@@ -1370,9 +1360,9 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
                 SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH);
                 goto f_err;
             }
-            if (!PACKET_copy_all(&cookie, clienthello.dtls_cookie,
+            if (!PACKET_copy_all(&cookie, clienthello->dtls_cookie,
                                  DTLS1_COOKIE_LENGTH,
-                                 &clienthello.dtls_cookie_len)) {
+                                 &clienthello->dtls_cookie_len)) {
                 al = SSL_AD_DECODE_ERROR;
                 SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH);
                 goto f_err;
@@ -1383,12 +1373,12 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
              * So check cookie length...
              */
             if (SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE) {
-                if (clienthello.dtls_cookie_len == 0)
+                if (clienthello->dtls_cookie_len == 0)
                     return 1;
             }
         }
 
-        if (!PACKET_get_length_prefixed_2(pkt, &clienthello.ciphersuites)) {
+        if (!PACKET_get_length_prefixed_2(pkt, &clienthello->ciphersuites)) {
             al = SSL_AD_DECODE_ERROR;
             SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH);
             goto f_err;
@@ -1402,9 +1392,9 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
 
         /* Could be empty. */
         if (PACKET_remaining(pkt) == 0) {
-            PACKET_null_init(&clienthello.extensions);
+            PACKET_null_init(&clienthello->extensions);
         } else {
-            if (!PACKET_get_length_prefixed_2(pkt, &clienthello.extensions)) {
+            if (!PACKET_get_length_prefixed_2(pkt, &clienthello->extensions)) {
                 al = SSL_AD_DECODE_ERROR;
                 SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH);
                 goto f_err;
@@ -1412,95 +1402,137 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
         }
     }
 
-    if (!PACKET_copy_all(&compression, clienthello.compressions,
+    if (!PACKET_copy_all(&compression, clienthello->compressions,
                          MAX_COMPRESSIONS_SIZE,
-                         &clienthello.compressions_len)) {
+                         &clienthello->compressions_len)) {
         al = SSL_AD_DECODE_ERROR;
         SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_LENGTH_MISMATCH);
         goto f_err;
     }
 
     /* Preserve the raw extensions PACKET for later use */
-    extensions = clienthello.extensions;
+    extensions = clienthello->extensions;
     if (!tls_collect_extensions(s, &extensions, EXT_CLIENT_HELLO,
-                                &clienthello.pre_proc_exts, &al)) {
+                                &clienthello->pre_proc_exts, &al,
+                                &clienthello->pre_proc_exts_len)) {
         /* SSLerr already been called */
         goto f_err;
     }
+    s->clienthello = clienthello;
+
+    return MSG_PROCESS_CONTINUE_PROCESSING;
+ f_err:
+    ssl3_send_alert(s, SSL3_AL_FATAL, al);
+ err:
+    ossl_statem_set_error(s);
+
+    OPENSSL_free(clienthello->pre_proc_exts);
+    OPENSSL_free(clienthello);
+
+    return MSG_PROCESS_ERROR;
+}
 
+static int tls_early_post_process_client_hello(SSL *s, int *al)
+{
+    unsigned int j;
+    int i;
+    int protverr;
+    size_t loop;
+    unsigned long id;
+#ifndef OPENSSL_NO_COMP
+    SSL_COMP *comp = NULL;
+#endif
+    const SSL_CIPHER *c;
+    STACK_OF(SSL_CIPHER) *ciphers = NULL;
+    STACK_OF(SSL_CIPHER) *scsvs = NULL;
+    CLIENTHELLO_MSG *clienthello = s->clienthello;
+
+    *al = SSL_AD_INTERNAL_ERROR;
     /* Finished parsing the ClientHello, now we can start processing it */
+    /* Give the early callback a crack at things */
+    if (s->ctx->early_cb != NULL) {
+        int code;
+        /* A failure in the early callback terminates the connection. */
+        code = s->ctx->early_cb(s, al, s->ctx->early_cb_arg);
+        if (code == 0)
+            goto err;
+        if (code < 0) {
+            s->rwstate = SSL_EARLY_WORK;
+            return code;
+        }
+    }
 
     /* Set up the client_random */
-    memcpy(s->s3->client_random, clienthello.random, SSL3_RANDOM_SIZE);
+    memcpy(s->s3->client_random, clienthello->random, SSL3_RANDOM_SIZE);
 
     /* Choose the version */
 
-    if (clienthello.isv2) {
-        if (clienthello.legacy_version == SSL2_VERSION
-                || (clienthello.legacy_version & 0xff00)
+    if (clienthello->isv2) {
+        if (clienthello->legacy_version == SSL2_VERSION
+                || (clienthello->legacy_version & 0xff00)
                    != (SSL3_VERSION_MAJOR << 8)) {
             /*
              * This is real SSLv2 or something complete unknown. We don't
              * support it.
              */
-            SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_UNKNOWN_PROTOCOL);
+            SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_UNKNOWN_PROTOCOL);
             goto err;
         }
         /* SSLv3/TLS */
-        s->client_version = clienthello.legacy_version;
+        s->client_version = clienthello->legacy_version;
     }
     /*
      * Do SSL/TLS version negotiation if applicable. For DTLS we just check
      * versions are potentially compatible. Version negotiation comes later.
      */
     if (!SSL_IS_DTLS(s)) {
-        protverr = ssl_choose_server_version(s, &clienthello);
+        protverr = ssl_choose_server_version(s, clienthello);
     } else if (s->method->version != DTLS_ANY_VERSION &&
-               DTLS_VERSION_LT((int)clienthello.legacy_version, s->version)) {
+               DTLS_VERSION_LT((int)clienthello->legacy_version, s->version)) {
         protverr = SSL_R_VERSION_TOO_LOW;
     } else {
         protverr = 0;
     }
 
     if (protverr) {
-        SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, protverr);
+        SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, protverr);
         if (SSL_IS_FIRST_HANDSHAKE(s)) {
             /* like ssl3_get_record, send alert using remote version number */
-            s->version = s->client_version = clienthello.legacy_version;
+            s->version = s->client_version = clienthello->legacy_version;
         }
-        al = SSL_AD_PROTOCOL_VERSION;
-        goto f_err;
+        *al = SSL_AD_PROTOCOL_VERSION;
+        goto err;
     }
 
     if (SSL_IS_DTLS(s)) {
         /* Empty cookie was already handled above by returning early. */
         if (SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE) {
             if (s->ctx->app_verify_cookie_cb != NULL) {
-                if (s->ctx->app_verify_cookie_cb(s, clienthello.dtls_cookie,
-                        clienthello.dtls_cookie_len) == 0) {
-                    al = SSL_AD_HANDSHAKE_FAILURE;
-                    SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO,
+                if (s->ctx->app_verify_cookie_cb(s, clienthello->dtls_cookie,
+                        clienthello->dtls_cookie_len) == 0) {
+                    *al = SSL_AD_HANDSHAKE_FAILURE;
+                    SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO,
                            SSL_R_COOKIE_MISMATCH);
-                    goto f_err;
+                    goto err;
                     /* else cookie verification succeeded */
                 }
                 /* default verification */
-            } else if (s->d1->cookie_len != clienthello.dtls_cookie_len
-                    || memcmp(clienthello.dtls_cookie, s->d1->cookie,
+            } else if (s->d1->cookie_len != clienthello->dtls_cookie_len
+                    || memcmp(clienthello->dtls_cookie, s->d1->cookie,
                               s->d1->cookie_len) != 0) {
-                al = SSL_AD_HANDSHAKE_FAILURE;
-                SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_COOKIE_MISMATCH);
-                goto f_err;
+                *al = SSL_AD_HANDSHAKE_FAILURE;
+                SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_COOKIE_MISMATCH);
+                goto err;
             }
             s->d1->cookie_verified = 1;
         }
         if (s->method->version == DTLS_ANY_VERSION) {
-            protverr = ssl_choose_server_version(s, &clienthello);
+            protverr = ssl_choose_server_version(s, clienthello);
             if (protverr != 0) {
-                SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, protverr);
+                SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, protverr);
                 s->version = s->client_version;
-                al = SSL_AD_PROTOCOL_VERSION;
-                goto f_err;
+                *al = SSL_AD_PROTOCOL_VERSION;
+                goto err;
             }
         }
     }
@@ -1510,9 +1542,9 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
     /* We need to do this before getting the session */
     if (!tls_parse_extension(s, TLSEXT_IDX_extended_master_secret,
                              EXT_CLIENT_HELLO,
-                             clienthello.pre_proc_exts, NULL, 0, &al)) {
-        SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_CLIENTHELLO_TLSEXT);
-        goto f_err;
+                             clienthello->pre_proc_exts, NULL, 0, al)) {
+        SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_CLIENTHELLO_TLSEXT);
+        goto err;
     }
 
     /*
@@ -1531,18 +1563,18 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
      * SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION setting will be
      * ignored.
      */
-    if (clienthello.isv2 ||
+    if (clienthello->isv2 ||
         (s->new_session &&
          (s->options & SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION))) {
         if (!ssl_get_new_session(s, 1))
             goto err;
     } else {
-        i = ssl_get_prev_session(s, &clienthello, &al);
+        i = ssl_get_prev_session(s, clienthello, al);
         if (i == 1) {
             /* previous session */
             s->hit = 1;
         } else if (i == -1) {
-            goto f_err;
+            goto err;
         } else {
             /* i == 0 */
             if (!ssl_get_new_session(s, 1))
@@ -1550,9 +1582,42 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
         }
     }
 
-    if (ssl_bytes_to_cipher_list(s, &clienthello.ciphersuites, &ciphers,
-                                 clienthello.isv2, &al) == NULL) {
-        goto f_err;
+    if (!ssl_cache_cipherlist(s, &clienthello->ciphersuites,
+                              clienthello->isv2, al) ||
+        !bytes_to_cipher_list(s, &clienthello->ciphersuites, &ciphers, &scsvs,
+                             clienthello->isv2, al)) {
+        goto err;
+    }
+
+    s->s3->send_connection_binding = 0;
+    /* Check what signalling cipher-suite values were received. */
+    if (scsvs != NULL) {
+        for(i = 0; i < sk_SSL_CIPHER_num(scsvs); i++) {
+            c = sk_SSL_CIPHER_value(scsvs, i);
+            if (SSL_CIPHER_get_id(c) == SSL3_CK_SCSV) {
+                if (s->renegotiate) {
+                    /* SCSV is fatal if renegotiating */
+                    SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO,
+                           SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING);
+                    *al = SSL_AD_HANDSHAKE_FAILURE;
+                    goto err;
+                }
+                s->s3->send_connection_binding = 1;
+            } else if (SSL_CIPHER_get_id(c) == SSL3_CK_FALLBACK_SCSV &&
+                       !ssl_check_version_downgrade(s)) {
+                /*
+                 * This SCSV indicates that the client previously tried
+                 * a higher version.  We should fail if the current version
+                 * is an unexpected downgrade, as that indicates that the first
+                 * connection may have been tampered with in order to trigger
+                 * an insecure downgrade.
+                 */
+                SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO,
+                       SSL_R_INAPPROPRIATE_FALLBACK);
+                *al = SSL_AD_INAPPROPRIATE_FALLBACK;
+                goto err;
+            }
+        }
     }
 
     /* If it is a hit, check that the cipher is in the list */
@@ -1579,35 +1644,35 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
              * we need to have the cipher in the cipher list if we are asked
              * to reuse it
              */
-            al = SSL_AD_ILLEGAL_PARAMETER;
-            SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO,
+            *al = SSL_AD_ILLEGAL_PARAMETER;
+            SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO,
                    SSL_R_REQUIRED_CIPHER_MISSING);
-            goto f_err;
+            goto err;
         }
     }
 
-    for (loop = 0; loop < clienthello.compressions_len; loop++) {
-        if (clienthello.compressions[loop] == 0)
+    for (loop = 0; loop < clienthello->compressions_len; loop++) {
+        if (clienthello->compressions[loop] == 0)
             break;
     }
 
-    if (loop >= clienthello.compressions_len) {
+    if (loop >= clienthello->compressions_len) {
         /* no compress */
-        al = SSL_AD_DECODE_ERROR;
-        SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_NO_COMPRESSION_SPECIFIED);
-        goto f_err;
+        *al = SSL_AD_DECODE_ERROR;
+        SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_NO_COMPRESSION_SPECIFIED);
+        goto err;
     }
 
 #ifndef OPENSSL_NO_EC
     if (s->options & SSL_OP_SAFARI_ECDHE_ECDSA_BUG)
-        ssl_check_for_safari(s, &clienthello);
+        ssl_check_for_safari(s, clienthello);
 #endif                          /* !OPENSSL_NO_EC */
 
     /* TLS extensions */
     if (!tls_parse_all_extensions(s, EXT_CLIENT_HELLO,
-                                  clienthello.pre_proc_exts, NULL, 0, &al)) {
-        SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_PARSE_TLSEXT);
-        goto f_err;
+                                  clienthello->pre_proc_exts, NULL, 0, al)) {
+        SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_PARSE_TLSEXT);
+        goto err;
     }
 
     /*
@@ -1620,7 +1685,7 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
         unsigned char *pos;
         pos = s->s3->server_random;
         if (ssl_fill_hello_random(s, 1, pos, SSL3_RANDOM_SIZE) <= 0) {
-            goto f_err;
+            goto err;
         }
     }
 
@@ -1650,9 +1715,9 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
                 pref_cipher = ssl3_choose_cipher(s, s->session->ciphers,
                                                  SSL_get_ciphers(s));
             if (pref_cipher == NULL) {
-                al = SSL_AD_HANDSHAKE_FAILURE;
-                SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_NO_SHARED_CIPHER);
-                goto f_err;
+                *al = SSL_AD_HANDSHAKE_FAILURE;
+                SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_NO_SHARED_CIPHER);
+                goto err;
             }
 
             s->session->cipher = pref_cipher;
@@ -1677,9 +1742,9 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
         /* Perform sanity checks on resumed compression algorithm */
         /* Can't disable compression */
         if (!ssl_allow_compression(s)) {
-            SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO,
+            SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO,
                    SSL_R_INCONSISTENT_COMPRESSION);
-            goto f_err;
+            goto err;
         }
         /* Look for resumed compression method */
         for (m = 0; m < sk_SSL_COMP_num(s->ctx->comp_methods); m++) {
@@ -1690,20 +1755,20 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
             }
         }
         if (s->s3->tmp.new_compression == NULL) {
-            SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO,
+            SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO,
                    SSL_R_INVALID_COMPRESSION_ALGORITHM);
-            goto f_err;
+            goto err;
         }
         /* Look for resumed method in compression list */
-        for (k = 0; k < clienthello.compressions_len; k++) {
-            if (clienthello.compressions[k] == comp_id)
+        for (k = 0; k < clienthello->compressions_len; k++) {
+            if (clienthello->compressions[k] == comp_id)
                 break;
         }
-        if (k >= clienthello.compressions_len) {
-            al = SSL_AD_ILLEGAL_PARAMETER;
-            SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO,
+        if (k >= clienthello->compressions_len) {
+            *al = SSL_AD_ILLEGAL_PARAMETER;
+            SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO,
                    SSL_R_REQUIRED_COMPRESSION_ALGORITHM_MISSING);
-            goto f_err;
+            goto err;
         }
     } else if (s->hit)
         comp = NULL;
@@ -1716,8 +1781,8 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
         for (m = 0; m < nn; m++) {
             comp = sk_SSL_COMP_value(s->ctx->comp_methods, m);
             v = comp->id;
-            for (o = 0; o < clienthello.compressions_len; o++) {
-                if (v == clienthello.compressions[o]) {
+            for (o = 0; o < clienthello->compressions_len; o++) {
+                if (v == clienthello->compressions[o]) {
                     done = 1;
                     break;
                 }
@@ -1736,8 +1801,8 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
      * using compression.
      */
     if (s->session->compress_meth != 0) {
-        SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_INCONSISTENT_COMPRESSION);
-        goto f_err;
+        SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_INCONSISTENT_COMPRESSION);
+        goto err;
     }
 #endif
 
@@ -1754,29 +1819,33 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
         sk_SSL_CIPHER_free(s->session->ciphers);
         s->session->ciphers = ciphers;
         if (ciphers == NULL) {
-            al = SSL_AD_INTERNAL_ERROR;
-            SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, ERR_R_INTERNAL_ERROR);
-            goto f_err;
+            *al = SSL_AD_INTERNAL_ERROR;
+            SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, ERR_R_INTERNAL_ERROR);
+            goto err;
         }
         ciphers = NULL;
         if (!tls1_set_server_sigalgs(s)) {
-            SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_CLIENTHELLO_TLSEXT);
+            SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_CLIENTHELLO_TLSEXT);
             goto err;
         }
     }
 
     sk_SSL_CIPHER_free(ciphers);
-    OPENSSL_free(clienthello.pre_proc_exts);
-    return MSG_PROCESS_CONTINUE_PROCESSING;
- f_err:
-    ssl3_send_alert(s, SSL3_AL_FATAL, al);
+    sk_SSL_CIPHER_free(scsvs);
+    OPENSSL_free(clienthello->pre_proc_exts);
+    OPENSSL_free(s->clienthello);
+    s->clienthello = NULL;
+    return 1;
  err:
     ossl_statem_set_error(s);
 
     sk_SSL_CIPHER_free(ciphers);
-    OPENSSL_free(clienthello.pre_proc_exts);
+    sk_SSL_CIPHER_free(scsvs);
+    OPENSSL_free(clienthello->pre_proc_exts);
+    OPENSSL_free(s->clienthello);
+    s->clienthello = NULL;
 
-    return MSG_PROCESS_ERROR;
+    return 0;
 }
 
 /*
@@ -1833,6 +1902,16 @@ WORK_STATE tls_post_process_client_hello(SSL *s, WORK_STATE wst)
     const SSL_CIPHER *cipher;
 
     if (wst == WORK_MORE_A) {
+        int rv = tls_early_post_process_client_hello(s, &al);
+        if (rv == 0) {
+            /* SSLErr() was already called */
+            goto f_err;
+        }
+        if (rv < 0)
+            return WORK_MORE_A;
+        wst = WORK_MORE_B;
+    }
+    if (wst == WORK_MORE_B) {
         if (!s->hit) {
             /* Let cert callback update server certificates if required */
             if (s->cert->cert_cb) {
@@ -1845,7 +1924,7 @@ WORK_STATE tls_post_process_client_hello(SSL *s, WORK_STATE wst)
                 }
                 if (rv < 0) {
                     s->rwstate = SSL_X509_LOOKUP;
-                    return WORK_MORE_A;
+                    return WORK_MORE_B;
                 }
                 s->rwstate = SSL_NOTHING;
             }
@@ -1896,17 +1975,17 @@ WORK_STATE tls_post_process_client_hello(SSL *s, WORK_STATE wst)
             goto f_err;
         }
 
-        wst = WORK_MORE_B;
+        wst = WORK_MORE_C;
     }
 #ifndef OPENSSL_NO_SRP
-    if (wst == WORK_MORE_B) {
+    if (wst == WORK_MORE_C) {
         int ret;
         if ((ret = ssl_check_srp_ext_ClientHello(s, &al)) < 0) {
             /*
              * callback indicates further work to be done
              */
             s->rwstate = SSL_X509_LOOKUP;
-            return WORK_MORE_B;
+            return WORK_MORE_C;
         }
         if (ret != SSL_ERROR_NONE) {
             /*
@@ -3128,7 +3207,7 @@ MSG_PROCESS_RETURN tls_process_client_certificate(SSL *s, PACKET *pkt)
                 goto f_err;
             }
             if (!tls_collect_extensions(s, &extensions, EXT_TLS1_3_CERTIFICATE,
-                                        &rawexts, &al)
+                                        &rawexts, &al, NULL)
                     || !tls_parse_all_extensions(s, EXT_TLS1_3_CERTIFICATE,
                                                  rawexts, x, chainidx, &al)) {
                 OPENSSL_free(rawexts);
@@ -3507,153 +3586,6 @@ static int tls_construct_encrypted_extensions(SSL *s, WPACKET *pkt)
     return 1;
 }
 
-#define SSLV2_CIPHER_LEN    3
-
-STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s,
-                                               PACKET *cipher_suites,
-                                               STACK_OF(SSL_CIPHER) **skp,
-                                               int sslv2format, int *al)
-{
-    const SSL_CIPHER *c;
-    STACK_OF(SSL_CIPHER) *sk;
-    int n;
-    /* 3 = SSLV2_CIPHER_LEN > TLS_CIPHER_LEN = 2. */
-    unsigned char cipher[SSLV2_CIPHER_LEN];
-
-    s->s3->send_connection_binding = 0;
-
-    n = sslv2format ? SSLV2_CIPHER_LEN : TLS_CIPHER_LEN;
-
-    if (PACKET_remaining(cipher_suites) == 0) {
-        SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST, SSL_R_NO_CIPHERS_SPECIFIED);
-        *al = SSL_AD_ILLEGAL_PARAMETER;
-        return NULL;
-    }
-
-    if (PACKET_remaining(cipher_suites) % n != 0) {
-        SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST,
-               SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST);
-        *al = SSL_AD_DECODE_ERROR;
-        return NULL;
-    }
-
-    sk = sk_SSL_CIPHER_new_null();
-    if (sk == NULL) {
-        SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST, ERR_R_MALLOC_FAILURE);
-        *al = SSL_AD_INTERNAL_ERROR;
-        return NULL;
-    }
-
-    OPENSSL_free(s->s3->tmp.ciphers_raw);
-    s->s3->tmp.ciphers_raw = NULL;
-    s->s3->tmp.ciphers_rawlen = 0;
-
-    if (sslv2format) {
-        size_t numciphers = PACKET_remaining(cipher_suites) / n;
-        PACKET sslv2ciphers = *cipher_suites;
-        unsigned int leadbyte;
-        unsigned char *raw;
-
-        /*
-         * We store the raw ciphers list in SSLv3+ format so we need to do some
-         * preprocessing to convert the list first. If there are any SSLv2 only
-         * ciphersuites with a non-zero leading byte then we are going to
-         * slightly over allocate because we won't store those. But that isn't a
-         * problem.
-         */
-        raw = OPENSSL_malloc(numciphers * TLS_CIPHER_LEN);
-        s->s3->tmp.ciphers_raw = raw;
-        if (raw == NULL) {
-            *al = SSL_AD_INTERNAL_ERROR;
-            goto err;
-        }
-        for (s->s3->tmp.ciphers_rawlen = 0;
-             PACKET_remaining(&sslv2ciphers) > 0;
-             raw += TLS_CIPHER_LEN) {
-            if (!PACKET_get_1(&sslv2ciphers, &leadbyte)
-                    || (leadbyte == 0
-                        && !PACKET_copy_bytes(&sslv2ciphers, raw,
-                                              TLS_CIPHER_LEN))
-                    || (leadbyte != 0
-                        && !PACKET_forward(&sslv2ciphers, TLS_CIPHER_LEN))) {
-                *al = SSL_AD_INTERNAL_ERROR;
-                OPENSSL_free(s->s3->tmp.ciphers_raw);
-                s->s3->tmp.ciphers_raw = NULL;
-                s->s3->tmp.ciphers_rawlen = 0;
-                goto err;
-            }
-            if (leadbyte == 0)
-                s->s3->tmp.ciphers_rawlen += TLS_CIPHER_LEN;
-        }
-    } else if (!PACKET_memdup(cipher_suites, &s->s3->tmp.ciphers_raw,
-                           &s->s3->tmp.ciphers_rawlen)) {
-        *al = SSL_AD_INTERNAL_ERROR;
-        goto err;
-    }
-
-    while (PACKET_copy_bytes(cipher_suites, cipher, n)) {
-        /*
-         * SSLv3 ciphers wrapped in an SSLv2-compatible ClientHello have the
-         * first byte set to zero, while true SSLv2 ciphers have a non-zero
-         * first byte. We don't support any true SSLv2 ciphers, so skip them.
-         */
-        if (sslv2format && cipher[0] != '\0')
-            continue;
-
-        /* Check for TLS_EMPTY_RENEGOTIATION_INFO_SCSV */
-        if ((cipher[n - 2] == ((SSL3_CK_SCSV >> 8) & 0xff)) &&
-            (cipher[n - 1] == (SSL3_CK_SCSV & 0xff))) {
-            /* SCSV fatal if renegotiating */
-            if (s->renegotiate) {
-                SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST,
-                       SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING);
-                *al = SSL_AD_HANDSHAKE_FAILURE;
-                goto err;
-            }
-            s->s3->send_connection_binding = 1;
-            continue;
-        }
-
-        /* Check for TLS_FALLBACK_SCSV */
-        if ((cipher[n - 2] == ((SSL3_CK_FALLBACK_SCSV >> 8) & 0xff)) &&
-            (cipher[n - 1] == (SSL3_CK_FALLBACK_SCSV & 0xff))) {
-            /*
-             * The SCSV indicates that the client previously tried a higher
-             * version. Fail if the current version is an unexpected
-             * downgrade.
-             */
-            if (!ssl_check_version_downgrade(s)) {
-                SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST,
-                       SSL_R_INAPPROPRIATE_FALLBACK);
-                *al = SSL_AD_INAPPROPRIATE_FALLBACK;
-                goto err;
-            }
-            continue;
-        }
-
-        /* For SSLv2-compat, ignore leading 0-byte. */
-        c = ssl_get_cipher_by_char(s, sslv2format ? &cipher[1] : cipher);
-        if (c != NULL) {
-            if (!sk_SSL_CIPHER_push(sk, c)) {
-                SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST, ERR_R_MALLOC_FAILURE);
-                *al = SSL_AD_INTERNAL_ERROR;
-                goto err;
-            }
-        }
-    }
-    if (PACKET_remaining(cipher_suites) > 0) {
-        *al = SSL_AD_INTERNAL_ERROR;
-        SSLerr(SSL_F_SSL_BYTES_TO_CIPHER_LIST, ERR_R_INTERNAL_ERROR);
-        goto err;
-    }
-
-    *skp = sk;
-    return sk;
- err:
-    sk_SSL_CIPHER_free(sk);
-    return NULL;
-}
-
 static int tls_construct_hello_retry_request(SSL *s, WPACKET *pkt)
 {
     int al = SSL_AD_INTERNAL_ERROR;
diff --git a/test/asynciotest.c b/test/asynciotest.c
index d4edd94..f418bbe 100644
--- a/test/asynciotest.c
+++ b/test/asynciotest.c
@@ -303,7 +303,7 @@ int main(int argc, char *argv[])
             goto end;
         }
 
-        if (!create_ssl_connection(serverssl, clientssl)) {
+        if (!create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)) {
             printf("Test %d failed: Create SSL connection failed\n", test);
             goto end;
         }
diff --git a/test/build.info b/test/build.info
index 4a43e5b..7c1a055 100644
--- a/test/build.info
+++ b/test/build.info
@@ -28,7 +28,7 @@ IF[{- !$disabled{tests} -}]
           dtlsv1listentest ct_test threadstest afalgtest d2i_test \
           ssl_test_ctx_test ssl_test x509aux cipherlist_test asynciotest \
           bioprinttest sslapitest dtlstest sslcorrupttest bio_enc_test \
-          pkey_meth_test uitest
+          pkey_meth_test uitest cipherbytes_test
 
   SOURCE[aborttest]=aborttest.c
   INCLUDE[aborttest]=../include
@@ -320,6 +320,10 @@ IF[{- !$disabled{tests} -}]
   INCLUDE[uitest]=.. ../include
   DEPEND[uitest]=../libcrypto ../libssl
 
+  SOURCE[cipherbytes_test]=cipherbytes_test.c
+  INCLUDE[cipherbytes_test]=.. ../include
+  DEPEND[cipherbytes_test]=../libcrypto ../libssl
+
   # Internal test programs.  These are essentially a collection of internal
   # test routines.  Some of them need to reach internal symbols that aren't
   # available through the shared library (at least on Linux, Solaris, Windows
diff --git a/test/cipherbytes_test.c b/test/cipherbytes_test.c
new file mode 100644
index 0000000..9be42f7
--- /dev/null
+++ b/test/cipherbytes_test.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2017 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the OpenSSL licenses, (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * https://www.openssl.org/source/license.html
+ * or in the file LICENSE in the source distribution.
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include <openssl/opensslconf.h>
+#include <openssl/err.h>
+#include <openssl/e_os2.h>
+#include <openssl/ssl.h>
+#include <openssl/ssl3.h>
+#include <openssl/tls1.h>
+
+#include "e_os.h"
+#include "test_main.h"
+#include "testutil.h"
+
+static int test_empty(SSL *s)
+{
+    STACK_OF(SSL_CIPHER) *sk = NULL, *scsv = NULL;
+    const unsigned char bytes[] = {0x00};
+
+    if (SSL_bytes_to_cipher_list(s, bytes, 0, 0, &sk, &scsv) ||
+        sk != NULL || scsv != NULL)
+        return 0;
+    return 1;
+}
+
+static int test_unsupported(SSL *s)
+{
+    STACK_OF(SSL_CIPHER) *sk, *scsv;
+    /* ECDH-RSA-AES256 (unsupported), ECDHE-ECDSA-AES128, <unassigned> */
+    const unsigned char bytes[] = {0xc0, 0x0f, 0x00, 0x2f, 0x01, 0x00};
+
+    if (!SSL_bytes_to_cipher_list(s, bytes, sizeof(bytes), 0, &sk, &scsv) ||
+        sk == NULL || sk_SSL_CIPHER_num(sk) != 1 || scsv == NULL ||
+        sk_SSL_CIPHER_num(scsv) != 0)
+        return 0;
+    if (strcmp(SSL_CIPHER_get_name(sk_SSL_CIPHER_value(sk, 0)),
+                "AES128-SHA") != 0)
+        return 0;
+
+    sk_SSL_CIPHER_free(sk);
+    sk_SSL_CIPHER_free(scsv);
+    return 1;
+}
+
+static int test_v2(SSL *s)
+{
+    STACK_OF(SSL_CIPHER) *sk, *scsv;
+    /* ECDHE-ECDSA-AES256GCM, SSL2_RC4_1238_WITH_MD5,
+     * ECDHE-ECDSA-CHACHA20-POLY1305 */
+    const unsigned char bytes[] = {0x00, 0x00, 0x35, 0x01, 0x00, 0x80,
+                                   0x00, 0x00, 0x33};
+
+    if (!SSL_bytes_to_cipher_list(s, bytes, sizeof(bytes), 1, &sk, &scsv) ||
+        sk == NULL || sk_SSL_CIPHER_num(sk) != 2 || scsv == NULL ||
+        sk_SSL_CIPHER_num(scsv) != 0)
+        return 0;
+    if (strcmp(SSL_CIPHER_get_name(sk_SSL_CIPHER_value(sk, 0)),
+               "AES256-SHA") != 0 ||
+        strcmp(SSL_CIPHER_get_name(sk_SSL_CIPHER_value(sk, 1)),
+               "DHE-RSA-AES128-SHA") != 0)
+        return 0;
+    sk_SSL_CIPHER_free(sk);
+    sk_SSL_CIPHER_free(scsv);
+    return 1;
+}
+
+static int test_v3(SSL *s)
+{
+    STACK_OF(SSL_CIPHER) *sk, *scsv;
+    /* ECDHE-ECDSA-AES256GCM, ECDHE-ECDSA-CHACHAPOLY, DHE-RSA-AES256GCM,
+     * EMPTY-RENEGOTIATION-INFO-SCSV, FALLBACK-SCSV */
+    const unsigned char bytes[] = {0x00, 0x2f, 0x00, 0x33, 0x00, 0x9f, 0x00, 0xff,
+                                   0x56, 0x00};
+
+    if (!SSL_bytes_to_cipher_list(s, bytes, sizeof(bytes), 0, &sk, &scsv) ||
+        sk == NULL || sk_SSL_CIPHER_num(sk) != 3 || scsv == NULL ||
+        sk_SSL_CIPHER_num(scsv) != 2)
+        return 0;
+    if (strcmp(SSL_CIPHER_get_name(sk_SSL_CIPHER_value(sk, 0)),
+               "AES128-SHA") != 0 ||
+        strcmp(SSL_CIPHER_get_name(sk_SSL_CIPHER_value(sk, 1)),
+               "DHE-RSA-AES128-SHA") != 0 ||
+        strcmp(SSL_CIPHER_get_name(sk_SSL_CIPHER_value(sk, 2)),
+               "DHE-RSA-AES256-GCM-SHA384") != 0 ||
+        strcmp(SSL_CIPHER_get_name(sk_SSL_CIPHER_value(scsv, 0)),
+               "TLS_EMPTY_RENEGOTIATION_INFO_SCSV") != 0 ||
+        strcmp(SSL_CIPHER_get_name(sk_SSL_CIPHER_value(scsv, 1)),
+               "TLS_FALLBACK_SCSV") != 0)
+        return 0;
+    sk_SSL_CIPHER_free(sk);
+    sk_SSL_CIPHER_free(scsv);
+    return 1;
+}
+
+int main(int argc, char **argv)
+{
+    SSL_CTX *ctx;
+    SSL *s;
+
+    ctx = SSL_CTX_new(TLS_server_method());
+    s = SSL_new(ctx);
+    OPENSSL_assert(s != NULL);
+
+    if (!test_empty(s))
+        return 1;
+
+    if (!test_unsupported(s))
+        return 1;
+
+    if (!test_v2(s))
+        return 1;
+
+    if (!test_v3(s))
+        return 1;
+
+    printf("PASS\n");
+    SSL_free(s);
+    SSL_CTX_free(ctx);
+    return 0;
+}
diff --git a/test/dtls_mtu_test.c b/test/dtls_mtu_test.c
index 1a05c54..24d4ccc 100644
--- a/test/dtls_mtu_test.c
+++ b/test/dtls_mtu_test.c
@@ -70,7 +70,7 @@ static int mtu_test(SSL_CTX *ctx, const char *cs, int no_etm)
     }
     sc_bio = SSL_get_rbio(srvr_ssl);
 
-    if (create_ssl_connection(clnt_ssl, srvr_ssl) != 1)
+    if (create_ssl_connection(clnt_ssl, srvr_ssl, SSL_ERROR_NONE) != 1)
         goto out;
 
     if (debug)
diff --git a/test/dtlstest.c b/test/dtlstest.c
index b4a756f..bc22d32 100644
--- a/test/dtlstest.c
+++ b/test/dtlstest.c
@@ -89,7 +89,7 @@ static int test_dtls_unprocessed(int testidx)
     mempacket_test_inject(c_to_s_mempacket, (char *)certstatus,
                           sizeof(certstatus), 1, INJECT_PACKET_IGNORE_REC_SEQ);
 
-    if (!create_ssl_connection(serverssl1, clientssl1)) {
+    if (!create_ssl_connection(serverssl1, clientssl1, SSL_ERROR_NONE)) {
         printf("Unable to create SSL connection\n");
         ERR_print_errors_fp(stdout);
         goto end;
diff --git a/test/handshake_helper.c b/test/handshake_helper.c
index c82581c..88f6aec 100644
--- a/test/handshake_helper.c
+++ b/test/handshake_helper.c
@@ -123,6 +123,67 @@ static int select_server_ctx(SSL *s, void *arg, int ignore)
     }
 }
 
+static int early_select_server_ctx(SSL *s, void *arg, int ignore)
+{
+    const char *servername;
+    const unsigned char *p;
+    size_t len, remaining;
+    HANDSHAKE_EX_DATA *ex_data =
+        (HANDSHAKE_EX_DATA*)(SSL_get_ex_data(s, ex_data_idx));
+
+    /*
+     * The server_name extension was given too much extensibility when it
+     * was written, so parsing the normal case is a bit complex.
+     */
+    if (!SSL_early_get0_ext(s, TLSEXT_TYPE_server_name, &p, &remaining) ||
+        remaining <= 2)
+        return 0;
+    /* Extract the length of the supplied list of names. */
+    len = (*(p++) << 1);
+    len += *(p++);
+    if (len + 2 != remaining)
+        return 0;
+    remaining = len;
+    /*
+     * The list in practice only has a single element, so we only consider
+     * the first one.
+     */
+    if (remaining == 0 || *p++ != TLSEXT_NAMETYPE_host_name)
+        return 0;
+    remaining--;
+    /* Now we can finally pull out the byte array with the actual hostname. */
+    if (remaining <= 2)
+        return 0;
+    len = (*(p++) << 1);
+    len += *(p++);
+    if (len + 2 > remaining)
+        return 0;
+    remaining = len;
+    servername = (const char *)p;
+
+    if (len == strlen("server2") && strncmp(servername, "server2", len) == 0) {
+        SSL_CTX *new_ctx = arg;
+        SSL_set_SSL_CTX(s, new_ctx);
+        /*
+         * Copy over all the SSL_CTX options - reasonable behavior
+         * allows testing of cases where the options between two
+         * contexts differ/conflict
+         */
+        SSL_clear_options(s, 0xFFFFFFFFL);
+        SSL_set_options(s, SSL_CTX_get_options(new_ctx));
+
+        ex_data->servername = SSL_TEST_SERVERNAME_SERVER2;
+        return 1;
+    } else if (len == strlen("server1") &&
+               strncmp(servername, "server1", len) == 0) {
+        ex_data->servername = SSL_TEST_SERVERNAME_SERVER1;
+        return 1;
+    } else if (ignore) {
+        ex_data->servername = SSL_TEST_SERVERNAME_SERVER1;
+        return 1;
+    }
+    return 0;
+}
 /*
  * (RFC 6066):
  *  If the server understood the ClientHello extension but
@@ -144,6 +205,50 @@ static int servername_reject_cb(SSL *s, int *ad, void *arg)
     return select_server_ctx(s, arg, 0);
 }
 
+static int early_ignore_cb(SSL *s, int *al, void *arg)
+{
+    if (!early_select_server_ctx(s, arg, 1)) {
+        *al = SSL_AD_UNRECOGNIZED_NAME;
+        return 0;
+    }
+    return 1;
+}
+
+static int early_reject_cb(SSL *s, int *al, void *arg)
+{
+    if (!early_select_server_ctx(s, arg, 0)) {
+        *al = SSL_AD_UNRECOGNIZED_NAME;
+        return 0;
+    }
+    return 1;
+}
+
+static int early_nov12_cb(SSL *s, int *al, void *arg)
+{
+    int ret;
+    unsigned int v;
+    const unsigned char *p;
+
+    v = SSL_early_get0_legacy_version(s);
+    if (v > TLS1_2_VERSION || v < SSL3_VERSION) {
+        *al = SSL_AD_PROTOCOL_VERSION;
+        return 0;
+    }
+    (void)SSL_early_get0_session_id(s, &p);
+    if (p == NULL ||
+        SSL_early_get0_random(s, &p) == 0 ||
+        SSL_early_get0_ciphers(s, &p) == 0 ||
+        SSL_early_get0_compression_methods(s, &p) == 0) {
+        *al = SSL_AD_INTERNAL_ERROR;
+        return 0;
+    }
+    ret = early_select_server_ctx(s, arg, 0);
+    SSL_set_max_proto_version(s, TLS1_1_VERSION);
+    if (!ret)
+        *al = SSL_AD_UNRECOGNIZED_NAME;
+    return ret;
+}
+
 static unsigned char dummy_ocsp_resp_good_val = 0xff;
 static unsigned char dummy_ocsp_resp_bad_val = 0xfe;
 
@@ -337,7 +442,10 @@ static void configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
         break;
     }
 
-    /* link the two contexts for SNI purposes */
+    /*
+     * Link the two contexts for SNI purposes.
+     * Also do early callbacks here, as setting both early and SNI is bad.
+     */
     switch (extra->server.servername_callback) {
     case SSL_TEST_SERVERNAME_IGNORE_MISMATCH:
         SSL_CTX_set_tlsext_servername_callback(server_ctx, servername_ignore_cb);
@@ -349,6 +457,14 @@ static void configure_handshake_ctx(SSL_CTX *server_ctx, SSL_CTX *server2_ctx,
         break;
     case SSL_TEST_SERVERNAME_CB_NONE:
         break;
+    case SSL_TEST_SERVERNAME_EARLY_IGNORE_MISMATCH:
+        SSL_CTX_set_early_cb(server_ctx, early_ignore_cb, server2_ctx);
+        break;
+    case SSL_TEST_SERVERNAME_EARLY_REJECT_MISMATCH:
+        SSL_CTX_set_early_cb(server_ctx, early_reject_cb, server2_ctx);
+        break;
+    case SSL_TEST_SERVERNAME_EARLY_NO_V12:
+        SSL_CTX_set_early_cb(server_ctx, early_nov12_cb, server2_ctx);
     }
 
     if (extra->server.cert_status != SSL_TEST_CERT_STATUS_NONE) {
diff --git a/test/recipes/70-test_sslrecords.t b/test/recipes/70-test_sslrecords.t
index f699058..2ffa2cd 100644
--- a/test/recipes/70-test_sslrecords.t
+++ b/test/recipes/70-test_sslrecords.t
@@ -97,7 +97,7 @@ $sslv2testtype = SSLV2_IN_SSLV2;
 $proxy->clear();
 $proxy->serverflags("-tls1_2");
 $proxy->start();
-ok(!TLSProxy::Message->end(), "SSLv2 in SSLv2 ClientHello test");
+ok(TLSProxy::Message->fail(), "SSLv2 in SSLv2 ClientHello test");
 
 #Test 7: Sanity check ClientHello fragmentation. This isn't really an SSLv2 test
 #        at all, but it gives us confidence that Test 8 fails for the right
diff --git a/test/recipes/80-test_cipherlist.t b/test/recipes/80-test_cipherbytes.t
similarity index 78%
copy from test/recipes/80-test_cipherlist.t
copy to test/recipes/80-test_cipherbytes.t
index 98d537e..27d627e 100644
--- a/test/recipes/80-test_cipherlist.t
+++ b/test/recipes/80-test_cipherbytes.t
@@ -1,6 +1,6 @@
 #! /usr/bin/perl
 #
-# Copyright 2016-2016 The OpenSSL Project Authors. All Rights Reserved.
+# Copyright 2017 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
@@ -15,7 +15,7 @@ use OpenSSL::Test::Simple;
 use OpenSSL::Test;
 use OpenSSL::Test::Utils qw(alldisabled available_protocols);
 
-setup("test_cipherlist");
+setup("test_cipherbytes");
 
 my $no_anytls = alldisabled(available_protocols("tls"));
 
@@ -23,4 +23,4 @@ my $no_anytls = alldisabled(available_protocols("tls"));
 plan skip_all => "No SSL/TLS protocol is supported by this OpenSSL build."
     if $no_anytls;
 
-simple_test("test_cipherlist", "cipherlist_test", "cipherlist");
+simple_test("test_cipherbytes", "cipherbytes_test", "bytes_to_cipherlist");
diff --git a/test/ssl-tests/05-sni.conf b/test/ssl-tests/05-sni.conf
index e1fb3d9..d5d350e 100644
--- a/test/ssl-tests/05-sni.conf
+++ b/test/ssl-tests/05-sni.conf
@@ -1,6 +1,6 @@
 # Generated with generate_ssl_tests.pl
 
-num_tests = 6
+num_tests = 9
 
 test-0 = 0-SNI-switch-context
 test-1 = 1-SNI-keep-context
@@ -8,6 +8,9 @@ test-2 = 2-SNI-no-server-support
 test-3 = 3-SNI-no-client-support
 test-4 = 4-SNI-bad-sni-ignore-mismatch
 test-5 = 5-SNI-bad-sni-reject-mismatch
+test-6 = 6-SNI-bad-early-sni-ignore-mismatch
+test-7 = 7-SNI-bad-early-sni-reject-mismatch
+test-8 = 8-SNI-early-disable-v12
 # ===========================================================
 
 [0-SNI-switch-context]
@@ -201,3 +204,105 @@ ServerNameCallback = RejectMismatch
 ServerName = invalid
 
 
+# ===========================================================
+
+[6-SNI-bad-early-sni-ignore-mismatch]
+ssl_conf = 6-SNI-bad-early-sni-ignore-mismatch-ssl
+
+[6-SNI-bad-early-sni-ignore-mismatch-ssl]
+server = 6-SNI-bad-early-sni-ignore-mismatch-server
+client = 6-SNI-bad-early-sni-ignore-mismatch-client
+server2 = 6-SNI-bad-early-sni-ignore-mismatch-server
+
+[6-SNI-bad-early-sni-ignore-mismatch-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[6-SNI-bad-early-sni-ignore-mismatch-client]
+CipherString = DEFAULT
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-6]
+ExpectedResult = Success
+ExpectedServerName = server1
+server = 6-SNI-bad-early-sni-ignore-mismatch-server-extra
+server2 = 6-SNI-bad-early-sni-ignore-mismatch-server-extra
+client = 6-SNI-bad-early-sni-ignore-mismatch-client-extra
+
+[6-SNI-bad-early-sni-ignore-mismatch-server-extra]
+ServerNameCallback = EarlyIgnoreMismatch
+
+[6-SNI-bad-early-sni-ignore-mismatch-client-extra]
+ServerName = invalid
+
+
+# ===========================================================
+
+[7-SNI-bad-early-sni-reject-mismatch]
+ssl_conf = 7-SNI-bad-early-sni-reject-mismatch-ssl
+
+[7-SNI-bad-early-sni-reject-mismatch-ssl]
+server = 7-SNI-bad-early-sni-reject-mismatch-server
+client = 7-SNI-bad-early-sni-reject-mismatch-client
+server2 = 7-SNI-bad-early-sni-reject-mismatch-server
+
+[7-SNI-bad-early-sni-reject-mismatch-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[7-SNI-bad-early-sni-reject-mismatch-client]
+CipherString = DEFAULT
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-7]
+ExpectedResult = ServerFail
+ExpectedServerAlert = UnrecognizedName
+server = 7-SNI-bad-early-sni-reject-mismatch-server-extra
+server2 = 7-SNI-bad-early-sni-reject-mismatch-server-extra
+client = 7-SNI-bad-early-sni-reject-mismatch-client-extra
+
+[7-SNI-bad-early-sni-reject-mismatch-server-extra]
+ServerNameCallback = EarlyRejectMismatch
+
+[7-SNI-bad-early-sni-reject-mismatch-client-extra]
+ServerName = invalid
+
+
+# ===========================================================
+
+[8-SNI-early-disable-v12]
+ssl_conf = 8-SNI-early-disable-v12-ssl
+
+[8-SNI-early-disable-v12-ssl]
+server = 8-SNI-early-disable-v12-server
+client = 8-SNI-early-disable-v12-client
+server2 = 8-SNI-early-disable-v12-server
+
+[8-SNI-early-disable-v12-server]
+Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem
+CipherString = DEFAULT
+PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem
+
+[8-SNI-early-disable-v12-client]
+CipherString = DEFAULT
+VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem
+VerifyMode = Peer
+
+[test-8]
+ExpectedProtocol = TLSv1.1
+ExpectedServerName = server2
+server = 8-SNI-early-disable-v12-server-extra
+server2 = 8-SNI-early-disable-v12-server-extra
+client = 8-SNI-early-disable-v12-client-extra
+
+[8-SNI-early-disable-v12-server-extra]
+ServerNameCallback = EarlyNoV12
+
+[8-SNI-early-disable-v12-client-extra]
+ServerName = server2
+
+
diff --git a/test/ssl-tests/05-sni.conf.in b/test/ssl-tests/05-sni.conf.in
index 76003e7..63b295d 100644
--- a/test/ssl-tests/05-sni.conf.in
+++ b/test/ssl-tests/05-sni.conf.in
@@ -13,6 +13,7 @@ use strict;
 use warnings;
 
 package ssltests;
+use OpenSSL::Test::Utils;
 
 our @tests = (
     {
@@ -109,4 +110,60 @@ our @tests = (
             "ExpectedServerAlert" => "UnrecognizedName"
         },
     },
+    {
+        name => "SNI-bad-early-sni-ignore-mismatch",
+        server => {
+            extra => {
+                "ServerNameCallback" => "EarlyIgnoreMismatch",
+            },
+        },
+        client => {
+            extra => {
+                "ServerName" => "invalid",
+            },
+        },
+        test   => {
+            "ExpectedServerName" => "server1",
+            "ExpectedResult" => "Success"
+        },
+    },
+    {
+        name => "SNI-bad-early-sni-reject-mismatch",
+        server => {
+            extra => {
+                "ServerNameCallback" => "EarlyRejectMismatch",
+            },
+        },
+        client => {
+            extra => {
+                "ServerName" => "invalid",
+            },
+        },
+        test   => {
+            "ExpectedResult" => "ServerFail",
+            "ExpectedServerAlert" => "UnrecognizedName"
+        },
+    },
 );
+
+our @tests_tls_1_1 = (
+    {
+        name => "SNI-early-disable-v12",
+        server => {
+            extra => {
+                "ServerNameCallback" => "EarlyNoV12",
+            },
+        },
+        client => {
+            extra => {
+                "ServerName" => "server2",
+            },
+        },
+        test   => {
+            "ExpectedProtocol" => "TLSv1.1",
+            "ExpectedServerName" => "server2",
+        },
+    },
+);
+
+push @tests, @tests_tls_1_1 unless disabled("tls1_1");
diff --git a/test/ssl_test_ctx.c b/test/ssl_test_ctx.c
index 66fb31c..f9e74d1 100644
--- a/test/ssl_test_ctx.c
+++ b/test/ssl_test_ctx.c
@@ -237,6 +237,9 @@ static const test_enum ssl_servername_callbacks[] = {
     {"None", SSL_TEST_SERVERNAME_CB_NONE},
     {"IgnoreMismatch", SSL_TEST_SERVERNAME_IGNORE_MISMATCH},
     {"RejectMismatch", SSL_TEST_SERVERNAME_REJECT_MISMATCH},
+    {"EarlyIgnoreMismatch", SSL_TEST_SERVERNAME_EARLY_IGNORE_MISMATCH},
+    {"EarlyRejectMismatch", SSL_TEST_SERVERNAME_EARLY_REJECT_MISMATCH},
+    {"EarlyNoV12", SSL_TEST_SERVERNAME_EARLY_NO_V12},
 };
 
 __owur static int parse_servername_callback(SSL_TEST_SERVER_CONF *server_conf,
diff --git a/test/ssl_test_ctx.h b/test/ssl_test_ctx.h
index 1c66740..499e314 100644
--- a/test/ssl_test_ctx.h
+++ b/test/ssl_test_ctx.h
@@ -38,7 +38,10 @@ typedef enum {
 typedef enum {
     SSL_TEST_SERVERNAME_CB_NONE = 0,  /* Default */
     SSL_TEST_SERVERNAME_IGNORE_MISMATCH,
-    SSL_TEST_SERVERNAME_REJECT_MISMATCH
+    SSL_TEST_SERVERNAME_REJECT_MISMATCH,
+    SSL_TEST_SERVERNAME_EARLY_IGNORE_MISMATCH,
+    SSL_TEST_SERVERNAME_EARLY_REJECT_MISMATCH,
+    SSL_TEST_SERVERNAME_EARLY_NO_V12
 } ssl_servername_callback_t;
 
 typedef enum {
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 47f008a..f492e0b 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -338,7 +338,7 @@ static int test_keylog(void) {
         goto end;
     }
 
-    if (!create_ssl_connection(serverssl, clientssl)) {
+    if (!create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)) {
         printf("Unable to create SSL connection\n");
         goto end;
     }
@@ -435,7 +435,7 @@ static int test_keylog_no_master_key(void) {
         goto end;
     }
 
-    if (!create_ssl_connection(serverssl, clientssl)) {
+    if (!create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)) {
         printf("Unable to create SSL connection\n");
         goto end;
     }
@@ -477,6 +477,89 @@ end:
 }
 #endif
 
+static int full_early_callback(SSL *s, int *al, void *arg)
+{
+    int *ctr = arg;
+    const unsigned char *p;
+    /* We only configure two ciphers, but the SCSV is added automatically. */
+#ifdef OPENSSL_NO_EC
+    const unsigned char expected_ciphers[] = {0x00, 0x9d, 0x00, 0xff};
+#else
+    const unsigned char expected_ciphers[] = {0x00, 0x9d, 0xc0,
+                                              0x2c, 0x00, 0xff};
+#endif
+    size_t len;
+
+    /* Make sure we can defer processing and get called back. */
+    if ((*ctr)++ == 0)
+        return -1;
+
+    len = SSL_early_get0_ciphers(s, &p);
+    if (len != sizeof(expected_ciphers) ||
+        memcmp(p, expected_ciphers, len) != 0) {
+        printf("Early callback expected ciphers mismatch\n");
+        return 0;
+    }
+    len = SSL_early_get0_compression_methods(s, &p);
+    if (len != 1 || *p != 0) {
+        printf("Early callback expected comperssion methods mismatch\n");
+        return 0;
+    }
+    return 1;
+}
+
+static int test_early_cb(void) {
+    SSL_CTX *cctx = NULL, *sctx = NULL;
+    SSL *clientssl = NULL, *serverssl = NULL;
+    int testctr = 0, testresult = 0;
+
+    if (!create_ssl_ctx_pair(TLS_server_method(), TLS_client_method(), &sctx,
+                             &cctx, cert, privkey)) {
+        printf("Unable to create SSL_CTX pair\n");
+        goto end;
+    }
+
+    SSL_CTX_set_early_cb(sctx, full_early_callback, &testctr);
+    /* The gimpy cipher list we configure can't do TLS 1.3. */
+    SSL_CTX_set_max_proto_version(cctx, TLS1_2_VERSION);
+    if (!SSL_CTX_set_cipher_list(cctx,
+            "AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384")) {
+        printf("Failed to set cipher list\n");
+        goto end;
+    }
+
+    if (!create_ssl_objects(sctx, cctx, &serverssl, &clientssl, NULL, NULL)) {
+        printf("Unable to create SSL objects\n");
+        goto end;
+    }
+
+    if (create_ssl_connection(serverssl, clientssl, SSL_ERROR_WANT_EARLY)) {
+        printf("Creating SSL connection succeeded with async early return\n");
+        goto end;
+    }
+
+    /* Passing a -1 literal is a hack since the real value was lost. */
+    if (SSL_get_error(serverssl, -1) != SSL_ERROR_WANT_EARLY) {
+        printf("Early callback failed to make state SSL_ERROR_WANT_EARLY\n");
+        goto end;
+    }
+
+    if (!create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)) {
+        printf("Restarting SSL connection failed\n");
+        goto end;
+    }
+
+    testresult = 1;
+
+end:
+    SSL_free(serverssl);
+    SSL_free(clientssl);
+    SSL_CTX_free(sctx);
+    SSL_CTX_free(cctx);
+
+    return testresult;
+}
+
 static int execute_test_large_message(const SSL_METHOD *smeth,
                                       const SSL_METHOD *cmeth, int read_ahead)
 {
@@ -541,7 +624,7 @@ static int execute_test_large_message(const SSL_METHOD *smeth,
         goto end;
     }
 
-    if (!create_ssl_connection(serverssl, clientssl)) {
+    if (!create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)) {
         printf("Unable to create SSL connection\n");
         goto end;
     }
@@ -719,7 +802,7 @@ static int test_tlsext_status_type(void)
         goto end;
     }
 
-    if (!create_ssl_connection(serverssl, clientssl)) {
+    if (!create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)) {
         printf("Unable to create SSL connection\n");
         goto end;
     }
@@ -745,7 +828,7 @@ static int test_tlsext_status_type(void)
     }
 
     /* This should fail because the callback will fail */
-    if (create_ssl_connection(serverssl, clientssl)) {
+    if (create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)) {
         printf("Unexpected success creating the connection\n");
         goto end;
     }
@@ -799,7 +882,7 @@ static int test_tlsext_status_type(void)
     BIO_free(certbio);
     certbio = NULL;
 
-    if (!create_ssl_connection(serverssl, clientssl)) {
+    if (!create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)) {
         printf("Unable to create SSL connection\n");
         goto end;
     }
@@ -906,7 +989,7 @@ static int execute_test_session(SSL_SESSION_TEST_FIXTURE fix)
         goto end;
     }
 
-    if (!create_ssl_connection(serverssl1, clientssl1)) {
+    if (!create_ssl_connection(serverssl1, clientssl1, SSL_ERROR_NONE)) {
         printf("Unable to create SSL connection\n");
         goto end;
     }
@@ -932,7 +1015,7 @@ static int execute_test_session(SSL_SESSION_TEST_FIXTURE fix)
         goto end;
     }
 
-    if (!create_ssl_connection(serverssl2, clientssl2)) {
+    if (!create_ssl_connection(serverssl2, clientssl2, SSL_ERROR_NONE)) {
         printf("Unable to create second SSL connection\n");
         goto end;
     }
@@ -1015,7 +1098,7 @@ static int execute_test_session(SSL_SESSION_TEST_FIXTURE fix)
     }
 
     /* This should fail because of the mismatched protocol versions */
-    if (create_ssl_connection(serverssl3, clientssl3)) {
+    if (create_ssl_connection(serverssl3, clientssl3, SSL_ERROR_NONE)) {
         printf("Unable to create third SSL connection\n");
         goto end;
     }
@@ -1436,7 +1519,7 @@ static int test_set_sigalgs(int idx)
         }
     }
 
-    if (curr->connsuccess != create_ssl_connection(serverssl, clientssl)) {
+    if (curr->connsuccess != create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)) {
         printf("Unexpected return value creating SSL connection (%d)\n", idx);
         goto end;
     }
@@ -1485,6 +1568,7 @@ int test_main(int argc, char *argv[])
 #ifndef OPENSSL_NO_TLS1_3
     ADD_TEST(test_keylog_no_master_key);
 #endif
+    ADD_TEST(test_early_cb);
 
     testresult = run_tests(argv[0]);
 
diff --git a/test/sslcorrupttest.c b/test/sslcorrupttest.c
index c1f074b..8ccad16 100644
--- a/test/sslcorrupttest.c
+++ b/test/sslcorrupttest.c
@@ -240,7 +240,7 @@ static int test_ssl_corrupt(int testidx)
         goto end;
     }
 
-    if (!create_ssl_connection(server, client)) {
+    if (!create_ssl_connection(server, client, SSL_ERROR_NONE)) {
         printf("Unable to create SSL connection\n");
         ERR_print_errors_fp(stdout);
         goto end;
diff --git a/test/ssltestlib.c b/test/ssltestlib.c
index 1981cb5..64aa916 100644
--- a/test/ssltestlib.c
+++ b/test/ssltestlib.c
@@ -641,7 +641,7 @@ int create_ssl_objects(SSL_CTX *serverctx, SSL_CTX *clientctx, SSL **sssl,
     return 0;
 }
 
-int create_ssl_connection(SSL *serverssl, SSL *clientssl)
+int create_ssl_connection(SSL *serverssl, SSL *clientssl, int want)
 {
     int retc = -1, rets = -1, err, abortctr = 0;
     int clienterr = 0, servererr = 0;
@@ -660,6 +660,8 @@ int create_ssl_connection(SSL *serverssl, SSL *clientssl)
             printf("SSL_connect() failed %d, %d\n", retc, err);
             clienterr = 1;
         }
+        if (want != SSL_ERROR_NONE && err == want)
+            return 0;
 
         err = SSL_ERROR_WANT_WRITE;
         while (!servererr && rets <= 0 && err == SSL_ERROR_WANT_WRITE) {
@@ -669,9 +671,11 @@ int create_ssl_connection(SSL *serverssl, SSL *clientssl)
         }
 
         if (!servererr && rets <= 0 && err != SSL_ERROR_WANT_READ) {
-            printf("SSL_accept() failed %d, %d\n", retc, err);
+            printf("SSL_accept() failed %d, %d\n", rets, err);
             servererr = 1;
         }
+        if (want != SSL_ERROR_NONE && err == want)
+            return 0;
         if (clienterr && servererr)
             return 0;
         if (++abortctr == MAXLOOPS) {
diff --git a/test/ssltestlib.h b/test/ssltestlib.h
index bd9272f..e74a5cc 100644
--- a/test/ssltestlib.h
+++ b/test/ssltestlib.h
@@ -17,7 +17,7 @@ int create_ssl_ctx_pair(const SSL_METHOD *sm, const SSL_METHOD *cm,
                         char *privkeyfile);
 int create_ssl_objects(SSL_CTX *serverctx, SSL_CTX *clientctx, SSL **sssl,
                        SSL **cssl, BIO *s_to_c_fbio, BIO *c_to_s_fbio);
-int create_ssl_connection(SSL *serverssl, SSL *clientssl);
+int create_ssl_connection(SSL *serverssl, SSL *clientssl, int want);
 
 /* Note: Not thread safe! */
 const BIO_METHOD *bio_f_tls_dump_filter(void);
diff --git a/util/libssl.num b/util/libssl.num
index 32b5f76..20642ce 100644
--- a/util/libssl.num
+++ b/util/libssl.num
@@ -415,3 +415,12 @@ SSL_CTX_get_keylog_callback             415	1_1_1	EXIST::FUNCTION:
 SSL_get_peer_signature_type_nid         416	1_1_1	EXIST::FUNCTION:
 SSL_key_update                          417	1_1_1	EXIST::FUNCTION:
 SSL_get_key_update_type                 418	1_1_1	EXIST::FUNCTION:
+SSL_bytes_to_cipher_list                419	1_1_1	EXIST::FUNCTION:
+SSL_early_get0_compression_methods      420	1_1_1	EXIST::FUNCTION:
+SSL_early_get0_ciphers                  421	1_1_1	EXIST::FUNCTION:
+SSL_early_get0_ext                      422	1_1_1	EXIST::FUNCTION:
+SSL_early_get0_session_id               423	1_1_1	EXIST::FUNCTION:
+SSL_early_get0_random                   424	1_1_1	EXIST::FUNCTION:
+SSL_CTX_set_early_cb                    425	1_1_1	EXIST::FUNCTION:
+SSL_early_get0_legacy_version           426	1_1_1	EXIST::FUNCTION:
+SSL_early_isv2                          427	1_1_1	EXIST::FUNCTION:


More information about the openssl-commits mailing list