Compare commits
No commits in common. 'c9' and 'c8' 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,
|
@ -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