summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Garcia Campos <cgarcia@igalia.com>2021-04-19 15:07:55 +0200
committerCarlos Garcia Campos <carlosgc@gnome.org>2021-06-02 07:22:46 +0000
commitd72d9eabd74880be037818deb4c6ae73fdda697b (patch)
tree50e800cac7e20f0d0b25b519670d57d7dee9a8ee
parente13c09112c0a5506c9625f35ca0d3739f92b8516 (diff)
downloadlibsoup-d72d9eabd74880be037818deb4c6ae73fdda697b.tar.gz
message: add API to handle client side certificates
When SoupSession doesn't have a GTlsInteraction set, SoupMessage can handle client side certificates. A new signal request-certificate is emitted when the connection requests a certificate and soup_message_set_tls_client_certificate() can be called to complete the request.
-rw-r--r--docs/reference/libsoup-3.0-sections.txt1
-rw-r--r--docs/reference/meson.build1
-rw-r--r--libsoup/auth/soup-tls-interaction.c96
-rw-r--r--libsoup/auth/soup-tls-interaction.h19
-rw-r--r--libsoup/meson.build1
-rw-r--r--libsoup/soup-connection.c82
-rw-r--r--libsoup/soup-connection.h8
-rw-r--r--libsoup/soup-message.c115
-rw-r--r--libsoup/soup-message.h4
-rw-r--r--tests/ssl-test.c133
10 files changed, 459 insertions, 1 deletions
diff --git a/docs/reference/libsoup-3.0-sections.txt b/docs/reference/libsoup-3.0-sections.txt
index b34559f4..19d814a9 100644
--- a/docs/reference/libsoup-3.0-sections.txt
+++ b/docs/reference/libsoup-3.0-sections.txt
@@ -31,6 +31,7 @@ soup_message_get_connection_id
soup_message_get_remote_address
soup_message_get_tls_peer_certificate
soup_message_get_tls_peer_certificate_errors
+soup_message_set_tls_client_certificate
<SUBSECTION>
soup_message_set_first_party
soup_message_get_first_party
diff --git a/docs/reference/meson.build b/docs/reference/meson.build
index c11db34a..19732d5b 100644
--- a/docs/reference/meson.build
+++ b/docs/reference/meson.build
@@ -43,6 +43,7 @@ ignore_headers = [
'soup-client-message-io-http1.h',
'soup-client-message-io-http2.h',
'soup-body-input-stream-http2.h',
+ 'soup-tls-interaction.h',
]
mkdb_args = [
diff --git a/libsoup/auth/soup-tls-interaction.c b/libsoup/auth/soup-tls-interaction.c
new file mode 100644
index 00000000..0ade927e
--- /dev/null
+++ b/libsoup/auth/soup-tls-interaction.c
@@ -0,0 +1,96 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * soup-tls-interaction.c: TLS interaction implementation
+ *
+ * Copyright (C) 2021 Igalia S.L.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-tls-interaction.h"
+
+struct _SoupTlsInteraction {
+ GTlsInteraction parent;
+};
+
+typedef struct {
+ SoupConnection *conn;
+} SoupTlsInteractionPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (SoupTlsInteraction, soup_tls_interaction, G_TYPE_TLS_INTERACTION)
+
+static void
+soup_tls_interaction_request_certificate_async (GTlsInteraction *tls_interaction,
+ GTlsConnection *connection,
+ GTlsCertificateRequestFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SoupTlsInteractionPrivate *priv = soup_tls_interaction_get_instance_private (SOUP_TLS_INTERACTION (tls_interaction));
+ GTask *task;
+
+ task = g_task_new (tls_interaction, cancellable, callback, user_data);
+ if (priv->conn)
+ soup_connection_request_tls_certificate (priv->conn, connection, task);
+ else
+ g_task_return_int (task, G_TLS_INTERACTION_FAILED);
+ g_object_unref (task);
+}
+
+static GTlsInteractionResult
+soup_tls_interaction_request_certificate_finish (GTlsInteraction *tls_interaction,
+ GAsyncResult *result,
+ GError **error)
+{
+ int task_result;
+
+ task_result = g_task_propagate_int (G_TASK (result), error);
+ return task_result != -1 ? task_result : G_TLS_INTERACTION_FAILED;
+}
+
+static void
+soup_tls_interaction_finalize (GObject *object)
+{
+ SoupTlsInteractionPrivate *priv = soup_tls_interaction_get_instance_private (SOUP_TLS_INTERACTION (object));
+
+ if (priv->conn) {
+ g_object_remove_weak_pointer (G_OBJECT (priv->conn), (gpointer*)&priv->conn);
+ priv->conn = NULL;
+ }
+
+ G_OBJECT_CLASS (soup_tls_interaction_parent_class)->finalize (object);
+}
+
+static void
+soup_tls_interaction_init (SoupTlsInteraction *interaction)
+{
+}
+
+static void
+soup_tls_interaction_class_init (SoupTlsInteractionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GTlsInteractionClass *interaction_class = G_TLS_INTERACTION_CLASS (klass);
+
+ object_class->finalize = soup_tls_interaction_finalize;
+
+ interaction_class->request_certificate_async = soup_tls_interaction_request_certificate_async;
+ interaction_class->request_certificate_finish = soup_tls_interaction_request_certificate_finish;
+}
+
+GTlsInteraction *
+soup_tls_interaction_new (SoupConnection *conn)
+{
+ GTlsInteraction *interaction;
+ SoupTlsInteractionPrivate *priv;
+
+ interaction = g_object_new (SOUP_TYPE_TLS_INTERACTION, NULL);
+ priv = soup_tls_interaction_get_instance_private (SOUP_TLS_INTERACTION (interaction));
+ priv->conn = conn;
+ g_object_add_weak_pointer (G_OBJECT (priv->conn), (gpointer*)&priv->conn);
+
+ return interaction;
+}
diff --git a/libsoup/auth/soup-tls-interaction.h b/libsoup/auth/soup-tls-interaction.h
new file mode 100644
index 00000000..f6ba25ee
--- /dev/null
+++ b/libsoup/auth/soup-tls-interaction.h
@@ -0,0 +1,19 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2021 Igalia S.L.
+ */
+
+#pragma once
+
+#include "soup-connection.h"
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_TLS_INTERACTION (soup_tls_interaction_get_type ())
+G_DECLARE_FINAL_TYPE (SoupTlsInteraction, soup_tls_interaction, SOUP, TLS_INTERACTION, GTlsInteraction)
+
+GType soup_tls_interaction_get_type (void);
+GTlsInteraction *soup_tls_interaction_new (SoupConnection *conn);
+
+G_END_DECLS
diff --git a/libsoup/meson.build b/libsoup/meson.build
index a9faf833..6142ef7b 100644
--- a/libsoup/meson.build
+++ b/libsoup/meson.build
@@ -11,6 +11,7 @@ soup_sources = [
'auth/soup-auth-negotiate.c',
'auth/soup-auth-manager.c',
'auth/soup-connection-auth.c',
+ 'auth/soup-tls-interaction.c',
'cache/soup-cache.c',
'cache/soup-cache-client-input-stream.c',
diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c
index 6ead5709..0da2696f 100644
--- a/libsoup/soup-connection.c
+++ b/libsoup/soup-connection.c
@@ -17,6 +17,7 @@
#include "soup-client-message-io-http2.h"
#include "soup-socket-properties.h"
#include "soup-private-enum-types.h"
+#include "soup-tls-interaction.h"
#include <gio/gnetworking.h>
struct _SoupConnection {
@@ -43,6 +44,8 @@ typedef struct {
guint in_use;
SoupHTTPVersion http_version;
+ GTlsCertificate *tls_client_cert;
+
GCancellable *cancellable;
} SoupConnectionPrivate;
@@ -51,6 +54,7 @@ G_DEFINE_TYPE_WITH_PRIVATE (SoupConnection, soup_connection, G_TYPE_OBJECT)
enum {
EVENT,
ACCEPT_CERTIFICATE,
+ REQUEST_CERTIFICATE,
DISCONNECTED,
LAST_SIGNAL
};
@@ -114,6 +118,7 @@ soup_connection_finalize (GObject *object)
}
g_clear_object (&priv->iostream);
+ g_clear_object (&priv->tls_client_cert);
G_OBJECT_CLASS (soup_connection_parent_class)->finalize (object);
}
@@ -229,6 +234,16 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
G_TYPE_BOOLEAN, 2,
G_TYPE_TLS_CERTIFICATE,
G_TYPE_TLS_CERTIFICATE_FLAGS);
+ signals[REQUEST_CERTIFICATE] =
+ g_signal_new ("request-certificate",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ g_signal_accumulator_true_handled, NULL,
+ NULL,
+ G_TYPE_BOOLEAN, 2,
+ G_TYPE_TLS_CLIENT_CONNECTION,
+ G_TYPE_TASK);
signals[DISCONNECTED] =
g_signal_new ("disconnected",
G_OBJECT_CLASS_TYPE (object_class),
@@ -503,6 +518,7 @@ new_tls_connection (SoupConnection *conn,
{
SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
GTlsClientConnection *tls_connection;
+ GTlsInteraction *tls_interaction;
GPtrArray *advertised_protocols = g_ptr_array_sized_new (4);
// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml
@@ -513,12 +529,13 @@ new_tls_connection (SoupConnection *conn,
g_ptr_array_add (advertised_protocols, "http/1.0");
g_ptr_array_add (advertised_protocols, NULL);
+ tls_interaction = priv->socket_props->tls_interaction ? g_object_ref (priv->socket_props->tls_interaction) : soup_tls_interaction_new (conn);
tls_connection = g_initable_new (g_tls_backend_get_client_connection_type (g_tls_backend_get_default ()),
priv->cancellable, error,
"base-io-stream", connection,
"server-identity", priv->remote_connectable,
"require-close-notify", FALSE,
- "interaction", priv->socket_props->tls_interaction,
+ "interaction", tls_interaction,
"advertised-protocols", advertised_protocols->pdata,
NULL);
@@ -1120,6 +1137,69 @@ soup_connection_get_tls_certificate_errors (SoupConnection *conn)
return g_tls_connection_get_peer_certificate_errors (G_TLS_CONNECTION (priv->connection));
}
+void
+soup_connection_set_tls_client_certificate (SoupConnection *conn,
+ GTlsCertificate *certificate)
+{
+ SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
+
+ if (G_IS_TLS_CONNECTION (priv->connection)) {
+ g_tls_connection_set_certificate (G_TLS_CONNECTION (priv->connection),
+ certificate);
+ g_clear_object (&priv->tls_client_cert);
+ return;
+ }
+
+ if (priv->tls_client_cert == certificate)
+ return;
+
+ g_clear_object (&priv->tls_client_cert);
+ priv->tls_client_cert = g_object_ref (certificate);
+}
+
+void
+soup_connection_request_tls_certificate (SoupConnection *conn,
+ GTlsConnection *connection,
+ GTask *task)
+{
+ SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
+ gboolean handled = FALSE;
+
+ if (!G_IS_TLS_CONNECTION (priv->connection) || G_TLS_CONNECTION (priv->connection) != connection) {
+ g_task_return_int (task, G_TLS_INTERACTION_FAILED);
+ return;
+ }
+
+ if (priv->tls_client_cert) {
+ soup_connection_complete_tls_certificate_request (conn,
+ priv->tls_client_cert,
+ g_object_ref (task));
+ g_clear_object (&priv->tls_client_cert);
+ return;
+ }
+
+ g_signal_emit (conn, signals[REQUEST_CERTIFICATE], 0, connection, task, &handled);
+ if (!handled)
+ g_task_return_int (task, G_TLS_INTERACTION_FAILED);
+}
+
+void
+soup_connection_complete_tls_certificate_request (SoupConnection *conn,
+ GTlsCertificate *certificate,
+ GTask *task)
+{
+ SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
+
+ if (G_IS_TLS_CONNECTION (priv->connection) && certificate) {
+ g_tls_connection_set_certificate (G_TLS_CONNECTION (priv->connection),
+ certificate);
+ g_task_return_int (task, G_TLS_INTERACTION_HANDLED);
+ } else {
+ g_task_return_int (task, G_TLS_INTERACTION_FAILED);
+ }
+ g_object_unref (task);
+}
+
guint64
soup_connection_get_id (SoupConnection *conn)
{
diff --git a/libsoup/soup-connection.h b/libsoup/soup-connection.h
index 399866a8..701b2d94 100644
--- a/libsoup/soup-connection.h
+++ b/libsoup/soup-connection.h
@@ -65,6 +65,14 @@ SoupClientMessageIO *soup_connection_setup_message_io (SoupConnection *conn,
GTlsCertificate *soup_connection_get_tls_certificate (SoupConnection *conn);
GTlsCertificateFlags soup_connection_get_tls_certificate_errors (SoupConnection *conn);
+void soup_connection_request_tls_certificate (SoupConnection *conn,
+ GTlsConnection *connection,
+ GTask *task);
+void soup_connection_complete_tls_certificate_request (SoupConnection *conn,
+ GTlsCertificate *certificate,
+ GTask *task);
+void soup_connection_set_tls_client_certificate (SoupConnection *conn,
+ GTlsCertificate *certificate);
guint64 soup_connection_get_id (SoupConnection *conn);
GSocketAddress *soup_connection_get_remote_address (SoupConnection *conn);
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index be6f26b9..1d627cb5 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -89,6 +89,9 @@ typedef struct {
GTlsCertificate *tls_peer_certificate;
GTlsCertificateFlags tls_peer_certificate_errors;
+ GTlsCertificate *tls_client_certificate;
+ GTask *pending_tls_cert_request;
+
SoupMessagePriority priority;
gboolean is_top_level_navigation;
@@ -119,6 +122,7 @@ enum {
AUTHENTICATE,
NETWORK_EVENT,
ACCEPT_CERTIFICATE,
+ REQUEST_CERTIFICATE,
HSTS_ENFORCED,
LAST_SIGNAL
@@ -169,6 +173,11 @@ soup_message_finalize (GObject *object)
SoupMessage *msg = SOUP_MESSAGE (object);
SoupMessagePrivate *priv = soup_message_get_instance_private (msg);
+ if (priv->pending_tls_cert_request) {
+ g_task_return_int (priv->pending_tls_cert_request, G_TLS_INTERACTION_FAILED);
+ g_object_unref (priv->pending_tls_cert_request);
+ }
+
soup_message_set_connection (msg, NULL);
g_clear_pointer (&priv->uri, g_uri_unref);
@@ -183,6 +192,7 @@ soup_message_finalize (GObject *object)
g_clear_object (&priv->tls_peer_certificate);
g_clear_object (&priv->remote_address);
+ g_clear_object (&priv->tls_client_certificate);
soup_message_headers_unref (priv->request_headers);
soup_message_headers_unref (priv->response_headers);
@@ -594,6 +604,35 @@ soup_message_class_init (SoupMessageClass *message_class)
G_TYPE_TLS_CERTIFICATE,
G_TYPE_TLS_CERTIFICATE_FLAGS);
+ /**
+ * SoupMessage::request-certificate:
+ * @msg: the message
+ * @tls_connection: the #GTlsClientConnection
+ *
+ * Emitted during the @msg's connection TLS handshake when
+ * @tls_connection requests a certificate from the client.
+ * You can set the client certificate by calling
+ * soup_message_set_tls_client_certificate() and returning %TRUE.
+ * It's possible to handle the request asynchornously by returning
+ * %TRUE and call soup_message_set_tls_client_certificate() later
+ * once the certificate is available.
+ * Note that this signal is not emitted if #SoupSession::tls-interaction
+ * was set, or if soup_message_set_tls_client_certificate() was called
+ * before the connection TLS handshake started.
+ *
+ * Returns: %TRUE to handle the request, or %FALSE to make the connection
+ * fail with %G_TLS_ERROR_CERTIFICATE_REQUIRED.
+ */
+ signals[REQUEST_CERTIFICATE] =
+ g_signal_new ("request-certificate",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ g_signal_accumulator_true_handled, NULL,
+ NULL,
+ G_TYPE_BOOLEAN, 1,
+ G_TYPE_TLS_CLIENT_CONNECTION);
+
/**
* SoupMessage::hsts-enforced:
* @msg: the message
@@ -1391,6 +1430,23 @@ re_emit_accept_certificate (SoupMessage *msg,
return accept;
}
+static gboolean
+re_emit_request_certificate (SoupMessage *msg,
+ GTlsClientConnection *tls_conn,
+ GTask *task)
+{
+ SoupMessagePrivate *priv = soup_message_get_instance_private (msg);
+ gboolean handled = FALSE;
+
+ priv->pending_tls_cert_request = g_object_ref (task);
+
+ g_signal_emit (msg, signals[REQUEST_CERTIFICATE], 0, tls_conn, &handled);
+ if (!handled)
+ g_clear_object (&priv->pending_tls_cert_request);
+
+ return handled;
+}
+
static void
re_emit_tls_certificate_changed (SoupMessage *msg,
GParamSpec *pspec,
@@ -1421,6 +1477,13 @@ soup_message_set_connection (SoupMessage *msg,
if (priv->connection) {
g_signal_handlers_disconnect_by_data (priv->connection, msg);
priv->io_data = NULL;
+
+ if (priv->pending_tls_cert_request) {
+ soup_connection_complete_tls_certificate_request (priv->connection,
+ priv->tls_client_certificate,
+ g_steal_pointer (&priv->pending_tls_cert_request));
+ g_clear_object (&priv->tls_client_certificate);
+ }
g_object_remove_weak_pointer (G_OBJECT (priv->connection), (gpointer*)&priv->connection);
soup_connection_set_in_use (priv->connection, FALSE);
}
@@ -1438,12 +1501,21 @@ soup_message_set_connection (SoupMessage *msg,
soup_connection_get_tls_certificate_errors (priv->connection));
soup_message_set_remote_address (msg, soup_connection_get_remote_address (priv->connection));
+ if (priv->tls_client_certificate) {
+ soup_connection_set_tls_client_certificate (priv->connection,
+ priv->tls_client_certificate);
+ g_clear_object (&priv->tls_client_certificate);
+ }
+
g_signal_connect_object (priv->connection, "event",
G_CALLBACK (re_emit_connection_event),
msg, G_CONNECT_SWAPPED);
g_signal_connect_object (priv->connection, "accept-certificate",
G_CALLBACK (re_emit_accept_certificate),
msg, G_CONNECT_SWAPPED);
+ g_signal_connect_object (priv->connection, "request-certificate",
+ G_CALLBACK (re_emit_request_certificate),
+ msg, G_CONNECT_SWAPPED);
g_signal_connect_object (priv->connection, "notify::tls-certificate",
G_CALLBACK (re_emit_tls_certificate_changed),
msg, G_CONNECT_SWAPPED);
@@ -2101,6 +2173,49 @@ soup_message_get_tls_peer_certificate_errors (SoupMessage *msg)
}
/**
+ * soup_message_set_tls_client_certificate:
+ * @msg: a #SoupMessage
+ * @certificate: the #GTlsCertificate to set
+ *
+ * Sets the @certificate to be used by @msg's connection when a
+ * client certificate is requested during the TLS handshake.
+ * You can call this as a response to #SoupMessage::request-certificate
+ * signal, or before the connection is started.
+ * Note that the #GTlsCertificate set by this function will be ignored if
+ * #SoupSession::tls-interaction is not %NULL.
+ */
+void
+soup_message_set_tls_client_certificate (SoupMessage *msg,
+ GTlsCertificate *certificate)
+{
+ SoupMessagePrivate *priv;
+
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+ g_return_if_fail (G_IS_TLS_CERTIFICATE (certificate));
+
+ priv = soup_message_get_instance_private (msg);
+ if (priv->pending_tls_cert_request) {
+ g_assert (SOUP_IS_CONNECTION (priv->connection));
+ soup_connection_complete_tls_certificate_request (priv->connection,
+ certificate,
+ g_steal_pointer (&priv->pending_tls_cert_request));
+ return;
+ }
+
+ if (priv->connection) {
+ soup_connection_set_tls_client_certificate (priv->connection,
+ certificate);
+ return;
+ }
+
+ if (priv->tls_client_certificate == certificate)
+ return;
+
+ g_clear_object (&priv->tls_client_certificate);
+ priv->tls_client_certificate = g_object_ref (certificate);
+}
+
+/**
* SoupMessagePriority:
* @SOUP_MESSAGE_PRIORITY_VERY_LOW: The lowest priority, the messages
* with this priority will be the last ones to be attended.
diff --git a/libsoup/soup-message.h b/libsoup/soup-message.h
index 93fd9af0..5cd75551 100644
--- a/libsoup/soup-message.h
+++ b/libsoup/soup-message.h
@@ -106,6 +106,10 @@ GTlsCertificate *soup_message_get_tls_peer_certificate (SoupMessage *
SOUP_AVAILABLE_IN_ALL
GTlsCertificateFlags soup_message_get_tls_peer_certificate_errors (SoupMessage *msg);
+SOUP_AVAILABLE_IN_ALL
+void soup_message_set_tls_client_certificate (SoupMessage *msg,
+ GTlsCertificate *certificate);
+
/* Specialized signal handlers */
SOUP_AVAILABLE_IN_ALL
diff --git a/tests/ssl-test.c b/tests/ssl-test.c
index 650c7b87..d0c222aa 100644
--- a/tests/ssl-test.c
+++ b/tests/ssl-test.c
@@ -204,6 +204,138 @@ do_tls_interaction_test (gconstpointer data)
g_object_unref (certificate);
}
+static gboolean
+request_certificate_cb (SoupMessage *msg,
+ GTlsClientConnection *conn,
+ GTlsCertificate *certificate)
+{
+ soup_message_set_tls_client_certificate (msg, certificate);
+
+ return TRUE;
+}
+
+typedef struct {
+ SoupMessage *msg;
+ GTlsCertificate *certificate;
+} SetCertificateAsyncData;
+
+static gboolean
+set_certificate_idle_cb (SetCertificateAsyncData *data)
+{
+ soup_message_set_tls_client_certificate (data->msg, data->certificate);
+
+ return FALSE;
+}
+
+static gboolean
+request_certificate_async_cb (SoupMessage *msg,
+ GTlsClientConnection *conn,
+ GTlsCertificate *certificate)
+{
+ SetCertificateAsyncData *data;
+
+ data = g_new (SetCertificateAsyncData, 1);
+ data->msg = msg;
+ data->certificate = certificate;
+ g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+ (GSourceFunc)set_certificate_idle_cb,
+ data, g_free);
+
+ return TRUE;
+}
+
+static void
+do_tls_interaction_msg_test (gconstpointer data)
+{
+ SoupServer *server = (SoupServer *)data;
+ SoupSession *session;
+ SoupMessage *msg;
+ GBytes *body;
+ GTlsDatabase *tls_db;
+ GTlsCertificate *certificate;
+ GError *error = NULL;
+
+ SOUP_TEST_SKIP_IF_NO_TLS;
+
+ session = soup_test_session_new (NULL);
+ tls_db = soup_session_get_tls_database (session);
+
+ g_signal_connect (server, "request-started",
+ G_CALLBACK (server_request_started),
+ tls_db);
+
+ /* Not handling request-certificate signal */
+ msg = soup_message_new_from_uri ("GET", uri);
+ body = soup_test_session_async_send (session, msg, NULL, &error);
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED))
+ g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED);
+ g_clear_error (&error);
+ g_bytes_unref (body);
+ g_object_unref (msg);
+
+ /* Handling the request-certificate signal synchronously */
+ g_object_get (server, "tls-certificate", &certificate, NULL);
+ g_assert_nonnull (certificate);
+ 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_clear_error (&error);
+ g_bytes_unref (body);
+ g_object_unref (msg);
+
+ /* Next load doesn't emit request-certificate because the connection is reused */
+ 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);
+
+ /* It fails for a new connection */
+ msg = soup_message_new_from_uri ("GET", uri);
+ soup_message_add_flags (msg, SOUP_MESSAGE_NEW_CONNECTION);
+ body = soup_test_session_async_send (session, msg, NULL, &error);
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED))
+ g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED);
+ g_clear_error (&error);
+ g_bytes_unref (body);
+ g_object_unref (msg);
+
+ /* request-certificate is not emitted if the certificate is set before the load */
+ msg = soup_message_new_from_uri ("GET", uri);
+ soup_message_add_flags (msg, SOUP_MESSAGE_NEW_CONNECTION);
+ 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_clear_error (&error);
+ g_bytes_unref (body);
+ g_object_unref (msg);
+
+ /* Handling the request-certificate signal asynchronously */
+ msg = soup_message_new_from_uri ("GET", uri);
+ soup_message_add_flags (msg, SOUP_MESSAGE_NEW_CONNECTION);
+ g_signal_connect (msg, "request-certificate",
+ G_CALLBACK (request_certificate_async_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_clear_error (&error);
+ g_bytes_unref (body);
+ g_object_unref (msg);
+
+ g_signal_handlers_disconnect_by_data (server, tls_db);
+
+ soup_test_session_abort_unref (session);
+ g_object_unref (certificate);
+}
+
static void
server_handler (SoupServer *server,
SoupServerMessage *msg,
@@ -233,6 +365,7 @@ main (int argc, char **argv)
uri = NULL;
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);
for (i = 0; i < G_N_ELEMENTS (strictness_tests); i++) {
g_test_add_data_func (strictness_tests[i].name,