[libsoup] session: do not emit tls interaction signals for preconnect requests
- From: Carlos Garcia Campos <carlosgc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsoup] session: do not emit tls interaction signals for preconnect requests
- Date: Wed, 16 Jun 2021 11:52:50 +0000 (UTC)
commit f4c7545e59655a3fb3f7f68ee269c8456c54aa5c
Author: Carlos Garcia Campos <cgarcia igalia com>
Date: Wed Jun 16 12:09:40 2021 +0200
session: do not emit tls interaction signals for preconnect requests
Keep the tls pending interactions pending and transfer them to the
actual message when the connection is stolen.
libsoup/soup-message-private.h | 6 ++
libsoup/soup-message.c | 97 +++++++++++++++++++++++
libsoup/soup-session.c | 5 +-
tests/ssl-test.c | 174 +++++++++++++++++++++++++++++++++++++++++
4 files changed, 280 insertions(+), 2 deletions(-)
---
diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h
index 82bf7d89..8b3ad56f 100644
--- a/libsoup/soup-message-private.h
+++ b/libsoup/soup-message-private.h
@@ -100,6 +100,12 @@ GList *soup_message_get_disabled_features (SoupMessage *msg);
SoupConnection *soup_message_get_connection (SoupMessage *msg);
void soup_message_set_connection (SoupMessage *msg,
SoupConnection *conn);
+void soup_message_transfer_connection (SoupMessage *preconnect_msg,
+ SoupMessage *msg);
+void soup_message_set_is_preconnect (SoupMessage *msg,
+ gboolean is_preconnect);
+gboolean soup_message_has_pending_tls_cert_request (SoupMessage *msg);
+gboolean soup_message_has_pending_tls_cert_pass_request (SoupMessage *msg);
SoupClientMessageIO *soup_message_get_io_data (SoupMessage *msg);
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index 0f977816..41aed806 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -92,12 +92,15 @@ typedef struct {
GTlsCertificate *tls_client_certificate;
GTask *pending_tls_cert_request;
+ GTlsClientConnection *pending_tls_cert_conn;
GTask *pending_tls_cert_pass_request;
+ GTlsPassword *pending_tls_cert_password;
SoupMessagePriority priority;
gboolean is_top_level_navigation;
gboolean is_options_ping;
+ gboolean is_preconnect;
gboolean force_http1;
gboolean is_misdirected_retry;
guint last_connection_id;
@@ -181,11 +184,13 @@ soup_message_finalize (GObject *object)
g_task_return_int (priv->pending_tls_cert_request, G_TLS_INTERACTION_FAILED);
g_object_unref (priv->pending_tls_cert_request);
}
+ g_clear_object (&priv->pending_tls_cert_conn);
if (priv->pending_tls_cert_pass_request) {
g_task_return_int (priv->pending_tls_cert_pass_request, G_TLS_INTERACTION_FAILED);
g_object_unref (priv->pending_tls_cert_pass_request);
}
+ g_clear_object (&priv->pending_tls_cert_password);
soup_message_set_connection (msg, NULL);
@@ -1476,6 +1481,15 @@ re_emit_request_certificate (SoupMessage *msg,
priv->pending_tls_cert_request = g_object_ref (task);
+ /* Skip interaction for preconnect requests, keep the operation
+ * pending that will be handled by the new message once the
+ * connection is transferred.
+ */
+ if (priv->is_preconnect) {
+ priv->pending_tls_cert_conn = g_object_ref (tls_conn);
+ return TRUE;
+ }
+
g_signal_emit (msg, signals[REQUEST_CERTIFICATE], 0, tls_conn, &handled);
if (!handled)
g_clear_object (&priv->pending_tls_cert_request);
@@ -1493,6 +1507,15 @@ re_emit_request_certificate_password (SoupMessage *msg,
priv->pending_tls_cert_pass_request = g_object_ref (task);
+ /* Skip interaction for preconnect requests, keep the operation
+ * pending that will be handled by the new message once the
+ * connection is transferred.
+ */
+ if (priv->is_preconnect) {
+ priv->pending_tls_cert_password = g_object_ref (password);
+ return TRUE;
+ }
+
g_signal_emit (msg, signals[REQUEST_CERTIFICATE_PASSWORD], 0, password, &handled);
if (!handled)
g_clear_object (&priv->pending_tls_cert_pass_request);
@@ -1580,6 +1603,80 @@ soup_message_set_connection (SoupMessage *msg,
msg, G_CONNECT_SWAPPED);
}
+void
+soup_message_set_is_preconnect (SoupMessage *msg,
+ gboolean is_preconnect)
+{
+ SoupMessagePrivate *priv = soup_message_get_instance_private (msg);
+
+ priv->is_preconnect = is_preconnect;
+}
+
+void
+soup_message_transfer_connection (SoupMessage *preconnect_msg,
+ SoupMessage *msg)
+{
+ SoupMessagePrivate *preconnect_priv = soup_message_get_instance_private (preconnect_msg);
+ SoupMessagePrivate *priv = soup_message_get_instance_private (msg);
+ GTlsCertificate *client_certificate = NULL;
+
+ g_assert (preconnect_priv->is_preconnect);
+ g_assert (!priv->connection);
+ client_certificate = g_steal_pointer (&priv->tls_client_certificate);
+ soup_message_set_connection (msg, preconnect_priv->connection);
+
+ /* If connection has pending interactions, transfer them too */
+ g_assert (!priv->pending_tls_cert_request);
+ priv->pending_tls_cert_request = g_steal_pointer (&preconnect_priv->pending_tls_cert_request);
+ if (priv->pending_tls_cert_request) {
+ if (client_certificate) {
+ soup_connection_complete_tls_certificate_request (priv->connection,
+ client_certificate,
+ g_steal_pointer
(&priv->pending_tls_cert_request));
+ g_object_unref (client_certificate);
+ } else {
+ gboolean handled = FALSE;
+
+ g_signal_emit (msg, signals[REQUEST_CERTIFICATE], 0,
preconnect_priv->pending_tls_cert_conn, &handled);
+ g_clear_object (&preconnect_priv->pending_tls_cert_conn);
+ if (!handled)
+ g_clear_object (&priv->pending_tls_cert_request);
+ }
+ } else if (client_certificate) {
+ soup_connection_set_tls_client_certificate (priv->connection, client_certificate);
+ g_object_unref (client_certificate);
+ }
+
+ g_assert (!priv->pending_tls_cert_pass_request);
+ priv->pending_tls_cert_pass_request = g_steal_pointer
(&preconnect_priv->pending_tls_cert_pass_request);
+ if (priv->pending_tls_cert_pass_request) {
+ gboolean handled = FALSE;
+
+ g_signal_emit (msg, signals[REQUEST_CERTIFICATE_PASSWORD], 0,
preconnect_priv->pending_tls_cert_password, &handled);
+ g_clear_object (&preconnect_priv->pending_tls_cert_password);
+ if (!handled)
+ g_clear_object (&priv->pending_tls_cert_pass_request);
+ }
+
+ soup_message_set_connection (preconnect_msg, NULL);
+}
+
+gboolean
+soup_message_has_pending_tls_cert_request (SoupMessage *msg)
+{
+ SoupMessagePrivate *priv = soup_message_get_instance_private (msg);
+
+ return priv->pending_tls_cert_request != NULL;
+}
+
+gboolean
+soup_message_has_pending_tls_cert_pass_request (SoupMessage *msg)
+{
+ SoupMessagePrivate *priv = soup_message_get_instance_private (msg);
+
+ return priv->pending_tls_cert_pass_request != NULL;
+}
+
/**
* soup_message_cleanup_response:
* @msg: a #SoupMessage
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index 4789fc63..9c674e57 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -1360,6 +1360,7 @@ soup_session_append_queue_item (SoupSession *session,
soup_message_set_metrics_timestamp (msg, SOUP_MESSAGE_METRICS_FETCH_START);
soup_message_cleanup_response (msg);
+ soup_message_set_is_preconnect (msg, FALSE);
item = soup_message_queue_item_new (session, msg, async, cancellable);
g_queue_insert_sorted (priv->queue,
@@ -1786,8 +1787,7 @@ steal_preconnection (SoupSession *session,
if (!preconnect_item->connect_only || preconnect_item->state != SOUP_MESSAGE_CONNECTING)
return FALSE;
- soup_message_set_connection (item->msg, conn);
- soup_message_set_connection (preconnect_item->msg, NULL);
+ soup_message_transfer_connection (preconnect_item->msg, item->msg);
g_assert (preconnect_item->related == NULL);
preconnect_item->related = soup_message_queue_item_ref (item);
@@ -4040,6 +4040,7 @@ soup_session_preconnect_async (SoupSession *session,
item = soup_session_append_queue_item (session, msg, TRUE, cancellable);
item->connect_only = TRUE;
item->io_priority = io_priority;
+ soup_message_set_is_preconnect (msg, TRUE);
task = g_task_new (session, item->cancellable, callback, user_data);
g_task_set_priority (task, io_priority);
diff --git a/tests/ssl-test.c b/tests/ssl-test.c
index 4f183a9a..08563515 100644
--- a/tests/ssl-test.c
+++ b/tests/ssl-test.c
@@ -1,6 +1,7 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
#include "test-utils.h"
+#include "soup-message-private.h"
#include "soup-server-message-private.h"
#if HAVE_GNUTLS
@@ -505,6 +506,178 @@ do_tls_interaction_msg_test (gconstpointer data)
g_object_unref (wrong_certificate);
}
+static gboolean
+preconnect_request_certificate (SoupMessage *msg,
+ GTlsClientConnection *conn,
+ gboolean *called)
+{
+ *called = TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+preconnect_request_certificate_password (SoupMessage *msg,
+ GTlsPassword *password,
+ gboolean *called)
+{
+ *called = TRUE;
+
+ return FALSE;
+}
+
+static void
+preconnect_finished_cb (SoupSession *session,
+ GAsyncResult *result,
+ gboolean *preconnect_finished)
+{
+ g_assert_true (soup_session_preconnect_finish (session, result, NULL));
+ *preconnect_finished = TRUE;
+}
+
+static void
+do_tls_interaction_preconnect_test (gconstpointer data)
+{
+ SoupServer *server = (SoupServer *)data;
+ SoupSession *session;
+ SoupMessage *preconnect_msg;
+ SoupMessage *msg;
+ GBytes *body;
+ GTlsDatabase *tls_db;
+ GTlsCertificate *certificate;
+ GError *error = NULL;
+ gboolean preconnect_request_cert_called = FALSE;
+ gboolean preconnect_request_cert_pass_called = FALSE;
+ gboolean preconnect_finished = FALSE;
+
+ SOUP_TEST_SKIP_IF_NO_TLS;
+
+ session = soup_test_session_new (NULL);
+ tls_db = soup_session_get_tls_database (session);
+ g_object_set (server, "tls-database", tls_db, "tls-auth-mode", G_TLS_AUTHENTICATION_REQUIRED, NULL);
+ g_object_get (server, "tls-certificate", &certificate, NULL);
+ g_signal_connect (server, "request-started",
+ G_CALLBACK (server_request_started),
+ session);
+
+ /* Start a preconnect until it get blocked on tls interaction */
+ preconnect_msg = soup_message_new_from_uri ("HEAD", uri);
+ g_signal_connect (preconnect_msg, "request-certificate",
+ G_CALLBACK (preconnect_request_certificate),
+ &preconnect_request_cert_called);
+ soup_session_preconnect_async (session, preconnect_msg, G_PRIORITY_DEFAULT, NULL, NULL, NULL);
+ while (!soup_message_has_pending_tls_cert_request (preconnect_msg))
+ g_main_context_iteration (NULL, TRUE);
+
+ /* New message should steal the preconnect connection */
+ msg = soup_message_new_from_uri ("GET", uri);
+ g_signal_connect (msg, "request-certificate",
+ G_CALLBACK (request_certificate_cb),
+ certificate);
+ body = soup_test_session_async_send (session, msg, NULL, &error);
+ g_assert_no_error (error);
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+ g_assert_false (preconnect_request_cert_called);
+ g_clear_error (&error);
+ g_bytes_unref (body);
+ g_object_unref (msg);
+ g_object_unref (preconnect_msg);
+
+ soup_session_abort (session);
+
+ /* Preconnect finishes if we set the certificate before */
+ preconnect_msg = soup_message_new_from_uri ("HEAD", uri);
+ g_signal_connect (preconnect_msg, "request-certificate",
+ G_CALLBACK (preconnect_request_certificate),
+ &preconnect_request_cert_called);
+ soup_message_set_tls_client_certificate (preconnect_msg, certificate);
+ soup_session_preconnect_async (session, preconnect_msg, G_PRIORITY_DEFAULT, NULL,
+ (GAsyncReadyCallback)preconnect_finished_cb,
+ &preconnect_finished);
+ while (!preconnect_finished)
+ g_main_context_iteration (NULL, TRUE);
+ g_assert_false (preconnect_request_cert_called);
+ g_object_unref (preconnect_msg);
+ /* New request will use the idle connection without having to provide a certificate */
+ msg = soup_message_new_from_uri ("GET", uri);
+ body = soup_test_session_async_send (session, msg, NULL, &error);
+ g_assert_no_error (error);
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+ g_clear_error (&error);
+ g_bytes_unref (body);
+ g_object_unref (msg);
+
+ soup_session_abort (session);
+
+ /* request-certificate signal is not emitted either if the message stealing the
+ * preconnect connection has a certificate set.
+ */
+ preconnect_msg = soup_message_new_from_uri ("HEAD", uri);
+ g_signal_connect (preconnect_msg, "request-certificate",
+ G_CALLBACK (preconnect_request_certificate),
+ &preconnect_request_cert_called);
+ soup_session_preconnect_async (session, preconnect_msg, G_PRIORITY_DEFAULT, NULL, NULL, NULL);
+ while (!soup_message_has_pending_tls_cert_request (preconnect_msg))
+ g_main_context_iteration (NULL, TRUE);
+
+ /* New message should steal the preconnect connection */
+ msg = soup_message_new_from_uri ("GET", uri);
+ soup_message_set_tls_client_certificate (msg, certificate);
+ body = soup_test_session_async_send (session, msg, NULL, &error);
+ g_assert_no_error (error);
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+ g_assert_false (preconnect_request_cert_called);
+ g_clear_error (&error);
+ g_bytes_unref (body);
+ g_object_unref (msg);
+ g_object_unref (preconnect_msg);
+
+ soup_session_abort (session);
+
+ /* Currently on the gnutls backend supports pkcs#11 */
+ if (g_strcmp0 (g_type_name (G_TYPE_FROM_INSTANCE (g_tls_backend_get_default ())),
"GTlsBackendGnutls") == 0) {
+ GTlsCertificate *pkcs11_certificate;
+
+ pkcs11_certificate = g_tls_certificate_new_from_pkcs11_uris (
+
"pkcs11:model=mock;serial=1;token=Mock%20Certificate;id=%4D%6F%63%6B%20%43%65%72%74%69%66%69%63%61%74%65;object=Mock%20Certificate;type=cert",
+
"pkcs11:model=mock;serial=1;token=Mock%20Certificate;id=%4D%6F%63%6B%20%50%72%69%76%61%74%65%20%4B%65%79;object=Mock%20Private%20Key;type=private",
+ &error
+ );
+ g_assert_no_error (error);
+
+ preconnect_msg = soup_message_new_from_uri ("HEAD", uri);
+ g_signal_connect (preconnect_msg, "request-certificate-password",
+ G_CALLBACK (preconnect_request_certificate_password),
+ &preconnect_request_cert_pass_called);
+ soup_message_set_tls_client_certificate (preconnect_msg, pkcs11_certificate);
+ soup_session_preconnect_async (session, preconnect_msg, G_PRIORITY_DEFAULT, NULL, NULL,
NULL);
+ while (!soup_message_has_pending_tls_cert_pass_request (preconnect_msg))
+ g_main_context_iteration (NULL, TRUE);
+
+ /* New message should steal the preconnect connection */
+ msg = soup_message_new_from_uri ("GET", uri);
+ g_signal_connect (msg, "request-certificate-password",
+ G_CALLBACK (request_certificate_password_cb),
+ "ABC123");
+ body = soup_test_session_async_send (session, msg, NULL, &error);
+ g_assert_no_error (error);
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+ g_assert_false (preconnect_request_cert_pass_called);
+ g_clear_error (&error);
+ g_bytes_unref (body);
+ g_object_unref (msg);
+ g_object_unref (preconnect_msg);
+
+ g_object_unref (pkcs11_certificate);
+ }
+
+ g_object_set (server, "tls-database", NULL, "tls-auth-mode", G_TLS_AUTHENTICATION_NONE, NULL);
+ g_signal_handlers_disconnect_by_data (server, session);
+
+ soup_test_session_abort_unref (session);
+ g_object_unref (certificate);
+}
+
static void
server_handler (SoupServer *server,
SoupServerMessage *msg,
@@ -544,6 +717,7 @@ main (int argc, char **argv)
g_test_add_data_func ("/ssl/tls-interaction", server, do_tls_interaction_test);
g_test_add_data_func ("/ssl/tls-interaction-msg", server, do_tls_interaction_msg_test);
+ g_test_add_data_func ("/ssl/tls-interaction/preconnect", server, do_tls_interaction_preconnect_test);
for (i = 0; i < G_N_ELEMENTS (strictness_tests); i++) {
g_test_add_data_func (strictness_tests[i].name,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]