Commit f4c7545e authored by Carlos Garcia Campos's avatar Carlos Garcia Campos
Browse files

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.
parent dac4b0ba
Pipeline #290177 passed with stages
in 5 minutes and 2 seconds
......@@ -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);
......
......@@ -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
......
......@@ -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);
......
/* -*- 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,
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment