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")));
- /* 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);
+ status = CheckMD5Auth(port, &logdetail);
break;
case uaPassword:
*----------------------------------------------------------------
*/
+static int
+CheckMD5Auth(Port *port, char **logdetail)
+{
+ /* include the salt to use for computing the response */
+ pg_strong_random(port->md5Salt, sizeof(port->md5Salt));
+
+ 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 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.
+ * as postmaster start time, so don't use random() for security-critical
+ * random values (use pg_strong_random() instead). At the time of first
+ * connection, BackendRun() will select a somewhat-more-random seed, based
+ * on the PID and session start timestamp, but that is still not suitable
+ * for security-critical values.
*/
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.
*/
- MyCancelKey = PostmasterRandom();
+ if (!pg_strong_random(&MyCancelKey, sizeof(MyCancelKey)))
+ {
+ ereport(LOG,
+ (errmsg("could not generate random query cancel key")));
+ return STATUS_ERROR;
+ }
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.
*/
- MyCancelKey = PostmasterRandom();
- bn->cancel_key = MyCancelKey;
+ if (pg_strong_random(&MyCancelKey, sizeof(MyCancelKey)))
+ {
+ 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;
+ /* all OK */
+ return;
+ }
+
+ /*
+ * fork failed, fall through to report -- actual error message was
+ * logged by StartAutoVacWorker
+ */
+ (void) ReleasePostmasterChildSlot(bn->child_slot);
}
+ 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.
*/
- MyCancelKey = PostmasterRandom();
+ if (!pg_strong_random(&MyCancelKey, sizeof(MyCancelKey)))
+ {
+ rw->rw_crashed_at = GetCurrentTimestamp();
+ return false;
+ }
bn->cancel_key = MyCancelKey;
bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
--- /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;
+}