Replace PostmasterRandom() with a stronger source, second attempt.
This adds a new routine, pg_strong_random() for generating random bytes,
for use in both frontend and backend. At the moment, it's only used in
the backend, but the upcoming SCRAM authentication patches need strong
random numbers in libpq as well.
pg_strong_random() is based on, and replaces, the existing implementation
in pgcrypto. It can acquire strong random numbers from a number of sources,
depending on what's available:
- OpenSSL RAND_bytes(), if built with OpenSSL
- On Windows, the native cryptographic functions are used
- /dev/urandom
Unlike the current pgcrypto function, the source is chosen by configure.
That makes it easier to test different implementations, and ensures that
we don't accidentally fall back to a less secure implementation, if the
primary source fails. All of those methods are quite reliable, it would be
pretty surprising for them to fail, so we'd rather find out by failing
hard.
If no strong random source is available, we fall back to using erand48(),
seeded from current timestamp, like PostmasterRandom() was. That isn't
cryptographically secure, but allows us to still work on platforms that
don't have any of the above stronger sources. Because it's not very secure,
the built-in implementation is only used if explicitly requested with
--disable-strong-random.
This replaces the more complicated Fortuna algorithm we used to have in
pgcrypto, which is unfortunate, but all modern platforms have /dev/urandom,
so it doesn't seem worth the maintenance effort to keep that. pgcrypto
functions that require strong random numbers will be disabled with
--disable-strong-random.
Original patch by Magnus Hagander, tons of further work by Michael Paquier
and me.
Discussion: https://www.postgresql.org/message-id/CAB7nPqRy3krN8quR9XujMVVHYtXJ0_60nqgVc6oUk8ygyVkZsA@mail.gmail.com
Discussion: https://www.postgresql.org/message-id/CAB7nPqRWkNYRRPJA7-cF+LfroYV10pvjdz6GNvxk-Eee9FypKA@mail.gmail.com
2016-12-05 12:42:59 +01:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
2018-01-03 05:30:12 +01:00
|
|
|
* Copyright (c) 1996-2018, PostgreSQL Global Development Group
|
Replace PostmasterRandom() with a stronger source, second attempt.
This adds a new routine, pg_strong_random() for generating random bytes,
for use in both frontend and backend. At the moment, it's only used in
the backend, but the upcoming SCRAM authentication patches need strong
random numbers in libpq as well.
pg_strong_random() is based on, and replaces, the existing implementation
in pgcrypto. It can acquire strong random numbers from a number of sources,
depending on what's available:
- OpenSSL RAND_bytes(), if built with OpenSSL
- On Windows, the native cryptographic functions are used
- /dev/urandom
Unlike the current pgcrypto function, the source is chosen by configure.
That makes it easier to test different implementations, and ensures that
we don't accidentally fall back to a less secure implementation, if the
primary source fails. All of those methods are quite reliable, it would be
pretty surprising for them to fail, so we'd rather find out by failing
hard.
If no strong random source is available, we fall back to using erand48(),
seeded from current timestamp, like PostmasterRandom() was. That isn't
cryptographically secure, but allows us to still work on platforms that
don't have any of the above stronger sources. Because it's not very secure,
the built-in implementation is only used if explicitly requested with
--disable-strong-random.
This replaces the more complicated Fortuna algorithm we used to have in
pgcrypto, which is unfortunate, but all modern platforms have /dev/urandom,
so it doesn't seem worth the maintenance effort to keep that. pgcrypto
functions that require strong random numbers will be disabled with
--disable-strong-random.
Original patch by Magnus Hagander, tons of further work by Michael Paquier
and me.
Discussion: https://www.postgresql.org/message-id/CAB7nPqRy3krN8quR9XujMVVHYtXJ0_60nqgVc6oUk8ygyVkZsA@mail.gmail.com
Discussion: https://www.postgresql.org/message-id/CAB7nPqRWkNYRRPJA7-cF+LfroYV10pvjdz6GNvxk-Eee9FypKA@mail.gmail.com
2016-12-05 12:42:59 +01:00
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
|
|
|
* src/port/pg_strong_random.c
|
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef FRONTEND
|
|
|
|
#include "postgres.h"
|
|
|
|
#else
|
|
|
|
#include "postgres_fe.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
|
|
|
#ifdef USE_OPENSSL
|
|
|
|
#include <openssl/rand.h>
|
|
|
|
#endif
|
|
|
|
#ifdef WIN32
|
2017-04-15 15:47:36 +02:00
|
|
|
#include <wincrypt.h>
|
Replace PostmasterRandom() with a stronger source, second attempt.
This adds a new routine, pg_strong_random() for generating random bytes,
for use in both frontend and backend. At the moment, it's only used in
the backend, but the upcoming SCRAM authentication patches need strong
random numbers in libpq as well.
pg_strong_random() is based on, and replaces, the existing implementation
in pgcrypto. It can acquire strong random numbers from a number of sources,
depending on what's available:
- OpenSSL RAND_bytes(), if built with OpenSSL
- On Windows, the native cryptographic functions are used
- /dev/urandom
Unlike the current pgcrypto function, the source is chosen by configure.
That makes it easier to test different implementations, and ensures that
we don't accidentally fall back to a less secure implementation, if the
primary source fails. All of those methods are quite reliable, it would be
pretty surprising for them to fail, so we'd rather find out by failing
hard.
If no strong random source is available, we fall back to using erand48(),
seeded from current timestamp, like PostmasterRandom() was. That isn't
cryptographically secure, but allows us to still work on platforms that
don't have any of the above stronger sources. Because it's not very secure,
the built-in implementation is only used if explicitly requested with
--disable-strong-random.
This replaces the more complicated Fortuna algorithm we used to have in
pgcrypto, which is unfortunate, but all modern platforms have /dev/urandom,
so it doesn't seem worth the maintenance effort to keep that. pgcrypto
functions that require strong random numbers will be disabled with
--disable-strong-random.
Original patch by Magnus Hagander, tons of further work by Michael Paquier
and me.
Discussion: https://www.postgresql.org/message-id/CAB7nPqRy3krN8quR9XujMVVHYtXJ0_60nqgVc6oUk8ygyVkZsA@mail.gmail.com
Discussion: https://www.postgresql.org/message-id/CAB7nPqRWkNYRRPJA7-cF+LfroYV10pvjdz6GNvxk-Eee9FypKA@mail.gmail.com
2016-12-05 12:42:59 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(USE_DEV_URANDOM)
|
|
|
|
/*
|
|
|
|
* Read (random) bytes from a file.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
random_from_file(char *filename, void *buf, size_t len)
|
|
|
|
{
|
|
|
|
int f;
|
|
|
|
char *p = buf;
|
|
|
|
ssize_t res;
|
|
|
|
|
|
|
|
f = open(filename, 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
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pg_strong_random
|
|
|
|
*
|
|
|
|
* Generate requested number of random bytes. The returned bytes are
|
|
|
|
* cryptographically secure, suitable for use e.g. in authentication.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*
|
|
|
|
* The configure script will choose which one to use, and set
|
|
|
|
* a USE_*_RANDOM flag accordingly.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
pg_strong_random(void *buf, size_t len)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* When built with OpenSSL, use OpenSSL's RAND_bytes function.
|
|
|
|
*/
|
|
|
|
#if defined(USE_OPENSSL_RANDOM)
|
|
|
|
if (RAND_bytes(buf, len) == 1)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Windows has CryptoAPI for strong cryptographic numbers.
|
|
|
|
*/
|
|
|
|
#elif defined(USE_WIN32_RANDOM)
|
|
|
|
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;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read /dev/urandom ourselves.
|
|
|
|
*/
|
|
|
|
#elif defined(USE_DEV_URANDOM)
|
|
|
|
if (random_from_file("/dev/urandom", buf, len))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
|
|
|
|
#else
|
|
|
|
/* The autoconf script should not have allowed this */
|
|
|
|
#error no source of random numbers configured
|
|
|
|
#endif
|
|
|
|
}
|