Use getentropy on macOS 10.12 and later.

Bug: 287
Change-Id: I40760bdba8dcaab9c5c38d52d6479138f52eccbd
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/37284
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/fipsmodule/rand/urandom.c b/crypto/fipsmodule/rand/urandom.c
index 1d26534..9fa0c97 100644
--- a/crypto/fipsmodule/rand/urandom.c
+++ b/crypto/fipsmodule/rand/urandom.c
@@ -54,6 +54,10 @@
 #endif
 #endif  // OPENSSL_LINUX
 
+#if defined(OPENSSL_MACOS)
+#include <sys/random.h>
+#endif
+
 #include <openssl/thread.h>
 #include <openssl/mem.h>
 
@@ -176,6 +180,15 @@
   }
 #endif  // USE_NR_getrandom
 
+#if defined(OPENSSL_MACOS)
+  // getentropy is available in macOS 10.12 and up. iOS 10 and up may also
+  // support it, but the header is missing. See https://crbug.com/boringssl/287.
+  if (__builtin_available(macos 10.12, *)) {
+    *urandom_fd_bss_get() = kHaveGetrandom;
+    return;
+  }
+#endif
+
   // Android FIPS builds must support getrandom.
 #if defined(BORINGSSL_FIPS) && defined(OPENSSL_ANDROID)
   perror("getrandom not found");
@@ -229,6 +242,9 @@
 static void wait_for_entropy(void) {
   int fd = *urandom_fd_bss_get();
   if (fd == kHaveGetrandom) {
+    // |getrandom| and |getentropy| support blocking in |fill_with_entropy|
+    // directly. For |getrandom|, we first probe with a non-blocking call to aid
+    // debugging.
 #if defined(USE_NR_getrandom)
     if (*getrandom_ready_bss_get()) {
       // The entropy pool was already initialized in |init_once|.
@@ -260,16 +276,12 @@
           boringssl_getrandom(&dummy, sizeof(dummy), 0 /* no flags */);
     }
 
-    if (getrandom_ret == 1) {
-      return;
+    if (getrandom_ret != 1) {
+      perror("getrandom");
+      abort();
     }
-
-    perror("getrandom");
-    abort();
-#else
-    fprintf(stderr, "urandom fd corrupt.\n");
-    abort();
 #endif  // USE_NR_getrandom
+    return;
   }
 
 #if defined(BORINGSSL_FIPS)
@@ -353,6 +365,19 @@
     if (*urandom_fd_bss_get() == kHaveGetrandom) {
 #if defined(USE_NR_getrandom)
       r = boringssl_getrandom(out, len, block ? 0 : GRND_NONBLOCK);
+#elif defined(OPENSSL_MACOS)
+      if (__builtin_available(macos 10.12, *)) {
+        // |getentropy| can only request 256 bytes at a time.
+        size_t todo = len <= 256 ? len : 256;
+        if (getentropy(out, todo) != 0) {
+          r = -1;
+        } else {
+          r = (ssize_t)todo;
+        }
+      } else {
+        fprintf(stderr, "urandom fd corrupt.\n");
+        abort();
+      }
 #else  // USE_NR_getrandom
       fprintf(stderr, "urandom fd corrupt.\n");
       abort();
diff --git a/include/openssl/base.h b/include/openssl/base.h
index cb1affa..e347c09 100644
--- a/include/openssl/base.h
+++ b/include/openssl/base.h
@@ -124,6 +124,11 @@
 
 #if defined(__APPLE__)
 #define OPENSSL_APPLE
+// Note |TARGET_OS_MAC| is set for all Apple OS variants. |TARGET_OS_OSX|
+// targets macOS specifically.
+#if defined(TARGET_OS_OSX) && TARGET_OS_OSX
+#define OPENSSL_MACOS
+#endif
 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
 #define OPENSSL_IOS
 #endif