summaryrefslogtreecommitdiff
path: root/lib/getrandom.c
diff options
context:
space:
mode:
authorBruno Haible <bruno@clisp.org>2020-05-31 18:23:04 +0200
committerBruno Haible <bruno@clisp.org>2020-05-31 18:23:04 +0200
commit8175e3ddda29a03d5e0aec4a1774339414996565 (patch)
tree6dda6c4f9711a1f925e9929277a42b41c62ce8fd /lib/getrandom.c
parent053dd57da26bcda9f03180758fce81fffe7747c8 (diff)
downloadgnulib-8175e3ddda29a03d5e0aec4a1774339414996565.tar.gz
getrandom: Add support for native Windows.
* lib/getrandom.c: Include <errno.h>, <windows.h>, <bcrypt.h>, <wincrypt.h>. (CRYPT_VERIFY_CONTEXT): New macro. (LoadLibrary, CryptAcquireContext): Redirect to the variant with suffix 'A'. (GetProcAddress): New macro. (BCryptGenRandomFuncType): New type. (BCryptGenRandomFunc, initialized): New variables. (initialize): New function. (getrandom): On native Windows, use <bcrypt.h> API when available, and <wincrypt.h> API as fallback. * m4/getrandom.m4 (gl_FUNC_GETRANDOM): Set LIB_GETRANDOM. * modules/getrandom (Link): New section. * modules/getentropy (Link): Likewise. * modules/getrandom-tests (Makefile.am): Link test-getrandom against $(LIB_GETRANDOM). * modules/getentropy-tests (Makefile.am): Link test-getentropy against $(LIB_GETRANDOM). * modules/sys_random-c++-tests (Makefile.am): Link test-sys_random-c++ against $(LIB_GETRANDOM). * doc/glibc-functions/getrandom.texi: Mention the native Windows support.
Diffstat (limited to 'lib/getrandom.c')
-rw-r--r--lib/getrandom.c104
1 files changed, 102 insertions, 2 deletions
diff --git a/lib/getrandom.c b/lib/getrandom.c
index 0cc3dc3d85..ad49cae896 100644
--- a/lib/getrandom.c
+++ b/lib/getrandom.c
@@ -21,14 +21,65 @@
#include <sys/random.h>
+#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <unistd.h>
+#if defined _WIN32 && ! defined __CYGWIN__
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# include <bcrypt.h>
+# if !HAVE_LIB_BCRYPT
+# include <wincrypt.h>
+# ifndef CRYPT_VERIFY_CONTEXT
+# define CRYPT_VERIFY_CONTEXT 0xF0000000
+# endif
+# endif
+#endif
+
#include "minmax.h"
+#if defined _WIN32 && ! defined __CYGWIN__
+
+/* Don't assume that UNICODE is not defined. */
+# undef LoadLibrary
+# define LoadLibrary LoadLibraryA
+# undef CryptAcquireContext
+# define CryptAcquireContext CryptAcquireContextA
+
+# if !HAVE_LIB_BCRYPT
+
+/* Avoid warnings from gcc -Wcast-function-type. */
+# define GetProcAddress \
+ (void *) GetProcAddress
+
+/* BCryptGenRandom with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag works only
+ starting with Windows 7. */
+typedef NTSTATUS (WINAPI * BCryptGenRandomFuncType) (BCRYPT_ALG_HANDLE, UCHAR *, ULONG, ULONG);
+static BCryptGenRandomFuncType BCryptGenRandomFunc = NULL;
+static BOOL initialized = FALSE;
+
+static void
+initialize (void)
+{
+ HMODULE bcrypt = LoadLibrary ("bcrypt.dll");
+ if (bcrypt != NULL)
+ {
+ BCryptGenRandomFunc =
+ (BCryptGenRandomFuncType) GetProcAddress (bcrypt, "BCryptGenRandom");
+ }
+ initialized = TRUE;
+}
+
+# else
+
+# define BCryptGenRandomFunc BCryptGenRandom
+
+# endif
+
+#else
/* These devices exist on all platforms except native Windows. */
-#if !(defined _WIN32 && ! defined __CYGWIN__)
/* Name of a device through which the kernel returns high quality random
numbers, from an entropy pool. When the pool is empty, the call blocks
@@ -52,7 +103,56 @@ ssize_t
getrandom (void *buffer, size_t length, unsigned int flags)
#undef getrandom
{
-#if HAVE_GETRANDOM
+#if defined _WIN32 && ! defined __CYGWIN__
+ /* BCryptGenRandom, defined in <bcrypt.h>
+ <https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom>
+ with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag
+ works in Windows 7 and newer. */
+ static int bcrypt_not_working /* = 0 */;
+ if (!bcrypt_not_working)
+ {
+# if !HAVE_LIB_BCRYPT
+ if (!initialized)
+ initialize ();
+# endif
+ if (BCryptGenRandomFunc != NULL
+ && BCryptGenRandomFunc (NULL, buffer, length,
+ BCRYPT_USE_SYSTEM_PREFERRED_RNG)
+ == 0 /*STATUS_SUCCESS*/)
+ return length;
+ bcrypt_not_working = 1;
+ }
+# if !HAVE_LIB_BCRYPT
+ /* CryptGenRandom, defined in <wincrypt.h>
+ <https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom>
+ works in older releases as well, but is now deprecated.
+ CryptAcquireContext, defined in <wincrypt.h>
+ <https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptacquirecontexta> */
+ {
+ static int crypt_initialized /* = 0 */;
+ static HCRYPTPROV provider;
+ if (!crypt_initialized)
+ {
+ if (CryptAcquireContext (&provider, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFY_CONTEXT))
+ crypt_initialized = 1;
+ else
+ crypt_initialized = -1;
+ }
+ if (crypt_initialized >= 0)
+ {
+ if (!CryptGenRandom (provider, length, buffer))
+ {
+ errno = EIO;
+ return -1;
+ }
+ return length;
+ }
+ }
+# endif
+ errno = ENOSYS;
+ return -1;
+#elif HAVE_GETRANDOM
return getrandom (buffer, length, flags);
#else
static int randfd[2] = { -1, -1 };