Compare commits
No commits in common. 'c9' and 'c8-beta' have entirely different histories.
@ -1 +1 @@
|
|||||||
SOURCES/libsoup-2.72.0.tar.xz
|
SOURCES/libsoup-2.62.3.tar.xz
|
||||||
|
@ -1 +1 @@
|
|||||||
6aaed6b49b13e287b7c3bba546ba49fec4ea72a5 SOURCES/libsoup-2.72.0.tar.xz
|
8b9c743348b1f9fc044c6abf3431899154e93ad1 SOURCES/libsoup-2.62.3.tar.xz
|
||||||
|
@ -0,0 +1,95 @@
|
|||||||
|
From d9c729aa5a7991182fa7bdb8d94442f8f0cf055b Mon Sep 17 00:00:00 2001
|
||||||
|
From: Carlos Garcia Campos <cgarcia@igalia.com>
|
||||||
|
Date: Fri, 19 Jul 2019 14:56:05 +0200
|
||||||
|
Subject: [PATCH] WebSockets: ignore any messages after close has been sent and
|
||||||
|
received
|
||||||
|
|
||||||
|
We currently ignore data frames when close has been received, but we
|
||||||
|
should also ignore any frame after close has been sent and received.
|
||||||
|
Currently, if we receive two close frames we end up with the code and
|
||||||
|
reason of the second frame, while the RFC says: "The WebSocket
|
||||||
|
Connection Close Code is defined as the status code contained in the
|
||||||
|
first Close control frame received by the application implementing
|
||||||
|
this protocol."
|
||||||
|
---
|
||||||
|
libsoup/soup-websocket-connection.c | 3 ++
|
||||||
|
tests/websocket-test.c | 48 +++++++++++++++++++++++++++++
|
||||||
|
2 files changed, 51 insertions(+)
|
||||||
|
|
||||||
|
diff --git libsoup/soup-websocket-connection.c libsoup/soup-websocket-connection.c
|
||||||
|
--- a/libsoup/soup-websocket-connection.c
|
||||||
|
+++ b/libsoup/soup-websocket-connection.c
|
||||||
|
@@ -690,6 +690,9 @@
|
||||||
|
SoupWebsocketConnectionPrivate *pv = self->pv;
|
||||||
|
GBytes *message;
|
||||||
|
|
||||||
|
+ if (pv->close_sent && pv->close_received)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
if (control) {
|
||||||
|
/* Control frames must never be fragmented */
|
||||||
|
if (!fin) {
|
||||||
|
--- a/tests/websocket-test.c
|
||||||
|
+++ b/tests/websocket-test.c
|
||||||
|
@@ -707,6 +707,49 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
static gpointer
|
||||||
|
+close_after_close_server_thread (gpointer user_data)
|
||||||
|
+{
|
||||||
|
+ Test *test = user_data;
|
||||||
|
+ gsize written;
|
||||||
|
+ const char frames[] =
|
||||||
|
+ "\x88\x09\x03\xe8""reason1"
|
||||||
|
+ "\x88\x09\x03\xe8""reason2";
|
||||||
|
+ GError *error = NULL;
|
||||||
|
+
|
||||||
|
+ g_mutex_lock (&test->mutex);
|
||||||
|
+ g_mutex_unlock (&test->mutex);
|
||||||
|
+
|
||||||
|
+ g_output_stream_write_all (g_io_stream_get_output_stream (test->raw_server),
|
||||||
|
+ frames, sizeof (frames) -1, &written, NULL, &error);
|
||||||
|
+ g_assert_no_error (error);
|
||||||
|
+ g_assert_cmpuint (written, ==, sizeof (frames) - 1);
|
||||||
|
+ g_io_stream_close (test->raw_server, NULL, &error);
|
||||||
|
+ g_assert_no_error (error);
|
||||||
|
+
|
||||||
|
+ return NULL;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+test_close_after_close (Test *test,
|
||||||
|
+ gconstpointer data)
|
||||||
|
+{
|
||||||
|
+ GThread *thread;
|
||||||
|
+
|
||||||
|
+ g_mutex_lock (&test->mutex);
|
||||||
|
+
|
||||||
|
+ thread = g_thread_new ("close-after-close-thread", close_after_close_server_thread, test);
|
||||||
|
+
|
||||||
|
+ soup_websocket_connection_close (test->client, SOUP_WEBSOCKET_CLOSE_NORMAL, "reason1");
|
||||||
|
+ g_mutex_unlock (&test->mutex);
|
||||||
|
+
|
||||||
|
+ g_thread_join (thread);
|
||||||
|
+
|
||||||
|
+ WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
|
||||||
|
+ g_assert_cmpuint (soup_websocket_connection_get_close_code (test->client), ==, SOUP_WEBSOCKET_CLOSE_NORMAL);
|
||||||
|
+ g_assert_cmpstr (soup_websocket_connection_get_close_data (test->client), ==, "reason1");
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static gpointer
|
||||||
|
timeout_server_thread (gpointer user_data)
|
||||||
|
{
|
||||||
|
Test *test = user_data;
|
||||||
|
@@ -918,6 +961,11 @@
|
||||||
|
test_message_after_closing,
|
||||||
|
teardown_soup_connection);
|
||||||
|
|
||||||
|
+ g_test_add ("/websocket/direct/close-after-close", Test, NULL,
|
||||||
|
+ setup_half_direct_connection,
|
||||||
|
+ test_close_after_close,
|
||||||
|
+ teardown_direct_connection);
|
||||||
|
+
|
||||||
|
|
||||||
|
g_test_add ("/websocket/direct/protocol-negotiate", Test, NULL, NULL,
|
||||||
|
test_protocol_negotiate_direct,
|
@ -1,145 +0,0 @@
|
|||||||
From 04df03bc092ac20607f3e150936624d4f536e68b Mon Sep 17 00:00:00 2001
|
|
||||||
From: Patrick Griffis <pgriffis@igalia.com>
|
|
||||||
Date: Mon, 8 Jul 2024 12:33:15 -0500
|
|
||||||
Subject: [PATCH] headers: Strictly don't allow NUL bytes
|
|
||||||
|
|
||||||
In the past (2015) this was allowed for some problematic sites. However Chromium also does not allow NUL bytes in either header names or values these days. So this should no longer be a problem.
|
|
||||||
---
|
|
||||||
libsoup/soup-headers.c | 15 +++------
|
|
||||||
tests/header-parsing-test.c | 62 +++++++++++++++++--------------------
|
|
||||||
2 files changed, 32 insertions(+), 45 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/libsoup/soup-headers.c b/libsoup/soup-headers.c
|
|
||||||
index a0cf351a..f30ee467 100644
|
|
||||||
--- a/libsoup/soup-headers.c
|
|
||||||
+++ b/libsoup/soup-headers.c
|
|
||||||
@@ -51,13 +51,14 @@ soup_headers_parse (const char *str, int len, SoupMessageHeaders *dest)
|
|
||||||
* ignorable trailing whitespace.
|
|
||||||
*/
|
|
||||||
|
|
||||||
+ /* No '\0's are allowed */
|
|
||||||
+ if (memchr (str, '\0', len))
|
|
||||||
+ return FALSE;
|
|
||||||
+
|
|
||||||
/* Skip over the Request-Line / Status-Line */
|
|
||||||
headers_start = memchr (str, '\n', len);
|
|
||||||
if (!headers_start)
|
|
||||||
return FALSE;
|
|
||||||
- /* No '\0's in the Request-Line / Status-Line */
|
|
||||||
- if (memchr (str, '\0', headers_start - str))
|
|
||||||
- return FALSE;
|
|
||||||
|
|
||||||
/* We work on a copy of the headers, which we can write '\0's
|
|
||||||
* into, so that we don't have to individually g_strndup and
|
|
||||||
@@ -69,14 +70,6 @@ soup_headers_parse (const char *str, int len, SoupMessageHeaders *dest)
|
|
||||||
headers_copy[copy_len] = '\0';
|
|
||||||
value_end = headers_copy;
|
|
||||||
|
|
||||||
- /* There shouldn't be any '\0's in the headers already, but
|
|
||||||
- * this is the web we're talking about.
|
|
||||||
- */
|
|
||||||
- while ((p = memchr (headers_copy, '\0', copy_len))) {
|
|
||||||
- memmove (p, p + 1, copy_len - (p - headers_copy));
|
|
||||||
- copy_len--;
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
while (*(value_end + 1)) {
|
|
||||||
name = value_end + 1;
|
|
||||||
name_end = strchr (name, ':');
|
|
||||||
diff --git a/tests/header-parsing-test.c b/tests/header-parsing-test.c
|
|
||||||
index edf8eebb..715c2c6f 100644
|
|
||||||
--- a/tests/header-parsing-test.c
|
|
||||||
+++ b/tests/header-parsing-test.c
|
|
||||||
@@ -358,24 +358,6 @@ static struct RequestTest {
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
- { "NUL in header name", "760832",
|
|
||||||
- "GET / HTTP/1.1\r\nHost\x00: example.com\r\n", 36,
|
|
||||||
- SOUP_STATUS_OK,
|
|
||||||
- "GET", "/", SOUP_HTTP_1_1,
|
|
||||||
- { { "Host", "example.com" },
|
|
||||||
- { NULL }
|
|
||||||
- }
|
|
||||||
- },
|
|
||||||
-
|
|
||||||
- { "NUL in header value", "760832",
|
|
||||||
- "GET / HTTP/1.1\r\nHost: example\x00" "com\r\n", 35,
|
|
||||||
- SOUP_STATUS_OK,
|
|
||||||
- "GET", "/", SOUP_HTTP_1_1,
|
|
||||||
- { { "Host", "examplecom" },
|
|
||||||
- { NULL }
|
|
||||||
- }
|
|
||||||
- },
|
|
||||||
-
|
|
||||||
/************************/
|
|
||||||
/*** INVALID REQUESTS ***/
|
|
||||||
/************************/
|
|
||||||
@@ -448,6 +430,21 @@ static struct RequestTest {
|
|
||||||
SOUP_STATUS_EXPECTATION_FAILED,
|
|
||||||
NULL, NULL, -1,
|
|
||||||
{ { NULL } }
|
|
||||||
+ },
|
|
||||||
+
|
|
||||||
+ // https://gitlab.gnome.org/GNOME/libsoup/-/issues/377
|
|
||||||
+ { "NUL in header name", NULL,
|
|
||||||
+ "GET / HTTP/1.1\r\nHost\x00: example.com\r\n", 36,
|
|
||||||
+ SOUP_STATUS_BAD_REQUEST,
|
|
||||||
+ NULL, NULL, -1,
|
|
||||||
+ { { NULL } }
|
|
||||||
+ },
|
|
||||||
+
|
|
||||||
+ { "NUL in header value", NULL,
|
|
||||||
+ "HTTP/1.1 200 OK\r\nFoo: b\x00" "ar\r\n", 28,
|
|
||||||
+ SOUP_STATUS_BAD_REQUEST,
|
|
||||||
+ NULL, NULL, -1,
|
|
||||||
+ { { NULL } }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
static const int num_reqtests = G_N_ELEMENTS (reqtests);
|
|
||||||
@@ -620,22 +617,6 @@ static struct ResponseTest {
|
|
||||||
{ NULL } }
|
|
||||||
},
|
|
||||||
|
|
||||||
- { "NUL in header name", "760832",
|
|
||||||
- "HTTP/1.1 200 OK\r\nF\x00oo: bar\r\n", 28,
|
|
||||||
- SOUP_HTTP_1_1, SOUP_STATUS_OK, "OK",
|
|
||||||
- { { "Foo", "bar" },
|
|
||||||
- { NULL }
|
|
||||||
- }
|
|
||||||
- },
|
|
||||||
-
|
|
||||||
- { "NUL in header value", "760832",
|
|
||||||
- "HTTP/1.1 200 OK\r\nFoo: b\x00" "ar\r\n", 28,
|
|
||||||
- SOUP_HTTP_1_1, SOUP_STATUS_OK, "OK",
|
|
||||||
- { { "Foo", "bar" },
|
|
||||||
- { NULL }
|
|
||||||
- }
|
|
||||||
- },
|
|
||||||
-
|
|
||||||
/********************************/
|
|
||||||
/*** VALID CONTINUE RESPONSES ***/
|
|
||||||
/********************************/
|
|
||||||
@@ -768,6 +749,19 @@ static struct ResponseTest {
|
|
||||||
{ { NULL }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
+
|
|
||||||
+ // https://gitlab.gnome.org/GNOME/libsoup/-/issues/377
|
|
||||||
+ { "NUL in header name", NULL,
|
|
||||||
+ "HTTP/1.1 200 OK\r\nF\x00oo: bar\r\n", 28,
|
|
||||||
+ -1, 0, NULL,
|
|
||||||
+ { { NULL } }
|
|
||||||
+ },
|
|
||||||
+
|
|
||||||
+ { "NUL in header value", "760832",
|
|
||||||
+ "HTTP/1.1 200 OK\r\nFoo: b\x00" "ar\r\n", 28,
|
|
||||||
+ -1, 0, NULL,
|
|
||||||
+ { { NULL } }
|
|
||||||
+ },
|
|
||||||
};
|
|
||||||
static const int num_resptests = G_N_ELEMENTS (resptests);
|
|
||||||
|
|
||||||
--
|
|
||||||
2.45.2
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
|||||||
diff -up libsoup-2.62.3/libsoup/soup-websocket-connection.c.cve-2024-52532 libsoup-2.62.3/libsoup/soup-websocket-connection.c
|
|
||||||
--- libsoup-2.62.3/libsoup/soup-websocket-connection.c.cve-2024-52532 2024-11-12 12:00:27.183570627 +0100
|
|
||||||
+++ libsoup-2.62.3/libsoup/soup-websocket-connection.c 2024-11-12 12:01:02.334987409 +0100
|
|
||||||
@@ -1041,9 +1041,9 @@ soup_websocket_connection_read (SoupWebs
|
|
||||||
}
|
|
||||||
|
|
||||||
pv->incoming->len = len + count;
|
|
||||||
- } while (count > 0);
|
|
||||||
+ process_incoming (self);
|
|
||||||
+ } while (count > 0 && !pv->close_sent && !pv->io_closing);
|
|
||||||
|
|
||||||
- process_incoming (self);
|
|
||||||
|
|
||||||
if (end) {
|
|
||||||
if (!pv->close_sent || !pv->close_received) {
|
|
@ -0,0 +1,152 @@
|
|||||||
|
From 109bb2f692c746bc63a0ade8737b584aecb0b1ad Mon Sep 17 00:00:00 2001
|
||||||
|
From: Carlos Garcia Campos <cgarcia@igalia.com>
|
||||||
|
Date: Thu, 27 Jun 2019 16:03:21 +0200
|
||||||
|
Subject: [PATCH] WebSockets: allow null characters in text messages data
|
||||||
|
|
||||||
|
RFC 6455 says that text messages should contains valid UTF-8, and null
|
||||||
|
characters valid according to RFC 3629. However, we are using
|
||||||
|
g_utf8_validate(), which considers null characters as errors, to
|
||||||
|
validate WebSockets text messages. This patch adds an internal
|
||||||
|
utf8_validate() function based on g_utf8_validate() but allowing null
|
||||||
|
characters and just returning a gboolean since we are always ignoring
|
||||||
|
the end parameter in case of errors.
|
||||||
|
soup_websocket_connection_send_text() assumes the given text is null
|
||||||
|
terminated, so we need a new public function to allow sending text
|
||||||
|
messages containing null characters. This patch adds
|
||||||
|
soup_websocket_connection_send_message() that receives a
|
||||||
|
SoupWebsocketDataType and GBytes, which is consistent with
|
||||||
|
SoupWebsocketConnection::message signal.
|
||||||
|
|
||||||
|
For RHEL backport, drop the addition of soup_websocket_connection_send_message()
|
||||||
|
as we don't need it and don't want to expose new API.
|
||||||
|
diff --git libsoup/soup-websocket-connection.c libsoup/soup-websocket-connection.c
|
||||||
|
index 66bd6871..67a98731 100644
|
||||||
|
--- a/libsoup/soup-websocket-connection.c
|
||||||
|
+++ b/libsoup/soup-websocket-connection.c
|
||||||
|
@@ -155,6 +155,82 @@
|
||||||
|
|
||||||
|
static void protocol_error_and_close (SoupWebsocketConnection *self);
|
||||||
|
|
||||||
|
+/* Code below is based on g_utf8_validate() implementation,
|
||||||
|
+ * but handling NULL characters as valid, as expected by
|
||||||
|
+ * WebSockets and compliant with RFC 3629.
|
||||||
|
+ */
|
||||||
|
+#define VALIDATE_BYTE(mask, expect) \
|
||||||
|
+ G_STMT_START { \
|
||||||
|
+ if (G_UNLIKELY((*(guchar *)p & (mask)) != (expect))) \
|
||||||
|
+ return FALSE; \
|
||||||
|
+ } G_STMT_END
|
||||||
|
+
|
||||||
|
+/* see IETF RFC 3629 Section 4 */
|
||||||
|
+static gboolean
|
||||||
|
+utf8_validate (const char *str,
|
||||||
|
+ gsize max_len)
|
||||||
|
+
|
||||||
|
+{
|
||||||
|
+ const gchar *p;
|
||||||
|
+
|
||||||
|
+ for (p = str; ((p - str) < max_len); p++) {
|
||||||
|
+ if (*(guchar *)p < 128)
|
||||||
|
+ /* done */;
|
||||||
|
+ else {
|
||||||
|
+ if (*(guchar *)p < 0xe0) { /* 110xxxxx */
|
||||||
|
+ if (G_UNLIKELY (max_len - (p - str) < 2))
|
||||||
|
+ return FALSE;
|
||||||
|
+
|
||||||
|
+ if (G_UNLIKELY (*(guchar *)p < 0xc2))
|
||||||
|
+ return FALSE;
|
||||||
|
+ } else {
|
||||||
|
+ if (*(guchar *)p < 0xf0) { /* 1110xxxx */
|
||||||
|
+ if (G_UNLIKELY (max_len - (p - str) < 3))
|
||||||
|
+ return FALSE;
|
||||||
|
+
|
||||||
|
+ switch (*(guchar *)p++ & 0x0f) {
|
||||||
|
+ case 0:
|
||||||
|
+ VALIDATE_BYTE(0xe0, 0xa0); /* 0xa0 ... 0xbf */
|
||||||
|
+ break;
|
||||||
|
+ case 0x0d:
|
||||||
|
+ VALIDATE_BYTE(0xe0, 0x80); /* 0x80 ... 0x9f */
|
||||||
|
+ break;
|
||||||
|
+ default:
|
||||||
|
+ VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
|
||||||
|
+ }
|
||||||
|
+ } else if (*(guchar *)p < 0xf5) { /* 11110xxx excluding out-of-range */
|
||||||
|
+ if (G_UNLIKELY (max_len - (p - str) < 4))
|
||||||
|
+ return FALSE;
|
||||||
|
+
|
||||||
|
+ switch (*(guchar *)p++ & 0x07) {
|
||||||
|
+ case 0:
|
||||||
|
+ VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
|
||||||
|
+ if (G_UNLIKELY((*(guchar *)p & 0x30) == 0))
|
||||||
|
+ return FALSE;
|
||||||
|
+ break;
|
||||||
|
+ case 4:
|
||||||
|
+ VALIDATE_BYTE(0xf0, 0x80); /* 0x80 ... 0x8f */
|
||||||
|
+ break;
|
||||||
|
+ default:
|
||||||
|
+ VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
|
||||||
|
+ }
|
||||||
|
+ p++;
|
||||||
|
+ VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
|
||||||
|
+ } else {
|
||||||
|
+ return FALSE;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ p++;
|
||||||
|
+ VALIDATE_BYTE(0xc0, 0x80); /* 10xxxxxx */
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return TRUE;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#undef VALIDATE_BYTE
|
||||||
|
+
|
||||||
|
static void
|
||||||
|
frame_free (gpointer data)
|
||||||
|
{
|
||||||
|
@@ -629,7 +705,7 @@
|
||||||
|
data += 2;
|
||||||
|
len -= 2;
|
||||||
|
|
||||||
|
- if (!g_utf8_validate ((char *)data, len, NULL)) {
|
||||||
|
+ if (!utf8_validate ((const char *)data, len)) {
|
||||||
|
g_debug ("received non-UTF8 close data: %d '%.*s' %d", (int)len, (int)len, (char *)data, (int)data[0]);
|
||||||
|
protocol_error_and_close (self);
|
||||||
|
return;
|
||||||
|
@@ -777,9 +853,8 @@
|
||||||
|
/* Actually deliver the message? */
|
||||||
|
if (fin) {
|
||||||
|
if (pv->message_opcode == 0x01 &&
|
||||||
|
- !g_utf8_validate((char *)pv->message_data->data,
|
||||||
|
- pv->message_data->len,
|
||||||
|
- NULL)) {
|
||||||
|
+ !utf8_validate((const char *)pv->message_data->data,
|
||||||
|
+ pv->message_data->len)) {
|
||||||
|
|
||||||
|
g_debug ("received invalid non-UTF8 text data");
|
||||||
|
|
||||||
|
@@ -1699,7 +1774,9 @@
|
||||||
|
* @self: the WebSocket
|
||||||
|
* @text: the message contents
|
||||||
|
*
|
||||||
|
- * Send a text (UTF-8) message to the peer.
|
||||||
|
+ * Send a %NULL-terminated text (UTF-8) message to the peer. If you need
|
||||||
|
+ * to send text messages containing %NULL characters use
|
||||||
|
+ * soup_websocket_connection_send_message() instead.
|
||||||
|
*
|
||||||
|
* The message is queued to be sent and will be sent when the main loop
|
||||||
|
* is run.
|
||||||
|
@@ -1717,7 +1794,7 @@
|
||||||
|
g_return_if_fail (text != NULL);
|
||||||
|
|
||||||
|
length = strlen (text);
|
||||||
|
- g_return_if_fail (g_utf8_validate (text, length, NULL));
|
||||||
|
+ g_return_if_fail (utf8_validate (text, length));
|
||||||
|
|
||||||
|
send_message (self, SOUP_WEBSOCKET_QUEUE_NORMAL, 0x01, (const guint8 *) text, length);
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.26.2
|
||||||
|
|
@ -1,38 +0,0 @@
|
|||||||
From 29b96fab2512666d7241e46c98cc45b60b795c0c Mon Sep 17 00:00:00 2001
|
|
||||||
From: Ignacio Casal Quinteiro <qignacio@amazon.com>
|
|
||||||
Date: Wed, 2 Oct 2024 11:17:19 +0200
|
|
||||||
Subject: [PATCH 2/2] websocket-test: disconnect error copy after the test ends
|
|
||||||
|
|
||||||
Otherwise the server will have already sent a few more wrong
|
|
||||||
bytes and the client will continue getting errors to copy
|
|
||||||
but the error is already != NULL and it will assert
|
|
||||||
---
|
|
||||||
tests/websocket-test.c | 4 +++-
|
|
||||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
|
||||||
|
|
||||||
diff --git a/tests/websocket-test.c b/tests/websocket-test.c
|
|
||||||
index 06c443bb..6a48c1f9 100644
|
|
||||||
--- a/tests/websocket-test.c
|
|
||||||
+++ b/tests/websocket-test.c
|
|
||||||
@@ -1539,8 +1539,9 @@ test_receive_invalid_encode_length_64 (Test *test,
|
|
||||||
GError *error = NULL;
|
|
||||||
InvalidEncodeLengthTest context = { test, NULL };
|
|
||||||
guint i;
|
|
||||||
+ guint error_id;
|
|
||||||
|
|
||||||
- g_signal_connect (test->client, "error", G_CALLBACK (on_error_copy), &error);
|
|
||||||
+ error_id = g_signal_connect (test->client, "error", G_CALLBACK (on_error_copy), &error);
|
|
||||||
g_signal_connect (test->client, "message", G_CALLBACK (on_binary_message), &received);
|
|
||||||
|
|
||||||
/* We use 127(\x7f) as payload length with 65535 extended length */
|
|
||||||
@@ -1553,6 +1554,7 @@ test_receive_invalid_encode_length_64 (Test *test,
|
|
||||||
WAIT_UNTIL (error != NULL || received != NULL);
|
|
||||||
g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_CLOSE_PROTOCOL_ERROR);
|
|
||||||
g_clear_error (&error);
|
|
||||||
+ g_signal_handler_disconnect (test->client, error_id);
|
|
||||||
g_assert_null (received);
|
|
||||||
|
|
||||||
g_thread_join (thread);
|
|
||||||
--
|
|
||||||
2.45.2
|
|
||||||
|
|
@ -0,0 +1,299 @@
|
|||||||
|
From 35f1bac5ff9ec694e64b65e51f0e7a3226aa3aaf Mon Sep 17 00:00:00 2001
|
||||||
|
From: Carlos Garcia Campos <cgarcia@igalia.com>
|
||||||
|
Date: Wed, 28 Aug 2019 10:51:18 +0200
|
||||||
|
Subject: [PATCH] WebSockets: only poll IO stream when needed
|
||||||
|
|
||||||
|
Instead of having two pollable sources constantly running, always try to
|
||||||
|
read/write without blocking and start polling if the operation returns
|
||||||
|
G_IO_ERROR_WOULD_BLOCK. This patch also fixes test
|
||||||
|
/websocket/direct/close-after-close that was passing but not actually
|
||||||
|
testing what we wanted, because the client close was never sent. When
|
||||||
|
the mutex is released, the frame has been queued, but not sent.
|
||||||
|
|
||||||
|
diff --git libsoup/soup-websocket-connection.c libsoup/soup-websocket-connection.c
|
||||||
|
index 345040fe..6afbbe67 100644
|
||||||
|
--- a/libsoup/soup-websocket-connection.c
|
||||||
|
+++ b/libsoup/soup-websocket-connection.c
|
||||||
|
@@ -147,6 +147,7 @@
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MAX_INCOMING_PAYLOAD_SIZE_DEFAULT 128 * 1024
|
||||||
|
+#define READ_BUFFER_SIZE 1024
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_WITH_PRIVATE (SoupWebsocketConnection, soup_websocket_connection, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
@@ -155,6 +156,11 @@
|
||||||
|
|
||||||
|
static void protocol_error_and_close (SoupWebsocketConnection *self);
|
||||||
|
|
||||||
|
+static gboolean on_web_socket_input (GObject *pollable_stream,
|
||||||
|
+ gpointer user_data);
|
||||||
|
+static gboolean on_web_socket_output (GObject *pollable_stream,
|
||||||
|
+ gpointer user_data);
|
||||||
|
+
|
||||||
|
/* Code below is based on g_utf8_validate() implementation,
|
||||||
|
* but handling NULL characters as valid, as expected by
|
||||||
|
* WebSockets and compliant with RFC 3629.
|
||||||
|
@@ -283,7 +289,20 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
-stop_input (SoupWebsocketConnection *self)
|
||||||
|
+soup_websocket_connection_start_input_source (SoupWebsocketConnection *self)
|
||||||
|
+{
|
||||||
|
+ SoupWebsocketConnectionPrivate *pv = self->pv;
|
||||||
|
+
|
||||||
|
+ if (pv->input_source)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ pv->input_source = g_pollable_input_stream_create_source (pv->input, NULL);
|
||||||
|
+ g_source_set_callback (pv->input_source, (GSourceFunc)on_web_socket_input, self, NULL);
|
||||||
|
+ g_source_attach (pv->input_source, pv->main_context);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+soup_websocket_connection_stop_input_source (SoupWebsocketConnection *self)
|
||||||
|
{
|
||||||
|
SoupWebsocketConnectionPrivate *pv = self->pv;
|
||||||
|
|
||||||
|
@@ -296,7 +315,20 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
-stop_output (SoupWebsocketConnection *self)
|
||||||
|
+soup_websocket_connection_start_output_source (SoupWebsocketConnection *self)
|
||||||
|
+{
|
||||||
|
+ SoupWebsocketConnectionPrivate *pv = self->pv;
|
||||||
|
+
|
||||||
|
+ if (pv->output_source)
|
||||||
|
+ return;
|
||||||
|
+
|
||||||
|
+ pv->output_source = g_pollable_output_stream_create_source (pv->output, NULL);
|
||||||
|
+ g_source_set_callback (pv->output_source, (GSourceFunc)on_web_socket_output, self, NULL);
|
||||||
|
+ g_source_attach (pv->output_source, pv->main_context);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+soup_websocket_connection_stop_output_source (SoupWebsocketConnection *self)
|
||||||
|
{
|
||||||
|
SoupWebsocketConnectionPrivate *pv = self->pv;
|
||||||
|
|
||||||
|
@@ -341,8 +373,8 @@
|
||||||
|
close_io_stop_timeout (self);
|
||||||
|
|
||||||
|
if (!pv->io_closing) {
|
||||||
|
- stop_input (self);
|
||||||
|
- stop_output (self);
|
||||||
|
+ soup_websocket_connection_stop_input_source (self);
|
||||||
|
+ soup_websocket_connection_stop_output_source (self);
|
||||||
|
pv->io_closing = TRUE;
|
||||||
|
g_debug ("closing io stream");
|
||||||
|
g_io_stream_close_async (pv->io_stream, G_PRIORITY_DEFAULT,
|
||||||
|
@@ -359,7 +391,7 @@
|
||||||
|
GSocket *socket;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
- stop_output (self);
|
||||||
|
+ soup_websocket_connection_stop_output_source (self);
|
||||||
|
|
||||||
|
if (G_IS_SOCKET_CONNECTION (pv->io_stream)) {
|
||||||
|
socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (pv->io_stream));
|
||||||
|
@@ -612,9 +644,6 @@
|
||||||
|
self->pv->connection_type == SOUP_WEBSOCKET_CONNECTION_SERVER ? "server" : "client",
|
||||||
|
payload_len, self->pv->max_incoming_payload_size);
|
||||||
|
emit_error_and_close (self, error, TRUE);
|
||||||
|
-
|
||||||
|
- /* The input is in an invalid state now */
|
||||||
|
- stop_input (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
@@ -981,32 +1010,31 @@
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static gboolean
|
||||||
|
-on_web_socket_input (GObject *pollable_stream,
|
||||||
|
- gpointer user_data)
|
||||||
|
+static void
|
||||||
|
+soup_websocket_connection_read (SoupWebsocketConnection *self)
|
||||||
|
{
|
||||||
|
- SoupWebsocketConnection *self = SOUP_WEBSOCKET_CONNECTION (user_data);
|
||||||
|
SoupWebsocketConnectionPrivate *pv = self->pv;
|
||||||
|
GError *error = NULL;
|
||||||
|
gboolean end = FALSE;
|
||||||
|
gssize count;
|
||||||
|
gsize len;
|
||||||
|
|
||||||
|
+ soup_websocket_connection_stop_input_source (self);
|
||||||
|
+
|
||||||
|
do {
|
||||||
|
len = pv->incoming->len;
|
||||||
|
- g_byte_array_set_size (pv->incoming, len + 1024);
|
||||||
|
+ g_byte_array_set_size (pv->incoming, len + READ_BUFFER_SIZE);
|
||||||
|
|
||||||
|
count = g_pollable_input_stream_read_nonblocking (pv->input,
|
||||||
|
pv->incoming->data + len,
|
||||||
|
- 1024, NULL, &error);
|
||||||
|
-
|
||||||
|
+ READ_BUFFER_SIZE, NULL, &error);
|
||||||
|
if (count < 0) {
|
||||||
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
|
||||||
|
g_error_free (error);
|
||||||
|
count = 0;
|
||||||
|
} else {
|
||||||
|
emit_error_and_close (self, error, TRUE);
|
||||||
|
- return TRUE;
|
||||||
|
+ return;
|
||||||
|
}
|
||||||
|
} else if (count == 0) {
|
||||||
|
end = TRUE;
|
||||||
|
@@ -1026,16 +1054,24 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
close_io_stream (self);
|
||||||
|
+ return;
|
||||||
|
}
|
||||||
|
|
||||||
|
- return TRUE;
|
||||||
|
+ soup_websocket_connection_start_input_source (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
-on_web_socket_output (GObject *pollable_stream,
|
||||||
|
- gpointer user_data)
|
||||||
|
+on_web_socket_input (GObject *pollable_stream,
|
||||||
|
+ gpointer user_data)
|
||||||
|
+{
|
||||||
|
+ soup_websocket_connection_read (SOUP_WEBSOCKET_CONNECTION (user_data));
|
||||||
|
+
|
||||||
|
+ return G_SOURCE_REMOVE;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+soup_websocket_connection_write (SoupWebsocketConnection *self)
|
||||||
|
{
|
||||||
|
- SoupWebsocketConnection *self = SOUP_WEBSOCKET_CONNECTION (user_data);
|
||||||
|
SoupWebsocketConnectionPrivate *pv = self->pv;
|
||||||
|
const guint8 *data;
|
||||||
|
GError *error = NULL;
|
||||||
|
@@ -1043,19 +1079,18 @@
|
||||||
|
gssize count;
|
||||||
|
gsize len;
|
||||||
|
|
||||||
|
+ soup_websocket_connection_stop_output_source (self);
|
||||||
|
+
|
||||||
|
if (soup_websocket_connection_get_state (self) == SOUP_WEBSOCKET_STATE_CLOSED) {
|
||||||
|
g_debug ("Ignoring message since the connection is closed");
|
||||||
|
- stop_output (self);
|
||||||
|
- return TRUE;
|
||||||
|
+ return;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame = g_queue_peek_head (&pv->outgoing);
|
||||||
|
|
||||||
|
/* No more frames to send */
|
||||||
|
- if (frame == NULL) {
|
||||||
|
- stop_output (self);
|
||||||
|
- return TRUE;
|
||||||
|
- }
|
||||||
|
+ if (frame == NULL)
|
||||||
|
+ return;
|
||||||
|
|
||||||
|
data = g_bytes_get_data (frame->data, &len);
|
||||||
|
g_assert (len > 0);
|
||||||
|
@@ -1075,7 +1110,7 @@
|
||||||
|
frame->pending = TRUE;
|
||||||
|
} else {
|
||||||
|
emit_error_and_close (self, error, TRUE);
|
||||||
|
- return FALSE;
|
||||||
|
+ return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1093,23 +1128,21 @@
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frame_free (frame);
|
||||||
|
+
|
||||||
|
+ if (g_queue_is_empty (&pv->outgoing))
|
||||||
|
+ return;
|
||||||
|
}
|
||||||
|
|
||||||
|
- return TRUE;
|
||||||
|
+ soup_websocket_connection_start_output_source (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
-static void
|
||||||
|
-start_output (SoupWebsocketConnection *self)
|
||||||
|
+static gboolean
|
||||||
|
+on_web_socket_output (GObject *pollable_stream,
|
||||||
|
+ gpointer user_data)
|
||||||
|
{
|
||||||
|
- SoupWebsocketConnectionPrivate *pv = self->pv;
|
||||||
|
-
|
||||||
|
- if (pv->output_source)
|
||||||
|
- return;
|
||||||
|
+ soup_websocket_connection_write (SOUP_WEBSOCKET_CONNECTION (user_data));
|
||||||
|
|
||||||
|
- g_debug ("starting output source");
|
||||||
|
- pv->output_source = g_pollable_output_stream_create_source (pv->output, NULL);
|
||||||
|
- g_source_set_callback (pv->output_source, (GSourceFunc)on_web_socket_output, self, NULL);
|
||||||
|
- g_source_attach (pv->output_source, pv->main_context);
|
||||||
|
+ return G_SOURCE_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
@@ -1150,7 +1183,7 @@
|
||||||
|
g_queue_push_tail (&pv->outgoing, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
- start_output (self);
|
||||||
|
+ soup_websocket_connection_write (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
@@ -1175,9 +1208,7 @@
|
||||||
|
pv->output = G_POLLABLE_OUTPUT_STREAM (os);
|
||||||
|
g_return_if_fail (g_pollable_output_stream_can_poll (pv->output));
|
||||||
|
|
||||||
|
- pv->input_source = g_pollable_input_stream_create_source (pv->input, NULL);
|
||||||
|
- g_source_set_callback (pv->input_source, (GSourceFunc)on_web_socket_input, self, NULL);
|
||||||
|
- g_source_attach (pv->input_source, pv->main_context);
|
||||||
|
+ soup_websocket_connection_start_input_source (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
diff --git tests/websocket-test.c tests/websocket-test.c
|
||||||
|
index 146fdf82..26d064df 100644
|
||||||
|
--- a/tests/websocket-test.c
|
||||||
|
+++ b/tests/websocket-test.c
|
||||||
|
@@ -733,6 +733,7 @@
|
||||||
|
const char frames[] =
|
||||||
|
"\x88\x09\x03\xe8""reason1"
|
||||||
|
"\x88\x09\x03\xe8""reason2";
|
||||||
|
+ GSocket *socket;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
g_mutex_lock (&test->mutex);
|
||||||
|
@@ -742,7 +743,8 @@
|
||||||
|
frames, sizeof (frames) -1, &written, NULL, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
g_assert_cmpuint (written, ==, sizeof (frames) - 1);
|
||||||
|
- g_io_stream_close (test->raw_server, NULL, &error);
|
||||||
|
+ socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (test->raw_server));
|
||||||
|
+ g_socket_shutdown (socket, FALSE, TRUE, &error);
|
||||||
|
g_assert_no_error (error);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
@@ -766,6 +768,7 @@
|
||||||
|
WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
|
||||||
|
g_assert_cmpuint (soup_websocket_connection_get_close_code (test->client), ==, SOUP_WEBSOCKET_CLOSE_NORMAL);
|
||||||
|
g_assert_cmpstr (soup_websocket_connection_get_close_data (test->client), ==, "reason1");
|
||||||
|
+ g_io_stream_close (test->raw_server, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gpointer
|
||||||
|
--
|
||||||
|
2.26.2
|
||||||
|
|
@ -0,0 +1,356 @@
|
|||||||
|
diff -up libsoup-2.62.3/libsoup/soup-auth-ntlm.c.4 libsoup-2.62.3/libsoup/soup-auth-ntlm.c
|
||||||
|
--- libsoup-2.62.3/libsoup/soup-auth-ntlm.c.4 2021-03-12 07:30:03.366088932 +0100
|
||||||
|
+++ libsoup-2.62.3/libsoup/soup-auth-ntlm.c 2021-03-12 07:30:25.296043405 +0100
|
||||||
|
@@ -12,6 +12,8 @@
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
+#include <stdio.h>
|
||||||
|
+
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#include "soup-auth-ntlm.h"
|
||||||
|
@@ -26,14 +28,20 @@ static char *soup_ntlm_request
|
||||||
|
static gboolean soup_ntlm_parse_challenge (const char *challenge,
|
||||||
|
char **nonce,
|
||||||
|
char **default_domain,
|
||||||
|
- gboolean *ntlmv2_session);
|
||||||
|
-static char *soup_ntlm_response (const char *nonce,
|
||||||
|
+ gboolean *ntlmv2_session,
|
||||||
|
+ gboolean *negotiate_target,
|
||||||
|
+ char **target_info,
|
||||||
|
+ size_t *target_info_sz);
|
||||||
|
+static char *soup_ntlm_response (const char *nonce,
|
||||||
|
const char *user,
|
||||||
|
guchar nt_hash[21],
|
||||||
|
guchar lm_hash[21],
|
||||||
|
const char *host,
|
||||||
|
const char *domain,
|
||||||
|
- gboolean ntlmv2_session);
|
||||||
|
+ gboolean ntlmv2_session,
|
||||||
|
+ gboolean negotiate_target,
|
||||||
|
+ const char *target_info,
|
||||||
|
+ size_t target_info_sz);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SOUP_NTLM_NEW,
|
||||||
|
@@ -49,6 +57,9 @@ typedef struct {
|
||||||
|
char *nonce;
|
||||||
|
char *response_header;
|
||||||
|
gboolean ntlmv2_session;
|
||||||
|
+ gboolean negotiate_target;
|
||||||
|
+ char *target_info;
|
||||||
|
+ size_t target_info_sz;
|
||||||
|
} SoupNTLMConnectionState;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
@@ -280,6 +291,7 @@ soup_auth_ntlm_free_connection_state (So
|
||||||
|
|
||||||
|
g_free (conn->nonce);
|
||||||
|
g_free (conn->response_header);
|
||||||
|
+ g_free (conn->target_info);
|
||||||
|
g_slice_free (SoupNTLMConnectionState, conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -339,7 +351,8 @@ soup_auth_ntlm_update_connection (SoupCo
|
||||||
|
|
||||||
|
if (!soup_ntlm_parse_challenge (auth_header + 5, &conn->nonce,
|
||||||
|
priv->domain ? NULL : &priv->domain,
|
||||||
|
- &conn->ntlmv2_session)) {
|
||||||
|
+ &conn->ntlmv2_session, &conn->negotiate_target,
|
||||||
|
+ &conn->target_info, &conn->target_info_sz)) {
|
||||||
|
conn->state = SOUP_NTLM_FAILED;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
@@ -521,7 +534,10 @@ soup_auth_ntlm_get_connection_authorizat
|
||||||
|
priv->lm_hash,
|
||||||
|
NULL,
|
||||||
|
priv->domain,
|
||||||
|
- conn->ntlmv2_session);
|
||||||
|
+ conn->ntlmv2_session,
|
||||||
|
+ conn->negotiate_target,
|
||||||
|
+ conn->target_info,
|
||||||
|
+ conn->target_info_sz);
|
||||||
|
}
|
||||||
|
g_clear_pointer (&conn->nonce, g_free);
|
||||||
|
conn->state = SOUP_NTLM_SENT_RESPONSE;
|
||||||
|
@@ -647,14 +663,20 @@ typedef struct {
|
||||||
|
#define NTLM_CHALLENGE_NONCE_OFFSET 24
|
||||||
|
#define NTLM_CHALLENGE_NONCE_LENGTH 8
|
||||||
|
#define NTLM_CHALLENGE_DOMAIN_STRING_OFFSET 12
|
||||||
|
+#define NTLM_CHALLENGE_TARGET_INFORMATION_OFFSET 40
|
||||||
|
|
||||||
|
#define NTLM_CHALLENGE_FLAGS_OFFSET 20
|
||||||
|
#define NTLM_FLAGS_NEGOTIATE_NTLMV2 0x00080000
|
||||||
|
+#define NTLM_FLAGS_NEGOTIATE_TARGET_INFORMATION 0x00800000
|
||||||
|
+#define NTLM_FLAGS_REQUEST_TARGET 0x00000004
|
||||||
|
|
||||||
|
#define NTLM_RESPONSE_HEADER "NTLMSSP\x00\x03\x00\x00\x00"
|
||||||
|
#define NTLM_RESPONSE_FLAGS 0x8201
|
||||||
|
+#define NTLM_RESPONSE_TARGET_INFORMATION_OFFSET 44
|
||||||
|
#define NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY 0x00080000
|
||||||
|
|
||||||
|
+#define HMAC_MD5_LENGTH 16
|
||||||
|
+
|
||||||
|
typedef struct {
|
||||||
|
guchar header[12];
|
||||||
|
|
||||||
|
@@ -686,10 +708,14 @@ static gboolean
|
||||||
|
soup_ntlm_parse_challenge (const char *challenge,
|
||||||
|
char **nonce,
|
||||||
|
char **default_domain,
|
||||||
|
- gboolean *ntlmv2_session)
|
||||||
|
+ gboolean *ntlmv2_session,
|
||||||
|
+ gboolean *negotiate_target,
|
||||||
|
+ char **target_info,
|
||||||
|
+ size_t *target_info_sz)
|
||||||
|
{
|
||||||
|
gsize clen;
|
||||||
|
NTLMString domain;
|
||||||
|
+ NTLMString target;
|
||||||
|
guchar *chall;
|
||||||
|
guint32 flags;
|
||||||
|
|
||||||
|
@@ -703,6 +729,14 @@ soup_ntlm_parse_challenge (const char *c
|
||||||
|
memcpy (&flags, chall + NTLM_CHALLENGE_FLAGS_OFFSET, sizeof(flags));
|
||||||
|
flags = GUINT_FROM_LE (flags);
|
||||||
|
*ntlmv2_session = (flags & NTLM_FLAGS_NEGOTIATE_NTLMV2) ? TRUE : FALSE;
|
||||||
|
+ /* To know if NTLMv2 responses should be calculated */
|
||||||
|
+ *negotiate_target = (flags & NTLM_FLAGS_NEGOTIATE_TARGET_INFORMATION ) ? TRUE : FALSE;
|
||||||
|
+ if (*negotiate_target) {
|
||||||
|
+ if (clen < NTLM_CHALLENGE_TARGET_INFORMATION_OFFSET + sizeof (target)) {
|
||||||
|
+ g_free (chall);
|
||||||
|
+ return FALSE;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
|
||||||
|
if (default_domain) {
|
||||||
|
memcpy (&domain, chall + NTLM_CHALLENGE_DOMAIN_STRING_OFFSET, sizeof (domain));
|
||||||
|
@@ -723,6 +757,19 @@ soup_ntlm_parse_challenge (const char *c
|
||||||
|
*nonce = g_memdup (chall + NTLM_CHALLENGE_NONCE_OFFSET,
|
||||||
|
NTLM_CHALLENGE_NONCE_LENGTH);
|
||||||
|
}
|
||||||
|
+ /* For NTLMv2 response */
|
||||||
|
+ if (*negotiate_target && target_info) {
|
||||||
|
+ memcpy (&target, chall + NTLM_CHALLENGE_TARGET_INFORMATION_OFFSET, sizeof (target));
|
||||||
|
+ target.length = GUINT16_FROM_LE (target.length);
|
||||||
|
+ target.offset = GUINT16_FROM_LE (target.offset);
|
||||||
|
+
|
||||||
|
+ if (clen < target.length + target.offset) {
|
||||||
|
+ g_free (chall);
|
||||||
|
+ return FALSE;
|
||||||
|
+ }
|
||||||
|
+ *target_info = g_memdup (chall + target.offset, target.length);
|
||||||
|
+ *target_info_sz = target.length;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
g_free (chall);
|
||||||
|
return TRUE;
|
||||||
|
@@ -761,6 +808,115 @@ calc_ntlm2_session_response (const char
|
||||||
|
calc_response (nt_hash, ntlmv2_hash, nt_resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
+/* Compute HMAC-MD5 with Glib function*/
|
||||||
|
+static void
|
||||||
|
+calc_hmac_md5 (unsigned char *hmac, const guchar *key, gsize key_sz, const guchar *data, gsize data_sz)
|
||||||
|
+{
|
||||||
|
+ char *hmac_hex, *hex_pos;
|
||||||
|
+ size_t count;
|
||||||
|
+
|
||||||
|
+ hmac_hex = g_compute_hmac_for_data(G_CHECKSUM_MD5, key, key_sz, data, data_sz);
|
||||||
|
+ hex_pos = hmac_hex;
|
||||||
|
+ for (count = 0; count < HMAC_MD5_LENGTH; count++)
|
||||||
|
+ {
|
||||||
|
+ /* The 'hh' sscanf format modifier is C99, so we enable it on
|
||||||
|
+ * non-Windows or if __USE_MINGW_ANSI_STDIO is enabled or`
|
||||||
|
+ * if we are building on Visual Studio 2015 or later
|
||||||
|
+ */
|
||||||
|
+#if !defined (G_OS_WIN32) || (__USE_MINGW_ANSI_STDIO == 1) || (_MSC_VER >= 1900)
|
||||||
|
+ sscanf(hex_pos, "%2hhx", &hmac[count]);
|
||||||
|
+#else
|
||||||
|
+ unsigned int tmp_hmac;
|
||||||
|
+ sscanf(hex_pos, "%2x", &tmp_hmac);
|
||||||
|
+ hmac[count] = (guint8)tmp_hmac;
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+ hex_pos += 2;
|
||||||
|
+ }
|
||||||
|
+ g_free(hmac_hex);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+calc_ntlmv2_response (const char *user, const char *domain,
|
||||||
|
+ const guchar *nt_hash, const gsize nt_hash_sz,
|
||||||
|
+ const guchar *nonce,
|
||||||
|
+ const char *target_info, size_t target_info_sz,
|
||||||
|
+ guchar *lm_resp, size_t lm_resp_sz,
|
||||||
|
+ guchar *nt_resp, size_t nt_resp_sz)
|
||||||
|
+{
|
||||||
|
+ const unsigned char blob_signature[] = {0x01,0x01,0x00,0x00};
|
||||||
|
+ const unsigned char blob_reserved[] = {0x00,0x00,0x00,0x00};
|
||||||
|
+ gint64 blob_timestamp;
|
||||||
|
+ unsigned char client_nonce[8];
|
||||||
|
+ const unsigned char blob_unknown[] = {0x00,0x00,0x00,0x00};
|
||||||
|
+
|
||||||
|
+ unsigned char ntv2_hash[HMAC_MD5_LENGTH];
|
||||||
|
+ guchar *nonce_blob, *blob, *p_blob;
|
||||||
|
+ unsigned char nonce_blob_hash[HMAC_MD5_LENGTH];
|
||||||
|
+ unsigned char nonce_client_nonce[16], nonce_client_nonce_hash[HMAC_MD5_LENGTH];
|
||||||
|
+ gchar *user_uppercase, *user_domain, *user_domain_conv;
|
||||||
|
+ gsize user_domain_conv_sz;
|
||||||
|
+ size_t blob_sz;
|
||||||
|
+ int i;
|
||||||
|
+
|
||||||
|
+ /* create HMAC-MD5 hash of Unicode uppercase username and Unicode domain */
|
||||||
|
+ user_uppercase = g_utf8_strup (user, strlen (user));
|
||||||
|
+ user_domain = g_strconcat (user_uppercase, domain, NULL);
|
||||||
|
+ user_domain_conv = g_convert (user_domain, -1, "UCS-2LE", "UTF-8", NULL, &user_domain_conv_sz, NULL);
|
||||||
|
+ calc_hmac_md5 (ntv2_hash, nt_hash, nt_hash_sz, (const guchar *)user_domain_conv, user_domain_conv_sz);
|
||||||
|
+ g_free (user_uppercase);
|
||||||
|
+ g_free (user_domain);
|
||||||
|
+ g_free (user_domain_conv);
|
||||||
|
+
|
||||||
|
+ /* create random client nonce */
|
||||||
|
+ for (i = 0; i < sizeof (client_nonce); i++)
|
||||||
|
+ {
|
||||||
|
+ client_nonce[i] = g_random_int();
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* create timestamp for blob
|
||||||
|
+ * LE, 64-bit signed value, number of tenths of a ms since January 1, 1601.*/
|
||||||
|
+ blob_timestamp = GINT64_TO_LE(((unsigned long)time(NULL) + 11644473600) * 10000000);
|
||||||
|
+
|
||||||
|
+ /* create blob */
|
||||||
|
+ blob_sz = sizeof (blob_signature) + sizeof (blob_reserved) +
|
||||||
|
+ sizeof (blob_timestamp) + sizeof (client_nonce) +
|
||||||
|
+ sizeof (blob_unknown) + target_info_sz;
|
||||||
|
+ p_blob = blob = g_malloc (blob_sz);
|
||||||
|
+ memset (blob, 0, blob_sz);
|
||||||
|
+ memcpy (p_blob, blob_signature, sizeof (blob_signature));
|
||||||
|
+ memcpy (p_blob += sizeof (blob_signature), blob_reserved, sizeof (blob_reserved));
|
||||||
|
+ memcpy (p_blob += sizeof (blob_reserved), &blob_timestamp, sizeof (blob_timestamp));
|
||||||
|
+ memcpy (p_blob += sizeof (blob_timestamp), client_nonce, sizeof (client_nonce));
|
||||||
|
+ memcpy (p_blob += sizeof (client_nonce), blob_unknown, sizeof (blob_unknown));
|
||||||
|
+ memcpy (p_blob += sizeof (blob_unknown), target_info, target_info_sz);
|
||||||
|
+
|
||||||
|
+ /* create HMAC-MD5 hash of concatenated nonce and blob */
|
||||||
|
+ nonce_blob = g_malloc (NTLM_CHALLENGE_NONCE_LENGTH + blob_sz);
|
||||||
|
+ memcpy (nonce_blob, nonce, NTLM_CHALLENGE_NONCE_LENGTH);
|
||||||
|
+ memcpy (nonce_blob + NTLM_CHALLENGE_NONCE_LENGTH, blob, blob_sz);
|
||||||
|
+ calc_hmac_md5 (nonce_blob_hash, (const guchar *)ntv2_hash, (gsize) sizeof (ntv2_hash), (const guchar *) nonce_blob, (gsize) NTLM_CHALLENGE_NONCE_LENGTH + blob_sz);
|
||||||
|
+ g_free (nonce_blob);
|
||||||
|
+
|
||||||
|
+ /* create NTv2 response */
|
||||||
|
+ memset (nt_resp, 0, nt_resp_sz);
|
||||||
|
+ memcpy (nt_resp, nonce_blob_hash, sizeof (nonce_blob_hash));
|
||||||
|
+ memcpy (nt_resp + sizeof (nonce_blob_hash), blob, blob_sz);
|
||||||
|
+
|
||||||
|
+ g_free (blob);
|
||||||
|
+
|
||||||
|
+ /* LMv2
|
||||||
|
+ * create HMAC-MD5 hash of concatenated nonce and client nonce
|
||||||
|
+ */
|
||||||
|
+ memcpy (nonce_client_nonce, nonce, NTLM_CHALLENGE_NONCE_LENGTH);
|
||||||
|
+ memcpy (nonce_client_nonce + NTLM_CHALLENGE_NONCE_LENGTH, client_nonce, sizeof (client_nonce));
|
||||||
|
+ calc_hmac_md5 (nonce_client_nonce_hash, (const guchar *) ntv2_hash, (gsize) sizeof (ntv2_hash), (const guchar *) nonce_client_nonce, (gsize) NTLM_CHALLENGE_NONCE_LENGTH + sizeof (client_nonce));
|
||||||
|
+
|
||||||
|
+ /* create LMv2 response */
|
||||||
|
+ memset (lm_resp, 0, lm_resp_sz);
|
||||||
|
+ memcpy (lm_resp, nonce_client_nonce_hash, sizeof (nonce_client_nonce_hash));
|
||||||
|
+ memcpy (lm_resp + sizeof (nonce_client_nonce_hash), client_nonce, sizeof (client_nonce));
|
||||||
|
+}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
soup_ntlm_response (const char *nonce,
|
||||||
|
@@ -769,23 +925,45 @@ soup_ntlm_response (const char *nonce,
|
||||||
|
guchar lm_hash[21],
|
||||||
|
const char *host,
|
||||||
|
const char *domain,
|
||||||
|
- gboolean ntlmv2_session)
|
||||||
|
+ gboolean ntlmv2_session,
|
||||||
|
+ gboolean negotiate_target,
|
||||||
|
+ const char *target_info,
|
||||||
|
+ size_t target_info_sz)
|
||||||
|
{
|
||||||
|
+
|
||||||
|
int offset;
|
||||||
|
- gsize hlen, dlen, ulen;
|
||||||
|
- guchar lm_resp[24], nt_resp[24];
|
||||||
|
+ gsize hlen, dlen, ulen, nt_resp_sz;
|
||||||
|
+ guchar lm_resp[24], *nt_resp;
|
||||||
|
char *user_conv, *host_conv, *domain_conv;
|
||||||
|
NTLMResponse resp;
|
||||||
|
char *out, *p;
|
||||||
|
int state, save;
|
||||||
|
|
||||||
|
- if (ntlmv2_session) {
|
||||||
|
+ if (negotiate_target)
|
||||||
|
+ {
|
||||||
|
+ /* nonce_blob_hash 16 + blob_signature 4 + blob_reserved 4 +
|
||||||
|
+ * blob_timestamp 8 + client_nonce 8 + blob_unknown 4 +
|
||||||
|
+ * target_info*/
|
||||||
|
+ nt_resp_sz = NTLM_RESPONSE_TARGET_INFORMATION_OFFSET + target_info_sz;
|
||||||
|
+ } else {
|
||||||
|
+ nt_resp_sz = 24;
|
||||||
|
+ }
|
||||||
|
+ nt_resp = g_malloc (nt_resp_sz);
|
||||||
|
+
|
||||||
|
+ if (ntlmv2_session && !negotiate_target) {
|
||||||
|
calc_ntlm2_session_response (nonce, nt_hash, lm_hash,
|
||||||
|
lm_resp, sizeof(lm_resp), nt_resp);
|
||||||
|
- } else {
|
||||||
|
- /* Compute a regular response */
|
||||||
|
+ } else if (!negotiate_target){
|
||||||
|
+ /* Compute a regular NTLMv1 response */
|
||||||
|
calc_response (nt_hash, (guchar *) nonce, nt_resp);
|
||||||
|
calc_response (lm_hash, (guchar *) nonce, lm_resp);
|
||||||
|
+ } else {
|
||||||
|
+ calc_ntlmv2_response (user, domain,
|
||||||
|
+ nt_hash, 21,
|
||||||
|
+ (guchar *) nonce,
|
||||||
|
+ target_info, target_info_sz,
|
||||||
|
+ lm_resp, sizeof (lm_resp),
|
||||||
|
+ nt_resp, (size_t) nt_resp_sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset (&resp, 0, sizeof (resp));
|
||||||
|
@@ -793,7 +971,8 @@ soup_ntlm_response (const char *nonce,
|
||||||
|
resp.flags = GUINT32_TO_LE (NTLM_RESPONSE_FLAGS);
|
||||||
|
if (ntlmv2_session)
|
||||||
|
resp.flags |= GUINT32_TO_LE (NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY);
|
||||||
|
-
|
||||||
|
+ if (negotiate_target)
|
||||||
|
+ resp.flags |= GUINT32_TO_LE (NTLM_FLAGS_REQUEST_TARGET);
|
||||||
|
offset = sizeof (resp);
|
||||||
|
|
||||||
|
if (!host)
|
||||||
|
@@ -807,10 +986,10 @@ soup_ntlm_response (const char *nonce,
|
||||||
|
ntlm_set_string (&resp.user, &offset, ulen);
|
||||||
|
ntlm_set_string (&resp.host, &offset, hlen);
|
||||||
|
ntlm_set_string (&resp.lm_resp, &offset, sizeof (lm_resp));
|
||||||
|
- ntlm_set_string (&resp.nt_resp, &offset, sizeof (nt_resp));
|
||||||
|
+ ntlm_set_string (&resp.nt_resp, &offset, nt_resp_sz);
|
||||||
|
|
||||||
|
out = g_malloc (((offset + 3) * 4) / 3 + 6);
|
||||||
|
- strncpy (out, "NTLM ", 5);
|
||||||
|
+ memcpy (out, "NTLM ", 5);
|
||||||
|
p = out + 5;
|
||||||
|
|
||||||
|
state = save = 0;
|
||||||
|
@@ -825,7 +1004,7 @@ soup_ntlm_response (const char *nonce,
|
||||||
|
FALSE, p, &state, &save);
|
||||||
|
p += g_base64_encode_step (lm_resp, sizeof (lm_resp),
|
||||||
|
FALSE, p, &state, &save);
|
||||||
|
- p += g_base64_encode_step (nt_resp, sizeof (nt_resp),
|
||||||
|
+ p += g_base64_encode_step (nt_resp, nt_resp_sz,
|
||||||
|
FALSE, p, &state, &save);
|
||||||
|
p += g_base64_encode_close (FALSE, p, &state, &save);
|
||||||
|
*p = '\0';
|
||||||
|
@@ -833,6 +1012,7 @@ soup_ntlm_response (const char *nonce,
|
||||||
|
g_free (domain_conv);
|
||||||
|
g_free (user_conv);
|
||||||
|
g_free (host_conv);
|
||||||
|
+ g_free (nt_resp);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
From 87e8c2ab9f3bc79befb0e3b25ec513cfd36fefe9 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Carlos Garcia Campos <cgarcia@igalia.com>
|
||||||
|
Date: Tue, 21 Jan 2020 11:41:37 +0100
|
||||||
|
Subject: [PATCH] WebSockets: do not start the input source when IO is closing
|
||||||
|
|
||||||
|
We should not schedule a new read after reading the close message, since
|
||||||
|
we don't expect more input. This fixes a crash due to an assert that
|
||||||
|
checks that the input source is NULL when the connection is destroyed
|
||||||
|
that happens when the connection is destroyed in the closed callback.
|
||||||
|
|
||||||
|
Fixes #181
|
||||||
|
---
|
||||||
|
libsoup/soup-websocket-connection.c | 3 ++-
|
||||||
|
tests/websocket-test.c | 33 +++++++++++++++++++++++++++++
|
||||||
|
2 files changed, 35 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/libsoup/soup-websocket-connection.c b/libsoup/soup-websocket-connection.c
|
||||||
|
index 2c7fc1161..a4095e1c9 100644
|
||||||
|
--- a/libsoup/soup-websocket-connection.c
|
||||||
|
+++ b/libsoup/soup-websocket-connection.c
|
||||||
|
@@ -1156,7 +1156,8 @@ soup_websocket_connection_read (SoupWebsocketConnection *self)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
- soup_websocket_connection_start_input_source (self);
|
||||||
|
+ if (!pv->io_closing)
|
||||||
|
+ soup_websocket_connection_start_input_source (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
diff --git a/tests/websocket-test.c b/tests/websocket-test.c
|
||||||
|
index b5142612e..5e40cf364 100644
|
||||||
|
--- a/tests/websocket-test.c
|
||||||
|
+++ b/tests/websocket-test.c
|
||||||
|
@@ -1067,6 +1067,34 @@ test_close_after_close (Test *test,
|
||||||
|
g_io_stream_close (test->raw_server, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
+static gboolean
|
||||||
|
+on_close_unref_connection (SoupWebsocketConnection *ws,
|
||||||
|
+ gpointer user_data)
|
||||||
|
+{
|
||||||
|
+ Test *test = user_data;
|
||||||
|
+
|
||||||
|
+ g_assert_true (test->server == ws);
|
||||||
|
+ g_clear_object (&test->server);
|
||||||
|
+ return TRUE;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+test_server_unref_connection_on_close (Test *test,
|
||||||
|
+ gconstpointer data)
|
||||||
|
+{
|
||||||
|
+ gboolean close_event_client = FALSE;
|
||||||
|
+
|
||||||
|
+ g_signal_connect (test->client, "closed", G_CALLBACK (on_close_set_flag), &close_event_client);
|
||||||
|
+ g_signal_connect (test->server, "closed", G_CALLBACK (on_close_unref_connection), test);
|
||||||
|
+ soup_websocket_connection_close (test->client, SOUP_WEBSOCKET_CLOSE_GOING_AWAY, "client closed");
|
||||||
|
+ g_assert_cmpint (soup_websocket_connection_get_state (test->client), ==, SOUP_WEBSOCKET_STATE_CLOSING);
|
||||||
|
+
|
||||||
|
+ WAIT_UNTIL (test->server == NULL);
|
||||||
|
+ WAIT_UNTIL (soup_websocket_connection_get_state (test->client) == SOUP_WEBSOCKET_STATE_CLOSED);
|
||||||
|
+
|
||||||
|
+ g_assert_true (close_event_client);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static gpointer
|
||||||
|
timeout_server_thread (gpointer user_data)
|
||||||
|
{
|
||||||
|
@@ -1923,6 +1951,11 @@ main (int argc,
|
||||||
|
test_close_after_close,
|
||||||
|
teardown_direct_connection);
|
||||||
|
|
||||||
|
+ g_test_add ("/websocket/soup/server-unref-connection-on-close", Test, NULL,
|
||||||
|
+ setup_soup_connection,
|
||||||
|
+ test_server_unref_connection_on_close,
|
||||||
|
+ teardown_soup_connection);
|
||||||
|
+
|
||||||
|
|
||||||
|
g_test_add ("/websocket/direct/protocol-negotiate", Test, NULL, NULL,
|
||||||
|
test_protocol_negotiate_direct,
|
||||||
|
--
|
||||||
|
GitLab
|
||||||
|
|
Loading…
Reference in new issue