mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-02-22 18:22:11 +01:00
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
This commit is contained in:
parent
9ef5cacbf9
commit
3ef71255bb
@ -22,7 +22,7 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "gchecksum.h"
|
||||
#include "gchecksumprivate.h"
|
||||
|
||||
#include "gslice.h"
|
||||
#include "gmem.h"
|
||||
@ -176,9 +176,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;
|
||||
@ -197,6 +197,7 @@ digest_to_string (guint8 *digest,
|
||||
|
||||
return retval;
|
||||
}
|
||||
#define digest_to_string gchecksum_digest_to_string
|
||||
|
||||
/*
|
||||
* MD5 Checksum
|
||||
|
32
glib/gchecksumprivate.h
Normal file
32
glib/gchecksumprivate.h
Normal file
@ -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
|
187
glib/ghmac-gnutls.c
Normal file
187
glib/ghmac-gnutls.c
Normal file
@ -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);
|
||||
}
|
15
glib/ghmac.c
15
glib/ghmac.c
@ -35,6 +35,9 @@
|
||||
#include "gtypes.h"
|
||||
#include "glibintl.h"
|
||||
|
||||
#ifdef HAVE_GNUTLS
|
||||
#error "build configuration error"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* GHmac:
|
||||
@ -89,6 +92,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: (nullable) (transfer full): the newly created #GHmac, or %NULL.
|
||||
* Use g_hmac_unref() to free the memory allocated by it.
|
||||
*
|
||||
|
@ -288,7 +288,6 @@ glib_sources += files(
|
||||
'gfileutils.c',
|
||||
'ggettext.c',
|
||||
'ghash.c',
|
||||
'ghmac.c',
|
||||
'ghmac-utils.c',
|
||||
'ghook.c',
|
||||
'ghostutils.c',
|
||||
@ -342,6 +341,8 @@ glib_sources += files(
|
||||
'gunidecomp.c',
|
||||
'guri.c',
|
||||
'gutils.c',
|
||||
'gutilsprivate.h',
|
||||
'gchecksumprivate.h',
|
||||
'guuid.c',
|
||||
'gvariant.c',
|
||||
'gvariant-core.c',
|
||||
@ -401,6 +402,12 @@ else
|
||||
glib_dtrace_hdr = []
|
||||
endif
|
||||
|
||||
if get_option('gnutls')
|
||||
glib_sources += files('ghmac-gnutls.c')
|
||||
else
|
||||
glib_sources += files('ghmac.c')
|
||||
endif
|
||||
|
||||
pcre2_static_args = []
|
||||
|
||||
if use_pcre2_static_flag
|
||||
@ -422,6 +429,7 @@ libglib = library('glib-2.0',
|
||||
dependencies : [
|
||||
atomic_dep,
|
||||
gnulib_libm_dependency,
|
||||
libgnutls_dep,
|
||||
libiconv,
|
||||
libintl_deps,
|
||||
libm,
|
||||
|
@ -2324,6 +2324,13 @@ if host_system == 'linux'
|
||||
endif
|
||||
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')
|
||||
else
|
||||
|
@ -37,6 +37,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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user