root/lib/libssl/tls13_legacy.c
/*      $OpenBSD: tls13_legacy.c,v 1.45 2026/04/03 07:17:36 jsing Exp $ */
/*
 * Copyright (c) 2018, 2019 Joel Sing <jsing@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <limits.h>

#include "ssl_local.h"
#include "tls13_internal.h"

static ssize_t
tls13_legacy_wire_read(SSL *ssl, uint8_t *buf, size_t len)
{
        int n;

        if (ssl->rbio == NULL) {
                SSLerror(ssl, SSL_R_BIO_NOT_SET);
                return TLS13_IO_FAILURE;
        }

        ssl->rwstate = SSL_READING;
        errno = 0;

        if ((n = BIO_read(ssl->rbio, buf, len)) <= 0) {
                if (BIO_should_read(ssl->rbio))
                        return TLS13_IO_WANT_POLLIN;
                if (n == 0)
                        return TLS13_IO_EOF;

                if (ERR_peek_error() == 0 && errno != 0)
                        SYSerror(errno);

                return TLS13_IO_FAILURE;
        }

        if (n == len)
                ssl->rwstate = SSL_NOTHING;

        return n;
}

ssize_t
tls13_legacy_wire_read_cb(void *buf, size_t n, void *arg)
{
        struct tls13_ctx *ctx = arg;

        return tls13_legacy_wire_read(ctx->ssl, buf, n);
}

static ssize_t
tls13_legacy_wire_write(SSL *ssl, const uint8_t *buf, size_t len)
{
        int n;

        if (ssl->wbio == NULL) {
                SSLerror(ssl, SSL_R_BIO_NOT_SET);
                return TLS13_IO_FAILURE;
        }

        ssl->rwstate = SSL_WRITING;
        errno = 0;

        if ((n = BIO_write(ssl->wbio, buf, len)) <= 0) {
                if (BIO_should_write(ssl->wbio))
                        return TLS13_IO_WANT_POLLOUT;

                if (ERR_peek_error() == 0 && errno != 0)
                        SYSerror(errno);

                return TLS13_IO_FAILURE;
        }

        if (n == len)
                ssl->rwstate = SSL_NOTHING;

        return n;
}

ssize_t
tls13_legacy_wire_write_cb(const void *buf, size_t n, void *arg)
{
        struct tls13_ctx *ctx = arg;

        return tls13_legacy_wire_write(ctx->ssl, buf, n);
}

static ssize_t
tls13_legacy_wire_flush(SSL *ssl)
{
        if (BIO_flush(ssl->wbio) <= 0) {
                if (BIO_should_write(ssl->wbio))
                        return TLS13_IO_WANT_POLLOUT;

                if (ERR_peek_error() == 0 && errno != 0)
                        SYSerror(errno);

                return TLS13_IO_FAILURE;
        }

        return TLS13_IO_SUCCESS;
}

ssize_t
tls13_legacy_wire_flush_cb(void *arg)
{
        struct tls13_ctx *ctx = arg;

        return tls13_legacy_wire_flush(ctx->ssl);
}

static void
tls13_legacy_error(SSL *ssl)
{
        struct tls13_ctx *ctx = ssl->tls13;
        int reason = SSL_R_UNKNOWN;

        /* If we received a fatal alert we already put an error on the stack. */
        if (ssl->s3->fatal_alert != 0)
                return;

        switch (ctx->error.code) {
        case TLS13_ERR_VERIFY_FAILED:
                reason = SSL_R_CERTIFICATE_VERIFY_FAILED;
                break;
        case TLS13_ERR_HRR_FAILED:
                reason = SSL_R_NO_CIPHERS_AVAILABLE;
                break;
        case TLS13_ERR_TRAILING_DATA:
                reason = SSL_R_EXTRA_DATA_IN_MESSAGE;
                break;
        case TLS13_ERR_NO_SHARED_CIPHER:
                reason = SSL_R_NO_SHARED_CIPHER;
                break;
        case TLS13_ERR_NO_CERTIFICATE:
                reason = SSL_R_MISSING_RSA_CERTIFICATE; /* XXX */
                break;
        case TLS13_ERR_NO_PEER_CERTIFICATE:
                reason = SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE;
                break;
        }

        /* Something (probably libcrypto) already pushed an error on the stack. */
        if (reason == SSL_R_UNKNOWN && ERR_peek_error() != 0)
                return;

        ERR_put_error(ERR_LIB_SSL, (0xfff), reason, ctx->error.file,
            ctx->error.line);
}

