[libsoup] Add tls-interaction property to SoupSession



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]