From: Hugo Landau Date: Mon, 16 Jan 2023 15:21:29 +0000 (+0000) Subject: QUIC: Control SSL option setting X-Git-Tag: openssl-3.2.0-alpha1~528 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f0d9757cafef98a346088b9f7fa988964e301c67;p=thirdparty%2Fopenssl.git QUIC: Control SSL option setting Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/20061) --- diff --git a/include/internal/quic_ssl.h b/include/internal/quic_ssl.h index cfcd3a6b923..48f05bfb7cb 100644 --- a/include/internal/quic_ssl.h +++ b/include/internal/quic_ssl.h @@ -38,7 +38,6 @@ __owur int ossl_quic_key_update(SSL *s, int update_type); __owur int ossl_quic_get_key_update_type(const SSL *s); __owur int ossl_quic_num_ciphers(void); __owur const SSL_CIPHER *ossl_quic_get_cipher(unsigned int u); -__owur int ossl_quic_set_ssl_op(SSL *ssl, uint64_t op); int ossl_quic_renegotiate_check(SSL *ssl, int initok); typedef struct quic_conn_st QUIC_CONNECTION; @@ -96,6 +95,10 @@ __owur int ossl_quic_get_conn_close_info(SSL *ssl, SSL_CONN_CLOSE_INFO *info, size_t info_len); +uint64_t ossl_quic_set_options(SSL *s, uint64_t opts); +uint64_t ossl_quic_clear_options(SSL *s, uint64_t opts); +uint64_t ossl_quic_get_options(const SSL *s); + /* * Used to override ossl_time_now() for debug purposes. While this may be * overridden at any time, expect strange results if you change it after diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c index 7f79d2b4ade..68f3c21cd50 100644 --- a/ssl/quic/quic_impl.c +++ b/ssl/quic/quic_impl.c @@ -299,6 +299,10 @@ static void quic_unlock(QUIC_CONNECTION *qc) * ossl_quic_deinit * SSL_free => ossl_quic_free * + * SSL_set_options => ossl_quic_set_options + * SSL_get_options => ossl_quic_get_options + * SSL_clear_options => ossl_quic_clear_options + * */ /* SSL_new */ @@ -322,10 +326,13 @@ SSL *ossl_quic_new(SSL_CTX *ctx) qc->tls = ossl_ssl_connection_new_int(ctx, TLS_method()); 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_base; sc->s3.flags |= TLS1_FLAGS_QUIC; + /* Restrict options derived from the SSL_CTX. */ + sc->options &= OSSL_QUIC_PERMITTED_OPTIONS; + #if defined(OPENSSL_THREADS) if ((qc->mutex = ossl_crypto_mutex_new()) == NULL) goto err; @@ -608,6 +615,48 @@ static void qc_set_default_xso(QUIC_CONNECTION *qc, QUIC_XSO *xso, int touch) SSL_free(&old_xso->ssl); } +/* SSL_set_options */ +static uint64_t quic_mask_or_options(SSL *ssl, uint64_t mask_value, uint64_t or_value) +{ + QCTX ctx; + uint64_t r, options; + + if (!expect_quic_with_stream_lock(ssl, /*remote_init=*/-1, &ctx)) + return 0; + + /* + * Currently most options that we permit are handled in the handshake + * layer. + */ + options = (SSL_get_options(ctx.qc->tls) & ~mask_value) | or_value; + options &= OSSL_QUIC_PERMITTED_OPTIONS; + r = SSL_set_options(ctx.qc->tls, options); + + if (ctx.xso->stream != NULL && ctx.xso->stream->rstream != NULL) + ossl_quic_rstream_set_cleanse(ctx.xso->stream->rstream, + (options & SSL_OP_CLEANSE_PLAINTEXT) != 0); + + quic_unlock(ctx.qc); + return r; +} + +uint64_t ossl_quic_set_options(SSL *ssl, uint64_t options) +{ + return quic_mask_or_options(ssl, UINT64_MAX, options); +} + +/* SSL_clear_options */ +uint64_t ossl_quic_clear_options(SSL *ssl, uint64_t options) +{ + return quic_mask_or_options(ssl, options, 0); +} + +/* SSL_get_options */ +uint64_t ossl_quic_get_options(const SSL *ssl) +{ + return quic_mask_or_options((SSL *)ssl, 0, 0); +} + /* * QUIC Front-End I/O API: Network BIO Configuration * ================================================= diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index ad3afe33ebe..4288721eb2e 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -751,6 +751,7 @@ SSL *ossl_ssl_connection_new_int(SSL_CTX *ctx, const SSL_METHOD *method) RECORD_LAYER_init(&s->rlayer, s); s->options = ctx->options; + s->dane.flags = ctx->dane.flags; if (method->version == ctx->method->version) { s->min_proto_version = ctx->min_proto_version; @@ -5888,6 +5889,11 @@ uint64_t SSL_get_options(const SSL *s) { const SSL_CONNECTION *sc = SSL_CONNECTION_FROM_CONST_SSL(s); +#ifndef OPENSSL_NO_QUIC + if (IS_QUIC(s)) + return ossl_quic_get_options(s); +#endif + if (sc == NULL) return 0; @@ -5905,13 +5911,12 @@ uint64_t SSL_set_options(SSL *s, uint64_t op) OSSL_PARAM options[2], *opts = options; #ifndef OPENSSL_NO_QUIC - if (IS_QUIC(s) && ossl_quic_set_ssl_op(s, op)) - /* Handled by QUIC, return as set */ - return op; + if (IS_QUIC(s)) + return ossl_quic_set_options(s, op); #endif - sc = SSL_CONNECTION_FROM_SSL(s); - if (sc == NULL) + sc = SSL_CONNECTION_FROM_SSL(s); + if (sc == NULL) return 0; sc->options |= op; @@ -5935,6 +5940,11 @@ uint64_t SSL_clear_options(SSL *s, uint64_t op) { SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(s); +#ifndef OPENSSL_NO_QUIC + if (IS_QUIC(s)) + return ossl_quic_clear_options(s, op); +#endif + if (sc == NULL) return 0; diff --git a/ssl/ssl_local.h b/ssl/ssl_local.h index 890c757c96f..a24ec27e5a8 100644 --- a/ssl/ssl_local.h +++ b/ssl/ssl_local.h @@ -2999,4 +2999,63 @@ void ossl_ssl_set_custom_record_layer(SSL_CONNECTION *s, const OSSL_RECORD_METHOD *meth, void *rlarg); +/* + * Options which no longer have any effect, but which can be implemented + * as no-ops for QUIC. + */ +#define OSSL_LEGACY_SSL_OPTIONS \ + (SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG | \ + SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER | \ + SSL_OP_SSLEAY_080_CLIENT_DH_BUG | \ + SSL_OP_TLS_D5_BUG | \ + SSL_OP_TLS_BLOCK_PADDING_BUG | \ + SSL_OP_MSIE_SSLV2_RSA_PADDING | \ + SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG | \ + SSL_OP_MICROSOFT_SESS_ID_BUG | \ + SSL_OP_NETSCAPE_CHALLENGE_BUG | \ + SSL_OP_PKCS1_CHECK_1 | \ + SSL_OP_PKCS1_CHECK_2 | \ + SSL_OP_SINGLE_DH_USE | \ + SSL_OP_SINGLE_ECDH_USE | \ + SSL_OP_EPHEMERAL_RSA ) + +/* + * Options which are no-ops under QUIC or TLSv1.3 and which are therefore + * allowed but ignored under QUIC. + */ +#define OSSL_TLS1_2_OPTIONS \ + (SSL_OP_CRYPTOPRO_TLSEXT_BUG | \ + SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS | \ + SSL_OP_ALLOW_CLIENT_RENEGOTIATION | \ + SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION | \ + SSL_OP_NO_COMPRESSION | \ + SSL_OP_NO_SSLv3 | \ + SSL_OP_NO_TLSv1 | \ + SSL_OP_NO_TLSv1_1 | \ + SSL_OP_NO_TLSv1_2 | \ + SSL_OP_NO_DTLSv1 | \ + SSL_OP_NO_DTLSv1_2 | \ + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | \ + SSL_OP_CISCO_ANYCONNECT | \ + SSL_OP_NO_RENEGOTIATION | \ + SSL_OP_NO_EXTENDED_MASTER_SECRET | \ + SSL_OP_NO_ENCRYPT_THEN_MAC | \ + SSL_OP_COOKIE_EXCHANGE | \ + SSL_OP_LEGACY_SERVER_CONNECT | \ + SSL_OP_IGNORE_UNEXPECTED_EOF ) + +/* Total mask of options permitted or ignored under QUIC. */ +#define OSSL_QUIC_PERMITTED_OPTIONS \ + (OSSL_LEGACY_SSL_OPTIONS | \ + OSSL_TLS1_2_OPTIONS | \ + SSL_OP_CIPHER_SERVER_PREFERENCE | \ + SSL_OP_DISABLE_TLSEXT_CA_NAMES | \ + SSL_OP_NO_TX_CERTIFICATE_COMPRESSION | \ + SSL_OP_NO_RX_CERTIFICATE_COMPRESSION | \ + SSL_OP_PRIORITIZE_CHACHA | \ + SSL_OP_CLEANSE_PLAINTEXT | \ + SSL_OP_NO_QUERY_MTU | \ + SSL_OP_NO_TICKET | \ + SSL_OP_NO_ANTI_REPLAY ) + #endif diff --git a/test/quicapitest.c b/test/quicapitest.c index 5cffcff60d2..c2b28f728a2 100644 --- a/test/quicapitest.c +++ b/test/quicapitest.c @@ -17,6 +17,7 @@ #include "helpers/quictestlib.h" #include "testutil.h" #include "testutil/output.h" +#include "../ssl/ssl_local.h" static OSSL_LIB_CTX *libctx = NULL; static OSSL_PROVIDER *defctxnull = NULL; @@ -416,6 +417,47 @@ err: return testresult; } +static int test_quic_forbidden_options(void) +{ + int testresult = 0; + SSL_CTX *ctx = NULL; + SSL *ssl = NULL; + + if (!TEST_ptr(ctx = SSL_CTX_new_ex(libctx, NULL, OSSL_QUIC_client_method()))) + goto err; + + /* QUIC options restrictions do not affect SSL_CTX */ + SSL_CTX_set_options(ctx, UINT64_MAX); + + if (!TEST_uint64_t_eq(SSL_CTX_get_options(ctx), UINT64_MAX)) + goto err; + + if (!TEST_ptr(ssl = SSL_new(ctx))) + goto err; + + /* Only permitted options get transferred to SSL object */ + if (!TEST_uint64_t_eq(SSL_get_options(ssl), OSSL_QUIC_PERMITTED_OPTIONS)) + goto err; + + /* Try again using SSL_set_options */ + SSL_set_options(ssl, UINT64_MAX); + + if (!TEST_uint64_t_eq(SSL_get_options(ssl), OSSL_QUIC_PERMITTED_OPTIONS)) + goto err; + + /* Clear everything */ + SSL_clear_options(ssl, UINT64_MAX); + + if (!TEST_uint64_t_eq(SSL_get_options(ssl), 0)) + goto err; + + testresult = 1; +err: + SSL_free(ssl); + SSL_CTX_free(ctx); + return testresult; +} + OPT_TEST_DECLARE_USAGE("provider config certsdir datadir\n") int setup_tests(void) @@ -479,6 +521,7 @@ int setup_tests(void) #endif ADD_TEST(test_quic_forbidden_apis_ctx); ADD_TEST(test_quic_forbidden_apis); + ADD_TEST(test_quic_forbidden_options); return 1; err: cleanup_tests();