diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:19:15 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 12:19:15 +0000 |
commit | 6eb9c5a5657d1fe77b55cc261450f3538d35a94d (patch) | |
tree | 657d8194422a5daccecfd42d654b8a245ef7b4c8 /src/port/pg_strong_random.c | |
parent | Initial commit. (diff) | |
download | postgresql-13-6eb9c5a5657d1fe77b55cc261450f3538d35a94d.tar.xz postgresql-13-6eb9c5a5657d1fe77b55cc261450f3538d35a94d.zip |
Adding upstream version 13.4.upstream/13.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/port/pg_strong_random.c')
-rw-r--r-- | src/port/pg_strong_random.c | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/src/port/pg_strong_random.c b/src/port/pg_strong_random.c new file mode 100644 index 0000000..eed8b87 --- /dev/null +++ b/src/port/pg_strong_random.c @@ -0,0 +1,178 @@ +/*------------------------------------------------------------------------- + * + * 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-2020, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/port/pg_strong_random.c + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#include <fcntl.h> +#include <unistd.h> +#include <sys/time.h> + +#ifdef USE_OPENSSL +#include <openssl/rand.h> +#endif +#ifdef USE_WIN32_RANDOM +#include <wincrypt.h> +#endif + +#ifdef USE_WIN32_RANDOM +/* + * 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(const 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) + 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. + */ +#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; + } + + if (RAND_poll() == 0) + { + /* + * RAND_poll() failed to generate any seed data, which means that + * RAND_bytes() will probably fail. For now, just fall through + * and let that happen. XXX: maybe we could seed it some other + * way. + */ + break; + } + } + + 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 +} |