mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-10-01 22:51:16 +02:00
ca3b37487b
Backpatch-through: 9.5
183 lines
3.9 KiB
C
183 lines
3.9 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* pg_strong_random.c
|
|
* generate a cryptographically secure random number
|
|
*
|
|
* Our definition of "strong" is that it's suitable for generating random
|
|
* salts and query cancellation keys, during authentication.
|
|
*
|
|
* Note: this code is run quite early in postmaster and backend startup;
|
|
* therefore, even when built for backend, it cannot rely on backend
|
|
* infrastructure such as elog() or palloc().
|
|
*
|
|
* Copyright (c) 1996-2021, PostgreSQL Global Development Group
|
|
*
|
|
* IDENTIFICATION
|
|
* src/port/pg_strong_random.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "c.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <sys/time.h>
|
|
|
|
/*
|
|
* pg_strong_random & pg_strong_random_init
|
|
*
|
|
* Generate requested number of random bytes. The returned bytes are
|
|
* cryptographically secure, suitable for use e.g. in authentication.
|
|
*
|
|
* Before pg_strong_random is called in any process, the generator must first
|
|
* be initialized by calling pg_strong_random_init().
|
|
*
|
|
* We rely on system facilities for actually generating the numbers.
|
|
* We support a number of sources:
|
|
*
|
|
* 1. OpenSSL's RAND_bytes()
|
|
* 2. Windows' CryptGenRandom() function
|
|
* 3. /dev/urandom
|
|
*
|
|
* Returns true on success, and false if none of the sources
|
|
* were available. NB: It is important to check the return value!
|
|
* Proceeding with key generation when no random data was available
|
|
* would lead to predictable keys and security issues.
|
|
*/
|
|
|
|
|
|
|
|
#ifdef USE_OPENSSL
|
|
|
|
#include <openssl/rand.h>
|
|
|
|
void
|
|
pg_strong_random_init(void)
|
|
{
|
|
/*
|
|
* Make sure processes do not share OpenSSL randomness state. This is no
|
|
* longer required in OpenSSL 1.1.1 and later versions, but until we drop
|
|
* support for version < 1.1.1 we need to do this.
|
|
*/
|
|
RAND_poll();
|
|
}
|
|
|
|
bool
|
|
pg_strong_random(void *buf, size_t len)
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
* Check that OpenSSL's CSPRNG has been sufficiently seeded, and if not
|
|
* add more seed data using RAND_poll(). With some older versions of
|
|
* OpenSSL, it may be necessary to call RAND_poll() a number of times. If
|
|
* RAND_poll() fails to generate seed data within the given amount of
|
|
* retries, subsequent RAND_bytes() calls will fail, but we allow that to
|
|
* happen to let pg_strong_random() callers handle that with appropriate
|
|
* error handling.
|
|
*/
|
|
#define NUM_RAND_POLL_RETRIES 8
|
|
|
|
for (i = 0; i < NUM_RAND_POLL_RETRIES; i++)
|
|
{
|
|
if (RAND_status() == 1)
|
|
{
|
|
/* The CSPRNG is sufficiently seeded */
|
|
break;
|
|
}
|
|
|
|
RAND_poll();
|
|
}
|
|
|
|
if (RAND_bytes(buf, len) == 1)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
#elif WIN32
|
|
|
|
#include <wincrypt.h>
|
|
/*
|
|
* Cache a global crypto provider that only gets freed when the process
|
|
* exits, in case we need random numbers more than once.
|
|
*/
|
|
static HCRYPTPROV hProvider = 0;
|
|
|
|
void
|
|
pg_strong_random_init(void)
|
|
{
|
|
/* No initialization needed on WIN32 */
|
|
}
|
|
|
|
bool
|
|
pg_strong_random(void *buf, size_t len)
|
|
{
|
|
if (hProvider == 0)
|
|
{
|
|
if (!CryptAcquireContext(&hProvider,
|
|
NULL,
|
|
MS_DEF_PROV,
|
|
PROV_RSA_FULL,
|
|
CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
|
|
{
|
|
/*
|
|
* On failure, set back to 0 in case the value was for some reason
|
|
* modified.
|
|
*/
|
|
hProvider = 0;
|
|
}
|
|
}
|
|
/* Re-check in case we just retrieved the provider */
|
|
if (hProvider != 0)
|
|
{
|
|
if (CryptGenRandom(hProvider, len, buf))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#else /* not USE_OPENSSL or WIN32 */
|
|
|
|
/*
|
|
* Without OpenSSL or Win32 support, just read /dev/urandom ourselves.
|
|
*/
|
|
|
|
void
|
|
pg_strong_random_init(void)
|
|
{
|
|
/* No initialization needed */
|
|
}
|
|
|
|
bool
|
|
pg_strong_random(void *buf, size_t len)
|
|
{
|
|
int f;
|
|
char *p = buf;
|
|
ssize_t res;
|
|
|
|
f = open("/dev/urandom", O_RDONLY, 0);
|
|
if (f == -1)
|
|
return false;
|
|
|
|
while (len)
|
|
{
|
|
res = read(f, p, len);
|
|
if (res <= 0)
|
|
{
|
|
if (errno == EINTR)
|
|
continue; /* interrupted by signal, just retry */
|
|
|
|
close(f);
|
|
return false;
|
|
}
|
|
|
|
p += res;
|
|
len -= res;
|
|
}
|
|
|
|
close(f);
|
|
return true;
|
|
}
|
|
#endif
|