[libsoup/carlosgc/server-client-side-certs: 2/2] server: add support for client side certificates
- From: Carlos Garcia Campos <carlosgc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsoup/carlosgc/server-client-side-certs: 2/2] server: add support for client side certificates
- Date: Tue, 15 Jun 2021 14:16:12 +0000 (UTC)
commit b801f0447a3de91215eae989a056dae4d81f5a95
Author: Carlos Garcia Campos <cgarcia igalia com>
Date: Tue Jun 15 12:58:44 2021 +0200
server: add support for client side certificates
docs/reference/libsoup-3.0-sections.txt | 4 +
libsoup/server/soup-server-message.c | 45 ++++++++++
libsoup/server/soup-server.c | 149 ++++++++++++++++++++++++++++++--
libsoup/server/soup-server.h | 12 +++
libsoup/server/soup-socket.c | 68 ++++++++++++++-
tests/ssl-test.c | 32 +++----
6 files changed, 285 insertions(+), 25 deletions(-)
---
diff --git a/docs/reference/libsoup-3.0-sections.txt b/docs/reference/libsoup-3.0-sections.txt
index 5d695e69..15bb9b4b 100644
--- a/docs/reference/libsoup-3.0-sections.txt
+++ b/docs/reference/libsoup-3.0-sections.txt
@@ -227,6 +227,10 @@ SoupServer
soup_server_new
soup_server_set_tls_certificate
soup_server_get_tls_certificate
+soup_server_set_tls_database
+soup_server_get_tls_database
+soup_server_set_tls_auth_mode
+soup_server_get_tls_auth_mode
<SUBSECTION>
SoupServerListenOptions
soup_server_listen
diff --git a/libsoup/server/soup-server-message.c b/libsoup/server/soup-server-message.c
index 91acaa53..0aecd057 100644
--- a/libsoup/server/soup-server-message.c
+++ b/libsoup/server/soup-server-message.c
@@ -94,6 +94,8 @@ enum {
DISCONNECTED,
FINISHED,
+ ACCEPT_CERTIFICATE,
+
LAST_SIGNAL
};
@@ -313,6 +315,32 @@ soup_server_message_class_init (SoupServerMessageClass *klass)
NULL, NULL,
NULL,
G_TYPE_NONE, 0);
+
+ /**
+ * SoupServerMessage::accept-certificate:
+ * @msg: the message
+ * @tls_peer_certificate: the peer's #GTlsCertificate
+ * @tls_peer_errors: the tls errors of @tls_certificate
+ *
+ * Emitted during the @msg's connection TLS handshake
+ * after client TLS certificate has been received.
+ * You can return %TRUE to accept @tls_certificate despite
+ * @tls_errors.
+ *
+ * Returns: %TRUE to accept the TLS certificate and stop other
+ * handlers from being invoked, or %FALSE to propagate the
+ * event further.
+ */
+ signals[ACCEPT_CERTIFICATE] =
+ g_signal_new ("accept-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_CERTIFICATE,
+ G_TYPE_TLS_CERTIFICATE_FLAGS);
}
static void
@@ -321,6 +349,18 @@ socket_disconnected (SoupServerMessage *msg)
g_signal_emit (msg, signals[DISCONNECTED], 0);
}
+static gboolean
+socket_accept_certificate (SoupServerMessage *msg,
+ GTlsCertificate *tls_certificate,
+ GTlsCertificateFlags *tls_errors)
+{
+ gboolean accept = FALSE;
+
+ g_signal_emit (msg, signals[ACCEPT_CERTIFICATE], 0,
+ tls_certificate, tls_errors, &accept);
+ return accept;
+}
+
SoupServerMessage *
soup_server_message_new (SoupSocket *sock)
{
@@ -335,6 +375,9 @@ soup_server_message_new (SoupSocket *sock)
g_signal_connect_object (sock, "disconnected",
G_CALLBACK (socket_disconnected),
msg, G_CONNECT_SWAPPED);
+ g_signal_connect_object (sock, "accept-certificate",
+ G_CALLBACK (socket_accept_certificate),
+ msg, G_CONNECT_SWAPPED);
return msg;
}
@@ -922,6 +965,8 @@ soup_server_message_steal_connection (SoupServerMessage *msg)
g_object_unref);
}
+ g_signal_handlers_disconnect_by_data (msg, msg->sock);
+
socket_disconnected (msg);
g_object_unref (msg);
diff --git a/libsoup/server/soup-server.c b/libsoup/server/soup-server.c
index d2cce7c3..b317bf99 100644
--- a/libsoup/server/soup-server.c
+++ b/libsoup/server/soup-server.c
@@ -160,6 +160,8 @@ typedef struct {
GSList *clients;
GTlsCertificate *tls_cert;
+ GTlsDatabase *tls_database;
+ GTlsAuthenticationMode tls_auth_mode;
char *server_header;
@@ -183,6 +185,8 @@ enum {
PROP_0,
PROP_TLS_CERTIFICATE,
+ PROP_TLS_DATABASE,
+ PROP_TLS_AUTH_MODE,
PROP_RAW_PATHS,
PROP_SERVER_HEADER,
@@ -243,6 +247,7 @@ soup_server_finalize (GObject *object)
SoupServerPrivate *priv = soup_server_get_instance_private (server);
g_clear_object (&priv->tls_cert);
+ g_clear_object (&priv->tls_database);
g_free (priv->server_header);
@@ -269,6 +274,12 @@ soup_server_set_property (GObject *object, guint prop_id,
case PROP_TLS_CERTIFICATE:
soup_server_set_tls_certificate (server, g_value_get_object (value));
break;
+ case PROP_TLS_DATABASE:
+ soup_server_set_tls_database (server, g_value_get_object (value));
+ break;
+ case PROP_TLS_AUTH_MODE:
+ soup_server_set_tls_auth_mode (server, g_value_get_enum (value));
+ break;
case PROP_RAW_PATHS:
priv->raw_paths = g_value_get_boolean (value);
break;
@@ -304,6 +315,12 @@ soup_server_get_property (GObject *object, guint prop_id,
case PROP_TLS_CERTIFICATE:
g_value_set_object (value, priv->tls_cert);
break;
+ case PROP_TLS_DATABASE:
+ g_value_set_object (value, priv->tls_database);
+ break;
+ case PROP_TLS_AUTH_MODE:
+ g_value_set_enum (value, priv->tls_auth_mode);
+ break;
case PROP_RAW_PATHS:
g_value_set_boolean (value, priv->raw_paths);
break;
@@ -441,6 +458,35 @@ soup_server_class_init (SoupServerClass *server_class)
G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS);
+ /**
+ * SoupServer:tls-database:
+ *
+ * A #GTlsDatabase to use for validating SSL/TLS client certificates.
+ */
+ properties[PROP_TLS_DATABASE] =
+ g_param_spec_object ("tls-database",
+ "TLS database",
+ "GTlsDatabase to use for validating SSL/TLS client certificates",
+ G_TYPE_TLS_DATABASE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS);
+
+ /**
+ * SoupServer:tls-auth-mode:
+ *
+ * A #GTlsAuthenticationMode for SSL/TLS client authentication
+ */
+ properties[PROP_TLS_AUTH_MODE] =
+ g_param_spec_enum ("tls-auth-mode",
+ "TLS Authentication Mode",
+ "GTlsAuthenticationMode to use for SSL/TLS client authentication",
+ G_TYPE_TLS_AUTHENTICATION_MODE,
+ G_TLS_AUTHENTICATION_NONE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS);
+
properties[PROP_RAW_PATHS] =
g_param_spec_boolean ("raw-paths",
"Raw paths",
@@ -527,11 +573,11 @@ void
soup_server_set_tls_certificate (SoupServer *server,
GTlsCertificate *certificate)
{
- SoupServerPrivate *priv;
+ SoupServerPrivate *priv;
- g_return_if_fail (SOUP_IS_SERVER (server));
+ g_return_if_fail (SOUP_IS_SERVER (server));
- priv = soup_server_get_instance_private (server);
+ priv = soup_server_get_instance_private (server);
if (priv->tls_cert == certificate)
return;
@@ -559,6 +605,91 @@ soup_server_get_tls_certificate (SoupServer *server)
return priv->tls_cert;
}
+/**
+ * soup_server_set_tls_database:
+ * @server: a #SoupServer
+ * @tls_database: a #GTlsDatabase
+ *
+ * Sets @server's #GTlsDatabase to use for validating SSL/TLS client certificates
+ */
+void
+soup_server_set_tls_database (SoupServer *server,
+ GTlsDatabase *tls_database)
+{
+ SoupServerPrivate *priv;
+
+ g_return_if_fail (SOUP_IS_SERVER (server));
+
+ priv = soup_server_get_instance_private (server);
+ if (priv->tls_database == tls_database)
+ return;
+
+ g_clear_object (&priv->tls_database);
+ priv->tls_database = tls_database ? g_object_ref (tls_database) : NULL;
+ g_object_notify_by_pspec (G_OBJECT (server), properties[PROP_TLS_DATABASE]);
+}
+
+/**
+ * soup_server_get_tls_database:
+ * @server: a #SoupServer
+ *
+ * Gets the @server SSL/TLS database
+ *
+ * Returns: (transfer none) (nullable): a #GTlsDatabase or %NULL
+ */
+GTlsDatabase *
+soup_server_get_tls_database (SoupServer *server)
+{
+ SoupServerPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_SERVER (server), NULL);
+
+ priv = soup_server_get_instance_private (server);
+ return priv->tls_database;
+}
+
+/**
+ * soup_server_set_tls_auth_mode:
+ * @server: a #SoupServer
+ * @mode: a #GTlsAuthenticationMode
+ *
+ * Sets @server's #GTlsAuthenticationMode to use for SSL/TLS client authentication
+ */
+void
+soup_server_set_tls_auth_mode (SoupServer *server,
+ GTlsAuthenticationMode mode)
+{
+ SoupServerPrivate *priv;
+
+ g_return_if_fail (SOUP_IS_SERVER (server));
+
+ priv = soup_server_get_instance_private (server);
+ if (priv->tls_auth_mode == mode)
+ return;
+
+ priv->tls_auth_mode = mode;
+ g_object_notify_by_pspec (G_OBJECT (server), properties[PROP_TLS_AUTH_MODE]);
+}
+
+/**
+ * soup_server_get_tls_auth_mode:
+ * @server: a #SoupServer
+ *
+ * Gets the @server SSL/TLS client authentication mode
+ *
+ * Returns: a #GTlsAuthenticationMode
+ */
+GTlsAuthenticationMode
+soup_server_get_tls_auth_mode (SoupServer *server)
+{
+ SoupServerPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_SERVER (server), G_TLS_AUTHENTICATION_NONE);
+
+ priv = soup_server_get_instance_private (server);
+ return priv->tls_auth_mode;
+}
+
/**
* soup_server_is_https:
* @server: a #SoupServer
@@ -1071,9 +1202,15 @@ soup_server_listen_internal (SoupServer *server, SoupSocket *listener,
return FALSE;
}
- g_object_set (G_OBJECT (listener),
- "tls-certificate", priv->tls_cert,
- NULL);
+ g_object_bind_property (server, "tls-certificate",
+ listener, "tls-certificate",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property (server, "tls-database",
+ listener, "tls-database",
+ G_BINDING_SYNC_CREATE);
+ g_object_bind_property (server, "tls-auth-mode",
+ listener, "tls-auth-mode",
+ G_BINDING_SYNC_CREATE);
}
if (soup_socket_get_gsocket (listener) == NULL) {
diff --git a/libsoup/server/soup-server.h b/libsoup/server/soup-server.h
index 8f9a977a..d98ef512 100644
--- a/libsoup/server/soup-server.h
+++ b/libsoup/server/soup-server.h
@@ -47,6 +47,18 @@ void soup_server_set_tls_certificate (SoupServer *server
SOUP_AVAILABLE_IN_ALL
GTlsCertificate *soup_server_get_tls_certificate (SoupServer *server);
+SOUP_AVAILABLE_IN_ALL
+void soup_server_set_tls_database (SoupServer *server,
+ GTlsDatabase *tls_database);
+SOUP_AVAILABLE_IN_ALL
+GTlsDatabase *soup_server_get_tls_database (SoupServer *server);
+
+SOUP_AVAILABLE_IN_ALL
+void soup_server_set_tls_auth_mode (SoupServer *server,
+ GTlsAuthenticationMode mode);
+SOUP_AVAILABLE_IN_ALL
+GTlsAuthenticationMode soup_server_get_tls_auth_mode (SoupServer *server);
+
SOUP_AVAILABLE_IN_ALL
gboolean soup_server_is_https (SoupServer *server);
diff --git a/libsoup/server/soup-socket.c b/libsoup/server/soup-socket.c
index 208634cf..aa9815ee 100644
--- a/libsoup/server/soup-socket.c
+++ b/libsoup/server/soup-socket.c
@@ -31,6 +31,7 @@
enum {
DISCONNECTED,
NEW_CONNECTION,
+ ACCEPT_CERTIFICATE,
LAST_SIGNAL
};
@@ -46,6 +47,8 @@ enum {
PROP_REMOTE_CONNECTABLE,
PROP_IPV6_ONLY,
PROP_TLS_CERTIFICATE,
+ PROP_TLS_DATABASE,
+ PROP_TLS_AUTH_MODE,
LAST_PROPERTY
};
@@ -67,6 +70,8 @@ typedef struct {
guint ipv6_only:1;
guint ssl:1;
GTlsCertificate *tls_certificate;
+ GTlsDatabase *tls_database;
+ GTlsAuthenticationMode tls_auth_mode;
GMainContext *async_context;
GSource *watch_src;
@@ -162,6 +167,7 @@ soup_socket_finalize (GObject *object)
g_clear_object (&priv->remote_connectable);
g_clear_object (&priv->tls_certificate);
+ g_clear_object (&priv->tls_database);
if (priv->watch_src) {
g_source_destroy (priv->watch_src);
@@ -224,6 +230,12 @@ soup_socket_set_property (GObject *object, guint prop_id,
case PROP_TLS_CERTIFICATE:
priv->tls_certificate = g_value_dup_object (value);
break;
+ case PROP_TLS_DATABASE:
+ priv->tls_database = g_value_dup_object (value);
+ break;
+ case PROP_TLS_AUTH_MODE:
+ priv->tls_auth_mode = g_value_get_enum (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -253,6 +265,12 @@ soup_socket_get_property (GObject *object, guint prop_id,
case PROP_TLS_CERTIFICATE:
g_value_set_object (value, priv->tls_certificate);
break;
+ case PROP_TLS_DATABASE:
+ g_value_set_object (value, priv->tls_database);
+ break;
+ case PROP_TLS_AUTH_MODE:
+ g_value_set_enum (value, priv->tls_auth_mode);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -308,6 +326,17 @@ soup_socket_class_init (SoupSocketClass *socket_class)
G_TYPE_NONE, 1,
SOUP_TYPE_SOCKET);
+ signals[ACCEPT_CERTIFICATE] =
+ g_signal_new ("accept-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_CERTIFICATE,
+ G_TYPE_TLS_CERTIFICATE_FLAGS);
+
/* properties */
properties[PROP_GSOCKET] =
g_param_spec_object ("gsocket",
@@ -364,6 +393,23 @@ soup_socket_class_init (SoupSocketClass *socket_class)
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS);
+ properties[PROP_TLS_DATABASE] =
+ g_param_spec_object ("tls-database",
+ "TLS Database",
+ "The server TLS database",
+ G_TYPE_TLS_DATABASE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_TLS_AUTH_MODE] =
+ g_param_spec_enum ("tls-auth-mode",
+ "TLS Authentication Mode",
+ "The server TLS authentication mode",
+ G_TYPE_TLS_AUTHENTICATION_MODE,
+ G_TLS_AUTHENTICATION_NONE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
+
g_object_class_install_properties (object_class, LAST_PROPERTY, properties);
}
@@ -437,6 +483,18 @@ soup_socket_get_iostream (SoupSocket *sock)
return priv->iostream;
}
+static gboolean
+tls_connection_accept_certificate (SoupSocket *sock,
+ GTlsCertificate *tls_certificate,
+ GTlsCertificateFlags tls_errors)
+{
+ gboolean accept = FALSE;
+
+ g_signal_emit (sock, signals[ACCEPT_CERTIFICATE], 0,
+ tls_certificate, tls_errors, &accept);
+ return accept;
+}
+
static gboolean
soup_socket_setup_ssl (SoupSocket *sock)
{
@@ -453,7 +511,8 @@ soup_socket_setup_ssl (SoupSocket *sock)
NULL, NULL,
"base-io-stream", priv->conn,
"certificate", priv->tls_certificate,
- "use-system-certdb", FALSE,
+ "database", priv->tls_database,
+ "authentication-mode", priv->tls_auth_mode,
"require-close-notify", FALSE,
NULL);
if (!conn)
@@ -462,6 +521,10 @@ soup_socket_setup_ssl (SoupSocket *sock)
g_object_unref (priv->conn);
priv->conn = G_IO_STREAM (conn);
+ g_signal_connect_object (priv->conn, "accept-certificate",
+ G_CALLBACK (tls_connection_accept_certificate),
+ sock, G_CONNECT_SWAPPED);
+
g_clear_object (&priv->istream);
g_clear_object (&priv->ostream);
g_clear_object (&priv->iostream);
@@ -490,6 +553,9 @@ listen_watch (GObject *pollable, gpointer data)
new_priv->ssl = priv->ssl;
if (priv->tls_certificate)
new_priv->tls_certificate = g_object_ref (priv->tls_certificate);
+ if (priv->tls_database)
+ new_priv->tls_database = g_object_ref (priv->tls_database);
+ new_priv->tls_auth_mode = priv->tls_auth_mode;
finish_socket_setup (new);
if (new_priv->tls_certificate) {
diff --git a/tests/ssl-test.c b/tests/ssl-test.c
index 5b41246a..4f183a9a 100644
--- a/tests/ssl-test.c
+++ b/tests/ssl-test.c
@@ -127,7 +127,7 @@ test_tls_interaction_class_init (TestTlsInteractionClass *klass)
static gboolean
-accept_client_certificate (GTlsConnection *server,
+accept_client_certificate (SoupServerMessage *msg,
GTlsCertificate *client_cert,
GTlsCertificateFlags errors)
{
@@ -136,19 +136,11 @@ accept_client_certificate (GTlsConnection *server,
static void
server_request_started (SoupServer *server,
- SoupServerMessage *msg,
- GTlsDatabase *tls_db)
+ SoupServerMessage *msg)
{
- SoupSocket *sock;
- GIOStream *conn;
-
- sock = soup_server_message_get_soup_socket (msg);
- conn = soup_socket_get_connection (sock);
- g_tls_connection_set_database (G_TLS_CONNECTION (conn), tls_db);
- g_object_set (conn, "authentication-mode", G_TLS_AUTHENTICATION_REQUIRED, NULL);
- g_signal_connect (conn, "accept-certificate",
- G_CALLBACK (accept_client_certificate),
- NULL);
+ g_signal_connect (msg, "accept-certificate",
+ G_CALLBACK (accept_client_certificate),
+ NULL);
}
static void
@@ -167,10 +159,11 @@ do_tls_interaction_test (gconstpointer data)
session = soup_test_session_new (NULL);
tls_db = soup_session_get_tls_database (session);
+ g_object_set (server, "tls-database", tls_db, "tls-auth-mode", G_TLS_AUTHENTICATION_REQUIRED, NULL);
g_signal_connect (server, "request-started",
- G_CALLBACK (server_request_started),
- tls_db);
+ G_CALLBACK (server_request_started),
+ session);
/* Without a GTlsInteraction */
msg = soup_message_new_from_uri ("GET", uri);
@@ -203,7 +196,8 @@ do_tls_interaction_test (gconstpointer data)
g_bytes_unref (body);
g_object_unref (msg);
- g_signal_handlers_disconnect_by_data (server, tls_db);
+ g_object_set (server, "tls-database", NULL, "tls-auth-mode", G_TLS_AUTHENTICATION_NONE, NULL);
+ g_signal_handlers_disconnect_by_data (server, session);
soup_test_session_abort_unref (session);
g_object_unref (certificate);
@@ -313,10 +307,11 @@ do_tls_interaction_msg_test (gconstpointer data)
session = soup_test_session_new (NULL);
tls_db = soup_session_get_tls_database (session);
+ g_object_set (server, "tls-database", tls_db, "tls-auth-mode", G_TLS_AUTHENTICATION_REQUIRED, NULL);
g_signal_connect (server, "request-started",
G_CALLBACK (server_request_started),
- tls_db);
+ session);
/* Not handling request-certificate signal */
msg = soup_message_new_from_uri ("GET", uri);
@@ -502,7 +497,8 @@ do_tls_interaction_msg_test (gconstpointer data)
g_object_unref (pkcs11_certificate);
}
- g_signal_handlers_disconnect_by_data (server, tls_db);
+ g_object_set (server, "tls-database", NULL, "tls-auth-mode", G_TLS_AUTHENTICATION_NONE, NULL);
+ g_signal_handlers_disconnect_by_data (server, session);
soup_test_session_abort_unref (session);
g_object_unref (certificate);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]