You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1086 lines
32 KiB
1086 lines
32 KiB
From ff90bb8474b1e724727f4014b446e7c851e609bd Mon Sep 17 00:00:00 2001 |
|
From: Colin Walters <walters@verbum.org> |
|
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 <http://www.gnu.org/licenses/>. |
|
+ */ |
|
+ |
|
+#include "config.h" |
|
+ |
|
+#include <string.h> |
|
+ |
|
+#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 8c18e6de4..329b8d197 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.31.1 |
|
|
|
From 5395d36e6685e0b7377794c59c5820970bb472ef Mon Sep 17 00:00:00 2001 |
|
From: Colin Walters <walters@verbum.org> |
|
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 <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 329b8d197..2417de53d 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, libm] + [libsysprof_capture_dep], |
|
+ dependencies : pcre_deps + [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 e2eba1871..cca15f653 100644 |
|
--- a/meson.build |
|
+++ b/meson.build |
|
@@ -2090,6 +2090,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 072765361..c8f26ac02 100644 |
|
--- a/meson_options.txt |
|
+++ b/meson_options.txt |
|
@@ -39,6 +39,11 @@ option('internal_pcre', |
|
value : false, |
|
description : 'whether to use internal PCRE') |
|
|
|
+option('gnutls', |
|
+ type : 'boolean', |
|
+ value : false, |
|
+ description : 'build with gnutls support') |
|
+ |
|
option('man', |
|
type : 'boolean', |
|
value : false, |
|
-- |
|
2.31.1 |
|
|
|
From 61c175277acb8d1e080305acd444201c5ad1fb81 Mon Sep 17 00:00:00 2001 |
|
From: Michael Catanzaro <mcatanzaro@redhat.com> |
|
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 <dlfcn.h> |
|
#include <string.h> |
|
-#include <gnutls/crypto.h> |
|
|
|
#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 2417de53d..ba42951aa 100644 |
|
--- a/glib/meson.build |
|
+++ b/glib/meson.build |
|
@@ -384,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] + libgnutls_dep + libintl_deps + libiconv + platform_deps + [gnulib_libm_dependency, libm] + [libsysprof_capture_dep], |
|
+ dependencies : pcre_deps + [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 cca15f653..404ef1790 100644 |
|
--- a/meson.build |
|
+++ b/meson.build |
|
@@ -2090,11 +2090,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 7d1d96311b6ecd4f90ebbdd6fc58d28e06a86887 Mon Sep 17 00:00:00 2001 |
|
From: Michael Catanzaro <mcatanzaro@redhat.com> |
|
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 <glib.h> |
|
#include <string.h> |
|
#include <stdlib.h> |
|
|
|
+#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
|
|
|