Modify the DEVRANDOM source so that the files are kept open persistently.
authorPauli <paul.dale@oracle.com>
Wed, 6 Jun 2018 23:31:44 +0000 (09:31 +1000)
committerPauli <paul.dale@oracle.com>
Tue, 26 Jun 2018 21:15:36 +0000 (07:15 +1000)
This allows operation inside a chroot environment without having the
random device present.

A new call, RAND_keep_random_devices_open(), has been introduced that can
be used to control file descriptor use by the random seed sources. Some
seed sources maintain open file descriptors by default, which allows
such sources to operate in a chroot(2) jail without the associated device
nodes being available.

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Matthias St. Pierre <Matthias.St.Pierre@ncp-e.com>
(Merged from https://github.com/openssl/openssl/pull/6432)

CHANGES
crypto/include/internal/rand_int.h
crypto/rand/rand_lib.c
crypto/rand/rand_unix.c
crypto/rand/rand_vms.c
crypto/rand/rand_win.c
doc/man3/RAND_add.pod
include/openssl/rand.h
util/libcrypto.num

diff --git a/CHANGES b/CHANGES
index 4cdcf52f31e6c7742f42fe82b986b5c29e1ae602..2eb90a923409ac9ce15c4316798b2ebf53703ca3 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -9,6 +9,13 @@
 
  Changes between 1.1.0h and 1.1.1 [xx XXX xxxx]
 
+  *) Modified the random device based seed sources to keep the relevant
+     file descriptors open rather than reopening them on each access.
+     This allows such sources to operate in a chroot() jail without
+     the associated device nodes being available. This behaviour can be
+     controlled using RAND_keep_random_devices_open().
+     [Paul Dale]
+
   *) Numerous side-channel attack mitigations have been applied. This may have
      performance impacts for some algorithms for the benefit of improved
      security. Specific changes are noted in this change log by their respective
index daec926c674ffcdb4891dc6ee12229e253afa9e1..d91ee4c9342cbfac5e5dd9e712e9f1f3a9b3194a 100644 (file)
@@ -111,4 +111,21 @@ int rand_pool_add_nonce_data(RAND_POOL *pool);
  */
 int rand_pool_add_additional_data(RAND_POOL *pool);
 
+/*
+ * Initialise the random pool reseeding sources.
+ *
+ * Returns 1 on success and 0 on failure.
+ */
+int rand_pool_init(void);
+
+/*
+ * Finalise the random pool reseeding sources.
+ */
+void rand_pool_cleanup(void);
+
+/*
+ * Control the random pool use of open file descriptors.
+ */
+void rand_pool_keep_random_devices_open(int keep);
+
 #endif
index 1a9a7d2ce00eb8884daf6164eb8b393f385c21f8..d31735c5c45a1b01ca266072995edf3a78f30c8e 100644 (file)
@@ -324,8 +324,13 @@ DEFINE_RUN_ONCE_STATIC(do_rand_init)
     if (rand_nonce_lock == NULL)
         goto err2;
 
+    if (!rand_pool_init())
+        goto err3;
+
     return 1;
 
+err3:
+    rand_pool_cleanup();
 err2:
     CRYPTO_THREAD_lock_free(rand_meth_lock);
     rand_meth_lock = NULL;
@@ -343,6 +348,7 @@ void rand_cleanup_int(void)
 
     if (meth != NULL && meth->cleanup != NULL)
         meth->cleanup();
+    rand_pool_cleanup();
     RAND_set_rand_method(NULL);
 #ifndef OPENSSL_NO_ENGINE
     CRYPTO_THREAD_lock_free(rand_engine_lock);
@@ -354,6 +360,15 @@ void rand_cleanup_int(void)
     rand_nonce_lock = NULL;
 }
 
