From 440a178c5aad19050a3d5b5d76881931138af680 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/Makefile.am | 1 + glib/ghmac-utils.c | 145 +++++++++++++++++++++++++++++++++++++++++++++ glib/ghmac.c | 112 ---------------------------------- glib/meson.build | 1 + 4 files changed, 147 insertions(+), 112 deletions(-) create mode 100644 glib/ghmac-utils.c diff --git a/glib/Makefile.am b/glib/Makefile.am index 8da549c7f..c367b09ad 100644 --- a/glib/Makefile.am +++ b/glib/Makefile.am @@ -126,6 +126,7 @@ libglib_2_0_la_SOURCES = \ ggettext.c \ ghash.c \ ghmac.c \ + ghmac-utils.c \ ghook.c \ ghostutils.c \ giochannel.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 9b58fd81c..7db38e34a 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 9df77b6f9..c7f28b5b6 100644 --- a/glib/meson.build +++ b/glib/meson.build @@ -138,6 +138,7 @@ glib_sources = files( 'ggettext.c', 'ghash.c', 'ghmac.c', + 'ghmac-utils.c', 'ghook.c', 'ghostutils.c', 'giochannel.c', -- 2.21.0 From 423355787ba9133b310c0b72708024b1428d7d14 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. However, a fun wrinkle is that the GnuTLS HMAC API doesn't expose the necessary bits to implement `g_hmac_copy()`; OpenSSL does. I chose to just make that abort for now since I didn't find apps using it. --- glib/Makefile.am | 9 ++- glib/gchecksum.c | 9 +-- glib/gchecksumprivate.h | 32 +++++++++ glib/ghmac-gnutls.c | 151 ++++++++++++++++++++++++++++++++++++++++ glib/ghmac.c | 1 + glib/meson.build | 10 ++- glib/tests/hmac.c | 6 ++ meson.build | 7 ++ meson_options.txt | 5 ++ 9 files changed, 221 insertions(+), 9 deletions(-) create mode 100644 glib/gchecksumprivate.h create mode 100644 glib/ghmac-gnutls.c diff --git a/glib/Makefile.am b/glib/Makefile.am index c367b09ad..b0a721ad0 100644 --- a/glib/Makefile.am +++ b/glib/Makefile.am @@ -125,7 +125,7 @@ libglib_2_0_la_SOURCES = \ gfileutils.c \ ggettext.c \ ghash.c \ - ghmac.c \ + ghmac-gnutls.c \ ghmac-utils.c \ ghook.c \ ghostutils.c \ @@ -352,11 +352,14 @@ pcre_lib = pcre/libpcre.la pcre_inc = endif -libglib_2_0_la_CFLAGS = $(AM_CFLAGS) $(GLIB_HIDDEN_VISIBILITY_CFLAGS) $(LIBSYSTEMD_CFLAGS) +gnutls_libs = $(shell pkg-config --libs gnutls) +gnutls_cflags = $(shell pkg-config --cflags gnutls) + +libglib_2_0_la_CFLAGS = $(AM_CFLAGS) $(GLIB_HIDDEN_VISIBILITY_CFLAGS) $(LIBSYSTEMD_CFLAGS) $(gnutls_cflags) libglib_2_0_la_LIBADD = libcharset/libcharset.la $(printf_la) @GIO@ @GSPAWN@ @PLATFORMDEP@ @ICONV_LIBS@ @G_LIBS_EXTRA@ $(pcre_lib) $(G_THREAD_LIBS_EXTRA) $(G_THREAD_LIBS_FOR_GTHREAD) $(LIBSYSTEMD_LIBS) libglib_2_0_la_DEPENDENCIES = libcharset/libcharset.la $(printf_la) @GIO@ @GSPAWN@ @PLATFORMDEP@ $(glib_win32_res) $(glib_def) -libglib_2_0_la_LDFLAGS = $(GLIB_LINK_FLAGS) \ +libglib_2_0_la_LDFLAGS = $(GLIB_LINK_FLAGS) $(gnutls_libs) \ $(glib_win32_res_ldflag) \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ -export-dynamic $(no_undefined) diff --git a/glib/gchecksum.c b/glib/gchecksum.c index 40b1d50e2..2f59d4a66 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) { gint len = digest_len * 2; gint i; @@ -195,6 +195,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..3b4dfb872 --- /dev/null +++ b/glib/ghmac-gnutls.c @@ -0,0 +1,151 @@ +/* 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" + +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) +{ + g_error ("g_hmac_copy is not available with GnuTLS-backend GHmac"); +} + +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 7db38e34a..b12eb07c4 100644 --- a/glib/ghmac.c +++ b/glib/ghmac.c @@ -33,6 +33,7 @@ #include "gtypes.h" #include "glibintl.h" +#error "build configuration error" /** * SECTION:hmac diff --git a/glib/meson.build b/glib/meson.build index c7f28b5b6..a2f9da81c 100644 --- a/glib/meson.build +++ b/glib/meson.build @@ -137,7 +137,6 @@ glib_sources = files( 'gfileutils.c', 'ggettext.c', 'ghash.c', - 'ghmac.c', 'ghmac-utils.c', 'ghook.c', 'ghostutils.c', @@ -185,6 +184,7 @@ glib_sources = files( 'gunidecomp.c', 'gurifuncs.c', 'gutils.c', + 'gchecksumprivate.h', 'guuid.c', 'gvariant.c', 'gvariant-core.c', @@ -222,6 +222,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 @@ -238,7 +244,7 @@ libglib = library('glib-2.0', link_args : platform_ldflags + noseh_link_args, include_directories : configinc, link_with : [charset_lib, gnulib_lib], - dependencies : [pcre, thread_dep, libintl, librt] + libiconv + platform_deps, + dependencies : [pcre, thread_dep, libintl, librt] + libiconv + platform_deps + libgnutls_dep, c_args : ['-DG_LOG_DOMAIN="GLib"', '-DGLIB_COMPILATION'] + pcre_static_args + glib_hidden_visibility_args ) diff --git a/glib/tests/hmac.c b/glib/tests/hmac.c index 3ac3206df..5212c2523 100644 --- a/glib/tests/hmac.c +++ b/glib/tests/hmac.c @@ -1,3 +1,5 @@ +#include "config.h" + #include #include #include @@ -427,6 +429,9 @@ test_hmac_ref_unref (void) static void test_hmac_copy (void) { +#ifdef HAVE_GNUTLS + g_test_skip ("No g_hmac_copy with gnutls"); +#else GHmac *hmac, *check; hmac = g_hmac_new (G_CHECKSUM_SHA256, (guchar*)"aaa", 3); @@ -435,6 +440,7 @@ test_hmac_copy (void) g_assert_cmpstr (g_hmac_get_string (hmac), ==, g_hmac_get_string (check)); g_hmac_unref (check); g_hmac_unref (hmac); +#endif } static void diff --git a/meson.build b/meson.build index 0cefee51d..81b16b004 100644 --- a/meson.build +++ b/meson.build @@ -1596,6 +1596,13 @@ if host_system == 'linux' and get_option('libmount') libmount_dep = [dependency('mount', version : '>=2.23', required : true)] endif +# gnutls is used optionally by ghmac +libgnutls_dep = [] +if get_option('gnutls') + libgnutls_dep = [dependency('gnutls', version : '>=3.6.7', 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 4504c6858..d18c42a36 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -34,6 +34,11 @@ option('libmount', value : true, description : 'build with libmount support') +option('gnutls', + type : 'boolean', + value : false, + description : 'build with gnutls support') + option('internal_pcre', type : 'boolean', value : false, -- 2.21.0