windows: improve random source

- Use the Windows API to seed the fallback random generator.

  This ensures to always have a random seed, even when libcurl is built
  with a vtls backend lacking a random generator API, such as rustls
  (experimental), GSKit and certain mbedTLS builds, or, when libcurl is
  built without a TLS backend. We reuse the Windows-specific random
  function from the Schannel backend.

- Implement support for `BCryptGenRandom()` [1] on Windows, as a
  replacement for the deprecated `CryptGenRandom()` [2] function.

  It is used as the secure random generator for Schannel, and also to
  provide entropy for libcurl's fallback random generator. The new
  function is supported on Vista and newer via its `bcrypt.dll`. It is
  used automatically when building for supported versions. It also works
  in UWP apps (the old function did not).

- Clear entropy buffer before calling the Windows random generator.

  This avoids using arbitrary application memory as entropy (with
  `CryptGenRandom()`) and makes sure to return in a predictable state
  when an API call fails.

[1] https://docs.microsoft.com/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
[2] https://docs.microsoft.com/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom

Closes #9027
This commit is contained in:
Viktor Szakats 2022-07-04 09:38:24 +00:00
parent e6f8445ede
commit 76172511e7
No known key found for this signature in database
GPG key ID: B5ABD165E2AEF201
8 changed files with 110 additions and 19 deletions

View file

@ -279,7 +279,7 @@ ifdef SSH2
DLL_LIBS += -L"$(LIBSSH2_PATH)/win32" -lssh2
ifdef SCHANNEL
ifndef DYN
DLL_LIBS += -lbcrypt -lcrypt32
DLL_LIBS += -lcrypt32
endif
endif
endif
@ -400,7 +400,7 @@ ifndef USE_LDAP_OPENLDAP
DLL_LIBS += -lwldap32
endif
endif
DLL_LIBS += -lws2_32
DLL_LIBS += -lws2_32 -lbcrypt
# Makefile.inc provides the CSOURCES and HHEADERS defines
include Makefile.inc

View file

@ -38,6 +38,64 @@
#include "curl_memory.h"
#include "memdebug.h"
#ifdef WIN32
#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
# define HAVE_MINGW_ORIGINAL
#endif
#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 && \
!defined(HAVE_MINGW_ORIGINAL)
# define HAVE_WIN_BCRYPTGENRANDOM
# include <bcrypt.h>
# ifdef _MSC_VER
# pragma comment(lib, "bcrypt.lib")
# endif
# ifndef BCRYPT_USE_SYSTEM_PREFERRED_RNG
# define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002
# endif
# ifndef STATUS_SUCCESS
# define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
# endif
#elif defined(USE_WIN32_CRYPTO)
# include <wincrypt.h>
# ifdef _MSC_VER
# pragma comment(lib, "advapi32.lib")
# endif
#endif
CURLcode Curl_win32_random(unsigned char *entropy, size_t length)
{
memset(entropy, 0, length);
#if defined(HAVE_WIN_BCRYPTGENRANDOM)
if(BCryptGenRandom(NULL, entropy, (ULONG)length,
BCRYPT_USE_SYSTEM_PREFERRED_RNG) != STATUS_SUCCESS)
return CURLE_FAILED_INIT;
return CURLE_OK;
#elif defined(USE_WIN32_CRYPTO)
{
HCRYPTPROV hCryptProv = 0;
if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
return CURLE_FAILED_INIT;
if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) {
CryptReleaseContext(hCryptProv, 0UL);
return CURLE_FAILED_INIT;
}
CryptReleaseContext(hCryptProv, 0UL);
}
return CURLE_OK;
#else
return CURLE_NOT_BUILT_IN;
#endif
}
#endif
static CURLcode randit(struct Curl_easy *data, unsigned int *rnd)
{
unsigned int r;
@ -73,6 +131,14 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd)
/* ---- non-cryptographic version following ---- */
#ifdef WIN32
if(!seeded) {
result = Curl_win32_random((unsigned char *)rnd, sizeof(*rnd));
if(result != CURLE_NOT_BUILT_IN)
return result;
}
#endif
#if defined(RANDOM_FILE) && !defined(WIN32)
if(!seeded) {
/* if there's a random file to read a seed from, use it */

View file

@ -48,4 +48,10 @@ CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num);
CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd,
size_t num);
#ifdef WIN32
/* Random generator shared between the Schannel vtls and Curl_rand*()
functions */
CURLcode Curl_win32_random(unsigned char *entropy, size_t length);
#endif
#endif /* HEADER_CURL_RAND_H */

View file

@ -53,6 +53,7 @@
#include "curl_printf.h"
#include "multiif.h"
#include "version_win32.h"
#include "rand.h"
/* The last #include file should be: */
#include "curl_memory.h"
@ -2298,21 +2299,9 @@ static size_t schannel_version(char *buffer, size_t size)
static CURLcode schannel_random(struct Curl_easy *data UNUSED_PARAM,
unsigned char *entropy, size_t length)
{
HCRYPTPROV hCryptProv = 0;
(void)data;
if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
return CURLE_FAILED_INIT;
if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) {
CryptReleaseContext(hCryptProv, 0UL);
return CURLE_FAILED_INIT;
}
CryptReleaseContext(hCryptProv, 0UL);
return CURLE_OK;
return Curl_win32_random(entropy, length);
}
static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,