]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Allow trailing \r and \n in control channel message
authorArne Schwabe <arne@rfc2549.org>
Wed, 10 Jul 2024 14:06:23 +0000 (16:06 +0200)
committerGert Doering <gert@greenie.muc.de>
Wed, 17 Jul 2024 18:55:58 +0000 (20:55 +0200)
Writing a reason from a script will easily end up adding extra \r\n characters
at the end of the reason. Our current code pushes this to the peer. So be more
liberal in accepting these message.

Github: closes OpenVPN/openvpn#568

Change-Id: I47c992b6b73b1475cbff8a28f720cf50dc1fbe3e
Signed-off-by: Arne Schwabe <arne@rfc2549.org>
Acked-by: Frank Lichtenheld <frank@lichtenheld.com>
Message-Id: <20240710140623.172829-1-frank@lichtenheld.com>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg28910.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>
(cherry picked from commit be31325e1dfdffbb152374985c2ae7b6644e3519)

src/openvpn/forward.c
src/openvpn/ssl_pkt.c
src/openvpn/ssl_pkt.h
tests/unit_tests/openvpn/test_pkt.c

index ce71752469f674f9af6ba807172c32012e7d6895..0798337c19d90472681c6f323b995cd59aef515b 100644 (file)
@@ -290,41 +290,14 @@ check_incoming_control_channel(struct context *c)
     struct buffer buf = alloc_buf_gc(len, &gc);
     if (tls_rec_payload(c->c2.tls_multi, &buf))
     {
-
         while (BLEN(&buf) > 1)
         {
-            /* commands on the control channel are seperated by 0x00 bytes.
-             * cmdlen does not include the 0 byte of the string */
-            int cmdlen = (int)strnlen(BSTR(&buf), BLEN(&buf));
-
-            if (cmdlen < BLEN(&buf))
-            {
-                /* include the NUL byte and ensure NUL termination */
-                int cmdlen = (int)strlen(BSTR(&buf)) + 1;
+            struct buffer cmdbuf = extract_command_buffer(&buf, &gc);
 
-                /* Construct a buffer that only holds the current command and
-                 * its closing NUL byte */
-                struct buffer cmdbuf = alloc_buf_gc(cmdlen, &gc);
-                buf_write(&cmdbuf, BPTR(&buf), cmdlen);
-
-                /* check we have only printable characters or null byte in the
-                 * command string and no newlines */
-                if (!string_check_buf(&buf, CC_PRINT | CC_NULL, CC_CRLF))
-                {
-                    msg(D_PUSH_ERRORS, "WARNING: Received control with invalid characters: %s",
-                        format_hex(BPTR(&buf), BLEN(&buf), 256, &gc));
-                }
-                else
-                {
-                    parse_incoming_control_channel_command(c, &cmdbuf);
-                }
-            }
-            else
+            if (cmdbuf.len > 0)
             {
-                msg(D_PUSH_ERRORS, "WARNING: Ignoring control channel "
-                    "message command without NUL termination");
+                parse_incoming_control_channel_command(c, &cmdbuf);
             }
-            buf_advance(&buf, cmdlen);
         }
     }
     else
index 2ec0b2ff776484df942a07b43b228c7886c0a47f..689cd7f99f906abd1a15c2dd6d336afe561ac1d2 100644 (file)
@@ -557,3 +557,43 @@ check_session_id_hmac(struct tls_pre_decrypt_state *state,
     }
     return false;
 }
