diff options
author | Hugo Landau <hlandau@openssl.org> | 2022-11-17 15:33:11 +0000 |
---|---|---|
committer | Hugo Landau <hlandau@openssl.org> | 2023-01-13 13:20:15 +0000 |
commit | 22d53c88838d5899383af6955ae62ca4094308c3 (patch) | |
tree | 66a1bac4d991e2c4fa986212fdb54c475b63b7ac /ssl | |
parent | 8c94cf38a2a82d8b4f7ebab2e75c2110f16c9e2f (diff) | |
download | openssl-new-22d53c88838d5899383af6955ae62ca4094308c3.tar.gz |
QUIC Front-End I/O API
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/19703)
Diffstat (limited to 'ssl')
-rw-r--r-- | ssl/quic/quic_impl.c | 1236 | ||||
-rw-r--r-- | ssl/quic/quic_local.h | 188 | ||||
-rw-r--r-- | ssl/quic/quic_record_shared.c | 2 |
3 files changed, 1249 insertions, 177 deletions
diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c index 856fa5e188..b6df5f778b 100644 --- a/ssl/quic/quic_impl.c +++ b/ssl/quic/quic_impl.c @@ -9,271 +9,1241 @@ #include <openssl/macros.h> #include <openssl/objects.h> +#include <openssl/sslerr.h> +#include <crypto/rand.h> #include "quic_local.h" +#include "internal/quic_dummy_handshake.h" +#include "internal/quic_rx_depack.h" +#include "internal/quic_error.h" +#include "internal/time.h" +static void aon_write_finish(QUIC_CONNECTION *qc); + +/* + * QUIC Front-End I/O API: Common Utilities + * ======================================== + */ + +/* + * Block until a predicate is met. + * + * Precondition: Must have a channel. + */ +static int block_until_pred(QUIC_CONNECTION *qc, + int (*pred)(void *arg), void *pred_arg, + uint32_t flags) +{ + QUIC_REACTOR *rtor; + + assert(qc->ch != NULL); + + rtor = ossl_quic_channel_get_reactor(qc->ch); + return ossl_quic_reactor_block_until_pred(rtor, pred, pred_arg, flags); +} + +/* + * Raise a 'normal' error, meaning one that can be reported via SSL_get_error() + * rather than via ERR. + */ +static int quic_raise_normal_error(QUIC_CONNECTION *qc, + int err) +{ + qc->last_error = err; + return 0; +} + +/* + * Raise a 'non-normal' error, meaning any error that is not reported via + * SSL_get_error() and must be reported via ERR. + */ +static int quic_raise_non_normal_error(QUIC_CONNECTION *qc, + const char *file, + int line, + const char *func, + int reason, + const char *fmt, + ...) +{ + va_list args; + + ERR_new(); + ERR_set_debug(file, line, func); + + va_start(args, fmt); + ERR_vset_error(ERR_LIB_SSL, reason, fmt, args); + va_end(args); + + qc->last_error = SSL_ERROR_SSL; + return 0; +} + +#define QUIC_RAISE_NORMAL_ERROR(qc, err) \ + quic_raise_normal_error((qc), (err)) + +#define QUIC_RAISE_NON_NORMAL_ERROR(qc, reason, msg) \ + quic_raise_non_normal_error((qc), \ + OPENSSL_FILE, OPENSSL_LINE, \ + OPENSSL_FUNC, \ + (reason), \ + (msg)) + +/* + * Should be called at entry of every public function to confirm we have a valid + * QUIC_CONNECTION. + */ +static ossl_inline int expect_quic_conn(const QUIC_CONNECTION *qc) +{ + if (!ossl_assert(qc != NULL)) + return QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL); + + return 1; + +} + +/* + * QUIC Front-End I/O API: Initialization + * ====================================== + * + * SSL_new => ossl_quic_new + * ossl_quic_init + * SSL_reset => ossl_quic_reset + * SSL_clear => ossl_quic_clear + * ossl_quic_deinit + * SSL_free => ossl_quic_free + * + */ + +/* SSL_new */ SSL *ossl_quic_new(SSL_CTX *ctx) { - QUIC_CONNECTION *qc; - SSL *ssl = NULL; - SSL_CONNECTION *sc; + QUIC_CONNECTION *qc = NULL; + SSL *ssl_base = NULL; qc = OPENSSL_zalloc(sizeof(*qc)); if (qc == NULL) goto err; - ssl = &qc->stream.ssl; - if (!ossl_ssl_init(ssl, ctx, SSL_TYPE_QUIC_CONNECTION)) { - OPENSSL_free(qc); - ssl = NULL; + /* Initialise the QUIC_CONNECTION's stub header. */ + ssl_base = &qc->ssl; + if (!ossl_ssl_init(ssl_base, ctx, SSL_TYPE_QUIC_CONNECTION)) { + ssl_base = NULL; goto err; } - qc->tls = ossl_ssl_connection_new(ctx); - if (qc->tls == NULL || (sc = SSL_CONNECTION_FROM_SSL(qc->tls)) == NULL) - goto err; - /* override the user_ssl of the inner connection */ - sc->user_ssl = ssl; - /* We'll need to set proper TLS method on qc->tls here */ - return ssl; + /* Channel is not created yet. */ + qc->ssl_mode = qc->ssl.ctx->mode; + qc->last_error = SSL_ERROR_NONE; + qc->blocking = 1; + return ssl_base; + err: - ossl_quic_free(ssl); + OPENSSL_free(qc); return NULL; } +/* SSL_free */ +void ossl_quic_free(SSL *s) +{ + QUIC_CONNECTION *qc = QUIC_CONNECTION_FROM_SSL(s); + + /* We should never be called on anything but a QUIC_CONNECTION. */ + if (!expect_quic_conn(qc)) + return; + + ossl_quic_channel_free(qc->ch); + + /* Note: SSL_free calls OPENSSL_free(qc) for us */ +} + +/* SSL method init */ int ossl_quic_init(SSL *s) { - return s->method->ssl_clear(s); + QUIC_CONNECTION *qc = QUIC_CONNECTION_FROM_SSL(s); + + if (!expect_quic_conn(qc)) + return 0; + + /* Same op as SSL_clear, forward the call. */ + return ossl_quic_clear(s); } +/* SSL method deinit */ void ossl_quic_deinit(SSL *s) { - return; + /* No-op. */ } -void ossl_quic_free(SSL *s) +/* SSL_reset */ +int ossl_quic_reset(SSL *s) +{ + QUIC_CONNECTION *qc = QUIC_CONNECTION_FROM_SSL(s); + + if (!expect_quic_conn(qc)) + return 0; + + /* Currently a no-op. */ + return 1; +} + +/* SSL_clear */ +int ossl_quic_clear(SSL *s) { QUIC_CONNECTION *qc = QUIC_CONNECTION_FROM_SSL(s); - if (qc == NULL) { - /* TODO(QUIC): Temporarily needed to release the inner tls object */ - SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL_ONLY(s); + if (!expect_quic_conn(qc)) + return 0; + + /* Currently a no-op. */ + return 1; +} - if (sc != NULL) - ossl_ssl_connection_free(s); +/* + * QUIC Front-End I/O API: Network BIO Configuration + * ================================================= + * + * Handling the different BIOs is difficult: + * + * - It is more or less a requirement that we use non-blocking network I/O; + * we need to be able to have timeouts on recv() calls, and make best effort + * (non blocking) send() and recv() calls. + * + * The only sensible way to do this is to configure the socket into + * non-blocking mode. We could try to do select() before calling send() or + * recv() to get a guarantee that the call will not block, but this will + * probably run into issues with buggy OSes which generate spurious socket + * readiness events. In any case, relying on this to work reliably does not + * seem sane. + * + * Timeouts could be handled via setsockopt() socket timeout options, but + * this depends on OS support and adds another syscall to every network I/O + * operation. It also has obvious thread safety concerns if we want to move + * to concurrent use of a single socket at some later date. + * + * Some OSes support a MSG_DONTWAIT flag which allows a single I/O option to + * be made non-blocking. However some OSes (e.g. Windows) do not support + * this, so we cannot rely on this. + * + * As such, we need to configure any FD in non-blocking mode. This may + * confound users who pass a blocking socket to libssl. However, in practice + * it would be extremely strange for a user of QUIC to pass an FD to us, + * then also try and send receive traffic on the same socket(!). Thus the + * impact of this should be limited, and can be documented. + * + * - We support both blocking and non-blocking operation in terms of the API + * presented to the user. One prospect is to set the blocking mode based on + * whether the socket passed to us was already in blocking mode. However, + * Windows has no API for determining if a socket is in blocking mode (!), + * therefore this cannot be done portably. Currently therefore we expose an + * explicit API call to set this, and default to blocking mode. + * + * - We need to determine our initial destination UDP address. The "natural" + * way for a user to do this is to set the peer variable on a BIO_dgram. + * However, this has problems because BIO_dgram's peer variable is used for + * both transmission and reception. This means it can be constantly being + * changed to a malicious value (e.g. if some random unrelated entity on the + * network starts sending traffic to us) on every read call. This is not a + * direct issue because we use the 'stateless' BIO_sendmmsg and BIO_recvmmsg + * calls only, which do not use this variable. However, we do need to let + * the user specify the peer in a 'normal' manner. The compromise here is + * that we grab the current peer value set at the time the write BIO is set + * and do not read the value again. + * + * - We also need to support memory BIOs (e.g. BIO_dgram_pair) or custom BIOs. + * Currently we do this by only supporting non-blocking mode. + * + */ + +/* + * Determines what initial destination UDP address we should use, if possible. + * If this fails the client must set the destination address manually, or use a + * BIO which does not need a destination address. + */ +static int csm_analyse_init_peer_addr(BIO *net_wbio, BIO_ADDR *peer) +{ + if (!BIO_dgram_get_peer(net_wbio, peer)) + return 0; + + return 1; +} + +void ossl_quic_conn_set0_net_rbio(QUIC_CONNECTION *qc, BIO *net_rbio) +{ + if (qc->net_rbio == net_rbio) return; - } - SSL_free(qc->tls); - return; + if (qc->ch != NULL && !ossl_quic_channel_set0_net_rbio(qc->ch, net_rbio)) + return; + + qc->net_rbio = net_rbio; + + /* + * If what we have is not pollable (e.g. a BIO_dgram_pair) disable blocking + * mode as we do not support it for non-pollable BIOs. + */ + if (net_rbio != NULL) { + BIO_POLL_DESCRIPTOR d = {0}; + + if (!BIO_get_rpoll_descriptor(net_rbio, &d) + || d.type != BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD) { + qc->blocking = 0; + qc->can_poll_net_rbio = 0; + } else { + qc->can_poll_net_rbio = 1; + } + } } -int ossl_quic_reset(SSL *s) +void ossl_quic_conn_set0_net_wbio(QUIC_CONNECTION *qc, BIO *net_wbio) { - QUIC_CONNECTION *qc = QUIC_CONNECTION_FROM_SSL(s); + if (qc->net_wbio == net_wbio) + return; + + if (qc->ch != NULL && !ossl_quic_channel_set0_net_wbio(qc->ch, net_wbio)) + return; + + qc->net_wbio = net_wbio; + + if (net_wbio != NULL) { + BIO_POLL_DESCRIPTOR d = {0}; - if (qc == NULL) { - /* TODO(QUIC): Temporarily needed to reset the inner tls object */ - SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL_ONLY(s); + if (!BIO_get_wpoll_descriptor(net_wbio, &d) + || d.type != BIO_POLL_DESCRIPTOR_TYPE_SOCK_FD) { + qc->blocking = 0; + qc->can_poll_net_wbio = 0; + } else { + qc->can_poll_net_wbio = 1; + } - return sc != NULL ? ossl_ssl_connection_reset(s) : 0; + /* + * If we do not have a peer address yet, and we have not started trying + * to connect yet, try to autodetect one. + */ + if (BIO_ADDR_family(&qc->init_peer_addr) == AF_UNSPEC + && !qc->started) { + if (!csm_analyse_init_peer_addr(net_wbio, &qc->init_peer_addr)) + /* best effort */ + BIO_ADDR_clear(&qc->init_peer_addr); + + if (qc->ch != NULL) + ossl_quic_channel_set_peer_addr(qc->ch, &qc->init_peer_addr); + } } +} - return ossl_ssl_connection_reset(qc->tls); +BIO *ossl_quic_conn_get_net_rbio(const QUIC_CONNECTION *qc) +{ + return qc->net_rbio; } -int ossl_quic_clear(SSL *s) +BIO *ossl_quic_conn_get_net_wbio(const QUIC_CONNECTION *qc) +{ + return qc->net_wbio; +} + +int ossl_quic_conn_get_blocking_mode(const QUIC_CONNECTION *qc) { + return qc->blocking; +} + +int ossl_quic_conn_set_blocking_mode(QUIC_CONNECTION *qc, int blocking) +{ + /* Cannot enable blocking mode if we do not have pollable FDs. */ + if (blocking != 0 && + (!qc->can_poll_net_rbio || !qc->can_poll_net_wbio)) + return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_UNSUPPORTED, NULL); + + qc->blocking = (blocking != 0); return 1; } -int ossl_quic_accept(SSL *s) +int ossl_quic_conn_set_initial_peer_addr(QUIC_CONNECTION *qc, + const BIO_ADDR *peer_addr) { - SSL_CONNECTION *sc = SSL_CONNECTION_FROM_QUIC_SSL(s); + if (qc->started) + return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED, + NULL); - if (sc == NULL) - return 0; + if (peer_addr == NULL) { + BIO_ADDR_clear(&qc->init_peer_addr); + return 1; + } - ossl_statem_set_in_init(sc, 0); + qc->init_peer_addr = *peer_addr; return 1; } -int ossl_quic_connect(SSL *s) +/* + * QUIC Front-End I/O API: Asynchronous I/O Management + * =================================================== + * + * (BIO/)SSL_tick => ossl_quic_tick + * (BIO/)SSL_get_tick_timeout => ossl_quic_get_tick_timeout + * (BIO/)SSL_get_poll_fd => ossl_quic_get_poll_fd + * + */ + +/* Returns 1 if the connection is being used in blocking mode. */ +static int blocking_mode(const QUIC_CONNECTION *qc) { - SSL_CONNECTION *sc = SSL_CONNECTION_FROM_QUIC_SSL(s); + return qc->blocking; +} - if (sc == NULL) - return 0; +/* SSL_tick; ticks the reactor. */ +int ossl_quic_tick(QUIC_CONNECTION *qc) +{ + if (qc->ch == NULL) + return 1; - ossl_statem_set_in_init(sc, 0); + ossl_quic_reactor_tick(ossl_quic_channel_get_reactor(qc->ch)); return 1; } -int ossl_quic_read(SSL *s, void *buf, size_t len, size_t *readbytes) +/* + * SSL_get_tick_timeout. Get the time in milliseconds until the SSL object + * should be ticked by the application by calling SSL_tick(). tv is set to 0 if + * the object should be ticked immediately and tv->tv_sec is set to -1 if no + * timeout is currently active. + */ +int ossl_quic_get_tick_timeout(QUIC_CONNECTION *qc, struct timeval *tv) { - int ret; - BIO *rbio = SSL_get_rbio(s); - SSL_CONNECTION *sc = SSL_CONNECTION_FROM_QUIC_SSL(s); + OSSL_TIME now, deadline = ossl_time_infinite(); - if (sc == NULL || rbio == NULL) + if (qc->ch != NULL) + deadline + = ossl_quic_reactor_get_tick_deadline(ossl_quic_channel_get_reactor(qc->ch)); + + if (ossl_time_is_infinite(deadline)) { + tv->tv_sec = -1; + tv->tv_usec = 0; + return 1; + } + + now = ossl_time_now(); + if (ossl_time_compare(now, deadline) >= 0) { + tv->tv_sec = 0; + tv->tv_usec = 0; + return 1; + } + + *tv = ossl_time_to_timeval(ossl_time_subtract(deadline, now)); + return 1; +} + +/* SSL_get_rpoll_descriptor */ +int ossl_quic_get_rpoll_descriptor(QUIC_CONNECTION *qc, BIO_POLL_DESCRIPTOR *desc) +{ + if (desc == NULL || qc->net_rbio == NULL) return 0; - sc->rwstate = SSL_READING; - ret = BIO_read_ex(rbio, buf, len, readbytes); - if (ret > 0 || !BIO_should_retry(rbio)) - sc->rwstate = SSL_NOTHING; - return ret <= 0 ? -1 : ret; + return BIO_get_rpoll_descriptor(qc->net_rbio, desc); } -int ossl_quic_peek(SSL *s, void *buf, size_t len, size_t *readbytes) +/* SSL_get_wpoll_descriptor */ +int ossl_quic_get_wpoll_descriptor(QUIC_CONNECTION *qc, BIO_POLL_DESCRIPTOR *desc) { - return -1; + if (desc == NULL || qc->net_wbio == NULL) + return 0; + + return BIO_get_wpoll_descriptor(qc->net_wbio, desc); } -int ossl_quic_write(SSL *s, const void *buf, size_t len, size_t *written) +/* SSL_want_net_read */ +int ossl_quic_get_want_net_read(QUIC_CONNECTION *qc) { - BIO *wbio = SSL_get_wbio(s); - int ret; - SSL_CONNECTION *sc = SSL_CONNECTION_FROM_QUIC_SSL(s); + if (qc->ch == NULL) + return 0; + + return ossl_quic_reactor_want_net_read(ossl_quic_channel_get_reactor(qc->ch)); +} - if (sc == NULL || wbio == NULL) +/* SSL_want_net_write */ +int ossl_quic_get_want_net_write(QUIC_CONNECTION *qc) +{ + if (qc->ch == NULL) return 0; - sc->rwstate = SSL_WRITING; - ret = BIO_write_ex(wbio, buf, len, written); - if (ret > 0 || !BIO_should_retry(wbio)) - sc->rwstate = SSL_NOTHING; - return ret; + return ossl_quic_reactor_want_net_write(ossl_quic_channel_get_reactor(qc->ch)); } +/* + * QUIC Front-End I/O API: Connection Lifecycle Operations + * ======================================================= + * + * SSL_do_handshake => ossl_quic_do_handshake + * SSL_set_connect_state => ossl_quic_set_connect_state + * SSL_set_accept_state => ossl_quic_set_accept_state + * SSL_shutdown => ossl_quic_shutdown + * SSL_ctrl => ossl_quic_ctrl + * (BIO/)SSL_connect => ossl_quic_connect + * (BIO/)SSL_accept => ossl_quic_accept + * + */ + +/* SSL_shutdown */ int ossl_quic_shutdown(SSL *s) { + QUIC_CONNECTION *qc = QUIC_CONNECTION_FROM_SSL(s); + + if (!expect_quic_conn(qc)) + return 0; + + if (qc->ch != NULL) + ossl_quic_channel_local_close(qc->ch); + return 1; } +/* SSL_ctrl */ +static void fixup_mode_change(QUIC_CONNECTION *qc) +{ + /* If enabling EPW mode, cancel any AON write */ + if ((qc->ssl_mode & SSL_MODE_ENABLE_PARTIAL_WRITE) != 0) + aon_write_finish(qc); +} + long ossl_quic_ctrl(SSL *s, int cmd, long larg, void *parg) { - SSL_CONNECTION *sc = SSL_CONNECTION_FROM_QUIC_SSL(s); + QUIC_CONNECTION *qc = QUIC_CONNECTION_FROM_SSL(s); - if (sc == NULL) + if (!expect_quic_conn(qc)) return 0; - switch(cmd) { - case SSL_CTRL_CHAIN: - if (larg) - return ssl_cert_set1_chain(sc, NULL, (STACK_OF(X509) *)parg); - else - return ssl_cert_set0_chain(sc, NULL, (STACK_OF(X509) *)parg); + switch (cmd) { + case SSL_CTRL_MODE: + qc->ssl_mode |= (uint32_t)larg; + fixup_mode_change(qc); + return qc->ssl_mode; + case SSL_CTRL_CLEAR_MODE: + qc->ssl_mode &= ~(uint32_t)larg; + fixup_mode_change(qc); + return qc->ssl_mode; + default: + return 0; } +} + +/* SSL_set_connect_state */ +void ossl_quic_set_connect_state(QUIC_CONNECTION *qc) +{ + /* Cannot be changed after handshake started */ + if (qc->started) + return; + + qc->as_server = 0; +} + +/* SSL_set_accept_state */ +void ossl_quic_set_accept_state(QUIC_CONNECTION *qc) +{ + /* Cannot be changed after handshake started */ + if (qc->started) + return; + + qc->as_server = 1; +} + +/* SSL_do_handshake */ +struct quic_handshake_wait_args { + QUIC_CONNECTION *qc; +}; + +static int quic_handshake_wait(void *arg) +{ + struct quic_handshake_wait_args *args = arg; + + if (!ossl_quic_channel_is_active(args->qc->ch)) + return -1; + + if (ossl_quic_channel_is_handshake_complete(args->qc->ch)) + return 1; + return 0; } -long ossl_quic_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) +static int configure_channel(QUIC_CONNECTION *qc) { - switch(cmd) { - case SSL_CTRL_CHAIN: - if (larg) - return ssl_cert_set1_chain(NULL, ctx, (STACK_OF(X509) *)parg); - else - return ssl_cert_set0_chain(NULL, ctx, (STACK_OF(X509) *)parg); + assert(qc->ch != NULL); - case SSL_CTRL_SET_TLSEXT_TICKET_KEYS: - case SSL_CTRL_GET_TLSEXT_TICKET_KEYS: - /* TODO(QUIC): these will have to be implemented properly */ + if (!ossl_quic_channel_set0_net_rbio(qc->ch, qc->net_rbio) + || !ossl_quic_channel_set0_net_wbio(qc->ch, qc->net_wbio) + || !ossl_quic_channel_set_peer_addr(qc->ch, &qc->init_peer_addr)) + return 0; + + return 1; +} + +/* + * Creates a channel and configures it with the information we have accumulated + * via calls made to us from the application prior to starting a handshake + * attempt. + */ +static int ensure_channel_and_start(QUIC_CONNECTION *qc) +{ + QUIC_CHANNEL_ARGS args = {0}; + + if (qc->ch != NULL) return 1; + + args.libctx = qc->ssl.ctx->libctx; + args.propq = qc->ssl.ctx->propq; + args.is_server = 0; + + qc->ch = ossl_quic_channel_new(&args); + if (qc->ch == NULL) + return 0; + + if (!configure_channel(qc) + || !ossl_quic_channel_start(qc->ch)) { + ossl_quic_channel_free(qc->ch); + qc->ch = NULL; + return 0; } - return 0; + + qc->stream0 = ossl_quic_channel_get_stream_by_id(qc->ch, 0); + if (qc->stream0 == NULL) { + ossl_quic_channel_free(qc->ch); + qc->ch = NULL; + return 0; + } + + qc->started = 1; + return 1; } -long ossl_quic_callback_ctrl(SSL *s, int cmd, void (*fp) (void)) +int ossl_quic_do_handshake(QUIC_CONNECTION *qc) { - return 0; + int ret; + + fprintf(stderr, "# handshake begin\n"); + + if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch)) + return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); + + if (BIO_ADDR_family(&qc->init_peer_addr) == AF_UNSPEC) + /* Peer address must have been set. */ + return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_PASSED_INVALID_ARGUMENT, NULL); + + if (qc->as_server) + /* TODO(QUIC): Server mode not currently supported */ + return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_PASSED_INVALID_ARGUMENT, NULL); + + if (qc->net_rbio == NULL || qc->net_wbio == NULL) + /* Need read and write BIOs. */ + return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_PASSED_INVALID_ARGUMENT, NULL); + + /* + * Start connection process. Note we may come here multiple times in + * non-blocking mode, which is fine. + */ + if (!ensure_channel_and_start(qc)) + return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL); + + if (ossl_quic_channel_is_handshake_complete(qc->ch)) + /* The handshake is now done. */ + return 1; + + if (blocking_mode(qc)) { + /* In blocking mode, wait for the handshake to complete. */ + struct quic_handshake_wait_args args; + + args.qc = qc; + + ret = block_until_pred(qc, quic_handshake_wait, &args, 0); + if (!ossl_quic_channel_is_active(qc->ch)) + return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); + else if (ret <= 0) + return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL); + + assert(ossl_quic_channel_is_handshake_complete(qc->ch)); + return 1; + } else { + /* Otherwise, indicate that the handshake isn't done yet. */ + return QUIC_RAISE_NORMAL_ERROR(qc, SSL_ERROR_WANT_READ); + } } -long ossl_quic_ctx_callback_ctrl(SSL_CTX *ctx, int cmd, void (*fp) (void)) +/* SSL_connect */ +int ossl_quic_connect(SSL *s) { - return 0; + QUIC_CONNECTION *qc = QUIC_CONNECTION_FROM_SSL(s); + + if (!expect_quic_conn(qc)) + return 0; + + /* Ensure we are in connect state (no-op if non-idle). */ + ossl_quic_set_connect_state(qc); + + /* Begin or continue the handshake */ + return ossl_quic_do_handshake(qc); } -size_t ossl_quic_pending(const SSL *s) +/* SSL_accept */ +int ossl_quic_accept(SSL *s) { - return 0; + QUIC_CONNECTION *qc = QUIC_CONNECTION_FROM_SSL(s); + + if (!expect_quic_conn(qc)) + return 0; + + /* Ensure we are in accept state (no-op if non-idle). */ + ossl_quic_set_accept_state(qc); + + /* Begin or continue the handshake */ + return ossl_quic_do_handshake(qc); } -OSSL_TIME ossl_quic_default_timeout(void) +/* + * QUIC Front-End I/O API: Steady-State Operations + * =============================================== + * + * Here we dispatch calls to the steady-state front-end I/O API functions; that + * is, the functions used during the established phase of a QUIC connection + * (e.g. SSL_read, SSL_write). + * + * Each function must handle both blocking and non-blocking modes. As discussed + * above, all QUIC I/O is implemented using non-blocking mode internally. + * + * SSL_get_error => partially implemented by ossl_quic_get_error + * (BIO/)SSL_read => ossl_quic_read + * (BIO/)SSL_write => ossl_quic_write + * SSL_pending => ossl_quic_pending + */ + +/* SSL_get_error */ +int ossl_quic_get_error(const QUIC_CONNECTION *qc, int i) { - return ossl_time_zero(); + return qc->last_error; } -int ossl_quic_num_ciphers(void) +/* + * SSL_write + * --------- + * + * The set of functions below provide the implementation of the public SSL_write + * function. We must handle: + * + * - both blocking and non-blocking operation at the application level, + * depending on how we are configured; + * + * - SSL_MODE_ENABLE_PARTIAL_WRITE being on or off; + * + * - SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER. + * + */ +static void quic_post_write(QUIC_CONNECTION *qc, int did_append, int do_tick) +{ + /* + * We have appended at least one byte to the stream. + * Potentially mark stream as active, depending on FC. + */ + if (did_append) + ossl_quic_stream_map_update_state(ossl_quic_channel_get_qsm(qc->ch), + qc->stream0); + + /* + * Try and send. + * + * TODO(QUIC): It is probably inefficient to try and do this immediately, + * plus we should eventually consider Nagle's algorithm. + */ + if (do_tick) + ossl_quic_reactor_tick(ossl_quic_channel_get_reactor(qc->ch)); +} + +struct quic_write_again_args { + QUIC_CONNECTION *qc; + const unsigned char *buf; + size_t len; + size_t total_written; +}; + +static int quic_write_again(void *arg) +{ + struct quic_write_again_args *args = arg; + size_t actual_written = 0; + + if (!ossl_quic_channel_is_active(args->qc->ch)) + /* If connection is torn down due to an error while blocking, stop. */ + return -2; + + if (!ossl_quic_sstream_append(args->qc->stream0->sstream, + args->buf, args->len, &actual_written)) + return -2; + + quic_post_write(args->qc, actual_written > 0, 0); + + args->buf += actual_written; + args->len -= actual_written; + args->total_written += actual_written; + + if (actual_written == 0) + /* Written everything, done. */ + return 1; + + /* Not written everything yet, keep trying. */ + return 0; +} + +static int quic_write_blocking(QUIC_CONNECTION *qc, const void *buf, size_t len, + size_t *written) { + int res; + struct quic_write_again_args args; + size_t actual_written = 0; + + /* First make a best effort to append as much of the data as possible. */ + if (!ossl_quic_sstream_append(qc->stream0->sstream, buf, len, + &actual_written)) { + /* Stream already finished or allocation error. */ + *written = 0; + return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL); + } + + quic_post_write(qc, actual_written > 0, 1); + + if (actual_written == len) { + /* Managed to append everything on the first try. */ + *written = actual_written; + return 1; + } + + /* + * We did not manage to append all of the data immediately, so the stream + * buffer has probably filled up. This means we need to block until some of + * it is freed up. + */ + args.qc = qc; + args.buf = (const unsigned char *)buf + actual_written; + args.len = len - actual_written; + args.total_written = 0; + + res = block_until_pred(qc, quic_write_again, &args, 0); + if (res <= 0) { + if (!ossl_quic_channel_is_active(qc->ch)) + return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); + else + return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL); + } + + *written = args.total_written; return 1; } -const SSL_CIPHER *ossl_quic_get_cipher(unsigned int u) +static void aon_write_begin(QUIC_CONNECTION *qc, const unsigned char *buf, + size_t buf_len, size_t already_sent) +{ + assert(!qc->aon_write_in_progress); + + qc->aon_write_in_progress = 1; + qc->aon_buf_base = buf; + qc->aon_buf_pos = already_sent; + qc->aon_buf_len = buf_len; +} + +static void aon_write_finish(QUIC_CONNECTION *qc) +{ + qc->aon_write_in_progress = 0; + qc->aon_buf_base = NULL; + qc->aon_buf_pos = 0; + qc->aon_buf_len = 0; +} + +static int quic_write_nonblocking_aon(QUIC_CONNECTION *qc, const void *buf, + size_t len, size_t *written) { + const void *actual_buf; + size_t actual_len, actual_written = 0; + int accept_moving_buffer + = ((qc->ssl_mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER) != 0); + + if (qc->aon_write_in_progress) { + /* + * We are in the middle of an AON write (i.e., a previous write did not + * manage to append all data to the SSTREAM and we have EPW mode + * disabled.) + */ + if ((!accept_moving_buffer && qc->aon_buf_base != buf) + || len != qc->aon_buf_len) + /* + * Pointer must not have changed if we are not in accept moving + * buffer mode. Length must never change. + */ + return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_BAD_WRITE_RETRY, NULL); + + actual_buf = (unsigned char *)buf + qc->aon_buf_pos; + actual_len = len - qc->aon_buf_pos; + assert(actual_len > 0); + } else { + actual_buf = buf; + actual_len = len; + } + + /* First make a best effort to append as much of the data as possible. */ + if (!ossl_quic_sstream_append(qc->stream0->sstream, actual_buf, actual_len, + &actual_written)) { + /* Stream already finished or allocation error. */ + *written = 0; + return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL); + } + + quic_post_write(qc, actual_written > 0, 1); + + if (actual_written == actual_len) { + /* We have sent everything. */ + if (qc->aon_write_in_progress) { + /* + * We have sent everything, and we were in the middle of an AON + * write. The output write length is the total length of the AON + * buffer, not however many bytes we managed to write to the stream + * in this call. + */ + *written = qc->aon_buf_len; + aon_write_finish(qc); + } else { + *written = actual_written; + } + + return 1; + } + + if (qc->aon_write_in_progress) { + /* + * AON write is in progress but we have not written everything yet. We + * may have managed to send zero bytes, or some number of bytes less + * than the total remaining which need to be appended during this + * AON operation. + */ + qc->aon_buf_pos += actual_written; + assert(qc->aon_buf_pos < qc->aon_buf_len); + return QUIC_RAISE_NORMAL_ERROR(qc, SSL_ERROR_WANT_WRITE); + } + /* - * TODO(QUIC): This is needed so the SSL_CTX_set_cipher_list("DEFAULT"); - * produces at least one valid TLS-1.2 cipher. - * Later we should allow that there are none with QUIC protocol as - * SSL_CTX_set_cipher_list should still allow setting a SECLEVEL. + * Not in an existing AON operation but partial write is not enabled, so we + * need to begin a new AON operation. However we needn't bother if we didn't + * actually append anything. */ - static const SSL_CIPHER ciph = { - 1, - TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - TLS1_RFC_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - TLS1_CK_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - SSL_kECDHE, - SSL_aRSA, - SSL_AES256GCM, - SSL_AEAD, - TLS1_2_VERSION, TLS1_2_VERSION, - DTLS1_2_VERSION, DTLS1_2_VERSION, - SSL_HIGH | SSL_FIPS, - SSL_HANDSHAKE_MAC_SHA384 | TLS1_PRF_SHA384, - 256, - 256 - }; + if (actual_written > 0) + aon_write_begin(qc, buf, len, actual_written); - return &ciph; + /* + * AON - We do not publicly admit to having appended anything until AON + * completes. + */ + *written = 0; + return QUIC_RAISE_NORMAL_ERROR(qc, SSL_ERROR_WANT_WRITE); } -int ossl_quic_renegotiate_check(SSL *ssl, int initok) +static int quic_write_nonblocking_epw(QUIC_CONNECTION *qc, const void *buf, size_t len, + size_t *written) { + /* Simple best effort operation. */ + if (!ossl_quic_sstream_append(qc->stream0->sstream, buf, len, written)) { + /* Stream already finished or allocation error. */ + *written = 0; + return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL); + } + + quic_post_write(qc, *written > 0, 1); return 1; } -QUIC_CONNECTION *ossl_quic_conn_from_ssl(SSL *ssl) +int ossl_quic_write(SSL *s, const void *buf, size_t len, size_t *written) { - return QUIC_CONNECTION_FROM_SSL(ssl); + QUIC_CONNECTION *qc = QUIC_CONNECTION_FROM_SSL(s); + int partial_write = ((qc->ssl_mode & SSL_MODE_ENABLE_PARTIAL_WRITE) != 0); + + *written = 0; + + if (!expect_quic_conn(qc)) + return 0; + + /* If we haven't started the handshake, do so automatically. */ + if (!qc->started && !ossl_quic_do_handshake(qc)) + return 0; + + if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch)) + return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); + + if (qc->stream0 == NULL || qc->stream0->sstream == NULL) + return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL); + + if (blocking_mode(qc)) + return quic_write_blocking(qc, buf, len, written); + else if (partial_write) + return quic_write_nonblocking_epw(qc, buf, len, written); + else + return quic_write_nonblocking_aon(qc, buf, len, written); } /* - * The following are getters and setters of pointers, but they don't affect - * the objects being pointed at. They are CURRENTLY to be freed separately - * by the caller the set them in the first place. + * SSL_read + * -------- */ -int ossl_quic_conn_set_qrx(QUIC_CONNECTION *qc, OSSL_QRX *qrx) +struct quic_read_again_args { + QUIC_CONNECTION *qc; + QUIC_STREAM *stream; + void *buf; + size_t len; + size_t *bytes_read; + int peek; +}; + +static int quic_read_actual(QUIC_CONNECTION *qc, + QUIC_STREAM *stream, + void *buf, size_t buf_len, + size_t *bytes_read, + int peek) { - if (qc == NULL) - return 0; - qc->qrx = qrx; + int is_fin = 0; + + if (stream->rstream == NULL) + return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL); + + if (peek) { + if (!ossl_quic_rstream_peek(stream->rstream, buf, buf_len, + bytes_read, &is_fin)) + return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL); + + } else { + if (!ossl_quic_rstream_read(stream->rstream, buf, buf_len, + bytes_read, &is_fin)) + return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL); + } + + if (!peek) { + if (*bytes_read > 0) { + /* + * We have read at least one byte from the stream. Inform stream-level + * RXFC of the retirement of controlled bytes. Update the active stream + * status (the RXFC may now want to emit a frame granting more credit to + * the peer). + */ + OSSL_RTT_INFO rtt_info; + ossl_statm_get_rtt_info(ossl_quic_channel_get_statm(qc->ch), &rtt_info); + + if (!ossl_quic_rxfc_on_retire(&qc->stream0->rxfc, *bytes_read, + rtt_info.smoothed_rtt)) + return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL); + } + + if (is_fin) + stream->recv_fin_retired = 1; + + if (*bytes_read > 0) + ossl_quic_stream_map_update_state(ossl_quic_channel_get_qsm(qc->ch), + qc->stream0); + } + return 1; } -OSSL_QRX *ossl_quic_conn_get_qrx(QUIC_CONNECTION *qc) +static int quic_read_again(void *arg) { - return qc != NULL ? qc->qrx : NULL; + struct quic_read_again_args *args = arg; + + if (!ossl_quic_channel_is_active(args->qc->ch)) + /* If connection is torn down due to an error while blocking, stop. */ + return -2; + + if (!quic_read_actual(args->qc, args->stream, + args->buf, args->len, args->bytes_read, + args->peek)) + return -1; + + if (*args->bytes_read > 0) + /* got at least one byte, the SSL_read op can finish now */ + return 1; + + return 0; /* did not write anything, keep trying */ } -int ossl_quic_conn_set_ackm(QUIC_CONNECTION *qc, OSSL_ACKM *ackm) +static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek) { - if (qc == NULL) + int res; + QUIC_CONNECTION *qc = QUIC_CONNECTION_FROM_SSL(s); + struct quic_read_again_args args; + + *bytes_read = 0; + + if (!expect_quic_conn(qc)) + return 0; + + if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch)) + return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); + + /* If we haven't started the handshake, do so automatically. */ + if (!qc->started && !ossl_quic_do_handshake(qc)) + return 0; + + if (qc->stream0 == NULL) + return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL); + + if (!quic_read_actual(qc, qc->stream0, buf, len, bytes_read, peek)) return 0; - qc->ackm = ackm; + + if (*bytes_read > 0) { + /* + * Even though we succeeded, tick the reactor here to ensure we are + * handling other aspects of the QUIC connection. + */ + ossl_quic_reactor_tick(ossl_quic_channel_get_reactor(qc->ch)); + return 1; + } else if (blocking_mode(qc)) { + /* + * We were not able to read anything immediately, so our stream + * buffer is empty. This means we need to block until we get + * at least one byte. + */ + args.qc = qc; + args.stream = qc->stream0; + args.buf = buf; + args.len = len; + args.bytes_read = bytes_read; + args.peek = peek; + + res = block_until_pred(qc, quic_read_again, &args, 0); + if (res <= 0) { + if (!ossl_quic_channel_is_active(qc->ch)) + return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); + else + return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL); + } + } + return 1; } -OSSL_ACKM *ossl_quic_conn_set_akcm(QUIC_CONNECTION *qc) +int ossl_quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read) +{ + return quic_read(s, buf, len, bytes_read, 0); +} + +int ossl_quic_peek(SSL *s, void *buf, size_t len, size_t *bytes_read) +{ + return quic_read(s, buf, len, bytes_read, 1); +} + +/* + * SSL_pending + * ----------- + */ +size_t ossl_quic_pending(const SSL *s) +{ + const QUIC_CONNECTION *qc = QUIC_CONNECTION_FROM_CONST_SSL(s); + size_t avail = 0; + int fin = 0; + + if (!expect_quic_conn(qc)) + return 0; + + if (qc->stream0 == NULL || qc->stream0->rstream == NULL) + /* Cannot raise errors here because we are const, just fail. */ + return 0; + + if (!ossl_quic_rstream_available(qc->stream0->rstream, &avail, &fin)) + return 0; + + return avail; +} + +/* + * QUIC Front-End I/O API: SSL_CTX Management + * ========================================== + */ + +long ossl_quic_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) +{ + switch (cmd) { + default: + return 0; + } +} + +long ossl_quic_callback_ctrl(SSL *s, int cmd, void (*fp) (void)) +{ + return 0; +} + +long ossl_quic_ctx_callback_ctrl(SSL_CTX *ctx, int cmd, void (*fp) (void)) +{ + return 0; +} + +QUIC_CONNECTION *ossl_quic_conn_from_ssl(SSL *ssl) { - return qc != NULL ? qc->ackm : NULL; + return QUIC_CONNECTION_FROM_SSL(ssl); +} + +int ossl_quic_renegotiate_check(SSL *ssl, int initok) +{ + /* We never do renegotiation. */ + return 0; +} + +/* + * This is the subset of TLS1.3 ciphers which can be used with QUIC and which we + * actually support. + */ +static SSL_CIPHER tls13_quic_ciphers[] = { + { + 1, + TLS1_3_RFC_AES_128_GCM_SHA256, + TLS1_3_RFC_AES_128_GCM_SHA256, + TLS1_3_CK_AES_128_GCM_SHA256, + SSL_kANY, + SSL_aANY, + SSL_AES128GCM, + SSL_AEAD, + TLS1_3_VERSION, TLS1_3_VERSION, + 0, 0, + SSL_HIGH, + SSL_HANDSHAKE_MAC_SHA256, + 128, + 128, + }, { + 1, + TLS1_3_RFC_AES_256_GCM_SHA384, + TLS1_3_RFC_AES_256_GCM_SHA384, + TLS1_3_CK_AES_256_GCM_SHA384, + SSL_kANY, + SSL_aANY, + SSL_AES256GCM, + SSL_AEAD, + TLS1_3_VERSION, TLS1_3_VERSION, + 0, 0, + SSL_HIGH, + SSL_HANDSHAKE_MAC_SHA384, + 256, + 256, + }, + { + 1, + TLS1_3_RFC_CHACHA20_POLY1305_SHA256, + TLS1_3_RFC_CHACHA20_POLY1305_SHA256, + TLS1_3_CK_CHACHA20_POLY1305_SHA256, + SSL_kANY, + SSL_aANY, + SSL_CHACHA20POLY1305, + SSL_AEAD, + TLS1_3_VERSION, TLS1_3_VERSION, + 0, 0, + SSL_HIGH, + SSL_HANDSHAKE_MAC_SHA256, + 256, + 256, + } +}; + +int ossl_quic_num_ciphers(void) +{ + return OSSL_NELEM(tls13_quic_ciphers); +} + +const SSL_CIPHER *ossl_quic_get_cipher(unsigned int u) +{ + if (u >= OSSL_NELEM(tls13_quic_ciphers)) + return NULL; + + return &tls13_quic_ciphers[u]; } diff --git a/ssl/quic/quic_local.h b/ssl/quic/quic_local.h index 33301c9a13..ef2fbbed3c 100644 --- a/ssl/quic/quic_local.h +++ b/ssl/quic/quic_local.h @@ -12,53 +12,159 @@ # include <openssl/ssl.h> # include "internal/quic_ssl.h" /* QUIC_CONNECTION */ +# include "internal/quic_txp.h" +# include "internal/quic_statm.h" +# include "internal/quic_demux.h" +# include "internal/quic_record_rx.h" +# include "internal/quic_dummy_handshake.h" # include "internal/quic_fc.h" # include "internal/quic_stream.h" +# include "internal/quic_channel.h" +# include "internal/quic_reactor.h" # include "../ssl_local.h" -struct quic_stream_st { - /* type identifier and common data for the public SSL object */ - struct ssl_st ssl; - - /* QUIC_CONNECTION that this stream belongs to */ - QUIC_CONNECTION *conn; - /* receive flow controller */ - QUIC_RXFC *rxfc; - /* receive and send stream objects */ - QUIC_RSTREAM *rstream; - QUIC_SSTREAM *sstream; -}; +# ifndef OPENSSL_NO_QUIC struct quic_conn_st { - /* QUIC connection is always a stream (the stream id 0) */ - struct quic_stream_st stream; - /* the associated tls-1.3 connection data */ - SSL *tls; - - /* QUIC ack manager */ - OSSL_ACKM *ackm; - /* QUIC receive record layer */ - OSSL_QRX *qrx; + /* + * ssl_st is a common header for ordinary SSL objects, QUIC connection + * objects and QUIC stream objects, allowing objects of these different + * types to be disambiguated at runtime and providing some common fields. + * + * Note: This must come first in the QUIC_CONNECTION structure. + */ + struct ssl_st ssl; + + SSL *tls; + + /* + * The QUIC channel providing the core QUIC connection implementation. Note + * that this is not instantiated until we actually start trying to do the + * handshake. This is to allow us to gather information like whether we are + * going to be in client or server mode before committing to instantiating + * the channel, since we want to determine the channel arguments based on + * that. + * + * The channel remains available after connection termination until the SSL + * object is freed, thus (ch != NULL) iff (started == 1). + */ + QUIC_CHANNEL *ch; + + /* Our single bidirectional application data stream. */ + QUIC_STREAM *stream0; + + /* The network read and write BIOs. */ + BIO *net_rbio, *net_wbio; + + /* Initial peer L4 address. */ + BIO_ADDR init_peer_addr; + + /* Have we started? */ + unsigned int started : 1; + + /* Are we in blocking mode? */ + unsigned int blocking : 1; + + /* Can the read and write network BIOs support blocking? */ + unsigned int can_poll_net_rbio : 1; + unsigned int can_poll_net_wbio : 1; + + /* + * Has the application called SSL_set_accept_state? We do not support this + * but track it here so we can reject a subsequent handshake call. + */ + unsigned int as_server : 1; + + /* + * This state tracks SSL_write all-or-nothing (AON) write semantics + * emulation. + * + * Example chronology: + * + * t=0: aon_write_in_progress=0 + * t=1: SSL_write(ssl, b1, l1) called; + * too big to enqueue into sstream at once, SSL_ERROR_WANT_WRITE; + * aon_write_in_progress=1; aon_buf_base=b1; aon_buf_len=l1; + * aon_buf_pos < l1 (depends on how much room was in sstream); + * t=2: SSL_write(ssl, b2, l2); + * b2 must equal b1 (validated unless ACCEPT_MOVING_WRITE_BUFFER) + * l2 must equal l1 (always validated) + * append into sstream from [b2 + aon_buf_pos, b2 + aon_buf_len) + * if done, aon_write_in_progess=0 + * + */ + /* Is an AON write in progress? */ + unsigned int aon_write_in_progress : 1; + /* + * The base buffer pointer the caller passed us for the initial AON write + * call. We use this for validation purposes unless + * ACCEPT_MOVING_WRITE_BUFFER is enabled. + * + * NOTE: We never dereference this, as the caller might pass a different + * (but identical) buffer if using ACCEPT_MOVING_WRITE_BUFFER. It is for + * validation by pointer comparison only. + */ + const unsigned char *aon_buf_base; + /* The total length of the AON buffer being sent, in bytes. */ + size_t aon_buf_len; + /* + * The position in the AON buffer up to which we have successfully sent data + * so far. + */ + size_t aon_buf_pos; + + /* SSL_set_mode */ + uint32_t ssl_mode; + + /* + * Last 'normal' error during an app-level I/O operation, used by + * SSL_get_error(); used to track data-path errors like SSL_ERROR_WANT_READ + * and SSL_ERROR_WANT_WRITE. + */ + int last_error; }; -# define QUIC_CONNECTION_FROM_SSL_int(ssl, c) \ - ((ssl) == NULL ? NULL \ - : ((ssl)->type == SSL_TYPE_QUIC_CONNECTION \ - ? (c QUIC_CONNECTION *)(ssl) \ - : NULL)) - -# define QUIC_STREAM_FROM_SSL_int(ssl, c) \ - ((ssl) == NULL ? NULL \ - : ((ssl)->type == SSL_TYPE_QUIC_CONNECTION \ - || (ssl)->type == SSL_TYPE_QUIC_STREAM \ - ? (c QUIC_STREAM *)(ssl) \ - : NULL)) - -# define SSL_CONNECTION_FROM_QUIC_SSL_int(ssl, c) \ - ((ssl) == NULL ? NULL \ - : ((ssl)->type == SSL_TYPE_QUIC_CONNECTION \ - ? (c SSL_CONNECTION *)((c QUIC_CONNECTION *)(ssl))->tls \ - : NULL)) +/* Internal calls to the QUIC CSM which come from various places. */ +int ossl_quic_conn_on_handshake_confirmed(QUIC_CONNECTION *qc); + +/* + * To be called when a protocol violation occurs. The connection is torn down + * with the given error code, which should be a QUIC_ERR_* value. Reason string + * is optional and copied if provided. frame_type should be 0 if not applicable. + */ +void ossl_quic_conn_raise_protocol_error(QUIC_CONNECTION *qc, + uint64_t error_code, + uint64_t frame_type, + const char *reason); + +void ossl_quic_conn_on_remote_conn_close(QUIC_CONNECTION *qc, + OSSL_QUIC_FRAME_CONN_CLOSE *f); + +# define OSSL_QUIC_ANY_VERSION 0xFFFFF + +# define QUIC_CONNECTION_FROM_SSL_int(ssl, c) \ + ((ssl) == NULL ? NULL \ + : ((ssl)->type == SSL_TYPE_QUIC_CONNECTION \ + ? (c QUIC_CONNECTION *)(ssl) \ + : NULL)) + +# define QUIC_STREAM_FROM_SSL_int(ssl, c) \ + ((ssl) == NULL ? NULL \ + : ((ssl)->type == SSL_TYPE_QUIC_CONNECTION \ + || (ssl)->type == SSL_TYPE_QUIC_STREAM \ + ? (c QUIC_STREAM *)(ssl) \ + : NULL)) + +# define SSL_CONNECTION_FROM_QUIC_SSL_int(ssl, c) \ + ((ssl) == NULL ? NULL \ + : ((ssl)->type == SSL_TYPE_QUIC_CONNECTION \ + ? (c SSL_CONNECTION *)((c QUIC_CONNECTION *)(ssl))->tls \ + : NULL)) +# else +# define QUIC_CONNECTION_FROM_SSL_int(ssl, c) NULL +# define QUIC_STREAM_FROM_SSL_int(ssl, c) NULL +# define SSL_CONNECTION_FROM_QUIC_SSL_int(ssl, c) NULL +# endif # define QUIC_CONNECTION_FROM_SSL(ssl) \ QUIC_CONNECTION_FROM_SSL_int(ssl, SSL_CONNECTION_NO_CONST) @@ -73,8 +179,6 @@ struct quic_conn_st { # define SSL_CONNECTION_FROM_CONST_QUIC_SSL(ssl) \ SSL_CONNECTION_FROM_CONST_QUIC_SSL_int(ssl, const) -# define OSSL_QUIC_ANY_VERSION 0xFFFFF - # define IMPLEMENT_quic_meth_func(version, func_name, q_accept, \ q_connect, enc_data) \ const SSL_METHOD *func_name(void) \ @@ -107,7 +211,7 @@ const SSL_METHOD *func_name(void) \ ossl_quic_pending, \ ossl_quic_num_ciphers, \ ossl_quic_get_cipher, \ - ossl_quic_default_timeout, \ + tls1_default_timeout, \ &enc_data, \ ssl_undefined_void_function, \ ossl_quic_callback_ctrl, \ diff --git a/ssl/quic/quic_record_shared.c b/ssl/quic/quic_record_shared.c index d5c8ede58c..7f4e799905 100644 --- a/ssl/quic/quic_record_shared.c +++ b/ssl/quic/quic_record_shared.c @@ -217,8 +217,6 @@ int ossl_qrl_enc_level_set_provide_secret(OSSL_QRL_ENC_LEVEL_SET *els, if (el->state != QRL_EL_STATE_UNPROV) return 0; - - init_keyslot = is_tx ? 0 : init_key_phase_bit; hpr_key_len = ossl_qrl_get_suite_hdr_prot_key_len(suite_id); if (hpr_key_len == 0) |