[glib-networking/wip/danw/clicert: 1/2] gnutls: Add support for requesting certificate via GTlsInteraction
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking/wip/danw/clicert: 1/2] gnutls: Add support for requesting certificate via GTlsInteraction
- Date: Sat, 19 Oct 2013 15:51:29 +0000 (UTC)
commit 7e57450e0fa8b92c918a78508cac124450f30046
Author: Stef Walter <stefw gnome org>
Date: Thu Nov 29 09:52:53 2012 +0100
gnutls: Add support for requesting certificate via GTlsInteraction
When gnutls asks us for to provide a certificate, on the client we
ask our GTlsInteraction (if available) to provide that certificate.
This allows us to provide a certificate during the handshake, rather
than completing the handshake and forcing the caller to connect again
this time with a certificate.
https://bugzilla.gnome.org/show_bug.cgi?id=637257
tls/gnutls/gtlsclientconnection-gnutls.c | 10 +++-
tls/gnutls/gtlsconnection-gnutls.c | 31 +++++++++
tls/gnutls/gtlsconnection-gnutls.h | 2 +
tls/tests/Makefile.am | 3 +
tls/tests/connection.c | 107 +++++++++++++++++++++++++++++-
tls/tests/mock-interaction.c | 108 ++++++++++++++++++++++++++++--
tls/tests/mock-interaction.h | 11 +++-
tls/tests/pkcs11-slot.c | 2 +-
8 files changed, 265 insertions(+), 9 deletions(-)
---
diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c
index 07a3a00..c917ae3 100644
--- a/tls/gnutls/gtlsclientconnection-gnutls.c
+++ b/tls/gnutls/gtlsclientconnection-gnutls.c
@@ -238,6 +238,7 @@ g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t s
gnutls_retr2_st *st)
{
GTlsClientConnectionGnutls *gnutls = gnutls_transport_get_ptr (session);
+ GTlsConnectionGnutls *conn = G_TLS_CONNECTION_GNUTLS (gnutls);
GPtrArray *accepted_cas;
GByteArray *dn;
int i;
@@ -257,7 +258,14 @@ g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t s
gnutls->priv->accepted_cas = accepted_cas;
g_object_notify (G_OBJECT (gnutls), "accepted-cas");
- g_tls_connection_gnutls_get_certificate (G_TLS_CONNECTION_GNUTLS (gnutls), st);
+ g_tls_connection_gnutls_get_certificate (conn, st);
+
+ if (st->ncerts == 0)
+ {
+ if (g_tls_connection_gnutls_request_certificate (conn))
+ g_tls_connection_gnutls_get_certificate (conn, st);
+ }
+
return 0;
}
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 35bcaad..a5b4329 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -1711,3 +1711,34 @@ g_tls_connection_gnutls_initable_iface_init (GInitableIface *iface)
{
iface->init = g_tls_connection_gnutls_initable_init;
}
+
+gboolean
+g_tls_connection_gnutls_request_certificate (GTlsConnectionGnutls *self)
+{
+ GTlsInteractionResult res = G_TLS_INTERACTION_UNHANDLED;
+ GTlsInteraction *interaction;
+ GTlsConnection *conn;
+ GError *error = NULL;
+
+ g_return_val_if_fail (G_IS_TLS_CONNECTION_GNUTLS (self), FALSE);
+
+ conn = G_TLS_CONNECTION (self);
+
+ interaction = g_tls_connection_get_interaction (conn);
+ if (interaction)
+ {
+ res = g_tls_interaction_invoke_request_certificate (interaction, conn, 0,
+ self->priv->read_cancellable, &error);
+ }
+
+ if (error != NULL)
+ {
+ /* Propagate the error to the handshake */
+ if (self->priv->reading && !self->priv->handshake_error)
+ g_propagate_error (&self->priv->handshake_error, error);
+ else
+ g_error_free (error);
+ }
+
+ return res != G_TLS_INTERACTION_FAILED;
+}
diff --git a/tls/gnutls/gtlsconnection-gnutls.h b/tls/gnutls/gtlsconnection-gnutls.h
index 3aa8473..6a993f7 100644
--- a/tls/gnutls/gtlsconnection-gnutls.h
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -53,6 +53,8 @@ gnutls_session_t g_tls_connection_gnutls_get_session (GTlsCo
void g_tls_connection_gnutls_get_certificate (GTlsConnectionGnutls *gnutls,
gnutls_retr2_st *st);
+gboolean g_tls_connection_gnutls_request_certificate (GTlsConnectionGnutls *self);
+
gssize g_tls_connection_gnutls_read (GTlsConnectionGnutls *gnutls,
void *buffer,
gsize size,
diff --git a/tls/tests/Makefile.am b/tls/tests/Makefile.am
index df51f91..6287b9c 100644
--- a/tls/tests/Makefile.am
+++ b/tls/tests/Makefile.am
@@ -33,6 +33,9 @@ pkcs11_slot_SOURCES = pkcs11-slot.c \
mock-pkcs11.c mock-pkcs11.h \
mock-interaction.c mock-interaction.h
+connection_SOURCES = connection.c \
+ mock-interaction.c mock-interaction.h
+
endif
testfiles_data = \
diff --git a/tls/tests/connection.c b/tls/tests/connection.c
index 3c57c38..4f22fb4 100644
--- a/tls/tests/connection.c
+++ b/tls/tests/connection.c
@@ -19,6 +19,10 @@
* Author: Stef Walter <stefw collabora co uk>
*/
+#include "config.h"
+
+#include "mock-interaction.h"
+
#include <gio/gio.h>
#include <sys/types.h>
@@ -51,6 +55,7 @@ tls_test_file_path (const char *name)
#define TEST_DATA_LENGTH 24
typedef struct {
+ GMainContext *context;
GMainLoop *loop;
GSocketService *service;
GTlsDatabase *database;
@@ -76,7 +81,8 @@ setup_connection (TestConnection *test, gconstpointer data)
GInetAddress *inet;
guint16 port;
- test->loop = g_main_loop_new (NULL, FALSE);
+ test->context = g_main_context_default ();
+ test->loop = g_main_loop_new (test->context, FALSE);
test->auth_mode = G_TLS_AUTHENTICATION_NONE;
@@ -587,6 +593,101 @@ test_client_auth_failure (TestConnection *test,
}
static void
+test_client_auth_request_cert (TestConnection *test,
+ gconstpointer data)
+{
+ GIOStream *connection;
+ GError *error = NULL;
+ GTlsCertificate *cert;
+ GTlsCertificate *peer;
+ GTlsInteraction *interaction;
+ gboolean cas_changed;
+
+ test->database = g_tls_file_database_new (TEST_FILE ("ca-roots.pem"), &error);
+ g_assert_no_error (error);
+ g_assert (test->database);
+
+ connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_REQUIRED);
+ test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+ g_assert_no_error (error);
+ g_assert (test->client_connection);
+ g_object_unref (connection);
+
+ g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+ /* Have the interaction return a certificate */
+ cert = g_tls_certificate_new_from_file (TEST_FILE ("client-and-key.pem"), &error);
+ g_assert_no_error (error);
+ interaction = mock_interaction_new_static_certificate (cert);
+ g_tls_connection_set_interaction (G_TLS_CONNECTION (test->client_connection), interaction);
+ g_object_unref (interaction);
+
+ /* All validation in this test */
+ g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+ G_TLS_CERTIFICATE_VALIDATE_ALL);
+
+ cas_changed = FALSE;
+ g_signal_connect (test->client_connection, "notify::accepted-cas",
+ G_CALLBACK (on_notify_accepted_cas), &cas_changed);
+
+ read_test_data_async (test);
+ g_main_loop_run (test->loop);
+
+ g_assert_no_error (test->read_error);
+ g_assert_no_error (test->server_error);
+
+ peer = g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (test->server_connection));
+ g_assert (peer != NULL);
+ g_assert (g_tls_certificate_is_same (peer, cert));
+ g_assert (cas_changed == TRUE);
+
+ g_object_unref (cert);
+}
+
+static void
+test_client_auth_request_fail (TestConnection *test,
+ gconstpointer data)
+{
+ GIOStream *connection;
+ GError *error = NULL;
+ GTlsCertificate *cert;
+ GTlsInteraction *interaction;
+
+ test->database = g_tls_file_database_new (TEST_FILE ("ca-roots.pem"), &error);
+ g_assert_no_error (error);
+ g_assert (test->database);
+
+ connection = start_async_server_and_connect_to_it (test, G_TLS_AUTHENTICATION_REQUIRED);
+ test->client_connection = g_tls_client_connection_new (connection, test->identity, &error);
+ g_assert_no_error (error);
+ g_assert (test->client_connection);
+ g_object_unref (connection);
+
+ g_tls_connection_set_database (G_TLS_CONNECTION (test->client_connection), test->database);
+
+ /* Have the interaction return a certificate */
+ cert = g_tls_certificate_new_from_file (TEST_FILE ("client-and-key.pem"), &error);
+ g_assert_no_error (error);
+ interaction = mock_interaction_new_static_error (G_FILE_ERROR, G_FILE_ERROR_ACCES, "Request message");
+ g_tls_connection_set_interaction (G_TLS_CONNECTION (test->client_connection), interaction);
+ g_object_unref (interaction);
+
+ /* All validation in this test */
+ g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (test->client_connection),
+ G_TLS_CERTIFICATE_VALIDATE_ALL);
+
+ read_test_data_async (test);
+ g_main_loop_run (test->loop);
+
+ g_assert_error (test->read_error, G_FILE_ERROR, G_FILE_ERROR_ACCES);
+
+ g_io_stream_close (test->server_connection, NULL, NULL);
+ g_io_stream_close (test->client_connection, NULL, NULL);
+
+ g_object_unref (cert);
+}
+
+static void
test_connection_no_database (TestConnection *test,
gconstpointer data)
{
@@ -1104,6 +1205,10 @@ main (int argc,
setup_connection, test_client_auth_rehandshake, teardown_connection);
g_test_add ("/tls/connection/client-auth-failure", TestConnection, NULL,
setup_connection, test_client_auth_failure, teardown_connection);
+ g_test_add ("/tls/connection/client-auth-request-cert", TestConnection, NULL,
+ setup_connection, test_client_auth_request_cert, teardown_connection);
+ g_test_add ("/tls/connection/client-auth-request-fail", TestConnection, NULL,
+ setup_connection, test_client_auth_request_fail, teardown_connection);
g_test_add ("/tls/connection/no-database", TestConnection, NULL,
setup_connection, test_connection_no_database, teardown_connection);
g_test_add ("/tls/connection/failed", TestConnection, NULL,
diff --git a/tls/tests/mock-interaction.c b/tls/tests/mock-interaction.c
index ee518a5..9934d9b 100644
--- a/tls/tests/mock-interaction.c
+++ b/tls/tests/mock-interaction.c
@@ -40,7 +40,10 @@ mock_interaction_ask_password_async (GTlsInteraction *interaction,
task = g_task_new (interaction, cancellable, callback, user_data);
- g_tls_password_set_value (password, (const guchar *)self->static_password, -1);
+ if (self->static_error)
+ g_task_return_error (task, g_error_copy (self->static_error));
+ else
+ g_tls_password_set_value (password, (const guchar *)self->static_password, -1);
g_task_return_boolean (task, TRUE);
}
@@ -72,8 +75,77 @@ mock_interaction_ask_password (GTlsInteraction *interaction,
if (g_cancellable_set_error_if_cancelled (cancellable, error))
return G_TLS_INTERACTION_FAILED;
- g_tls_password_set_value (password, (const guchar *)self->static_password, -1);
- return G_TLS_INTERACTION_HANDLED;
+ if (self->static_error)
+ {
+ g_propagate_error (error, g_error_copy (self->static_error));
+ return G_TLS_INTERACTION_FAILED;
+ }
+ else
+ {
+ g_tls_password_set_value (password, (const guchar *)self->static_password, -1);
+ return G_TLS_INTERACTION_HANDLED;
+ }
+}
+
+static void
+mock_interaction_request_certificate_async (GTlsInteraction *interaction,
+ GTlsConnection *connection,
+ gint unused_flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MockInteraction *self = MOCK_INTERACTION (interaction);
+ GTask *task;
+
+ task = g_task_new (interaction, cancellable, callback, user_data);
+
+ if (self->static_error)
+ g_task_return_error (task, g_error_copy (self->static_error));
+ else
+ g_tls_connection_set_certificate (connection, self->static_certificate);
+ g_task_return_boolean (task, TRUE);
+}
+
+static GTlsInteractionResult
+mock_interaction_request_certificate_finish (GTlsInteraction *interaction,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (g_task_is_valid (result, interaction),
+ G_TLS_INTERACTION_UNHANDLED);
+
+ if (g_task_had_error (G_TASK (result)))
+ {
+ g_task_propagate_boolean (G_TASK (result), error);
+ return G_TLS_INTERACTION_FAILED;
+ }
+ else
+ return G_TLS_INTERACTION_HANDLED;
+}
+
+static GTlsInteractionResult
+mock_interaction_request_certificate (GTlsInteraction *interaction,
+ GTlsConnection *connection,
+ gint unused_flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ MockInteraction *self = MOCK_INTERACTION (interaction);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return G_TLS_INTERACTION_FAILED;
+
+ if (self->static_error)
+ {
+ g_propagate_error (error, g_error_copy (self->static_error));
+ return G_TLS_INTERACTION_FAILED;
+ }
+ else
+ {
+ g_tls_connection_set_certificate (connection, self->static_certificate);
+ return G_TLS_INTERACTION_HANDLED;
+ }
}
static void
@@ -103,11 +175,13 @@ mock_interaction_class_init (MockInteractionClass *klass)
interaction_class->ask_password = mock_interaction_ask_password;
interaction_class->ask_password_async = mock_interaction_ask_password_async;
interaction_class->ask_password_finish = mock_interaction_ask_password_finish;
-
+ interaction_class->request_certificate = mock_interaction_request_certificate;
+ interaction_class->request_certificate_async = mock_interaction_request_certificate_async;
+ interaction_class->request_certificate_finish = mock_interaction_request_certificate_finish;
}
GTlsInteraction *
-mock_interaction_new_static (const gchar *password)
+mock_interaction_new_static_password (const gchar *password)
{
MockInteraction *self;
@@ -116,3 +190,27 @@ mock_interaction_new_static (const gchar *password)
self->static_password = g_strdup (password);
return G_TLS_INTERACTION (self);
}
+
+GTlsInteraction *
+mock_interaction_new_static_certificate (GTlsCertificate *cert)
+{
+ MockInteraction *self;
+
+ self = g_object_new (MOCK_TYPE_INTERACTION, NULL);
+
+ self->static_certificate = cert ? g_object_ref (cert) : NULL;
+ return G_TLS_INTERACTION (self);
+}
+
+GTlsInteraction *
+mock_interaction_new_static_error (GQuark domain,
+ gint code,
+ const gchar *message)
+{
+ MockInteraction *self;
+
+ self = g_object_new (MOCK_TYPE_INTERACTION, NULL);
+
+ self->static_error = g_error_new (domain, code, "%s", message);
+ return G_TLS_INTERACTION (self);
+}
diff --git a/tls/tests/mock-interaction.h b/tls/tests/mock-interaction.h
index 90668c7..f357d8a 100644
--- a/tls/tests/mock-interaction.h
+++ b/tls/tests/mock-interaction.h
@@ -41,6 +41,8 @@ struct _MockInteraction
{
GTlsInteraction parent_instance;
gchar *static_password;
+ GTlsCertificate *static_certificate;
+ GError *static_error;
};
struct _MockInteractionClass
@@ -50,7 +52,14 @@ struct _MockInteractionClass
GType mock_interaction_get_type (void);
-GTlsInteraction *mock_interaction_new_static (const gchar *password);
+
+GTlsInteraction *mock_interaction_new_static_password (const gchar *password);
+
+GTlsInteraction *mock_interaction_new_static_certificate (GTlsCertificate *cert);
+
+GTlsInteraction *mock_interaction_new_static_error (GQuark domain,
+ gint code,
+ const gchar *message);
G_END_DECLS
diff --git a/tls/tests/pkcs11-slot.c b/tls/tests/pkcs11-slot.c
index 0d80044..6165bf3 100644
--- a/tls/tests/pkcs11-slot.c
+++ b/tls/tests/pkcs11-slot.c
@@ -463,7 +463,7 @@ test_enumerate_private (TestSlot *test,
/* This time we log in, and should have a match */
results = g_ptr_array_new_with_free_func ((GDestroyNotify)g_pkcs11_array_unref);
- interaction = mock_interaction_new_static (MOCK_SLOT_ONE_PIN);
+ interaction = mock_interaction_new_static_password (MOCK_SLOT_ONE_PIN);
state = g_pkcs11_slot_enumerate (test->slot, interaction,
match->attrs, match->count, TRUE,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]