[libsoup/carlosgc/client-side-certs2: 2/2] message: add API to handle password protected client certificates




commit d2d3a1306d063869a0e1d3d6f9ee11285210bb93
Author: Carlos Garcia Campos <cgarcia igalia com>
Date:   Tue Apr 27 13:22:27 2021 +0200

    message: add API to handle password protected client certificates
    
    Implement GTlsInteraction::ask_password to expose another signal in
    SoupMessage to ask the user for ther password.

 libsoup/auth/soup-tls-interaction.c | 33 ++++++++++++++
 libsoup/soup-connection.c           | 48 ++++++++++++++++++++
 libsoup/soup-connection.h           | 27 +++++++-----
 libsoup/soup-message.c              | 88 +++++++++++++++++++++++++++++++++++++
 libsoup/soup-message.h              | 13 ++++--
 5 files changed, 195 insertions(+), 14 deletions(-)
---
diff --git a/libsoup/auth/soup-tls-interaction.c b/libsoup/auth/soup-tls-interaction.c
index 0ade927e..d3bfa981 100644
--- a/libsoup/auth/soup-tls-interaction.c
+++ b/libsoup/auth/soup-tls-interaction.c
@@ -51,6 +51,37 @@ soup_tls_interaction_request_certificate_finish (GTlsInteraction *tls_interactio
         return task_result != -1 ? task_result : G_TLS_INTERACTION_FAILED;
 }
 
+static void
+soup_tls_interaction_ask_password_async (GTlsInteraction    *tls_interaction,
+                                         GTlsPassword       *password,
+                                         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) {
+                g_task_set_task_data (task, g_object_ref (password), g_object_unref);
+                soup_connection_request_tls_certificate_password (priv->conn, password, task);
+        } else {
+                g_task_return_int (task, G_TLS_INTERACTION_FAILED);
+        }
+        g_object_unref (task);
+}
+
+static GTlsInteractionResult
+soup_tls_interaction_ask_password_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)
 {
@@ -79,6 +110,8 @@ soup_tls_interaction_class_init (SoupTlsInteractionClass *klass)
 
         interaction_class->request_certificate_async = soup_tls_interaction_request_certificate_async;
         interaction_class->request_certificate_finish = soup_tls_interaction_request_certificate_finish;
+        interaction_class->ask_password_async = soup_tls_interaction_ask_password_async;
+        interaction_class->ask_password_finish = soup_tls_interaction_ask_password_finish;
 }
 
 GTlsInteraction *
diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c
index 5d1f3406..b93159bf 100644
--- a/libsoup/soup-connection.c
+++ b/libsoup/soup-connection.c
@@ -50,6 +50,7 @@ enum {
        EVENT,
        ACCEPT_CERTIFICATE,
         REQUEST_CERTIFICATE,
+        REQUEST_CERTIFICATE_PASSWORD,
        DISCONNECTED,
        LAST_SIGNAL
 };
@@ -225,6 +226,16 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
                               G_TYPE_BOOLEAN, 2,
                               G_TYPE_TLS_CLIENT_CONNECTION,
                               G_TYPE_TASK);
+        signals[REQUEST_CERTIFICATE_PASSWORD] =
+                g_signal_new ("request-certificate-password",
+                              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_PASSWORD,
+                              G_TYPE_TASK);
        signals[DISCONNECTED] =
                g_signal_new ("disconnected",
                              G_OBJECT_CLASS_TYPE (object_class),
@@ -1174,6 +1185,43 @@ soup_connection_complete_tls_certificate_request (SoupConnection  *conn,
         g_object_unref (task);
 }
 
+void
+soup_connection_request_tls_certificate_password (SoupConnection *conn,
+                                                  GTlsPassword   *password,
+                                                  GTask          *task)
+{
+        SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
+        gboolean handled = FALSE;
+
+        if (!G_IS_TLS_CONNECTION (priv->connection)) {
+                g_task_return_int (task, G_TLS_INTERACTION_FAILED);
+                return;
+        }
+
+        g_signal_emit (conn, signals[REQUEST_CERTIFICATE_PASSWORD], 0, password, task, &handled);
+        if (!handled)
+                g_task_return_int (task, G_TLS_INTERACTION_FAILED);
+}
+
+void
+soup_connection_complete_tls_certificate_password_request (SoupConnection *conn,
+                                                           const guchar   *password,
+                                                           gssize          length,
+                                                           GTask          *task)
+{
+        SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn);
+
+        if (G_IS_TLS_CONNECTION (priv->connection)) {
+                GTlsPassword *tls_password = g_task_get_task_data (task);
+
+                g_tls_password_set_value (tls_password, password, length);
+                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 881a546c..31fa02f8 100644
--- a/libsoup/soup-connection.h
+++ b/libsoup/soup-connection.h
@@ -70,16 +70,23 @@ SoupClientMessageIOData *soup_connection_setup_message_io    (SoupConnection *co
 void                     soup_connection_message_io_finished (SoupConnection *conn,
                                                               SoupMessage    *msg);
 
-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);
+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);
+void                 soup_connection_request_tls_certificate_password          (SoupConnection  *conn,
+                                                                                GTlsPassword    *password,
+                                                                                GTask           *task);
+void                 soup_connection_complete_tls_certificate_password_request (SoupConnection  *conn,
+                                                                                const guchar    *password,
+                                                                                gssize           length,
+                                                                                GTask           *task);
 
 guint64              soup_connection_get_id                     (SoupConnection *conn);
 
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index c2468f3a..458b7f7f 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -91,6 +91,7 @@ typedef struct {
 
         GTlsCertificate *tls_client_certificate;
         GTask *pending_tls_cert_request;
+        GTask *pending_tls_cert_pass_request;
 
        SoupMessagePriority priority;
 
@@ -121,6 +122,7 @@ enum {
        NETWORK_EVENT,
        ACCEPT_CERTIFICATE,
         REQUEST_CERTIFICATE,
+        REQUEST_CERTIFICATE_PASSWORD,
        HSTS_ENFORCED,
 
        LAST_SIGNAL
@@ -173,6 +175,11 @@ soup_message_finalize (GObject *object)
                 g_object_unref (priv->pending_tls_cert_request);
         }
 
+        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);
+        }
+
        soup_message_set_connection (msg, NULL);
 
        g_clear_pointer (&priv->uri, g_uri_unref);
@@ -624,6 +631,34 @@ soup_message_class_init (SoupMessageClass *message_class)
                               G_TYPE_BOOLEAN, 1,
                               G_TYPE_TLS_CLIENT_CONNECTION);
 
+        /**
+         * SoupMessage::request-certificate-password:
+         * @msg: the message
+         * @tls_password: the #GTlsPassword
+         *
+         * Emitted during the @msg's connection TLS handshake when
+         * @tls_connection requests a certificate password from the client.
+         * You can set the certificate password by calling
+         * soup_message_set_tls_client_certificate_password() and returning %TRUE.
+         * It's possible to handle the request asynchornously by returning
+         * %TRUE and call soup_message_set_tls_client_certificate_password() later
+         * once the certificate password is available.
+         * Note that this signal is not emitted if #SoupSession::tls-interaction
+         * was set.
+         *
+         * Returns: %TRUE to handle the request, or %FALSE to make the connection
+         *     fail with %G_TLS_ERROR_CERTIFICATE_REQUIRED.
+         */
+        signals[REQUEST_CERTIFICATE_PASSWORD] =
+                g_signal_new ("request-certificate-password",
+                              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_PASSWORD);
+
        /**
         * SoupMessage::hsts-enforced:
         * @msg: the message
@@ -1424,6 +1459,23 @@ re_emit_request_certificate (SoupMessage          *msg,
         return handled;
 }
 
+static gboolean
+re_emit_request_certificate_password (SoupMessage  *msg,
+                                      GTlsPassword *password,
+                                      GTask        *task)
+{
+        SoupMessagePrivate *priv = soup_message_get_instance_private (msg);
+        gboolean handled = FALSE;
+
+        priv->pending_tls_cert_pass_request = g_object_ref (task);
+
+        g_signal_emit (msg, signals[REQUEST_CERTIFICATE_PASSWORD], 0, password, &handled);
+        if (!handled)
+                g_clear_object (&priv->pending_tls_cert_pass_request);
+
+        return handled;
+}
+
 static void
 re_emit_tls_certificate_changed (SoupMessage    *msg,
                                 GParamSpec     *pspec,
@@ -1482,6 +1534,9 @@ soup_message_set_connection (SoupMessage    *msg,
         g_signal_connect_object (priv->connection, "request-certificate",
                                  G_CALLBACK (re_emit_request_certificate),
                                  msg, G_CONNECT_SWAPPED);
+        g_signal_connect_object (priv->connection, "request-certificate-password",
+                                 G_CALLBACK (re_emit_request_certificate_password),
+                                 msg, G_CONNECT_SWAPPED);
        g_signal_connect_object (priv->connection, "notify::tls-certificate",
                                 G_CALLBACK (re_emit_tls_certificate_changed),
                                 msg, G_CONNECT_SWAPPED);
@@ -2174,6 +2229,39 @@ soup_message_set_tls_client_certificate (SoupMessage     *msg,
         priv->tls_client_certificate = g_object_ref (certificate);
 }
 
+/**
+ * soup_message_set_tls_client_certificate_password:
+ * @msg: a #SoupMessage
+ * @password: the password to set
+ * @length: the @password's length or -1
+ *
+ * Sets the certificate @password to be used by @msg's connection when a
+ * client certificate password is requested during the TLS handshake.
+ * You must call this as a response to #SoupMessage::request-certificate-password
+ * signal.
+ */
+void
+soup_message_set_tls_client_certificate_password (SoupMessage  *msg,
+                                                  const guchar *password,
+                                                  gssize        length)
+{
+        SoupMessagePrivate *priv;
+
+        g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+        priv = soup_message_get_instance_private (msg);
+        if (!priv->pending_tls_cert_pass_request) {
+                g_warning ("soup_message_set_tls_client_certificate_password should only be called as a 
response to SoupMessage::request-certificate-password signal");
+                return;
+        }
+
+        g_assert (SOUP_IS_CONNECTION (priv->connection));
+        soup_connection_complete_tls_certificate_password_request (priv->connection,
+                                                                   password,
+                                                                   length,
+                                                                   g_steal_pointer 
(&priv->pending_tls_cert_pass_request));
+}
+
 /**
  * SoupMessagePriority:
  * @SOUP_MESSAGE_PRIORITY_VERY_LOW: The lowest priority, the messages
diff --git a/libsoup/soup-message.h b/libsoup/soup-message.h
index 5e82145c..28ed2b2f 100644
--- a/libsoup/soup-message.h
+++ b/libsoup/soup-message.h
@@ -101,14 +101,19 @@ gboolean         soup_message_query_flags         (SoupMessage           *msg,
                                                    SoupMessageFlags       flags);
 
 SOUP_AVAILABLE_IN_ALL
-GTlsCertificate     *soup_message_get_tls_peer_certificate        (SoupMessage *msg);
+GTlsCertificate     *soup_message_get_tls_peer_certificate            (SoupMessage     *msg);
 
 SOUP_AVAILABLE_IN_ALL
-GTlsCertificateFlags soup_message_get_tls_peer_certificate_errors (SoupMessage *msg);
+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);
+void                 soup_message_set_tls_client_certificate          (SoupMessage     *msg,
+                                                                       GTlsCertificate *certificate);
+
+SOUP_AVAILABLE_IN_ALL
+void                 soup_message_set_tls_client_certificate_password (SoupMessage     *msg,
+                                                                       const guchar    *password,
+                                                                       gssize           length);
 
 
 /* Specialized signal handlers */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]