From 44524b9daa622058e3e55617b9b0d4c986e3b8b3 Mon Sep 17 00:00:00 2001 From: Ruslan Marchenko Date: Thu, 25 Jun 2020 12:40:34 +0000 Subject: [PATCH] Add g_(d)tls_connection_get_channel_binding_data calls and enums * Add g_tls_connection_get_channel_binding_data API call * Add g_dtls_connection_get_channel_binding_data API call * Add get_binding_data method to GTlsConnection class * Add get_binding_data method to GDtlsConnection interface * Add GTlsChannelBindingType enum with tls-unique and tls-server-end-point types * Add GTlsChannelBindingError enum and G_TLS_CHANNEL_BINDING_ERROR quark * Add new API calls to documentation reference gio-sections-common --- docs/reference/gio/gio-sections-common.txt | 10 +++ gio/gdtlsconnection.c | 50 +++++++++++ gio/gdtlsconnection.h | 15 ++++ gio/gioenums.h | 55 ++++++++++++ gio/gtlsconnection.c | 60 +++++++++++++ gio/gtlsconnection.h | 29 ++++++- gio/tests/gtesttlsbackend.c | 6 ++ gio/tests/meson.build | 1 + gio/tests/tls-bindings.c | 97 ++++++++++++++++++++++ 9 files changed, 322 insertions(+), 1 deletion(-) create mode 100644 gio/tests/tls-bindings.c diff --git a/docs/reference/gio/gio-sections-common.txt b/docs/reference/gio/gio-sections-common.txt index cd62b8a44..9e606a1f0 100644 --- a/docs/reference/gio/gio-sections-common.txt +++ b/docs/reference/gio/gio-sections-common.txt @@ -3651,15 +3651,20 @@ g_pollable_return_get_type gtls G_TLS_ERROR GTlsError +G_TLS_CHANNEL_BINDING_ERROR +GTlsChannelBindingError GTlsAuthenticationMode GTlsCertificateFlags G_TYPE_TLS_AUTHENTICATION_MODE G_TYPE_TLS_CERTIFICATE_FLAGS +G_TYPE_TLS_CHANNEL_BINDING_ERROR G_TYPE_TLS_ERROR g_tls_authentication_mode_get_type g_tls_certificate_flags_get_type +g_tls_channel_binding_error_get_type +g_tls_channel_binding_error_quark g_tls_error_get_type @@ -3718,10 +3723,12 @@ g_tls_certificate_get_type gtlsconnection GTlsConnection GTlsConnection +GTlsChannelBindingType g_tls_connection_set_certificate g_tls_connection_get_certificate g_tls_connection_get_peer_certificate g_tls_connection_get_peer_certificate_errors +g_tls_connection_get_channel_binding_data g_tls_connection_set_require_close_notify g_tls_connection_get_require_close_notify GTlsRehandshakeMode @@ -3749,9 +3756,11 @@ G_IS_TLS_CONNECTION_CLASS G_TLS_CONNECTION G_TLS_CONNECTION_CLASS G_TLS_CONNECTION_GET_CLASS +G_TYPE_TLS_CHANNEL_BINDING_TYPE G_TYPE_TLS_CONNECTION G_TYPE_TLS_REHANDSHAKE_MODE +g_tls_channel_binding_type_get_type g_tls_connection_get_type g_tls_rehandshake_mode_get_type @@ -3916,6 +3925,7 @@ g_dtls_connection_set_certificate g_dtls_connection_get_certificate g_dtls_connection_get_peer_certificate g_dtls_connection_get_peer_certificate_errors +g_dtls_connection_get_channel_binding_data g_dtls_connection_set_require_close_notify g_dtls_connection_get_require_close_notify g_dtls_connection_set_rehandshake_mode diff --git a/gio/gdtlsconnection.c b/gio/gdtlsconnection.c index 2704133ff..4bbc88d7a 100644 --- a/gio/gdtlsconnection.c +++ b/gio/gdtlsconnection.c @@ -26,6 +26,7 @@ #include "gsocket.h" #include "gtlsbackend.h" #include "gtlscertificate.h" +#include "gtlsconnection.h" #include "gdtlsclientconnection.h" #include "gtlsdatabase.h" #include "gtlsinteraction.h" @@ -1073,3 +1074,52 @@ g_dtls_connection_get_negotiated_protocol (GDtlsConnection *conn) return iface->get_negotiated_protocol (conn); } + +/** + * g_dtls_connection_get_channel_binding_data: + * @conn: a #GDtlsConnection + * @type: #GTlsChannelBindingType type of data to fetch + * @data: (out callee-allocates)(optional)(transfer none): #GByteArray is + * filled with the binding data, or %NULL + * @error: a #GError pointer, or %NULL + * + * Query the TLS backend for TLS channel binding data of @type for @conn. + * + * This call retrieves TLS channel binding data as specified in RFC + * [5056](https://tools.ietf.org/html/rfc5056), RFC + * [5929](https://tools.ietf.org/html/rfc5929), and related RFCs. The + * binding data is returned in @data. The @data is resized by the callee + * using #GByteArray buffer management and will be freed when the @data + * is destroyed by g_byte_array_unref(). If @data is %NULL, it will only + * check whether TLS backend is able to fetch the data (e.g. whether @type + * is supported by the TLS backend). It does not guarantee that the data + * will be available though. That could happen if TLS connection does not + * support @type or the binding data is not available yet due to additional + * negotiation or input required. + * + * Returns: %TRUE on success, %FALSE otherwise + * + * Since: 2.66 + */ +gboolean +g_dtls_connection_get_channel_binding_data (GDtlsConnection *conn, + GTlsChannelBindingType type, + GByteArray *data, + GError **error) +{ + GDtlsConnectionInterface *iface; + + g_return_val_if_fail (G_IS_DTLS_CONNECTION (conn), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + iface = G_DTLS_CONNECTION_GET_INTERFACE (conn); + if (iface->get_binding_data == NULL) + { + g_set_error_literal (error, G_TLS_CHANNEL_BINDING_ERROR, + G_TLS_CHANNEL_BINDING_ERROR_NOT_IMPLEMENTED, + _("TLS backend does not implement TLS binding retrieval")); + return FALSE; + } + + return iface->get_binding_data (conn, type, data, error); +} diff --git a/gio/gdtlsconnection.h b/gio/gdtlsconnection.h index 3901cdc9e..e73cf1459 100644 --- a/gio/gdtlsconnection.h +++ b/gio/gdtlsconnection.h @@ -95,6 +95,13 @@ struct _GDtlsConnectionInterface void (*set_advertised_protocols) (GDtlsConnection *conn, const gchar * const *protocols); const gchar *(*get_negotiated_protocol) (GDtlsConnection *conn); + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + gboolean (*get_binding_data) (GDtlsConnection *conn, + GTlsChannelBindingType type, + GByteArray *data, + GError **error); +G_GNUC_END_IGNORE_DEPRECATIONS }; GLIB_AVAILABLE_IN_2_48 @@ -201,6 +208,14 @@ void g_dtls_connection_set_advertised_protocols (GDtlsConnec GLIB_AVAILABLE_IN_2_60 const gchar * g_dtls_connection_get_negotiated_protocol (GDtlsConnection *conn); +G_GNUC_BEGIN_IGNORE_DEPRECATIONS +GLIB_AVAILABLE_IN_2_66 +gboolean g_dtls_connection_get_channel_binding_data (GDtlsConnection *conn, + GTlsChannelBindingType type, + GByteArray *data, + GError **error); +G_GNUC_END_IGNORE_DEPRECATIONS + G_END_DECLS #endif /* __G_DTLS_CONNECTION_H__ */ diff --git a/gio/gioenums.h b/gio/gioenums.h index cd3654a3c..c6b525224 100644 --- a/gio/gioenums.h +++ b/gio/gioenums.h @@ -1611,6 +1611,61 @@ typedef enum { G_TLS_AUTHENTICATION_REQUIRED } GTlsAuthenticationMode; +/** + * GTlsChannelBindingType: + * @G_TLS_CHANNEL_BINDING_TLS_UNIQUE: + * [`tls-unique`](https://tools.ietf.org/html/rfc5929#section-3) binding + * type + * @G_TLS_CHANNEL_BINDING_TLS_SERVER_END_POINT: + * [`tls-server-end-point`](https://tools.ietf.org/html/rfc5929#section-4) + * binding type + * + * The type of TLS channel binding data to retrieve from #GTlsConnection + * or #GDtlsConnection, as documented by RFC 5929. The + * [`tls-unique-for-telnet`](https://tools.ietf.org/html/rfc5929#section-5) + * binding type is not currently implemented. + * + * Since: 2.66 + */ +GLIB_AVAILABLE_TYPE_IN_2_66 +typedef enum { + G_TLS_CHANNEL_BINDING_TLS_UNIQUE, + G_TLS_CHANNEL_BINDING_TLS_SERVER_END_POINT +} GTlsChannelBindingType; + +/** + * GTlsChannelBindingError: + * @G_TLS_CHANNEL_BINDING_ERROR_NOT_IMPLEMENTED: Either entire binding + * retrieval facility or specific binding type is not implemented in the + * TLS backend. + * @G_TLS_CHANNEL_BINDING_ERROR_INVALID_STATE: The handshake is not yet + * complete on the connection which is a strong requirement for any existing + * binding type. + * @G_TLS_CHANNEL_BINDING_ERROR_NOT_AVAILABLE: Handshake is complete but + * binding data is not available. That normally indicates the TLS + * implementation failed to provide the binding data. For example, some + * implementations do not provide a peer certificate for resumed connections. + * @G_TLS_CHANNEL_BINDING_ERROR_NOT_SUPPORTED: Binding type is not supported + * on the current connection. This error could be triggered when requesting + * `tls-server-end-point` binding data for a certificate which has no hash + * function or uses multiple hash functions. + * @G_TLS_CHANNEL_BINDING_ERROR_GENERAL_ERROR: Any other backend error + * preventing binding data retrieval. + * + * An error code used with %G_TLS_CHANNEL_BINDING_ERROR in a #GError to + * indicate a TLS channel binding retrieval error. + * + * Since: 2.66 + */ +GLIB_AVAILABLE_TYPE_IN_2_66 +typedef enum { + G_TLS_CHANNEL_BINDING_ERROR_NOT_IMPLEMENTED, + G_TLS_CHANNEL_BINDING_ERROR_INVALID_STATE, + G_TLS_CHANNEL_BINDING_ERROR_NOT_AVAILABLE, + G_TLS_CHANNEL_BINDING_ERROR_NOT_SUPPORTED, + G_TLS_CHANNEL_BINDING_ERROR_GENERAL_ERROR +} GTlsChannelBindingError; + /** * GTlsRehandshakeMode: * @G_TLS_REHANDSHAKE_NEVER: Never allow rehandshaking diff --git a/gio/gtlsconnection.c b/gio/gtlsconnection.c index f01e492d5..5654ca9ee 100644 --- a/gio/gtlsconnection.c +++ b/gio/gtlsconnection.c @@ -865,6 +865,66 @@ g_tls_connection_get_negotiated_protocol (GTlsConnection *conn) return priv->negotiated_protocol; } +/** + * g_tls_channel_binding_error_quark: + * + * Gets the TLS channel binding error quark. + * + * Returns: a #GQuark. + * + * Since: 2.66 + */ +G_DEFINE_QUARK (g-tls-channel-binding-error-quark, g_tls_channel_binding_error) + +/** + * g_tls_connection_get_channel_binding_data: + * @conn: a #GTlsConnection + * @type: #GTlsChannelBindingType type of data to fetch + * @data: (out callee-allocates)(optional)(transfer none): #GByteArray is + * filled with the binding data, or %NULL + * @error: a #GError pointer, or %NULL + * + * Query the TLS backend for TLS channel binding data of @type for @conn. + * + * This call retrieves TLS channel binding data as specified in RFC + * [5056](https://tools.ietf.org/html/rfc5056), RFC + * [5929](https://tools.ietf.org/html/rfc5929), and related RFCs. The + * binding data is returned in @data. The @data is resized by the callee + * using #GByteArray buffer management and will be freed when the @data + * is destroyed by g_byte_array_unref(). If @data is %NULL, it will only + * check whether TLS backend is able to fetch the data (e.g. whether @type + * is supported by the TLS backend). It does not guarantee that the data + * will be available though. That could happen if TLS connection does not + * support @type or the binding data is not available yet due to additional + * negotiation or input required. + * + * Returns: %TRUE on success, %FALSE otherwise + * + * Since: 2.66 + */ +gboolean +g_tls_connection_get_channel_binding_data (GTlsConnection *conn, + GTlsChannelBindingType type, + GByteArray *data, + GError **error) +{ + GTlsConnectionClass *class; + + g_return_val_if_fail (G_IS_TLS_CONNECTION (conn), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + class = G_TLS_CONNECTION_GET_CLASS (conn); + if (class->get_binding_data == NULL) + { + g_set_error_literal (error, G_TLS_CHANNEL_BINDING_ERROR, + G_TLS_CHANNEL_BINDING_ERROR_NOT_IMPLEMENTED, + _("TLS backend does not implement TLS binding retrieval")); + return FALSE; + } + + return class->get_binding_data (conn, type, data, error); +} + /** * g_tls_connection_handshake: * @conn: a #GTlsConnection diff --git a/gio/gtlsconnection.h b/gio/gtlsconnection.h index be38dcf5c..037222733 100644 --- a/gio/gtlsconnection.h +++ b/gio/gtlsconnection.h @@ -66,9 +66,16 @@ struct _GTlsConnectionClass GAsyncResult *result, GError **error); +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + gboolean ( *get_binding_data) (GTlsConnection *conn, + GTlsChannelBindingType type, + GByteArray *data, + GError **error); +G_GNUC_END_IGNORE_DEPRECATIONS + /*< private >*/ /* Padding for future expansion */ - gpointer padding[8]; + gpointer padding[7]; }; GLIB_AVAILABLE_IN_ALL @@ -124,6 +131,14 @@ void g_tls_connection_set_advertised_protocols (GTlsConnecti GLIB_AVAILABLE_IN_2_60 const gchar * g_tls_connection_get_negotiated_protocol (GTlsConnection *conn); +G_GNUC_BEGIN_IGNORE_DEPRECATIONS +GLIB_AVAILABLE_IN_2_66 +gboolean g_tls_connection_get_channel_binding_data (GTlsConnection *conn, + GTlsChannelBindingType type, + GByteArray *data, + GError **error); +G_GNUC_END_IGNORE_DEPRECATIONS + GLIB_AVAILABLE_IN_ALL gboolean g_tls_connection_handshake (GTlsConnection *conn, GCancellable *cancellable, @@ -151,6 +166,18 @@ gboolean g_tls_connection_handshake_finish (GTlsConnecti GLIB_AVAILABLE_IN_ALL GQuark g_tls_error_quark (void); +/** + * G_TLS_CHANNEL_BINDING_ERROR: + * + * Error domain for TLS channel binding. Errors in this domain will be from the + * #GTlsChannelBindingError enumeration. See #GError for more information on error + * domains. + * + * Since: 2.66 + */ +#define G_TLS_CHANNEL_BINDING_ERROR (g_tls_channel_binding_error_quark ()) +GLIB_AVAILABLE_IN_2_66 +GQuark g_tls_channel_binding_error_quark (void); /*< protected >*/ GLIB_AVAILABLE_IN_ALL diff --git a/gio/tests/gtesttlsbackend.c b/gio/tests/gtesttlsbackend.c index 157a4a3f3..346a20dd9 100644 --- a/gio/tests/gtesttlsbackend.c +++ b/gio/tests/gtesttlsbackend.c @@ -59,6 +59,8 @@ 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_dtls_client_connection_type = _g_test_tls_connection_get_type; + iface->get_dtls_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; } @@ -245,6 +247,7 @@ struct _GTestTlsConnectionClass { enum { PROP_CONN_BASE_IO_STREAM = 1, + PROP_CONN_BASE_SOCKET, PROP_CONN_USE_SYSTEM_CERTDB, PROP_CONN_REQUIRE_CLOSE_NOTIFY, PROP_CONN_REHANDSHAKE_MODE, @@ -264,6 +267,8 @@ static void g_test_tls_connection_initable_iface_init (GInitableIface *iface); G_DEFINE_TYPE_WITH_CODE (GTestTlsConnection, g_test_tls_connection, G_TYPE_TLS_CONNECTION, G_IMPLEMENT_INTERFACE (G_TYPE_TLS_CLIENT_CONNECTION, NULL) G_IMPLEMENT_INTERFACE (G_TYPE_TLS_SERVER_CONNECTION, NULL) + G_IMPLEMENT_INTERFACE (G_TYPE_DATAGRAM_BASED, NULL) + G_IMPLEMENT_INTERFACE (G_TYPE_DTLS_CONNECTION, NULL) G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, g_test_tls_connection_initable_iface_init)) @@ -308,6 +313,7 @@ g_test_tls_connection_class_init (GTestTlsConnectionClass *connection_class) io_stream_class->close_fn = g_test_tls_connection_close; g_object_class_override_property (gobject_class, PROP_CONN_BASE_IO_STREAM, "base-io-stream"); + g_object_class_override_property (gobject_class, PROP_CONN_BASE_SOCKET, "base-socket"); g_object_class_override_property (gobject_class, PROP_CONN_USE_SYSTEM_CERTDB, "use-system-certdb"); g_object_class_override_property (gobject_class, PROP_CONN_REQUIRE_CLOSE_NOTIFY, "require-close-notify"); g_object_class_override_property (gobject_class, PROP_CONN_REHANDSHAKE_MODE, "rehandshake-mode"); diff --git a/gio/tests/meson.build b/gio/tests/meson.build index c7c3e3035..d8ebd56ac 100644 --- a/gio/tests/meson.build +++ b/gio/tests/meson.build @@ -78,6 +78,7 @@ gio_tests = { 'tls-certificate' : {'extra_sources' : ['gtesttlsbackend.c']}, 'tls-interaction' : {'extra_sources' : ['gtesttlsbackend.c']}, 'tls-database' : {'extra_sources' : ['gtesttlsbackend.c']}, + 'tls-bindings' : {'extra_sources' : ['gtesttlsbackend.c']}, 'gdbus-address-get-session' : {}, 'win32-appinfo' : {}, } diff --git a/gio/tests/tls-bindings.c b/gio/tests/tls-bindings.c new file mode 100644 index 000000000..89890248a --- /dev/null +++ b/gio/tests/tls-bindings.c @@ -0,0 +1,97 @@ +/* + * Copyright 2020 (C) Ruslan N. Marchenko + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include + +#include "gtesttlsbackend.h" + +static void +get_tls_channel_binding (void) +{ + GTlsBackend *backend; + gchar *not_null = "NOT_NULL"; + GTlsConnection *tls = NULL; + GError *error = NULL; + + backend = g_tls_backend_get_default (); + g_assert_nonnull (backend); + + /* check unimplemented GTlsConnection API sanity */ + tls = G_TLS_CONNECTION (g_object_new ( + g_tls_backend_get_client_connection_type (backend), NULL)); + g_assert_nonnull (tls); + + g_assert_false (g_tls_connection_get_channel_binding_data (tls, + G_TLS_CHANNEL_BINDING_TLS_UNIQUE, NULL, NULL)); + + g_assert_false (g_tls_connection_get_channel_binding_data (tls, + G_TLS_CHANNEL_BINDING_TLS_UNIQUE, NULL, &error)); + g_assert_error (error, G_TLS_CHANNEL_BINDING_ERROR, + G_TLS_CHANNEL_BINDING_ERROR_NOT_IMPLEMENTED); + g_clear_error (&error); + + if (g_test_subprocess ()) + g_assert_false (g_tls_connection_get_channel_binding_data (tls, + G_TLS_CHANNEL_BINDING_TLS_UNIQUE, NULL, (GError **)¬_null)); + + g_object_unref (tls); + g_object_unref (backend); + g_test_trap_subprocess (NULL, 0, 0); + g_test_trap_assert_failed (); + g_test_trap_assert_stderr ("*GLib-GIO-CRITICAL*"); +} + +static void +get_dtls_channel_binding (void) +{ + GTlsBackend *backend; + gchar *not_null = "NOT_NULL"; + GDtlsConnection *dtls = NULL; + GError *error = NULL; + + backend = g_tls_backend_get_default (); + g_assert_nonnull (backend); + + /* repeat for the dtls now */ + dtls = G_DTLS_CONNECTION (g_object_new ( + g_tls_backend_get_dtls_client_connection_type (backend), NULL)); + g_assert_nonnull (dtls); + + g_assert_false (g_dtls_connection_get_channel_binding_data (dtls, + G_TLS_CHANNEL_BINDING_TLS_UNIQUE, NULL, NULL)); + + g_assert_false (g_dtls_connection_get_channel_binding_data (dtls, + G_TLS_CHANNEL_BINDING_TLS_UNIQUE, NULL, &error)); + g_assert_error (error, G_TLS_CHANNEL_BINDING_ERROR, + G_TLS_CHANNEL_BINDING_ERROR_NOT_IMPLEMENTED); + g_clear_error (&error); + + if (g_test_subprocess ()) + g_assert_false (g_dtls_connection_get_channel_binding_data (dtls, + G_TLS_CHANNEL_BINDING_TLS_UNIQUE, NULL, (GError **)¬_null)); + + g_object_unref (dtls); + g_object_unref (backend); + g_test_trap_subprocess (NULL, 0, 0); + g_test_trap_assert_failed (); + g_test_trap_assert_stderr ("*GLib-GIO-CRITICAL*"); +} + +int +main (int argc, + char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + _g_test_tls_backend_get_type (); + + g_test_add_func ("/tls-connection/get-tls-channel-binding", get_tls_channel_binding); + g_test_add_func ("/tls-connection/get-dtls-channel-binding", get_dtls_channel_binding); + + return g_test_run (); +}