[openssl-commits] [openssl] master update

Matt Caswell matt at openssl.org
Fri Apr 7 12:59:54 UTC 2017


The branch master has been updated
       via  a273157a3e10930b52b30e0ee39552eaf2a56e17 (commit)
       via  0f5af6b1b854a7086e025f51f1794b179131e61c (commit)
       via  787d9ec74c86b045ef1ed67e8dc5dc65134ed205 (commit)
       via  cd17bb190cf65413877bcddffb5d1fecdb85eef3 (commit)
       via  64350ab5877aa30dc8b89cf3373dc28c8b013e19 (commit)
       via  314aec07ef25844c498794f49dfb1fdf6b467323 (commit)
       via  a37008d90fc4d2bd31a747aebdaf59eaa4e6efaf (commit)
       via  43ae5eed6f8665b88f45445df666ab2688aae7b0 (commit)
       via  fe874d27d33faa527b5e945137787bf6b0f5c253 (commit)
       via  b443c845942289c831fe867ced1ef11574514385 (commit)
      from  789a2b6250d5e05dfde6ce259e79ef8c172c9f3f (commit)


- Log -----------------------------------------------------------------
commit a273157a3e10930b52b30e0ee39552eaf2a56e17
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Apr 7 11:56:27 2017 +0100

    Fix a test failure when configured without TLSv1.3
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3139)

commit 0f5af6b1b854a7086e025f51f1794b179131e61c
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Apr 7 11:23:00 2017 +0100

    Remove an out of date TODO
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3139)

commit 787d9ec74c86b045ef1ed67e8dc5dc65134ed205
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Apr 7 11:20:00 2017 +0100

    Create an ENDPOINT enum type for use internally
    
    We need it for the custom extensions API
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3139)

commit cd17bb190cf65413877bcddffb5d1fecdb85eef3
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Apr 7 11:04:38 2017 +0100

    Prefix custom extension API callback types with SSL_
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3139)

commit 64350ab5877aa30dc8b89cf3373dc28c8b013e19
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Apr 7 10:56:59 2017 +0100

    Various style tweaks based on feedback
    
    Style updates for the new custom extensions API
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3139)

commit 314aec07ef25844c498794f49dfb1fdf6b467323
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Apr 6 17:33:23 2017 +0100

    Add documentation for the new custom extensions API
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3139)

commit a37008d90fc4d2bd31a747aebdaf59eaa4e6efaf
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Apr 5 17:29:47 2017 +0100

    Add some tests for the new custom extensions API
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3139)

commit 43ae5eed6f8665b88f45445df666ab2688aae7b0
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Apr 5 11:59:23 2017 +0100

    Implement a new custom extensions API
    
    The old custom extensions API was not TLSv1.3 aware. Extensions are used
    extensively in TLSv1.3 and they can appear in many different types of
    messages. Therefore we need a new API to be able to cope with that.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3139)

commit fe874d27d33faa527b5e945137787bf6b0f5c253
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Apr 4 11:40:02 2017 +0100

    Move the extensions context codes into the public API
    
    This move prepares for the later addition of the new custom extensions
    API. The context codes have an additional "SSL_" added to their name to
    ensure we don't have name clashes with other applications.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3139)

commit b443c845942289c831fe867ced1ef11574514385
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Apr 4 11:11:58 2017 +0100

    Move ssl/t1_ext.c to ssl/statem/extensions_cust.c
    
    Brings all the extensions code together.
    
    Reviewed-by: Rich Salz <rsalz at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/3139)

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

Summary of changes:
 doc/man3/SSL_extension_supported.pod | 244 +++++++++++++----
 include/openssl/ssl.h                |  53 ++++
 ssl/build.info                       |   4 +-
 ssl/ssl_cert.c                       |   7 +-
 ssl/ssl_locl.h                       |  35 ++-
 ssl/ssl_rsa.c                        |  30 +--
 ssl/ssl_sess.c                       |   7 +-
 ssl/statem/extensions.c              | 255 +++++++++---------
 ssl/statem/extensions_clnt.c         |  10 +-
 ssl/statem/extensions_cust.c         | 494 +++++++++++++++++++++++++++++++++++
 ssl/statem/extensions_srvr.c         |   2 +-
 ssl/statem/statem_clnt.c             |  35 +--
 ssl/statem/statem_lib.c              |   2 +-
 ssl/statem/statem_locl.h             |  27 +-
 ssl/statem/statem_srvr.c             |  29 +-
 ssl/t1_ext.c                         | 277 --------------------
 test/sslapitest.c                    | 312 ++++++++++++++++++++++
 util/libssl.num                      |   1 +
 18 files changed, 1271 insertions(+), 553 deletions(-)
 create mode 100644 ssl/statem/extensions_cust.c
 delete mode 100644 ssl/t1_ext.c

diff --git a/doc/man3/SSL_extension_supported.pod b/doc/man3/SSL_extension_supported.pod
index 166c35a..54a55a7 100644
--- a/doc/man3/SSL_extension_supported.pod
+++ b/doc/man3/SSL_extension_supported.pod
@@ -3,6 +3,7 @@
 =head1 NAME
 
 SSL_extension_supported,
+SSL_CTX_add_custom_ext,
 SSL_CTX_add_client_custom_ext, SSL_CTX_add_server_custom_ext,
 custom_ext_add_cb, custom_ext_free_cb, custom_ext_parse_cb
 - custom TLS extension handling
@@ -11,19 +12,32 @@ custom_ext_add_cb, custom_ext_free_cb, custom_ext_parse_cb
 
  #include <openssl/ssl.h>
 
- int SSL_CTX_add_client_custom_ext(SSL_CTX *ctx, unsigned int ext_type,
-                                   custom_ext_add_cb add_cb,
-                                   custom_ext_free_cb free_cb, void *add_arg,
-                                   custom_ext_parse_cb parse_cb,
-                                   void *parse_arg);
-
- int SSL_CTX_add_server_custom_ext(SSL_CTX *ctx, unsigned int ext_type,
-                                   custom_ext_add_cb add_cb,
-                                   custom_ext_free_cb free_cb, void *add_arg,
-                                   custom_ext_parse_cb parse_cb,
-                                   void *parse_arg);
-
- int SSL_extension_supported(unsigned int ext_type);
+ typedef int (*SSL_custom_ext_add_cb_ex) (SSL *s, unsigned int ext_type,
+                                          unsigned int context,
+                                          const unsigned char **out,
+                                          size_t *outlen, X509 *x,
+                                          size_t chainidx, int *al,
+                                          void *add_arg);
+
+ typedef void (*SSL_custom_ext_free_cb_ex) (SSL *s, unsigned int ext_type,
+                                            unsigned int context,
+                                            const unsigned char *out,
+                                            void *add_arg);
+
+ typedef int (*SSL_custom_ext_parse_cb_ex) (SSL *s, unsigned int ext_type,
+                                            unsigned int context,
+                                            const unsigned char *in,
+                                            size_t inlen, X509 *x,
+                                            size_t chainidx, int *al,
+                                            void *parse_arg);
+
+ int SSL_CTX_add_custom_ext(SSL_CTX *ctx, unsigned int ext_type,
+                            unsigned int context,
+                            SSL_custom_ext_add_cb_ex add_cb,
+                            SSL_custom_ext_free_cb_ex free_cb,
+                            void *add_arg,
+                            SSL_custom_ext_parse_cb_ex parse_cb,
+                            void *parse_arg);
 
  typedef int (*custom_ext_add_cb)(SSL *s, unsigned int ext_type,
                                   const unsigned char **out,
@@ -39,19 +53,47 @@ custom_ext_add_cb, custom_ext_free_cb, custom_ext_parse_cb
                                     size_t inlen, int *al,
                                     void *parse_arg);
 
+ int SSL_CTX_add_client_custom_ext(SSL_CTX *ctx, unsigned int ext_type,
+                                   custom_ext_add_cb add_cb,
+                                   custom_ext_free_cb free_cb, void *add_arg,
+                                   custom_ext_parse_cb parse_cb,
+                                   void *parse_arg);
+
+ int SSL_CTX_add_server_custom_ext(SSL_CTX *ctx, unsigned int ext_type,
+                                   custom_ext_add_cb add_cb,
+                                   custom_ext_free_cb free_cb, void *add_arg,
+                                   custom_ext_parse_cb parse_cb,
+                                   void *parse_arg);
+
+ int SSL_extension_supported(unsigned int ext_type);
 
 =head1 DESCRIPTION
 
-SSL_CTX_add_client_custom_ext() adds a custom extension for a TLS client
+SSL_CTX_add_custom_ext() adds a custom extension for a TLS/DTLS client or server
+for all supported protocol versions with extension type B<ext_type> and
+callbacks B<add_cb>, B<free_cb> and B<parse_cb> (see the
+L</EXTENSION CALLBACKS> section below). The B<context> value determines
+which messages and under what conditions the extension will be added/parsed (see
+the L</EXTENSION CONTEXTS> section below).
+
+SSL_CTX_add_client_custom_ext() adds a custom extension for a TLS/DTLS client
 with extension type B<ext_type> and callbacks B<add_cb>, B<free_cb> and
-B<parse_cb>.
+B<parse_cb>. This function is similar to SSL_CTX_add_custom_ext() except it only
+applies to clients, uses the older style of callbacks, and implicitly sets the
+B<context> value to:
 
-SSL_CTX_add_server_custom_ext() adds a custom extension for a TLS server
+ SSL_EXT_TLS1_2_AND_BELOW_ONLY | SSL_EXT_CLIENT_HELLO
+ | SSL_EXT_TLS1_2_SERVER_HELLO | SSL_EXT_IGNORE_ON_RESUMPTION
+
+SSL_CTX_add_server_custom_ext() adds a custom extension for a TLS/DTLS server
 with extension type B<ext_type> and callbacks B<add_cb>, B<free_cb> and
-B<parse_cb>.
+B<parse_cb>. This function is similar to SSL_CTX_add_custom_ext() except it
+only applies to servers, uses the older style of callbacks, and implicitly sets
+the B<context> value to the same as for SSL_CTX_add_client_custom_ext() above.
 
-In both cases the extension type must not be handled by OpenSSL internally
-or an error occurs.
+The B<ext_type> parameter corresponds to the B<extension_type> field of
+RFC5246 et al. It is B<not> a NID. In all cases the extension type must not be
+handled by OpenSSL internally or an error occurs.
 
 SSL_extension_supported() returns 1 if the extension B<ext_type> is handled
 internally by OpenSSL and 0 otherwise.
@@ -59,9 +101,11 @@ internally by OpenSSL and 0 otherwise.
 =head1 EXTENSION CALLBACKS
 
 The callback B<add_cb> is called to send custom extension data to be
-included in ClientHello for TLS clients or ServerHello for servers. The
-B<ext_type> parameter is set to the extension type which will be added and
-B<add_arg> to the value set when the extension handler was added.
+included in various TLS messages. The B<ext_type> parameter is set to the
+extension type which will be added and B<add_arg> to the value set when the
+extension handler was added. When using the new style callbacks the B<context>
+parameter will indicate which message is currently being constructed e.g. for
+the ClientHello it will be set to B<SSL_EXT_CLIENT_HELLO>.
 
 If the application wishes to include the extension B<ext_type> it should
 set B<*out> to the extension data, set B<*outlen> to the length of the
@@ -72,16 +116,26 @@ If the B<add_cb> does not wish to include the extension it must return 0.
 If B<add_cb> returns -1 a fatal handshake error occurs using the TLS
 alert value specified in B<*al>.
 
-For clients (but not servers) if B<add_cb> is set to NULL a zero length
-extension is added for B<ext_type>.
+When constructing the ClientHello, if B<add_cb> is set to NULL a zero length
+extension is added for B<ext_type>. For all other messages if B<add_cb> is set
+to NULL then no extension is added.
 
-For clients every registered B<add_cb> is always called to see if the
-application wishes to add an extension to ClientHello.
+When constructing a Certificate message the callback will be called for each
+certificate in the message. The B<x> parameter will indicate the
+current certificate and the B<chainidx> parameter will indicate the position
+of the certificate in the message. The first certificate is always the end
+entity certificate and has a B<chainidx> value of 0. The certificates are in the
+order that they were received in the Certificate message.
 
-For servers every registered B<add_cb> is called once if and only if the
-corresponding extension was received in ClientHello to see if the application
-wishes to add the extension to ServerHello. That is, if no corresponding extension
-was received in ClientHello then B<add_cb> will not be called.
+For all messages except the ServerHello and EncryptedExtensions every
+registered B<add_cb> is always called to see if the application wishes to add an
+extension (as long as all requirements of the specified B<context> are met).
+
+For the ServerHello and EncryptedExtension messages every registered B<add_cb>
+is called once if and only if the requirements of the specified B<context> are
+met and the corresponding extension was received in the ClientHello. That is, if
+no corresponding extension was received in the ClientHello then B<add_cb> will
+not be called.
 
 If an extension is added (that is B<add_cb> returns 1) B<free_cb> is called
 (if it is set) with the value of B<out> set by the add callback. It can be
@@ -89,12 +143,19 @@ used to free up any dynamic extension data set by B<add_cb>. Since B<out> is
 constant (to permit use of constant data in B<add_cb>) applications may need to
 cast away const to free the data.
 
-The callback B<parse_cb> receives data for TLS extensions. For TLS clients
-the extension data will come from ServerHello and for TLS servers it will
-come from ClientHello.
+The callback B<parse_cb> receives data for TLS extensions. The callback is only
+called if the extension is present and relevant for the context (see
+L</EXTENSION CONTEXTS> below).
 
 The extension data consists of B<inlen> bytes in the buffer B<in> for the
-extension B<extension_type>.
+extension B<ext_type>.
+
+If the message being parsed is a TLSv1.3 compatible Certificate message then
+B<parse_cb> will be called for each certificate contained within the message.
+The B<x> parameter will indicate the current certificate and the B<chainidx>
+parameter will indicate the position of the certificate in the message. The
+first certificate is always the end entity certificate and has a B<chainidx>
+value of 0.
 
 If the B<parse_cb> considers the extension data acceptable it must return
 1. If it returns 0 or a negative value a fatal handshake error occurs
@@ -103,6 +164,88 @@ using the TLS alert value specified in B<*al>.
 The buffer B<in> is a temporary internal buffer which will not be valid after
 the callback returns.
 
+=head1 EXTENSION CONTEXTS
+
+An extension context defines which messages and under which conditions an
+extension should be added or expected. The context is built up by performing
+a bitwise OR of multiple pre-defined values together. The valid context values
+are:
+
+=over 4
+
+=item SSL_EXT_TLS_ONLY
+
+The extension is only allowed in TLS
+
+=item SSL_EXT_DTLS_ONLY
+
+The extension is only allowed in DTLS
+
+=item SSL_EXT_TLS_IMPLEMENTATION_ONLY
+
+The extension is allowed in DTLS, but there is only a TLS implementation
+available (so it is ignored in DTLS).
+
+=item SSL_EXT_SSL3_ALLOWED
+
+Extensions are not typically defined for SSLv3. Setting this value will allow
+the extension in SSLv3. Applications will not typically need to use this.
+
+=item SSL_EXT_TLS1_2_AND_BELOW_ONLY
+
+The extension is only defined for TLSv1.2/DTLSv1.2 and below. Servers will
+ignore this extension if it is present in the ClientHello and TLSv1.3 is
+negotiated.
+
+=item SSL_EXT_TLS1_3_ONLY
+
+The extension is only defined for TLS1.3 and above. Servers will ignore this
+extension if it is present in the ClientHello and TLSv1.2 or below is
+negotiated.
+
+=item SSL_EXT_IGNORE_ON_RESUMPTION
+
+The extension will be ignored during parsing if a previous session is being
+successfully resumed.
+
+=item SSL_EXT_CLIENT_HELLO
+
+The extension may be present in the ClientHello message.
+
+=item SSL_EXT_TLS1_2_SERVER_HELLO
+
+The extension may be present in a TLSv1.2 or below compatible ServerHello
+message.
+
+=item SSL_EXT_TLS1_3_SERVER_HELLO
+
+The extension may be present in a TLSv1.3 compatible ServerHello message.
+
+=item SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS
+
+The extension may be present in an EncryptedExtensions message.
+
+=item SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST
+
+The extension may be present in a HelloRetryRequest message.
+
+=item SSL_EXT_TLS1_3_CERTIFICATE
+
+The extension may be present in a TLSv1.3 compatible Certificate message.
+
+=item SSL_EXT_TLS1_3_NEW_SESSION_TICKET
+
+The extension may be present in a TLSv1.3 compatible NewSessionTicket message.
+
+=item SSL_EXT_TLS1_3_CERTIFICATE_REQUEST
+
+The extension may be present in a TLSv1.3 compatible CertificateRequest message.
+
+=back
+
+The context must include at least one message value (otherwise the extension
+will never be used).
+
 =head1 NOTES
 
 The B<add_arg> and B<parse_arg> parameters can be set to arbitrary values
@@ -110,32 +253,35 @@ which will be passed to the corresponding callbacks. They can, for example,
 be used to store the extension data received in a convenient structure or
 pass the extension data to be added or freed when adding extensions.
 
-The B<ext_type> parameter corresponds to the B<extension_type> field of
-RFC5246 et al. It is B<not> a NID.
-
 If the same custom extension type is received multiple times a fatal
 B<decode_error> alert is sent and the handshake aborts. If a custom extension
-is received in ServerHello which was not sent in ClientHello a fatal
-B<unsupported_extension> alert is sent and the handshake is aborted. The
-ServerHello B<add_cb> callback is only called if the corresponding extension
-was received in ClientHello. This is compliant with the TLS specifications.
-This behaviour ensures that each callback is called at most once and that
-an application can never send unsolicited extensions.
+is received in a ServerHello/EncryptedExtensions message which was not sent in
+the ClientHello a fatal B<unsupported_extension> alert is sent and the
+handshake is aborted. The ServerHello/EncryptedExtensions B<add_cb> callback is
+only called if the corresponding extension was received in the ClientHello. This
+is compliant with the TLS specifications. This behaviour ensures that each
+callback is called at most once and that an application can never send
+unsolicited extensions.
 
 =head1 RETURN VALUES
 
-SSL_CTX_add_client_custom_ext() and SSL_CTX_add_server_custom_ext() return 1 for
-success and 0 for failure. A failure can occur if an attempt is made to
-add the same B<ext_type> more than once, if an attempt is made to use an
-extension type handled internally by OpenSSL or if an internal error occurs
-(for example a memory allocation failure).
+SSL_CTX_add_custom_ext(), SSL_CTX_add_client_custom_ext() and
+SSL_CTX_add_server_custom_ext() return 1 for success and 0 for failure. A
+failure can occur if an attempt is made to add the same B<ext_type> more than
+once, if an attempt is made to use an extension type handled internally by
+OpenSSL or if an internal error occurs (for example a memory allocation
+failure).
 
 SSL_extension_supported() returns 1 if the extension B<ext_type> is handled
 internally by OpenSSL and 0 otherwise.
 
+=head1 HISTORY
+
+The function SSL_CTX_add_custom_ext() was added in OpenSSL version 1.1.1.
+
 =head1 COPYRIGHT
 
-Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2014-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 5ebd997..6e3b9c5 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -250,6 +250,31 @@ typedef int (*tls_session_secret_cb_fn) (SSL *s, void *secret,
                                          STACK_OF(SSL_CIPHER) *peer_ciphers,
                                          const SSL_CIPHER **cipher, void *arg);
 
+/* Extension context codes */
+/* This extension is only allowed in TLS */
+#define SSL_EXT_TLS_ONLY                        0x0001
+/* This extension is only allowed in DTLS */
+#define SSL_EXT_DTLS_ONLY                       0x0002
+/* Some extensions may be allowed in DTLS but we don't implement them for it */
+#define SSL_EXT_TLS_IMPLEMENTATION_ONLY         0x0004
+/* Most extensions are not defined for SSLv3 but EXT_TYPE_renegotiate is */
+#define SSL_EXT_SSL3_ALLOWED                    0x0008
+/* Extension is only defined for TLS1.2 and below */
+#define SSL_EXT_TLS1_2_AND_BELOW_ONLY           0x0010
+/* Extension is only defined for TLS1.3 and above */
+#define SSL_EXT_TLS1_3_ONLY                     0x0020
+/* Ignore this extension during parsing if we are resuming */
+#define SSL_EXT_IGNORE_ON_RESUMPTION            0x0040
+#define SSL_EXT_CLIENT_HELLO                    0x0080
+/* Really means TLS1.2 or below */
+#define SSL_EXT_TLS1_2_SERVER_HELLO             0x0100
+#define SSL_EXT_TLS1_3_SERVER_HELLO             0x0200
+#define SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS     0x0400
+#define SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST      0x0800
+#define SSL_EXT_TLS1_3_CERTIFICATE              0x1000
+#define SSL_EXT_TLS1_3_NEW_SESSION_TICKET       0x2000
+#define SSL_EXT_TLS1_3_CERTIFICATE_REQUEST      0x4000
+
 /* Typedefs for handling custom extensions */
 
 typedef int (*custom_ext_add_cb) (SSL *s, unsigned int ext_type,
@@ -263,6 +288,26 @@ typedef int (*custom_ext_parse_cb) (SSL *s, unsigned int ext_type,
                                     const unsigned char *in,
                                     size_t inlen, int *al, void *parse_arg);
 
+
+typedef int (*SSL_custom_ext_add_cb_ex) (SSL *s, unsigned int ext_type,
+                                         unsigned int context,
+                                         const unsigned char **out,
+                                         size_t *outlen, X509 *x,
+                                         size_t chainidx,
+                                         int *al, void *add_arg);
+
+typedef void (*SSL_custom_ext_free_cb_ex) (SSL *s, unsigned int ext_type,
+                                           unsigned int context,
+                                           const unsigned char *out,
+                                           void *add_arg);
+
+typedef int (*SSL_custom_ext_parse_cb_ex) (SSL *s, unsigned int ext_type,
+                                           unsigned int context,
+                                           const unsigned char *in,
+                                           size_t inlen, X509 *x,
+                                           size_t chainidx,
+                                           int *al, void *parse_arg);
+
 /* Typedef for verification callback */
 typedef int (*SSL_verify_cb)(int preverify_ok, X509_STORE_CTX *x509_ctx);
 
@@ -756,6 +801,14 @@ __owur int SSL_CTX_add_server_custom_ext(SSL_CTX *ctx, unsigned int ext_type,
                                   custom_ext_parse_cb parse_cb,
                                   void *parse_arg);
 
+__owur int SSL_CTX_add_custom_ext(SSL_CTX *ctx, unsigned int ext_type,
+                                  unsigned int context,
+                                  SSL_custom_ext_add_cb_ex add_cb,
+                                  SSL_custom_ext_free_cb_ex free_cb,
+                                  void *add_arg,
+                                  SSL_custom_ext_parse_cb_ex parse_cb,
+                                  void *parse_arg);
+
 __owur int SSL_extension_supported(unsigned int ext_type);
 
 # define SSL_NOTHING            1
diff --git a/ssl/build.info b/ssl/build.info
index f13c11f..bb2f1de 100644
--- a/ssl/build.info
+++ b/ssl/build.info
@@ -3,8 +3,8 @@ SOURCE[../libssl]=\
         pqueue.c packet.c \
         statem/statem_srvr.c statem/statem_clnt.c  s3_lib.c  s3_enc.c record/rec_layer_s3.c \
         statem/statem_lib.c statem/extensions.c statem/extensions_srvr.c \
-        statem/extensions_clnt.c s3_cbc.c s3_msg.c \
-        methods.c   t1_lib.c  t1_enc.c tls13_enc.c t1_ext.c \
+        statem/extensions_clnt.c statem/extensions_cust.c s3_cbc.c s3_msg.c \
+        methods.c   t1_lib.c  t1_enc.c tls13_enc.c \
         d1_lib.c  record/rec_layer_d1.c d1_msg.c \
         statem/statem_dtls.c d1_srtp.c \
         ssl_lib.c ssl_cert.c ssl_sess.c \
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index a4e7977..3a85ede 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -190,9 +190,7 @@ CERT *ssl_cert_dup(CERT *cert)
     ret->sec_level = cert->sec_level;
     ret->sec_ex = cert->sec_ex;
 
-    if (!custom_exts_copy(&ret->cli_ext, &cert->cli_ext))
-        goto err;
-    if (!custom_exts_copy(&ret->srv_ext, &cert->srv_ext))
+    if (!custom_exts_copy(&ret->custext, &cert->custext))
         goto err;
 #ifndef OPENSSL_NO_PSK
     if (cert->psk_identity_hint) {
@@ -254,8 +252,7 @@ void ssl_cert_free(CERT *c)
     OPENSSL_free(c->ctype);
     X509_STORE_free(c->verify_store);
     X509_STORE_free(c->chain_store);
-    custom_exts_free(&c->cli_ext);
-    custom_exts_free(&c->srv_ext);
+    custom_exts_free(&c->custext);
 #ifndef OPENSSL_NO_PSK
     OPENSSL_free(c->psk_identity_hint);
 #endif
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index f532931..5ca7237 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -1616,17 +1616,27 @@ struct cert_pkey_st {
 # define SSL_CERT_FLAGS_CHECK_TLS_STRICT \
         (SSL_CERT_FLAG_SUITEB_128_LOS|SSL_CERT_FLAG_TLS_STRICT)
 
+typedef enum {
+    ENDPOINT_CLIENT = 0,
+    ENDPOINT_SERVER,
+    ENDPOINT_BOTH
+} ENDPOINT;
+
+
 typedef struct {
     unsigned short ext_type;
+    ENDPOINT role;
+    /* The context which this extension applies to */
+    unsigned int context;
     /*
      * Per-connection flags relating to this extension type: not used if
      * part of an SSL_CTX structure.
      */
     uint32_t ext_flags;
-    custom_ext_add_cb add_cb;
-    custom_ext_free_cb free_cb;
+    SSL_custom_ext_add_cb_ex add_cb;
+    SSL_custom_ext_free_cb_ex free_cb;
     void *add_arg;
-    custom_ext_parse_cb parse_cb;
+    SSL_custom_ext_parse_cb_ex parse_cb;
     void *parse_arg;
 } custom_ext_method;
 
@@ -1706,9 +1716,8 @@ typedef struct cert_st {
      */
     X509_STORE *chain_store;
     X509_STORE *verify_store;
-    /* Custom extension methods for server and client */
-    custom_ext_methods cli_ext;
-    custom_ext_methods srv_ext;
+    /* Custom extensions */
+    custom_ext_methods custext;
     /* Security callback */
     int (*sec_cb) (const SSL *s, const SSL_CTX *ctx, int op, int bits, int nid,
                    void *other, void *ex);
@@ -2436,15 +2445,19 @@ __owur int srp_generate_server_master_secret(SSL *s);
 __owur int srp_generate_client_master_secret(SSL *s);
 __owur int srp_verify_server_param(SSL *s, int *al);
 
-/* t1_ext.c */
+/* statem/extensions_cust.c */
+
+custom_ext_method *custom_ext_find(const custom_ext_methods *exts,
+                                   ENDPOINT role, unsigned int ext_type,
+                                   size_t *idx);
 
 void custom_ext_init(custom_ext_methods *meths);
 
-__owur int custom_ext_parse(SSL *s, int server,
-                            unsigned int ext_type,
+__owur int custom_ext_parse(SSL *s, unsigned int context, unsigned int ext_type,
                             const unsigned char *ext_data, size_t ext_size,
-                            int *al);
-__owur int custom_ext_add(SSL *s, int server, WPACKET *pkt, int *al);
+                            X509 *x, size_t chainidx, int *al);
+__owur int custom_ext_add(SSL *s, int context, WPACKET *pkt, X509 *x,
+                          size_t chainidx, int maxversion, int *al);
 
 __owur int custom_exts_copy(custom_ext_methods *dst,
                             const custom_ext_methods *src);
diff --git a/ssl/ssl_rsa.c b/ssl/ssl_rsa.c
index a94fb13..87be646 100644
--- a/ssl/ssl_rsa.c
+++ b/ssl/ssl_rsa.c
@@ -797,26 +797,16 @@ static int serverinfo_process_buffer(const unsigned char *serverinfo,
 
         /* Register callbacks for extensions */
         ext_type = (serverinfo[0] << 8) + serverinfo[1];
-        if (ctx) {
-            int have_ext_cbs = 0;
-            size_t i;
-            custom_ext_methods *exts = &ctx->cert->srv_ext;
-            custom_ext_method *meth = exts->meths;
-
-            for (i = 0; i < exts->meths_count; i++, meth++) {
-                if (ext_type == meth->ext_type) {
-                    have_ext_cbs = 1;
-                    break;
-                }
-            }
-
-            if (!have_ext_cbs && !SSL_CTX_add_server_custom_ext(ctx, ext_type,
-                                                                serverinfo_srv_add_cb,
-                                                                NULL, NULL,
-                                                                serverinfo_srv_parse_cb,
-                                                                NULL))
-                return 0;
-        }
+        if (ctx != NULL
+                && custom_ext_find(&ctx->cert->custext, ENDPOINT_SERVER,
+                                   ext_type, NULL)
+                   == NULL
+                && !SSL_CTX_add_server_custom_ext(ctx, ext_type,
+                                                  serverinfo_srv_add_cb,
+                                                  NULL, NULL,
+                                                  serverinfo_srv_parse_cb,
+                                                  NULL))
+            return 0;
 
         serverinfo += 2;
         serverinfo_length -= 2;
diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c
index ab19497..d1a4014 100644
--- a/ssl/ssl_sess.c
+++ b/ssl/ssl_sess.c
@@ -468,9 +468,10 @@ int ssl_get_prev_session(SSL *s, CLIENTHELLO_MSG *hello, int *al)
     TICKET_RETURN r;
 
     if (SSL_IS_TLS13(s)) {
-        if (!tls_parse_extension(s, TLSEXT_IDX_psk_kex_modes, EXT_CLIENT_HELLO,
-                                 hello->pre_proc_exts, NULL, 0, al)
-                || !tls_parse_extension(s, TLSEXT_IDX_psk, EXT_CLIENT_HELLO,
+        if (!tls_parse_extension(s, TLSEXT_IDX_psk_kex_modes,
+                                 SSL_EXT_CLIENT_HELLO, hello->pre_proc_exts,
+                                 NULL, 0, al)
+                || !tls_parse_extension(s, TLSEXT_IDX_psk, SSL_EXT_CLIENT_HELLO,
                                         hello->pre_proc_exts, NULL, 0, al))
             return -1;
 
diff --git a/ssl/statem/extensions.c b/ssl/statem/extensions.c
index 96c5394..cd1d0bd 100644
--- a/ssl/statem/extensions.c
+++ b/ssl/statem/extensions.c
@@ -114,16 +114,16 @@ typedef struct extensions_definition_st {
 static const EXTENSION_DEFINITION ext_defs[] = {
     {
         TLSEXT_TYPE_renegotiate,
-        EXT_CLIENT_HELLO | EXT_TLS1_2_SERVER_HELLO | EXT_SSL3_ALLOWED
-        | EXT_TLS1_2_AND_BELOW_ONLY,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
+        | SSL_EXT_SSL3_ALLOWED | SSL_EXT_TLS1_2_AND_BELOW_ONLY,
         NULL, tls_parse_ctos_renegotiate, tls_parse_stoc_renegotiate,
         tls_construct_stoc_renegotiate, tls_construct_ctos_renegotiate,
         final_renegotiate
     },
     {
         TLSEXT_TYPE_server_name,
-        EXT_CLIENT_HELLO | EXT_TLS1_2_SERVER_HELLO
-        | EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
+        | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
         init_server_name,
         tls_parse_ctos_server_name, tls_parse_stoc_server_name,
         tls_construct_stoc_server_name, tls_construct_ctos_server_name,
@@ -132,7 +132,7 @@ static const EXTENSION_DEFINITION ext_defs[] = {
 #ifndef OPENSSL_NO_SRP
     {
         TLSEXT_TYPE_srp,
-        EXT_CLIENT_HELLO | EXT_TLS1_2_AND_BELOW_ONLY,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_AND_BELOW_ONLY,
         init_srp, tls_parse_ctos_srp, NULL, NULL, tls_construct_ctos_srp, NULL
     },
 #else
@@ -141,14 +141,15 @@ static const EXTENSION_DEFINITION ext_defs[] = {
 #ifndef OPENSSL_NO_EC
     {
         TLSEXT_TYPE_ec_point_formats,
-        EXT_CLIENT_HELLO | EXT_TLS1_2_SERVER_HELLO | EXT_TLS1_2_AND_BELOW_ONLY,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
+        | SSL_EXT_TLS1_2_AND_BELOW_ONLY,
         NULL, tls_parse_ctos_ec_pt_formats, tls_parse_stoc_ec_pt_formats,
         tls_construct_stoc_ec_pt_formats, tls_construct_ctos_ec_pt_formats,
         final_ec_pt_formats
     },
     {
         TLSEXT_TYPE_supported_groups,
-        EXT_CLIENT_HELLO | EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
         NULL, tls_parse_ctos_supported_groups, NULL,
         NULL /* TODO(TLS1.3): Need to add this */,
         tls_construct_ctos_supported_groups, NULL
@@ -159,14 +160,15 @@ static const EXTENSION_DEFINITION ext_defs[] = {
 #endif
     {
         TLSEXT_TYPE_session_ticket,
-        EXT_CLIENT_HELLO | EXT_TLS1_2_SERVER_HELLO | EXT_TLS1_2_AND_BELOW_ONLY,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
+        | SSL_EXT_TLS1_2_AND_BELOW_ONLY,
         init_session_ticket, tls_parse_ctos_session_ticket,
         tls_parse_stoc_session_ticket, tls_construct_stoc_session_ticket,
         tls_construct_ctos_session_ticket, NULL
     },
     {
         TLSEXT_TYPE_signature_algorithms,
-        EXT_CLIENT_HELLO | EXT_TLS1_3_CERTIFICATE_REQUEST,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_CERTIFICATE_REQUEST,
         init_sig_algs, tls_parse_ctos_sig_algs,
         tls_parse_ctos_sig_algs, tls_construct_ctos_sig_algs,
         tls_construct_ctos_sig_algs, final_sig_algs
@@ -174,8 +176,8 @@ static const EXTENSION_DEFINITION ext_defs[] = {
 #ifndef OPENSSL_NO_OCSP
     {
         TLSEXT_TYPE_status_request,
-        EXT_CLIENT_HELLO | EXT_TLS1_2_SERVER_HELLO
-        | EXT_TLS1_3_CERTIFICATE,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
+        | SSL_EXT_TLS1_3_CERTIFICATE,
         init_status_request, tls_parse_ctos_status_request,
         tls_parse_stoc_status_request, tls_construct_stoc_status_request,
         tls_construct_ctos_status_request, NULL
@@ -186,7 +188,8 @@ static const EXTENSION_DEFINITION ext_defs[] = {
 #ifndef OPENSSL_NO_NEXTPROTONEG
     {
         TLSEXT_TYPE_next_proto_neg,
-        EXT_CLIENT_HELLO | EXT_TLS1_2_SERVER_HELLO | EXT_TLS1_2_AND_BELOW_ONLY,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
+        | SSL_EXT_TLS1_2_AND_BELOW_ONLY,
         init_npn, tls_parse_ctos_npn, tls_parse_stoc_npn,
         tls_construct_stoc_next_proto_neg, tls_construct_ctos_npn, NULL
     },
@@ -199,16 +202,16 @@ static const EXTENSION_DEFINITION ext_defs[] = {
          * happens after server_name callbacks
          */
         TLSEXT_TYPE_application_layer_protocol_negotiation,
-        EXT_CLIENT_HELLO | EXT_TLS1_2_SERVER_HELLO
-        | EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
+        | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
         init_alpn, tls_parse_ctos_alpn, tls_parse_stoc_alpn,
         tls_construct_stoc_alpn, tls_construct_ctos_alpn, final_alpn
     },
 #ifndef OPENSSL_NO_SRTP
     {
         TLSEXT_TYPE_use_srtp,
-        EXT_CLIENT_HELLO | EXT_TLS1_2_SERVER_HELLO
-        | EXT_TLS1_3_ENCRYPTED_EXTENSIONS | EXT_DTLS_ONLY,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
+        | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS | SSL_EXT_DTLS_ONLY,
         init_srtp, tls_parse_ctos_use_srtp, tls_parse_stoc_use_srtp,
         tls_construct_stoc_use_srtp, tls_construct_ctos_use_srtp, NULL
     },
@@ -217,15 +220,16 @@ static const EXTENSION_DEFINITION ext_defs[] = {
 #endif
     {
         TLSEXT_TYPE_encrypt_then_mac,
-        EXT_CLIENT_HELLO | EXT_TLS1_2_SERVER_HELLO | EXT_TLS1_2_AND_BELOW_ONLY | EXT_SSL3_ALLOWED,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
+        | SSL_EXT_TLS1_2_AND_BELOW_ONLY,
         init_etm, tls_parse_ctos_etm, tls_parse_stoc_etm,
         tls_construct_stoc_etm, tls_construct_ctos_etm, NULL
     },
 #ifndef OPENSSL_NO_CT
     {
         TLSEXT_TYPE_signed_certificate_timestamp,
-        EXT_CLIENT_HELLO | EXT_TLS1_2_SERVER_HELLO
-        | EXT_TLS1_3_CERTIFICATE,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
+        | SSL_EXT_TLS1_3_CERTIFICATE,
         NULL,
         /*
          * No server side support for this, but can be provided by a custom
@@ -239,20 +243,23 @@ static const EXTENSION_DEFINITION ext_defs[] = {
 #endif
     {
         TLSEXT_TYPE_extended_master_secret,
-        EXT_CLIENT_HELLO | EXT_TLS1_2_SERVER_HELLO | EXT_TLS1_2_AND_BELOW_ONLY,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
+        | SSL_EXT_TLS1_2_AND_BELOW_ONLY,
         init_ems, tls_parse_ctos_ems, tls_parse_stoc_ems,
         tls_construct_stoc_ems, tls_construct_ctos_ems, final_ems
     },
     {
         TLSEXT_TYPE_supported_versions,
-        EXT_CLIENT_HELLO | EXT_TLS_IMPLEMENTATION_ONLY | EXT_TLS1_3_ONLY,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS_IMPLEMENTATION_ONLY
+        | SSL_EXT_TLS1_3_ONLY,
         NULL,
         /* Processed inline as part of version selection */
         NULL, NULL, NULL, tls_construct_ctos_supported_versions, NULL
     },
     {
         TLSEXT_TYPE_psk_kex_modes,
-        EXT_CLIENT_HELLO | EXT_TLS_IMPLEMENTATION_ONLY | EXT_TLS1_3_ONLY,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS_IMPLEMENTATION_ONLY
+        | SSL_EXT_TLS1_3_ONLY,
         init_psk_kex_modes, tls_parse_ctos_psk_kex_modes, NULL, NULL,
         tls_construct_ctos_psk_kex_modes, NULL
     },
@@ -263,9 +270,9 @@ static const EXTENSION_DEFINITION ext_defs[] = {
          * been parsed before we do this one.
          */
         TLSEXT_TYPE_key_share,
-        EXT_CLIENT_HELLO | EXT_TLS1_3_SERVER_HELLO
-        | EXT_TLS1_3_HELLO_RETRY_REQUEST | EXT_TLS_IMPLEMENTATION_ONLY
-        | EXT_TLS1_3_ONLY,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_SERVER_HELLO
+        | SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST | SSL_EXT_TLS_IMPLEMENTATION_ONLY
+        | SSL_EXT_TLS1_3_ONLY,
         NULL, tls_parse_ctos_key_share, tls_parse_stoc_key_share,
         tls_construct_stoc_key_share, tls_construct_ctos_key_share,
         final_key_share
@@ -273,8 +280,8 @@ static const EXTENSION_DEFINITION ext_defs[] = {
 #endif
     {
         TLSEXT_TYPE_cookie,
-        EXT_CLIENT_HELLO | EXT_TLS1_3_HELLO_RETRY_REQUEST
-        | EXT_TLS_IMPLEMENTATION_ONLY | EXT_TLS1_3_ONLY,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST
+        | SSL_EXT_TLS_IMPLEMENTATION_ONLY | SSL_EXT_TLS1_3_ONLY,
         NULL, NULL, tls_parse_stoc_cookie, NULL, tls_construct_ctos_cookie,
         NULL
     },
@@ -284,20 +291,21 @@ static const EXTENSION_DEFINITION ext_defs[] = {
          * SSL_OP_CRYPTOPRO_TLSEXT_BUG is set
          */
         TLSEXT_TYPE_cryptopro_bug,
-        EXT_TLS1_2_SERVER_HELLO | EXT_TLS1_2_AND_BELOW_ONLY,
+        SSL_EXT_TLS1_2_SERVER_HELLO | SSL_EXT_TLS1_2_AND_BELOW_ONLY,
         NULL, NULL, NULL, tls_construct_stoc_cryptopro_bug, NULL, NULL
     },
     {
         TLSEXT_TYPE_early_data,
-        EXT_CLIENT_HELLO | EXT_TLS1_3_ENCRYPTED_EXTENSIONS
-        | EXT_TLS1_3_NEW_SESSION_TICKET,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS
+        | SSL_EXT_TLS1_3_NEW_SESSION_TICKET,
         NULL, tls_parse_ctos_early_data, tls_parse_stoc_early_data,
         tls_construct_stoc_early_data, tls_construct_ctos_early_data,
         final_early_data
     },
     {
         TLSEXT_TYPE_certificate_authorities,
-        EXT_CLIENT_HELLO | EXT_TLS1_3_CERTIFICATE_REQUEST | EXT_TLS1_3_ONLY,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_CERTIFICATE_REQUEST
+        | SSL_EXT_TLS1_3_ONLY,
         init_certificate_authorities,
         tls_parse_certificate_authorities, tls_parse_certificate_authorities,
         tls_construct_certificate_authorities,
@@ -306,7 +314,7 @@ static const EXTENSION_DEFINITION ext_defs[] = {
     {
         /* Must be immediately before pre_shared_key */
         TLSEXT_TYPE_padding,
-        EXT_CLIENT_HELLO,
+        SSL_EXT_CLIENT_HELLO,
         NULL,
         /* We send this, but don't read it */
         NULL, NULL, NULL, tls_construct_ctos_padding, NULL
@@ -314,13 +322,30 @@ static const EXTENSION_DEFINITION ext_defs[] = {
     {
         /* Required by the TLSv1.3 spec to always be the last extension */
         TLSEXT_TYPE_psk,
-        EXT_CLIENT_HELLO | EXT_TLS1_3_SERVER_HELLO | EXT_TLS_IMPLEMENTATION_ONLY
-        | EXT_TLS1_3_ONLY,
+        SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_SERVER_HELLO
+        | SSL_EXT_TLS_IMPLEMENTATION_ONLY | SSL_EXT_TLS1_3_ONLY,
         NULL, tls_parse_ctos_psk, tls_parse_stoc_psk, tls_construct_stoc_psk,
         tls_construct_ctos_psk, NULL
     }
 };
 
+/* Check whether an extension's context matches the current context */
+static int validate_context(SSL *s, unsigned int extctx, unsigned int thisctx)
+{
+    /* Check we're allowed to use this extension in this context */
+    if ((thisctx & extctx) == 0)
+        return 0;
+
+    if (SSL_IS_DTLS(s)) {
+        if ((extctx & SSL_EXT_TLS_ONLY) != 0)
+            return 0;
+    } else if ((extctx & SSL_EXT_DTLS_ONLY) != 0) {
+        return 0;
+    }
+
+    return 1;
+}
+
 /*
  * Verify whether we are allowed to use the extension |type| in the current
  * |context|. Returns 1 to indicate the extension is allowed or unknown or 0 to
@@ -337,38 +362,31 @@ static int verify_extension(SSL *s, unsigned int context, unsigned int type,
 
     for (i = 0, thisext = ext_defs; i < builtin_num; i++, thisext++) {
         if (type == thisext->type) {
-            /* Check we're allowed to use this extension in this context */
-            if ((context & thisext->context) == 0)
+            if (!validate_context(s, thisext->context, context))
                 return 0;
 
-            if (SSL_IS_DTLS(s)) {
-                if ((thisext->context & EXT_TLS_ONLY) != 0)
-                    return 0;
-            } else if ((thisext->context & EXT_DTLS_ONLY) != 0) {
-                    return 0;
-            }
-
             *found = &rawexlist[i];
             return 1;
         }
     }
 
-    if ((context & (EXT_CLIENT_HELLO | EXT_TLS1_2_SERVER_HELLO)) == 0) {
-        /*
-         * Custom extensions only apply to <=TLS1.2. This extension is unknown
-         * in this context - we allow it
-         */
-        *found = NULL;
-        return 1;
-    }
-
     /* Check the custom extensions */
     if (meths != NULL) {
-        for (i = builtin_num; i < builtin_num + meths->meths_count; i++) {
-            if (meths->meths[i - builtin_num].ext_type == type) {
-                *found = &rawexlist[i];
-                return 1;
-            }
+        size_t offset = 0;
+        ENDPOINT role = ENDPOINT_BOTH;
+        custom_ext_method *meth = NULL;
+
+        if ((context & SSL_EXT_CLIENT_HELLO) != 0)
+            role = ENDPOINT_SERVER;
+        else if ((context & SSL_EXT_TLS1_2_SERVER_HELLO) != 0)
+            role = ENDPOINT_CLIENT;
+
+        meth = custom_ext_find(meths, role, type, &offset);
+        if (meth != NULL) {
+            if (!validate_context(s, meth->context, context))
+                return 0;
+            *found = &rawexlist[offset + builtin_num];
+            return 1;
         }
     }
 
@@ -382,16 +400,16 @@ static int verify_extension(SSL *s, unsigned int context, unsigned int type,
  * the extension is relevant for the current context |thisctx| or not. Returns
  * 1 if the extension is relevant for this context, and 0 otherwise
  */
-static int extension_is_relevant(SSL *s, unsigned int extctx,
-                                 unsigned int thisctx)
+int extension_is_relevant(SSL *s, unsigned int extctx, unsigned int thisctx)
 {
     if ((SSL_IS_DTLS(s)
-                && (extctx & EXT_TLS_IMPLEMENTATION_ONLY) != 0)
+                && (extctx & SSL_EXT_TLS_IMPLEMENTATION_ONLY) != 0)
             || (s->version == SSL3_VERSION
-                    && (extctx & EXT_SSL3_ALLOWED) == 0)
+                    && (extctx & SSL_EXT_SSL3_ALLOWED) == 0)
             || (SSL_IS_TLS13(s)
-                && (extctx & EXT_TLS1_2_AND_BELOW_ONLY) != 0)
-            || (!SSL_IS_TLS13(s) && (extctx & EXT_TLS1_3_ONLY) != 0))
+                && (extctx & SSL_EXT_TLS1_2_AND_BELOW_ONLY) != 0)
+            || (!SSL_IS_TLS13(s) && (extctx & SSL_EXT_TLS1_3_ONLY) != 0)
+            || (s->hit && (extctx & SSL_EXT_IGNORE_ON_RESUMPTION) != 0))
         return 0;
 
     return 1;
@@ -419,7 +437,7 @@ int tls_collect_extensions(SSL *s, PACKET *packet, unsigned int context,
     PACKET extensions = *packet;
     size_t i = 0;
     size_t num_exts;
-    custom_ext_methods *exts = NULL;
+    custom_ext_methods *exts = &s->cert->custext;
     RAW_EXTENSION *raw_extensions = NULL;
     const EXTENSION_DEFINITION *thisexd;
 
@@ -429,12 +447,8 @@ int tls_collect_extensions(SSL *s, PACKET *packet, unsigned int context,
      * Initialise server side custom extensions. Client side is done during
      * construction of extensions for the ClientHello.
      */
-    if ((context & EXT_CLIENT_HELLO) != 0) {
-        exts = &s->cert->srv_ext;
-        custom_ext_init(&s->cert->srv_ext);
-    } else if ((context & EXT_TLS1_2_SERVER_HELLO) != 0) {
-        exts = &s->cert->cli_ext;
-    }
+    if ((context & SSL_EXT_CLIENT_HELLO) != 0)
+        custom_ext_init(&s->cert->custext);
 
     num_exts = OSSL_NELEM(ext_defs) + (exts != NULL ? exts->meths_count : 0);
     raw_extensions = OPENSSL_zalloc(num_exts * sizeof(*raw_extensions));
@@ -463,7 +477,7 @@ int tls_collect_extensions(SSL *s, PACKET *packet, unsigned int context,
         if (!verify_extension(s, context, type, exts, raw_extensions, &thisex)
                 || (thisex != NULL && thisex->present == 1)
                 || (type == TLSEXT_TYPE_psk
-                    && (context & EXT_CLIENT_HELLO) != 0
+                    && (context & SSL_EXT_CLIENT_HELLO) != 0
                     && PACKET_remaining(&extensions) != 0)) {
             SSLerr(SSL_F_TLS_COLLECT_EXTENSIONS, SSL_R_BAD_EXTENSION);
             *al = SSL_AD_ILLEGAL_PARAMETER;
@@ -552,21 +566,11 @@ int tls_parse_extension(SSL *s, TLSEXT_INDEX idx, int context,
          */
     }
 
-    /*
-     * This is a custom extension. We only allow this if it is a non
-     * resumed session on the server side.
-     *chain
-     * TODO(TLS1.3): We only allow old style <=TLS1.2 custom extensions.
-     * We're going to need a new mechanism for TLS1.3 to specify which
-     * messages to add the custom extensions to.
-     */
-    if ((!s->hit || !s->server)
-            && (context
-                & (EXT_CLIENT_HELLO | EXT_TLS1_2_SERVER_HELLO)) != 0
-            && custom_ext_parse(s, s->server, currext->type,
-                                PACKET_data(&currext->data),
-                                PACKET_remaining(&currext->data),
-                                al) <= 0)
+    /* Parse custom extensions */
+    if (custom_ext_parse(s, context, currext->type,
+                         PACKET_data(&currext->data),
+                         PACKET_remaining(&currext->data),
+                         x, chainidx, al) <= 0)
         return 0;
 
     return 1;
@@ -587,11 +591,7 @@ int tls_parse_all_extensions(SSL *s, int context, RAW_EXTENSION *exts, X509 *x,
     const EXTENSION_DEFINITION *thisexd;
 
     /* Calculate the number of extensions in the extensions list */
-    if ((context & EXT_CLIENT_HELLO) != 0) {
-        numexts += s->cert->srv_ext.meths_count;
-    } else if ((context & EXT_TLS1_2_SERVER_HELLO) != 0) {
-        numexts += s->cert->cli_ext.meths_count;
-    }
+    numexts += s->cert->custext.meths_count;
 
     /* Parse each extension in turn */
     for (i = 0; i < numexts; i++) {
@@ -613,6 +613,30 @@ int tls_parse_all_extensions(SSL *s, int context, RAW_EXTENSION *exts, X509 *x,
     return 1;
 }
 
+int should_add_extension(SSL *s, unsigned int extctx, unsigned int thisctx,
+                         int max_version)
+{
+    /* Skip if not relevant for our context */
+    if ((extctx & thisctx) == 0)
+        return 0;
+
+    /* Check if this extension is defined for our protocol. If not, skip */
+    if ((SSL_IS_DTLS(s) && (extctx & SSL_EXT_TLS_IMPLEMENTATION_ONLY) != 0)
+            || (s->version == SSL3_VERSION
+                    && (extctx & SSL_EXT_SSL3_ALLOWED) == 0)
+            || (SSL_IS_TLS13(s)
+                && (extctx & SSL_EXT_TLS1_2_AND_BELOW_ONLY) != 0)
+            || (!SSL_IS_TLS13(s)
+                && (extctx & SSL_EXT_TLS1_3_ONLY) != 0
+                && (thisctx & SSL_EXT_CLIENT_HELLO) == 0)
+            || ((extctx & SSL_EXT_TLS1_3_ONLY) != 0
+                && (thisctx & SSL_EXT_CLIENT_HELLO) != 0
+                && (SSL_IS_DTLS(s) || max_version < TLS1_3_VERSION)))
+        return 0;
+
+    return 1;
+}
+
 /*
  * Construct all the extensions relevant to the current |context| and write
  * them to |pkt|. If this is an extension for a Certificate in a Certificate
@@ -626,7 +650,7 @@ int tls_construct_extensions(SSL *s, WPACKET *pkt, unsigned int context,
                              X509 *x, size_t chainidx, int *al)
 {
     size_t i;
-    int addcustom = 0, min_version, max_version = 0, reason, tmpal;
+    int min_version, max_version = 0, reason, tmpal;
     const EXTENSION_DEFINITION *thisexd;
 
     /*
@@ -640,15 +664,16 @@ int tls_construct_extensions(SSL *s, WPACKET *pkt, unsigned int context,
                 * If extensions are of zero length then we don't even add the
                 * extensions length bytes to a ClientHello/ServerHello in SSLv3
                 */
-            || ((context & (EXT_CLIENT_HELLO | EXT_TLS1_2_SERVER_HELLO)) != 0
-               && s->version == SSL3_VERSION
-               && !WPACKET_set_flags(pkt,
+            || ((context &
+                 (SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO)) != 0
+                && s->version == SSL3_VERSION
+                && !WPACKET_set_flags(pkt,
                                      WPACKET_FLAGS_ABANDON_ON_ZERO_LENGTH))) {
         SSLerr(SSL_F_TLS_CONSTRUCT_EXTENSIONS, ERR_R_INTERNAL_ERROR);
         goto err;
     }
 
-    if ((context & EXT_CLIENT_HELLO) != 0) {
+    if ((context & SSL_EXT_CLIENT_HELLO) != 0) {
         reason = ssl_get_client_min_max_version(s, &min_version, &max_version);
         if (reason != 0) {
             SSLerr(SSL_F_TLS_CONSTRUCT_EXTENSIONS, reason);
@@ -657,22 +682,11 @@ int tls_construct_extensions(SSL *s, WPACKET *pkt, unsigned int context,
     }
 
     /* Add custom extensions first */
-    if ((context & EXT_CLIENT_HELLO) != 0) {
-        custom_ext_init(&s->cert->cli_ext);
-        addcustom = 1;
-    } else if ((context & EXT_TLS1_2_SERVER_HELLO) != 0) {
-        /*
-         * We already initialised the custom extensions during ClientHello
-         * parsing.
-         *
-         * TODO(TLS1.3): We're going to need a new custom extension mechanism
-         * for TLS1.3, so that custom extensions can specify which of the
-         * multiple message they wish to add themselves to.
-         */
-        addcustom = 1;
+    if ((context & SSL_EXT_CLIENT_HELLO) != 0) {
+        /* On the server side with initiase during ClientHello parsing */
+        custom_ext_init(&s->cert->custext);
     }
-
-    if (addcustom && !custom_ext_add(s, s->server, pkt, &tmpal)) {
+    if (!custom_ext_add(s, context, pkt, x, chainidx, max_version, &tmpal)) {
         SSLerr(SSL_F_TLS_CONSTRUCT_EXTENSIONS, ERR_R_INTERNAL_ERROR);
         goto err;
     }
@@ -682,28 +696,13 @@ int tls_construct_extensions(SSL *s, WPACKET *pkt, unsigned int context,
                          size_t chainidx, int *al);
 
         /* Skip if not relevant for our context */
-        if ((thisexd->context & context) == 0)
+        if (!should_add_extension(s, thisexd->context, context, max_version))
             continue;
 
         construct = s->server ? thisexd->construct_stoc
                               : thisexd->construct_ctos;
 
-        /* Check if this extension is defined for our protocol. If not, skip */
-        if ((SSL_IS_DTLS(s)
-                    && (thisexd->context & EXT_TLS_IMPLEMENTATION_ONLY)
-                       != 0)
-                || (s->version == SSL3_VERSION
-                        && (thisexd->context & EXT_SSL3_ALLOWED) == 0)
-                || (SSL_IS_TLS13(s)
-                    && (thisexd->context & EXT_TLS1_2_AND_BELOW_ONLY)
-                       != 0)
-                || (!SSL_IS_TLS13(s)
-                    && (thisexd->context & EXT_TLS1_3_ONLY) != 0
-                    && (context & EXT_CLIENT_HELLO) == 0)
-                || ((thisexd->context & EXT_TLS1_3_ONLY) != 0
-                    && (context & EXT_CLIENT_HELLO) != 0
-                    && (SSL_IS_DTLS(s) || max_version < TLS1_3_VERSION))
-                || construct == NULL)
+        if (construct == NULL)
             continue;
 
         if (!construct(s, pkt, context, x, chainidx, &tmpal))
diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c
index 8bb9a88..7d2a4b0 100644
--- a/ssl/statem/extensions_clnt.c
+++ b/ssl/statem/extensions_clnt.c
@@ -1092,8 +1092,10 @@ int tls_parse_stoc_sct(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
             }
         }
     } else {
-        if (custom_ext_parse(s, 0, TLSEXT_TYPE_signed_certificate_timestamp,
-                             PACKET_data(pkt), PACKET_remaining(pkt), al) <= 0)
+        if (custom_ext_parse(s, context,
+                             TLSEXT_TYPE_signed_certificate_timestamp,
+                             PACKET_data(pkt), PACKET_remaining(pkt),
+                             x, chainidx, al) <= 0)
             return 0;
     }
 
@@ -1307,7 +1309,7 @@ int tls_parse_stoc_key_share(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
         return 0;
     }
 
-    if ((context & EXT_TLS1_3_HELLO_RETRY_REQUEST) != 0) {
+    if ((context & SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST) != 0) {
         unsigned const char *pcurves = NULL;
         size_t i, num_curves;
 
@@ -1411,7 +1413,7 @@ int tls_parse_stoc_cookie(SSL *s, PACKET *pkt, unsigned int context, X509 *x,
 int tls_parse_stoc_early_data(SSL *s, PACKET *pkt, unsigned int context,
                               X509 *x, size_t chainidx, int *al)
 {
-    if (context == EXT_TLS1_3_NEW_SESSION_TICKET) {
+    if (context == SSL_EXT_TLS1_3_NEW_SESSION_TICKET) {
         unsigned long max_early_data;
 
         if (!PACKET_get_net_4(pkt, &max_early_data)
diff --git a/ssl/statem/extensions_cust.c b/ssl/statem/extensions_cust.c
new file mode 100644
index 0000000..3188ade
--- /dev/null
+++ b/ssl/statem/extensions_cust.c
@@ -0,0 +1,494 @@
+/*
+ * Copyright 2014-2016 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
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Custom extension utility functions */
+
+#include <assert.h>
+#include <openssl/ct.h>
+#include "../ssl_locl.h"
+#include "statem_locl.h"
+
+typedef struct {
+    void *add_arg;
+    custom_ext_add_cb add_cb;
+    custom_ext_free_cb free_cb;
+} custom_ext_add_cb_wrap;
+
+typedef struct {
+    void *parse_arg;
+    custom_ext_parse_cb parse_cb;
+} custom_ext_parse_cb_wrap;
+
+/*
+ * Provide thin wrapper callbacks which convert new style arguments to old style
+ */
+static int custom_ext_add_old_cb_wrap(SSL *s, unsigned int ext_type,
+                                      unsigned int context,
+                                      const unsigned char **out,
+                                      size_t *outlen, X509 *x, size_t chainidx,
+                                      int *al, void *add_arg)
+{
+    custom_ext_add_cb_wrap *add_cb_wrap = (custom_ext_add_cb_wrap *)add_arg;
+
+    if (add_cb_wrap->add_cb == NULL)
+        return 1;
+
+    return add_cb_wrap->add_cb(s, ext_type, out, outlen, al,
+                               add_cb_wrap->add_arg);
+}
+
+static void custom_ext_free_old_cb_wrap(SSL *s, unsigned int ext_type,
+                                        unsigned int context,
+                                        const unsigned char *out, void *add_arg)
+{
+    custom_ext_add_cb_wrap *add_cb_wrap = (custom_ext_add_cb_wrap *)add_arg;
+
+    if (add_cb_wrap->free_cb == NULL)
+        return;
+
+    add_cb_wrap->free_cb(s, ext_type, out, add_cb_wrap->add_arg);
+}
+
+static int custom_ext_parse_old_cb_wrap(SSL *s, unsigned int ext_type,
+                                        unsigned int context,
+                                        const unsigned char *in,
+                                        size_t inlen, X509 *x, size_t chainidx,
+                                        int *al, void *parse_arg)
+{
+    custom_ext_parse_cb_wrap *parse_cb_wrap =
+        (custom_ext_parse_cb_wrap *)parse_arg;
+
+    return parse_cb_wrap->parse_cb(s, ext_type, in, inlen, al,
+                                   parse_cb_wrap->parse_arg);
+}
+
+/*
+ * Find a custom extension from the list. The |role| param is there to
+ * support the legacy API where custom extensions for client and server could
+ * be set independently on the same SSL_CTX. It is set to ENDPOINT_SERVER if we
+ * are trying to find a method relevant to the server, ENDPOINT_CLIENT for the
+ * client, or ENDPOINT_BOTH for either
+ */
+custom_ext_method *custom_ext_find(const custom_ext_methods *exts,
+                                   ENDPOINT role, unsigned int ext_type,
+                                   size_t *idx)
+{
+    size_t i;
+    custom_ext_method *meth = exts->meths;
+
+    for (i = 0; i < exts->meths_count; i++, meth++) {
+        if (ext_type == meth->ext_type
+                && (role == ENDPOINT_BOTH || role == meth->role
+                    || meth->role == ENDPOINT_BOTH)) {
+            if (idx != NULL)
+                *idx = i;
+            return meth;
+        }
+    }
+    return NULL;
+}
+
+/*
+ * Initialise custom extensions flags to indicate neither sent nor received.
+ */
+void custom_ext_init(custom_ext_methods *exts)
+{
+    size_t i;
+    custom_ext_method *meth = exts->meths;
+
+    for (i = 0; i < exts->meths_count; i++, meth++)
+        meth->ext_flags = 0;
+}
+
+/* Pass received custom extension data to the application for parsing. */
+int custom_ext_parse(SSL *s, unsigned int context, unsigned int ext_type,
+                     const unsigned char *ext_data, size_t ext_size, X509 *x,
+                     size_t chainidx, int *al)
+{
+    custom_ext_methods *exts = &s->cert->custext;
+    custom_ext_method *meth;
+    ENDPOINT role = ENDPOINT_BOTH;
+
+    if ((context & (SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO)) != 0)
+        role = s->server ? ENDPOINT_SERVER : ENDPOINT_CLIENT;
+
+    meth = custom_ext_find(exts, role, ext_type, NULL);
+    /* If not found return success */
+    if (!meth)
+        return 1;
+
+    /* Check if extension is defined for our protocol. If not, skip */
+    if (!extension_is_relevant(s, meth->context, context))
+        return 1;
+
+    if ((context & (SSL_EXT_TLS1_2_SERVER_HELLO
+                    | SSL_EXT_TLS1_3_SERVER_HELLO
+                    | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS)) != 0) {
+        /*
+         * If it's ServerHello or EncryptedExtensions we can't have any
+         * extensions not sent in ClientHello.
+         */
+        if ((meth->ext_flags & SSL_EXT_FLAG_SENT) == 0) {
+            *al = TLS1_AD_UNSUPPORTED_EXTENSION;
+            return 0;
+        }
+    }
+
+    /*
+     * Extensions received in the ClientHello are marked with the
+     * SSL_EXT_FLAG_RECEIVED. This is so we know to add the equivalent
+     * extensions in the ServerHello/EncryptedExtensions message
+     */
+    if ((context & SSL_EXT_CLIENT_HELLO) != 0)
+        meth->ext_flags |= SSL_EXT_FLAG_RECEIVED;
+
+    /* If no parse function set return success */
+    if (!meth->parse_cb)
+        return 1;
+
+    return meth->parse_cb(s, ext_type, context, ext_data, ext_size, x, chainidx,
+                          al, meth->parse_arg);
+}
+
+/*
+ * Request custom extension data from the application and add to the return
+ * buffer.
+ */
+int custom_ext_add(SSL *s, int context, WPACKET *pkt, X509 *x, size_t chainidx,
+                   int maxversion, int *al)
+{
+    custom_ext_methods *exts = &s->cert->custext;
+    custom_ext_method *meth;
+    size_t i;
+
+    for (i = 0; i < exts->meths_count; i++) {
+        const unsigned char *out = NULL;
+        size_t outlen = 0;
+
+        meth = exts->meths + i;
+
+        if (!should_add_extension(s, meth->context, context, maxversion))
+            continue;
+
+        if ((context & (SSL_EXT_TLS1_2_SERVER_HELLO
+                        | SSL_EXT_TLS1_3_SERVER_HELLO
+                        | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS)) != 0) {
+            /*
+             * For ServerHello/EncryptedExtensions only send extensions present
+             * in ClientHello.
+             */
+            if (!(meth->ext_flags & SSL_EXT_FLAG_RECEIVED))
+                continue;
+        }
+        /*
+         * We skip it if the callback is absent - except for a ClientHello where
+         * we add an empty extension.
+         */
+        if ((context & SSL_EXT_CLIENT_HELLO) == 0 && meth->add_cb == NULL)
+            continue;
+
+        if (meth->add_cb != NULL) {
+            int cb_retval = meth->add_cb(s, meth->ext_type, context, &out,
+                                         &outlen, x, chainidx, al,
+                                         meth->add_arg);
+
+            if (cb_retval < 0)
+                return 0;       /* error */
+            if (cb_retval == 0)
+                continue;       /* skip this extension */
+        }
+
+        if (!WPACKET_put_bytes_u16(pkt, meth->ext_type)
+                || !WPACKET_start_sub_packet_u16(pkt)
+                || (outlen > 0 && !WPACKET_memcpy(pkt, out, outlen))
+                || !WPACKET_close(pkt)) {
+            *al = SSL_AD_INTERNAL_ERROR;
+            return 0;
+        }
+        if ((context & SSL_EXT_CLIENT_HELLO) != 0) {
+            /*
+             * We can't send duplicates: code logic should prevent this.
+             */
+            assert((meth->ext_flags & SSL_EXT_FLAG_SENT) == 0);
+            /*
+             * Indicate extension has been sent: this is both a sanity check to
+             * ensure we don't send duplicate extensions and indicates that it
+             * is not an error if the extension is present in ServerHello.
+             */
+            meth->ext_flags |= SSL_EXT_FLAG_SENT;
+        }
+        if (meth->free_cb != NULL)
+            meth->free_cb(s, meth->ext_type, context, out, meth->add_arg);
+    }
+    return 1;
+}
+
+/* Copy table of custom extensions */
+int custom_exts_copy(custom_ext_methods *dst, const custom_ext_methods *src)
+{
+    size_t i;
+    int err = 0;
+
+    if (src->meths_count > 0) {
+        dst->meths =
+            OPENSSL_memdup(src->meths,
+                           sizeof(*src->meths) * src->meths_count);
+        if (dst->meths == NULL)
+            return 0;
+        dst->meths_count = src->meths_count;
+
+        for (i = 0; i < src->meths_count; i++) {
+            custom_ext_method *methsrc = src->meths + i;
+            custom_ext_method *methdst = dst->meths + i;
+
+            if (methsrc->add_cb != custom_ext_add_old_cb_wrap)
+                continue;
+
+            /*
+             * We have found an old style API wrapper. We need to copy the
+             * arguments too.
+             */
+
+            if (err) {
+                methdst->add_arg = NULL;
+                methdst->parse_arg = NULL;
+                continue;
+            }
+
+            methdst->add_arg = OPENSSL_memdup(methsrc->add_arg,
+                                              sizeof(custom_ext_add_cb_wrap));
+            methdst->parse_arg = OPENSSL_memdup(methsrc->parse_arg,
+                                            sizeof(custom_ext_parse_cb_wrap));
+
+            if (methdst->add_arg == NULL || methdst->parse_arg == NULL)
+                err = 1;
+        }
+    }
+
+    if (err) {
+        custom_exts_free(dst);
+        return 0;
+    }
+
+    return 1;
+}
+
+void custom_exts_free(custom_ext_methods *exts)
+{
+    size_t i;
+    custom_ext_method *meth;
+
+    for (i = 0, meth = exts->meths; i < exts->meths_count; i++, meth++) {
+        if (meth->add_cb != custom_ext_add_old_cb_wrap)
+            continue;
+
+        /* Old style API wrapper. Need to free the arguments too */
+        OPENSSL_free(meth->add_arg);
+        OPENSSL_free(meth->parse_arg);
+    }
+    OPENSSL_free(exts->meths);
+}
+
+/* Return true if a client custom extension exists, false otherwise */
+int SSL_CTX_has_client_custom_ext(const SSL_CTX *ctx, unsigned int ext_type)
+{
+    return custom_ext_find(&ctx->cert->custext, ENDPOINT_CLIENT, ext_type,
+                           NULL) != NULL;
+}
+
+static int add_custom_ext_intern(SSL_CTX *ctx, ENDPOINT role,
+                                 unsigned int ext_type,
+                                 unsigned int context,
+                                 SSL_custom_ext_add_cb_ex add_cb,
+                                 SSL_custom_ext_free_cb_ex free_cb,
+                                 void *add_arg,
+                                 SSL_custom_ext_parse_cb_ex parse_cb,
+                                 void *parse_arg)
+{
+    custom_ext_methods *exts = &ctx->cert->custext;
+    custom_ext_method *meth, *tmp;
+
+    /*
+     * Check application error: if add_cb is not set free_cb will never be
+     * called.
+     */
+    if (add_cb == NULL && free_cb != NULL)
+        return 0;
+
+#ifndef OPENSSL_NO_CT
+    /*
+     * We don't want applications registering callbacks for SCT extensions
+     * whilst simultaneously using the built-in SCT validation features, as
+     * these two things may not play well together.
+     */
+    if (ext_type == TLSEXT_TYPE_signed_certificate_timestamp
+            && (context & SSL_EXT_CLIENT_HELLO) != 0
+            && SSL_CTX_ct_is_enabled(ctx))
+        return 0;
+#endif
+
+    /*
+     * Don't add if extension supported internally, but make exception
+     * for extension types that previously were not supported, but now are.
+     */
+    if (SSL_extension_supported(ext_type)
+            && ext_type != TLSEXT_TYPE_signed_certificate_timestamp)
+        return 0;
+
+    /* Extension type must fit in 16 bits */
+    if (ext_type > 0xffff)
+        return 0;
+    /* Search for duplicate */
+    if (custom_ext_find(exts, role, ext_type, NULL))
+        return 0;
+    tmp = OPENSSL_realloc(exts->meths,
+                          (exts->meths_count + 1) * sizeof(custom_ext_method));
+    if (tmp == NULL)
+        return 0;
+
+    exts->meths = tmp;
+    meth = exts->meths + exts->meths_count;
+    memset(meth, 0, sizeof(*meth));
+    meth->role = role;
+    meth->context = context;
+    meth->parse_cb = parse_cb;
+    meth->add_cb = add_cb;
+    meth->free_cb = free_cb;
+    meth->ext_type = ext_type;
+    meth->add_arg = add_arg;
+    meth->parse_arg = parse_arg;
+    exts->meths_count++;
+    return 1;
+}
+
+static int add_old_custom_ext(SSL_CTX *ctx, ENDPOINT role,
+                              unsigned int ext_type,
+                              unsigned int context,
+                              custom_ext_add_cb add_cb,
+                              custom_ext_free_cb free_cb,
+                              void *add_arg,
+                              custom_ext_parse_cb parse_cb, void *parse_arg)
+{
+    custom_ext_add_cb_wrap *add_cb_wrap
+        = OPENSSL_malloc(sizeof(*add_cb_wrap));
+    custom_ext_parse_cb_wrap *parse_cb_wrap
+        = OPENSSL_malloc(sizeof(*parse_cb_wrap));
+    int ret;
+
+    if (add_cb_wrap == NULL || parse_cb_wrap == NULL) {
+        OPENSSL_free(add_cb_wrap);
+        OPENSSL_free(parse_cb_wrap);
+        return 0;
+    }
+
+    add_cb_wrap->add_arg = add_arg;
+    add_cb_wrap->add_cb = add_cb;
+    add_cb_wrap->free_cb = free_cb;
+    parse_cb_wrap->parse_arg = parse_arg;
+    parse_cb_wrap->parse_cb = parse_cb;
+
+    ret = add_custom_ext_intern(ctx, role, ext_type,
+                                context,
+                                custom_ext_add_old_cb_wrap,
+                                custom_ext_free_old_cb_wrap,
+                                add_cb_wrap,
+                                custom_ext_parse_old_cb_wrap,
+                                parse_cb_wrap);
+
+    if (!ret) {
+        OPENSSL_free(add_cb_wrap);
+        OPENSSL_free(parse_cb_wrap);
+    }
+
+    return ret;
+}
+
+/* Application level functions to add the old custom extension callbacks */
+int SSL_CTX_add_client_custom_ext(SSL_CTX *ctx, unsigned int ext_type,
+                                  custom_ext_add_cb add_cb,
+                                  custom_ext_free_cb free_cb,
+                                  void *add_arg,
+                                  custom_ext_parse_cb parse_cb, void *parse_arg)
+{
+    return add_old_custom_ext(ctx, ENDPOINT_CLIENT, ext_type,
+                              SSL_EXT_TLS1_2_AND_BELOW_ONLY
+                              | SSL_EXT_CLIENT_HELLO
+                              | SSL_EXT_TLS1_2_SERVER_HELLO
+                              | SSL_EXT_IGNORE_ON_RESUMPTION,
+                              add_cb, free_cb, add_arg, parse_cb, parse_arg);
+}
+
+int SSL_CTX_add_server_custom_ext(SSL_CTX *ctx, unsigned int ext_type,
+                                  custom_ext_add_cb add_cb,
+                                  custom_ext_free_cb free_cb,
+                                  void *add_arg,
+                                  custom_ext_parse_cb parse_cb, void *parse_arg)
+{
+    return add_old_custom_ext(ctx, ENDPOINT_SERVER, ext_type,
+                              SSL_EXT_TLS1_2_AND_BELOW_ONLY
+                              | SSL_EXT_CLIENT_HELLO
+                              | SSL_EXT_TLS1_2_SERVER_HELLO
+                              | SSL_EXT_IGNORE_ON_RESUMPTION,
+                              add_cb, free_cb, add_arg, parse_cb, parse_arg);
+}
+
+int SSL_CTX_add_custom_ext(SSL_CTX *ctx, unsigned int ext_type,
+                           unsigned int context,
+                           SSL_custom_ext_add_cb_ex add_cb,
+                           SSL_custom_ext_free_cb_ex free_cb,
+                           void *add_arg,
+                           SSL_custom_ext_parse_cb_ex parse_cb, void *parse_arg)
+{
+    return add_custom_ext_intern(ctx, ENDPOINT_BOTH, ext_type, context, add_cb,
+                                 free_cb, add_arg, parse_cb, parse_arg);
+}
+
+int SSL_extension_supported(unsigned int ext_type)
+{
+    switch (ext_type) {
+        /* Internally supported extensions. */
+    case TLSEXT_TYPE_application_layer_protocol_negotiation:
+#ifndef OPENSSL_NO_EC
+    case TLSEXT_TYPE_ec_point_formats:
+    case TLSEXT_TYPE_supported_groups:
+    case TLSEXT_TYPE_key_share:
+#endif
+#ifndef OPENSSL_NO_NEXTPROTONEG
+    case TLSEXT_TYPE_next_proto_neg:
+#endif
+    case TLSEXT_TYPE_padding:
+    case TLSEXT_TYPE_renegotiate:
+    case TLSEXT_TYPE_server_name:
+    case TLSEXT_TYPE_session_ticket:
+    case TLSEXT_TYPE_signature_algorithms:
+#ifndef OPENSSL_NO_SRP
+    case TLSEXT_TYPE_srp:
+#endif
+#ifndef OPENSSL_NO_OCSP
+    case TLSEXT_TYPE_status_request:
+#endif
+#ifndef OPENSSL_NO_CT
+    case TLSEXT_TYPE_signed_certificate_timestamp:
+#endif
+#ifndef OPENSSL_NO_SRTP
+    case TLSEXT_TYPE_use_srtp:
+#endif
+    case TLSEXT_TYPE_encrypt_then_mac:
+    case TLSEXT_TYPE_supported_versions:
+    case TLSEXT_TYPE_extended_master_secret:
+    case TLSEXT_TYPE_psk_kex_modes:
+    case TLSEXT_TYPE_cookie:
+    case TLSEXT_TYPE_early_data:
+    case TLSEXT_TYPE_certificate_authorities:
+    case TLSEXT_TYPE_psk:
+        return 1;
+    default:
+        return 0;
+    }
+}
diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c
index 076a635..da7e8c8 100644
--- a/ssl/statem/extensions_srvr.c
+++ b/ssl/statem/extensions_srvr.c
@@ -1133,7 +1133,7 @@ int tls_construct_stoc_cryptopro_bug(SSL *s, WPACKET *pkt, unsigned int context,
 int tls_construct_stoc_early_data(SSL *s, WPACKET *pkt, unsigned int context,
                                   X509 *x, size_t chainidx, int *al)
 {
-    if (context == EXT_TLS1_3_NEW_SESSION_TICKET) {
+    if (context == SSL_EXT_TLS1_3_NEW_SESSION_TICKET) {
         if (s->max_early_data == 0)
             return 1;
 
diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c
index d4f8e0a..8207dde 100644
--- a/ssl/statem/statem_clnt.c
+++ b/ssl/statem/statem_clnt.c
@@ -1200,7 +1200,7 @@ int tls_construct_client_hello(SSL *s, WPACKET *pkt)
     }
 
     /* TLS extensions */
-    if (!tls_construct_extensions(s, pkt, EXT_CLIENT_HELLO, NULL, 0, &al)) {
+    if (!tls_construct_extensions(s, pkt, SSL_EXT_CLIENT_HELLO, NULL, 0, &al)) {
         ssl3_send_alert(s, SSL3_AL_FATAL, al);
         SSLerr(SSL_F_TLS_CONSTRUCT_CLIENT_HELLO, ERR_R_INTERNAL_ERROR);
         return 0;
@@ -1390,8 +1390,8 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
         goto f_err;
     }
 
-    context = SSL_IS_TLS13(s) ? EXT_TLS1_3_SERVER_HELLO
-                              : EXT_TLS1_2_SERVER_HELLO;
+    context = SSL_IS_TLS13(s) ? SSL_EXT_TLS1_3_SERVER_HELLO
+                              : SSL_EXT_TLS1_2_SERVER_HELLO;
     if (!tls_collect_extensions(s, &extpkt, context, &extensions, &al, NULL))
         goto f_err;
 
@@ -1400,7 +1400,7 @@ MSG_PROCESS_RETURN tls_process_server_hello(SSL *s, PACKET *pkt)
     if (SSL_IS_TLS13(s)) {
         /* This will set s->hit if we are resuming */
         if (!tls_parse_extension(s, TLSEXT_IDX_psk,
-                                 EXT_TLS1_3_SERVER_HELLO,
+                                 SSL_EXT_TLS1_3_SERVER_HELLO,
                                  extensions, NULL, 0, &al))
             goto f_err;
     } else {
@@ -1634,9 +1634,9 @@ static MSG_PROCESS_RETURN tls_process_hello_retry_request(SSL *s, PACKET *pkt)
         goto f_err;
     }
 
-    if (!tls_collect_extensions(s, &extpkt, EXT_TLS1_3_HELLO_RETRY_REQUEST,
+    if (!tls_collect_extensions(s, &extpkt, SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST,
                                 &extensions, &al, NULL)
-            || !tls_parse_all_extensions(s, EXT_TLS1_3_HELLO_RETRY_REQUEST,
+            || !tls_parse_all_extensions(s, SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST,
                                          extensions, NULL, 0, &al))
         goto f_err;
 
@@ -1728,9 +1728,10 @@ MSG_PROCESS_RETURN tls_process_server_certificate(SSL *s, PACKET *pkt)
                 SSLerr(SSL_F_TLS_PROCESS_SERVER_CERTIFICATE, SSL_R_BAD_LENGTH);
                 goto f_err;
             }
-            if (!tls_collect_extensions(s, &extensions, EXT_TLS1_3_CERTIFICATE,
-                                        &rawexts, &al, NULL)
-                    || !tls_parse_all_extensions(s, EXT_TLS1_3_CERTIFICATE,
+            if (!tls_collect_extensions(s, &extensions,
+                                        SSL_EXT_TLS1_3_CERTIFICATE, &rawexts,
+                                        &al, NULL)
+                    || !tls_parse_all_extensions(s, SSL_EXT_TLS1_3_CERTIFICATE,
                                                  rawexts, x, chainidx, &al)) {
                 OPENSSL_free(rawexts);
                 goto f_err;
@@ -2357,9 +2358,9 @@ MSG_PROCESS_RETURN tls_process_certificate_request(SSL *s, PACKET *pkt)
                 goto err;
         }
         if (!tls_collect_extensions(s, &extensions,
-                                    EXT_TLS1_3_CERTIFICATE_REQUEST,
+                                    SSL_EXT_TLS1_3_CERTIFICATE_REQUEST,
                                     &rawexts, &al, NULL)
-            || !tls_parse_all_extensions(s, EXT_TLS1_3_CERTIFICATE_REQUEST,
+            || !tls_parse_all_extensions(s, SSL_EXT_TLS1_3_CERTIFICATE_REQUEST,
                                          rawexts, NULL, 0, &al)) {
             OPENSSL_free(rawexts);
             goto err;
@@ -2511,9 +2512,10 @@ 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,
+                                           SSL_EXT_TLS1_3_NEW_SESSION_TICKET,
                                            &exts, &al, NULL)
-                || !tls_parse_all_extensions(s, EXT_TLS1_3_NEW_SESSION_TICKET,
+                || !tls_parse_all_extensions(s,
+                                             SSL_EXT_TLS1_3_NEW_SESSION_TICKET,
                                              exts, NULL, 0, &al)) {
             SSLerr(SSL_F_TLS_PROCESS_NEW_SESSION_TICKET, SSL_R_BAD_EXTENSION);
             goto f_err;
@@ -3479,9 +3481,10 @@ static MSG_PROCESS_RETURN tls_process_encrypted_extensions(SSL *s, PACKET *pkt)
         goto err;
     }
 
-    if (!tls_collect_extensions(s, &extensions, EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
-                                &rawexts, &al, NULL)
-            || !tls_parse_all_extensions(s, EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
+    if (!tls_collect_extensions(s, &extensions,
+                                SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS, &rawexts,
+                                &al, NULL)
+            || !tls_parse_all_extensions(s, SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
                                          rawexts, NULL, 0, &al))
         goto err;
 
diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c
index f292b82..d5e87f7 100644
--- a/ssl/statem/statem_lib.c
+++ b/ssl/statem/statem_lib.c
@@ -801,7 +801,7 @@ static int ssl_add_cert_to_wpacket(SSL *s, WPACKET *pkt, X509 *x, int chain,
     }
 
     if (SSL_IS_TLS13(s)
-            && !tls_construct_extensions(s, pkt, EXT_TLS1_3_CERTIFICATE, x,
+            && !tls_construct_extensions(s, pkt, SSL_EXT_TLS1_3_CERTIFICATE, x,
                                          chain, al))
         return 0;
 
diff --git a/ssl/statem/statem_locl.h b/ssl/statem/statem_locl.h
index 9bf1d8a..2352c6a 100644
--- a/ssl/statem/statem_locl.h
+++ b/ssl/statem/statem_locl.h
@@ -32,29 +32,6 @@
 /* The maximum number of incoming KeyUpdate messages we will accept */
 #define MAX_KEY_UPDATE_MESSAGES     32
 
-/* Extension context codes */
-/* This extension is only allowed in TLS */
-#define EXT_TLS_ONLY                        0x0001
-/* This extension is only allowed in DTLS */
-#define EXT_DTLS_ONLY                       0x0002
-/* Some extensions may be allowed in DTLS but we don't implement them for it */
-#define EXT_TLS_IMPLEMENTATION_ONLY         0x0004
-/* Most extensions are not defined for SSLv3 but EXT_TYPE_renegotiate is */
-#define EXT_SSL3_ALLOWED                    0x0008
-/* Extension is only defined for TLS1.2 and above */
-#define EXT_TLS1_2_AND_BELOW_ONLY           0x0010
-/* Extension is only defined for TLS1.3 and above */
-#define EXT_TLS1_3_ONLY                     0x0020
-#define EXT_CLIENT_HELLO                    0x0040
-/* Really means TLS1.2 or below */
-#define EXT_TLS1_2_SERVER_HELLO             0x0080
-#define EXT_TLS1_3_SERVER_HELLO             0x0100
-#define EXT_TLS1_3_ENCRYPTED_EXTENSIONS     0x0200
-#define EXT_TLS1_3_HELLO_RETRY_REQUEST      0x0400
-#define EXT_TLS1_3_CERTIFICATE              0x0800
-#define EXT_TLS1_3_NEW_SESSION_TICKET       0x1000
-#define EXT_TLS1_3_CERTIFICATE_REQUEST      0x2000
-
 /* Dummy message type */
 #define SSL3_MT_DUMMY   -1
 
@@ -176,6 +153,8 @@ MSG_PROCESS_RETURN tls_process_end_of_early_data(SSL *s, PACKET *pkt);
 
 /* Extension processing */
 
+__owur int extension_is_relevant(SSL *s, unsigned int extctx,
+                                 unsigned int thisctx);
 __owur int tls_collect_extensions(SSL *s, PACKET *packet, unsigned int context,
                                   RAW_EXTENSION **res, int *al, size_t *len);
 __owur int tls_parse_extension(SSL *s, TLSEXT_INDEX idx, int context,
@@ -183,6 +162,8 @@ __owur int tls_parse_extension(SSL *s, TLSEXT_INDEX idx, int context,
                                int *al);
 __owur int tls_parse_all_extensions(SSL *s, int context, RAW_EXTENSION *exts,
                                     X509 *x, size_t chainidx, int *al);
+__owur int should_add_extension(SSL *s, unsigned int extctx,
+                                unsigned int thisctx, int max_version);
 __owur int tls_construct_extensions(SSL *s, WPACKET *pkt, unsigned int context,
                                     X509 *x, size_t chainidx, int *al);
 
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index 1c6c35e..d931c7f 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -1442,7 +1442,7 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt)
 
     /* Preserve the raw extensions PACKET for later use */
     extensions = clienthello->extensions;
-    if (!tls_collect_extensions(s, &extensions, EXT_CLIENT_HELLO,
+    if (!tls_collect_extensions(s, &extensions, SSL_EXT_CLIENT_HELLO,
                                 &clienthello->pre_proc_exts, &al,
                                 &clienthello->pre_proc_exts_len)) {
         /* SSLerr already been called */
@@ -1580,7 +1580,7 @@ static int tls_early_post_process_client_hello(SSL *s, int *al)
 
     /* We need to do this before getting the session */
     if (!tls_parse_extension(s, TLSEXT_IDX_extended_master_secret,
-                             EXT_CLIENT_HELLO,
+                             SSL_EXT_CLIENT_HELLO,
                              clienthello->pre_proc_exts, NULL, 0, al)) {
         SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_CLIENTHELLO_TLSEXT);
         goto err;
@@ -1708,7 +1708,7 @@ static int tls_early_post_process_client_hello(SSL *s, int *al)
 #endif                          /* !OPENSSL_NO_EC */
 
     /* TLS extensions */
-    if (!tls_parse_all_extensions(s, EXT_CLIENT_HELLO,
+    if (!tls_parse_all_extensions(s, SSL_EXT_CLIENT_HELLO,
                                   clienthello->pre_proc_exts, NULL, 0, al)) {
         SSLerr(SSL_F_TLS_EARLY_POST_PROCESS_CLIENT_HELLO, SSL_R_PARSE_TLSEXT);
         goto err;
@@ -2127,8 +2127,8 @@ int tls_construct_server_hello(SSL *s, WPACKET *pkt)
                 && !WPACKET_put_bytes_u8(pkt, compm))
             || !tls_construct_extensions(s, pkt,
                                          SSL_IS_TLS13(s)
-                                            ? EXT_TLS1_3_SERVER_HELLO
-                                            : EXT_TLS1_2_SERVER_HELLO,
+                                            ? SSL_EXT_TLS1_3_SERVER_HELLO
+                                            : SSL_EXT_TLS1_2_SERVER_HELLO,
                                          NULL, 0, &al)) {
         SSLerr(SSL_F_TLS_CONSTRUCT_SERVER_HELLO, ERR_R_INTERNAL_ERROR);
         goto err;
@@ -2510,8 +2510,9 @@ int tls_construct_certificate_request(SSL *s, WPACKET *pkt)
             goto err;
         }
 
-        if (!tls_construct_extensions(s, pkt, EXT_TLS1_3_CERTIFICATE_REQUEST,
-                                      NULL, 0, &al)) {
+        if (!tls_construct_extensions(s, pkt,
+                                      SSL_EXT_TLS1_3_CERTIFICATE_REQUEST, NULL,
+                                      0, &al)) {
             SSLerr(SSL_F_TLS_CONSTRUCT_CERTIFICATE_REQUEST,
                    ERR_R_INTERNAL_ERROR);
             goto err;
@@ -3251,9 +3252,10 @@ MSG_PROCESS_RETURN tls_process_client_certificate(SSL *s, PACKET *pkt)
                 SSLerr(SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE, SSL_R_BAD_LENGTH);
                 goto f_err;
             }
-            if (!tls_collect_extensions(s, &extensions, EXT_TLS1_3_CERTIFICATE,
-                                        &rawexts, &al, NULL)
-                    || !tls_parse_all_extensions(s, EXT_TLS1_3_CERTIFICATE,
+            if (!tls_collect_extensions(s, &extensions,
+                                        SSL_EXT_TLS1_3_CERTIFICATE, &rawexts,
+                                        &al, NULL)
+                    || !tls_parse_all_extensions(s, SSL_EXT_TLS1_3_CERTIFICATE,
                                                  rawexts, x, chainidx, &al)) {
                 OPENSSL_free(rawexts);
                 goto f_err;
@@ -3550,7 +3552,7 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt)
             || !WPACKET_close(pkt)
             || (SSL_IS_TLS13(s)
                 && !tls_construct_extensions(s, pkt,
-                                             EXT_TLS1_3_NEW_SESSION_TICKET,
+                                             SSL_EXT_TLS1_3_NEW_SESSION_TICKET,
                                              NULL, 0, &al))) {
         SSLerr(SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR);
         goto err;
@@ -3637,7 +3639,7 @@ static int tls_construct_encrypted_extensions(SSL *s, WPACKET *pkt)
 {
     int al;
 
-    if (!tls_construct_extensions(s, pkt, EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
+    if (!tls_construct_extensions(s, pkt, SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
                                   NULL, 0, &al)) {
         ssl3_send_alert(s, SSL3_AL_FATAL, al);
         SSLerr(SSL_F_TLS_CONSTRUCT_ENCRYPTED_EXTENSIONS, ERR_R_INTERNAL_ERROR);
@@ -3659,7 +3661,8 @@ static int tls_construct_hello_retry_request(SSL *s, WPACKET *pkt)
      */
     if (!WPACKET_put_bytes_u16(pkt, TLS1_3_VERSION_DRAFT)
             || !s->method->put_cipher_by_char(s->s3->tmp.new_cipher, pkt, &len)
-            || !tls_construct_extensions(s, pkt, EXT_TLS1_3_HELLO_RETRY_REQUEST,
+            || !tls_construct_extensions(s, pkt,
+                                         SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST,
                                          NULL, 0, &al)) {
         SSLerr(SSL_F_TLS_CONSTRUCT_HELLO_RETRY_REQUEST, ERR_R_INTERNAL_ERROR);
         goto err;
diff --git a/ssl/t1_ext.c b/ssl/t1_ext.c
deleted file mode 100644
index e3bcb63..0000000
--- a/ssl/t1_ext.c
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * Copyright 2014-2016 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
- * https://www.openssl.org/source/license.html
- */
-
-/* Custom extension utility functions */
-
-#include <openssl/ct.h>
-#include "ssl_locl.h"
-
-/* Find a custom extension from the list. */
-static custom_ext_method *custom_ext_find(const custom_ext_methods *exts,
-                                          unsigned int ext_type)
-{
-    size_t i;
-    custom_ext_method *meth = exts->meths;
-    for (i = 0; i < exts->meths_count; i++, meth++) {
-        if (ext_type == meth->ext_type)
-            return meth;
-    }
-    return NULL;
-}
-
-/*
- * Initialise custom extensions flags to indicate neither sent nor received.
- */
-void custom_ext_init(custom_ext_methods *exts)
-{
-    size_t i;
-    custom_ext_method *meth = exts->meths;
-    for (i = 0; i < exts->meths_count; i++, meth++)
-        meth->ext_flags = 0;
-}
-
-/* Pass received custom extension data to the application for parsing. */
-int custom_ext_parse(SSL *s, int server,
-                     unsigned int ext_type,
-                     const unsigned char *ext_data, size_t ext_size, int *al)
-{
-    custom_ext_methods *exts = server ? &s->cert->srv_ext : &s->cert->cli_ext;
-    custom_ext_method *meth;
-    meth = custom_ext_find(exts, ext_type);
-    /* If not found return success */
-    if (!meth)
-        return 1;
-    if (!server) {
-        /*
-         * If it's ServerHello we can't have any extensions not sent in
-         * ClientHello.
-         */
-        if (!(meth->ext_flags & SSL_EXT_FLAG_SENT)) {
-            *al = TLS1_AD_UNSUPPORTED_EXTENSION;
-            return 0;
-        }
-    }
-    /* If already present it's a duplicate */
-    if (meth->ext_flags & SSL_EXT_FLAG_RECEIVED) {
-        *al = TLS1_AD_DECODE_ERROR;
-        return 0;
-    }
-    meth->ext_flags |= SSL_EXT_FLAG_RECEIVED;
-    /* If no parse function set return success */
-    if (!meth->parse_cb)
-        return 1;
-
-    return meth->parse_cb(s, ext_type, ext_data, ext_size, al, meth->parse_arg);
-}
-
-/*
- * Request custom extension data from the application and add to the return
- * buffer.
- */
-int custom_ext_add(SSL *s, int server, WPACKET *pkt, int *al)
-{
-    custom_ext_methods *exts = server ? &s->cert->srv_ext : &s->cert->cli_ext;
-    custom_ext_method *meth;
-    size_t i;
-
-    for (i = 0; i < exts->meths_count; i++) {
-        const unsigned char *out = NULL;
-        size_t outlen = 0;
-
-        meth = exts->meths + i;
-
-        if (server) {
-            /*
-             * For ServerHello only send extensions present in ClientHello.
-             */
-            if (!(meth->ext_flags & SSL_EXT_FLAG_RECEIVED))
-                continue;
-            /* If callback absent for server skip it */
-            if (!meth->add_cb)
-                continue;
-        }
-        if (meth->add_cb) {
-            int cb_retval = 0;
-            cb_retval = meth->add_cb(s, meth->ext_type,
-                                     &out, &outlen, al, meth->add_arg);
-            if (cb_retval < 0)
-                return 0;       /* error */
-            if (cb_retval == 0)
-                continue;       /* skip this extension */
-        }
-
-        if (!WPACKET_put_bytes_u16(pkt, meth->ext_type)
-                || !WPACKET_start_sub_packet_u16(pkt)
-                || (outlen > 0 && !WPACKET_memcpy(pkt, out, outlen))
-                || !WPACKET_close(pkt)) {
-            *al = SSL_AD_INTERNAL_ERROR;
-            return 0;
-        }
-        /*
-         * We can't send duplicates: code logic should prevent this.
-         */
-        OPENSSL_assert(!(meth->ext_flags & SSL_EXT_FLAG_SENT));
-        /*
-         * Indicate extension has been sent: this is both a sanity check to
-         * ensure we don't send duplicate extensions and indicates that it is
-         * not an error if the extension is present in ServerHello.
-         */
-        meth->ext_flags |= SSL_EXT_FLAG_SENT;
-        if (meth->free_cb)
-            meth->free_cb(s, meth->ext_type, out, meth->add_arg);
-    }
-    return 1;
-}
-
-/* Copy table of custom extensions */
-int custom_exts_copy(custom_ext_methods *dst, const custom_ext_methods *src)
-{
-    if (src->meths_count) {
-        dst->meths =
-            OPENSSL_memdup(src->meths,
-                           sizeof(custom_ext_method) * src->meths_count);
-        if (dst->meths == NULL)
-            return 0;
-        dst->meths_count = src->meths_count;
-    }
-    return 1;
-}
-
-void custom_exts_free(custom_ext_methods *exts)
-{
-    OPENSSL_free(exts->meths);
-}
-
-/* Set callbacks for a custom extension. */
-static int custom_ext_meth_add(custom_ext_methods *exts,
-                               unsigned int ext_type,
-                               custom_ext_add_cb add_cb,
-                               custom_ext_free_cb free_cb,
-                               void *add_arg,
-                               custom_ext_parse_cb parse_cb, void *parse_arg)
-{
-    custom_ext_method *meth, *tmp;
-    /*
-     * Check application error: if add_cb is not set free_cb will never be
-     * called.
-     */
-    if (!add_cb && free_cb)
-        return 0;
-    /*
-     * Don't add if extension supported internally, but make exception
-     * for extension types that previously were not supported, but now are.
-     */
-    if (SSL_extension_supported(ext_type) &&
-        ext_type != TLSEXT_TYPE_signed_certificate_timestamp)
-        return 0;
-    /* Extension type must fit in 16 bits */
-    if (ext_type > 0xffff)
-        return 0;
-    /* Search for duplicate */
-    if (custom_ext_find(exts, ext_type))
-        return 0;
-    tmp = OPENSSL_realloc(exts->meths,
-                          (exts->meths_count + 1) * sizeof(custom_ext_method));
-
-    if (tmp == NULL)
-        return 0;
-
-    exts->meths = tmp;
-    meth = exts->meths + exts->meths_count;
-    memset(meth, 0, sizeof(*meth));
-    meth->parse_cb = parse_cb;
-    meth->add_cb = add_cb;
-    meth->free_cb = free_cb;
-    meth->ext_type = ext_type;
-    meth->add_arg = add_arg;
-    meth->parse_arg = parse_arg;
-    exts->meths_count++;
-    return 1;
-}
-
-/* Return true if a client custom extension exists, false otherwise */
-int SSL_CTX_has_client_custom_ext(const SSL_CTX *ctx, unsigned int ext_type)
-{
-    return custom_ext_find(&ctx->cert->cli_ext, ext_type) != NULL;
-}
-
-/* Application level functions to add custom extension callbacks */
-int SSL_CTX_add_client_custom_ext(SSL_CTX *ctx, unsigned int ext_type,
-                                  custom_ext_add_cb add_cb,
-                                  custom_ext_free_cb free_cb,
-                                  void *add_arg,
-                                  custom_ext_parse_cb parse_cb, void *parse_arg)
-{
-#ifndef OPENSSL_NO_CT
-    /*
-     * We don't want applications registering callbacks for SCT extensions
-     * whilst simultaneously using the built-in SCT validation features, as
-     * these two things may not play well together.
-     */
-    if (ext_type == TLSEXT_TYPE_signed_certificate_timestamp &&
-        SSL_CTX_ct_is_enabled(ctx))
-        return 0;
-#endif
-    return custom_ext_meth_add(&ctx->cert->cli_ext, ext_type, add_cb,
-                               free_cb, add_arg, parse_cb, parse_arg);
-}
-
-int SSL_CTX_add_server_custom_ext(SSL_CTX *ctx, unsigned int ext_type,
-                                  custom_ext_add_cb add_cb,
-                                  custom_ext_free_cb free_cb,
-                                  void *add_arg,
-                                  custom_ext_parse_cb parse_cb, void *parse_arg)
-{
-    return custom_ext_meth_add(&ctx->cert->srv_ext, ext_type,
-                               add_cb, free_cb, add_arg, parse_cb, parse_arg);
-}
-
-int SSL_extension_supported(unsigned int ext_type)
-{
-    switch (ext_type) {
-        /* Internally supported extensions. */
-    case TLSEXT_TYPE_application_layer_protocol_negotiation:
-#ifndef OPENSSL_NO_EC
-    case TLSEXT_TYPE_ec_point_formats:
-    case TLSEXT_TYPE_supported_groups:
-    case TLSEXT_TYPE_key_share:
-#endif
-#ifndef OPENSSL_NO_NEXTPROTONEG
-    case TLSEXT_TYPE_next_proto_neg:
-#endif
-    case TLSEXT_TYPE_padding:
-    case TLSEXT_TYPE_renegotiate:
-    case TLSEXT_TYPE_server_name:
-    case TLSEXT_TYPE_session_ticket:
-    case TLSEXT_TYPE_signature_algorithms:
-#ifndef OPENSSL_NO_SRP
-    case TLSEXT_TYPE_srp:
-#endif
-#ifndef OPENSSL_NO_OCSP
-    case TLSEXT_TYPE_status_request:
-#endif
-#ifndef OPENSSL_NO_CT
-    case TLSEXT_TYPE_signed_certificate_timestamp:
-#endif
-#ifndef OPENSSL_NO_SRTP
-    case TLSEXT_TYPE_use_srtp:
-#endif
-    case TLSEXT_TYPE_encrypt_then_mac:
-    case TLSEXT_TYPE_supported_versions:
-    case TLSEXT_TYPE_extended_master_secret:
-    case TLSEXT_TYPE_psk_kex_modes:
-    case TLSEXT_TYPE_cookie:
-    case TLSEXT_TYPE_early_data:
-    case TLSEXT_TYPE_certificate_authorities:
-    case TLSEXT_TYPE_psk:
-        return 1;
-    default:
-        return 0;
-    }
-}
diff --git a/test/sslapitest.c b/test/sslapitest.c
index ed804c9..3b4d01a 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -2250,6 +2250,313 @@ static int test_early_data_tls1_2(int idx)
 # endif
 #endif
 
+static int clntaddoldcb = 0;
+static int clntparseoldcb = 0;
+static int srvaddoldcb = 0;
+static int srvparseoldcb = 0;
+static int clntaddnewcb = 0;
+static int clntparsenewcb = 0;
+static int srvaddnewcb = 0;
+static int srvparsenewcb = 0;
+
+#define TEST_EXT_TYPE1  0xff00
+
+static int old_add_cb(SSL *s, unsigned int ext_type, const unsigned char **out,
+                      size_t *outlen, int *al, void *add_arg)
+{
+    int *server = (int *)add_arg;
+    unsigned char *data;
+
+    if (SSL_is_server(s))
+        srvaddoldcb++;
+    else
+        clntaddoldcb++;
+
+    if (*server != SSL_is_server(s))
+        return -1;
+
+    data = OPENSSL_malloc(sizeof(char));
+    if (data == NULL)
+        return -1;
+
+    *data = 1;
+    *out = data;
+    *outlen = sizeof(char);
+
+    return 1;
+}
+
+static void old_free_cb(SSL *s, unsigned int ext_type, const unsigned char *out,
+                        void *add_arg)
+{
+    OPENSSL_free((unsigned char *)out);
+}
+
+static int old_parse_cb(SSL *s, unsigned int ext_type, const unsigned char *in,
+                        size_t inlen, int *al, void *parse_arg)
+{
+    int *server = (int *)parse_arg;
+
+    if (SSL_is_server(s))
+        srvparseoldcb++;
+    else
+        clntparseoldcb++;
+
+    if (*server != SSL_is_server(s))
+        return -1;
+
+    if (inlen != sizeof(char) || *in != 1)
+        return -1;
+
+    return 1;
+}
+
+static int new_add_cb(SSL *s, unsigned int ext_type, unsigned int context,
+                      const unsigned char **out, size_t *outlen, X509 *x,
+                      size_t chainidx, int *al, void *add_arg)
+{
+    int *server = (int *)add_arg;
+    unsigned char *data;
+
+    if (SSL_is_server(s))
+        srvaddnewcb++;
+    else
+        clntaddnewcb++;
+
+    if (*server != SSL_is_server(s))
+        return -1;
+
+    data = OPENSSL_malloc(sizeof(char));
+    if (data == NULL)
+        return -1;
+
+    *data = 1;
+    *out = data;
+    *outlen = sizeof(char);
+
+    return 1;
+}
+
+static void new_free_cb(SSL *s, unsigned int ext_type, unsigned int context,
+                        const unsigned char *out, void *add_arg)
+{
+    OPENSSL_free((unsigned char *)out);
+}
+
+static int new_parse_cb(SSL *s, unsigned int ext_type, unsigned int context,
+                        const unsigned char *in, size_t inlen, X509 *x,
+                        size_t chainidx, int *al, void *parse_arg)
+{
+    int *server = (int *)parse_arg;
+
+    if (SSL_is_server(s))
+        srvparsenewcb++;
+    else
+        clntparsenewcb++;
+
+    if (*server != SSL_is_server(s))
+        return -1;
+
+    if (inlen != sizeof(char) || *in != 1)
+        return -1;
+
+    return 1;
+}
+/*
+ * Custom call back tests.
+ * Test 0: Old style callbacks in TLSv1.2
+ * Test 1: New style callbacks in TLSv1.2
+ * Test 2: New style callbacks in TLSv1.3. Extensions in CH and EE
+ * Test 3: New style callbacks in TLSv1.3. Extensions in CH, SH, EE, Cert + NST
+ */
+static int test_custom_exts(int tst) {
+    SSL_CTX *cctx = NULL, *sctx = NULL;
+    SSL *clientssl = NULL, *serverssl = NULL;
+    int testresult = 0;
+    static int server = 1;
+    static int client = 0;
+    SSL_SESSION *sess = NULL;
+    unsigned int context;
+
+    /* Reset callback counters */
+    clntaddoldcb = clntparseoldcb = srvaddoldcb = srvparseoldcb = 0;
+    clntaddnewcb = clntparsenewcb = srvaddnewcb = srvparsenewcb = 0;
+
+    if (!create_ssl_ctx_pair(TLS_server_method(), TLS_client_method(), &sctx,
+                             &cctx, cert, privkey)) {
+        printf("Unable to create SSL_CTX pair\n");
+        return 0;
+    }
+
+    if (tst < 2) {
+        SSL_CTX_set_options(cctx, SSL_OP_NO_TLSv1_3);
+        SSL_CTX_set_options(sctx, SSL_OP_NO_TLSv1_3);
+    }
+
+    if (tst == 3) {
+        context = SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
+                  | SSL_EXT_TLS1_3_SERVER_HELLO
+                  | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS
+                  | SSL_EXT_TLS1_3_CERTIFICATE
+                  | SSL_EXT_TLS1_3_NEW_SESSION_TICKET;
+    } else {
+        context = SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO
+                  | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS;
+    }
+
+    /* Create a client side custom extension */
+    if (tst == 0) {
+        if (!SSL_CTX_add_client_custom_ext(cctx, TEST_EXT_TYPE1, old_add_cb,
+                                           old_free_cb, &client, old_parse_cb,
+                                           &client)) {
+            printf("Unable to create old style client side custom extension\n");
+            return 0;
+        }
+    } else {
+        if (!SSL_CTX_add_custom_ext(cctx, TEST_EXT_TYPE1, context, new_add_cb,
+                                    new_free_cb, &client, new_parse_cb,
+                                    &client)) {
+            printf("Unable to create new style client side custom extension\n");
+            return 0;
+        }
+    }
+
+    /* Should not be able to add duplicates */
+    if (SSL_CTX_add_client_custom_ext(cctx, TEST_EXT_TYPE1, old_add_cb,
+                                      old_free_cb, &client, old_parse_cb,
+                                      &client)) {
+        printf("Unexpected success adding duplicate client custom extension\n");
+        return 0;
+    }
+    if (SSL_CTX_add_custom_ext(cctx, TEST_EXT_TYPE1, context, new_add_cb,
+                               new_free_cb, &client, new_parse_cb, &client)) {
+        printf("Unexpected success adding duplicate client custom extension\n");
+        return 0;
+    }
+
+    /* Create a server side custom extension */
+    if (tst == 0) {
+        if (!SSL_CTX_add_server_custom_ext(sctx, TEST_EXT_TYPE1, old_add_cb,
+                                           old_free_cb, &server, old_parse_cb,
+                                           &server)) {
+            printf("Unable to create old style server side custom extension\n");
+            return 0;
+        }
+    } else {
+        if (!SSL_CTX_add_custom_ext(sctx, TEST_EXT_TYPE1, context, new_add_cb,
+                                    new_free_cb, &server, new_parse_cb,
+                                    &server)) {
+            printf("Unable to create new style server side custom extension\n");
+            return 0;
+        }
+    }
+
+    /* Should not be able to add duplicates */
+    if (SSL_CTX_add_server_custom_ext(sctx, TEST_EXT_TYPE1, old_add_cb,
+                                      old_free_cb, &server, old_parse_cb,
+                                      &server)) {
+        printf("Unexpected success adding duplicate server custom extension\n");
+        return 0;
+    }
+    if (SSL_CTX_add_custom_ext(sctx, TEST_EXT_TYPE1, context, new_add_cb,
+                               new_free_cb, &server, new_parse_cb, &server)) {
+        printf("Unexpected success adding duplicate server custom extension\n");
+        return 0;
+    }
+
+    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_NONE)) {
+        printf("Unable to create SSL connection\n");
+        goto end;
+    }
+
+    if (tst == 0) {
+        if (clntaddoldcb != 1 || clntparseoldcb != 1 || srvaddoldcb != 1
+                || srvparseoldcb != 1) {
+            printf("Custom extension callbacks not called\n");
+            goto end;
+        }
+    } else if (tst == 1 || tst == 2) {
+        if (clntaddnewcb != 1 || clntparsenewcb != 1 || srvaddnewcb != 1
+                || srvparsenewcb != 1) {
+            printf("Custom extension callbacks not called\n");
+            goto end;
+        }
+    } else {
+        if (clntaddnewcb != 1 || clntparsenewcb != 4 || srvaddnewcb != 4
+                || srvparsenewcb != 1) {
+            printf("Custom extension callbacks not called\n");
+            goto end;
+        }
+    }
+
+    sess = SSL_get1_session(clientssl);
+
+    SSL_shutdown(clientssl);
+    SSL_shutdown(serverssl);
+
+    SSL_free(serverssl);
+    SSL_free(clientssl);
+    serverssl = clientssl = NULL;
+
+    if (!create_ssl_objects(sctx, cctx, &serverssl, &clientssl, NULL, NULL)) {
+        printf("Unable to create SSL objects (2)\n");
+        goto end;
+    }
+
+    if (!SSL_set_session(clientssl, sess)) {
+        printf("Failed setting session\n");
+        goto end;
+    }
+
+    if (!create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)) {
+        printf("Unable to create SSL connection (2)\n");
+        goto end;
+    }
+
+    /*
+     * For a resumed session we expect to add the ClientHello extension. For the
+     * old style callbacks we ignore it on the server side because they set
+     * SSL_EXT_IGNORE_ON_RESUMPTION. The new style callbacks do not ignore
+     * them.
+     */
+    if (tst == 0) {
+        if (clntaddoldcb != 2 || clntparseoldcb != 1 || srvaddoldcb != 1
+                || srvparseoldcb != 1) {
+            printf("Unexpected custom extension callback calls\n");
+            goto end;
+        }
+    } else if (tst == 1 || tst == 2) {
+        if (clntaddnewcb != 2 || clntparsenewcb != 2 || srvaddnewcb != 2
+                || srvparsenewcb != 2) {
+            printf("Unexpected custom extension callback calls\n");
+            goto end;
+        }
+    } else {
+        /* No Certificate message extensions in the resumption handshake */
+        if (clntaddnewcb != 2 || clntparsenewcb != 7 || srvaddnewcb != 7
+                || srvparsenewcb != 2) {
+            printf("Unexpected custom extension callback calls\n");
+            goto end;
+        }
+    }
+
+    testresult = 1;
+
+end:
+    SSL_SESSION_free(sess);
+    SSL_free(serverssl);
+    SSL_free(clientssl);
+    SSL_CTX_free(sctx);
+    SSL_CTX_free(cctx);
+
+    return testresult;
+}
+
 int test_main(int argc, char *argv[])
 {
     int testresult = 1;
@@ -2295,6 +2602,11 @@ int test_main(int argc, char *argv[])
     ADD_ALL_TESTS(test_early_data_tls1_2, 2);
 # endif
 #endif
+#ifndef OPENSSL_NO_TLS1_3
+    ADD_ALL_TESTS(test_custom_exts, 4);
+#else
+    ADD_ALL_TESTS(test_custom_exts, 2);
+#endif
 
     testresult = run_tests(argv[0]);
 
diff --git a/util/libssl.num b/util/libssl.num
index 4994910..49974c9 100644
--- a/util/libssl.num
+++ b/util/libssl.num
@@ -439,3 +439,4 @@ SSL_get0_CA_list                        439	1_1_1	EXIST::FUNCTION:
 SSL_get0_peer_CA_list                   440	1_1_1	EXIST::FUNCTION:
 SSL_CTX_add1_CA_list                    441	1_1_1	EXIST::FUNCTION:
 SSL_CTX_get0_CA_list                    442	1_1_1	EXIST::FUNCTION:
+SSL_CTX_add_custom_ext                  443	1_1_1	EXIST::FUNCTION:


More information about the openssl-commits mailing list