#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;
}
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;
}
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;
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;
OSSL_HPKE_SUITE hpke_suite_in = OSSL_HPKE_SUITE_DEFAULT;
s = SSL_CONNECTION_FROM_SSL(ssl);
if (s == NULL)
return 0;
if (OSSL_HPKE_str2suite(suite, &hpke_suite_in) != 1) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
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.grease = OSSL_ECH_IS_GREASE;
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.grease = OSSL_ECH_IS_GREASE;
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
|| s->ext.ech.returned_len > INT_MAX) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
goto err;
}
if (s->ext.ech.returned == NULL) {
*ec = NULL;
*eclen = 0;
return 1;
}
if (SSL_is_init_finished(ssl) != 1 && s->ext.ech.retry_configs_ok != 1) {
ERR_raise(ERR_LIB_SSL, ERR_R_OSSL_STORE_LIB);
goto err;
}
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, (int)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;
}
*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;
}
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_INVALID_ARGUMENT);
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;
}
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 || ecl_len > INT_MAX) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_INVALID_ARGUMENT);
goto err;
}
if ((es_in = BIO_new_mem_buf(ecl, (int)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;
}