Hello Werner, all.

Steffen Nurpmeso wrote in
<20200529155411.tgyu1%stef...@sdaoden.eu>:
 |Werner Koch wrote in
 |<87sgfjrqf1....@wheatstone.g10code.de>:
 ||On Thu, 28 May 2020 14:43, Steffen Nurpmeso said:
 ...
 |out for NAME_OF_DEV_*RANDOM at all .. hmm .. i must admit
 |random/rndlinux.c:_gcry_rndlinux_gather_random() seems strange to
 |me.  :)  Two possible calls to getpid, could be "((apid = XPID) !=
 ...
 |I still would not do it like that, because if software cannot rely
 ...
 |Anyhow, unless i am mistaken from this five minute looking, that
 |random/random-csprng.c:getfnc_gather_random()
 |
 |  #if USE_RNDLINUX
 |    if ( !access (NAME_OF_DEV_RANDOM, R_OK)
 |         && !access (NAME_OF_DEV_URANDOM, R_OK))
 |      {
 |        fnc = _gcry_rndlinux_gather_random;
 |        return fnc;
 |}
 |  #endif
 |
 |i would change, maybe with a new call-in to rndlinux.c which
 |should be made responsible for Linux-only environmental detections
 |imho.  Like that it could solely depend on getrandom, and make all
 |the FDs optional, maybe by testing for NOSYS with a one byte read
 |or what at first, or by later aborting if collecting random fails
 |if that is possible.  (For my MUA i use this for seeding only
 |anyhow.)
 |
 ||Are you running in FIPS mode?
 ||
 ||Can you run the Libgcrypt test suite?  In particular
 ||
 ||$ libgcrypt/tests/version
 ||$ libgcrypt/tests/random --verbose --debug

So with the attached patch libgcrypt solely relies upon getentropy
if available, no FD handling is done no more if at all possible.
The test suite passes, a short review makes me think it is alright.

- The setup could block when the OS cannot serve 1 byte of strong
  entropy.  This is different to before, access(2) does not.

  (On the other hand neither on OpenBSD nor on newer Linux (5.4 or
  5.6 i think) this should matter.  And it is likely it does not
  elsewhere, either people seem to have used things like my
  entropy-saver or even hammers like haveged which reveal how
  strange entropy counting was, imho.)

- Some tests aka code places directly reach into
  _gcry_rndlinux_gather_random() and thus only give errors in
  open_device() not the warning that initiated this ML thread.
  This i did not get at first, the tests suite passed nonetheless.

- P.S.: even if this patch is not used, i would suggest an audit
  of this file.

- RANDOM_CONF_ONLY_URANDOM lost its meaning in the past, and this
  patch does not reinstantiate that.  It cannot be done portably,
  except for OSs which provide getrandom(2).

- I shortly thought about using "extern", but i think doing so in
  an isolated fashion is surely wrong.

Ciao,

--steffen
|
|Der Kragenbaer,                The moon bear,
|der holt sich munter           he cheerfully and one by one
|einen nach dem anderen runter  wa.ks himself off
|(By Robert Gernhardt)
commit d37a0f8e (HEAD -> refs/heads/master)
Author:     Steffen Nurpmeso <stef...@sdaoden.eu>
AuthorDate: 2020-05-29 21:44:59 +0200
Commit:     Steffen Nurpmeso <stef...@sdaoden.eu>
CommitDate: 2020-05-29 22:03:43 +0200

    random/rndlinux.c: avoid redundant actions on FDs if possible
---
 random/rand-internal.h |   5 +-
 random/random-csprng.c |   3 +-
 random/random-drbg.c   |   4 +-
 random/rndlinux.c      | 216 ++++++++++++++++++++++++++++---------------------
 4 files changed, 133 insertions(+), 95 deletions(-)

diff --git a/random/rand-internal.h b/random/rand-internal.h
index d99c6671..4e1298c1 100644
--- a/random/rand-internal.h
+++ b/random/rand-internal.h
@@ -90,10 +90,13 @@ void _gcry_rngsystem_randomize (void *buffer, size_t length,
 
 
 /*-- rndlinux.c --*/
+#if USE_RNDLINUX
+int _gcry_rndlinux_setup (void);
 int _gcry_rndlinux_gather_random (void (*add) (const void *, size_t,
                                                enum random_origins),
-                                   enum random_origins origin,
+                                  enum random_origins origin,
                                   size_t length, int level);
+#endif
 
 /*-- rndunix.c --*/
 int _gcry_rndunix_gather_random (void (*add) (const void *, size_t,
diff --git a/random/random-csprng.c b/random/random-csprng.c
index b06810a0..55557ae8 100644
--- a/random/random-csprng.c
+++ b/random/random-csprng.c
@@ -1120,8 +1120,7 @@ getfnc_gather_random (void))(void (*)(const void*, size_t,
              enum random_origins, size_t, int);
 
 #if USE_RNDLINUX
-  if ( !access (NAME_OF_DEV_RANDOM, R_OK)
-       && !access (NAME_OF_DEV_URANDOM, R_OK))
+  if (_gcry_rndlinux_setup () != -1)
     {
       fnc = _gcry_rndlinux_gather_random;
       return fnc;
diff --git a/random/random-drbg.c b/random/random-drbg.c
index 6124f5fb..7f63fade 100644
--- a/random/random-drbg.c
+++ b/random/random-drbg.c
@@ -1865,11 +1865,11 @@ _gcry_rngdrbg_reinit (const char *flagstr, gcry_buffer_t *pers, int npers)
 void
 _gcry_rngdrbg_close_fds (void)
 {
-#if USE_RNDLINUX
   drbg_lock ();
+#if USE_RNDLINUX
   _gcry_rndlinux_gather_random (NULL, 0, 0, 0);
-  drbg_unlock ();
 #endif
+  drbg_unlock ();
 }
 
 /* Print some statistics about the RNG.  */
diff --git a/random/rndlinux.c b/random/rndlinux.c
index 04e2a464..c710d368 100644
--- a/random/rndlinux.c
+++ b/random/rndlinux.c
@@ -18,7 +18,6 @@
  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-
 #include <config.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -32,21 +31,27 @@
 #include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
-#if defined(__linux__) || !defined(HAVE_GETENTROPY)
-#ifdef HAVE_SYSCALL
-# include <sys/syscall.h>
-# ifdef __NR_getrandom
-# define getentropy(buf,buflen) syscall (__NR_getrandom, buf, buflen, 0)
+
+#undef HAVE_GETRANDOM_SYSCALL
+#if !defined(HAVE_GETENTROPY)
+# if defined(__linux__) defined(HAVE_SYSCALL)
+#  include <sys/syscall.h>
+#  ifdef __NR_getrandom
+#   define HAVE_GETENTROPY
+#   define HAVE_GETRANDOM_SYSCALL
+#   define getentropy(buf,buflen) syscall (__NR_getrandom, buf, buflen, 0)
+#  endif
 # endif
 #endif
-#endif
 
 #include "types.h"
 #include "g10lib.h"
 #include "rand-internal.h"
 
-static int open_device (const char *name, int retry);
+static int a_rndlinux_have_getentropy;
 
+static int set_cloexec_flag (int fd);
+static int open_device (const char *name, int retry);
 
 static int
 set_cloexec_flag (int fd)
@@ -60,8 +65,6 @@ set_cloexec_flag (int fd)
   return fcntl (fd, F_SETFD, oldflags);
 }
 
-
-
 /*
  * Used to open the /dev/random devices (Linux, xBSD, Solaris (if it
  * exists)).  If RETRY is true, the function does not terminate with
@@ -107,6 +110,43 @@ open_device (const char *name, int retry)
   return fd;
 }
 
+int
+_gcry_rndlinux_setup (void)
+{
+  int rv = 0;
+
+#if HAVE_GETENTROPY
+  if (!a_rndlinux_have_getentropy)
+    {
+      char buf[8];
+      long ret;
+
+      do
+        {
+          _gcry_pre_syscall ();
+          ret =
+# ifdef HAVE_GETRANDOM_SYSCALL
+                syscall (__NR_getrandom, buf, 1, GRND_NONBLOCK
+# else
+                getentropy (buf, 1
+# endif
+                );
+          _gcry_post_syscall ();
+        }
+      while (ret == -1 && errno == EINTR);
+
+      a_rndlinux_have_getentropy = (ret != -1 || errno != ENOSYS) ? ++rv : -1;
+    }
+  else
+    rv = (a_rndlinux_have_getentropy != -1);
+#endif /* HAVE_GETENTROPY */
+
+  if (!rv && !access (NAME_OF_DEV_RANDOM, R_OK)
+      && !access (NAME_OF_DEV_URANDOM, R_OK))
+    ++rv;
+
+  return --rv;
+}
 
 /* Note that the caller needs to make sure that this function is only
  * called by one thread at a time.  The function returns 0 on success
@@ -137,55 +177,55 @@ _gcry_rndlinux_gather_random (void (*add)(const void*, size_t,
   int any_need_entropy = 0;
   int delay;
 
-  /* On the first call read the conf file to check whether we want to
-   * use only urandom.  */
-  if (only_urandom == -1)
+  /* XXX Some modules directly address this function whether usable or not */
+  if (!a_rndlinux_have_getentropy)
+    _gcry_rndlinux_setup ();
+
+  if (a_rndlinux_have_getentropy != -1)
     {
-      my_pid = getpid ();
-      if ((_gcry_random_read_conf () & RANDOM_CONF_ONLY_URANDOM))
-        only_urandom = 1;
-      else
-        only_urandom = 0;
+      /* Special destructor call? */
+      if (!add)
+        return 0;
     }
-
-  if (!add)
+  else
     {
-      /* Special mode to close the descriptors.  */
-      if (fd_random != -1)
+      /* Detect a fork and close the devices so that we do not use the old
+       * file descriptors.  Note that open_device will be called in retry
+       * mode if the devices was opened by the parent process.
+       * !add is the special destructor call */
+      if (!add || (apid = getpid ()) != my_pid)
         {
-          close (fd_random);
-          fd_random = -1;
-        }
-      if (fd_urandom != -1)
-        {
-          close (fd_urandom);
-          fd_urandom = -1;
-        }
+          if (fd_random != -1)
+            {
+              close (fd_random);
+              fd_random = -1;
+            }
+          if (fd_urandom != -1)
+            {
+              close (fd_urandom);
+              fd_urandom = -1;
+            }
 
-      _gcry_rndjent_fini ();
-      return 0;
-    }
+          if (!add)
+            {
+              _gcry_rndjent_fini ();
+              return 0;
+            }
 
-  /* Detect a fork and close the devices so that we don't use the old
-   * file descriptors.  Note that open_device will be called in retry
-   * mode if the devices was opened by the parent process.  */
-  apid = getpid ();
-  if (my_pid != apid)
-    {
-      if (fd_random != -1)
-        {
-          close (fd_random);
-          fd_random = -1;
+          my_pid = apid;
         }
-      if (fd_urandom != -1)
+
+      /* On the first call read the conf file to check whether we want to
+       * use only urandom.  XXX Does not play well with getrandom */
+      if (only_urandom == -1)
         {
-          close (fd_urandom);
-          fd_urandom = -1;
+          if ((_gcry_random_read_conf () & RANDOM_CONF_ONLY_URANDOM))
+            only_urandom = 1;
+          else
+            only_urandom = 0;
         }
-      my_pid = apid;
     }
 
-
   /* First read from a hardware source.  However let it account only
      for up to 50% (or 25% for RDRAND) of the requested bytes.  */
   n_hw = _gcry_rndhw_poll_slow (add, origin);
@@ -214,6 +254,39 @@ _gcry_rndlinux_gather_random (void (*add)(const void*, size_t,
         length -= n_hw;
     }
 
+  /* If we have a modern kernel, try to use the new getentropy function.
+   * It guarantees that the kernel's RNG has been properly seeded before
+   * returning any data. */
+#if HAVE_GETENTROPY
+  if (a_rndlinux_have_getentropy != -1)
+    {
+      long ret;
+      size_t nbytes;
+
+      for (;;)
+        {
+          if (length == 0)
+            goto jhave_data;
+
+          do
+            {
+              nbytes = length < sizeof(buffer) ? length : sizeof(buffer);
+              if (nbytes > 256)
+                nbytes = 256;
+              _gcry_pre_syscall ();
+              ret = getentropy (buffer, nbytes);
+              _gcry_post_syscall ();
+            }
+          while (ret == -1 && errno == EINTR);
+          if (ret == -1)
+            break;
+
+          (*add)(buffer, nbytes, origin);
+          length -= nbytes;
+        }
+      log_fatal ("unexpected error from getentropy: %s\n", strerror (errno));
+    }
+#endif /* HAVE_GETENTROPY */
 
   /* Open the requested device.  The first time a device is to be
      opened we fail with a fatal error if the device does not exists.
@@ -252,48 +325,6 @@ _gcry_rndlinux_gather_random (void (*add)(const void*, size_t,
       struct timeval tv;
       int rc;
 
-      /* If we have a modern operating system, we first try to use the new
-       * getentropy function.  That call guarantees that the kernel's
-       * RNG has been properly seeded before returning any data.  This
-       * is different from /dev/urandom which may, due to its
-       * non-blocking semantics, return data even if the kernel has
-       * not been properly seeded.  And it differs from /dev/random by never
-       * blocking once the kernel is seeded.  */
-#if defined(HAVE_GETENTROPY) || defined(__NR_getrandom)
-        {
-          long ret;
-          size_t nbytes;
-
-          do
-            {
-              nbytes = length < sizeof(buffer)? length : sizeof(buffer);
-              if (nbytes > 256)
-                nbytes = 256;
-              _gcry_pre_syscall ();
-              ret = getentropy (buffer, nbytes);
-              _gcry_post_syscall ();
-            }
-          while (ret == -1 && errno == EINTR);
-          if (ret == -1 && errno == ENOSYS)
-            ; /* getentropy is not supported - fallback to pulling from fd.  */
-          else
-            { /* getentropy is supported.  Some sanity checks.  */
-              if (ret == -1)
-                log_fatal ("unexpected error from getentropy: %s\n",
-                           strerror (errno));
-#ifdef __NR_getrandom
-              else if (ret != nbytes)
-                log_fatal ("getentropy returned only"
-                           " %ld of %zu requested bytes\n", ret, nbytes);
-#endif
-
-              (*add)(buffer, nbytes, origin);
-              length -= nbytes;
-              continue; /* until LENGTH is zero.  */
-            }
-        }
-#endif
-
       /* If we collected some bytes update the progress indicator.  We
          do this always and not just if the select timed out because
          often just a few bytes are gathered within the timeout
@@ -356,6 +387,11 @@ _gcry_rndlinux_gather_random (void (*add)(const void*, size_t,
       (*add)(buffer, n, origin);
       length -= n;
     }
+
+#if HAVE_GETENTROPY
+jhave_data:
+#endif
+
   wipememory (buffer, sizeof buffer);
 
   if (any_need_entropy)
_______________________________________________
Gnupg-users mailing list
Gnupg-users@gnupg.org
http://lists.gnupg.org/mailman/listinfo/gnupg-users

Reply via email to