From 561b594fe58c3ea1c6387c64c59f4816c1fd1c38 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 7 Jun 2019 18:44:43 +0000 Subject: [PATCH 1/4] ghmac: Split off wrapper functions into ghmac-utils.c Prep for adding a GnuTLS HMAC implementation; these are just utility functions that call the "core" API. --- glib/ghmac-utils.c | 145 +++++++++++++++++++++++++++++++++++++++++++++ glib/ghmac.c | 112 ---------------------------------- glib/meson.build | 1 + 3 files changed, 146 insertions(+), 112 deletions(-) create mode 100644 glib/ghmac-utils.c diff --git a/glib/ghmac-utils.c b/glib/ghmac-utils.c new file mode 100644 index 000000000..a17359ff1 --- /dev/null +++ b/glib/ghmac-utils.c @@ -0,0 +1,145 @@ +/* ghmac.h - data hashing functions + * + * Copyright (C) 2011 Collabora Ltd. + * Copyright (C) 2019 Red Hat, Inc. + * + * 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 "ghmac.h" + +#include "glib/galloca.h" +#include "gatomic.h" +#include "gslice.h" +#include "gmem.h" +#include "gstrfuncs.h" +#include "gtestutils.h" +#include "gtypes.h" +#include "glibintl.h" + +/** + * g_compute_hmac_for_data: + * @digest_type: a #GChecksumType to use for the HMAC + * @key: (array length=key_len): the key to use in the HMAC + * @key_len: the length of the key + * @data: (array length=length): binary blob to compute the HMAC of + * @length: length of @data + * + * Computes the HMAC for a binary @data of @length. This is a + * convenience wrapper for g_hmac_new(), g_hmac_get_string() + * and g_hmac_unref(). + * + * The hexadecimal string returned will be in lower case. + * + * Returns: the HMAC of the binary data as a string in hexadecimal. + * The returned string should be freed with g_free() when done using it. + * + * Since: 2.30 + */ +gchar * +g_compute_hmac_for_data (GChecksumType digest_type, + const guchar *key, + gsize key_len, + const guchar *data, + gsize length) +{ + GHmac *hmac; + gchar *retval; + + g_return_val_if_fail (length == 0 || data != NULL, NULL); + + hmac = g_hmac_new (digest_type, key, key_len); + if (!hmac) + return NULL; + + g_hmac_update (hmac, data, length); + retval = g_strdup (g_hmac_get_string (hmac)); + g_hmac_unref (hmac); + + return retval; +} + +/** + * g_compute_hmac_for_bytes: + * @digest_type: a #GChecksumType to use for the HMAC + * @key: the key to use in the HMAC + * @data: binary blob to compute the HMAC of + * + * Computes the HMAC for a binary @data. This is a + * convenience wrapper for g_hmac_new(), g_hmac_get_string() + * and g_hmac_unref(). + * + * The hexadecimal string returned will be in lower case. + * + * Returns: the HMAC of the binary data as a string in hexadecimal. + * The returned string should be freed with g_free() when done using it. + * + * Since: 2.50 + */ +gchar * +g_compute_hmac_for_bytes (GChecksumType digest_type, + GBytes *key, + GBytes *data) +{ + gconstpointer byte_data; + gsize length; + gconstpointer key_data; + gsize key_len; + + g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail (key != NULL, NULL); + + byte_data = g_bytes_get_data (data, &length); + key_data = g_bytes_get_data (key, &key_len); + return g_compute_hmac_for_data (digest_type, key_data, key_len, byte_data, length); +} + + +/** + * g_compute_hmac_for_string: + * @digest_type: a #GChecksumType to use for the HMAC + * @key: (array length=key_len): the key to use in the HMAC + * @key_len: the length of the key + * @str: the string to compute the HMAC for + * @length: the length of the string, or -1 if the string is nul-terminated + * + * Computes the HMAC for a string. + * + * The hexadecimal string returned will be in lower case. + * + * Returns: the HMAC as a hexadecimal string. + * The returned string should be freed with g_free() + * when done using it. + * + * Since: 2.30 + */ +gchar * +g_compute_hmac_for_string (GChecksumType digest_type, + const guchar *key, + gsize key_len, + const gchar *str, + gssize length) +{ + g_return_val_if_fail (length == 0 || str != NULL, NULL); + + if (length < 0) + length = strlen (str); + + return g_compute_hmac_for_data (digest_type, key, key_len, + (const guchar *) str, length); +} diff --git a/glib/ghmac.c b/glib/ghmac.c index 49fd272f0..4f181f21f 100644 --- a/glib/ghmac.c +++ b/glib/ghmac.c @@ -329,115 +329,3 @@ g_hmac_get_digest (GHmac *hmac, g_checksum_update (hmac->digesto, buffer, len); g_checksum_get_digest (hmac->digesto, buffer, digest_len); } - -/** - * g_compute_hmac_for_data: - * @digest_type: a #GChecksumType to use for the HMAC - * @key: (array length=key_len): the key to use in the HMAC - * @key_len: the length of the key - * @data: (array length=length): binary blob to compute the HMAC of - * @length: length of @data - * - * Computes the HMAC for a binary @data of @length. This is a - * convenience wrapper for g_hmac_new(), g_hmac_get_string() - * and g_hmac_unref(). - * - * The hexadecimal string returned will be in lower case. - * - * Returns: the HMAC of the binary data as a string in hexadecimal. - * The returned string should be freed with g_free() when done using it. - * - * Since: 2.30 - */ -gchar * -g_compute_hmac_for_data (GChecksumType digest_type, - const guchar *key, - gsize key_len, - const guchar *data, - gsize length) -{ - GHmac *hmac; - gchar *retval; - - g_return_val_if_fail (length == 0 || data != NULL, NULL); - - hmac = g_hmac_new (digest_type, key, key_len); - if (!hmac) - return NULL; - - g_hmac_update (hmac, data, length); - retval = g_strdup (g_hmac_get_string (hmac)); - g_hmac_unref (hmac); - - return retval; -} - -/** - * g_compute_hmac_for_bytes: - * @digest_type: a #GChecksumType to use for the HMAC - * @key: the key to use in the HMAC - * @data: binary blob to compute the HMAC of - * - * Computes the HMAC for a binary @data. This is a - * convenience wrapper for g_hmac_new(), g_hmac_get_string() - * and g_hmac_unref(). - * - * The hexadecimal string returned will be in lower case. - * - * Returns: the HMAC of the binary data as a string in hexadecimal. - * The returned string should be freed with g_free() when done using it. - * - * Since: 2.50 - */ -gchar * -g_compute_hmac_for_bytes (GChecksumType digest_type, - GBytes *key, - GBytes *data) -{ - gconstpointer byte_data; - gsize length; - gconstpointer key_data; - gsize key_len; - - g_return_val_if_fail (data != NULL, NULL); - g_return_val_if_fail (key != NULL, NULL); - - byte_data = g_bytes_get_data (data, &length); - key_data = g_bytes_get_data (key, &key_len); - return g_compute_hmac_for_data (digest_type, key_data, key_len, byte_data, length); -} - - -/** - * g_compute_hmac_for_string: - * @digest_type: a #GChecksumType to use for the HMAC - * @key: (array length=key_len): the key to use in the HMAC - * @key_len: the length of the key - * @str: the string to compute the HMAC for - * @length: the length of the string, or -1 if the string is nul-terminated - * - * Computes the HMAC for a string. - * - * The hexadecimal string returned will be in lower case. - * - * Returns: the HMAC as a hexadecimal string. - * The returned string should be freed with g_free() - * when done using it. - * - * Since: 2.30 - */ -gchar * -g_compute_hmac_for_string (GChecksumType digest_type, - const guchar *key, - gsize key_len, - const gchar *str, - gssize length) -{ - g_return_val_if_fail (length == 0 || str != NULL, NULL); - - if (length < 0) - length = strlen (str); - - return g_compute_hmac_for_data (digest_type, key, key_len, - (const guchar *) str, length); -} diff --git a/glib/meson.build b/glib/meson.build index 93600b29e..cce338969 100644 --- a/glib/meson.build +++ b/glib/meson.build @@ -251,6 +251,7 @@ glib_sources = files( 'ggettext.c', 'ghash.c', 'ghmac.c', + 'ghmac-utils.c', 'ghook.c', 'ghostutils.c', 'giochannel.c', -- 2.31.1 From b87b28e06c6300479e292782100e9b94ebb6a140 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 7 Jun 2019 19:36:54 +0000 Subject: [PATCH 2/4] Add a gnutls backend for GHmac For RHEL we want apps to use FIPS-certified crypto libraries, and HMAC apparently counts as "keyed" and hence needs to be validated. Bug: https://bugzilla.redhat.com/show_bug.cgi?id=1630260 Replaces: https://gitlab.gnome.org/GNOME/glib/merge_requests/897 This is a build-time option that backs the GHmac API with GnuTLS. Most distributors ship glib-networking built with GnuTLS, and most apps use glib-networking, so this isn't a net-new library in most cases. ======================================================================= mcatanzaro note: I've updated Colin's original patch with several enhancements: Implement g_hmac_copy() using gnutls_hmac_copy(), which didn't exist when Colin developed this patch. Removed use of GSlice Better error checking in g_hmac_new(). It is possible for gnutls_hmac_init() to fail if running in FIPS mode and an MD5 digest is requested. In this case, we should return NULL rather than returning a broken GHmac with a NULL gnutls_hmac_hd_t. This was leading to a later null pointer dereference inside gnutls_hmac_update(). Applications are responsible for checking to ensure the return value of g_hmac_new() is not NULL since it is annotated as nullable. Added documentation to indicate this possibility. Properly handle length -1 in g_hmac_update(). This means we've been given a NUL-terminated string and should use strlen(). GnuTLS doesn't accept -1, so let's call strlen() ourselves. Crash the application with g_error() if gnutls_hmac() fails for any reason. This is necessary because g_hmac_update() is not fallible, so we have no way to indicate error. Crashing seems better than returning the wrong result later when g_hmac_get_string() or g_hmac_get_digest() is later called. (Those functions are also not fallible.) Fortunately, I don't think this error should actually be hit in practice. https://gitlab.gnome.org/GNOME/glib/-/merge_requests/903 --- glib/gchecksum.c | 9 +- glib/gchecksumprivate.h | 32 +++++++ glib/ghmac-gnutls.c | 187 ++++++++++++++++++++++++++++++++++++++++ glib/ghmac.c | 15 ++++ glib/meson.build | 10 ++- meson.build | 7 ++ meson_options.txt | 7 +- 7 files changed, 260 insertions(+), 7 deletions(-) create mode 100644 glib/gchecksumprivate.h create mode 100644 glib/ghmac-gnutls.c diff --git a/glib/gchecksum.c b/glib/gchecksum.c index 29b479bc6..929958c3a 100644 --- a/glib/gchecksum.c +++ b/glib/gchecksum.c @@ -20,7 +20,7 @@ #include -#include "gchecksum.h" +#include "gchecksumprivate.h" #include "gslice.h" #include "gmem.h" @@ -173,9 +173,9 @@ sha_byte_reverse (guint32 *buffer, } #endif /* G_BYTE_ORDER == G_BIG_ENDIAN */ -static gchar * -digest_to_string (guint8 *digest, - gsize digest_len) +gchar * +gchecksum_digest_to_string (guint8 *digest, + gsize digest_len) { gsize i, len = digest_len * 2; gchar *retval; @@ -194,6 +194,7 @@ digest_to_string (guint8 *digest, return retval; } +#define digest_to_string gchecksum_digest_to_string /* * MD5 Checksum diff --git a/glib/gchecksumprivate.h b/glib/gchecksumprivate.h new file mode 100644 index 000000000..86c7a3b61 --- /dev/null +++ b/glib/gchecksumprivate.h @@ -0,0 +1,32 @@ +/* gstdioprivate.h - Private GLib stdio functions + * + * Copyright 2017 Руслан Ижбулатов + * + * 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 . + */ + +#ifndef __G_CHECKSUMPRIVATE_H__ +#define __G_CHECKSUMPRIVATE_H__ + +#include "gchecksum.h" + +G_BEGIN_DECLS + +gchar * +gchecksum_digest_to_string (guint8 *digest, + gsize digest_len); + +G_END_DECLS + +#endif \ No newline at end of file diff --git a/glib/ghmac-gnutls.c b/glib/ghmac-gnutls.c new file mode 100644 index 000000000..9fb775f89 --- /dev/null +++ b/glib/ghmac-gnutls.c @@ -0,0 +1,187 @@ +/* ghmac.h - data hashing functions + * + * Copyright (C) 2011 Collabora Ltd. + * Copyright (C) 2019 Red Hat, Inc. + * + * 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 + +#include "ghmac.h" + +#include "glib/galloca.h" +#include "gatomic.h" +#include "gslice.h" +#include "gmem.h" +#include "gstrfuncs.h" +#include "gchecksumprivate.h" +#include "gtestutils.h" +#include "gtypes.h" +#include "glibintl.h" + +#ifndef HAVE_GNUTLS +#error "build configuration error" +#endif + +struct _GHmac +{ + int ref_count; + GChecksumType digest_type; + gnutls_hmac_hd_t hmac; + gchar *digest_str; +}; + +GHmac * +g_hmac_new (GChecksumType digest_type, + const guchar *key, + gsize key_len) +{ + gnutls_mac_algorithm_t algo; + GHmac *hmac = g_new0 (GHmac, 1); + int ret; + + hmac->ref_count = 1; + hmac->digest_type = digest_type; + + switch (digest_type) + { + case G_CHECKSUM_MD5: + algo = GNUTLS_MAC_MD5; + break; + case G_CHECKSUM_SHA1: + algo = GNUTLS_MAC_SHA1; + break; + case G_CHECKSUM_SHA256: + algo = GNUTLS_MAC_SHA256; + break; + case G_CHECKSUM_SHA384: + algo = GNUTLS_MAC_SHA384; + break; + case G_CHECKSUM_SHA512: + algo = GNUTLS_MAC_SHA512; + break; + default: + g_free (hmac); + g_return_val_if_reached (NULL); + } + + ret = gnutls_hmac_init (&hmac->hmac, algo, key, key_len); + if (ret != 0) + { + /* There is no way to report an error here, but one possible cause of + * failure is that the requested digest may be disabled by FIPS mode. + */ + g_free (hmac); + return NULL; + } + + return hmac; +} + +GHmac * +g_hmac_copy (const GHmac *hmac) +{ + GHmac *copy; + + g_return_val_if_fail (hmac != NULL, NULL); + + copy = g_new0 (GHmac, 1); + copy->ref_count = 1; + copy->digest_type = hmac->digest_type; + copy->hmac = gnutls_hmac_copy (hmac->hmac); + + /* g_hmac_copy is not allowed to fail, so we'll have to crash on error. */ + if (!copy->hmac) + g_error ("gnutls_hmac_copy failed"); + + return copy; +} + +GHmac * +g_hmac_ref (GHmac *hmac) +{ + g_return_val_if_fail (hmac != NULL, NULL); + + g_atomic_int_inc (&hmac->ref_count); + + return hmac; +} + +void +g_hmac_unref (GHmac *hmac) +{ + g_return_if_fail (hmac != NULL); + + if (g_atomic_int_dec_and_test (&hmac->ref_count)) + { + gnutls_hmac_deinit (hmac->hmac, NULL); + g_free (hmac->digest_str); + g_free (hmac); + } +} + + +void +g_hmac_update (GHmac *hmac, + const guchar *data, + gssize length) +{ + int ret; + + g_return_if_fail (hmac != NULL); + g_return_if_fail (length == 0 || data != NULL); + + if (length == -1) + length = strlen ((const char *)data); + + /* g_hmac_update is not allowed to fail, so we'll have to crash on error. */ + ret = gnutls_hmac (hmac->hmac, data, length); + if (ret != 0) + g_error ("gnutls_hmac failed: %s", gnutls_strerror (ret)); +} + +const gchar * +g_hmac_get_string (GHmac *hmac) +{ + guint8 *buffer; + gsize digest_len; + + g_return_val_if_fail (hmac != NULL, NULL); + + if (hmac->digest_str) + return hmac->digest_str; + + digest_len = g_checksum_type_get_length (hmac->digest_type); + buffer = g_alloca (digest_len); + + gnutls_hmac_output (hmac->hmac, buffer); + hmac->digest_str = gchecksum_digest_to_string (buffer, digest_len); + return hmac->digest_str; +} + + +void +g_hmac_get_digest (GHmac *hmac, + guint8 *buffer, + gsize *digest_len) +{ + g_return_if_fail (hmac != NULL); + + gnutls_hmac_output (hmac->hmac, buffer); + *digest_len = g_checksum_type_get_length (hmac->digest_type); +} diff --git a/glib/ghmac.c b/glib/ghmac.c index 4f181f21f..0e39ea40a 100644 --- a/glib/ghmac.c +++ b/glib/ghmac.c @@ -33,6 +33,9 @@ #include "gtypes.h" #include "glibintl.h" +#ifdef HAVE_GNUTLS +#error "build configuration error" +#endif /** * SECTION:hmac @@ -84,6 +87,18 @@ struct _GHmac * Support for digests of type %G_CHECKSUM_SHA512 has been added in GLib 2.42. * Support for %G_CHECKSUM_SHA384 was added in GLib 2.52. * + * Note that #GHmac creation may fail, in which case this function will + * return %NULL. Since there is no error parameter, it is not possible + * to indicate why. + * + * In Fedora, CentOS Stream, and Red Hat Enterprise Linux, GLib is + * configured to use GnuTLS to implement #GHmac in order to support FIPS + * compliance. This introduces additional failure possibilities that are + * not present in upstream GLib. For example, the creation of a #GHmac + * will fail if @digest_type is %G_CHECKSUM_MD5 and the system is + * running in FIPS mode. #GHmac creation may also fail if GLib is unable + * to load GnuTLS. + * * Returns: the newly created #GHmac, or %NULL. * Use g_hmac_unref() to free the memory allocated by it. * diff --git a/glib/meson.build b/glib/meson.build index cce338969..1f6bb35d1 100644 --- a/glib/meson.build +++ b/glib/meson.build @@ -250,7 +250,6 @@ glib_sources = files( 'gfileutils.c', 'ggettext.c', 'ghash.c', - 'ghmac.c', 'ghmac-utils.c', 'ghook.c', 'ghostutils.c', @@ -306,6 +305,7 @@ glib_sources = files( 'guriprivate.h', 'gutils.c', 'gutilsprivate.h', + 'gchecksumprivate.h', 'guuid.c', 'gvariant.c', 'gvariant-core.c', @@ -350,6 +350,12 @@ else glib_dtrace_hdr = [] endif +if get_option('gnutls') + glib_sources += files('ghmac-gnutls.c') +else + glib_sources += files('ghmac.c') +endif + pcre_static_args = [] if use_pcre_static_flag @@ -368,7 +374,7 @@ libglib = library('glib-2.0', # intl.lib is not compatible with SAFESEH link_args : [noseh_link_args, glib_link_flags, win32_ldflags], include_directories : configinc, - dependencies : [pcre, thread_dep, librt] + libintl_deps + libiconv + platform_deps + [gnulib_libm_dependency, libm] + [libsysprof_capture_dep], + dependencies : [pcre, thread_dep, librt] + libgnutls_dep + libintl_deps + libiconv + platform_deps + [gnulib_libm_dependency, libm] + [libsysprof_capture_dep], c_args : glib_c_args, objc_args : glib_c_args, ) diff --git a/meson.build b/meson.build index 1b54fdcae..98f8925a2 100644 --- a/meson.build +++ b/meson.build @@ -2095,6 +2095,13 @@ if host_system == 'linux' glib_conf.set('HAVE_LIBMOUNT', libmount_dep.found()) endif +# gnutls is used optionally by ghmac +libgnutls_dep = [] +if get_option('gnutls') + libgnutls_dep = [dependency('gnutls', version : '>=3.6.9', required : true)] + glib_conf.set('HAVE_GNUTLS', 1) +endif + if host_system == 'windows' winsock2 = cc.find_library('ws2_32') endif diff --git a/meson_options.txt b/meson_options.txt index 6cd7bc90a..65af1d276 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -34,6 +34,11 @@ option('libmount', value : 'auto', description : 'build with libmount support') +option('gnutls', + type : 'boolean', + value : false, + description : 'build with gnutls support') + option('man', type : 'boolean', value : false, @@ -121,4 +126,4 @@ option('glib_checks', option('libelf', type : 'feature', value : 'auto', - description : 'Enable support for listing and extracting from ELF resource files with gresource tool') \ No newline at end of file + description : 'Enable support for listing and extracting from ELF resource files with gresource tool') -- 2.31.1 From 379e559f5aa390272368f83ca3e6af092066ae75 Mon Sep 17 00:00:00 2001 From: Michael Catanzaro Date: Wed, 16 Jun 2021 20:35:00 -0500 Subject: [PATCH 3/4] dlopen GnuTLS instead of linking directly I'd like to enable our GnuTLS GHmac patchset in Fedora in order to ensure it is receiving sufficient real-world testing, since we've discovered several bugs thus far. Problem is Fedora has one requirement that RHEL does not: it needs to build glib as a static lib. This is needed by QEMU in Fedora for complicated technical reasons that I don't understand. However, nothing in RHEL needs it. This means we failed to notice that glib2-static is broken in RHEL, because there is no gnutls-static! We could fix this by adding a gnutls-static package, but that seems like overkill, and adding more static libraries where they're not truly necessary is not the direction we want to move in anyway. So instead, let's just dlopen GnuTLS to sidestep this problem entirely. This would not be a good solution for upstream, but upstream has made clear that this patchset is already non-upstreamable, so it will be fine for our purposes. --- glib/ghmac-gnutls.c | 101 ++++++++++++++++++++++++++++++++++++++++++-- glib/ghmac.c | 2 +- glib/meson.build | 2 +- meson.build | 6 +-- 4 files changed, 102 insertions(+), 9 deletions(-) diff --git a/glib/ghmac-gnutls.c b/glib/ghmac-gnutls.c index 9fb775f89..1800fc2e0 100644 --- a/glib/ghmac-gnutls.c +++ b/glib/ghmac-gnutls.c @@ -19,8 +19,8 @@ #include "config.h" +#include #include -#include #include "ghmac.h" @@ -31,13 +31,16 @@ #include "gstrfuncs.h" #include "gchecksumprivate.h" #include "gtestutils.h" +#include "gthread.h" #include "gtypes.h" #include "glibintl.h" -#ifndef HAVE_GNUTLS +#ifndef USE_GNUTLS #error "build configuration error" #endif +typedef gpointer gnutls_hmac_hd_t; + struct _GHmac { int ref_count; @@ -46,15 +49,107 @@ struct _GHmac gchar *digest_str; }; +typedef enum +{ + GNUTLS_MAC_MD5 = 2, + GNUTLS_MAC_SHA1 = 3, + GNUTLS_MAC_SHA256 = 6, + GNUTLS_MAC_SHA384 = 7, + GNUTLS_MAC_SHA512 = 8, +} gnutls_mac_algorithm_t; + +/* Why are we dlopening GnuTLS instead of linking to it directly? Because we + * want to be able to build GLib as a static library without depending on a + * static build of GnuTLS. QEMU depends on static linking with GLib, but Fedora + * does not ship a static build of GnuTLS, and this allows us to avoid changing + * that. + */ +static int (*gnutls_hmac_init) (gnutls_hmac_hd_t *dig, gnutls_mac_algorithm_t algorithm, const void *key, size_t keylen); +static gnutls_hmac_hd_t (*gnutls_hmac_copy) (gnutls_hmac_hd_t handle); +static void (*gnutls_hmac_deinit) (gnutls_hmac_hd_t handle, void *digest); +static int (*gnutls_hmac) (gnutls_hmac_hd_t handle, const void *ptext, size_t ptext_len); +static void (*gnutls_hmac_output) (gnutls_hmac_hd_t handle, void *digest); +static const char * (*gnutls_strerror) (int error); + +static gsize gnutls_initialize_attempted = 0; +static gboolean gnutls_initialize_successful = FALSE; + +static void +initialize_gnutls (void) +{ + gpointer libgnutls; + + libgnutls = dlopen ("libgnutls.so.30", RTLD_LAZY | RTLD_GLOBAL); + if (!libgnutls) + { + g_warning ("Cannot use GHmac: failed to load libgnutls.so.30: %s", dlerror ()); + return; + } + + gnutls_hmac_init = dlsym (libgnutls, "gnutls_hmac_init"); + if (!gnutls_hmac_init) + { + g_warning ("Cannot use GHmac: failed to load gnutls_hmac_init: %s", dlerror ()); + return; + } + + gnutls_hmac_copy = dlsym (libgnutls, "gnutls_hmac_copy"); + if (!gnutls_hmac_copy) + { + g_warning ("Cannot use GHmac: failed to load gnutls_hmac_copy: %s", dlerror ()); + return; + } + + gnutls_hmac_deinit = dlsym (libgnutls, "gnutls_hmac_deinit"); + if (!gnutls_hmac_deinit) + { + g_warning ("Cannot use GHmac: failed to load gnutls_hmac_deinit: %s", dlerror ()); + return; + } + + gnutls_hmac = dlsym (libgnutls, "gnutls_hmac"); + if (!gnutls_hmac) + { + g_warning ("Cannot use GHmac: failed to load gnutls_hmac: %s", dlerror ()); + return; + } + + gnutls_hmac_output = dlsym (libgnutls, "gnutls_hmac_output"); + if (!gnutls_hmac_output) + { + g_warning ("Cannot use GHmac: failed to load gnutls_hmac_output: %s", dlerror ()); + return; + } + + gnutls_strerror = dlsym (libgnutls, "gnutls_strerror"); + if (!gnutls_strerror) + { + g_warning ("Cannot use GHmac: failed to load gnutls_strerror: %s", dlerror ()); + return; + } + + gnutls_initialize_successful = TRUE; +} + GHmac * g_hmac_new (GChecksumType digest_type, const guchar *key, gsize key_len) { gnutls_mac_algorithm_t algo; - GHmac *hmac = g_new0 (GHmac, 1); + GHmac *hmac; int ret; + if (g_once_init_enter (&gnutls_initialize_attempted)) + { + initialize_gnutls (); + g_once_init_leave (&gnutls_initialize_attempted, 1); + } + + if (!gnutls_initialize_successful) + return NULL; + + hmac = g_new0 (GHmac, 1); hmac->ref_count = 1; hmac->digest_type = digest_type; diff --git a/glib/ghmac.c b/glib/ghmac.c index 0e39ea40a..2d9be91b8 100644 --- a/glib/ghmac.c +++ b/glib/ghmac.c @@ -33,7 +33,7 @@ #include "gtypes.h" #include "glibintl.h" -#ifdef HAVE_GNUTLS +#ifdef USE_GNUTLS #error "build configuration error" #endif diff --git a/glib/meson.build b/glib/meson.build index 1f6bb35d1..f4041e50c 100644 --- a/glib/meson.build +++ b/glib/meson.build @@ -374,7 +374,7 @@ libglib = library('glib-2.0', # intl.lib is not compatible with SAFESEH link_args : [noseh_link_args, glib_link_flags, win32_ldflags], include_directories : configinc, - dependencies : [pcre, thread_dep, librt] + libgnutls_dep + libintl_deps + libiconv + platform_deps + [gnulib_libm_dependency, libm] + [libsysprof_capture_dep], + dependencies : [pcre, thread_dep, librt] + libintl_deps + libiconv + platform_deps + [gnulib_libm_dependency, libm] + [libsysprof_capture_dep] + [libdl_dep], c_args : glib_c_args, objc_args : glib_c_args, ) diff --git a/meson.build b/meson.build index 98f8925a2..bbe26e4e4 100644 --- a/meson.build +++ b/meson.build @@ -2095,11 +2095,9 @@ if host_system == 'linux' glib_conf.set('HAVE_LIBMOUNT', libmount_dep.found()) endif -# gnutls is used optionally by ghmac -libgnutls_dep = [] +# gnutls is used optionally by GHmac if get_option('gnutls') - libgnutls_dep = [dependency('gnutls', version : '>=3.6.9', required : true)] - glib_conf.set('HAVE_GNUTLS', 1) + glib_conf.set('USE_GNUTLS', 1) endif if host_system == 'windows' -- 2.31.1 From 54e7d0982c73692e92ddd8eaa328f2fbedf05688 Mon Sep 17 00:00:00 2001 From: Michael Catanzaro Date: Wed, 16 Jun 2021 20:46:24 -0500 Subject: [PATCH 4/4] Add test for GHmac in FIPS mode This will test a few problems that we hit recently: g_hmac_copy() is broken, https://bugzilla.redhat.com/show_bug.cgi?id=1786538 Crash in g_hmac_update() in FIPS mode, https://bugzilla.redhat.com/show_bug.cgi?id=1971533 Crash when passing -1 length to g_hmac_update() (discovered in #1971533) We'll also test to ensure MD5 fails, and stop compiling the other MD5 tests. --- glib/tests/hmac.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/glib/tests/hmac.c b/glib/tests/hmac.c index 3ac3206df..2fa447984 100644 --- a/glib/tests/hmac.c +++ b/glib/tests/hmac.c @@ -1,7 +1,10 @@ +#include "config.h" + #include #include #include +#ifndef USE_GNUTLS /* HMAC-MD5 test vectors as per RFC 2202 */ /* Test 1 */ @@ -81,6 +84,7 @@ guint8 key_md5_test7[] = { guint8 result_md5_test7[] = { 0x6f, 0x63, 0x0f, 0xad, 0x67, 0xcd, 0xa0, 0xee, 0x1f, 0xb1, 0xf5, 0x62, 0xdb, 0x3a, 0xa5, 0x3e }; +#endif /* HMAC-SHA1, HMAC-SHA256, HMAC-SHA384 and HMAC-SHA512 test vectors * as per RFCs 2202 and 4868. @@ -299,6 +303,7 @@ typedef struct { gconstpointer result; } HmacCase; +#ifndef USE_GNUTLS HmacCase hmac_md5_tests[] = { { G_CHECKSUM_MD5, key_md5_test1, 16, "Hi There", 8, result_md5_test1 }, { G_CHECKSUM_MD5, "Jefe", 4, "what do ya want for nothing?", 28, @@ -317,6 +322,7 @@ HmacCase hmac_md5_tests[] = { 73, result_md5_test7 }, { -1, NULL, 0, NULL, 0, NULL }, }; +#endif HmacCase hmac_sha1_tests[] = { { G_CHECKSUM_SHA1, key_sha_test1, 20, "Hi There", 8, result_sha1_test1 }, @@ -493,11 +499,45 @@ test_hmac_for_bytes (void) g_bytes_unref (data); } +#ifdef USE_GNUTLS +static void +test_gnutls_fips_mode (void) +{ + GHmac *hmac; + GHmac *copy; + + /* No MD5 in FIPS mode. */ + hmac = g_hmac_new (G_CHECKSUM_MD5, "abc123", sizeof ("abc123")); + g_assert_null (hmac); + + /* SHA-256 should be good. */ + hmac = g_hmac_new (G_CHECKSUM_SHA256, "abc123", sizeof ("abc123")); + g_assert_nonnull (hmac); + + /* Ensure g_hmac_update() does not crash when called with -1. */ + g_hmac_update (hmac, "You win again, gravity!", -1); + + /* Ensure g_hmac_copy() does not crash. */ + copy = g_hmac_copy (hmac); + g_assert_nonnull (hmac); + g_hmac_unref (hmac); + + g_assert_cmpstr (g_hmac_get_string (copy), ==, "795ba6900bcb22e8ce65c2ec02db4e85697da921deb960ee3143bf88a4a60f83"); + g_hmac_unref (copy); +} +#endif + int main (int argc, char **argv) { int i; + +#ifdef USE_GNUTLS + /* This has to happen before GnuTLS is dlopened. */ + g_setenv ("GNUTLS_FORCE_FIPS_MODE", "1", FALSE); +#endif + g_test_init (&argc, &argv, NULL); for (i = 0 ; hmac_sha1_tests[i].key_len > 0 ; i++) @@ -532,6 +572,7 @@ main (int argc, g_free (name); } +#ifndef USE_GNUTLS for (i = 0 ; hmac_md5_tests[i].key_len > 0 ; i++) { gchar *name = g_strdup_printf ("/hmac/md5-%d", i + 1); @@ -539,6 +580,7 @@ main (int argc, (void (*)(const void *)) test_hmac); g_free (name); } +#endif g_test_add_func ("/hmac/ref-unref", test_hmac_ref_unref); g_test_add_func ("/hmac/copy", test_hmac_copy); @@ -546,5 +588,9 @@ main (int argc, g_test_add_func ("/hmac/for-string", test_hmac_for_string); g_test_add_func ("/hmac/for-bytes", test_hmac_for_bytes); +#ifdef USE_GNUTLS + g_test_add_func ("/hmac/gnutls-fips-mode", test_gnutls_fips_mode); +#endif + return g_test_run (); } -- 2.31.1