[libsoup/carlosgc/client-side-certs2: 2/10] message: add API to handle client side certificates
- From: Carlos Garcia Campos <carlosgc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsoup/carlosgc/client-side-certs2: 2/10] message: add API to handle client side certificates
- Date: Fri, 30 Apr 2021 09:52:04 +0000 (UTC)
commit 1d4e320a12bd7a744cc5dbf06625562051c57f4e
Author: Carlos Garcia Campos <cgarcia igalia com>
Date: Mon Apr 19 15:07:55 2021 +0200
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.
docs/reference/libsoup-3.0-sections.txt | 1 +
docs/reference/meson.build | 1 +
libsoup/auth/soup-tls-interaction.c | 96 +++++++++++++++++++++++
libsoup/auth/soup-tls-interaction.h | 19 +++++
libsoup/meson.build | 1 +
libsoup/soup-connection.c | 84 +++++++++++++++++++-
libsoup/soup-connection.h | 8 ++
libsoup/soup-message.c | 115 +++++++++++++++++++++++++++
libsoup/soup-message.h | 4 +
tests/ssl-test.c | 133 ++++++++++++++++++++++++++++++++
10 files changed, 461 insertions(+), 1 deletion(-)
---
diff --git a/docs/reference/libsoup-3.0-sections.txt b/docs/reference/libsoup-3.0-sections.txt
index 4d1432c4..dc21cd5e 100644
--- a/docs/reference/libsoup-3.0-sections.txt
+++ b/docs/reference/libsoup-3.0-sections.txt
@@ -30,6 +30,7 @@ soup_message_is_keepalive
soup_message_get_connection_id
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 10e64bb8..9ab1b4cd 100644
--- a/docs/reference/meson.build
+++ b/docs/reference/meson.build
@@ -38,6 +38,7 @@ ignore_headers = [
'soup-uri-utils-private.h',
'soup-session-feature-private.h',
'soup-message-metrics-private.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 7bdaf0ac..ab758e5c 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 2049165a..5d1f3406 100644
--- a/libsoup/soup-connection.c
+++ b/libsoup/soup-connection.c
@@ -15,6 +15,7 @@
#include "soup-message-queue-item.h"
#include "soup-socket-properties.h"
#include "soup-private-enum-types.h"
+#include "soup-tls-interaction.h"
#include <gio/gnetworking.h>
struct _SoupConnection {
@@ -38,6 +39,8 @@ typedef struct {
GSource *idle_timeout_src;
gboolean reusable;
+ GTlsCertificate *tls_client_cert;
+
GCancellable *cancellable;
} SoupConnectionPrivate;
@@ -46,6 +49,7 @@ G_DEFINE_TYPE_WITH_PRIVATE (SoupConnection, soup_connection, G_TYPE_OBJECT)
enum {
EVENT,
ACCEPT_CERTIFICATE,
+ REQUEST_CERTIFICATE,
DISCONNECTED,
LAST_SIGNAL
};
@@ -101,6 +105,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);
}
@@ -210,6 +215,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),
@@ -456,14 +471,18 @@ new_tls_connection (SoupConnection *conn,
{
SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
GTlsClientConnection *tls_connection;
+ GTlsInteraction *tls_interaction;
+ 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,
NULL);
+ g_object_unref (tls_interaction);
+
if (!tls_connection)
return NULL;
@@ -1092,6 +1111,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 bc0550d9..881a546c 100644
--- a/libsoup/soup-connection.h
+++ b/libsoup/soup-connection.h
@@ -72,6 +72,14 @@ void soup_connection_message_io_finished (SoupConnection *co
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);
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index c39ad4b8..c2468f3a 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;
@@ -117,6 +120,7 @@ enum {
AUTHENTICATE,
NETWORK_EVENT,
ACCEPT_CERTIFICATE,
+ REQUEST_CERTIFICATE,
HSTS_ENFORCED,
LAST_SIGNAL
@@ -164,6 +168,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);
@@ -177,6 +186,7 @@ soup_message_finalize (GObject *object)
g_clear_pointer (&priv->disabled_features, g_hash_table_destroy);
g_clear_object (&priv->tls_peer_certificate);
+ g_clear_object (&priv->tls_client_certificate);
soup_message_headers_unref (priv->request_headers);
soup_message_headers_unref (priv->response_headers);
@@ -585,6 +595,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
@@ -1368,6 +1407,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,
@@ -1390,6 +1446,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);
}
@@ -1404,12 +1467,21 @@ soup_message_set_connection (SoupMessage *msg,
soup_connection_get_tls_certificate (priv->connection),
soup_connection_get_tls_certificate_errors
(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);
@@ -2059,6 +2131,49 @@ soup_message_get_tls_peer_certificate_errors (SoupMessage *msg)
return priv->tls_peer_certificate_errors;
}
+/**
+ * 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
diff --git a/libsoup/soup-message.h b/libsoup/soup-message.h
index 3b1f8811..5e82145c 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,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]