From d9a41e87fa10f6a54760d43d3987134370632442 Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Wed, 12 Oct 2016 15:10:44 +0900 Subject: [PATCH 1/8] Introduce pg_strong_random() This new routine, available for both frontend and backend, is aimed at producing strong random numbers based on a hierarchy: - OpenSSL if support is built. - On Windows, the native cryptographic functions are used. - /dev/urandom. - /dev/random. PostmasterRandom is replaced by that, making the initial random number generation less predictible. --- src/backend/postmaster/postmaster.c | 89 ++--------------------- src/include/port.h | 3 + src/port/Makefile | 2 +- src/port/pgsrandom.c | 140 ++++++++++++++++++++++++++++++++++++ src/tools/msvc/Mkvcbuild.pm | 2 +- 5 files changed, 150 insertions(+), 86 deletions(-) create mode 100644 src/port/pgsrandom.c diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 2d43506..8bf69ea 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -358,14 +358,6 @@ static volatile bool avlauncher_needs_signal = false; 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 @@ -403,8 +395,6 @@ static void processCancelRequest(Port *port, void *pkt); 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); @@ -581,7 +571,7 @@ PostmasterMain(int argc, char *argv[]) * Note: the seed is pretty predictable from externally-visible facts such * 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. + * connection, pg_strong_random will select a hopefully-more-random seed. */ srandom((unsigned int) (MyProcPid ^ MyStartTime)); @@ -1292,8 +1282,6 @@ PostmasterMain(int argc, char *argv[]) * Remember postmaster startup time */ PgStartTime = GetCurrentTimestamp(); - /* PostmasterRandom wants its own copy */ - gettimeofday(&random_start_time, NULL); /* * We're ready to rock and roll... @@ -2350,7 +2338,7 @@ ConnCreate(int serverFd) * 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)); + pg_strong_random(port->md5Salt, sizeof(port->md5Salt)); /* * Allocate GSSAPI specific state struct @@ -3904,7 +3892,7 @@ BackendStartup(Port *port) * backend will have its own copy in the forked-off process' value of * MyCancelKey, so that it can transmit the key to the frontend. */ - MyCancelKey = PostmasterRandom(); + pg_strong_random(&MyCancelKey, sizeof(MyCancelKey)); bn->cancel_key = MyCancelKey; /* Pass down canAcceptConnections state */ @@ -4212,13 +4200,6 @@ BackendRun(Port *port) 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)); @@ -5067,66 +5048,6 @@ StartupPacketTimeoutHandler(void) /* - * 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). */ @@ -5303,7 +5224,7 @@ StartAutovacuumWorker(void) * we'd better have something random in the field to prevent * unfriendly people from sending cancels to them. */ - MyCancelKey = PostmasterRandom(); + pg_strong_random(&MyCancelKey, sizeof(MyCancelKey)); bn->cancel_key = MyCancelKey; /* Autovac workers are not dead_end and need a child slot */ @@ -5615,7 +5536,7 @@ assign_backendlist_entry(RegisteredBgWorker *rw) * have something random in the field to prevent unfriendly people from * sending cancels to them. */ - MyCancelKey = PostmasterRandom(); + pg_strong_random(&MyCancelKey, sizeof(MyCancelKey)); bn->cancel_key = MyCancelKey; bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot(); diff --git a/src/include/port.h b/src/include/port.h index b81fa4a..957dfe4 100644 --- a/src/include/port.h +++ b/src/include/port.h @@ -460,6 +460,9 @@ extern int pg_check_dir(const char *dir); /* port/pgmkdirp.c */ extern int pg_mkdir_p(char *path, int omode); +/* port/pgsrandom.c */ +extern void pg_strong_random(void *buf, size_t len); + /* port/pqsignal.c */ typedef void (*pqsigfunc) (int signo); extern pqsigfunc pqsignal(int signo, pqsigfunc func); diff --git a/src/port/Makefile b/src/port/Makefile index bc9b63a..7150043 100644 --- a/src/port/Makefile +++ b/src/port/Makefile @@ -32,7 +32,7 @@ LIBS += $(PTHREAD_LIBS) OBJS = $(LIBOBJS) $(PG_CRC32C_OBJS) chklocale.o erand48.o inet_net_ntop.o \ noblock.o path.o pgcheckdir.o pgmkdirp.o pgsleep.o \ - pgstrcasecmp.o pqsignal.o \ + pgsrandom.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 diff --git a/src/port/pgsrandom.c b/src/port/pgsrandom.c new file mode 100644 index 0000000..9aee85e --- /dev/null +++ b/src/port/pgsrandom.c @@ -0,0 +1,140 @@ +/*------------------------------------------------------------------------- + * + * pgsrandom.c + * pg_strong_random() function to return a strong random number + * + * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * src/port/pgsrandom.c + * + *------------------------------------------------------------------------- + */ + +#ifndef FRONTEND +#include "postgres.h" +#else +#include "postgres_fe.h" +#endif + +#include +#include + +#ifdef USE_SSL +#include +#endif + +static bool +random_from_file(char *filename, void *buf, size_t len); + +#ifdef WIN32 +/* + * Cache a global crypto provider that gets freed when the process + * exits only, in case we're going to end up getting random numbers + * more than once. + */ +static HCRYPTPROV hProvider = 0; +#endif + +/* + * Read a random number from a file. This is wanted as non-interruptible + * to give the user the insurance that a random number is returned. + */ +static bool +random_from_file(char *filename, void *buf, size_t len) +{ + int f; + char *p = buf; + int 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; + close(f); + return false; + } + + p += res; + len -= res; + } + + close(f); + return true; +} + +/* + * pg_strong_random + * + * Generate a strong random number based on a hierarchy of what is available, + * falling back to the next method if the previous one failed: + * - First try OpenSSL's RAND_bytes() if support is provided. + * - On Windows, use the in-core cryptographic functions. + * - Use /dev/urandom. + * - Use /dev/random. + */ +void +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; +#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; + } +#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; + if (random_from_file("/dev/random", buf, len)) + return; + + /* This should really be impossible */ +#ifndef FRONTEND + elog(FATAL, "could not generate random data."); +#else + fprintf(stderr, _("could not generate random data.")); + exit(1); +#endif +} diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index de764dd..eae8630 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -92,7 +92,7 @@ sub mkvcbuild 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 qsort.c qsort_arg.c quotes.c system.c + mkdtemp.c pgsrandom.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); -- 2.10.1