# contrib/pgcrypto/Makefile
INT_SRCS = md5.c sha1.c sha2.c internal.c internal-sha2.c blf.c rijndael.c \
- fortuna.c pgp-mpi-internal.c imath.c
+ fortuna.c random.c pgp-mpi-internal.c imath.c
INT_TESTS = sha2
OSSL_SRCS = openssl.c pgp-mpi-openssl.c
static void
system_reseed(void)
{
+ uint8 buf[1024];
+ int n;
time_t t;
int skip = 1;
else if (check_time == 0 ||
(t - check_time) > SYSTEM_RESEED_CHECK_TIME)
{
- uint8 buf;
-
check_time = t;
/* roll dice */
- px_get_random_bytes(&buf, 1);
- skip = (buf >= SYSTEM_RESEED_CHANCE);
-
- /* clear 1 byte */
- px_memset(&buf, 0, sizeof(buf));
- }
- if (!skip)
- {
- /*
- * fortuna_add_entropy passes the input to SHA-256, so there's no
- * point in giving it more than 256 bits of input to begin with.
- */
- uint8 buf[32];
-
- if (!pg_strong_random(buf, sizeof(buf)))
- ereport(ERROR,
- (errcode(ERRCODE_INTERNAL_ERROR),
- errmsg("could not acquire random data")));
- fortuna_add_entropy(buf, sizeof(buf));
-
- seed_time = t;
- px_memset(buf, 0, sizeof(buf));
+ px_get_random_bytes(buf, 1);
+ skip = buf[0] >= SYSTEM_RESEED_CHANCE;
}
+ /* clear 1 byte */
+ px_memset(buf, 0, sizeof(buf));
+
+ if (skip)
+ return;
+
+ n = px_acquire_system_randomness(buf);
+ if (n > 0)
+ fortuna_add_entropy(buf, n);
+
+ seed_time = t;
+ px_memset(buf, 0, sizeof(buf));
}
int
--- /dev/null
+/*
+ * random.c
+ * Acquire randomness from system. For seeding RNG.
+ *
+ * Copyright (c) 2001 Marko Kreen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * contrib/pgcrypto/random.c
+ */
+
+#include "postgres.h"
+
+#include "px.h"
+#include "utils/memdebug.h"
+
+/* how many bytes to ask from system random provider */
+#define RND_BYTES 32
+
+/*
+ * Try to read from /dev/urandom or /dev/random on these OS'es.
+ *
+ * The list can be pretty liberal, as the device not existing
+ * is expected event.
+ */
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) \
+ || defined(__NetBSD__) || defined(__DragonFly__) \
+ || defined(__darwin__) || defined(__SOLARIS__) \
+ || defined(__hpux) || defined(__HPUX__) \
+ || defined(__CYGWIN__) || defined(_AIX)
+
+#define TRY_DEV_RANDOM
+
+#include <fcntl.h>
+#include <unistd.h>
+
+static int
+safe_read(int fd, void *buf, size_t count)
+{
+ int done = 0;
+ char *p = buf;
+ int res;
+
+ while (count)
+ {
+ res = read(fd, p, count);
+ if (res <= 0)
+ {
+ if (errno == EINTR)
+ continue;
+ return PXE_DEV_READ_ERROR;
+ }
+ p += res;
+ done += res;
+ count -= res;
+ }
+ return done;
+}
+
+static uint8 *
+try_dev_random(uint8 *dst)
+{
+ int fd;
+ int res;
+
+ fd = open("/dev/urandom", O_RDONLY, 0);
+ if (fd == -1)
+ {
+ fd = open("/dev/random", O_RDONLY, 0);
+ if (fd == -1)
+ return dst;
+ }
+ res = safe_read(fd, dst, RND_BYTES);
+ close(fd);
+ if (res > 0)
+ dst += res;
+ return dst;
+}
+#endif
+
+/*
+ * Try to find randomness on Windows
+ */
+#ifdef WIN32
+
+#define TRY_WIN32_GENRAND
+#define TRY_WIN32_PERFC
+
+#include <windows.h>
+#include <wincrypt.h>
+
+/*
+ * this function is from libtomcrypt
+ *
+ * try to use Microsoft crypto API
+ */
+static uint8 *
+try_win32_genrand(uint8 *dst)
+{
+ int res;
+ HCRYPTPROV h = 0;
+
+ res = CryptAcquireContext(&h, NULL, MS_DEF_PROV, PROV_RSA_FULL,
+ (CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET));
+ if (!res)
+ res = CryptAcquireContext(&h, NULL, MS_DEF_PROV, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET);
+ if (!res)
+ return dst;
+
+ res = CryptGenRandom(h, RND_BYTES, dst);
+ if (res == TRUE)
+ dst += RND_BYTES;
+
+ CryptReleaseContext(h, 0);
+ return dst;
+}
+
+static uint8 *
+try_win32_perfc(uint8 *dst)
+{
+ int res;
+ LARGE_INTEGER time;
+
+ res = QueryPerformanceCounter(&time);
+ if (!res)
+ return dst;
+
+ memcpy(dst, &time, sizeof(time));
+ return dst + sizeof(time);
+}
+#endif /* WIN32 */
+
+
+/*
+ * If we are not on Windows, then hopefully we are
+ * on a unix-like system. Use the usual suspects
+ * for randomness.
+ */
+#ifndef WIN32
+
+#define TRY_UNIXSTD
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+/*
+ * Everything here is predictible, only needs some patience.
+ *
+ * But there is a chance that the system-specific functions
+ * did not work. So keep faith and try to slow the attacker down.
+ */
+static uint8 *
+try_unix_std(uint8 *dst)
+{
+ pid_t pid;
+ int x;
+ PX_MD *md;
+ struct timeval tv;
+ int res;
+
+ /* process id */
+ pid = getpid();
+ memcpy(dst, (uint8 *) &pid, sizeof(pid));
+ dst += sizeof(pid);
+
+ /* time */
+ gettimeofday(&tv, NULL);
+ memcpy(dst, (uint8 *) &tv, sizeof(tv));
+ dst += sizeof(tv);
+
+ /* pointless, but should not hurt */
+ x = random();
+ memcpy(dst, (uint8 *) &x, sizeof(x));
+ dst += sizeof(x);
+
+ /* hash of uninitialized stack and heap allocations */
+ res = px_find_digest("sha1", &md);
+ if (res >= 0)
+ {
+ uint8 *ptr;
+ uint8 stack[8192];
+ int alloc = 32 * 1024;
+
+ VALGRIND_MAKE_MEM_DEFINED(stack, sizeof(stack));
+ px_md_update(md, stack, sizeof(stack));
+ ptr = px_alloc(alloc);
+ VALGRIND_MAKE_MEM_DEFINED(ptr, alloc);
+ px_md_update(md, ptr, alloc);
+ px_free(ptr);
+
+ px_md_finish(md, dst);
+ px_md_free(md);
+
+ dst += 20;
+ }
+
+ return dst;
+}
+#endif
+
+/*
+ * try to extract some randomness for initial seeding
+ *
+ * dst should have room for 1024 bytes.
+ */
+unsigned
+px_acquire_system_randomness(uint8 *dst)
+{
+ uint8 *p = dst;
+
+#ifdef TRY_DEV_RANDOM
+ p = try_dev_random(p);
+#endif
+#ifdef TRY_WIN32_GENRAND
+ p = try_win32_genrand(p);
+#endif
+#ifdef TRY_WIN32_PERFC
+ p = try_win32_perfc(p);
+#endif
+#ifdef TRY_UNIXSTD
+ p = try_unix_std(p);
+#endif
+ return p - dst;
+}
static char *recv_password_packet(Port *port);
static int recv_and_check_password_packet(Port *port, char **logdetail);
-/*----------------------------------------------------------------
- * MD5 authentication
- *----------------------------------------------------------------
- */
-static int CheckMD5Auth(Port *port, char **logdetail);
-
/*----------------------------------------------------------------
* Ident authentication
ereport(FATAL,
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
errmsg("MD5 authentication is not supported when \"db_user_namespace\" is enabled")));
- status = CheckMD5Auth(port, &logdetail);
+ /* include the salt to use for computing the response */
+ sendAuthRequest(port, AUTH_REQ_MD5, port->md5Salt, 4);
+ status = recv_and_check_password_packet(port, &logdetail);
break;
case uaPassword:
/*----------------------------------------------------------------
- * MD5 and password authentication
+ * MD5 authentication
*----------------------------------------------------------------
*/
-static int
-CheckMD5Auth(Port *port, char **logdetail)
-{
- /* include the salt to use for computing the response */
- if (!pg_strong_random(port->md5Salt, sizeof(port->md5Salt)))
- {
- *logdetail = psprintf(_("Could not generate random salt"));
- return STATUS_ERROR;
- }
-
- sendAuthRequest(port, AUTH_REQ_MD5, port->md5Salt, 4);
- return recv_and_check_password_packet(port, logdetail);
-}
-
-
/*
* Called when we have sent an authorization request for a password.
* Get the response and check it.
static volatile bool StartWorkerNeeded = true;
static volatile bool HaveCrashedWorker = false;
+/*
+ * State for assigning random salts and cancel keys.
+ * Also, the global MyCancelKey passes the cancel key assigned to a given
+ * backend from the postmaster to that backend (via fork).
+ */
+static unsigned int random_seed = 0;
+static struct timeval random_start_time;
+
#ifdef USE_BONJOUR
static DNSServiceRef bonjour_sdref = NULL;
#endif
static int initMasks(fd_set *rmask);
static void report_fork_failure_to_client(Port *port, int errnum);
static CAC_state canAcceptConnections(void);
+static long PostmasterRandom(void);
+static void RandomSalt(char *salt, int len);
static void signal_child(pid_t pid, int signal);
static bool SignalSomeChildren(int signal, int targets);
static void TerminateChildren(int signal);
* Initialize random(3) so we don't get the same values in every run.
*
* Note: the seed is pretty predictable from externally-visible facts such
- * as postmaster start time, so don't use random() for security-critical
- * random values (use pg_strong_random() instead). Backends select a
- * somewhat more random seed after forking, in BackendRun(), based on the
- * PID and session start timestamp, but that is still not suitable for
- * security-critical values.
+ * as postmaster start time, so avoid using random() for security-critical
+ * random values during postmaster startup. At the time of first
+ * connection, PostmasterRandom will select a hopefully-more-random seed.
*/
srandom((unsigned int) (MyProcPid ^ MyStartTime));
* Remember postmaster startup time
*/
PgStartTime = GetCurrentTimestamp();
+ /* PostmasterRandom wants its own copy */
+ gettimeofday(&random_start_time, NULL);
/*
* We're ready to rock and roll...
return NULL;
}
+ /*
+ * Precompute password salt values to use for this connection. It's
+ * slightly annoying to do this long in advance of knowing whether we'll
+ * need 'em or not, but we must do the random() calls before we fork, not
+ * after. Else the postmaster's random sequence won't get advanced, and
+ * all backends would end up using the same salt...
+ */
+ RandomSalt(port->md5Salt, sizeof(port->md5Salt));
+
/*
* Allocate GSSAPI specific state struct
*/
* backend will have its own copy in the forked-off process' value of
* MyCancelKey, so that it can transmit the key to the frontend.
*/
- if (!pg_strong_random(&MyCancelKey, sizeof(MyCancelKey)))
- {
- ereport(LOG,
- (errmsg("could not generate random query cancel key")));
- return STATUS_ERROR;
- }
+ MyCancelKey = PostmasterRandom();
bn->cancel_key = MyCancelKey;
/* Pass down canAcceptConnections state */
int usecs;
int i;
+ /*
+ * Don't want backend to be able to see the postmaster random number
+ * generator state. We have to clobber the static random_seed *and* start
+ * a new random sequence in the random() library function.
+ */
+ random_seed = 0;
+ random_start_time.tv_usec = 0;
/* slightly hacky way to convert timestamptz into integers */
TimestampDifference(0, port->SessionStartTime, &secs, &usecs);
srandom((unsigned int) (MyProcPid ^ (usecs << 12) ^ secs));
}
+/*
+ * RandomSalt
+ */
+static void
+RandomSalt(char *salt, int len)
+{
+ long rand;
+ int i;
+
+ /*
+ * We use % 255, sacrificing one possible byte value, so as to ensure that
+ * all bits of the random() value participate in the result. While at it,
+ * add one to avoid generating any null bytes.
+ */
+ for (i = 0; i < len; i++)
+ {
+ rand = PostmasterRandom();
+ salt[i] = (rand % 255) + 1;
+ }
+}
+
+/*
+ * PostmasterRandom
+ *
+ * Caution: use this only for values needed during connection-request
+ * processing. Otherwise, the intended property of having an unpredictable
+ * delay between random_start_time and random_stop_time will be broken.
+ */
+static long
+PostmasterRandom(void)
+{
+ /*
+ * Select a random seed at the time of first receiving a request.
+ */
+ if (random_seed == 0)
+ {
+ do
+ {
+ struct timeval random_stop_time;
+
+ gettimeofday(&random_stop_time, NULL);
+
+ /*
+ * We are not sure how much precision is in tv_usec, so we swap
+ * the high and low 16 bits of 'random_stop_time' and XOR them
+ * with 'random_start_time'. On the off chance that the result is
+ * 0, we loop until it isn't.
+ */
+ random_seed = random_start_time.tv_usec ^
+ ((random_stop_time.tv_usec << 16) |
+ ((random_stop_time.tv_usec >> 16) & 0xffff));
+ }
+ while (random_seed == 0);
+
+ srandom(random_seed);
+ }
+
+ return random();
+}
+
/*
* Count up number of child processes of specified types (dead_end chidren
* are always excluded).
* we'd better have something random in the field to prevent
* unfriendly people from sending cancels to them.
*/
- if (pg_strong_random(&MyCancelKey, sizeof(MyCancelKey)))
- {
- bn->cancel_key = MyCancelKey;
+ MyCancelKey = PostmasterRandom();
+ bn->cancel_key = MyCancelKey;
- /* Autovac workers are not dead_end and need a child slot */
- bn->dead_end = false;
- bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
- bn->bgworker_notify = false;
+ /* Autovac workers are not dead_end and need a child slot */
+ bn->dead_end = false;
+ bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
+ bn->bgworker_notify = false;
- bn->pid = StartAutoVacWorker();
- if (bn->pid > 0)
- {
- bn->bkend_type = BACKEND_TYPE_AUTOVAC;
- dlist_push_head(&BackendList, &bn->elem);
+ bn->pid = StartAutoVacWorker();
+ if (bn->pid > 0)
+ {
+ bn->bkend_type = BACKEND_TYPE_AUTOVAC;
+ dlist_push_head(&BackendList, &bn->elem);
#ifdef EXEC_BACKEND
- ShmemBackendArrayAdd(bn);
+ ShmemBackendArrayAdd(bn);
#endif
- /* all OK */
- return;
- }
-
- /*
- * fork failed, fall through to report -- actual error message
- * was logged by StartAutoVacWorker
- */
- (void) ReleasePostmasterChildSlot(bn->child_slot);
+ /* all OK */
+ return;
}
- else
- ereport(LOG,
- (errmsg("could not generate random query cancel key")));
+ /*
+ * fork failed, fall through to report -- actual error message was
+ * logged by StartAutoVacWorker
+ */
+ (void) ReleasePostmasterChildSlot(bn->child_slot);
free(bn);
}
else
* have something random in the field to prevent unfriendly people from
* sending cancels to them.
*/
- if (!pg_strong_random(&MyCancelKey, sizeof(MyCancelKey)))
- {
- rw->rw_crashed_at = GetCurrentTimestamp();
- return false;
- }
+ MyCancelKey = PostmasterRandom();
bn->cancel_key = MyCancelKey;
bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
extern char *inet_net_ntop(int af, const void *src, int bits,
char *dst, size_t size);
-/* port/pg_strong_random.c */
-extern bool pg_strong_random(void *buf, size_t len);
-
/* port/pgcheckdir.c */
extern int pg_check_dir(const char *dir);
OBJS = $(LIBOBJS) $(PG_CRC32C_OBJS) chklocale.o erand48.o inet_net_ntop.o \
noblock.o path.o pgcheckdir.o pgmkdirp.o pgsleep.o \
- pg_strong_random.o pgstrcasecmp.o pqsignal.o \
+ pgstrcasecmp.o pqsignal.o \
qsort.o qsort_arg.o quotes.o sprompt.o tar.o thread.o
# foo_srv.o and foo.o are both built from foo.c, but only foo.o has -DFRONTEND
+++ /dev/null
-/*-------------------------------------------------------------------------
- *
- * pg_strong_random.c
- * pg_strong_random() function to return a strong random number
- *
- * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
- *
- *
- * IDENTIFICATION
- * src/port/pg_strong_random.c
- *
- *-------------------------------------------------------------------------
- */
-
-#ifndef FRONTEND
-#include "postgres.h"
-#else
-#include "postgres_fe.h"
-#endif
-
-#include <fcntl.h>
-#include <unistd.h>
-
-#ifdef USE_SSL
-#include <openssl/rand.h>
-#endif
-#ifdef WIN32
-#include <Wincrypt.h>
-#endif
-
-static bool random_from_file(char *filename, void *buf, size_t len);
-
-#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
-
-/*
- * 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;
-}
-
-/*
- * pg_strong_random
- *
- * Generate requested number of random bytes. The bytes are
- * cryptographically strong random, suitable for use e.g. in key
- * generation.
- *
- * The bytes can be acquired from a number of sources, depending
- * on what's available. We try the following, in this order:
- *
- * 1. OpenSSL's RAND_bytes()
- * 2. Windows' CryptGenRandom() function
- * 3. /dev/urandom
- * 4. /dev/random
- *
- * 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)
-{
-#ifdef USE_SSL
-
- /*
- * When built with OpenSSL, first try the random generation function from
- * there.
- */
- if (RAND_bytes(buf, len) == 1)
- return true;
-#endif
-
-#ifdef WIN32
-
- /*
- * Windows has CryptoAPI for strong cryptographic numbers.
- */
- 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;
- }
-#endif
-
- /*
- * If there is no OpenSSL and no CryptoAPI (or they didn't work), then
- * fall back on reading /dev/urandom or even /dev/random.
- */
- if (random_from_file("/dev/urandom", buf, len))
- return true;
- if (random_from_file("/dev/random", buf, len))
- return true;
-
- /* None of the sources were available. */
- return false;
-}
srandom.c getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
erand48.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
pgcheckdir.c pgmkdirp.c pgsleep.c pgstrcasecmp.c pqsignal.c
- mkdtemp.c pg_strong_random.c qsort.c qsort_arg.c quotes.c system.c
+ mkdtemp.c qsort.c qsort_arg.c quotes.c system.c
sprompt.c tar.c thread.c getopt.c getopt_long.c dirent.c
win32env.c win32error.c win32security.c win32setlocale.c);
'sha1.c', 'sha2.c',
'internal.c', 'internal-sha2.c',
'blf.c', 'rijndael.c',
- 'fortuna.c', 'pgp-mpi-internal.c',
- 'imath.c');
+ 'fortuna.c', 'random.c',
+ 'pgp-mpi-internal.c', 'imath.c');
}
$pgcrypto->AddReference($postgres);
$pgcrypto->AddLibrary('ws2_32.lib');