From 78fd5d7dbc72f42d286729d4c3d63314ef9b6159 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 15 Dec 2022 09:33:01 +0100 Subject: [PATCH] Fix wolfSSL_set_SSL_CTX() to be usable during handshake. This method requires some explanation. Its sibling is int SetSSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup) which re-inits the WOLFSSL* with all settings in the new CTX. That one is the right one to use *before* a handshake is started. This method was added by OpenSSL to be used *during* the handshake, e.g. when a server inspects the SNI in a ClientHello callback and decides which set of certificates to use. Since, at the time the SNI callback is run, some decisions on Extensions or the ServerHello might already have been taken, this method is very restricted in what it does: - changing the server certificate(s) - changing the server id for session handling and everything else in WOLFSSL* needs to remain untouched. --- src/ssl.c | 63 ++++++++++++++++++++++++++++++++++-- tests/api.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+), 2 deletions(-) diff --git a/src/ssl.c b/src/ssl.c index fc09d9810..61d60b583 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -31987,9 +31987,68 @@ const char * wolfSSL_get_servername(WOLFSSL* ssl, byte type) WOLFSSL_CTX* wolfSSL_set_SSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx) { - if (ssl && ctx && SetSSL_CTX(ssl, ctx, 0) == WOLFSSL_SUCCESS) + /* This method requires some explanation. Its sibling is + * int SetSSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup) + * which re-inits the WOLFSSL* with all settings in the new CTX. + * That one is the right one to use *before* a handshake is started. + * + * This method was added by OpenSSL to be used *during* the handshake, e.g. + * when a server inspects the SNI in a ClientHello callback and + * decides which set of certificates to use. + * + * Since, at the time the SNI callback is run, some decisions on + * Extensions or the ServerHello might already have been taken, this + * method is very restricted in what it does: + * - changing the server certificate(s) + * - changing the server id for session handling + * and everything else in WOLFSSL* needs to remain untouched. + */ + WOLFSSL_ENTER("wolfSSL_set_SSL_CTX"); + if (ssl == NULL || ctx == NULL) + return NULL; + if (ssl->ctx == ctx) return ssl->ctx; - return NULL; + + if (SSL_CTX_RefCount(ctx, 1) < 0) { + /* can only fail on serious stuff, like mutex not working + * or ctx refcount out of whack. */ + return NULL; + } + if (ssl->ctx) { + wolfSSL_CTX_free(ssl->ctx); + } + ssl->ctx = ctx; + +#ifndef NO_CERTS + /* ctx owns certificate, certChain and key */ + ssl->buffers.certificate = ctx->certificate; + ssl->buffers.certChain = ctx->certChain; +#ifdef WOLFSSL_TLS13 + ssl->buffers.certChainCnt = ctx->certChainCnt; +#endif + ssl->buffers.key = ctx->privateKey; + ssl->buffers.keyType = ctx->privateKeyType; + ssl->buffers.keyId = ctx->privateKeyId; + ssl->buffers.keyLabel = ctx->privateKeyLabel; + ssl->buffers.keySz = ctx->privateKeySz; + ssl->buffers.keyDevId = ctx->privateKeyDevId; + /* flags indicating what certs/keys are available */ + ssl->options.haveRSA = ctx->haveRSA; + ssl->options.haveDH = ctx->haveDH; + ssl->options.haveECDSAsig = ctx->haveECDSAsig; + ssl->options.haveECC = ctx->haveECC; + ssl->options.haveStaticECC = ctx->haveStaticECC; + ssl->options.haveFalconSig = ctx->haveFalconSig; + ssl->options.haveDilithiumSig = ctx->haveDilithiumSig; +#endif + +#ifdef OPENSSL_EXTRA + /* copy over application session context ID */ + ssl->sessionCtxSz = ctx->sessionCtxSz; + XMEMCPY(ssl->sessionCtx, ctx->sessionCtx, ctx->sessionCtxSz); +#endif + + return ssl->ctx; } diff --git a/tests/api.c b/tests/api.c index 43ced5e44..6099083a8 100644 --- a/tests/api.c +++ b/tests/api.c @@ -57299,6 +57299,93 @@ static int test_wolfSSL_CTX_get_min_proto_version(void) return res; } +#if defined(OPENSSL_ALL) || (defined(OPENSSL_EXTRA) && \ + (defined(HAVE_STUNNEL) || defined(WOLFSSL_NGINX) || \ + defined(HAVE_LIGHTY) || defined(WOLFSSL_HAPROXY) || \ + defined(WOLFSSL_OPENSSH) || defined(HAVE_SBLIM_SFCB))) +static int test_wolfSSL_set_SSL_CTX(void) +{ + int res = TEST_SKIPPED; +#if (defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)) \ + && !defined(WOLFSSL_NO_TLS12) && defined(WOLFSSL_TLS13) + WOLFSSL_CTX *ctx1, *ctx2; + WOLFSSL *ssl; + const byte *session_id1 = (const byte *)"CTX1"; + const byte *session_id2 = (const byte *)"CTX2"; + + AssertNotNull(ctx1 = wolfSSL_CTX_new(wolfTLS_server_method())); + AssertTrue(wolfSSL_CTX_use_certificate_file(ctx1, svrCertFile, + WOLFSSL_FILETYPE_PEM)); + AssertTrue(wolfSSL_CTX_use_PrivateKey_file(ctx1, svrKeyFile, + WOLFSSL_FILETYPE_PEM)); + AssertIntEQ(wolfSSL_CTX_set_min_proto_version(ctx1, TLS1_2_VERSION), + WOLFSSL_SUCCESS); + AssertIntEQ(wolfSSL_CTX_get_min_proto_version(ctx1), TLS1_2_VERSION); + AssertIntEQ(wolfSSL_CTX_get_max_proto_version(ctx1), TLS1_3_VERSION); + AssertIntEQ(wolfSSL_CTX_set_session_id_context(ctx1, session_id1, 4), + WOLFSSL_SUCCESS); + + AssertNotNull(ctx2 = wolfSSL_CTX_new(wolfTLS_server_method())); + AssertTrue(wolfSSL_CTX_use_certificate_file(ctx2, svrCertFile, + WOLFSSL_FILETYPE_PEM)); + AssertTrue(wolfSSL_CTX_use_PrivateKey_file(ctx2, svrKeyFile, + WOLFSSL_FILETYPE_PEM)); + AssertIntEQ(wolfSSL_CTX_set_min_proto_version(ctx2, TLS1_2_VERSION), + WOLFSSL_SUCCESS); + AssertIntEQ(wolfSSL_CTX_set_max_proto_version(ctx2, TLS1_2_VERSION), + WOLFSSL_SUCCESS); + AssertIntEQ(wolfSSL_CTX_get_min_proto_version(ctx2), TLS1_2_VERSION); + AssertIntEQ(wolfSSL_CTX_get_max_proto_version(ctx2), TLS1_2_VERSION); + AssertIntEQ(wolfSSL_CTX_set_session_id_context(ctx2, session_id2, 4), + WOLFSSL_SUCCESS); + +#ifdef HAVE_SESSION_TICKET + AssertIntEQ((wolfSSL_CTX_get_options(ctx1) & SSL_OP_NO_TICKET), 0); + wolfSSL_CTX_set_options(ctx2, SSL_OP_NO_TICKET); + AssertIntNE((wolfSSL_CTX_get_options(ctx2) & SSL_OP_NO_TICKET), 0); +#endif + + AssertNotNull(ssl = wolfSSL_new(ctx2)); + AssertIntNE((wolfSSL_get_options(ssl) & WOLFSSL_OP_NO_TLSv1_3), 0); +#ifdef WOLFSSL_INT_H + AssertIntEQ(XMEMCMP(ssl->sessionCtx, session_id2, 4), 0); + AssertTrue(ssl->buffers.certificate == ctx2->certificate); + AssertTrue(ssl->buffers.certChain == ctx2->certChain); +#endif + +#ifdef HAVE_SESSION_TICKET + AssertIntNE((wolfSSL_get_options(ssl) & SSL_OP_NO_TICKET), 0); +#endif + + /* Set the ctx1 that has TLSv1.3 as max proto version */ + AssertNotNull(wolfSSL_set_SSL_CTX(ssl, ctx1)); + + /* MUST not change proto versions of ssl */ + AssertIntNE((wolfSSL_get_options(ssl) & WOLFSSL_OP_NO_TLSv1_3), 0); +#ifdef HAVE_SESSION_TICKET + /* MUST not change */ + AssertIntNE((wolfSSL_get_options(ssl) & SSL_OP_NO_TICKET), 0); +#endif + /* MUST change */ +#ifdef WOLFSSL_INT_H + AssertTrue(ssl->buffers.certificate == ctx1->certificate); + AssertTrue(ssl->buffers.certChain == ctx1->certChain); + AssertIntEQ(XMEMCMP(ssl->sessionCtx, session_id1, 4), 0); +#endif + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx1); + wolfSSL_CTX_free(ctx2); + + res = TEST_RES_CHECK(1); +#endif /* defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL) */ + return res; +} +#endif /* defined(OPENSSL_ALL) || (defined(OPENSSL_EXTRA) && \ + (defined(HAVE_STUNNEL) || defined(WOLFSSL_NGINX) || \ + defined(HAVE_LIGHTY) || defined(WOLFSSL_HAPROXY) || \ + defined(WOLFSSL_OPENSSH) || defined(HAVE_SBLIM_SFCB))) */ + static int test_wolfSSL_security_level(void) { int res = TEST_SKIPPED; @@ -60062,6 +60149,12 @@ TEST_CASE testCases[] = { #endif TEST_DECL(test_wolfSSL_CTX_get_min_proto_version), +#if defined(OPENSSL_ALL) || (defined(OPENSSL_EXTRA) && \ + (defined(HAVE_STUNNEL) || defined(WOLFSSL_NGINX) || \ + defined(HAVE_LIGHTY) || defined(WOLFSSL_HAPROXY) || \ + defined(WOLFSSL_OPENSSH) || defined(HAVE_SBLIM_SFCB))) + TEST_DECL(test_wolfSSL_set_SSL_CTX), +#endif TEST_DECL(test_wolfSSL_security_level), TEST_DECL(test_wolfSSL_SSL_in_init),