QUIC APL/CHANNEL: Wire up connection closure reason
authorHugo Landau <hlandau@openssl.org>
Wed, 26 Jul 2023 17:10:16 +0000 (18:10 +0100)
committerHugo Landau <hlandau@openssl.org>
Thu, 10 Aug 2023 17:19:45 +0000 (18:19 +0100)
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/21565)

include/internal/quic_channel.h
include/openssl/ssl.h.in
ssl/quic/quic_channel.c
ssl/quic/quic_impl.c
ssl/quic/quic_tserver.c

index e36acb4eebec8e74719b4bcdfe9d27835fe16646..2524a65fe770b3db84d2c8f22c522345bf019273 100644 (file)
@@ -149,6 +149,21 @@ typedef struct quic_terminate_cause_st {
      */
     uint64_t                        frame_type;
 
+    /*
+     * Optional reason string. When calling ossl_quic_channel_local_close, if a
+     * reason string pointer is passed, it is copied and stored inside
+     * QUIC_CHANNEL for the remainder of the lifetime of the channel object.
+     * Thus the string pointed to by this value, if non-NULL, is valid for the
+     * lifetime of the QUIC_CHANNEL object.
+     */
+    const char                      *reason;
+
+    /*
+     * Length of reason in bytes. The reason is supposed to contain a UTF-8
+     * string but may be arbitrary data if the reason came from the network.
+     */
+    size_t                          reason_len;
+
     /* Is this error code in the transport (0) or application (1) space? */
     unsigned int                    app : 1;
 
@@ -196,7 +211,8 @@ int ossl_quic_channel_set_mutator(QUIC_CHANNEL *ch,
 int ossl_quic_channel_start(QUIC_CHANNEL *ch);
 
 /* Start a locally initiated connection shutdown. */
-void ossl_quic_channel_local_close(QUIC_CHANNEL *ch, uint64_t app_error_code);
+void ossl_quic_channel_local_close(QUIC_CHANNEL *ch, uint64_t app_error_code,
+                                   const char *app_reason);
 
 /*
  * Called when the handshake is confirmed.
index beedd8956d60b4c6f8348b449055967933a16905..66ebaa4784a54384ce408020aabb6fdeabc63029 100644 (file)
@@ -2339,10 +2339,10 @@ __owur int SSL_get_stream_read_error_code(SSL *ssl, uint64_t *app_error_code);
 __owur int SSL_get_stream_write_error_code(SSL *ssl, uint64_t *app_error_code);
 
 typedef struct ssl_conn_close_info_st {
-    uint64_t error_code;
-    char     *reason;
-    size_t    reason_len;
-    int       is_local, is_transport;
+    uint64_t    error_code;
+    const char  *reason;
+    size_t      reason_len;
+    int         is_local, is_transport;
 } SSL_CONN_CLOSE_INFO;
 
 __owur int SSL_get_conn_close_info(SSL *ssl,
index 6931f79d011682147623b416d1a9bad1a05b896b..a4f451d6b913ade93af013d1121e76bf4651d749 100644 (file)
@@ -371,6 +371,7 @@ static void ch_cleanup(QUIC_CHANNEL *ch)
     ossl_qrx_free(ch->qrx);
     ossl_quic_demux_free(ch->demux);
     OPENSSL_free(ch->local_transport_params);
+    OPENSSL_free((char *)ch->terminate_cause.reason);
     OSSL_ERR_STATE_free(ch->err_state);
 }
 
@@ -2438,7 +2439,8 @@ int ossl_quic_channel_start(QUIC_CHANNEL *ch)
 }
 
 /* Start a locally initiated connection shutdown. */
-void ossl_quic_channel_local_close(QUIC_CHANNEL *ch, uint64_t app_error_code)
+void ossl_quic_channel_local_close(QUIC_CHANNEL *ch, uint64_t app_error_code,
+                                   const char *app_reason)
 {
     QUIC_TERMINATE_CAUSE tcause = {0};
 
@@ -2447,6 +2449,8 @@ void ossl_quic_channel_local_close(QUIC_CHANNEL *ch, uint64_t app_error_code)
 
     tcause.app          = 1;
     tcause.error_code   = app_error_code;
+    tcause.reason       = app_reason;
+    tcause.reason_len   = app_reason != NULL ? strlen(app_reason) : 0;
     ch_start_terminating(ch, &tcause, 0);
 }
 
@@ -2622,6 +2626,37 @@ int ossl_quic_channel_on_handshake_confirmed(QUIC_CHANNEL *ch)
  *      closing state and send a packet containing a CONNECTION_CLOSE
  *      frame in response to any UDP datagram that is received.
  */
+static void copy_tcause(QUIC_TERMINATE_CAUSE *dst,
+                        const QUIC_TERMINATE_CAUSE *src)
+{
+    dst->error_code = src->error_code;
+    dst->frame_type = src->frame_type;
+    dst->app        = src->app;
+    dst->remote     = src->remote;
+
+    dst->reason     = NULL;
+    dst->reason_len = 0;
+
+    if (src->reason != NULL && src->reason_len > 0) {
+        size_t l = src->reason_len;
+        char *r;
+
+        if (l >= SIZE_MAX)
+            --l;
+
+        /*
+         * If this fails, dst->reason becomes NULL and we simply do not use a
+         * reason. This ensures termination is infallible.
+         */
+        dst->reason = r = OPENSSL_memdup(src->reason, l + 1);
+        if (r == NULL)
+            return;
+
+        r[l]  = '\0';
+        dst->reason_len = l;
+    }
+}
+
 static void ch_start_terminating(QUIC_CHANNEL *ch,
                                  const QUIC_TERMINATE_CAUSE *tcause,
                                  int force_immediate)
@@ -2629,12 +2664,12 @@ static void ch_start_terminating(QUIC_CHANNEL *ch,
     switch (ch->state) {
     default:
     case QUIC_CHANNEL_STATE_IDLE:
-        ch->terminate_cause = *tcause;
+        copy_tcause(&ch->terminate_cause, tcause);
         ch_on_terminating_timeout(ch);
         break;
 
     case QUIC_CHANNEL_STATE_ACTIVE:
-        ch->terminate_cause = *tcause;
+        copy_tcause(&ch->terminate_cause, tcause);
 
         if (!force_immediate) {
             ch->state = tcause->remote ? QUIC_CHANNEL_STATE_TERMINATING_DRAINING
@@ -2656,6 +2691,8 @@ static void ch_start_terminating(QUIC_CHANNEL *ch,
                 f.error_code = ch->terminate_cause.error_code;
                 f.frame_type = ch->terminate_cause.frame_type;
                 f.is_app     = ch->terminate_cause.app;
+                f.reason     = (char *)ch->terminate_cause.reason;
+                f.reason_len = ch->terminate_cause.reason_len;
                 ossl_quic_tx_packetiser_schedule_conn_close(ch->txp, &f);
                 /*
                  * RFC 9000 s. 10.2.2 Draining Connection State:
@@ -2714,7 +2751,8 @@ void ossl_quic_channel_on_remote_conn_close(QUIC_CHANNEL *ch,
     tcause.app        = f->is_app;
     tcause.error_code = f->error_code;
     tcause.frame_type = f->frame_type;
-
+    tcause.reason     = f->reason;
+    tcause.reason_len = f->reason_len;
     ch_start_terminating(ch, &tcause, 0);
 }
 
@@ -2967,6 +3005,7 @@ void ossl_quic_channel_raise_protocol_error_loc(QUIC_CHANNEL *ch,
 
     tcause.error_code = error_code;
     tcause.frame_type = frame_type;
+    tcause.reason     = reason;
 
     ch_start_terminating(ch, &tcause, 0);
 }
index ca00fcd476b42cd0a845ae400a5f8c73087cbbc4..ba5b27eeb4acbc6eedd37cb7a21c476f11f72dbb 100644 (file)
@@ -1200,7 +1200,8 @@ int ossl_quic_conn_shutdown(SSL *s, uint64_t flags,
 
     /* Phase 2: Connection Closure */
     ossl_quic_channel_local_close(ctx.qc->ch,
-                                  args != NULL ? args->quic_error_code : 0);
+                                  args != NULL ? args->quic_error_code : 0,
+                                  args != NULL ? args->quic_reason : NULL);
 
     SSL_set_shutdown(ctx.qc->tls, SSL_SENT_SHUTDOWN);
 
@@ -3043,8 +3044,8 @@ int ossl_quic_get_conn_close_info(SSL *ssl,
         return 0;
 
     info->error_code    = tc->error_code;
-    info->reason        = NULL; /* TODO(QUIC): Wire reason */
-    info->reason_len    = 0;
+    info->reason        = tc->reason;
+    info->reason_len    = tc->reason_len;
     info->is_local      = !tc->remote;
     info->is_transport  = !tc->app;
     return 1;
index 15444ecc5bc1e4e3d4a81429cd612987fd3c497c..b92b1de8da043fbadf883cc39b48fbb75dd84101 100644 (file)
@@ -488,7 +488,7 @@ OSSL_TIME ossl_quic_tserver_get_deadline(QUIC_TSERVER *srv)
 
 int ossl_quic_tserver_shutdown(QUIC_TSERVER *srv)
 {
-    ossl_quic_channel_local_close(srv->ch, 0);
+    ossl_quic_channel_local_close(srv->ch, 0, NULL);
 
     /* TODO(QUIC): !SSL_SHUTDOWN_FLAG_NO_STREAM_FLUSH */