Page MenuHome GnuPG

libgcrypt-1.10.2: getrandom() is not available everywhere
Closed, ResolvedPublic

Description

libgcrypt-1.10.2 fails to build on systems where getrandom(2) is not availalble, like macOS (aka Darwin) or NetBSD 9.

getrandom() presence should be detected by configure and the function must be used conditionally.

My set of quick patches:

--- configure.orig	2023-04-06 19:07:18.000000000 +0000
+++ configure
@@ -18809,7 +18809,7 @@ _ACEOF
 fi
 done
 
-for ac_func in explicit_bzero explicit_memset getentropy
+for ac_func in explicit_bzero explicit_memset getentropy getrandom
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
--- config.h.in.orig	2023-04-07 08:54:23.000000000 +0000
+++ config.h.in
@@ -294,6 +294,9 @@
 /* Define to 1 if you have the `getpid' function. */
 #undef HAVE_GETPID
 
+/* Define to 1 if you have the `getrandom' function. */
+#undef HAVE_GETRANDOM
+
 /* Define to 1 if you have the `getrusage' function. */
 #undef HAVE_GETRUSAGE
--- random/rndgetentropy.c.orig	2023-04-07 08:56:42.000000000 +0000
+++ random/rndgetentropy.c
@@ -81,6 +81,7 @@ _gcry_rndgetentropy_gather_random (void 
       do
         {
           _gcry_pre_syscall ();
+#ifdef HAVE_GETRANDOM
           if (fips_mode ())
             {
               /* DRBG chaining defined in SP 800-90A (rev 1) specify
@@ -98,6 +99,7 @@ _gcry_rndgetentropy_gather_random (void 
               ret = getrandom (buffer, nbytes, GRND_RANDOM);
             }
           else
+#endif
             {
               nbytes = length < sizeof (buffer) ? length : sizeof (buffer);
               ret = getentropy (buffer, nbytes);

Details

Version
1.10.2

Event Timeline

I just ran into this issue while attempting to update the MacPorts Portfile to version 1.10.2.

The patch works of me!

werner added projects: libgcrypt, MacOS.

I just ran into this, too, on macOS.

Here is the git diff that I used:

diff --git a/configure.ac b/configure.ac
index 24ec2eb4..702ef0a5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2201,7 +2201,7 @@ AC_CHECK_FUNCS(strtoul memmove stricmp atexit raise)
 AC_CHECK_FUNCS(strerror rand mmap getpagesize sysconf waitpid wait4)
 AC_CHECK_FUNCS(gettimeofday getrusage gethrtime clock_gettime syslog)
 AC_CHECK_FUNCS(syscall fcntl ftruncate flockfile getauxval elf_aux_info)
-AC_CHECK_FUNCS(explicit_bzero explicit_memset getentropy)
+AC_CHECK_FUNCS(explicit_bzero explicit_memset getentropy getrandom)
 
 GNUPG_CHECK_MLOCK
 
diff --git a/random/rndgetentropy.c b/random/rndgetentropy.c
index 513da0b9..5acf91c1 100644
--- a/random/rndgetentropy.c
+++ b/random/rndgetentropy.c
@@ -81,6 +81,7 @@ _gcry_rndgetentropy_gather_random (void (*add)(const void*, size_t,
       do
         {
           _gcry_pre_syscall ();
+#ifdef HAVE_GETRANDOM
           if (fips_mode ())
             {
               /* DRBG chaining defined in SP 800-90A (rev 1) specify
@@ -98,6 +99,7 @@ _gcry_rndgetentropy_gather_random (void (*add)(const void*, size_t,
               ret = getrandom (buffer, nbytes, GRND_RANDOM);
             }
           else
+#endif
             {
               nbytes = length < sizeof (buffer) ? length : sizeof (buffer);
               ret = getentropy (buffer, nbytes);

Thank you for the report.

The specific use of getrandom is introduced in rCaab1d63e4def: random: Use getrandom (GRND_RANDOM) in FIPS mode.
and it's for specific Linux kernel, which only makes sense with GRND_RANDOM.

I think that the change can be simpler (no detection of getrandom is needed).

From fa21ddc158b5d7b5900856e5b131071302217a51 Mon Sep 17 00:00:00 2001
From: NIIBE Yutaka <gniibe@fsij.org>
Date: Mon, 10 Apr 2023 11:45:00 +0900
Subject: [PATCH] random: Use getrandom only when it's appropriate.

* random/rndgetentropy.c (_gcry_rndgetentropy_gather_random)
[GRND_RANDOM]: Conditionalize the use of getrandom, as it's
not a portable function.

--

Fixes-commit: aab1d63e4def41593312f76de016c885ffafecde
GnuPG-bug-id: 6442
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
---
 random/rndgetentropy.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/random/rndgetentropy.c b/random/rndgetentropy.c
index 513da0b9..a6f3c4ab 100644
--- a/random/rndgetentropy.c
+++ b/random/rndgetentropy.c
@@ -81,6 +81,7 @@ _gcry_rndgetentropy_gather_random (void (*add)(const void*, size_t,
       do
         {
           _gcry_pre_syscall ();
+#ifdef GRND_RANDOM
           if (fips_mode ())
             {
               /* DRBG chaining defined in SP 800-90A (rev 1) specify
@@ -98,6 +99,7 @@ _gcry_rndgetentropy_gather_random (void (*add)(const void*, size_t,
               ret = getrandom (buffer, nbytes, GRND_RANDOM);
             }
           else
+#endif
             {
               nbytes = length < sizeof (buffer) ? length : sizeof (buffer);
               ret = getentropy (buffer, nbytes);
-- 
2.39.1

Please test.

Tested. I applied the above diff to libgcrypt-1.10.2, and it builds and runs.

gniibe changed the task status from Open to Testing.Apr 10 2023, 7:18 AM
gniibe claimed this task.

What about

         if (fips_mode ())
           {
             /* DRBG chaining defined in SP 800-90A (rev 1) specify  */
#ifdef GRND_RANDOM
             ret = getrandom (buffer, nbytes, GRND_RANDOM);
#else
             ret = -1;
             gpg_err_set_errno (ENOSYS);
#endif
           }

which makes it clear that FIPS mode is not possible.

What Werner wrote was also my thought. If getrandom is mandatory for FIPS, then it must not be possible to disable it silently.

Returning ENOSYS is too strict, in my opinion; Because the code in question doesn't work for machines other than CentOS/Fedora/RHEL. For other machines, it would be natural to just rely on getentropy (rather standard call).

IIUC, calling getrandom with GRND_RANDOM for FIPS is a kind of os/kernel specific kluge, so that os/kernel can make sure to run correctly under FIPS mode (for that specific os/kernel).
Original semantics defined for GRND_RANDOM was use of /dev/random (instead of /dev/urandom). But that semantics has been changed multiple times, in the history of kernel changes.
See https://www.theregister.com/2022/03/21/new_linux_kernel_has_improved/

The specific change in kernel is this one:
https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/commit/c72f53614e08d1dd53651292f19c5db7cc8b2bf3

Please see the line Upstream Status: RHEL only in the change.

Returning ENOSYS is too strict, in my opinion; It doesn't work for machines other than CentOS/Fedora/RHEL.

Isn't ENOSYS in POSIX?

ENOSYS is POSIX. My point is that: getrandom was introduced in Linux kernel with flags for particular purpose (differentiate use of /dev/random and /dev/urandom), but that feature has gone.
But, for FIPS behavior, RHEL and related OS use (possibly, some would say misuse) getrandom with GRND_RANDOM. This use is RHEL specific (not for other GNU/Linux). Use of getrandom is non-POSIX.

I see, your issue is with the use of getrandom for FIPS. I understand now.

Actually Linux already returns ENOSYS on older kernels where there is no getrandom libc call. Thus returning ENOSYS if we don't have the libc version of that syscall (i.e. getrandom) in FIPS mode seems to be the Right Thing to do. My whole comment was about fips mode - it does not make much sense to enable FIPS mode if the system is not appropriate for it.

It is a bit complicated. Let me describe the situation.

First of all, returning ENOSYS here makes rndgetentropy module useless for other POSIX system in FIPS mode, as it results log_fatal with "getentropy is not supported:".
I think that it is good for other POSIX system to support possibility of using libgcrypt in FIPS mode.

Second of all, use of getrandom in this way (in FIPS mode) is RHEL/CentOS/Fedora specific.

It is repurposed; getrandom system call was introduced to provide fine control by flags, but not for FIPS mode or not. Then, the use of flags was nullified because /dev/random now behaves as same as /dev/urandom and the change of initialization of entropy pool handling. It resulted getrandom no-use (it makes getrandom equal to getentropy). After that, the idea of using getrandom with flags for FIPS mode comes in. (And there are no consensus for this usage among GNU/Linux, yet.)

In GNU C library, getrandom and getentropy (as well as its flags: GRND_RANDOM and GRND_NONBLOCK) were introduced at the same time.

In libgcrypt, there were support of getrandom system call even with older GNU C library with no getrandom function, in rndoldlinux module (originally named rndlinux module). For system with older libc (but newer kernel with getrandom system call), it can be used as an option.

rndgetentropy module is introduced in libgcrypt 1.10. That's because rndlinux module being complicated to support many other OSes other than one with Linux kernel, and getentropy becomes the portable standard method.

But again, to support RHEL specific use, rndgetentropy module now has getrandom call.

This problem was introduced by commit cf10c74bd9d5aa80798f1c0e23a9126f381b26b3. Perhaps that change should be backed out in the interim so that a portable fix can be considered for the original issue?

I don't see a reason backing off the original commit. A fix for macOS is now available (rCfa21ddc158b5) and will be in the next release. No reason for other changes.