+
+struct buffer
+extract_command_buffer(struct buffer *buf, struct gc_arena *gc)
+{
+    /* commands on the control channel are seperated by 0x00 bytes.
+     * cmdlen does not include the 0 byte of the string */
+    int cmdlen = (int)strnlen(BSTR(buf), BLEN(buf));
+
+    if (cmdlen >= BLEN(buf))
+    {
+        buf_advance(buf, cmdlen);
+        /* Return empty buffer */
+        struct buffer empty = { 0 };
+        return empty;
+    }
+
+    /* include the NUL byte and ensure NUL termination */
+    cmdlen +=  1;
+
+    /* Construct a buffer that only holds the current command and
+     * its closing NUL byte */
+    struct buffer cmdbuf = alloc_buf_gc(cmdlen, gc);
+    buf_write(&cmdbuf, BPTR(buf), cmdlen);
+
+    /* Remove \r and \n at the end of the buffer to avoid
+     * problems with scripts and other that add extra \r and \n */
+    buf_chomp(&cmdbuf);
+
+    /* check we have only printable characters or null byte in the
+     * command string and no newlines */
+    if (!string_check_buf(&cmdbuf, CC_PRINT | CC_NULL, CC_CRLF))
+    {
+        msg(D_PUSH_ERRORS, "WARNING: Received control with invalid characters: %s",
+            format_hex(BPTR(&cmdbuf), BLEN(&cmdbuf), 256, gc));
+        cmdbuf.len = 0;
+    }
+
+    buf_advance(buf, cmdlen);
+    return cmdbuf;
+}
index 88b9e8c38aa81b8c5939b8809f7ac915d02ba316..c8a27fba9d7eb1b6d1856ca239aceda8919a6f27 100644 (file)
@@ -230,6 +230,20 @@ tls_reset_standalone(struct tls_wrap_ctx *ctx,
                      uint8_t header,
                      bool request_resend_wkc);
 
+
+/**
+ * Extracts a control channel message from buf and adjusts the size of
+ * buf after the message has been extracted
+ * @param buf   The buffer the message should be extracted from
+ * @param gc    gc_arena to be used for the returned buffer and displaying
+ *              diagnostic messages
+ * @return      A buffer with a control channel message or a buffer with
+ *              with length 0 if there is no message or the message has
+ *              invalid characters.
+ */
+struct buffer
+extract_command_buffer(struct buffer *buf, struct gc_arena *gc);
+
 static inline const char *
 packet_opcode_name(int op)
 {
index 7f0518414ad0ce777957abf1d004be1f21659d0a..74d7311f74d270909487a9628fba0b9f7bcbd6b6 100644 (file)
@@ -625,6 +625,40 @@ test_generate_reset_packet_tls_auth(void **ut_state)
     free_tas(&tas_server);
 }
 
+static void
+test_extract_control_message(void **ut_state)
+{
+    struct gc_arena gc = gc_new();
+    struct buffer input_buf = alloc_buf_gc(1024, &gc);
+
+    /* This message will have a \0x00 at the end since it is a C string */
+    const char input[] = "valid control message\r\n\0\0Invalid\r\none\0valid one again";
+
+    buf_write(&input_buf, input, sizeof(input));
+    struct buffer cmd1 = extract_command_buffer(&input_buf, &gc);
+    struct buffer cmd2 = extract_command_buffer(&input_buf, &gc);
+    struct buffer cmd3 = extract_command_buffer(&input_buf, &gc);
+    struct buffer cmd4 = extract_command_buffer(&input_buf, &gc);
+    struct buffer cmd5 = extract_command_buffer(&input_buf, &gc);
+
+    assert_string_equal(BSTR(&cmd1), "valid control message");
+    /* empty message with just a \0x00 */
+    assert_int_equal(cmd2.len, 1);
+    assert_string_equal(BSTR(&cmd2), "");
+    assert_int_equal(cmd3.len, 0);
+    assert_string_equal(BSTR(&cmd4), "valid one again");
+    assert_int_equal(cmd5.len, 0);
+
+    const uint8_t nonull[6] = { 'n', 'o', ' ', 'N', 'U', 'L'};
+    struct buffer nonull_buf = alloc_buf_gc(1024, &gc);
+
+    buf_write(&nonull_buf, nonull, sizeof(nonull));
+    struct buffer nonullcmd = extract_command_buffer(&nonull_buf, &gc);
+    assert_int_equal(nonullcmd.len, 0);
+
+    gc_free(&gc);
+}
+
 int
 main(void)
 {
@@ -638,6 +672,7 @@ main(void)
         cmocka_unit_test(test_verify_hmac_tls_auth),
         cmocka_unit_test(test_generate_reset_packet_plain),
         cmocka_unit_test(test_generate_reset_packet_tls_auth),
+        cmocka_unit_test(test_extract_control_message)
     };
 
 #if defined(ENABLE_CRYPTO_OPENSSL)