diff options
Diffstat (limited to 'src/backend/postmaster/postmaster.c')
-rw-r--r-- | src/backend/postmaster/postmaster.c | 153 |
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(); |