summaryrefslogtreecommitdiff
path: root/libsoup/server
diff options
context:
space:
mode:
authorCarlos Garcia Campos <cgarcia@igalia.com>2021-06-15 12:58:44 +0200
committerPatrick Griffis <tingping@tingping.se>2021-06-15 16:48:45 +0000
commit0821e8967af443de1695ca02a9d08777390d0562 (patch)
tree531adbc1a1ee1451c0b178402ce27ea2f6c450c8 /libsoup/server
parentc8b5ae435163e93980e30f4712ce1f6639c6f9ee (diff)
downloadlibsoup-0821e8967af443de1695ca02a9d08777390d0562.tar.gz
server: add support for client side certificates
Diffstat (limited to 'libsoup/server')
-rw-r--r--libsoup/server/soup-server-message.c45
-rw-r--r--libsoup/server/soup-server.c149
-rw-r--r--libsoup/server/soup-server.h12
-rw-r--r--libsoup/server/soup-socket.c68
4 files changed, 267 insertions, 7 deletions
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;
@@ -560,6 +606,91 @@ soup_server_get_tls_certificate (SoupServer *server)
}
/**
+ * 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
@@ -48,6 +48,18 @@ 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);
SOUP_AVAILABLE_IN_ALL
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);
}
@@ -438,6 +484,18 @@ soup_socket_get_iostream (SoupSocket *sock)
}
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)
{
SoupSocketPrivate *priv = soup_socket_get_instance_private (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) {