static int
tls13_legacy_return_code(SSL *ssl, ssize_t ret)
{
        if (ret > INT_MAX) {
                SSLerror(ssl, ERR_R_INTERNAL_ERROR);
                return -1;
        }

        /* A successful read, write or other operation. */
        if (ret > 0)
                return ret;

        ssl->rwstate = SSL_NOTHING;

        switch (ret) {
        case TLS13_IO_EOF:
                return 0;

        case TLS13_IO_FAILURE:
                tls13_legacy_error(ssl);
                return -1;

        case TLS13_IO_ALERT:
                tls13_legacy_error(ssl);
                return -1;

        case TLS13_IO_WANT_POLLIN:
                BIO_set_retry_read(ssl->rbio);
                ssl->rwstate = SSL_READING;
                return -1;

        case TLS13_IO_WANT_POLLOUT:
                BIO_set_retry_write(ssl->wbio);
                ssl->rwstate = SSL_WRITING;
                return -1;

        case TLS13_IO_WANT_RETRY:
                SSLerror(ssl, ERR_R_INTERNAL_ERROR);
                return -1;
        }

        SSLerror(ssl, ERR_R_INTERNAL_ERROR);
        return -1;
}

int
tls13_legacy_pending(const SSL *ssl)
{
        struct tls13_ctx *ctx = ssl->tls13;
        ssize_t ret;

        if (ctx == NULL)
                return 0;

        ret = tls13_pending_application_data(ctx->rl);
        if (ret < 0 || ret > INT_MAX)
                return 0;

        return ret;
}

