Path: blob/main/crypto/openssl/ssl/statem/extensions_cust.c
105667 views
/*1* Copyright 2014-2025 The OpenSSL Project Authors. All Rights Reserved.2*3* Licensed under the Apache License 2.0 (the "License"). You may not use4* this file except in compliance with the License. You can obtain a copy5* in the file LICENSE in the source distribution or at6* https://www.openssl.org/source/license.html7*/89/* Custom extension utility functions */1011#include <openssl/ct.h>12#include "../ssl_local.h"13#include "internal/cryptlib.h"14#include "internal/ssl_unwrap.h"15#include "statem_local.h"1617typedef struct {18void *add_arg;19custom_ext_add_cb add_cb;20custom_ext_free_cb free_cb;21} custom_ext_add_cb_wrap;2223typedef struct {24void *parse_arg;25custom_ext_parse_cb parse_cb;26} custom_ext_parse_cb_wrap;2728/*29* Provide thin wrapper callbacks which convert new style arguments to old style30*/31static int custom_ext_add_old_cb_wrap(SSL *s, unsigned int ext_type,32unsigned int context,33const unsigned char **out,34size_t *outlen, X509 *x, size_t chainidx,35int *al, void *add_arg)36{37custom_ext_add_cb_wrap *add_cb_wrap = (custom_ext_add_cb_wrap *)add_arg;3839if (add_cb_wrap->add_cb == NULL)40return 1;4142return add_cb_wrap->add_cb(s, ext_type, out, outlen, al,43add_cb_wrap->add_arg);44}4546static void custom_ext_free_old_cb_wrap(SSL *s, unsigned int ext_type,47unsigned int context,48const unsigned char *out, void *add_arg)49{50custom_ext_add_cb_wrap *add_cb_wrap = (custom_ext_add_cb_wrap *)add_arg;5152if (add_cb_wrap->free_cb == NULL)53return;5455add_cb_wrap->free_cb(s, ext_type, out, add_cb_wrap->add_arg);56}5758static int custom_ext_parse_old_cb_wrap(SSL *s, unsigned int ext_type,59unsigned int context,60const unsigned char *in,61size_t inlen, X509 *x, size_t chainidx,62int *al, void *parse_arg)63{64custom_ext_parse_cb_wrap *parse_cb_wrap = (custom_ext_parse_cb_wrap *)parse_arg;6566if (parse_cb_wrap->parse_cb == NULL)67return 1;6869return parse_cb_wrap->parse_cb(s, ext_type, in, inlen, al,70parse_cb_wrap->parse_arg);71}7273/*74* Find a custom extension from the list. The |role| param is there to75* support the legacy API where custom extensions for client and server could76* be set independently on the same SSL_CTX. It is set to ENDPOINT_SERVER if we77* are trying to find a method relevant to the server, ENDPOINT_CLIENT for the78* client, or ENDPOINT_BOTH for either79*/80custom_ext_method *custom_ext_find(const custom_ext_methods *exts,81ENDPOINT role, unsigned int ext_type,82size_t *idx)83{84size_t i;85custom_ext_method *meth = exts->meths;8687for (i = 0; i < exts->meths_count; i++, meth++) {88if (ext_type == meth->ext_type89&& (role == ENDPOINT_BOTH || role == meth->role90|| meth->role == ENDPOINT_BOTH)) {91if (idx != NULL)92*idx = i;93return meth;94}95}96return NULL;97}9899/*100* Initialise custom extensions flags to indicate neither sent nor received.101*/102void custom_ext_init(custom_ext_methods *exts)103{104size_t i;105custom_ext_method *meth = exts->meths;106107for (i = 0; i < exts->meths_count; i++, meth++)108meth->ext_flags &= ~(SSL_EXT_FLAG_SENT | SSL_EXT_FLAG_RECEIVED);109}110111/* Pass received custom extension data to the application for parsing. */112int custom_ext_parse(SSL_CONNECTION *s, unsigned int context,113unsigned int ext_type,114const unsigned char *ext_data, size_t ext_size, X509 *x,115size_t chainidx)116{117int al = 0;118custom_ext_methods *exts = &s->cert->custext;119custom_ext_method *meth;120ENDPOINT role = ENDPOINT_BOTH;121122if ((context & (SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_2_SERVER_HELLO)) != 0)123role = s->server ? ENDPOINT_SERVER : ENDPOINT_CLIENT;124125meth = custom_ext_find(exts, role, ext_type, NULL);126/* If not found return success */127if (!meth)128return 1;129130/* Check if extension is defined for our protocol. If not, skip */131if (!extension_is_relevant(s, meth->context, context))132return 1;133134if ((context & (SSL_EXT_TLS1_2_SERVER_HELLO | SSL_EXT_TLS1_3_SERVER_HELLO | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS)) != 0) {135/*136* If it's ServerHello or EncryptedExtensions we can't have any137* extensions not sent in ClientHello.138*/139if ((meth->ext_flags & SSL_EXT_FLAG_SENT) == 0) {140SSLfatal(s, TLS1_AD_UNSUPPORTED_EXTENSION, SSL_R_BAD_EXTENSION);141return 0;142}143}144145/*146* Extensions received in the ClientHello or CertificateRequest are marked147* with the SSL_EXT_FLAG_RECEIVED. This is so we know to add the equivalent148* extensions in the response messages149*/150if ((context & (SSL_EXT_CLIENT_HELLO | SSL_EXT_TLS1_3_CERTIFICATE_REQUEST))151!= 0)152meth->ext_flags |= SSL_EXT_FLAG_RECEIVED;153154/* If no parse function set return success */155if (meth->parse_cb == NULL)156return 1;157158if (meth->parse_cb(SSL_CONNECTION_GET_USER_SSL(s), ext_type, context, ext_data,159ext_size, x, chainidx, &al, meth->parse_arg)160<= 0) {161SSLfatal(s, al, SSL_R_BAD_EXTENSION);162return 0;163}164165return 1;166}167168/*169* Request custom extension data from the application and add to the return170* buffer.171*/172int custom_ext_add(SSL_CONNECTION *s, int context, WPACKET *pkt, X509 *x,173size_t chainidx, int maxversion)174{175custom_ext_methods *exts = &s->cert->custext;176custom_ext_method *meth;177size_t i;178int al;179int for_comp = (context & SSL_EXT_TLS1_3_CERTIFICATE_COMPRESSION) != 0;180181for (i = 0; i < exts->meths_count; i++) {182const unsigned char *out = NULL;183size_t outlen = 0;184185meth = exts->meths + i;186187if (!should_add_extension(s, meth->context, context, maxversion))188continue;189190if ((context & (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_RAW_PUBLIC_KEY | SSL_EXT_TLS1_3_HELLO_RETRY_REQUEST)) != 0) {191/* Only send extensions present in ClientHello/CertificateRequest */192if (!(meth->ext_flags & SSL_EXT_FLAG_RECEIVED))193continue;194}195/*196* We skip it if the callback is absent - except for a ClientHello where197* we add an empty extension.198*/199if ((context & SSL_EXT_CLIENT_HELLO) == 0 && meth->add_cb == NULL)200continue;201202if (meth->add_cb != NULL) {203int cb_retval = meth->add_cb(SSL_CONNECTION_GET_USER_SSL(s),204meth->ext_type, context, &out,205&outlen, x, chainidx, &al,206meth->add_arg);207208if (cb_retval < 0) {209if (!for_comp)210SSLfatal(s, al, SSL_R_CALLBACK_FAILED);211return 0; /* error */212}213if (cb_retval == 0)214continue; /* skip this extension */215}216217if (!WPACKET_put_bytes_u16(pkt, meth->ext_type)218|| !WPACKET_start_sub_packet_u16(pkt)219|| (outlen > 0 && !WPACKET_memcpy(pkt, out, outlen))220|| !WPACKET_close(pkt)) {221if (meth->free_cb != NULL)222meth->free_cb(SSL_CONNECTION_GET_USER_SSL(s), meth->ext_type,223context, out, meth->add_arg);224if (!for_comp)225SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);226return 0;227}228if ((context & SSL_EXT_CLIENT_HELLO) != 0) {229/*230* We can't send duplicates: code logic should prevent this.231*/232if (!ossl_assert((meth->ext_flags & SSL_EXT_FLAG_SENT) == 0)) {233if (meth->free_cb != NULL)234meth->free_cb(SSL_CONNECTION_GET_USER_SSL(s), meth->ext_type,235context, out, meth->add_arg);236if (!for_comp)237SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);238return 0;239}240/*241* Indicate extension has been sent: this is both a sanity check to242* ensure we don't send duplicate extensions and indicates that it243* is not an error if the extension is present in ServerHello.244*/245meth->ext_flags |= SSL_EXT_FLAG_SENT;246}247if (meth->free_cb != NULL)248meth->free_cb(SSL_CONNECTION_GET_USER_SSL(s), meth->ext_type,249context, out, meth->add_arg);250}251return 1;252}253254/* Copy the flags from src to dst for any extensions that exist in both */255int custom_exts_copy_flags(custom_ext_methods *dst,256const custom_ext_methods *src)257{258size_t i;259custom_ext_method *methsrc = src->meths;260261for (i = 0; i < src->meths_count; i++, methsrc++) {262custom_ext_method *methdst = custom_ext_find(dst, methsrc->role,263methsrc->ext_type, NULL);264265if (methdst == NULL)266continue;267268methdst->ext_flags = methsrc->ext_flags;269}270271return 1;272}273274/* Copy old style API wrapper arguments */275static void custom_ext_copy_old_cb(custom_ext_method *methdst,276const custom_ext_method *methsrc,277int *err)278{279if (methsrc->add_cb != custom_ext_add_old_cb_wrap)280return;281282if (*err) {283methdst->add_arg = NULL;284methdst->parse_arg = NULL;285return;286}287288methdst->add_arg = OPENSSL_memdup(methsrc->add_arg,289sizeof(custom_ext_add_cb_wrap));290methdst->parse_arg = OPENSSL_memdup(methsrc->parse_arg,291sizeof(custom_ext_parse_cb_wrap));292293if (methdst->add_arg == NULL || methdst->parse_arg == NULL)294*err = 1;295296return;297}298299/* Copy table of custom extensions */300int custom_exts_copy(custom_ext_methods *dst, const custom_ext_methods *src)301{302size_t i;303int err = 0;304305if (src->meths_count > 0) {306dst->meths = OPENSSL_memdup(src->meths,307sizeof(*src->meths) * src->meths_count);308if (dst->meths == NULL)309return 0;310dst->meths_count = src->meths_count;311312for (i = 0; i < src->meths_count; i++)313custom_ext_copy_old_cb(&dst->meths[i], &src->meths[i], &err);314}315316if (err) {317custom_exts_free(dst);318return 0;319}320321return 1;322}323324/* Copy custom extensions that were set on connection */325int custom_exts_copy_conn(custom_ext_methods *dst,326const custom_ext_methods *src)327{328size_t i;329int err = 0;330331if (src->meths_count > 0) {332size_t meths_count = 0;333334for (i = 0; i < src->meths_count; i++)335if ((src->meths[i].ext_flags & SSL_EXT_FLAG_CONN) != 0)336meths_count++;337338if (meths_count > 0) {339custom_ext_method *methdst = OPENSSL_realloc(dst->meths,340(dst->meths_count + meths_count) * sizeof(custom_ext_method));341342if (methdst == NULL)343return 0;344345for (i = 0; i < dst->meths_count; i++)346custom_ext_copy_old_cb(&methdst[i], &dst->meths[i], &err);347348dst->meths = methdst;349methdst += dst->meths_count;350351for (i = 0; i < src->meths_count; i++) {352custom_ext_method *methsrc = &src->meths[i];353354if ((methsrc->ext_flags & SSL_EXT_FLAG_CONN) == 0)355continue;356357memcpy(methdst, methsrc, sizeof(custom_ext_method));358custom_ext_copy_old_cb(methdst, methsrc, &err);359methdst++;360}361362dst->meths_count += meths_count;363}364}365366if (err) {367custom_exts_free(dst);368return 0;369}370371return 1;372}373374void custom_exts_free(custom_ext_methods *exts)375{376size_t i;377custom_ext_method *meth;378379for (i = 0, meth = exts->meths; i < exts->meths_count; i++, meth++) {380if (meth->add_cb != custom_ext_add_old_cb_wrap)381continue;382383/* Old style API wrapper. Need to free the arguments too */384OPENSSL_free(meth->add_arg);385OPENSSL_free(meth->parse_arg);386}387OPENSSL_free(exts->meths);388exts->meths = NULL;389exts->meths_count = 0;390}391392/* Return true if a client custom extension exists, false otherwise */393int SSL_CTX_has_client_custom_ext(const SSL_CTX *ctx, unsigned int ext_type)394{395return custom_ext_find(&ctx->cert->custext, ENDPOINT_CLIENT, ext_type,396NULL)397!= NULL;398}399400int ossl_tls_add_custom_ext_intern(SSL_CTX *ctx, custom_ext_methods *exts,401ENDPOINT role, unsigned int ext_type,402unsigned int context,403SSL_custom_ext_add_cb_ex add_cb,404SSL_custom_ext_free_cb_ex free_cb,405void *add_arg,406SSL_custom_ext_parse_cb_ex parse_cb,407void *parse_arg)408{409custom_ext_method *meth, *tmp;410411/*412* Check application error: if add_cb is not set free_cb will never be413* called.414*/415if (add_cb == NULL && free_cb != NULL)416return 0;417418if (exts == NULL)419exts = &ctx->cert->custext;420421#ifndef OPENSSL_NO_CT422/*423* We don't want applications registering callbacks for SCT extensions424* whilst simultaneously using the built-in SCT validation features, as425* these two things may not play well together.426*/427if (ext_type == TLSEXT_TYPE_signed_certificate_timestamp428&& (context & SSL_EXT_CLIENT_HELLO) != 0429&& ctx != NULL430&& SSL_CTX_ct_is_enabled(ctx))431return 0;432#endif433434/*435* Don't add if extension supported internally, but make exception436* for extension types that previously were not supported, but now are.437*/438if (SSL_extension_supported(ext_type)439&& ext_type != TLSEXT_TYPE_signed_certificate_timestamp)440return 0;441442/* Extension type must fit in 16 bits */443if (ext_type > 0xffff)444return 0;445/* Search for duplicate */446if (custom_ext_find(exts, role, ext_type, NULL))447return 0;448tmp = OPENSSL_realloc(exts->meths,449(exts->meths_count + 1) * sizeof(custom_ext_method));450if (tmp == NULL)451return 0;452453exts->meths = tmp;454meth = exts->meths + exts->meths_count;455memset(meth, 0, sizeof(*meth));456meth->role = role;457meth->context = context;458meth->parse_cb = parse_cb;459meth->add_cb = add_cb;460meth->free_cb = free_cb;461meth->ext_type = ext_type;462meth->ext_flags = (ctx == NULL) ? SSL_EXT_FLAG_CONN : 0;463meth->add_arg = add_arg;464meth->parse_arg = parse_arg;465exts->meths_count++;466return 1;467}468469static int add_old_custom_ext(SSL_CTX *ctx, ENDPOINT role,470unsigned int ext_type,471unsigned int context,472custom_ext_add_cb add_cb,473custom_ext_free_cb free_cb,474void *add_arg,475custom_ext_parse_cb parse_cb, void *parse_arg)476{477custom_ext_add_cb_wrap *add_cb_wrap478= OPENSSL_malloc(sizeof(*add_cb_wrap));479custom_ext_parse_cb_wrap *parse_cb_wrap480= OPENSSL_malloc(sizeof(*parse_cb_wrap));481int ret;482483if (add_cb_wrap == NULL || parse_cb_wrap == NULL) {484OPENSSL_free(add_cb_wrap);485OPENSSL_free(parse_cb_wrap);486return 0;487}488489add_cb_wrap->add_arg = add_arg;490add_cb_wrap->add_cb = add_cb;491add_cb_wrap->free_cb = free_cb;492parse_cb_wrap->parse_arg = parse_arg;493parse_cb_wrap->parse_cb = parse_cb;494495ret = ossl_tls_add_custom_ext_intern(ctx, NULL, role, ext_type,496context,497custom_ext_add_old_cb_wrap,498custom_ext_free_old_cb_wrap,499add_cb_wrap,500custom_ext_parse_old_cb_wrap,501parse_cb_wrap);502503if (!ret) {504OPENSSL_free(add_cb_wrap);505OPENSSL_free(parse_cb_wrap);506}507508return ret;509}510511/* Application level functions to add the old custom extension callbacks */512int SSL_CTX_add_client_custom_ext(SSL_CTX *ctx, unsigned int ext_type,513custom_ext_add_cb add_cb,514custom_ext_free_cb free_cb,515void *add_arg,516custom_ext_parse_cb parse_cb, void *parse_arg)517{518return add_old_custom_ext(ctx, ENDPOINT_CLIENT, ext_type,519SSL_EXT_TLS1_2_AND_BELOW_ONLY520| SSL_EXT_CLIENT_HELLO521| SSL_EXT_TLS1_2_SERVER_HELLO522| SSL_EXT_IGNORE_ON_RESUMPTION,523add_cb, free_cb, add_arg, parse_cb, parse_arg);524}525526int SSL_CTX_add_server_custom_ext(SSL_CTX *ctx, unsigned int ext_type,527custom_ext_add_cb add_cb,528custom_ext_free_cb free_cb,529void *add_arg,530custom_ext_parse_cb parse_cb, void *parse_arg)531{532return add_old_custom_ext(ctx, ENDPOINT_SERVER, ext_type,533SSL_EXT_TLS1_2_AND_BELOW_ONLY534| SSL_EXT_CLIENT_HELLO535| SSL_EXT_TLS1_2_SERVER_HELLO536| SSL_EXT_IGNORE_ON_RESUMPTION,537add_cb, free_cb, add_arg, parse_cb, parse_arg);538}539540int SSL_CTX_add_custom_ext(SSL_CTX *ctx, unsigned int ext_type,541unsigned int context,542SSL_custom_ext_add_cb_ex add_cb,543SSL_custom_ext_free_cb_ex free_cb,544void *add_arg,545SSL_custom_ext_parse_cb_ex parse_cb, void *parse_arg)546{547return ossl_tls_add_custom_ext_intern(ctx, NULL, ENDPOINT_BOTH, ext_type,548context, add_cb, free_cb, add_arg,549parse_cb, parse_arg);550}551552int SSL_extension_supported(unsigned int ext_type)553{554switch (ext_type) {555/* Internally supported extensions. */556case TLSEXT_TYPE_application_layer_protocol_negotiation:557case TLSEXT_TYPE_ec_point_formats:558case TLSEXT_TYPE_supported_groups:559case TLSEXT_TYPE_key_share:560#ifndef OPENSSL_NO_NEXTPROTONEG561case TLSEXT_TYPE_next_proto_neg:562#endif563case TLSEXT_TYPE_padding:564case TLSEXT_TYPE_renegotiate:565case TLSEXT_TYPE_max_fragment_length:566case TLSEXT_TYPE_server_name:567case TLSEXT_TYPE_session_ticket:568case TLSEXT_TYPE_signature_algorithms:569#ifndef OPENSSL_NO_SRP570case TLSEXT_TYPE_srp:571#endif572#ifndef OPENSSL_NO_OCSP573case TLSEXT_TYPE_status_request:574#endif575#ifndef OPENSSL_NO_CT576case TLSEXT_TYPE_signed_certificate_timestamp:577#endif578#ifndef OPENSSL_NO_SRTP579case TLSEXT_TYPE_use_srtp:580#endif581case TLSEXT_TYPE_encrypt_then_mac:582case TLSEXT_TYPE_supported_versions:583case TLSEXT_TYPE_extended_master_secret:584case TLSEXT_TYPE_psk_kex_modes:585case TLSEXT_TYPE_cookie:586case TLSEXT_TYPE_early_data:587case TLSEXT_TYPE_certificate_authorities:588case TLSEXT_TYPE_psk:589case TLSEXT_TYPE_post_handshake_auth:590case TLSEXT_TYPE_compress_certificate:591case TLSEXT_TYPE_client_cert_type:592case TLSEXT_TYPE_server_cert_type:593return 1;594default:595return 0;596}597}598599600