Fix glibc specific conditional for Mac OS/X
[openssl.git] / crypto / rand / rand_lib.c
index 6f8deca1f9ff16e3aa72df7c14af90ad14ec339c..69c3c79c6da29624d0c61e04fed4c05d45e149a2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1995-2017 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved.
  *
  * Licensed under the OpenSSL license (the "License").  You may not use
  * this file except in compliance with the License.  You can obtain a copy
 #include <openssl/engine.h>
 #include "internal/thread_once.h"
 #include "rand_lcl.h"
+#ifdef OPENSSL_SYS_UNIX
+# include <sys/types.h>
+# include <unistd.h>
+# include <sys/time.h>
+#endif
+#include "e_os.h"
+
+/* Macro to convert two thirty two bit values into a sixty four bit one */
+#define TWO32TO64(a, b) ((((uint64_t)(a)) << 32) + (b))
 
 #ifndef OPENSSL_NO_ENGINE
 /* non-NULL if default_RAND_meth is ENGINE-provided */
@@ -155,12 +164,20 @@ size_t rand_drbg_get_entropy(RAND_DRBG *drbg,
         if (buffer != NULL) {
             size_t bytes = 0;
 
-            /* Get entropy from parent, include our state as additional input */
+            /*
+             * Get random from parent, include our state as additional input.
+             * Our lock is already held, but we need to lock our parent before
+             * generating bits from it.
+             */
+            if (drbg->parent->lock)
+                CRYPTO_THREAD_write_lock(drbg->parent->lock);
             if (RAND_DRBG_generate(drbg->parent,
                                    buffer, bytes_needed,
                                    0,
                                    (unsigned char *)drbg, sizeof(*drbg)) != 0)
                 bytes = bytes_needed;
+            if (drbg->parent->lock)
+                CRYPTO_THREAD_unlock(drbg->parent->lock);
 
             entropy_available = RAND_POOL_add_end(pool, bytes, 8 * bytes);
         }
@@ -179,6 +196,122 @@ size_t rand_drbg_get_entropy(RAND_DRBG *drbg,
     return ret;
 }
 
+/*
+ * Find a suitable system time.  Start with the highest resolution source
+ * and work down to the slower ones.  This is added as additional data and
+ * isn't counted as randomness, so any result is acceptable.
+ */
+static uint64_t get_timer_bits(void)
+{
+    uint64_t res = OPENSSL_rdtsc();
+
+    if (res != 0)
+        return res;
+#if defined(_WIN32)
+    {
+        LARGE_INTEGER t;
+        FILETIME ft;
+
+        if (QueryPerformanceCounter(&t) != 0)
+            return t.QuadPart;
+        GetSystemTimeAsFileTime(&ft);
+        return TWO32TO64(ft.dwHighDateTime, ft.dwLowDateTime);
+    }
+#elif defined(__sun) || defined(__hpux)
+    return gethrtime();
+#elif defined(_AIX)
+    {
+        timebasestruct_t t;
+
+        read_wall_time(&t, TIMEBASE_SZ);
+        return TWO32TO64(t.tb_high, t.tb_low);
+    }
+#else
+
+# if defined(_POSIX_C_SOURCE) \
+     && defined(_POSIX_TIMERS) \
+     && _POSIX_C_SOURCE >= 199309L \
+     && (!defined(__GLIBC__) \
+         || (defined(__GLIBC_PREREQ) && __GLIBC_PREREQ(2, 17)))
+    {
+        struct timespec ts;
+        clockid_t cid;
+
+#  ifdef CLOCK_BOOTTIME
+        cid = CLOCK_BOOTTIME;
+#  elif defined(_POSIX_MONOTONIC_CLOCK)
+        cid = CLOCK_MONOTONIC;
+#  else
+        cid = CLOCK_REALTIME;
+#  endif
+
+        if (clock_gettime(cid, &ts) == 0)
+            return TWO32TO64(ts.tv_sec, ts.tv_nsec);
+    }
+# endif
+# if defined(__unix__) \
+     || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L)
+    {
+        struct timeval tv;
+
+        if (gettimeofday(&tv, NULL) == 0)
+            return TWO32TO64(tv.tv_sec, tv.tv_usec);
+    }
+# endif
+    return time(NULL);
+#endif
+}
+
+/*
+ * Generate additional data that can be used for the drbg. The data does
+ * not need to contain entropy, but it's useful if it contains at least
+ * some bits that are unpredictable.
+ *
+ * Returns 0 on failure.
+ *
+ * On success it allocates a buffer at |*pout| and returns the length of
+ * the data. The buffer should get freed using OPENSSL_secure_clear_free().
+ */
+size_t rand_drbg_get_additional_data(unsigned char **pout, size_t max_len)
+{
+    RAND_POOL *pool;
+    CRYPTO_THREAD_ID thread_id;
+    size_t len;
+#ifdef OPENSSL_SYS_UNIX
+    pid_t pid;
+#elif defined(OPENSSL_SYS_WIN32)
+    DWORD pid;
+#endif
+    uint64_t tbits;
+
+    pool = RAND_POOL_new(0, 0, max_len);
+    if (pool == NULL)
+        return 0;
+
+#ifdef OPENSSL_SYS_UNIX
+    pid = getpid();
+    RAND_POOL_add(pool, (unsigned char *)&pid, sizeof(pid), 0);
+#elif defined(OPENSSL_SYS_WIN32)
+    pid = GetCurrentProcessId();
+    RAND_POOL_add(pool, (unsigned char *)&pid, sizeof(pid), 0);
+#endif
+
+    thread_id = CRYPTO_THREAD_get_current_id();
+    if (thread_id != 0)
+        RAND_POOL_add(pool, (unsigned char *)&thread_id, sizeof(thread_id), 0);
+
+    tbits = get_timer_bits();
+    RAND_POOL_add(pool, (unsigned char *)&tbits, sizeof(tbits), 0);
+
+    /* TODO: Use RDSEED? */
+
+    len = RAND_POOL_length(pool);
+    if (len != 0)
+        *pout = RAND_POOL_detach(pool);
+    RAND_POOL_free(pool);
+
+    return len;
+}
 
 /*
  * Implements the cleanup_entropy() callback (see RAND_DRBG_set_callbacks())
@@ -200,10 +333,10 @@ DEFINE_RUN_ONCE_STATIC(do_rand_init)
     int ret = 1;
 
 #ifndef OPENSSL_NO_ENGINE
-    rand_engine_lock = CRYPTO_THREAD_glock_new("rand_engine");
+    rand_engine_lock = CRYPTO_THREAD_lock_new();
     ret &= rand_engine_lock != NULL;
 #endif
-    rand_meth_lock = CRYPTO_THREAD_glock_new("rand_meth");
+    rand_meth_lock = CRYPTO_THREAD_lock_new();
     ret &= rand_meth_lock != NULL;
 
     return ret;
@@ -238,8 +371,8 @@ int RAND_poll(void)
     const RAND_METHOD *meth = RAND_get_rand_method();
 
     if (meth == RAND_OpenSSL()) {
-        /* fill random pool and seed the default DRBG */
-        RAND_DRBG *drbg = RAND_DRBG_get0_global();
+        /* fill random pool and seed the master DRBG */
+        RAND_DRBG *drbg = RAND_DRBG_get0_master();
 
         if (drbg == NULL)
             return 0;
@@ -626,15 +759,20 @@ int RAND_priv_bytes(unsigned char *buf, int num)
 {
     const RAND_METHOD *meth = RAND_get_rand_method();
     RAND_DRBG *drbg;
+    int ret;
 
     if (meth != RAND_OpenSSL())
         return RAND_bytes(buf, num);
 
-    drbg = RAND_DRBG_get0_priv_global();
+    drbg = RAND_DRBG_get0_private();
     if (drbg == NULL)
         return 0;
 
-    return RAND_DRBG_generate(drbg, buf, num, 0, NULL, 0);
+    /* We have to lock the DRBG before generating bits from it. */
+    CRYPTO_THREAD_write_lock(drbg->lock);
+    ret = RAND_DRBG_bytes(drbg, buf, num);
+    CRYPTO_THREAD_unlock(drbg->lock);
+    return ret;
 }
 
 int RAND_bytes(unsigned char *buf, int num)