Commit 0821e896 authored by Carlos Garcia Campos's avatar Carlos Garcia Campos Committed by Patrick Griffis
Browse files

server: add support for client side certificates

parent c8b5ae43
Pipeline #290033 passed with stages
in 5 minutes and 16 seconds
......@@ -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
......
......@@ -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);
......
......@@ -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) {
......
......@@ -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);
......
......@@ -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) {
......
......@@ -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);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment