From 43ae5eed6f8665b88f45445df666ab2688aae7b0 Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Wed, 5 Apr 2017 11:59:23 +0100 Subject: [PATCH] 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 (Merged from https://github.com/openssl/openssl/pull/3139) --- include/openssl/ssl.h | 45 ++++- ssl/ssl_cert.c | 7 +- ssl/ssl_locl.h | 31 +-- ssl/ssl_rsa.c | 29 +-- ssl/statem/extensions.c | 160 ++++++++-------- ssl/statem/extensions_clnt.c | 6 +- ssl/statem/extensions_cust.c | 359 ++++++++++++++++++++++++++++------- ssl/statem/statem_locl.h | 4 + util/libssl.num | 1 + 9 files changed, 440 insertions(+), 202 deletions(-) diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 8e2d934fde..8dbfe91f63 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -259,19 +259,21 @@ typedef int (*tls_session_secret_cb_fn) (SSL *s, void *secret, #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 above */ +/* 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 -#define SSL_EXT_CLIENT_HELLO 0x0040 +/* 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 0x0080 -#define SSL_EXT_TLS1_3_SERVER_HELLO 0x0100 -#define SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS 0x0200 -#define SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST 0x0400 -#define SSL_EXT_TLS1_3_CERTIFICATE 0x0800 -#define SSL_EXT_TLS1_3_NEW_SESSION_TICKET 0x1000 -#define SSL_EXT_TLS1_3_CERTIFICATE_REQUEST 0x2000 +#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 */ @@ -286,6 +288,23 @@ 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 (*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 (*custom_ext_free_cb_ex) (SSL *s, unsigned int ext_type, + unsigned int context, + const unsigned char *out, void *add_arg); + +typedef int (*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); @@ -779,6 +798,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, + 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); + __owur int SSL_extension_supported(unsigned int ext_type); # define SSL_NOTHING 1 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 @@ -1618,15 +1618,22 @@ 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 #include #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); diff --git a/util/libssl.num b/util/libssl.num index 49949100da..49974c9c94 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: -- 2.25.1