mirror of https://github.com/openssl/openssl.git
433 lines
13 KiB
C
433 lines
13 KiB
C
/*
|
|
* Copyright 2024 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
|
|
*/
|
|
|
|
#include <openssl/ssl.h>
|
|
#include <openssl/ech.h>
|
|
#include "internal/ssl_unwrap.h"
|
|
#include "../ssl_local.h"
|
|
|
|
int SSL_CTX_set1_echstore(SSL_CTX *ctx, OSSL_ECHSTORE *es)
|
|
{
|
|
if (ctx == NULL) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
|
|
return 0;
|
|
}
|
|
OSSL_ECHSTORE_free(ctx->ext.ech.es);
|
|
ctx->ext.ech.es = NULL;
|
|
if (es == NULL)
|
|
return 1;
|
|
if ((ctx->ext.ech.es = ossl_echstore_dup(es)) == NULL) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int SSL_set1_echstore(SSL *ssl, OSSL_ECHSTORE *es)
|
|
{
|
|
SSL_CONNECTION *s;
|
|
|
|
s = SSL_CONNECTION_FROM_SSL(ssl);
|
|
if (s == NULL)
|
|
return 0;
|
|
OSSL_ECHSTORE_free(s->ext.ech.es);
|
|
s->ext.ech.es = NULL;
|
|
if (es == NULL)
|
|
return 1;
|
|
if ((s->ext.ech.es = ossl_echstore_dup(es)) == NULL) {
|
|
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
|
|
return 0;
|
|
}
|
|
/*
|
|
* Here, and below, if the application calls an API that implies it
|
|
* wants to try ECH, then we set attempted to 1
|
|
*/
|
|
s->ext.ech.attempted = 1;
|
|
return 1;
|
|
}
|
|
|
|
OSSL_ECHSTORE *SSL_CTX_get1_echstore(const SSL_CTX *ctx)
|
|
{
|
|
OSSL_ECHSTORE *dup = NULL;
|
|
|
|
if (ctx == NULL) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
|
|
return NULL;
|
|
}
|
|
if (ctx->ext.ech.es == NULL)
|
|
return NULL;
|
|
if ((dup = ossl_echstore_dup(ctx->ext.ech.es)) == NULL) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
|
|
return NULL;
|
|
}
|
|
return dup;
|
|
}
|
|
|
|
OSSL_ECHSTORE *SSL_get1_echstore(const SSL *ssl)
|
|
{
|
|
SSL_CONNECTION *s;
|
|
OSSL_ECHSTORE *dup = NULL;
|
|
|
|
s = SSL_CONNECTION_FROM_SSL(ssl);
|
|
if (s == NULL) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
|
|
return NULL;
|
|
}
|
|
if (s->ext.ech.es == NULL)
|
|
return NULL;
|
|
if ((dup = ossl_echstore_dup(s->ext.ech.es)) == NULL) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
|
|
return NULL;
|
|
}
|
|
return dup;
|
|
}
|
|
|
|
int SSL_ech_set1_server_names(SSL *ssl, const char *inner_name,
|
|
const char *outer_name, int no_outer)
|
|
{
|
|
SSL_CONNECTION *s;
|
|
|
|
s = SSL_CONNECTION_FROM_SSL(ssl);
|
|
if (s == NULL)
|
|
return 0;
|
|
OPENSSL_free(s->ext.hostname);
|
|
s->ext.hostname = NULL;
|
|
if (inner_name != NULL) {
|
|
s->ext.hostname = OPENSSL_strdup(inner_name);
|
|
if (s->ext.hostname == NULL)
|
|
return 0;
|
|
}
|
|
OPENSSL_free(s->ext.ech.outer_hostname);
|
|
s->ext.ech.outer_hostname = NULL;
|
|
if (no_outer == 0 && outer_name != NULL && strlen(outer_name) > 0) {
|
|
s->ext.ech.outer_hostname = OPENSSL_strdup(outer_name);
|
|
if (s->ext.ech.outer_hostname == NULL)
|
|
return 0;
|
|
}
|
|
s->ext.ech.no_outer = no_outer;
|
|
s->ext.ech.attempted = 1;
|
|
return 1;
|
|
}
|
|
|
|
int SSL_ech_set1_outer_server_name(SSL *ssl, const char *outer_name,
|
|
int no_outer)
|
|
{
|
|
SSL_CONNECTION *s;
|
|
|
|
s = SSL_CONNECTION_FROM_SSL(ssl);
|
|
if (s == NULL)
|
|
return 0;
|
|
OPENSSL_free(s->ext.ech.outer_hostname);
|
|
s->ext.ech.outer_hostname = NULL;
|
|
if (no_outer == 0 && outer_name != NULL && strlen(outer_name) > 0) {
|
|
s->ext.ech.outer_hostname = OPENSSL_strdup(outer_name);
|
|
if (s->ext.ech.outer_hostname == NULL)
|
|
return 0;
|
|
}
|
|
s->ext.ech.no_outer = no_outer;
|
|
s->ext.ech.attempted = 1;
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Note that this function returns 1 for success and 0 for error. This
|
|
* contrasts with SSL_set1_alpn_protos() which (unusually for OpenSSL)
|
|
* returns 0 for success and 1 on error.
|
|
*/
|
|
int SSL_ech_set1_outer_alpn_protos(SSL *ssl, const unsigned char *protos,
|
|
const size_t protos_len)
|
|
{
|
|
SSL_CONNECTION *s;
|
|
|
|
s = SSL_CONNECTION_FROM_SSL(ssl);
|
|
if (s == NULL)
|
|
return 0;
|
|
OPENSSL_free(s->ext.ech.alpn_outer);
|
|
s->ext.ech.alpn_outer = NULL;
|
|
if (protos == NULL)
|
|
return 1;
|
|
if (protos_len == 0) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
|
|
return 0;
|
|
}
|
|
s->ext.ech.alpn_outer = OPENSSL_memdup(protos, protos_len);
|
|
if (s->ext.ech.alpn_outer == NULL)
|
|
return 0;
|
|
s->ext.ech.alpn_outer_len = protos_len;
|
|
s->ext.ech.attempted = 1;
|
|
return 1;
|
|
}
|
|
|
|
int SSL_ech_get1_status(SSL *ssl, char **inner_sni, char **outer_sni)
|
|
{
|
|
char *sinner = NULL;
|
|
char *souter = NULL;
|
|
SSL_CONNECTION *s = SSL_CONNECTION_FROM_SSL(ssl);
|
|
|
|
if (s == NULL) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
|
|
return SSL_ECH_STATUS_FAILED;
|
|
}
|
|
if (outer_sni == NULL || inner_sni == NULL) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
|
|
return SSL_ECH_STATUS_FAILED;
|
|
}
|
|
*outer_sni = NULL;
|
|
*inner_sni = NULL;
|
|
if (s->ext.ech.grease == OSSL_ECH_IS_GREASE) {
|
|
if (s->ext.ech.returned != NULL)
|
|
return SSL_ECH_STATUS_GREASE_ECH;
|
|
return SSL_ECH_STATUS_GREASE;
|
|
}
|
|
if ((s->options & SSL_OP_ECH_GREASE) != 0 && s->ext.ech.attempted != 1)
|
|
return SSL_ECH_STATUS_GREASE;
|
|
if (s->ext.ech.backend == 1) {
|
|
if (s->ext.hostname != NULL
|
|
&& (*inner_sni = OPENSSL_strdup(s->ext.hostname)) == NULL) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
|
|
return SSL_ECH_STATUS_FAILED;
|
|
}
|
|
return SSL_ECH_STATUS_BACKEND;
|
|
}
|
|
if (s->ext.ech.es == NULL)
|
|
return SSL_ECH_STATUS_NOT_CONFIGURED;
|
|
/* Set output vars - note we may be pointing to NULL which is fine */
|
|
if (s->server == 0) {
|
|
sinner = s->ext.hostname;
|
|
if (s->ext.ech.attempted == 1 && s->ext.ech.success == 0)
|
|
sinner = s->ext.ech.former_inner;
|
|
if (s->ext.ech.no_outer == 0)
|
|
souter = s->ext.ech.outer_hostname;
|
|
else
|
|
souter = NULL;
|
|
} else {
|
|
if (s->ext.ech.es != NULL && s->ext.ech.success == 1) {
|
|
sinner = s->ext.hostname;
|
|
souter = s->ext.ech.outer_hostname;
|
|
}
|
|
}
|
|
if (s->ext.ech.es != NULL && s->ext.ech.attempted == 1
|
|
&& s->ext.ech.attempted_type == TLSEXT_TYPE_ech
|
|
&& s->ext.ech.grease != OSSL_ECH_IS_GREASE) {
|
|
long vr = X509_V_OK;
|
|
|
|
vr = SSL_get_verify_result(ssl);
|
|
if (sinner != NULL
|
|
&& (*inner_sni = OPENSSL_strdup(sinner)) == NULL) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
|
|
return SSL_ECH_STATUS_FAILED;
|
|
}
|
|
if (souter != NULL
|
|
&& (*outer_sni = OPENSSL_strdup(souter)) == NULL) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
|
|
return SSL_ECH_STATUS_FAILED;
|
|
}
|
|
if (s->ext.ech.success == 1) {
|
|
if (vr == X509_V_OK)
|
|
return SSL_ECH_STATUS_SUCCESS;
|
|
else
|
|
return SSL_ECH_STATUS_BAD_NAME;
|
|
} else {
|
|
if (vr == X509_V_OK && s->ext.ech.returned != NULL)
|
|
return SSL_ECH_STATUS_FAILED_ECH;
|
|
else if (vr != X509_V_OK && s->ext.ech.returned != NULL)
|
|
return SSL_ECH_STATUS_FAILED_ECH_BAD_NAME;
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
|
|
return SSL_ECH_STATUS_FAILED;
|
|
}
|
|
}
|
|
return SSL_ECH_STATUS_NOT_TRIED;
|
|
}
|
|
|
|
int SSL_ech_set1_grease_suite(SSL *ssl, const char *suite)
|
|
{
|
|
SSL_CONNECTION *s;
|
|
|
|
s = SSL_CONNECTION_FROM_SSL(ssl);
|
|
if (s == NULL)
|
|
return 0;
|
|
OPENSSL_free(s->ext.ech.grease_suite);
|
|
s->ext.ech.grease_suite = NULL;
|
|
if (suite == NULL)
|
|
return 1;
|
|
s->ext.ech.grease_suite = OPENSSL_strdup(suite);
|
|
if (s->ext.ech.grease_suite == NULL)
|
|
return 0;
|
|
s->ext.ech.attempted = 1;
|
|
return 1;
|
|
}
|
|
|
|
int SSL_ech_set_grease_type(SSL *ssl, uint16_t type)
|
|
{
|
|
SSL_CONNECTION *s;
|
|
|
|
s = SSL_CONNECTION_FROM_SSL(ssl);
|
|
if (s == NULL)
|
|
return 0;
|
|
s->ext.ech.attempted_type = type;
|
|
s->ext.ech.attempted = 1;
|
|
return 1;
|
|
}
|
|
|
|
void SSL_ech_set_callback(SSL *ssl, SSL_ech_cb_func f)
|
|
{
|
|
SSL_CONNECTION *s;
|
|
|
|
s = SSL_CONNECTION_FROM_SSL(ssl);
|
|
if (s == NULL)
|
|
return;
|
|
s->ext.ech.cb = f;
|
|
return;
|
|
}
|
|
|
|
int SSL_ech_get1_retry_config(SSL *ssl, unsigned char **ec, size_t *eclen)
|
|
{
|
|
SSL_CONNECTION *s;
|
|
OSSL_ECHSTORE *ve = NULL;
|
|
BIO *in = NULL;
|
|
int rv = 0;
|
|
OSSL_LIB_CTX *libctx = NULL;
|
|
const char *propq = NULL;
|
|
|
|
s = SSL_CONNECTION_FROM_SSL(ssl);
|
|
if (s == NULL || ec == NULL || eclen == NULL)
|
|
goto err;
|
|
if (s->ext.ech.returned == NULL) {
|
|
*ec = NULL;
|
|
*eclen = 0;
|
|
return 1;
|
|
}
|
|
/*
|
|
* To not hand rubbish to application, we'll decode the value we have
|
|
* so only syntactically good things are passed up. We won't insist
|
|
* though that every entry in the retry_config list seems good - it
|
|
* could be that e.g. one is a newer version than we support now,
|
|
* and letting the application see that might cause someone to do an
|
|
* upgrade.
|
|
*/
|
|
if (s->ext.ech.es != NULL) {
|
|
libctx = s->ext.ech.es->libctx;
|
|
propq = s->ext.ech.es->propq;
|
|
}
|
|
if ((in = BIO_new(BIO_s_mem())) == NULL
|
|
|| BIO_write(in, s->ext.ech.returned, s->ext.ech.returned_len) <= 0
|
|
|| (ve = OSSL_ECHSTORE_new(libctx, propq)) == NULL) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
|
|
goto err;
|
|
}
|
|
if (OSSL_ECHSTORE_read_echconfiglist(ve, in) != 1) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
|
|
goto err;
|
|
}
|
|
/* all good, copy and return */
|
|
*ec = OPENSSL_memdup(s->ext.ech.returned, s->ext.ech.returned_len);
|
|
if (*ec == NULL)
|
|
goto err;
|
|
*eclen = s->ext.ech.returned_len;
|
|
rv = 1;
|
|
err:
|
|
OSSL_ECHSTORE_free(ve);
|
|
BIO_free_all(in);
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Note that this function returns 1 for success and 0 for error. This
|
|
* contrasts with SSL_CTX_set1_alpn_protos() which (unusually for OpenSSL)
|
|
* returns 0 for success and 1 on error.
|
|
*/
|
|
int SSL_CTX_ech_set1_outer_alpn_protos(SSL_CTX *ctx,
|
|
const unsigned char *protos,
|
|
const size_t protos_len)
|
|
{
|
|
if (ctx == NULL) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
|
|
return 0;
|
|
}
|
|
OPENSSL_free(ctx->ext.ech.alpn_outer);
|
|
ctx->ext.ech.alpn_outer = NULL;
|
|
if (protos == NULL)
|
|
return 1;
|
|
if (protos_len == 0) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
|
|
return 0;
|
|
}
|
|
ctx->ext.ech.alpn_outer = OPENSSL_memdup(protos, protos_len);
|
|
if (ctx->ext.ech.alpn_outer == NULL)
|
|
return 0;
|
|
ctx->ext.ech.alpn_outer_len = protos_len;
|
|
return 1;
|
|
}
|
|
|
|
int SSL_CTX_ech_raw_decrypt(SSL_CTX *ctx,
|
|
int *decrypted_ok,
|
|
char **inner_sni, char **outer_sni,
|
|
unsigned char *outer_ch, size_t outer_len,
|
|
unsigned char *inner_ch, size_t *inner_len,
|
|
unsigned char **hrrtok, size_t *toklen)
|
|
{
|
|
if (ctx == NULL) {
|
|
/*
|
|
* TODO(ECH): this is a bit of a bogus error, just so as
|
|
* to get the `make update` command to add the required
|
|
* error number. We don't need it yet, but it's involved
|
|
* in some of the build artefacts, so may as well jump
|
|
* the gun a bit on it.
|
|
*/
|
|
ERR_raise(ERR_LIB_SSL, SSL_R_ECH_REQUIRED);
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void SSL_CTX_ech_set_callback(SSL_CTX *ctx, SSL_ech_cb_func f)
|
|
{
|
|
if (ctx == NULL) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
|
|
return;
|
|
}
|
|
ctx->ext.ech.cb = f;
|
|
return;
|
|
}
|
|
|
|
int SSL_set1_ech_config_list(SSL *ssl, const uint8_t *ecl, size_t ecl_len)
|
|
{
|
|
int rv = 0;
|
|
SSL_CONNECTION *s;
|
|
OSSL_ECHSTORE *es = NULL;
|
|
BIO *es_in = NULL;
|
|
|
|
s = SSL_CONNECTION_FROM_SSL(ssl);
|
|
if (s == NULL) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
|
|
goto err;
|
|
}
|
|
if (ecl == NULL) {
|
|
OSSL_ECHSTORE_free(s->ext.ech.es);
|
|
s->ext.ech.es = NULL;
|
|
return 1;
|
|
}
|
|
if (ecl_len == 0) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
|
|
goto err;
|
|
}
|
|
if ((es_in = BIO_new_mem_buf(ecl, ecl_len)) == NULL
|
|
|| (es = OSSL_ECHSTORE_new(NULL, NULL)) == NULL
|
|
|| OSSL_ECHSTORE_read_echconfiglist(es, es_in) != 1
|
|
|| SSL_set1_echstore(ssl, es) != 1) {
|
|
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
|
|
goto err;
|
|
}
|
|
rv = 1;
|
|
err:
|
|
OSSL_ECHSTORE_free(es);
|
|
BIO_free_all(es_in);
|
|
return rv;
|
|
}
|