int
tls13_legacy_read_bytes(SSL *ssl, int type, unsigned char *buf, int len, int peek)
{
        struct tls13_ctx *ctx = ssl->tls13;
        ssize_t ret;

        if (ctx == NULL || !ctx->handshake_completed) {
                if ((ret = ssl->handshake_func(ssl)) <= 0)
                        return ret;
                if (len == 0)
                        return 0;
                return tls13_legacy_return_code(ssl, TLS13_IO_WANT_POLLIN);
        }

        tls13_record_layer_set_retry_after_phh(ctx->rl,
            (ctx->ssl->mode & SSL_MODE_AUTO_RETRY) != 0);

        if (type != SSL3_RT_APPLICATION_DATA) {
                SSLerror(ssl, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
                return -1;
        }
        if (len < 0) {
                SSLerror(ssl, SSL_R_BAD_LENGTH);
                return -1;
        }

        if (peek)
                ret = tls13_peek_application_data(ctx->rl, buf, len);
        else
                ret = tls13_read_application_data(ctx->rl, buf, len);

        return tls13_legacy_return_code(ssl, ret);
}

int
tls13_legacy_write_bytes(SSL *ssl, int type, const void *vbuf, int len)
{
        struct tls13_ctx *ctx = ssl->tls13;
        const uint8_t *buf = vbuf;
        size_t n, sent;
        ssize_t ret;

        if (ctx == NULL || !ctx->handshake_completed) {
                if ((ret = ssl->handshake_func(ssl)) <= 0)
                        return ret;
                if (len == 0)
                        return 0;
                return tls13_legacy_return_code(ssl, TLS13_IO_WANT_POLLOUT);
        }

        if (type != SSL3_RT_APPLICATION_DATA) {
                SSLerror(ssl, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
                return -1;
        }
        if (len < 0) {
                SSLerror(ssl, SSL_R_BAD_LENGTH);
                return -1;
        }

        /*
         * The TLSv1.3 record layer write behaviour is the same as
         * SSL_MODE_ENABLE_PARTIAL_WRITE.
         */
        if (ssl->mode & SSL_MODE_ENABLE_PARTIAL_WRITE) {
                ret = tls13_write_application_data(ctx->rl, buf, len);
                return tls13_legacy_return_code(ssl, ret);
        }

        /*
         * In the non-SSL_MODE_ENABLE_PARTIAL_WRITE case we have to loop until
         * we have written out all of the requested data.
         */
        sent = ssl->s3->wnum;
        if (len < sent) {
                SSLerror(ssl, SSL_R_BAD_LENGTH);
                return -1;
        }
        n = len - sent;
        for (;;) {
                if (n == 0) {
                        ssl->s3->wnum = 0;
                        return sent;
                }
                if ((ret = tls13_write_application_data(ctx->rl,
                    &buf[sent], n)) <= 0) {
                        ssl->s3->wnum = sent;
                        return tls13_legacy_return_code(ssl, ret);
                }
                sent += ret;
                n -= ret;
        }
}

static int
tls13_use_legacy_stack(struct tls13_ctx *ctx)
{
        SSL *s = ctx->ssl;
        CBB cbb, fragment;
        CBS cbs;

        memset(&cbb, 0, sizeof(cbb));

        if (!ssl3_setup_init_buffer(s))
                goto err;
        if (!ssl3_setup_buffers(s))
                goto err;
        if (!ssl_init_wbio_buffer(s, 1))
                goto err;

        /* Stash any unprocessed data from the last record. */
        tls13_record_layer_rcontent(ctx->rl, &cbs);
        if (CBS_len(&cbs) > 0) {
                if (!CBB_init_fixed(&cbb, s->s3->rbuf.buf,
                    s->s3->rbuf.len))
                        goto err;
                if (!CBB_add_u8(&cbb, SSL3_RT_HANDSHAKE))
                        goto err;
                if (!CBB_add_u16(&cbb, TLS1_2_VERSION))
                        goto err;
                if (!CBB_add_u16_length_prefixed(&cbb, &fragment))
                        goto err;
                if (!CBB_add_bytes(&fragment, CBS_data(&cbs), CBS_len(&cbs)))
                        goto err;
                if (!CBB_finish(&cbb, NULL, NULL))
                        goto err;

                s->s3->rbuf.offset = SSL3_RT_HEADER_LENGTH;
                s->s3->rbuf.left = CBS_len(&cbs);
                s->s3->rrec.type = SSL3_RT_HANDSHAKE;
                s->s3->rrec.length = CBS_len(&cbs);
                s->rstate = SSL_ST_READ_BODY;
                s->packet = s->s3->rbuf.buf;
                s->packet_length = SSL3_RT_HEADER_LENGTH;
        }

        /* Stash the current handshake message. */
        tls13_handshake_msg_data(ctx->hs_msg, &cbs);
        if (!BUF_MEM_grow_clean(s->init_buf, CBS_len(&cbs)))
                goto err;
        if (!CBS_write_bytes(&cbs, s->init_buf->data,
            s->init_buf->length, NULL))
                goto err;

        s->s3->hs.tls12.reuse_message = 1;
        s->s3->hs.tls12.message_type = tls13_handshake_msg_type(ctx->hs_msg);
        s->s3->hs.tls12.message_size = CBS_len(&cbs) - SSL3_HM_HEADER_LENGTH;

        /*
         * Only switch the method after initialization is complete
         * as we start part way into the legacy state machine.
         */
        s->method = tls_legacy_method();

        return 1;

 err:
        CBB_cleanup(&cbb);

        return 0;
}

int
tls13_use_legacy_client(struct tls13_ctx *ctx)
{
        SSL *s = ctx->ssl;

        if (!tls13_use_legacy_stack(ctx))
                return 0;

        s->handshake_func = s->method->ssl_connect;
        s->version = s->method->max_tls_version;

        return 1;
}

int
tls13_use_legacy_server(struct tls13_ctx *ctx)
{
        SSL *s = ctx->ssl;

        if (!tls13_use_legacy_stack(ctx))
                return 0;

        s->handshake_func = s->method->ssl_accept;
        s->version = s->method->max_tls_version;
        s->server = 1;

        return 1;
}

int
tls13_legacy_accept(SSL *ssl)
{
        struct tls13_ctx *ctx = ssl->tls13;
        int ret;

        if (ctx == NULL) {
                if ((ctx = tls13_ctx_new(TLS13_HS_SERVER, ssl)) == NULL) {
                        SSLerror(ssl, ERR_R_INTERNAL_ERROR); /* XXX */
                        return -1;
                }
                if (!tls13_server_init(ctx)) {
                        if (ERR_peek_error() == 0)
                                SSLerror(ssl, ERR_R_INTERNAL_ERROR); /* XXX */
                        return -1;
                }
        }

        ERR_clear_error();

        ret = tls13_server_accept(ctx);
        if (ret == TLS13_IO_USE_LEGACY)
                return ssl->method->ssl_accept(ssl);

        ret = tls13_legacy_return_code(ssl, ret);

        if (ctx->info_cb != NULL)
                ctx->info_cb(ctx, TLS13_INFO_ACCEPT_EXIT, ret);

        return ret;
}

int
tls13_legacy_connect(SSL *ssl)
{
        struct tls13_ctx *ctx = ssl->tls13;
        int ret;

        if (ctx == NULL) {
                if ((ctx = tls13_ctx_new(TLS13_HS_CLIENT, ssl)) == NULL) {
                        SSLerror(ssl, ERR_R_INTERNAL_ERROR); /* XXX */
                        return -1;
                }
                if (!tls13_client_init(ctx)) {
                        if (ERR_peek_error() == 0)
                                SSLerror(ssl, ERR_R_INTERNAL_ERROR); /* XXX */
                        return -1;
                }
        }

        ERR_clear_error();

        ret = tls13_client_connect(ctx);
        if (ret == TLS13_IO_USE_LEGACY)
                return ssl->method->ssl_connect(ssl);

        ret = tls13_legacy_return_code(ssl, ret);

        if (ctx->info_cb != NULL)
                ctx->info_cb(ctx, TLS13_INFO_CONNECT_EXIT, ret);

        return ret;
}

int
tls13_legacy_shutdown(SSL *ssl)
{
        struct tls13_ctx *ctx = ssl->tls13;
        uint8_t buf[512]; /* XXX */
        ssize_t ret;

        /*
         * We need to return 0 at the point that we have completed sending a
         * close-notify. We return 1 when we have sent and received close-notify
         * alerts. All other cases, including EOF, return -1 and set internal
         * state appropriately. Note that all of this insanity can also be
         * externally controlled by manipulating the shutdown flags.
         */
        if (ctx == NULL || ssl->quiet_shutdown) {
                ssl->shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN;
                return 1;
        }

        if ((ssl->shutdown & SSL_SENT_SHUTDOWN) == 0) {
                ssl->shutdown |= SSL_SENT_SHUTDOWN;
                ret = tls13_send_alert(ctx->rl, TLS13_ALERT_CLOSE_NOTIFY);
                if (ret == TLS13_IO_EOF)
                        return -1;
                if (ret != TLS13_IO_SUCCESS)
                        return tls13_legacy_return_code(ssl, ret);
                goto done;
        }

        ret = tls13_record_layer_send_pending(ctx->rl);
        if (ret == TLS13_IO_EOF)
                return -1;
        if (ret != TLS13_IO_SUCCESS)
                return tls13_legacy_return_code(ssl, ret);

        if ((ssl->shutdown & SSL_RECEIVED_SHUTDOWN) == 0) {
                /*
                 * If there is no application data pending, attempt to read more
                 * data in order to receive a close-notify. This should trigger
                 * a record to be read from the wire, which may be application
                 * handshake or alert data. Only one attempt is made with no
                 * error handling, in order to match previous semantics.
                 */
                if (tls13_pending_application_data(ctx->rl) == 0) {
                        (void)tls13_read_application_data(ctx->rl, buf, sizeof(buf));
                        if (!ctx->close_notify_recv)
                                return -1;
                }
        }

 done:
        if (ssl->shutdown == (SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN))
                return 1;

        return 0;
}

int
tls13_legacy_servername_process(struct tls13_ctx *ctx, uint8_t *alert)
{
        int legacy_alert = SSL_AD_UNRECOGNIZED_NAME;
        int ret = SSL_TLSEXT_ERR_NOACK;
        SSL_CTX *ssl_ctx = ctx->ssl->ctx;
        SSL *s = ctx->ssl;

        if (ssl_ctx->tlsext_servername_callback == NULL)
                ssl_ctx = s->initial_ctx;
        if (ssl_ctx->tlsext_servername_callback == NULL)
                return 1;

        ret = ssl_ctx->tlsext_servername_callback(s, &legacy_alert,
            ssl_ctx->tlsext_servername_arg);

        /*
         * Ignore SSL_TLSEXT_ERR_ALERT_WARNING returns to match OpenSSL's
         * behavior: the only warning alerts in TLSv1.3 are close_notify and
         * user_canceled, neither of which should be returned by the callback.
         */
        if (ret == SSL_TLSEXT_ERR_ALERT_FATAL) {
                if (legacy_alert >= 0 && legacy_alert <= 255)
                        *alert = legacy_alert;
                return 0;
        }

        return 1;
}