aboutsummaryrefslogtreecommitdiff
path: root/src/backend/postmaster/postmaster.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/postmaster/postmaster.c')
-rw-r--r--src/backend/postmaster/postmaster.c153
1 files changed, 112 insertions, 41 deletions
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 44201386733..2d43506cd0e 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -358,6 +358,14 @@ 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
@@ -395,6 +403,8 @@ 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);
@@ -569,11 +579,9 @@ PostmasterMain(int argc, char *argv[])
* 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));
@@ -1284,6 +1292,8 @@ 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...
@@ -2334,6 +2344,15 @@ ConnCreate(int serverFd)
}
/*
+ * 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
*/
#ifndef EXEC_BACKEND
@@ -3885,12 +3904,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.
*/
- 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 */
@@ -4198,6 +4212,13 @@ 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));
@@ -5046,6 +5067,66 @@ 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).
*/
@@ -5222,37 +5303,31 @@ StartAutovacuumWorker(void)
* 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
@@ -5540,11 +5615,7 @@ assign_backendlist_entry(RegisteredBgWorker *rw)
* 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();