[libsoup] Add tls-interaction property to SoupSession
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsoup] Add tls-interaction property to SoupSession
- Date: Sat, 3 May 2014 17:32:25 +0000 (UTC)
commit 0ee90b166ba4a9ca3c8c66a531d97c661536087d
Author: Dan Winship <danw gnome org>
Date: Fri May 2 16:01:24 2014 -0400
Add tls-interaction property to SoupSession
This can be used by applications to do client-side certificates via
the new g_tls_interaction_request_certificate(). Will be used by
OSTree at least.
Also add a test to tls-test.
Based on a patch from Colin Walters.
https://bugzilla.gnome.org/show_bug.cgi?id=334021
libsoup/soup-session.c | 35 ++++++++
libsoup/soup-session.h | 1 +
libsoup/soup-socket-private.h | 18 +++--
libsoup/soup-socket-properties.c | 19 +++--
libsoup/soup-socket.c | 14 +++
tests/ssl-test.c | 167 ++++++++++++++++++++++++++++++++++++++
6 files changed, 238 insertions(+), 16 deletions(-)
---
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index cbefc64..04d311e 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -87,6 +87,7 @@ typedef struct {
gboolean disposed;
GTlsDatabase *tlsdb;
+ GTlsInteraction *tls_interaction;
char *ssl_ca_file;
gboolean ssl_strict;
gboolean tlsdb_use_default;
@@ -200,6 +201,7 @@ enum {
PROP_HTTP_ALIASES,
PROP_HTTPS_ALIASES,
PROP_LOCAL_ADDRESS,
+ PROP_TLS_INTERACTION,
LAST_PROP
};
@@ -337,6 +339,7 @@ soup_session_finalize (GObject *object)
g_free (priv->accept_language);
g_clear_object (&priv->tlsdb);
+ g_clear_object (&priv->tls_interaction);
g_free (priv->ssl_ca_file);
g_clear_pointer (&priv->async_context, g_main_context_unref);
@@ -384,6 +387,7 @@ ensure_socket_props (SoupSession *session)
priv->proxy_resolver,
priv->local_addr,
priv->tlsdb,
+ priv->tls_interaction,
ssl_strict,
priv->io_timeout,
priv->idle_timeout);
@@ -683,6 +687,10 @@ soup_session_set_property (GObject *object, guint prop_id,
set_tlsdb (session, g_value_get_object (value));
socket_props_changed = TRUE;
break;
+ case PROP_TLS_INTERACTION:
+ priv->tls_interaction = g_value_dup_object (value);
+ socket_props_changed = TRUE;
+ break;
case PROP_SSL_STRICT:
priv->ssl_strict = g_value_get_boolean (value);
socket_props_changed = TRUE;
@@ -821,6 +829,9 @@ soup_session_get_property (GObject *object, guint prop_id,
ensure_socket_props (session);
g_value_set_object (value, priv->tlsdb);
break;
+ case PROP_TLS_INTERACTION:
+ g_value_set_object (value, priv->tls_interaction);
+ break;
case PROP_SSL_STRICT:
g_value_set_boolean (value, priv->ssl_strict);
break;
@@ -3748,6 +3759,30 @@ soup_session_class_init (SoupSessionClass *session_class)
"Address of local end of socket",
SOUP_TYPE_ADDRESS,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * SOUP_SESSION_TLS_INTERACTION:
+ *
+ * Alias for the #SoupSession:tls-interaction property, qv.
+ *
+ * Since: 2.48
+ **/
+ /**
+ * SoupSession:tls-interaction:
+ *
+ * A #GTlsInteraction object that will be passed on to any
+ * #GTlsConnections created by the session. (This can be used to
+ * provide client-side certificates, for example.)
+ *
+ * Since: 2.48
+ **/
+ g_object_class_install_property (
+ object_class, PROP_TLS_INTERACTION,
+ g_param_spec_object (SOUP_SESSION_TLS_INTERACTION,
+ "TLS Interaction",
+ "TLS interaction to use",
+ G_TYPE_TLS_INTERACTION,
+ G_PARAM_READWRITE));
}
diff --git a/libsoup/soup-session.h b/libsoup/soup-session.h
index 67a59ea..eed392d 100644
--- a/libsoup/soup-session.h
+++ b/libsoup/soup-session.h
@@ -70,6 +70,7 @@ GType soup_session_get_type (void);
#define SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE "ssl-use-system-ca-file"
#define SOUP_SESSION_TLS_DATABASE "tls-database"
#define SOUP_SESSION_SSL_STRICT "ssl-strict"
+#define SOUP_SESSION_TLS_INTERACTION "tls-interaction"
#define SOUP_SESSION_ASYNC_CONTEXT "async-context"
#define SOUP_SESSION_USE_THREAD_CONTEXT "use-thread-context"
#define SOUP_SESSION_TIMEOUT "timeout"
diff --git a/libsoup/soup-socket-private.h b/libsoup/soup-socket-private.h
index 55a3773..68a392f 100644
--- a/libsoup/soup-socket-private.h
+++ b/libsoup/soup-socket-private.h
@@ -53,6 +53,7 @@ typedef struct {
SoupAddress *local_addr;
GTlsDatabase *tlsdb;
+ GTlsInteraction *tls_interaction;
gboolean ssl_strict;
guint io_timeout;
@@ -65,14 +66,15 @@ typedef struct {
GType soup_socket_properties_get_type (void);
#define SOUP_TYPE_SOCKET_PROPERTIES (soup_socket_properties_get_type ())
-SoupSocketProperties *soup_socket_properties_new (GMainContext *async_context,
- gboolean use_thread_context,
- GProxyResolver *proxy_resolver,
- SoupAddress *local_addr,
- GTlsDatabase *tlsdb,
- gboolean ssl_strict,
- guint io_timeout,
- guint idle_timeout);
+SoupSocketProperties *soup_socket_properties_new (GMainContext *async_context,
+ gboolean use_thread_context,
+ GProxyResolver *proxy_resolver,
+ SoupAddress *local_addr,
+ GTlsDatabase *tlsdb,
+ GTlsInteraction *tls_interaction,
+ gboolean ssl_strict,
+ guint io_timeout,
+ guint idle_timeout);
SoupSocketProperties *soup_socket_properties_ref (SoupSocketProperties *props);
void soup_socket_properties_unref (SoupSocketProperties *props);
diff --git a/libsoup/soup-socket-properties.c b/libsoup/soup-socket-properties.c
index a7ed512..0482c44 100644
--- a/libsoup/soup-socket-properties.c
+++ b/libsoup/soup-socket-properties.c
@@ -7,14 +7,15 @@
#include "soup.h"
SoupSocketProperties *
-soup_socket_properties_new (GMainContext *async_context,
- gboolean use_thread_context,
- GProxyResolver *proxy_resolver,
- SoupAddress *local_addr,
- GTlsDatabase *tlsdb,
- gboolean ssl_strict,
- guint io_timeout,
- guint idle_timeout)
+soup_socket_properties_new (GMainContext *async_context,
+ gboolean use_thread_context,
+ GProxyResolver *proxy_resolver,
+ SoupAddress *local_addr,
+ GTlsDatabase *tlsdb,
+ GTlsInteraction *tls_interaction,
+ gboolean ssl_strict,
+ guint io_timeout,
+ guint idle_timeout)
{
SoupSocketProperties *props;
@@ -28,6 +29,7 @@ soup_socket_properties_new (GMainContext *async_context,
props->local_addr = local_addr ? g_object_ref (local_addr) : NULL;
props->tlsdb = tlsdb ? g_object_ref (tlsdb) : NULL;
+ props->tls_interaction = tls_interaction ? g_object_ref (tls_interaction) : NULL;
props->ssl_strict = ssl_strict;
props->io_timeout = io_timeout;
@@ -53,6 +55,7 @@ soup_socket_properties_unref (SoupSocketProperties *props)
g_clear_object (&props->proxy_resolver);
g_clear_object (&props->local_addr);
g_clear_object (&props->tlsdb);
+ g_clear_object (&props->tls_interaction);
g_slice_free (SoupSocketProperties, props);
}
diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c
index 61329a9..157a4cf 100644
--- a/libsoup/soup-socket.c
+++ b/libsoup/soup-socket.c
@@ -79,6 +79,7 @@ typedef struct {
GInputStream *istream;
GOutputStream *ostream;
GTlsCertificateFlags tls_errors;
+ GTlsInteraction *tls_interaction;
GProxyResolver *proxy_resolver;
guint non_blocking:1;
@@ -216,6 +217,7 @@ soup_socket_finalize (GObject *object)
g_clear_object (&priv->local_addr);
g_clear_object (&priv->remote_addr);
+ g_clear_object (&priv->tls_interaction);
g_clear_object (&priv->proxy_resolver);
g_clear_object (&priv->ssl_creds);
@@ -314,6 +316,7 @@ soup_socket_set_property (GObject *object, guint prop_id,
g_clear_object (&priv->proxy_resolver);
if (props->proxy_resolver)
priv->proxy_resolver = g_object_ref (props->proxy_resolver);
+
g_clear_object (&priv->local_addr);
if (props->local_addr)
priv->local_addr = g_object_ref (props->local_addr);
@@ -321,6 +324,9 @@ soup_socket_set_property (GObject *object, guint prop_id,
g_clear_object (&priv->ssl_creds);
if (props->tlsdb)
priv->ssl_creds = g_object_ref (props->tlsdb);
+ g_clear_object (&priv->tls_interaction);
+ if (props->tls_interaction)
+ priv->tls_interaction = g_object_ref (props->tls_interaction);
priv->ssl_strict = props->ssl_strict;
priv->timeout = props->io_timeout;
@@ -1309,6 +1315,14 @@ soup_socket_setup_ssl (SoupSocket *sock,
if (!conn)
return FALSE;
+ /* GLib < 2.41 mistakenly doesn't implement this property in the
+ * dummy TLS backend, so we don't include it in the g_initable_new()
+ * call above.
+ */
+ g_object_set (G_OBJECT (conn),
+ "interaction", priv->tls_interaction,
+ NULL);
+
g_object_unref (priv->conn);
priv->conn = G_IO_STREAM (conn);
diff --git a/tests/ssl-test.c b/tests/ssl-test.c
index 989c48d..8ff50a3 100644
--- a/tests/ssl-test.c
+++ b/tests/ssl-test.c
@@ -235,6 +235,172 @@ do_session_property_tests (void)
soup_test_session_abort_unref (session);
}
+/* GTlsInteraction subclass for do_interaction_test */
+typedef GTlsInteraction TestTlsInteraction;
+typedef GTlsInteractionClass TestTlsInteractionClass;
+
+GType test_tls_interaction_get_type (void);
+
+G_DEFINE_TYPE (TestTlsInteraction, test_tls_interaction, G_TYPE_TLS_INTERACTION);
+
+static void
+test_tls_interaction_init (TestTlsInteraction *interaction)
+{
+
+}
+
+static GTlsInteractionResult
+test_tls_interaction_request_certificate (GTlsInteraction *interaction,
+ GTlsConnection *connection,
+ GTlsCertificateRequestFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsCertificate *cert;
+ const char *ssl_cert_file, *ssl_key_file;
+ GError *my_error = NULL;
+
+ /* Yes, we use the same certificate for the client as for the server. Shrug */
+ ssl_cert_file = g_test_get_filename (G_TEST_DIST, "test-cert.pem", NULL);
+ ssl_key_file = g_test_get_filename (G_TEST_DIST, "test-key.pem", NULL);
+ cert = g_tls_certificate_new_from_files (ssl_cert_file,
+ ssl_key_file,
+ &my_error);
+ g_assert_no_error (my_error);
+
+ g_tls_connection_set_certificate (connection, cert);
+ g_object_unref (cert);
+
+ return G_TLS_INTERACTION_HANDLED;
+}
+
+static void
+test_tls_interaction_class_init (TestTlsInteractionClass *klass)
+{
+ GTlsInteractionClass *interaction_class = G_TLS_INTERACTION_CLASS (klass);
+
+ interaction_class->request_certificate = test_tls_interaction_request_certificate;
+}
+
+
+#define INTERACTION_TEST_HTTP_RESPONSE "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\nOK\r\n"
+
+static gboolean
+accept_client_certificate (GTlsConnection *server,
+ GTlsCertificate *client_cert,
+ GTlsCertificateFlags errors)
+{
+ return TRUE;
+}
+
+static void
+got_connection (GThreadedSocketService *service,
+ GSocketConnection *connection,
+ GObject *source_object)
+{
+ GIOStream *tls;
+ GTlsCertificate *server_cert;
+ GError *error = NULL;
+ const char *ssl_cert_file, *ssl_key_file;
+ GMainContext *thread_context;
+
+ thread_context = g_main_context_new ();
+ g_main_context_push_thread_default (thread_context);
+
+ ssl_cert_file = g_test_get_filename (G_TEST_DIST, "test-cert.pem", NULL);
+ ssl_key_file = g_test_get_filename (G_TEST_DIST, "test-key.pem", NULL);
+ server_cert = g_tls_certificate_new_from_files (ssl_cert_file,
+ ssl_key_file,
+ &error);
+ g_assert_no_error (error);
+
+ tls = g_tls_server_connection_new (G_IO_STREAM (connection),
+ server_cert, &error);
+ g_assert_no_error (error);
+ g_object_unref (server_cert);
+
+ g_object_set (G_OBJECT (tls),
+ "authentication-mode", G_TLS_AUTHENTICATION_REQUIRED,
+ NULL);
+ g_signal_connect (tls, "accept-certificate",
+ G_CALLBACK (accept_client_certificate), NULL);
+
+ if (g_tls_connection_handshake (G_TLS_CONNECTION (tls), NULL, &error)) {
+ g_output_stream_write_all (g_io_stream_get_output_stream (tls),
+ INTERACTION_TEST_HTTP_RESPONSE,
+ strlen (INTERACTION_TEST_HTTP_RESPONSE),
+ NULL, NULL, &error);
+ g_assert_no_error (error);
+ } else {
+ g_assert_error (error, G_TLS_ERROR, G_TLS_ERROR_CERTIFICATE_REQUIRED);
+ g_clear_error (&error);
+ }
+
+ g_io_stream_close (tls, NULL, &error);
+ g_assert_no_error (error);
+
+ g_object_unref (tls);
+
+ g_main_context_pop_thread_default (thread_context);
+ g_main_context_unref (thread_context);
+}
+
+static void
+do_tls_interaction_test (void)
+{
+ GSocketService *service;
+ GSocketAddress *address, *bound_address;
+ SoupSession *session;
+ SoupMessage *msg;
+ GTlsInteraction *interaction;
+ SoupURI *test_uri;
+ GError *error = NULL;
+
+ SOUP_TEST_SKIP_IF_NO_TLS;
+
+ service = g_threaded_socket_service_new (1);
+ address = g_inet_socket_address_new_from_string ("127.0.0.1", 0);
+ g_socket_listener_add_address (G_SOCKET_LISTENER (service), address,
+ G_SOCKET_TYPE_STREAM,
+ G_SOCKET_PROTOCOL_TCP,
+ NULL, &bound_address, &error);
+ g_assert_no_error (error);
+ g_object_unref (address);
+ g_signal_connect (service, "run", G_CALLBACK (got_connection), NULL);
+ g_socket_service_start (service);
+
+ test_uri = soup_uri_new ("https://127.0.0.1");
+ soup_uri_set_port (test_uri, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (bound_address)));
+ g_object_unref (bound_address);
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+
+ /* Without a GTlsInteraction */
+ msg = soup_message_new_from_uri ("GET", test_uri);
+ soup_session_send_message (session, msg);
+ soup_test_assert_message_status (msg, SOUP_STATUS_SSL_FAILED);
+ g_object_unref (msg);
+
+ interaction = g_object_new (test_tls_interaction_get_type (), NULL);
+ g_object_set (G_OBJECT (session),
+ SOUP_SESSION_TLS_INTERACTION, interaction,
+ NULL);
+ g_object_unref (interaction);
+
+ /* With a GTlsInteraction */
+ msg = soup_message_new_from_uri ("GET", test_uri);
+ soup_session_send_message (session, msg);
+ soup_test_assert_message_status (msg, SOUP_STATUS_OK);
+ g_assert_true (soup_message_get_https_status (msg, NULL, NULL));
+ g_object_unref (msg);
+
+ soup_uri_free (test_uri);
+ soup_test_session_abort_unref (session);
+
+ g_socket_service_stop (service);
+ g_object_unref (service);
+}
+
static void
server_handler (SoupServer *server,
SoupMessage *msg,
@@ -267,6 +433,7 @@ main (int argc, char **argv)
g_test_add_func ("/ssl/session-properties", do_session_property_tests);
g_test_add_func ("/ssl/message-properties/async", do_async_properties_tests);
g_test_add_func ("/ssl/message-properties/sync", do_sync_properties_tests);
+ g_test_add_func ("/ssl/tls-interaction", do_tls_interaction_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]