/*------------------------------------------------------------------------- * * 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-2022, PostgreSQL Global Development Group * * IDENTIFICATION * src/port/pg_strong_random.c * *------------------------------------------------------------------------- */ #include "c.h" #include #include #include /* * 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 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 /* * 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