QUIC: Add handling of SSL_get_shutdown()
authorTomas Mraz <tomas@openssl.org>
Tue, 17 Oct 2023 08:00:58 +0000 (10:00 +0200)
committerMatt Caswell <matt@openssl.org>
Fri, 20 Oct 2023 15:29:28 +0000 (16:29 +0100)
Return SSL_SENT_SHUTDOWN and SSL_RECEIVED_SHUTDOWN with semantics
similar to TLS connections.

Reviewed-by: Hugo Landau <hlandau@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/22408)

doc/man3/SSL_CTX_set_quiet_shutdown.pod
doc/man3/SSL_set_shutdown.pod
include/internal/quic_channel.h
include/internal/quic_ssl.h
ssl/quic/quic_channel.c
ssl/quic/quic_impl.c
ssl/ssl_lib.c
test/quicapitest.c

index b7c2a32069174c2cff439b1cd54fb2959b3c1331..4894e2f5d25f9a31b7b47c2c2f9c75d0d9031aa6 100644 (file)
@@ -2,7 +2,8 @@
 
 =head1 NAME
 
-SSL_CTX_set_quiet_shutdown, SSL_CTX_get_quiet_shutdown, SSL_set_quiet_shutdown, SSL_get_quiet_shutdown - manipulate shutdown behaviour
+SSL_CTX_set_quiet_shutdown, SSL_CTX_get_quiet_shutdown, SSL_set_quiet_shutdown,
+SSL_get_quiet_shutdown - manipulate shutdown behaviour
 
 =head1 SYNOPSIS
 
@@ -54,7 +55,7 @@ The default is normal shutdown behaviour as described by the TLS standard.
 SSL_CTX_set_quiet_shutdown() and SSL_set_quiet_shutdown() do not return
 diagnostic information.
 
-SSL_CTX_get_quiet_shutdown() and SSL_get_quiet_shutdown return the current
+SSL_CTX_get_quiet_shutdown() and SSL_get_quiet_shutdown() return the current
 setting.
 
 =head1 SEE ALSO
index c3b613a247d1e68ab864a3febc01864a799f1181..9a7eb463a8275823b3d9ad26db50a8387d0e4225 100644 (file)
@@ -57,13 +57,21 @@ If a close_notify was received, SSL_RECEIVED_SHUTDOWN will be set,
 for setting SSL_SENT_SHUTDOWN the application must however still call
 L<SSL_shutdown(3)> or SSL_set_shutdown() itself.
 
-These functions are not supported for QUIC SSL objects.
+SSL_set_shutdown() is not supported for QUIC SSL objects.
 
 =head1 RETURN VALUES
 
 SSL_set_shutdown() does not return diagnostic information.
 
-SSL_get_shutdown() returns the current setting.
+SSL_get_shutdown() returns the current shutdown state as set or based
+on the actual connection state.
+
+SSL_get_shutdown() returns 0 if called on a QUIC stream SSL object. If it
+is called on a QUIC connection SSL object, it returns a value with
+SSL_SENT_SHUTDOWN set if CONNECTION_CLOSE has been sent to the peer and
+it returns a value with SSL_RECEIVED_SHUTDOWN set if CONNECTION_CLOSE
+has been received from the peer or the QUIC connection is fully terminated
+for other reasons.
 
 =head1 SEE ALSO
 
index 0841001c23da97aed31a512c09132e3d05aa5e6a..f46db0637e91c6a34745f56b6822cd164035c7c5 100644 (file)
@@ -320,6 +320,7 @@ QUIC_STREAM *ossl_quic_channel_get_stream_by_id(QUIC_CHANNEL *ch,
 int ossl_quic_channel_is_term_any(const QUIC_CHANNEL *ch);
 const QUIC_TERMINATE_CAUSE *
 ossl_quic_channel_get_terminate_cause(const QUIC_CHANNEL *ch);
+int ossl_quic_channel_is_closing(const QUIC_CHANNEL *ch);
 int ossl_quic_channel_is_terminated(const QUIC_CHANNEL *ch);
 int ossl_quic_channel_is_active(const QUIC_CHANNEL *ch);
 int ossl_quic_channel_is_handshake_complete(const QUIC_CHANNEL *ch);
index 66cea1bfe12ce5962fa59e26ed187522726d4f6f..52d4527c811021e172d3af247181cc834ffc5a02 100644 (file)
@@ -125,6 +125,7 @@ void ossl_quic_conn_force_assist_thread_wake(SSL *s);
 QUIC_CHANNEL *ossl_quic_conn_get_channel(SSL *s);
 
 int ossl_quic_has_pending(const SSL *s);
+int ossl_quic_get_shutdown(const SSL *s);
 
 # endif
 
index 3b9993b96adb57d370dcbb31235bd91d4fc6dde3..8e75eda539f44ec7c6c958ff758a1095fd48c519 100644 (file)
@@ -635,7 +635,7 @@ int ossl_quic_channel_is_active(const QUIC_CHANNEL *ch)
     return ch != NULL && ch->state == QUIC_CHANNEL_STATE_ACTIVE;
 }
 
-static int ossl_quic_channel_is_closing(const QUIC_CHANNEL *ch)
+int ossl_quic_channel_is_closing(const QUIC_CHANNEL *ch)
 {
     return ch->state == QUIC_CHANNEL_STATE_TERMINATING_CLOSING;
 }
index 29a283dca0b27707fc91bb9ffce1af047c8e65e4..bdf5d5fea882dea4cce5f73a741b8bf89352c8f1 100644 (file)
@@ -3571,6 +3571,27 @@ const SSL_CIPHER *ossl_quic_get_cipher(unsigned int u)
     return NULL;
 }
 
+/*
+ * SSL_get_shutdown()
+ * ------------------
+ */
+int ossl_quic_get_shutdown(const SSL *s)
+{
+    QCTX ctx;
+    int shut = 0;
+
+    if (!expect_quic_conn_only(s, &ctx))
+        return 0;
+
+    if (ossl_quic_channel_is_term_any(ctx.qc->ch)) {
+        shut |= SSL_SENT_SHUTDOWN;
+        if (!ossl_quic_channel_is_closing(ctx.qc->ch))
+            shut |= SSL_RECEIVED_SHUTDOWN;
+    }
+
+    return shut;
+}
+
 /*
  * Internal Testing APIs
  * =====================
index f15fe126a22f54b2d85e186592a1450757a2ce40..bd9160b7561b2e9076f4e0e5fe93655a37ac5425 100644 (file)
@@ -5142,7 +5142,7 @@ void SSL_set_quiet_shutdown(SSL *s, int mode)
 {
     SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL_ONLY(s);
 
-    /* TODO(QUIC): Currently not supported for QUIC. */
+    /* Not supported with QUIC */
     if (sc == NULL)
         return;
 
@@ -5153,7 +5153,7 @@ int SSL_get_quiet_shutdown(const SSL *s)
 {
     const SSL_CONNECTION *sc = SSL_CONNECTION_FROM_CONST_SSL_ONLY(s);
 
-    /* TODO(QUIC): Currently not supported for QUIC. */
+    /* Not supported with QUIC */
     if (sc == NULL)
         return 0;
 
@@ -5164,7 +5164,7 @@ void SSL_set_shutdown(SSL *s, int mode)
 {
     SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL_ONLY(s);
 
-    /* TODO(QUIC): Do we want this for QUIC? */
+    /* Not supported with QUIC */
     if (sc == NULL)
         return;
 
@@ -5175,7 +5175,12 @@ int SSL_get_shutdown(const SSL *s)
 {
     const SSL_CONNECTION *sc = SSL_CONNECTION_FROM_CONST_SSL_ONLY(s);
 
-    /* TODO(QUIC): Do we want this for QUIC? */
+#ifndef OPENSSL_NO_QUIC
+    /* QUIC: Just indicate whether the connection was shutdown cleanly. */
+    if (IS_QUIC(s))
+        return ossl_quic_get_shutdown(s);
+#endif
+
     if (sc == NULL)
         return 0;
 
index 81c8c215bd43a9666754f0228a0efe39d60148b9..37d7803005cb93aecea7f55ab0c30c6487c9501c 100644 (file)
@@ -1335,6 +1335,52 @@ static int test_alpn(int idx)
     return testresult;
 }
 
+/*
+ * Test SSL_get_shutdown() behavior.
+ */
+static int test_get_shutdown(void)
+{
+    SSL_CTX *cctx = SSL_CTX_new_ex(libctx, NULL, OSSL_QUIC_client_method());
+    SSL *clientquic = NULL;
+    QUIC_TSERVER *qtserv = NULL;
+    int testresult = 0;
+
+    if (!TEST_ptr(cctx)
+            || !TEST_true(qtest_create_quic_objects(libctx, cctx, NULL, cert,
+                                                    privkey,
+                                                    QTEST_FLAG_FAKE_TIME,
+                                                    &qtserv, &clientquic,
+                                                    NULL, NULL))
+            || !TEST_true(qtest_create_quic_connection(qtserv, clientquic)))
+        goto err;
+
+    if (!TEST_int_eq(SSL_get_shutdown(clientquic), 0))
+        goto err;
+
+    if (!TEST_int_eq(SSL_shutdown(clientquic), 0))
+        goto err;
+
+    if (!TEST_int_eq(SSL_get_shutdown(clientquic), SSL_SENT_SHUTDOWN))
+        goto err;
+
+    do {
+        ossl_quic_tserver_tick(qtserv);
+        qtest_add_time(100);
+    } while (SSL_shutdown(clientquic) == 0);
+
+    if (!TEST_int_eq(SSL_get_shutdown(clientquic),
+                     SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN))
+        goto err;
+
+    testresult = 1;
+ err:
+    ossl_quic_tserver_free(qtserv);
+    SSL_free(clientquic);
+    SSL_CTX_free(cctx);
+
+    return testresult;
+}
+
 #define MAX_LOOPS   2000
 
 /*
@@ -1586,6 +1632,7 @@ int setup_tests(void)
     ADD_ALL_TESTS(test_client_auth, 2);
     ADD_ALL_TESTS(test_alpn, 2);
     ADD_ALL_TESTS(test_noisy_dgram, 2);
+    ADD_TEST(test_get_shutdown);
 
     return 1;
  err: