Merge pull request #3423 from niacat/sysctl-arnd

entropy: Add support for BSD sysctl(KERN_ARND)
diff --git a/ChangeLog.d/sysctl-arnd-support.txt b/ChangeLog.d/sysctl-arnd-support.txt
new file mode 100644
index 0000000..14ad674
--- /dev/null
+++ b/ChangeLog.d/sysctl-arnd-support.txt
@@ -0,0 +1,2 @@
+Features
+   * Added support to entropy_poll for the kern.arandom syscall supported on some BSD systems. Contributed by Nia Alarie in #3423.
diff --git a/library/entropy_poll.c b/library/entropy_poll.c
index 8b4a5af..dc62183 100644
--- a/library/entropy_poll.c
+++ b/library/entropy_poll.c
@@ -115,6 +115,41 @@
 #endif /* SYS_getrandom */
 #endif /* __linux__ || __midipix__ */
 
+/*
+ * Some BSD systems provide KERN_ARND.
+ * This is equivalent to reading from /dev/urandom, only it doesn't require an
+ * open file descriptor, and provides up to 256 bytes per call (basically the
+ * same as getentropy(), but with a longer history).
+ *
+ * Documentation: https://netbsd.gw.com/cgi-bin/man-cgi?sysctl+7
+ */
+#if (defined(__FreeBSD__) || defined(__NetBSD__)) && !defined(HAVE_GETRANDOM)
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#if defined(KERN_ARND)
+#define HAVE_SYSCTL_ARND
+
+static int sysctl_arnd_wrapper( unsigned char *buf, size_t buflen )
+{
+    int name[2];
+    size_t len;
+
+    name[0] = CTL_KERN;
+    name[1] = KERN_ARND;
+
+    while( buflen > 0 )
+    {
+        len = buflen > 256 ? 256 : buflen;
+        if( sysctl(name, 2, buf, &len, NULL, 0) == -1 )
+            return( -1 );
+        buflen -= len;
+        buf += len;
+    }
+    return( 0 );
+}
+#endif /* KERN_ARND */
+#endif /* __FreeBSD__ || __NetBSD__ */
+
 #include <stdio.h>
 
 int mbedtls_platform_entropy_poll( void *data,
@@ -139,6 +174,15 @@
     ((void) ret);
 #endif /* HAVE_GETRANDOM */
 
+#if defined(HAVE_SYSCTL_ARND)
+    ((void) file);
+    ((void) read_len);
+    if( sysctl_arnd_wrapper( output, len ) == -1 )
+        return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED );
+    *olen = len;
+    return( 0 );
+#else
+
     *olen = 0;
 
     file = fopen( "/dev/urandom", "rb" );
@@ -156,6 +200,7 @@
     *olen = len;
 
     return( 0 );
+#endif /* HAVE_SYSCTL_ARND */
 }
 #endif /* _WIN32 && !EFIX64 && !EFI32 */
 #endif /* !MBEDTLS_NO_PLATFORM_ENTROPY */