+/*
+ * RAND_close_seed_files() ensures that any seed file decriptors are
+ * closed after use.
+ */
+void RAND_keep_random_devices_open(int keep)
+{
+    rand_pool_keep_random_devices_open(keep);
+}
+
 /*
  * RAND_poll() reseeds the default RNG using random input
  *
index a545e08d8c28a3cad0c745177e44fe8a23002bed..b64cf278255007128af660d79448e9f861738f02 100644 (file)
@@ -30,6 +30,8 @@
 
 #if defined(OPENSSL_SYS_UNIX) || defined(__DJGPP__)
 # include <sys/types.h>
+# include <sys/stat.h>
+# include <fcntl.h>
 # include <unistd.h>
 # include <sys/time.h>
 
@@ -154,6 +156,14 @@ size_t rand_pool_acquire_entropy(RAND_POOL *pool)
     return rand_pool_entropy_available(pool);
 }
 
+void rand_pool_cleanup(void)
+{
+}
+
+void rand_pool_keep_random_devices_open(int keep)
+{
+}
+
 # else
 
 #  if defined(OPENSSL_RAND_SEED_EGD) && \
@@ -274,6 +284,134 @@ int syscall_random(void *buf, size_t buflen)
     return -1;
 }
 
+#if  !defined(OPENSSL_RAND_SEED_NONE) && defined(OPENSSL_RAND_SEED_DEVRANDOM)
+static const char *random_device_paths[] = { DEVRANDOM };
+static struct random_device {
+    int fd;
+    dev_t dev;
+    ino_t ino;
+    mode_t mode;
+    dev_t rdev;
+} random_devices[OSSL_NELEM(random_device_paths)];
+static int keep_random_devices_open = 1;
+
+/*
+ * Verify that the file descriptor associated with the random source is
+ * still valid. The rationale for doing this is the fact that it is not
+ * uncommon for daemons to close all open file handles when daemonizing.
+ * So the handle might have been closed or even reused for opening
+ * another file.
+ */
+static int check_random_device(struct random_device * rd)
+{
+    struct stat st;
+
+    return rd->fd != -1
+           && fstat(rd->fd, &st) != -1
+           && rd->dev == st.st_dev
+           && rd->ino == st.st_ino
+           && ((rd->mode ^ st.st_mode) & ~(S_IRWXU | S_IRWXG | S_IRWXO)) == 0
+           && rd->rdev == st.st_rdev;
+}
+
+/*
+ * Open a random device if required and return its file descriptor or -1 on error
+ */
+static int get_random_device(size_t n)
+{
+    struct stat st;
+    struct random_device * rd = &random_devices[n];
+
+    /* reuse existing file descriptor if it is (still) valid */
+    if (check_random_device(rd))
+        return rd->fd;
+
+    /* open the random device ... */
+    if ((rd->fd = open(random_device_paths[n], O_RDONLY)) == -1)
+        return rd->fd;
+
+    /* ... and cache its relevant stat(2) data */
+    if (fstat(rd->fd, &st) != -1) {
+        rd->dev = st.st_dev;
+        rd->ino = st.st_ino;
+        rd->mode = st.st_mode;
+        rd->rdev = st.st_rdev;
+    } else {
+        close(rd->fd);
+        rd->fd = -1;
+    }
+
+    return rd->fd;
+}
+
+/*
+ * Close a random device making sure it is a random device
+ */
+static void close_random_device(size_t n)
+{
+    struct random_device * rd = &random_devices[n];
+
+    if (check_random_device(rd))
+        close(rd->fd);
+    rd->fd = -1;
+}
+
+static void open_random_devices(void)
+{
+    size_t i;
+
+    for (i = 0; i < OSSL_NELEM(random_devices); i++)
+        (void)get_random_device(i);
+}
+
+int rand_pool_init(void)
+{
+    size_t i;
+
+    for (i = 0; i < OSSL_NELEM(random_devices); i++)
+        random_devices[i].fd = -1;
+    open_random_devices();
+    return 1;
+}
+
+void rand_pool_cleanup(void)
+{
+    size_t i;
+
+    for (i = 0; i < OSSL_NELEM(random_devices); i++)
+        close_random_device(i);
+}
+
+void rand_pool_keep_random_devices_open(int keep)
+{
+    if (keep)
+        open_random_devices();
+    else
+        rand_pool_cleanup();
+    keep_random_devices_open = keep;
+}
+
+#  else     /* defined(OPENSSL_RAND_SEED_NONE)
+             * || !defined(OPENSSL_RAND_SEED_DEVRANDOM)
+             */
+
+int rand_pool_init(void)
+{
+    return 1;
+}
+
+void rand_pool_cleanup(void)
+{
+}
+
+void rand_pool_keep_random_devices_open(int keep)
+{
+}
+
+#  endif    /* !defined(OPENSSL_RAND_SEED_NONE)
+             * && defined(OPENSSL_RAND_SEED_DEVRANDOM)
+             */
+
 /*
  * Try the various seeding methods in turn, exit when successful.
  *
@@ -324,30 +462,33 @@ size_t rand_pool_acquire_entropy(RAND_POOL *pool)
 
 #   ifdef OPENSSL_RAND_SEED_DEVRANDOM
     bytes_needed = rand_pool_bytes_needed(pool, 1 /*entropy_factor*/);
-    if (bytes_needed > 0) {
-        static const char *paths[] = { DEVRANDOM, NULL };
-        FILE *fp;
-        int i;
+    {
+        size_t i;
 
-        for (i = 0; paths[i] != NULL; i++) {
-            if ((fp = fopen(paths[i], "rb")) == NULL)
+        for (i = 0; bytes_needed > 0 && i < OSSL_NELEM(random_device_paths); i++) {
+            const int fd = get_random_device(i);
+
+            if (fd == -1)
                 continue;
-            setbuf(fp, NULL);
             buffer = rand_pool_add_begin(pool, bytes_needed);
             if (buffer != NULL) {
-                size_t bytes = 0;
-                if (fread(buffer, 1, bytes_needed, fp) == bytes_needed)
-                    bytes = bytes_needed;
+                const ssize_t n = read(fd, buffer, bytes_needed);
 
-                rand_pool_add_end(pool, bytes, 8 * bytes);
-                entropy_available = rand_pool_entropy_available(pool);
+                if (n <= 0) {
+                    close_random_device(i);
+                    continue;
+                }
+
+                rand_pool_add_end(pool, n, 8 * n);
             }
-            fclose(fp);
-            if (entropy_available > 0)
-                return entropy_available;
+            if (!keep_random_devices_open)
+                close_random_device(i);
 
             bytes_needed = rand_pool_bytes_needed(pool, 1 /*entropy_factor*/);
         }
+        entropy_available = rand_pool_entropy_available(pool);
+        if (entropy_available > 0)
+            return entropy_available;
     }
 #   endif
 
@@ -433,7 +574,6 @@ int rand_pool_add_additional_data(RAND_POOL *pool)
 }
 
 
-
 /*
  * Get the current time with the highest possible resolution
  *
index b263f94208b3dcb1b86646fa52e996254361c883..43dddf3c93d101218f4fd1bf7d4cea4e4bec64f7 100644 (file)
@@ -507,4 +507,17 @@ int rand_pool_add_additional_data(RAND_POOL *pool)
     return rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0);
 }
 
+int rand_pool_init(void)
+{
+    return 1;
+}
+
+void rand_pool_cleanup(void)
+{
+}
+
+void rand_pool_keep_random_devices_open(int keep)
+{
+}
+
 #endif
index f2059eb412b5f49cd3c60daca74932b70834e627..34c2a8b92443655d37817b1647863b584c9f6a9a 100644 (file)
@@ -169,4 +169,17 @@ void RAND_screen(void)
 }
 # endif
 
+int rand_pool_init(void)
+{
+    return 1;
+}
+
+void rand_pool_cleanup(void)
+{
+}
+
+void rand_pool_keep_random_devices_open(int keep)
+{
+}
+
 #endif
index 1b06d1be87b95af60000bf7b4d78eebf770b7273..b6753fd2ed0b1b0cac0885303226b90d76ed0622 100644 (file)
@@ -2,7 +2,8 @@
 
 =head1 NAME
 
-RAND_add, RAND_poll, RAND_seed, RAND_status, RAND_event, RAND_screen
+RAND_add, RAND_poll, RAND_seed, RAND_status, RAND_event, RAND_screen,
+RAND_keep_random_devices_open
 - add randomness to the PRNG or get its status
 
 =head1 SYNOPSIS
@@ -15,6 +16,8 @@ RAND_add, RAND_poll, RAND_seed, RAND_status, RAND_event, RAND_screen
  void RAND_add(const void *buf, int num, double randomness);
  void RAND_seed(const void *buf, int num);
 
+ void RAND_keep_random_devices_open(int keep);
+
 Deprecated:
 
  #if OPENSSL_API_COMPAT < 0x10100000L
@@ -54,6 +57,15 @@ should consider using L<RAND_load_file(3)> instead.
 
 RAND_seed() is equivalent to RAND_add() with B<randomness> set to B<num>.
 
+RAND_keep_random_devices_open() is used to control file descriptor
+usage by the random seed sources. Some seed sources maintain open file
+descriptors by default, which allows such sources to operate in a
+chroot(2) jail without the associated device nodes being available. When
+the B<keep> argument is zero, this call disables the retention of file
+descriptors. Conversely, a non-zero argument enables the retention of
+file descriptors. This function is usually called during initialization
+and it takes effect immediately.
+
 RAND_event() and RAND_screen() are equivalent to RAND_poll() and exist
 for compatibility reasons only. See HISTORY section below.
 
index 058ece631a0dcc6a024824136b9794f6d0070681..38a2a2718f8b8d6d2cf1687bddd5e8c19a3c3aaf 100644 (file)
@@ -44,6 +44,7 @@ int RAND_priv_bytes(unsigned char *buf, int num);
 DEPRECATEDIN_1_1_0(int RAND_pseudo_bytes(unsigned char *buf, int num))
 
 void RAND_seed(const void *buf, int num);
+void RAND_keep_random_devices_open(int keep);
 
 # if defined(__ANDROID__) && defined(__NDK_FPABI__)
 __NDK_FPABI__  /* __attribute__((pcs("aapcs"))) on ARM */
index 9ca14763bff79a54ccb53da52c265e52022003c2..f193729f71dc0a04990f83836c094b6f98559e3f 100644 (file)
@@ -4568,3 +4568,4 @@ EVP_PKEY_get_raw_private_key            4519      1_1_1   EXIST::FUNCTION:
 EVP_PKEY_asn1_set_get_priv_key          4520   1_1_1   EXIST::FUNCTION:
 EVP_PKEY_asn1_set_get_pub_key           4521   1_1_1   EXIST::FUNCTION:
 EVP_PKEY_set_alias_type                 4522   1_1_1   EXIST::FUNCTION:
+RAND_keep_random_devices_open           4523   1_1_1   EXIST::FUNCTION: