openssl/ssl/quic/quic_tls.c
Matt Caswell 2eb91b0ec3 Make the data field for get_record() const
Improves consistency with the QUIC rstream implementation - and improves
the abstraction between the TLS implementation and the abstract record
layer. We should not expect that the TLS implementation should be able to
change the underlying buffer. Future record layers may not expect that.

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/20404)
2023-04-12 11:02:01 +10:00

736 lines
23 KiB
C

/*
* Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (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 "internal/recordmethod.h"
#include "internal/quic_tls.h"
#include "../ssl_local.h"
#define QUIC_TLS_FATAL(rl, ad, err) \
do { \
(rl)->alert = (ad); \
ERR_raise(ERR_LIB_SSL, (err)); \
(rl)->qtls->inerror = 1; \
} while(0)
struct quic_tls_st {
QUIC_TLS_ARGS args;
/*
* Transport parameters which client should send. Buffer lifetime must
* exceed the lifetime of the QUIC_TLS object.
*/
const unsigned char *local_transport_params;
size_t local_transport_params_len;
/* Whether our SSL object for TLS has been configured for use in QUIC */
unsigned int configured : 1;
/* Set if we have hit any error state */
unsigned int inerror : 1;
/* Set if the handshake has completed */
unsigned int complete : 1;
};
struct ossl_record_layer_st {
QUIC_TLS *qtls;
/* Protection level */
int level;
/* Only used for retry flags */
BIO *dummybio;
/* Number of bytes written so far if we are part way through a write */
size_t written;
/* If we are part way through a write, a copy of the template */
OSSL_RECORD_TEMPLATE template;
/*
* If we hit an error, what alert code should be used
*/
int alert;
/* Amount of crypto stream data we have received but not yet released */
size_t recread;
/* Callbacks */
OSSL_FUNC_rlayer_msg_callback_fn *msg_callback;
void *cbarg;
};
static int
quic_new_record_layer(OSSL_LIB_CTX *libctx, const char *propq, int vers,
int role, int direction, int level, uint16_t epoch,
unsigned char *secret, size_t secretlen,
unsigned char *key, size_t keylen, unsigned char *iv,
size_t ivlen, unsigned char *mackey, size_t mackeylen,
const EVP_CIPHER *ciph, size_t taglen,
int mactype,
const EVP_MD *md, COMP_METHOD *comp,
const EVP_MD *kdfdigest, BIO *prev, BIO *transport,
BIO *next, BIO_ADDR *local, BIO_ADDR *peer,
const OSSL_PARAM *settings, const OSSL_PARAM *options,
const OSSL_DISPATCH *fns, void *cbarg, void *rlarg,
OSSL_RECORD_LAYER **retrl)
{
OSSL_RECORD_LAYER *rl = OPENSSL_zalloc(sizeof(*rl));
uint32_t enc_level;
int qdir;
uint32_t suite_id = 0;
if (rl == NULL) {
QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
rl->qtls = (QUIC_TLS *)rlarg;
rl->level = level;
rl->dummybio = transport;
rl->cbarg = cbarg;
*retrl = rl;
if (fns != NULL) {
for (; fns->function_id != 0; fns++) {
switch (fns->function_id) {
break;
case OSSL_FUNC_RLAYER_MSG_CALLBACK:
rl->msg_callback = OSSL_FUNC_rlayer_msg_callback(fns);
break;
default:
/* Just ignore anything we don't understand */
break;
}
}
}
switch (level) {
case OSSL_RECORD_PROTECTION_LEVEL_NONE:
return 1;
case OSSL_RECORD_PROTECTION_LEVEL_EARLY:
enc_level = QUIC_ENC_LEVEL_0RTT;
break;
case OSSL_RECORD_PROTECTION_LEVEL_HANDSHAKE:
enc_level = QUIC_ENC_LEVEL_HANDSHAKE;
break;
case OSSL_RECORD_PROTECTION_LEVEL_APPLICATION:
enc_level = QUIC_ENC_LEVEL_1RTT;
break;
default:
QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
if (direction == OSSL_RECORD_DIRECTION_READ)
qdir = 0;
else
qdir = 1;
if (EVP_CIPHER_is_a(ciph, "AES-128-GCM")) {
suite_id = QRL_SUITE_AES128GCM;
} else if (EVP_CIPHER_is_a(ciph, "AES-256-GCM")) {
suite_id = QRL_SUITE_AES256GCM;
} else if (EVP_CIPHER_is_a(ciph, "CHACHA20-POLY1305")) {
suite_id = QRL_SUITE_CHACHA20POLY1305;
} else {
QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, SSL_R_UNKNOWN_CIPHER_TYPE);
goto err;
}
/* We pass a ref to the md in a successful yield_secret_cb call */
/* TODO(QUIC): This cast is horrible. We should try and remove it */
if (!EVP_MD_up_ref((EVP_MD *)kdfdigest)) {
QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto err;
}
if (!rl->qtls->args.yield_secret_cb(enc_level, qdir, suite_id,
(EVP_MD *)kdfdigest, secret, secretlen,
rl->qtls->args.yield_secret_cb_arg)) {
QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
EVP_MD_free((EVP_MD *)kdfdigest);
goto err;
}
return 1;
err:
*retrl = NULL;
OPENSSL_free(rl);
return 0;
}
static int quic_free(OSSL_RECORD_LAYER *rl)
{
if (rl == NULL)
return 1;
OPENSSL_free(rl);
return 1;
}
static int quic_unprocessed_read_pending(OSSL_RECORD_LAYER *rl)
{
/*
* Read ahead isn't really a thing for QUIC so we never have unprocessed
* data pending
*/
return 0;
}
static int quic_processed_read_pending(OSSL_RECORD_LAYER *rl)
{
/*
* This is currently only ever used by:
* - SSL_has_pending()
* - to check whether we have more records that we want to supply to the
* upper layers
*
* We only ever supply 1 record at a time to the upper layers, and
* SSL_has_pending() will go via the QUIC method not the TLS method so that
* use case doesn't apply here.
* Therefore we can ignore this for now and always return 0. We might
* eventually want to change this to check in the receive buffers to see if
* we have any more data pending.
*/
return 0;
}
static size_t quic_get_max_records(OSSL_RECORD_LAYER *rl, int type, size_t len,
size_t maxfrag, size_t *preffrag)
{
return 1;
}
static int quic_write_records(OSSL_RECORD_LAYER *rl,
OSSL_RECORD_TEMPLATE *template,
size_t numtempl)
{
size_t consumed;
unsigned char alert;
if (!ossl_assert(numtempl == 1)) {
/* How could this be? quic_get_max_records() always returns 1 */
QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return OSSL_RECORD_RETURN_FATAL;
}
BIO_clear_retry_flags(rl->dummybio);
if (rl->msg_callback != NULL) {
unsigned char dummyrec[SSL3_RT_HEADER_LENGTH];
/*
* For the purposes of the callback we "pretend" to be normal TLS,
* and manufacture a dummy record header
*/
dummyrec[0] = (rl->level == OSSL_RECORD_PROTECTION_LEVEL_NONE)
? template->type
: SSL3_RT_APPLICATION_DATA;
dummyrec[1] = (unsigned char)((template->version >> 8) & 0xff);
dummyrec[2] = (unsigned char)(template->version & 0xff);
/*
* We assume that buflen is always <= UINT16_MAX. Since this is
* generated by libssl itself we actually expect it to never
* exceed SSL3_RT_MAX_PLAIN_LENGTH - so it should be a safe
* assumption
*/
dummyrec[3] = (unsigned char)((template->buflen >> 8) & 0xff);
dummyrec[4] = (unsigned char)(template->buflen & 0xff);
rl->msg_callback(1, TLS1_3_VERSION, SSL3_RT_HEADER, dummyrec,
SSL3_RT_HEADER_LENGTH, rl->cbarg);
if (rl->level != OSSL_RECORD_PROTECTION_LEVEL_NONE) {
rl->msg_callback(1, TLS1_3_VERSION, SSL3_RT_INNER_CONTENT_TYPE,
&template->type, 1, rl->cbarg);
}
}
switch (template->type) {
case SSL3_RT_ALERT:
if (template->buflen != 2) {
/*
* We assume that libssl always sends both bytes of an alert to
* us in one go, and never fragments it. If we ever get more
* or less bytes than exactly 2 then this is very unexpected.
*/
QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_VALUE);
return OSSL_RECORD_RETURN_FATAL;
}
/*
* Byte 0 is the alert level (we ignore it) and byte 1 is the alert
* description that we are actually interested in.
*/
alert = template->buf[1];
if (!rl->qtls->args.alert_cb(rl->qtls->args.alert_cb_arg, alert)) {
QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return OSSL_RECORD_RETURN_FATAL;
}
break;
case SSL3_RT_HANDSHAKE:
/*
* We expect this to only fail on some fatal error (e.g. malloc
* failure)
*/
if (!rl->qtls->args.crypto_send_cb(template->buf + rl->written,
template->buflen - rl->written,
&consumed,
rl->qtls->args.crypto_send_cb_arg)) {
QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return OSSL_RECORD_RETURN_FATAL;
}
/*
* We might have written less than we wanted to if we have filled the
* send stream buffer.
*/
if (consumed + rl->written != template->buflen) {
if (!ossl_assert(consumed + rl->written < template->buflen)) {
QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return OSSL_RECORD_RETURN_FATAL;
}
/*
* We've not written everything we wanted to. Take a copy of the
* template, remember how much we wrote so far and signal a retry.
* The buffer supplied in the template is guaranteed to be the same
* on a retry for handshake data
*/
rl->written += consumed;
rl->template = *template;
BIO_set_retry_write(rl->dummybio);
return OSSL_RECORD_RETURN_RETRY;
}
rl->written = 0;
break;
default:
/* Anything else is unexpected and an error */
QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return OSSL_RECORD_RETURN_FATAL;
}
return OSSL_RECORD_RETURN_SUCCESS;
}
static int quic_retry_write_records(OSSL_RECORD_LAYER *rl)
{
return quic_write_records(rl, &rl->template, 1);
}
static int quic_read_record(OSSL_RECORD_LAYER *rl, void **rechandle,
int *rversion, int *type, const unsigned char **data,
size_t *datalen, uint16_t *epoch,
unsigned char *seq_num)
{
if (rl->recread != 0)
return OSSL_RECORD_RETURN_FATAL;
BIO_clear_retry_flags(rl->dummybio);
if (!rl->qtls->args.crypto_recv_rcd_cb(data, datalen,
rl->qtls->args.crypto_recv_rcd_cb_arg)) {
QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return OSSL_RECORD_RETURN_FATAL;
}
if (*datalen == 0) {
BIO_set_retry_read(rl->dummybio);
return OSSL_RECORD_RETURN_RETRY;
}
*rechandle = rl;
*rversion = TLS1_3_VERSION;
*type = SSL3_RT_HANDSHAKE;
rl->recread = *datalen;
/* epoch/seq_num are not relevant for TLS */
if (rl->msg_callback != NULL) {
unsigned char dummyrec[SSL3_RT_HEADER_LENGTH];
/*
* For the purposes of the callback we "pretend" to be normal TLS,
* and manufacture a dummy record header
*/
dummyrec[0] = (rl->level == OSSL_RECORD_PROTECTION_LEVEL_NONE)
? SSL3_RT_HANDSHAKE
: SSL3_RT_APPLICATION_DATA;
dummyrec[1] = (unsigned char)((TLS1_2_VERSION >> 8) & 0xff);
dummyrec[2] = (unsigned char)(TLS1_2_VERSION & 0xff);
/*
* *datalen will always fit into 2 bytes because our original buffer
* size is less than that.
*/
dummyrec[3] = (unsigned char)((*datalen >> 8) & 0xff);
dummyrec[4] = (unsigned char)(*datalen & 0xff);
rl->msg_callback(0, TLS1_3_VERSION, SSL3_RT_HEADER, dummyrec,
SSL3_RT_HEADER_LENGTH, rl->cbarg);
rl->msg_callback(0, TLS1_3_VERSION, SSL3_RT_INNER_CONTENT_TYPE, type, 1,
rl->cbarg);
}
return OSSL_RECORD_RETURN_SUCCESS;
}
static int quic_release_record(OSSL_RECORD_LAYER *rl, void *rechandle)
{
if (!ossl_assert(rl->recread > 0) || !ossl_assert(rl == rechandle)) {
QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
if (!rl->qtls->args.crypto_release_rcd_cb(rl->recread,
rl->qtls->args.crypto_release_rcd_cb_arg)) {
QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return OSSL_RECORD_RETURN_FATAL;
}
rl->recread = 0;
return 1;
}
static int quic_get_alert_code(OSSL_RECORD_LAYER *rl)
{
return rl->alert;
}
static int quic_set_protocol_version(OSSL_RECORD_LAYER *rl, int version)
{
/* We only support TLSv1.3, so its bad if we negotiate anything else */
if (!ossl_assert(version == TLS1_3_VERSION)) {
QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
return 1;
}
static void quic_set_plain_alerts(OSSL_RECORD_LAYER *rl, int allow)
{
/* We don't care */
}
static void quic_set_first_handshake(OSSL_RECORD_LAYER *rl, int first)
{
/* We don't care */
}
static void quic_set_max_pipelines(OSSL_RECORD_LAYER *rl, size_t max_pipelines)
{
/* We don't care */
}
static void quic_get_state(OSSL_RECORD_LAYER *rl, const char **shortstr,
const char **longstr)
{
/*
* According to the docs, valid read state strings are: "RH"/"read header",
* "RB"/"read body", "RD"/"read done" and "unknown"/"unknown". We don't
* read records in quite that way, so we report every "normal" state as
* "read done". In the event of error then we report "unknown".
*/
if (rl->qtls->inerror) {
if (shortstr != NULL)
*shortstr = "unknown";
if (longstr != NULL)
*longstr = "unknown";
} else {
if (shortstr != NULL)
*shortstr = "RD";
if (longstr != NULL)
*longstr = "read done";
}
}
static int quic_set_options(OSSL_RECORD_LAYER *rl, const OSSL_PARAM *options)
{
/*
* We don't support any options yet - but we might do at some point so
* this could be useful.
*/
return 1;
}
static const COMP_METHOD *quic_get_compression(OSSL_RECORD_LAYER *rl)
{
/* We only support TLSv1.3 which doesn't have compression */
return NULL;
}
static void quic_set_max_frag_len(OSSL_RECORD_LAYER *rl, size_t max_frag_len)
{
/* This really doesn't make any sense for QUIC. Ignore it */
}
static int quic_alloc_buffers(OSSL_RECORD_LAYER *rl)
{
/*
* This is a hint only. We don't support it (yet), so just ignore the
* request
*/
return 1;
}
static int quic_free_buffers(OSSL_RECORD_LAYER *rl)
{
/*
* This is a hint only. We don't support it (yet), so just ignore the
* request
*/
return 1;
}
static int quic_set1_bio(OSSL_RECORD_LAYER *rl, BIO *bio)
{
/*
* Can be called to set the buffering BIO - which is then never used by us.
* We ignore it
*/
return 1;
}
/*
* Never called functions
*
* Due to the way we are configured and used we never expect any of the next set
* of functions to be called. Therefore we set them to always fail.
*/
static size_t quic_app_data_pending(OSSL_RECORD_LAYER *rl)
{
QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return (size_t)ossl_assert(0);
}
static size_t quic_get_max_record_overhead(OSSL_RECORD_LAYER *rl)
{
QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return (size_t)ossl_assert(0);
}
static int quic_increment_sequence_ctr(OSSL_RECORD_LAYER *rl)
{
QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return ossl_assert(0);
}
/* End of never called functions */
static const OSSL_RECORD_METHOD quic_tls_record_method = {
quic_new_record_layer,
quic_free,
quic_unprocessed_read_pending,
quic_processed_read_pending,
quic_app_data_pending, /* Never called */
quic_get_max_records,
quic_write_records,
quic_retry_write_records,
quic_read_record,
quic_release_record,
quic_get_alert_code,
quic_set1_bio,
quic_set_protocol_version,
quic_set_plain_alerts,
quic_set_first_handshake,
quic_set_max_pipelines,
NULL, /* set_in_init: Optional - we don't need it */
quic_get_state,
quic_set_options,
quic_get_compression,
quic_set_max_frag_len,
quic_get_max_record_overhead, /* Never called */
quic_increment_sequence_ctr, /* Never called */
quic_alloc_buffers,
quic_free_buffers
};
static int add_transport_params_cb(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)
{
QUIC_TLS *qtls = add_arg;
*out = qtls->local_transport_params;
*outlen = qtls->local_transport_params_len;
return 1;
}
static void free_transport_params_cb(SSL *s, unsigned int ext_type,
unsigned int context,
const unsigned char *out,
void *add_arg)
{
}
static int parse_transport_params_cb(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)
{
QUIC_TLS *qtls = parse_arg;
return qtls->args.got_transport_params_cb(in, inlen,
qtls->args.got_transport_params_cb_arg);
}
QUIC_TLS *ossl_quic_tls_new(const QUIC_TLS_ARGS *args)
{
QUIC_TLS *qtls;
if (args->crypto_send_cb == NULL
|| args->crypto_recv_rcd_cb == NULL
|| args->crypto_release_rcd_cb == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
qtls = OPENSSL_zalloc(sizeof(*qtls));
if (qtls == NULL)
return NULL;
qtls->args = *args;
return qtls;
}
void ossl_quic_tls_free(QUIC_TLS *qtls)
{
OPENSSL_free(qtls);
}
int ossl_quic_tls_tick(QUIC_TLS *qtls)
{
int ret;
const unsigned char *alpn;
unsigned int alpnlen;
/*
* TODO(QUIC): There are various calls here that could fail and ordinarily
* would result in an ERR_raise call - but "tick" calls aren't supposed to
* fail "loudly" - so its unclear how we will report these errors. The
* ERR_raise calls are omitted from this function for now.
*/
if (qtls->inerror)
return 0;
if (qtls->complete)
return 1;
if (!qtls->configured) {
SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(qtls->args.s);
SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(sc);
BIO *nullbio;
/*
* No matter how the user has configured us, there are certain
* requirements for QUIC-TLS that we enforce
*/
/* ALPN is a requirement for QUIC and must be set */
if (qtls->args.is_server) {
if (sctx->ext.alpn_select_cb == NULL) {
qtls->inerror = 1;
return 0;
}
} else {
if (sc->ext.alpn == NULL || sc->ext.alpn_len == 0) {
qtls->inerror = 1;
return 0;
}
}
if (!SSL_set_min_proto_version(qtls->args.s, TLS1_3_VERSION)) {
qtls->inerror = 1;
return 0;
}
SSL_clear_options(qtls->args.s, SSL_OP_ENABLE_MIDDLEBOX_COMPAT);
ossl_ssl_set_custom_record_layer(sc, &quic_tls_record_method, qtls);
if (!ossl_tls_add_custom_ext_intern(NULL, &sc->cert->custext,
qtls->args.is_server ? ENDPOINT_SERVER
: ENDPOINT_CLIENT,
TLSEXT_TYPE_quic_transport_parameters,
SSL_EXT_TLS1_3_ONLY
| SSL_EXT_CLIENT_HELLO
| SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
add_transport_params_cb,
free_transport_params_cb, qtls,
parse_transport_params_cb, qtls)) {
qtls->inerror = 1;
return 0;
}
nullbio = BIO_new(BIO_s_null());
if (nullbio == NULL) {
qtls->inerror = 1;
return 0;
}
/*
* Our custom record layer doesn't use the BIO - but libssl generally
* expects one to be present.
*/
SSL_set_bio(qtls->args.s, nullbio, nullbio);
if (qtls->args.is_server) {
SSL_set_accept_state(qtls->args.s);
if (!SSL_set_num_tickets(qtls->args.s, 0)) {
qtls->inerror = 1;
return 0;
}
} else {
SSL_set_connect_state(qtls->args.s);
}
qtls->configured = 1;
}
ret = SSL_do_handshake(qtls->args.s);
if (ret <= 0) {
switch (SSL_get_error(qtls->args.s, ret)) {
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
return 1;
default:
qtls->inerror = 1;
return 0;
}
}
/* Validate that we have ALPN */
SSL_get0_alpn_selected(qtls->args.s, &alpn, &alpnlen);
if (alpn == NULL || alpnlen == 0) {
qtls->inerror = 1;
return 0;
}
qtls->complete = 1;
return qtls->args.handshake_complete_cb(qtls->args.handshake_complete_cb_arg);
}
int ossl_quic_tls_set_transport_params(QUIC_TLS *qtls,
const unsigned char *transport_params,
size_t transport_params_len)
{
qtls->local_transport_params = transport_params;
qtls->local_transport_params_len = transport_params_len;
return 1;
}