diff options
author | Carlos Garcia Campos <cgarcia@igalia.com> | 2021-06-15 12:58:44 +0200 |
---|---|---|
committer | Patrick Griffis <tingping@tingping.se> | 2021-06-15 16:48:45 +0000 |
commit | 0821e8967af443de1695ca02a9d08777390d0562 (patch) | |
tree | 531adbc1a1ee1451c0b178402ce27ea2f6c450c8 /libsoup/server | |
parent | c8b5ae435163e93980e30f4712ce1f6639c6f9ee (diff) | |
download | libsoup-0821e8967af443de1695ca02a9d08777390d0562.tar.gz |
server: add support for client side certificates
Diffstat (limited to 'libsoup/server')
-rw-r--r-- | libsoup/server/soup-server-message.c | 45 | ||||
-rw-r--r-- | libsoup/server/soup-server.c | 149 | ||||
-rw-r--r-- | libsoup/server/soup-server.h | 12 | ||||
-rw-r--r-- | libsoup/server/soup-socket.c | 68 |
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) { |