From 6545e7558c458b83806d80f781c31a86344911b2 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Mon, 27 Aug 2018 23:45:44 +1000 Subject: [PATCH 1/3] gversionmacros: add version macros for GLib 2.60 --- glib/gversionmacros.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/glib/gversionmacros.h b/glib/gversionmacros.h index 9d2995828..0ba8f635b 100644 --- a/glib/gversionmacros.h +++ b/glib/gversionmacros.h @@ -205,6 +205,16 @@ */ #define GLIB_VERSION_2_58 (G_ENCODE_VERSION (2, 58)) +/** + * GLIB_VERSION_2_60: + * + * A macro that evaluates to the 2.60 version of GLib, in a format + * that can be used by the C pre-processor. + * + * Since: 2.60 + */ +#define GLIB_VERSION_2_60 (G_ENCODE_VERSION (2, 60)) + /* evaluates to the current stable version; for development cycles, * this means the next stable target */ @@ -534,4 +544,18 @@ # define GLIB_AVAILABLE_IN_2_58 _GLIB_EXTERN #endif +#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_60 +# define GLIB_DEPRECATED_IN_2_60 GLIB_DEPRECATED +# define GLIB_DEPRECATED_IN_2_60_FOR(f) GLIB_DEPRECATED_FOR(f) +#else +# define GLIB_DEPRECATED_IN_2_60 _GLIB_EXTERN +# define GLIB_DEPRECATED_IN_2_60_FOR(f) _GLIB_EXTERN +#endif + +#if GLIB_VERSION_MAX_ALLOWED < GLIB_VERSION_2_60 +# define GLIB_AVAILABLE_IN_2_60 GLIB_UNAVAILABLE(2, 60) +#else +# define GLIB_AVAILABLE_IN_2_60 _GLIB_EXTERN +#endif + #endif /* __G_VERSION_MACROS_H__ */ From b84951eb6ff742c22dd4cf3f509bde4c22c5d067 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Thu, 16 Aug 2018 23:25:29 +1000 Subject: [PATCH 2/3] gtlsbackend: add support for setting the default TLS database There are many cases where a default TLS database is not able to be defined within the constraints of a system. For example glib-networking (or glib-openssl) cannot retrieve the default certificate store on iOS or Android and need to be initialized from a cert file of certificates bundled with the application. Previously GStreamer was relying on a custom patch to glib-networking to populate the default database from the file pointed to by the CA_CERTIFICATES environment variable however the mechanism that enabled this was recently remove from glib-networking. Adding a more generic g_tls_backend_set_default_database() API allows application developers to override the default database using their own certificates as well as allowing equivalent functionality on Android/iOS (or others) as on the default database handling Linux. Fixes https://gitlab.gnome.org/GNOME/glib-networking/issues/35 --- docs/reference/gio/gio-sections.txt | 1 + gio/gtlsbackend.c | 43 +++++++- gio/gtlsbackend.h | 3 + gio/tests/.gitignore | 1 + gio/tests/Makefile.am | 2 + gio/tests/gtesttlsbackend.c | 146 ++++++++++++++++++++++++++-- gio/tests/meson.build | 1 + gio/tests/tls-database.c | 75 ++++++++++++++ 8 files changed, 265 insertions(+), 7 deletions(-) create mode 100644 gio/tests/tls-database.c diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index 0eb560716..3b7cc28e7 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -3634,6 +3634,7 @@ g_tls_backend_get_default g_tls_backend_supports_tls g_tls_backend_supports_dtls g_tls_backend_get_default_database +g_tls_backend_set_default_database g_tls_backend_get_certificate_type g_tls_backend_get_client_connection_type g_tls_backend_get_server_connection_type diff --git a/gio/gtlsbackend.c b/gio/gtlsbackend.c index 80af6ad49..d67e49012 100644 --- a/gio/gtlsbackend.c +++ b/gio/gtlsbackend.c @@ -21,6 +21,7 @@ #include "glib.h" #include "gtlsbackend.h" +#include "gtlsdatabase.h" #include "gdummytlsbackend.h" #include "gioenumtypes.h" #include "giomodule-priv.h" @@ -84,6 +85,9 @@ G_DEFINE_INTERFACE (GTlsBackend, g_tls_backend, G_TYPE_OBJECT) +static GTlsDatabase *default_database; +G_LOCK_DEFINE_STATIC (default_database_lock); + static void g_tls_backend_default_init (GTlsBackendInterface *iface) { @@ -161,13 +165,50 @@ g_tls_backend_supports_dtls (GTlsBackend *backend) GTlsDatabase * g_tls_backend_get_default_database (GTlsBackend *backend) { + GTlsDatabase *db; + g_return_val_if_fail (G_IS_TLS_BACKEND (backend), NULL); /* This method was added later, so accept the (remote) possibility it can be NULL */ if (!G_TLS_BACKEND_GET_INTERFACE (backend)->get_default_database) return NULL; - return G_TLS_BACKEND_GET_INTERFACE (backend)->get_default_database (backend); + G_LOCK (default_database_lock); + + if (!default_database) + default_database = G_TLS_BACKEND_GET_INTERFACE (backend)->get_default_database (backend); + db = default_database ? g_object_ref (default_database) : NULL; + G_UNLOCK (default_database_lock); + + return db; +} + +/** + * g_tls_backend_set_default_database: + * @backend: the #GTlsBackend + * @database: (nullable): the #GTlsDatabase + * + * Set the default #GTlsDatabase used to verify TLS connections + * + * Any subsequent call to g_tls_backend_get_default_database() will return + * the database set in this call. Existing databases and connections are not + * modified. + * + * Setting a %NULL default database will reset to using the system default + * database as if g_tls_backend_set_default_database() had never been called. + * + * Since: 2.60 + */ +void +g_tls_backend_set_default_database (GTlsBackend *backend, + GTlsDatabase *database) +{ + g_return_if_fail (G_IS_TLS_BACKEND (backend)); + g_return_if_fail (database == NULL || G_IS_TLS_DATABASE (database)); + + G_LOCK (default_database_lock); + g_set_object (&default_database, database); + G_UNLOCK (default_database_lock); } /** diff --git a/gio/gtlsbackend.h b/gio/gtlsbackend.h index a75036fec..f19ab7e3c 100644 --- a/gio/gtlsbackend.h +++ b/gio/gtlsbackend.h @@ -85,6 +85,9 @@ GTlsBackend * g_tls_backend_get_default (void); GLIB_AVAILABLE_IN_ALL GTlsDatabase * g_tls_backend_get_default_database (GTlsBackend *backend); +GLIB_AVAILABLE_IN_2_60 +void g_tls_backend_set_default_database (GTlsBackend *backend, + GTlsDatabase *database); GLIB_AVAILABLE_IN_ALL gboolean g_tls_backend_supports_tls (GTlsBackend *backend); diff --git a/gio/tests/.gitignore b/gio/tests/.gitignore index a6942d241..0b772ad93 100644 --- a/gio/tests/.gitignore +++ b/gio/tests/.gitignore @@ -135,6 +135,7 @@ test_resources2.h testfilemonitor thumbnail-verification tls-certificate +tls-database tls-interaction unix-fd unix-streams diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am index 1f0bed7b6..2df156e6b 100644 --- a/gio/tests/Makefile.am +++ b/gio/tests/Makefile.am @@ -65,6 +65,7 @@ test_programs = \ socket-service \ srvtarget \ task \ + tls-database \ tls-interaction \ vfs \ volumemonitor \ @@ -522,6 +523,7 @@ endif # OS_UNIX endif # HAVE_DBUS_DAEMON tls_interaction_SOURCES = tls-interaction.c gtesttlsbackend.c gtesttlsbackend.h +tls_database_SOURCES = tls-database.c gtesttlsbackend.c gtesttlsbackend.h # ----------------------------------------------------------------------------- diff --git a/gio/tests/gtesttlsbackend.c b/gio/tests/gtesttlsbackend.c index 83dad364d..157a4a3f3 100644 --- a/gio/tests/gtesttlsbackend.c +++ b/gio/tests/gtesttlsbackend.c @@ -22,6 +22,8 @@ static GType _g_test_tls_certificate_get_type (void); static GType _g_test_tls_connection_get_type (void); +static GTlsDatabase * _g_test_tls_backend_get_default_database (GTlsBackend * backend); +static GType _g_test_tls_database_get_type (void); struct _GTestTlsBackend { GObject parent_instance; @@ -57,6 +59,26 @@ g_test_tls_backend_iface_init (GTlsBackendInterface *iface) iface->get_certificate_type = _g_test_tls_certificate_get_type; iface->get_client_connection_type = _g_test_tls_connection_get_type; iface->get_server_connection_type = _g_test_tls_connection_get_type; + iface->get_default_database = _g_test_tls_backend_get_default_database; + iface->get_file_database_type = _g_test_tls_database_get_type; +} + +static GTlsDatabase * +_g_test_tls_backend_get_default_database (GTlsBackend * backend) +{ + static GTlsDatabase *default_db; + GError *error = NULL; + + if (!default_db) + { + default_db = g_initable_new (_g_test_tls_database_get_type (), + NULL, + &error, + NULL); + g_assert_no_error (error); + } + + return default_db; } /* Test certificate type */ @@ -77,9 +99,7 @@ struct _GTestTlsCertificateClass { enum { - PROP_CERTIFICATE_0, - - PROP_CERT_CERTIFICATE, + PROP_CERT_CERTIFICATE = 1, PROP_CERT_CERTIFICATE_PEM, PROP_CERT_PRIVATE_KEY, PROP_CERT_PRIVATE_KEY_PEM, @@ -164,6 +184,8 @@ g_test_tls_certificate_finalize (GObject *object) g_free (cert->cert_pem); g_free (cert->key_pem); g_clear_object (&cert->issuer); + + G_OBJECT_CLASS (g_test_tls_certificate_parent_class)->finalize (object); } static void @@ -222,9 +244,7 @@ struct _GTestTlsConnectionClass { enum { - PROP_CONNECTION_0, - - PROP_CONN_BASE_IO_STREAM, + PROP_CONN_BASE_IO_STREAM = 1, PROP_CONN_USE_SYSTEM_CERTDB, PROP_CONN_REQUIRE_CLOSE_NOTIFY, PROP_CONN_REHANDSHAKE_MODE, @@ -327,3 +347,117 @@ g_test_tls_connection_get_private_key_pem (GTlsCertificate *cert) { return ((GTestTlsCertificate *)cert)->key_pem; } + +/* Test database type */ + +typedef struct _GTestTlsDatabase GTestTlsDatabase; +typedef struct _GTestTlsDatabaseClass GTestTlsDatabaseClass; + +struct _GTestTlsDatabase { + GTlsDatabase parent_instance; + gchar *anchors; +}; + +struct _GTestTlsDatabaseClass { + GTlsDatabaseClass parent_class; +}; + +enum +{ + PROP_DATABASE_ANCHORS = 1, +}; + +static void g_test_tls_database_initable_iface_init (GInitableIface *iface); +static void g_test_tls_file_database_file_database_interface_init (GInitableIface *iface); + +#define g_test_tls_database_get_type _g_test_tls_database_get_type +G_DEFINE_TYPE_WITH_CODE (GTestTlsDatabase, g_test_tls_database, G_TYPE_TLS_DATABASE, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + g_test_tls_database_initable_iface_init); + G_IMPLEMENT_INTERFACE (G_TYPE_TLS_FILE_DATABASE, + g_test_tls_file_database_file_database_interface_init)) + +static void +g_test_tls_database_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GTestTlsDatabase *db = (GTestTlsDatabase *) object; + + switch (prop_id) + { + case PROP_DATABASE_ANCHORS: + g_value_set_string (value, db->anchors); + break; + default: + g_assert_not_reached (); + break; + } +} + +static void +g_test_tls_database_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GTestTlsDatabase *db = (GTestTlsDatabase *) object; + + switch (prop_id) + { + case PROP_DATABASE_ANCHORS: + g_free (db->anchors); + db->anchors = g_value_dup_string (value); + break; + default: + g_assert_not_reached (); + break; + } +} + +static void +g_test_tls_database_finalize (GObject *object) +{ + GTestTlsDatabase *db = (GTestTlsDatabase *) object; + + g_free (db->anchors); + + G_OBJECT_CLASS (g_test_tls_database_parent_class)->finalize (object); +} + +static void +g_test_tls_database_class_init (GTestTlsDatabaseClass *test_class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (test_class); + + gobject_class->get_property = g_test_tls_database_get_property; + gobject_class->set_property = g_test_tls_database_set_property; + gobject_class->finalize = g_test_tls_database_finalize; + + g_object_class_override_property (gobject_class, PROP_DATABASE_ANCHORS, "anchors"); +} + +static void +g_test_tls_database_init (GTestTlsDatabase *database) +{ +} + +static gboolean +g_test_tls_database_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + return TRUE; +} + +static void +g_test_tls_file_database_file_database_interface_init (GInitableIface *iface) +{ +} + +static void +g_test_tls_database_initable_iface_init (GInitableIface *iface) +{ + iface->init = g_test_tls_database_initable_init; +} diff --git a/gio/tests/meson.build b/gio/tests/meson.build index 4e5ad25df..2b9eada24 100644 --- a/gio/tests/meson.build +++ b/gio/tests/meson.build @@ -68,6 +68,7 @@ gio_tests = [{ 'thumbnail-verification' : {}, 'tls-certificate' : {'extra_sources' : ['gtesttlsbackend.c']}, 'tls-interaction' : {'extra_sources' : ['gtesttlsbackend.c']}, + 'tls-database' : {'extra_sources' : ['gtesttlsbackend.c']}, }] # FIXME: We are using list of dictionnaries until we can depend on Meson 0.48.0 diff --git a/gio/tests/tls-database.c b/gio/tests/tls-database.c new file mode 100644 index 000000000..1bad25587 --- /dev/null +++ b/gio/tests/tls-database.c @@ -0,0 +1,75 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) Matthew Waters . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, see . + */ + +#include "config.h" + +#include + +#include "gtesttlsbackend.h" + +static void +set_default_database (void) +{ + GTlsBackend *backend; + GTlsDatabase *default_db, *file_db, *test_db; + GError *error = NULL; + gchar *path; + + backend = g_tls_backend_get_default (); + g_assert_nonnull (backend); + + default_db = g_tls_backend_get_default_database (backend); + g_assert_nonnull (default_db); + + path = g_test_build_filename (G_TEST_DIST, "cert-tests", "cert1.pem", NULL); + file_db = g_tls_file_database_new (path, &error); + g_assert_no_error (error); + g_assert_nonnull (file_db); + + /* setting a default database makes get_default_database return that database */ + g_tls_backend_set_default_database (backend, file_db); + test_db = g_tls_backend_get_default_database (backend); + g_assert_nonnull (test_db); + g_assert_true (test_db == file_db); + g_object_unref (test_db); + + /* setting a NULL default database returns the original default database */ + g_tls_backend_set_default_database (backend, NULL); + test_db = g_tls_backend_get_default_database (backend); + g_assert_nonnull (test_db); + g_assert_true (test_db == default_db); + + g_object_unref (default_db); + g_object_unref (file_db); + g_object_unref (test_db); + g_free (path); +} + +int +main (int argc, + char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + _g_test_tls_backend_get_type (); + + g_test_add_func ("/tls-backend/set-default-database", + set_default_database); + + return g_test_run(); +} From 27fca3474c7b8d1e7dc0dc5c93981b6bb4c8aca3 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Mon, 27 Aug 2018 21:36:11 +1000 Subject: [PATCH 3/3] gtlsdatabase: document that implementations must be thread-safe --- gio/gtlsdatabase.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gio/gtlsdatabase.c b/gio/gtlsdatabase.c index 5a77b56d7..4725d8c81 100644 --- a/gio/gtlsdatabase.c +++ b/gio/gtlsdatabase.c @@ -39,6 +39,9 @@ * from a certificate or key store. It is an abstract base class which * TLS library specific subtypes override. * + * A #GTlsDatabase may be accessed from multiple threads by the TLS backend. + * All implementations are required to be fully thread-safe. + * * Most common client applications will not directly interact with * #GTlsDatabase. It is used internally by #GTlsConnection. *