[Openvpn-devel] Improve data key id not found error message

Message ID 20220824093723.64618-1-arne@rfc2549.org
State Superseded
Headers show
Series [Openvpn-devel] Improve data key id not found error message | expand

Commit Message

Arne Schwabe Aug. 23, 2022, 11:37 p.m. UTC
With delayed data key generation now with deferred auth, NCP and similar
mechanism the "TLS Error: local/remote TLS keys are out of sync" is shown
much too frequent and confuses a lot of people.

This also removes the dead code of printing multi not ready keys and
replace it with an assert.

Factor out printing of error messages into an extra function to make
the code easier to understand and also to only call into that function
in the case that a key is not found and avoid the overhead.

Signed-off-by: Arne Schwabe <arne@rfc2549.org>
---
 src/openvpn/ssl.c | 74 +++++++++++++++++++++++++++++++++++------------
 1 file changed, 56 insertions(+), 18 deletions(-)

Comments

Frank Lichtenheld Aug. 24, 2022, 12:34 a.m. UTC | #1
On Wed, Aug 24, 2022 at 11:37:23AM +0200, Arne Schwabe wrote:
> With delayed data key generation now with deferred auth, NCP and similar
> mechanism the "TLS Error: local/remote TLS keys are out of sync" is shown
> much too frequent and confuses a lot of people.
> 
> This also removes the dead code of printing multi not ready keys and
> replace it with an assert.
> 
> Factor out printing of error messages into an extra function to make
> the code easier to understand and also to only call into that function
> in the case that a key is not found and avoid the overhead.

Okay, I have to say that this patch confuses me. Might be my limited
understanding of the involved structures, though.

> diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
> index 33e145b3f..6a3473944 100644
> --- a/src/openvpn/ssl.c
> +++ b/src/openvpn/ssl.c
> @@ -3202,11 +3202,61 @@ nohard:
>      return (tas == TLS_AUTHENTICATION_FAILED) ? TLSMP_KILL : active;
>  }
>  
> -/*
> - * Pre and post-process the encryption & decryption buffers in order
> - * to implement a multiplexed TLS channel over the TCP/UDP port.
> +/**
> + * We have not found a matching key to decrypt data channel packet,
> + * try to generate a sensible error message and print it
>   */
> +static void
> +print_key_id_not_found_reason(struct tls_multi *multi,
> +                              const struct link_socket_actual *from, int key_id)
> +{
> +    struct gc_arena gc = gc_new();
> +    const char *source = print_link_socket_actual(from, &gc);
> +
> +
> +    for (int i = 0; i < KEY_SCAN_SIZE; ++i)
> +    {
> +        struct key_state *ks = get_key_scan(multi, i);
> +        /*
> +         * Our key state has been progressed far enough to be part of an valid
> +         * session but has not generated keys. Since there can
> +         * be multiple valid/semi valid key states with key id 0 (especially in
> +         * p2p mode when a client reconnects), we still need
> +         * to loop through all keys and only remember here that there is at
> +         * least one key that is not ready yet*/
> +        if (ks->state >= S_INITIAL && key_id < S_GENERATED_KEYS)

Why are you comparing key_id to a state value? Shouldn't you compare
ks->state to S_GENERATED_KEYS and ks->key_id to the key_id?

> +        {
> +            msg(D_MULTI_DROPPED,
> +                "Key %s [%d] not initialized (yet), dropping packet.",
> +                source, key_id);
> +            gc_free(&gc);
> +            return;
> +        }
> +        if (ks->state >= S_ACTIVE && ks->authenticated == KS_AUTH_FALSE)

While here we do check key_id at all?

> +        {
> +            msg(D_MULTI_DROPPED,
> +                "Key %s [%d] no longer authorized (yet), dropping packet.",
> +                source, key_id);
> +            gc_free(&gc);
> +            return;
> +        }
> +    }


Regards,

Patch

diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 33e145b3f..6a3473944 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -3202,11 +3202,61 @@  nohard:
     return (tas == TLS_AUTHENTICATION_FAILED) ? TLSMP_KILL : active;
 }
 
-/*
- * Pre and post-process the encryption & decryption buffers in order
- * to implement a multiplexed TLS channel over the TCP/UDP port.
+/**
+ * We have not found a matching key to decrypt data channel packet,
+ * try to generate a sensible error message and print it
  */
+static void
+print_key_id_not_found_reason(struct tls_multi *multi,
+                              const struct link_socket_actual *from, int key_id)
+{
+    struct gc_arena gc = gc_new();
+    const char *source = print_link_socket_actual(from, &gc);
+
+
+    for (int i = 0; i < KEY_SCAN_SIZE; ++i)
+    {
+        struct key_state *ks = get_key_scan(multi, i);
+        /*
+         * Our key state has been progressed far enough to be part of an valid
+         * session but has not generated keys. Since there can
+         * be multiple valid/semi valid key states with key id 0 (especially in
+         * p2p mode when a client reconnects), we still need
+         * to loop through all keys and only remember here that there is at
+         * least one key that is not ready yet*/
+        if (ks->state >= S_INITIAL && key_id < S_GENERATED_KEYS)
+        {
+            msg(D_MULTI_DROPPED,
+                "Key %s [%d] not initialized (yet), dropping packet.",
+                source, key_id);
+            gc_free(&gc);
+            return;
+        }
+        if (ks->state >= S_ACTIVE && ks->authenticated == KS_AUTH_FALSE)
+        {
+            msg(D_MULTI_DROPPED,
+                "Key %s [%d] no longer authorized (yet), dropping packet.",
+                source, key_id);
+            gc_free(&gc);
+            return;
+        }
+    }
 
+    msg(D_TLS_ERRORS,
+        "TLS Error: local/remote TLS keys are out of sync: %s "
+        "(received key id: %d, known key ids: %s)",
+        source, key_id,
+        print_key_id(multi, &gc));
+    gc_free(&gc);
+}
+
+/**
+ * Check the keyid of the an incoming data channel packet and
+ * return the matching crypto parameters in \c opt if found.
+ * Also move the \c buf to the start of the encrypted data, skipping
+ * the opcode and peer id header and setting also set \c ad_start for
+ * AEAD ciphers to the start of the authenticated data.
+ */
 static inline void
 handle_data_channel_packet(struct tls_multi *multi,
                            const struct link_socket_actual *from,
@@ -3221,7 +3271,6 @@  handle_data_channel_packet(struct tls_multi *multi,
     int op = c >> P_OPCODE_SHIFT;
     int key_id = c & P_KEY_ID_MASK;
 
-    /* data channel packet */
     for (int i = 0; i < KEY_SCAN_SIZE; ++i)
     {
         struct key_state *ks = get_key_scan(multi, i);
@@ -3243,14 +3292,7 @@  handle_data_channel_packet(struct tls_multi *multi,
             && ks->authenticated == KS_AUTH_TRUE
             && (floated || link_socket_actual_match(from, &ks->remote_addr)))
         {
-            if (!ks->crypto_options.key_ctx_bi.initialized)
-            {
-                msg(D_MULTI_DROPPED,
-                    "Key %s [%d] not initialized (yet), dropping packet.",
-                    print_link_socket_actual(from, &gc), key_id);
-                goto done;
-            }
-
+            ASSERT(ks->crypto_options.key_ctx_bi.initialized);
             /* return appropriate data channel decrypt key in opt */
             *opt = &ks->crypto_options;
             if (op == P_DATA_V2)
@@ -3284,17 +3326,13 @@  handle_data_channel_packet(struct tls_multi *multi,
         }
     }
 
-    msg(D_TLS_ERRORS,
-        "TLS Error: local/remote TLS keys are out of sync: %s "
-        "(received key id: %d, known key ids: %s)",
-        print_link_socket_actual(from, &gc), key_id,
-        print_key_id(multi, &gc));
+    print_key_id_not_found_reason(multi, from, key_id);
 
 done:
+    gc_free(&gc);
     tls_clear_error();
     buf->len = 0;
     *opt = NULL;
-    gc_free(&gc);
 }
 
 /*