From f75b3d1db6d5d7eef347c19f3597692a8a20b7b9 Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Wed, 24 Apr 2024 10:03:50 +0100 Subject: [PATCH] QUIC APL: Add QUIC Domain SSL Object: Implementation Reviewed-by: Matt Caswell Reviewed-by: Neil Horman (Merged from https://github.com/openssl/openssl/pull/24971) --- include/internal/quic_ssl.h | 3 + include/openssl/ssl.h.in | 5 + ssl/quic/quic_impl.c | 200 +++++++++++++++++++++++++++++++++--- ssl/ssl_lib.c | 41 ++++++++ 4 files changed, 237 insertions(+), 12 deletions(-) diff --git a/include/internal/quic_ssl.h b/include/internal/quic_ssl.h index ae70233aeb8..ab91d0a49d6 100644 --- a/include/internal/quic_ssl.h +++ b/include/internal/quic_ssl.h @@ -21,7 +21,9 @@ __owur SSL *ossl_quic_new(SSL_CTX *ctx); __owur SSL *ossl_quic_new_listener(SSL_CTX *ctx, uint64_t flags); +__owur SSL *ossl_quic_new_listener_from(SSL *ssl, uint64_t flags); __owur SSL *ossl_quic_new_from_listener(SSL *ssl, uint64_t flags); +__owur SSL *ossl_quic_new_domain(SSL_CTX *ctx, uint64_t flags); __owur int ossl_quic_init(SSL *s); void ossl_quic_deinit(SSL *s); void ossl_quic_free(SSL *s); @@ -76,6 +78,7 @@ __owur int ossl_quic_conn_set_initial_peer_addr(SSL *s, __owur SSL *ossl_quic_conn_stream_new(SSL *s, uint64_t flags); __owur SSL *ossl_quic_get0_connection(SSL *s); __owur SSL *ossl_quic_get0_listener(SSL *s); +__owur SSL *ossl_quic_get0_domain(SSL *s); __owur int ossl_quic_get_stream_type(SSL *s); __owur uint64_t ossl_quic_get_stream_id(SSL *s); __owur int ossl_quic_is_stream_local(SSL *s); diff --git a/include/openssl/ssl.h.in b/include/openssl/ssl.h.in index 31a32a9b80a..7a62e3af3cf 100644 --- a/include/openssl/ssl.h.in +++ b/include/openssl/ssl.h.in @@ -2306,12 +2306,17 @@ __owur int SSL_is_listener(SSL *ssl); __owur SSL *SSL_get0_listener(SSL *s); #define SSL_LISTENER_FLAG_NO_ACCEPT (1UL << 0) __owur SSL *SSL_new_listener(SSL_CTX *ctx, uint64_t flags); +__owur SSL *SSL_new_listener_from(SSL *ssl, uint64_t flags); __owur SSL *SSL_new_from_listener(SSL *ssl, uint64_t flags); #define SSL_ACCEPT_CONNECTION_NO_BLOCK (1UL << 0) __owur SSL *SSL_accept_connection(SSL *ssl, uint64_t flags); __owur size_t SSL_get_accept_connection_queue_len(SSL *ssl); __owur int SSL_listen(SSL *ssl); +__owur int SSL_is_domain(SSL *s); +__owur SSL *SSL_get0_domain(SSL *s); +__owur SSL *SSL_new_domain(SSL_CTX *ctx, uint64_t flags); + #define SSL_STREAM_TYPE_NONE 0 #define SSL_STREAM_TYPE_READ (1U << 0) #define SSL_STREAM_TYPE_WRITE (1U << 1) diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c index 4fb2d45af71..fb71192ad65 100644 --- a/ssl/quic/quic_impl.c +++ b/ssl/quic/quic_impl.c @@ -86,10 +86,11 @@ static OSSL_TIME get_time_cb(void *arg) */ struct qctx_st { QUIC_OBJ *obj; + QUIC_DOMAIN *qd; QUIC_LISTENER *ql; QUIC_CONNECTION *qc; QUIC_XSO *xso; - int is_stream, is_listener, in_io; + int is_stream, is_listener, is_domain, in_io; }; QUIC_NEEDS_LOCK @@ -211,6 +212,7 @@ static int quic_raise_non_normal_error(QCTX *ctx, #define QCTX_REMOTE_INIT (1U << 4) #define QCTX_LOCK (1U << 5) #define QCTX_IO (1U << 6) +#define QCTX_D (1U << 7) /* * Called when expect_quic failed. Used to diagnose why such a call failed and @@ -218,10 +220,12 @@ static int quic_raise_non_normal_error(QCTX *ctx, */ static int wrong_type(const SSL *s, uint32_t flags) { - const uint32_t mask = QCTX_C | QCTX_S | QCTX_L; + const uint32_t mask = QCTX_C | QCTX_S | QCTX_L | QCTX_D; int code = ERR_R_UNSUPPORTED; - if ((flags & mask) == QCTX_L) + if ((flags & mask) == QCTX_D) + code = SSL_R_DOMAIN_USE_ONLY; + else if ((flags & mask) == QCTX_L) code = SSL_R_LISTENER_USE_ONLY; else if ((flags & mask) == QCTX_C) code = SSL_R_CONN_USE_ONLY; @@ -247,30 +251,35 @@ static int wrong_type(const SSL *s, uint32_t flags) * the SSL object, and assuming the preconditions demanded by the flags field as * described above are met: * - * QLSO QCSO QSSO - * ql non-NULL maybe maybe - * qc NULL non-NULL non-NULL - * xso NULL maybe non-NULL - * is_stream 0 0 1 - * is_listener 1 0 0 + * QDSO QLSO QCSO QSSO + * qd non-NULL maybe maybe maybe + * ql NULL non-NULL maybe maybe + * qc NULL NULL non-NULL non-NULL + * xso NULL NULL maybe non-NULL + * is_stream 0 0 0 1 + * is_listener 0 1 0 0 + * is_domain 1 0 0 0 * */ static int expect_quic_as(const SSL *s, QCTX *ctx, uint32_t flags) { int ok = 0, locked = 0, lock_requested = ((flags & QCTX_LOCK) != 0); - QUIC_CONNECTION *qc; + QUIC_DOMAIN *qd; QUIC_LISTENER *ql; + QUIC_CONNECTION *qc; QUIC_XSO *xso; if ((flags & QCTX_AUTO_S) != 0) flags |= QCTX_S; ctx->obj = NULL; + ctx->qd = NULL; ctx->ql = NULL; ctx->qc = NULL; ctx->xso = NULL; ctx->is_stream = 0; ctx->is_listener = 0; + ctx->is_domain = 0; ctx->in_io = ((flags & QCTX_IO) != 0); if (s == NULL) { @@ -279,6 +288,18 @@ static int expect_quic_as(const SSL *s, QCTX *ctx, uint32_t flags) } switch (s->type) { + case SSL_TYPE_QUIC_DOMAIN: + if ((flags & QCTX_D) == 0) { + wrong_type(s, flags); + goto err; + } + + qd = (QUIC_DOMAIN *)s; + ctx->obj = &qd->obj; + ctx->qd = qd; + ctx->is_domain = 1; + break; + case SSL_TYPE_QUIC_LISTENER: if ((flags & QCTX_L) == 0) { wrong_type(s, flags); @@ -381,13 +402,23 @@ static int expect_quic_csl(const SSL *s, QCTX *ctx) return expect_quic_as(s, ctx, QCTX_C | QCTX_S | QCTX_L); } -#define expect_quic_any expect_quic_csl +static int expect_quic_csld(const SSL *s, QCTX *ctx) +{ + return expect_quic_as(s, ctx, QCTX_C | QCTX_S | QCTX_L | QCTX_D); +} + +#define expect_quic_any expect_quic_csld static int expect_quic_listener(const SSL *s, QCTX *ctx) { return expect_quic_as(s, ctx, QCTX_L); } +static int expect_quic_domain(const SSL *s, QCTX *ctx) +{ + return expect_quic_as(s, ctx, QCTX_D); +} + /* * Like expect_quic_cs(), but requires a QUIC_XSO be contextually available. In * other words, requires that the passed QSO be a QSSO or a QCSO with a default @@ -671,6 +702,22 @@ static void quic_free_listener(QCTX *ctx) quic_unref_port_bios(ctx->ql->port); ossl_quic_port_drop_incoming(ctx->ql->port); ossl_quic_port_free(ctx->ql->port); + + if (ctx->ql->domain == NULL) { + ossl_quic_engine_free(ctx->ql->engine); +#if defined(OPENSSL_THREADS) + ossl_crypto_mutex_free(&ctx->ql->mutex); +#endif + } + + if (ctx->ql->domain != NULL) + SSL_free(&ctx->ql->domain->obj.ssl); +} + +/* SSL_free */ +QUIC_TAKES_LOCK +static void quic_free_domain(QCTX *ctx) +{ ossl_quic_engine_free(ctx->ql->engine); #if defined(OPENSSL_THREADS) ossl_crypto_mutex_free(&ctx->ql->mutex); @@ -687,6 +734,11 @@ void ossl_quic_free(SSL *s) if (!expect_quic_any(s, &ctx)) return; + if (ctx.is_domain) { + quic_free_domain(&ctx); + return; + } + if (ctx.is_listener) { quic_free_listener(&ctx); return; @@ -772,6 +824,8 @@ void ossl_quic_free(SSL *s) if (ctx.qc->listener != NULL) SSL_free(&ctx.qc->listener->obj.ssl); + if (ctx.qc->domain != NULL) + SSL_free(&ctx.qc->domain->obj.ssl); } /* SSL method init */ @@ -3186,6 +3240,20 @@ SSL *ossl_quic_get0_listener(SSL *s) return ctx.ql != NULL ? &ctx.ql->obj.ssl : NULL; } +/* + * SSL_get0_domain + * --------------- + */ +SSL *ossl_quic_get0_domain(SSL *s) +{ + QCTX ctx; + + if (!expect_quic_csld(s, &ctx)) + return NULL; + + return ctx.qd != NULL ? &ctx.qd->obj.ssl : NULL; +} + /* * SSL_get_stream_type * ------------------- @@ -4163,7 +4231,7 @@ SSL *ossl_quic_new_listener(SSL_CTX *ctx, uint64_t flags) ossl_quic_port_set_allow_incoming(ql->port, 1); - /* Initialise the QUIC_LISTENER'S object header. */ + /* Initialise the QUIC_LISTENER's object header. */ if (!ossl_quic_obj_init(&ql->obj, ctx, SSL_TYPE_QUIC_LISTENER, NULL, ql->engine, ql->port)) goto err; @@ -4181,6 +4249,60 @@ err: return NULL; } +/* + * SSL_new_listener_from + * --------------------- + */ +SSL *ossl_quic_new_listener_from(SSL *ssl, uint64_t flags) +{ + QCTX ctx; + QUIC_LISTENER *ql = NULL; + QUIC_PORT_ARGS port_args = {0}; + + if (!expect_quic_domain(ssl, &ctx)) + return NULL; + + qctx_lock(&ctx); + + if ((ql = OPENSSL_zalloc(sizeof(*ql))) == NULL) { + QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL); + goto err; + } + + port_args.channel_ctx = ssl->ctx; + port_args.is_multi_conn = 1; + ql->port = ossl_quic_engine_create_port(ctx.qd->engine, &port_args); + if (ql->port == NULL) { + QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL); + goto err; + } + + ql->domain = ctx.qd; + ql->engine = ctx.qd->engine; + ql->mutex = ctx.qd->mutex; + + /* TODO(QUIC SERVER): Implement SSL_LISTENER_FLAG_NO_ACCEPT */ + + ossl_quic_port_set_allow_incoming(ql->port, 1); + + /* Initialise the QUIC_LISTENER's object header. */ + if (!ossl_quic_obj_init(&ql->obj, ssl->ctx, SSL_TYPE_QUIC_LISTENER, + &ctx.qd->obj.ssl, + ctx.qd->engine, ql->port)) + goto err; + + qctx_unlock(&ctx); + return &ql->obj.ssl; + +err: + if (ql != NULL) + ossl_quic_port_free(ql->port); + + OPENSSL_free(ql); + qctx_unlock(&ctx); + return NULL; +} + /* * SSL_new_from_listener * --------------------- @@ -4363,6 +4485,60 @@ size_t ossl_quic_get_accept_connection_queue_len(SSL *ssl) return ret; } +/* + * QUIC Front-End I/O API: Domains + * =============================== + */ + +/* + * SSL_new_domain + * -------------- + */ +SSL *ossl_quic_new_domain(SSL_CTX *ctx, uint64_t flags) +{ + QUIC_DOMAIN *qd = NULL; + QUIC_ENGINE_ARGS engine_args = {0}; + + if ((qd = OPENSSL_zalloc(sizeof(*qd))) == NULL) { + QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL); + goto err; + } + +#if defined(OPENSSL_THREADS) + if ((qd->mutex = ossl_crypto_mutex_new()) == NULL) { + QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL); + goto err; + } +#endif + + engine_args.libctx = ctx->libctx; + engine_args.propq = ctx->propq; +#if defined(OPENSSL_THREADS) + engine_args.mutex = qd->mutex; +#endif + if ((qd->engine = ossl_quic_engine_new(&engine_args)) == NULL) { + QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL); + goto err; + } + + /* Initialise the QUIC_DOMAIN's object header. */ + if (!ossl_quic_obj_init(&qd->obj, ctx, SSL_TYPE_QUIC_DOMAIN, NULL, + qd->engine, NULL)) + goto err; + + return &qd->obj.ssl; + +err: + if (qd != NULL) + ossl_quic_engine_free(qd->engine); + +#if defined(OPENSSL_THREADS) + ossl_crypto_mutex_free(&qd->mutex); +#endif + OPENSSL_free(qd); + return NULL; +} + /* * QUIC Front-End I/O API: SSL_CTX Management * ========================================== diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 2a160d34b96..346dc2c8665 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -7710,11 +7710,28 @@ SSL *SSL_get0_listener(SSL *s) #endif } +SSL *SSL_get0_domain(SSL *s) +{ +#ifndef OPENSSL_NO_QUIC + if (!IS_QUIC(s)) + return NULL; + + return ossl_quic_get0_domain(s); +#else + return NULL; +#endif +} + int SSL_is_listener(SSL *s) { return SSL_get0_listener(s) == s; } +int SSL_is_domain(SSL *s) +{ + return SSL_get0_domain(s) == s; +} + int SSL_get_stream_type(SSL *s) { #ifndef OPENSSL_NO_QUIC @@ -7910,6 +7927,18 @@ SSL *SSL_new_listener(SSL_CTX *ctx, uint64_t flags) #endif } +SSL *SSL_new_listener_from(SSL *ssl, uint64_t flags) +{ +#ifndef OPENSSL_NO_QUIC + if (!IS_QUIC(ssl)) + return NULL; + + return ossl_quic_new_listener_from(ssl, flags); +#else + return NULL; +#endif +} + SSL *SSL_new_from_listener(SSL *ssl, uint64_t flags) { #ifndef OPENSSL_NO_QUIC @@ -7958,6 +7987,18 @@ int SSL_listen(SSL *ssl) #endif } +SSL *SSL_new_domain(SSL_CTX *ctx, uint64_t flags) +{ +#ifndef OPENSSL_NO_QUIC + if (!IS_QUIC_CTX(ctx)) + return NULL; + + return ossl_quic_new_domain(ctx, flags); +#else + return NULL; +#endif +} + int SSL_add_expected_rpk(SSL *s, EVP_PKEY *rpk) { unsigned char *data = NULL; -- 2.47.3