Add support for the msg_callback
authorMatt Caswell <matt@openssl.org>
Fri, 18 Nov 2022 11:39:33 +0000 (11:39 +0000)
committerMatt Caswell <matt@openssl.org>
Tue, 24 Jan 2023 17:16:29 +0000 (17:16 +0000)
Having support for the msg_callback will improve debug capabilities.

For record headers we "manufacture" dummy ones so that as far as the
callback is concerned we are doing "normal" TLS.

Reviewed-by: Hugo Landau <hlandau@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/19748)

ssl/quic/quic_tls.c

index 4a2d5bb043de9fd04a59e633befc4c528b4b6c08..d7e3dd4acdcab26159cab1c879fc1dd9e6ccda83 100644 (file)
@@ -40,6 +40,10 @@ struct quic_tls_st {
 
 struct ossl_record_layer_st {
     QUIC_TLS *qtls;
+
+    /* Protection level */
+    int level;
+
     /* Only used for retry flags */
     BIO *dummybio;
 
@@ -62,6 +66,10 @@ struct ossl_record_layer_st {
 
     /* Set if recbuf is populated with data */
     unsigned int recread : 1;
+
+    /* Callbacks */
+    OSSL_FUNC_rlayer_msg_callback_fn *msg_callback;
+    void *cbarg;
 };
 
 static int
@@ -90,9 +98,25 @@ quic_new_record_layer(OSSL_LIB_CTX *libctx, const char *propq, int vers,
     }
 
     rl->qtls = (QUIC_TLS *)rlarg;
+    rl->level = level;
     rl->dummybio = transport;
+    rl->cbarg = cbarg;
     *retrl = rl;
 
+    if (fns != NULL) {
+        for (; fns->function_id != 0; fns++) {
+            switch (fns->function_id) {
+                break;
+            case OSSL_FUNC_RLAYER_MSG_CALLBACK:
+                rl->msg_callback = OSSL_FUNC_rlayer_msg_callback(fns);
+                break;
+            default:
+                /* Just ignore anything we don't understand */
+                break;
+            }
+        }
+    }
+
     switch (level) {
     case OSSL_RECORD_PROTECTION_LEVEL_NONE:
         return 1;
@@ -209,6 +233,36 @@ static int quic_write_records(OSSL_RECORD_LAYER *rl,
 
     BIO_clear_retry_flags(rl->dummybio);
 
+    if (rl->msg_callback != NULL) {
+        unsigned char dummyrec[SSL3_RT_HEADER_LENGTH];
+
+        /*
+         * For the purposes of the callback we "pretend" to be normal TLS,
+         * and manufacture a dummy record header
+         */
+        dummyrec[0] = (rl->level == OSSL_RECORD_PROTECTION_LEVEL_NONE)
+                        ? template->type
+                        : SSL3_RT_APPLICATION_DATA;
+        dummyrec[1] = (unsigned char)((template->version >> 8) & 0xff);
+        dummyrec[2] = (unsigned char)(template->version & 0xff);
+        /*
+         * We assume that buflen is always <= UINT16_MAX. Since this is
+         * generated by libssl itself we actually expect it to never
+         * exceed SSL3_RT_MAX_PLAIN_LENGTH - so it should be a safe
+         * assumption
+         */
+        dummyrec[3] = (unsigned char)((template->buflen >> 8) & 0xff);
+        dummyrec[4] = (unsigned char)(template->buflen & 0xff);
+
+        rl->msg_callback(1, TLS1_3_VERSION, SSL3_RT_HEADER, dummyrec,
+                            SSL3_RT_HEADER_LENGTH, rl->cbarg);
+
+        if (rl->level != OSSL_RECORD_PROTECTION_LEVEL_NONE) {
+            rl->msg_callback(1, TLS1_3_VERSION, SSL3_RT_INNER_CONTENT_TYPE,
+                             &template->type, 1, rl->cbarg);
+        }
+    }
+
     switch (template->type) {
     case SSL3_RT_ALERT:
         if (template->buflen != 2) {
@@ -315,6 +369,31 @@ static int quic_read_record(OSSL_RECORD_LAYER *rl, void **rechandle,
     rl->recread = 1;
     /* epoch/seq_num are not relevant for TLS */
 
+    if (rl->msg_callback != NULL) {
+        unsigned char dummyrec[SSL3_RT_HEADER_LENGTH];
+
+        /*
+         * For the purposes of the callback we "pretend" to be normal TLS,
+         * and manufacture a dummy record header
+         */
+        dummyrec[0] = (rl->level == OSSL_RECORD_PROTECTION_LEVEL_NONE)
+                      ? SSL3_RT_HANDSHAKE
+                      : SSL3_RT_APPLICATION_DATA;
+        dummyrec[1] = (unsigned char)((TLS1_2_VERSION >> 8) & 0xff);
+        dummyrec[2] = (unsigned char)(TLS1_2_VERSION & 0xff);
+        /*
+         * *datalen will always fit into 2 bytes because our original buffer
+         * size is less than that.
+         */
+        dummyrec[3] = (unsigned char)((*datalen >> 8) & 0xff);
+        dummyrec[4] = (unsigned char)(*datalen & 0xff);
+
+        rl->msg_callback(0, TLS1_3_VERSION, SSL3_RT_HEADER, dummyrec,
+                         SSL3_RT_HEADER_LENGTH, rl->cbarg);
+        rl->msg_callback(0, TLS1_3_VERSION, SSL3_RT_INNER_CONTENT_TYPE, type, 1,
+                         rl->cbarg);
+    }
+
     return OSSL_RECORD_RETURN_SUCCESS;
 }