Look up availability of getentropy() at runtime.
[openssl.git] / crypto / rand / rand_unix.c
index 1f0084db4ee0d9891a6e78a898ad563270910bf8..0c8e0dc48885233953483a8c5b648070d0f06b7c 100644 (file)
@@ -15,6 +15,7 @@
 #include "rand_lcl.h"
 #include "internal/rand_int.h"
 #include <stdio.h>
+#include "internal/dso.h"
 #if defined(__linux)
 # include <sys/syscall.h>
 #endif
@@ -23,7 +24,7 @@
 # include <sys/sysctl.h>
 # include <sys/param.h>
 #endif
-#if defined(__OpenBSD__)
+#if defined(__OpenBSD__) || defined(__NetBSD__)
 # include <sys/param.h>
 #endif
 #ifdef OPENSSL_SYS_UNIX
@@ -189,7 +190,7 @@ size_t rand_pool_acquire_entropy(RAND_POOL *pool)
 #   error "librandom not (yet) supported"
 #  endif
 
-#  if defined(__FreeBSD__) && defined(KERN_ARND)
+#  if (defined(__FreeBSD__) || defined(__NetBSD__)) && defined(KERN_ARND)
 /*
  * sysctl_random(): Use sysctl() to read a random number from the kernel
  * Returns the size on success, 0 on failure.
@@ -201,14 +202,25 @@ static size_t sysctl_random(char *buf, size_t buflen)
     size_t len;
 
     /*
-     * Old implementations returned longs, newer versions support variable
-     * sizes up to 256 byte. The code below would not work properly when
-     * the sysctl returns long and we want to request something not a multiple
-     * of longs, which should never be the case.
+     * On FreeBSD old implementations returned longs, newer versions support
+     * variable sizes up to 256 byte. The code below would not work properly
+     * when the sysctl returns long and we want to request something not a
+     * multiple of longs, which should never be the case.
      */
     if (!ossl_assert(buflen % sizeof(long) == 0))
         return 0;
 
+    /*
+     * On NetBSD before 4.0 KERN_ARND was an alias for KERN_URND, and only
+     * filled in an int, leaving the rest uninitialized. Since NetBSD 4.0
+     * it returns a variable number of bytes with the current version supporting
+     * up to 256 bytes.
+     * Just return an error on older NetBSD versions.
+     */
+#if   defined(__NetBSD__) && __NetBSD_Version__ < 400000000
+    return 0;
+#endif
+
     mib[0] = CTL_KERN;
     mib[1] = KERN_ARND;
 
@@ -231,23 +243,41 @@ static size_t sysctl_random(char *buf, size_t buflen)
  */
 int syscall_random(void *buf, size_t buflen)
 {
+    union {
+        void *p;
+        int (*f)(void *buffer, size_t length);
+    } p_getentropy;
+
+    /*
+     * Do runtime detection to find getentropy().
+     *
+     * We could cache the result of the lookup, but we normally don't
+     * call this function often.
+     *
+     * Known OSs that should support this:
+     * - Darwin since 16 (OSX 10.12, IOS 10.0).
+     * - Solaris since 11.3
+     * - OpenBSD since 5.6
+     * - Linux since 3.17 with glibc 2.25
+     * - FreeBSD since 12.0 (1200061)
+     */
+    p_getentropy.p = DSO_global_lookup("getentropy");
+    if (p_getentropy.p != NULL)
+        return p_getentropy.f(buf, buflen);
+
 #  if defined(OPENSSL_HAVE_GETRANDOM)
     return (int)getrandom(buf, buflen, 0);
 #  endif
 
+    /* Linux supports this since version 3.17 */
 #  if defined(__linux) && defined(SYS_getrandom)
     return (int)syscall(SYS_getrandom, buf, buflen, 0);
 #  endif
 
-#  if defined(__FreeBSD__) && defined(KERN_ARND)
+#  if (defined(__FreeBSD__) || defined(__NetBSD__)) && defined(KERN_ARND)
     return (int)sysctl_random(buf, buflen);
 #  endif
 
-   /* Supported since OpenBSD 5.6 */
-#  if defined(__OpenBSD__) && OpenBSD >= 201411
-    return getentropy(buf, buflen);
-#  endif
-
     return -1;
 }