aboutsummaryrefslogtreecommitdiffstats
path: root/ssl
diff options
context:
space:
mode:
authorMatt Caswell <matt@openssl.org>2017-04-05 11:59:23 +0100
committerMatt Caswell <matt@openssl.org>2017-04-07 13:41:04 +0100
commit43ae5eed6f8665b88f45445df666ab2688aae7b0 (patch)
tree33413025b37f6fb6f4d406591c9fbbb066702d1b /ssl
parentfe874d27d33faa527b5e945137787bf6b0f5c253 (diff)
downloadopenssl-43ae5eed6f8665b88f45445df666ab2688aae7b0.tar.gz
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@openssl.org> (Merged from https://github.com/openssl/openssl/pull/3139)
Diffstat (limited to 'ssl')
-rw-r--r--ssl/ssl_cert.c7
-rw-r--r--ssl/ssl_locl.h31
-rw-r--r--ssl/ssl_rsa.c29
-rw-r--r--ssl/statem/extensions.c160
-rw-r--r--ssl/statem/extensions_clnt.c6
-rw-r--r--ssl/statem/extensions_cust.c359
-rw-r--r--ssl/statem/statem_locl.h4
7 files changed, 403 insertions, 193 deletions
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index a4e7977012..3a85ede638 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 f53293109b..a9ac840e10 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -1619,14 +1619,21 @@ struct cert_pkey_st {
typedef struct {
unsigned short ext_type;
/*
+ * Set to 1 if this is only for the server side, 0 if it is only for the
+ * client side, or -1 if it is either.
+ */
+ int server;
+ /* 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;
+ custom_ext_add_cb_ex add_cb;
+ custom_ext_free_cb_ex free_cb;
void *add_arg;
- custom_ext_parse_cb parse_cb;
+ custom_ext_parse_cb_ex parse_cb;
void *parse_arg;
} custom_ext_method;
@@ -1706,9 +1713,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 +2442,18 @@ __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, int server,
+ 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 a94fb13b89..ecf2ff3e73 100644
--- a/ssl/ssl_rsa.c
+++ b/ssl/ssl_rsa.c
@@ -797,26 +797,15 @@ 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, 1, 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/statem/extensions.c b/ssl/statem/extensions.c
index c2e0411240..49a7156a2c 100644
--- a/ssl/statem/extensions.c
+++ b/ssl/statem/extensions.c
@@ -329,6 +329,23 @@ static const EXTENSION_DEFINITION ext_defs[] = {
}
};
+/* 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
@@ -345,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 & SSL_EXT_TLS_ONLY) != 0)
- return 0;
- } else if ((thisext->context & SSL_EXT_DTLS_ONLY) != 0) {
- return 0;
- }
-
*found = &rawexlist[i];
return 1;
}
}
- if ((context & (SSL_EXT_CLIENT_HELLO | SSL_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;
+ int server = -1;
+ custom_ext_method *meth = NULL;
+
+ if ((context & SSL_EXT_CLIENT_HELLO) != 0)
+ server = 1;
+ else if ((context & SSL_EXT_TLS1_2_SERVER_HELLO) != 0)
+ server = 0;
+
+ meth = custom_ext_find(meths, server, type, &offset);
+ if (meth != NULL) {
+ if (!validate_context(s, meth->context, context))
+ return 0;
+ *found = &rawexlist[offset + builtin_num];
+ return 1;
}
}
@@ -390,8 +400,7 @@ 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 & SSL_EXT_TLS_IMPLEMENTATION_ONLY) != 0)
@@ -399,7 +408,8 @@ static int extension_is_relevant(SSL *s, unsigned int extctx,
&& (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))
+ || (!SSL_IS_TLS13(s) && (extctx & SSL_EXT_TLS1_3_ONLY) != 0)
+ || (s->hit && (extctx & SSL_EXT_IGNORE_ON_RESUMPTION) != 0))
return 0;
return 1;
@@ -427,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;
@@ -437,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 & SSL_EXT_CLIENT_HELLO) != 0) {
- exts = &s->cert->srv_ext;
- custom_ext_init(&s->cert->srv_ext);
- } else if ((context & SSL_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));
@@ -560,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
- & (SSL_EXT_CLIENT_HELLO | SSL_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;
@@ -595,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 & SSL_EXT_CLIENT_HELLO) != 0) {
- numexts += s->cert->srv_ext.meths_count;
- } else if ((context & SSL_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++) {
@@ -621,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
@@ -634,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;
/*
@@ -667,21 +683,10 @@ int tls_construct_extensions(SSL *s, WPACKET *pkt, unsigned int context,
/* Add custom extensions first */
if ((context & SSL_EXT_CLIENT_HELLO) != 0) {
- custom_ext_init(&s->cert->cli_ext);
- addcustom = 1;
- } else if ((context & SSL_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;
+ /* 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;
}
@@ -691,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 & SSL_EXT_TLS_IMPLEMENTATION_ONLY)
- != 0)
- || (s->version == SSL3_VERSION
- && (thisexd->context & SSL_EXT_SSL3_ALLOWED) == 0)
- || (SSL_IS_TLS13(s)
- && (thisexd->context & SSL_EXT_TLS1_2_AND_BELOW_ONLY)
- != 0)
- || (!SSL_IS_TLS13(s)
- && (thisexd->context & SSL_EXT_TLS1_3_ONLY) != 0
- && (context & SSL_EXT_CLIENT_HELLO) == 0)
- || ((thisexd->context & SSL_EXT_TLS1_3_ONLY) != 0
- && (context & SSL_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 af89494507..7d2a4b04a5 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;
}
diff --git a/ssl/statem/extensions_cust.c b/ssl/statem/extensions_cust.c
index 374fe1e55e..ec1ab6d539 100644
--- a/ssl/statem/extensions_cust.c
+++ b/ssl/statem/extensions_cust.c
@@ -9,18 +9,86 @@
/* Custom extension utility functions */
+#include <assert.h>
#include <openssl/ct.h>
#include "../ssl_locl.h"
+#include "statem_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)
+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 |server| 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 1 if we are trying
+ * to find a method relevant to the server, 0 for the client, or -1 if we don't
+ * care
+ */
+custom_ext_method *custom_ext_find(const custom_ext_methods *exts, int server,
+ 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)
+ if (ext_type == meth->ext_type
+ && (server == -1 || server == meth->server
+ || meth->server == -1)) {
+ if (idx != NULL)
+ *idx = i;
return meth;
+ }
}
return NULL;
}
@@ -37,46 +105,63 @@ void custom_ext_init(custom_ext_methods *exts)
}
/* 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)
+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 = server ? &s->cert->srv_ext : &s->cert->cli_ext;
+ custom_ext_methods *exts = &s->cert->custext;
custom_ext_method *meth;
- meth = custom_ext_find(exts, ext_type);
+ int server = -1;
+
+ if ((context & (SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO)) != 0)
+ server = s->server;
+
+ meth = custom_ext_find(exts, server, ext_type, NULL);
/* If not found return success */
if (!meth)
return 1;
- if (!server) {
+
+ /* 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 we can't have any extensions not sent in
- * ClientHello.
+ * If it's ServerHello or EncryptedExtensions we can't have any
+ * extensions not sent in ClientHello.
*/
- if (!(meth->ext_flags & SSL_EXT_FLAG_SENT)) {
+ if ((meth->ext_flags & SSL_EXT_FLAG_SENT) == 0) {
*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;
+
+ /*
+ * 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, ext_data, ext_size, al, meth->parse_arg);
+ 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 server, WPACKET *pkt, int *al)
+int custom_ext_add(SSL *s, int context, WPACKET *pkt, X509 *x, size_t chainidx,
+ int maxversion, int *al)
{
- custom_ext_methods *exts = server ? &s->cert->srv_ext : &s->cert->cli_ext;
+ custom_ext_methods *exts = &s->cert->custext;
custom_ext_method *meth;
size_t i;
@@ -86,20 +171,30 @@ int custom_ext_add(SSL *s, int server, WPACKET *pkt, int *al)
meth = exts->meths + i;
- if (server) {
+ 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 only send extensions present in ClientHello.
+ * For ServerHello/EncryptedExtensions 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) {
+ /*
+ * 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 = 0;
- cb_retval = meth->add_cb(s, meth->ext_type,
- &out, &outlen, al, meth->add_arg);
+ 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)
@@ -113,18 +208,20 @@ int custom_ext_add(SSL *s, int server, WPACKET *pkt, int *al)
*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 ((context & SSL_EXT_CLIENT_HELLO) != 0) {
+ /*
+ * We can't send duplicates: code logic should prevent this.
+ */
+ 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);
+ meth->free_cb(s, meth->ext_type, context, out, meth->add_arg);
}
return 1;
}
@@ -132,49 +229,120 @@ int custom_ext_add(SSL *s, int server, WPACKET *pkt, int *al)
/* Copy table of custom extensions */
int custom_exts_copy(custom_ext_methods *dst, const custom_ext_methods *src)
{
- if (src->meths_count) {
+ size_t i;
+ int err = 0;
+
+ if (src->meths_count > 0) {
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;
+
+ 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;
+
+ for (i = 0; i < exts->meths_count; i++) {
+ custom_ext_method *meth = exts->meths + i;
+
+ 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);
}
-/* 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)
+/* 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, 0, ext_type, NULL) != NULL;
+}
+
+static int add_custom_ext_intern(SSL_CTX *ctx, int server,
+ unsigned int ext_type,
+ unsigned int context,
+ custom_ext_add_cb_ex add_cb,
+ custom_ext_free_cb_ex free_cb,
+ void *add_arg,
+ 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 && free_cb)
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)
+ 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))
+ if (custom_ext_find(exts, server, ext_type, NULL))
return 0;
tmp = OPENSSL_realloc(exts->meths,
(exts->meths_count + 1) * sizeof(custom_ext_method));
@@ -185,6 +353,8 @@ static int custom_ext_meth_add(custom_ext_methods *exts,
exts->meths = tmp;
meth = exts->meths + exts->meths_count;
memset(meth, 0, sizeof(*meth));
+ meth->server = server;
+ meth->context = context;
meth->parse_cb = parse_cb;
meth->add_cb = add_cb;
meth->free_cb = free_cb;
@@ -195,31 +365,65 @@ static int custom_ext_meth_add(custom_ext_methods *exts,
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)
+static int add_old_custom_ext(SSL_CTX *ctx, int server, 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)
{
- return custom_ext_find(&ctx->cert->cli_ext, ext_type) != NULL;
+ custom_ext_add_cb_wrap *add_cb_wrap
+ = OPENSSL_malloc(sizeof(custom_ext_add_cb_wrap));
+ custom_ext_parse_cb_wrap *parse_cb_wrap
+ = OPENSSL_malloc(sizeof(custom_ext_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;
+
+ /*
+ * TODO(TLS1.3): Is it possible with the old API to add custom exts for both
+ * client and server for the same type in the same SSL_CTX? We don't handle
+ * that yet.
+ */
+ ret = add_custom_ext_intern(ctx, server, 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 custom extension callbacks */
+/* 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)
{
-#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);
+ return add_old_custom_ext(ctx, 0, 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,
@@ -228,8 +432,23 @@ int SSL_CTX_add_server_custom_ext(SSL_CTX *ctx, unsigned int ext_type,
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);
+ return add_old_custom_ext(ctx, 1, 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,
+ custom_ext_add_cb_ex add_cb,
+ custom_ext_free_cb_ex free_cb,
+ void *add_arg,
+ custom_ext_parse_cb_ex parse_cb, void *parse_arg)
+{
+ return add_custom_ext_intern(ctx, -1, ext_type, context, add_cb, free_cb,
+ add_arg, parse_cb, parse_arg);
}
int SSL_extension_supported(unsigned int ext_type)
diff --git a/ssl/statem/statem_locl.h b/ssl/statem/statem_locl.h
index 43d79b8b94..2352c6a11e 100644
--- a/ssl/statem/statem_locl.h
+++ b/ssl/statem/statem_locl.h
@@ -153,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,
@@ -160,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);