From f52f413589ce4a1e698957767d490e46e95f75a1 Mon Sep 17 00:00:00 2001 From: Michael Catanzaro Date: Tue, 10 Nov 2020 16:52:14 -0600 Subject: [PATCH] Use GnuTLS to implement GHmac (thanks to Colin Walters) --- glib2.spec | 11 +- gnutls-hmac.patch | 668 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 678 insertions(+), 1 deletion(-) create mode 100644 gnutls-hmac.patch diff --git a/glib2.spec b/glib2.spec index f2f97d7..4e5f51e 100644 --- a/glib2.spec +++ b/glib2.spec @@ -1,6 +1,6 @@ Name: glib2 Version: 2.67.0 -Release: 2%{?dist} +Release: 3%{?dist} Summary: A library of handy utility functions License: LGPLv2+ @@ -10,6 +10,12 @@ Source0: http://download.gnome.org/sources/glib/2.67/glib-%{version}.tar.xz # https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1728 Patch0: 1728.patch +# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/903 +Patch1: gnutls-hmac.patch + +# For gnutls-hmac.patch +BuildRequires: pkgconfig(gnutls) + BuildRequires: chrpath BuildRequires: gcc BuildRequires: gcc-c++ @@ -217,6 +223,9 @@ glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || : %{_datadir}/installed-tests %changelog +* Tue Nov 10 2020 Michael Catanzaro - 2.67.0-3 +- Use GnuTLS to implement GHmac (thanks to Colin Walters) + * Wed Nov 04 2020 Michael Catanzaro - 2.67.0-2 - Backport fix for GSocketClient crash diff --git a/gnutls-hmac.patch b/gnutls-hmac.patch new file mode 100644 index 0000000..8860632 --- /dev/null +++ b/gnutls-hmac.patch @@ -0,0 +1,668 @@ +From afb5735506e2ed1c638a8c916aa3748bf0615f32 Mon Sep 17 00:00:00 2001 +From: Colin Walters +Date: Fri, 7 Jun 2019 18:44:43 +0000 +Subject: [PATCH 1/2] 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 aaf40a218..b3bf067c7 100644 +--- a/glib/meson.build ++++ b/glib/meson.build +@@ -253,6 +253,7 @@ glib_sources = files( + 'ggettext.c', + 'ghash.c', + 'ghmac.c', ++ 'ghmac-utils.c', + 'ghook.c', + 'ghostutils.c', + 'giochannel.c', +-- +2.28.0 + + +From 703e63f9d8b3ea4f26f41f0d2287b301025a73cc Mon Sep 17 00:00:00 2001 +From: Colin Walters +Date: Fri, 7 Jun 2019 19:36:54 +0000 +Subject: [PATCH 2/2] 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 to implement +g_hmac_copy() using gnutls_hmac_copy(), which didn't exist when Colin +developed this patch. I also decided it should be enabled by default. +--- + glib/gchecksum.c | 9 ++- + glib/gchecksumprivate.h | 32 ++++++++ + glib/ghmac-gnutls.c | 164 ++++++++++++++++++++++++++++++++++++++++ + glib/ghmac.c | 3 + + glib/meson.build | 10 ++- + meson.build | 7 ++ + meson_options.txt | 5 ++ + 7 files changed, 224 insertions(+), 6 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 f8a3f9ab8..b391a6264 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..f1a74a849 +--- /dev/null ++++ b/glib/ghmac-gnutls.c +@@ -0,0 +1,164 @@ ++/* 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_slice_new0 (GHmac); ++ 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_return_val_if_reached (NULL); ++ } ++ ++ gnutls_hmac_init (&hmac->hmac, algo, key, key_len); ++ ++ return hmac; ++} ++ ++GHmac * ++g_hmac_copy (const GHmac *hmac) ++{ ++ GHmac *copy; ++ ++ g_return_val_if_fail (hmac != NULL, NULL); ++ ++ copy = g_slice_new0 (GHmac); ++ copy->ref_count = 1; ++ copy->digest_type = hmac->digest_type; ++ copy->hmac = gnutls_hmac_copy (hmac->hmac); ++ ++ 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_slice_free (GHmac, hmac); ++ } ++} ++ ++ ++void ++g_hmac_update (GHmac *hmac, ++ const guchar *data, ++ gssize length) ++{ ++ g_return_if_fail (hmac != NULL); ++ g_return_if_fail (length == 0 || data != NULL); ++ ++ gnutls_hmac (hmac->hmac, data, length); ++} ++ ++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..c62d9ce4e 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 +diff --git a/glib/meson.build b/glib/meson.build +index b3bf067c7..3cdc3b573 100644 +--- a/glib/meson.build ++++ b/glib/meson.build +@@ -252,7 +252,6 @@ glib_sources = files( + 'gfileutils.c', + 'ggettext.c', + 'ghash.c', +- 'ghmac.c', + 'ghmac-utils.c', + 'ghook.c', + 'ghostutils.c', +@@ -308,6 +307,7 @@ glib_sources = files( + 'guriprivate.h', + 'gutils.c', + 'gutilsprivate.h', ++ 'gchecksumprivate.h', + 'guuid.c', + 'gvariant.c', + 'gvariant-core.c', +@@ -352,6 +352,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 +@@ -378,7 +384,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_deps + [thread_dep, librt] + libintl_deps + libiconv + platform_deps + gnulib_libm_dependency + [libsysprof_capture_dep], ++ dependencies : pcre_deps + libgnutls_dep + [thread_dep, librt] + libintl_deps + libiconv + platform_deps + gnulib_libm_dependency + [libsysprof_capture_dep], + c_args : glib_c_args, + objc_args : glib_c_args, + ) +diff --git a/meson.build b/meson.build +index e0b308a25..70dd5355e 100644 +--- a/meson.build ++++ b/meson.build +@@ -2056,6 +2056,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 af9645eda..2c4b2c37e 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 : true, ++ description : 'build with gnutls support') ++ + option('internal_pcre', + type : 'boolean', + value : false, +-- +2.28.0