diff options
author | Colin Walters <walters@verbum.org> | 2019-06-07 19:36:54 +0000 |
---|---|---|
committer | Michael Catanzaro <mcatanzaro@redhat.com> | 2021-07-01 11:29:27 -0500 |
commit | 33d87c121432d2948f1b82f7aa6f37ee0bb3a2e5 (patch) | |
tree | 9e6c993fc34d151d2f907be5e548ade0f3097940 | |
parent | 5c7c55ac6bbf94d9fd23819b14f75c87851d309d (diff) | |
download | glib-33d87c121432d2948f1b82f7aa6f37ee0bb3a2e5.tar.gz |
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
-rw-r--r-- | glib/gchecksum.c | 9 | ||||
-rw-r--r-- | glib/gchecksumprivate.h | 32 | ||||
-rw-r--r-- | glib/ghmac-gnutls.c | 187 | ||||
-rw-r--r-- | glib/ghmac.c | 15 | ||||
-rw-r--r-- | glib/meson.build | 10 | ||||
-rw-r--r-- | meson.build | 7 | ||||
-rw-r--r-- | meson_options.txt | 7 |
7 files changed, 260 insertions, 7 deletions
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 <string.h> -#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 <http://www.gnu.org/licenses/>. + */ + +#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 <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <string.h> +#include <gnutls/crypto.h> + +#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') |