QUIC: Control SSL option setting
authorHugo Landau <hlandau@openssl.org>
Mon, 16 Jan 2023 15:21:29 +0000 (15:21 +0000)
committerPauli <pauli@openssl.org>
Tue, 4 Jul 2023 23:02:26 +0000 (09:02 +1000)
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/20061)

include/internal/quic_ssl.h
ssl/quic/quic_impl.c
ssl/ssl_lib.c
ssl/ssl_local.h
test/quicapitest.c

index cfcd3a6b923e6bf3eed8b136546a7caa126fe061..48f05bfb7cb0a88da385ee1db19f64696f49f0c4 100644 (file)
@@ -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
index 7f79d2b4ade8e8778edfe021adf75e0ff13e5d15..68f3c21cd50cab9c52bfc16dd09781591e1b04e1 100644 (file)
@@ -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
  * =================================================
index ad3afe33ebec9089d236dff05fced964adf1ecae..4288721eb2efba9dba2798715d0be04d1fb5874e 100644 (file)
@@ -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;
 
index 890c757c96fb46272a6d93e6eb1108b7a1e91f37..a24ec27e5a858845c5e11559761f37bd120d79fb 100644 (file)
@@ -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
index 5cffcff60d2827535354b96afbe450e4b989d9e9..c2b28f728a29a1a9550ad5f84155553a3c637ddd 100644 (file)
@@ -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();