summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/libpq/auth.c62
-rw-r--r--src/backend/libpq/crypt.c10
-rw-r--r--src/backend/postmaster/postmaster.c146
-rw-r--r--src/backend/storage/ipc/ipci.c3
-rw-r--r--src/backend/storage/lmgr/lwlocknames.txt1
-rw-r--r--src/backend/utils/init/globals.c2
-rw-r--r--src/backend/utils/misc/Makefile5
-rw-r--r--src/backend/utils/misc/backend_random.c158
8 files changed, 292 insertions, 95 deletions
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 0ba85301149..5d166db5744 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -33,6 +33,7 @@
#include "miscadmin.h"
#include "replication/walsender.h"
#include "storage/ipc.h"
+#include "utils/backend_random.h"
/*----------------------------------------------------------------
@@ -43,10 +44,22 @@ static void sendAuthRequest(Port *port, AuthRequest areq, char *extradata,
int extralen);
static void auth_failed(Port *port, int status, char *logdetail);
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);
+
+/*----------------------------------------------------------------
+ * Plaintext password authentication
+ *----------------------------------------------------------------
+ */
+
+static int CheckPasswordAuth(Port *port, char **logdetail);
+
+/*----------------------------------------------------------------
* Ident authentication
*----------------------------------------------------------------
*/
@@ -536,13 +549,11 @@ ClientAuthentication(Port *port)
(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:
- sendAuthRequest(port, AUTH_REQ_PASSWORD, NULL, 0);
- status = recv_and_check_password_packet(port, &logdetail);
+ status = CheckPasswordAuth(port, &logdetail);
break;
case uaPAM:
@@ -696,23 +707,48 @@ recv_password_packet(Port *port)
*----------------------------------------------------------------
*/
-/*
- * Called when we have sent an authorization request for a password.
- * Get the response and check it.
- * On error, optionally store a detail string at *logdetail.
+static int
+CheckMD5Auth(Port *port, char **logdetail)
+{
+ char md5Salt[4]; /* Password salt */
+ char *passwd;
+ int result;
+
+ pg_backend_random(md5Salt, 4);
+
+ sendAuthRequest(port, AUTH_REQ_MD5, md5Salt, 4);
+
+ passwd = recv_password_packet(port);
+
+ if (passwd == NULL)
+ return STATUS_EOF; /* client wouldn't send password */
+
+ result = md5_crypt_verify(port, port->user_name, passwd, md5Salt, 4, logdetail);
+
+ pfree(passwd);
+
+ return result;
+}
+
+/*----------------------------------------------------------------
+ * Plaintext password authentication
+ *----------------------------------------------------------------
*/
+
static int
-recv_and_check_password_packet(Port *port, char **logdetail)
+CheckPasswordAuth(Port *port, char **logdetail)
{
char *passwd;
int result;
+ sendAuthRequest(port, AUTH_REQ_PASSWORD, NULL, 0);
+
passwd = recv_password_packet(port);
if (passwd == NULL)
return STATUS_EOF; /* client wouldn't send password */
- result = md5_crypt_verify(port, port->user_name, passwd, logdetail);
+ result = md5_crypt_verify(port, port->user_name, passwd, NULL, 0, logdetail);
pfree(passwd);
@@ -920,7 +956,7 @@ pg_GSS_recvauth(Port *port)
(unsigned int) port->gss->outbuf.length);
sendAuthRequest(port, AUTH_REQ_GSS_CONT,
- port->gss->outbuf.value, port->gss->outbuf.length);
+ port->gss->outbuf.value, port->gss->outbuf.length);
gss_release_buffer(&lmin_s, &port->gss->outbuf);
}
@@ -1166,7 +1202,7 @@ pg_SSPI_recvauth(Port *port)
port->gss->outbuf.value = outbuf.pBuffers[0].pvBuffer;
sendAuthRequest(port, AUTH_REQ_GSS_CONT,
- port->gss->outbuf.value, port->gss->outbuf.length);
+ port->gss->outbuf.value, port->gss->outbuf.length);
FreeContextBuffer(outbuf.pBuffers[0].pvBuffer);
}
diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c
index d84a1803304..35b657adbbe 100644
--- a/src/backend/libpq/crypt.c
+++ b/src/backend/libpq/crypt.c
@@ -36,7 +36,7 @@
*/
int
md5_crypt_verify(const Port *port, const char *role, char *client_pass,
- char **logdetail)
+ char *md5_salt, int md5_salt_len, char **logdetail)
{
int retval = STATUS_ERROR;
char *shadow_pass,
@@ -91,13 +91,14 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass,
switch (port->hba->auth_method)
{
case uaMD5:
+ Assert(md5_salt != NULL && md5_salt_len > 0);
crypt_pwd = palloc(MD5_PASSWD_LEN + 1);
if (isMD5(shadow_pass))
{
/* stored password already encrypted, only do salt */
if (!pg_md5_encrypt(shadow_pass + strlen("md5"),
- port->md5Salt,
- sizeof(port->md5Salt), crypt_pwd))
+ md5_salt, md5_salt_len,
+ crypt_pwd))
{
pfree(crypt_pwd);
return STATUS_ERROR;
@@ -118,8 +119,7 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass,
return STATUS_ERROR;
}
if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"),
- port->md5Salt,
- sizeof(port->md5Salt),
+ md5_salt, md5_salt_len,
crypt_pwd))
{
pfree(crypt_pwd);
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 24add74512f..f0ed5233711 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -164,7 +164,7 @@
typedef struct bkend
{
pid_t pid; /* process id of backend */
- long cancel_key; /* cancel key for cancels for this backend */
+ int32 cancel_key; /* cancel key for cancels for this backend */
int child_slot; /* PMChildSlot for this backend, if any */
/*
@@ -358,13 +358,15 @@ static volatile bool avlauncher_needs_signal = false;
static volatile bool StartWorkerNeeded = true;
static volatile bool HaveCrashedWorker = false;
+#ifndef HAVE_STRONG_RANDOM
/*
- * State for assigning random salts and cancel keys.
+ * State for assigning 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;
+#endif
#ifdef USE_BONJOUR
static DNSServiceRef bonjour_sdref = NULL;
@@ -403,8 +405,7 @@ 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 bool RandomCancelKey(int32 *cancel_key);
static void signal_child(pid_t pid, int signal);
static bool SignalSomeChildren(int signal, int targets);
static void TerminateChildren(int signal);
@@ -471,7 +472,7 @@ typedef struct
InheritableSocket portsocket;
char DataDir[MAXPGPATH];
pgsocket ListenSocket[MAXLISTEN];
- long MyCancelKey;
+ int32 MyCancelKey;
int MyPMChildSlot;
#ifndef WIN32
unsigned long UsedShmemSegID;
@@ -1292,8 +1293,10 @@ PostmasterMain(int argc, char *argv[])
* Remember postmaster startup time
*/
PgStartTime = GetCurrentTimestamp();
- /* PostmasterRandom wants its own copy */
+#ifndef HAVE_STRONG_RANDOM
+ /* RandomCancelKey wants its own copy */
gettimeofday(&random_start_time, NULL);
+#endif
/*
* We're ready to rock and roll...
@@ -2345,15 +2348,6 @@ 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
@@ -3905,7 +3899,14 @@ 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();
+ if (!RandomCancelKey(&MyCancelKey))
+ {
+ ereport(LOG,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("could not acquire random number")));
+ return STATUS_ERROR;
+ }
+
bn->cancel_key = MyCancelKey;
/* Pass down canAcceptConnections state */
@@ -4218,8 +4219,10 @@ BackendRun(Port *port)
* generator state. We have to clobber the static random_seed *and* start
* a new random sequence in the random() library function.
*/
+#ifndef HAVE_STRONG_RANDOM
random_seed = 0;
random_start_time.tv_usec = 0;
+#endif
/* slightly hacky way to convert timestamptz into integers */
TimestampDifference(0, port->SessionStartTime, &secs, &usecs);
srandom((unsigned int) (MyProcPid ^ (usecs << 12) ^ secs));
@@ -5068,63 +5071,42 @@ StartupPacketTimeoutHandler(void)
/*
- * RandomSalt
+ * Generate a random cancel key.
*/
-static void
-RandomSalt(char *salt, int len)
+static bool
+RandomCancelKey(int32 *cancel_key)
{
- long rand;
- int i;
-
+#ifdef HAVE_STRONG_RANDOM
+ return pg_strong_random((char *) cancel_key, sizeof(int32));
+#else
/*
- * 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.
+ * If built with --disable-strong-random, use plain old erand48.
+ *
+ * We cannot use pg_backend_random() in postmaster, because it stores
+ * its state in shared memory.
*/
- for (i = 0; i < len; i++)
- {
- rand = PostmasterRandom();
- salt[i] = (rand % 255) + 1;
- }
-}
+ static unsigned short seed[3];
-/*
- * 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;
+ struct timeval random_stop_time;
- gettimeofday(&random_stop_time, NULL);
+ 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);
+ seed[0] = (unsigned short) random_start_time.tv_usec;
+ seed[1] = (unsigned short) (random_stop_time.tv_usec) ^ (random_start_time.tv_usec >> 16);
+ seed[2] = (unsigned short) (random_stop_time.tv_usec >> 16);
- srandom(random_seed);
+ random_seed = 1;
}
- return random();
+ *cancel_key = pg_jrand48(seed);
+
+ return true;
+#endif
}
/*
@@ -5295,16 +5277,23 @@ StartAutovacuumWorker(void)
*/
if (canAcceptConnections() == CAC_OK)
{
+ /*
+ * Compute the cancel key that will be assigned to this session.
+ * We probably don't need cancel keys for autovac workers, but
+ * we'd better have something random in the field to prevent
+ * unfriendly people from sending cancels to them.
+ */
+ if (!RandomCancelKey(&MyCancelKey))
+ {
+ ereport(LOG,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("could not acquire random number")));
+ return;
+ }
+
bn = (Backend *) malloc(sizeof(Backend));
if (bn)
{
- /*
- * Compute the cancel key that will be assigned to this session.
- * We probably don't need cancel keys for autovac workers, but
- * we'd better have something random in the field to prevent
- * unfriendly people from sending cancels to them.
- */
- MyCancelKey = PostmasterRandom();
bn->cancel_key = MyCancelKey;
/* Autovac workers are not dead_end and need a child slot */
@@ -5592,8 +5581,25 @@ bgworker_should_start_now(BgWorkerStartTime start_time)
static bool
assign_backendlist_entry(RegisteredBgWorker *rw)
{
- Backend *bn = malloc(sizeof(Backend));
+ Backend *bn;
+ /*
+ * Compute the cancel key that will be assigned to this session. We
+ * probably don't need cancel keys for background workers, but we'd better
+ * have something random in the field to prevent unfriendly people from
+ * sending cancels to them.
+ */
+ if (!RandomCancelKey(&MyCancelKey))
+ {
+ ereport(LOG,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("could not acquire random number")));
+
+ rw->rw_crashed_at = GetCurrentTimestamp();
+ return false;
+ }
+
+ bn = malloc(sizeof(Backend));
if (bn == NULL)
{
ereport(LOG,
@@ -5610,15 +5616,7 @@ assign_backendlist_entry(RegisteredBgWorker *rw)
return false;
}
- /*
- * Compute the cancel key that will be assigned to this session. We
- * probably don't need cancel keys for background workers, but we'd better
- * have something random in the field to prevent unfriendly people from
- * sending cancels to them.
- */
- MyCancelKey = PostmasterRandom();
bn->cancel_key = MyCancelKey;
-
bn->child_slot = MyPMChildSlot = AssignPostmasterChildSlot();
bn->bkend_type = BACKEND_TYPE_BGWORKER;
bn->dead_end = false;
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index c04b17fa8ea..01bddcea16c 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -43,6 +43,7 @@
#include "storage/procsignal.h"
#include "storage/sinvaladt.h"
#include "storage/spin.h"
+#include "utils/backend_random.h"
#include "utils/snapmgr.h"
@@ -141,6 +142,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
size = add_size(size, BTreeShmemSize());
size = add_size(size, SyncScanShmemSize());
size = add_size(size, AsyncShmemSize());
+ size = add_size(size, BackendRandomShmemSize());
#ifdef EXEC_BACKEND
size = add_size(size, ShmemBackendArraySize());
#endif
@@ -253,6 +255,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
BTreeShmemInit();
SyncScanShmemInit();
AsyncShmemInit();
+ BackendRandomShmemInit();
#ifdef EXEC_BACKEND
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index f8996cd21a5..0dcf7effd41 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -47,3 +47,4 @@ CommitTsLock 39
ReplicationOriginLock 40
MultiXactTruncationLock 41
OldSnapshotTimeMapLock 42
+BackendRandomLock 43
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index c564ae396da..6ab03cea170 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -38,7 +38,7 @@ volatile uint32 CritSectionCount = 0;
int MyProcPid;
pg_time_t MyStartTime;
struct Port *MyProcPort;
-long MyCancelKey;
+int32 MyCancelKey;
int MyPMChildSlot;
/*
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index a5b487d0b64..0ad1b8b5954 100644
--- a/src/backend/utils/misc/Makefile
+++ b/src/backend/utils/misc/Makefile
@@ -14,8 +14,9 @@ include $(top_builddir)/src/Makefile.global
override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
-OBJS = guc.o help_config.o pg_config.o pg_controldata.o pg_rusage.o \
- ps_status.o rls.o sampling.o superuser.o timeout.o tzparser.o
+OBJS = backend_random.o guc.o help_config.o pg_config.o pg_controldata.o \
+ pg_rusage.o ps_status.o rls.o sampling.o superuser.o timeout.o \
+ tzparser.o
# This location might depend on the installation directories. Therefore
# we can't subsitute it into pg_config.h.
diff --git a/src/backend/utils/misc/backend_random.c b/src/backend/utils/misc/backend_random.c
new file mode 100644
index 00000000000..1bc239d1ddc
--- /dev/null
+++ b/src/backend/utils/misc/backend_random.c
@@ -0,0 +1,158 @@
+/*-------------------------------------------------------------------------
+ *
+ * backend_random.c
+ * Backend random number generation routine.
+ *
+ * pg_backend_random() function fills a buffer with random bytes. Normally,
+ * it is just a thin wrapper around pg_strong_random(), but when compiled
+ * with --disable-strong-random, we provide a built-in implementation.
+ *
+ * This function is used for generating nonces in authentication, and for
+ * random salt generation in pgcrypto. The built-in implementation is not
+ * cryptographically strong, but if the user asked for it, we'll go ahead
+ * and use it anyway.
+ *
+ * The built-in implementation uses the standard erand48 algorithm, with
+ * a seed shared between all backends.
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/misc/backend_random.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include <sys/time.h>
+
+#include "miscadmin.h"
+#include "storage/lwlock.h"
+#include "storage/shmem.h"
+#include "utils/backend_random.h"
+#include "utils/timestamp.h"
+
+#ifdef HAVE_STRONG_RANDOM
+
+Size
+BackendRandomShmemSize(void)
+{
+ return 0;
+}
+
+void
+BackendRandomShmemInit(void)
+{
+ /* do nothing */
+}
+
+bool
+pg_backend_random(char *dst, int len)
+{
+ /* should not be called in postmaster */
+ Assert (IsUnderPostmaster || !IsPostmasterEnvironment);
+
+ return pg_strong_random(dst, len);
+}
+
+#else
+
+/*
+ * Seed for the PRNG, stored in shared memory.
+ *
+ * Protected by BackendRandomLock.
+ */
+typedef struct
+{
+ bool initialized;
+ unsigned short seed[3];
+} BackendRandomShmemStruct;
+
+static BackendRandomShmemStruct *BackendRandomShmem;
+
+Size
+BackendRandomShmemSize(void)
+{
+ return sizeof(BackendRandomShmemStruct);
+}
+
+void
+BackendRandomShmemInit(void)
+{
+ bool found;
+
+ BackendRandomShmem = (BackendRandomShmemStruct *)
+ ShmemInitStruct("Backend PRNG state",
+ BackendRandomShmemSize(),
+ &found);
+
+ if (!IsUnderPostmaster)
+ {
+ Assert(!found);
+
+ BackendRandomShmem->initialized = false;
+ }
+ else
+ Assert(found);
+}
+
+bool
+pg_backend_random(char *dst, int len)
+{
+ int i;
+ char *end = dst + len;
+
+ /* should not be called in postmaster */
+ Assert (IsUnderPostmaster || !IsPostmasterEnvironment);
+
+ LWLockAcquire(BackendRandomLock, LW_EXCLUSIVE);
+
+ /*
+ * Seed the PRNG on the first use.
+ */
+ if (!BackendRandomShmem->initialized)
+ {
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+
+ BackendRandomShmem->seed[0] = now.tv_sec;
+ BackendRandomShmem->seed[1] = (unsigned short) (now.tv_usec);
+ BackendRandomShmem->seed[2] = (unsigned short) (now.tv_usec >> 16);
+
+ /*
+ * Mix in the cancel key, generated by the postmaster. This adds
+ * what little entropy the postmaster had to the seed.
+ */
+ BackendRandomShmem->seed[0] ^= (MyCancelKey);
+ BackendRandomShmem->seed[1] ^= (MyCancelKey >> 16);
+
+ BackendRandomShmem->initialized = true;
+ }
+
+ for (i = 0; dst < end; i++)
+ {
+ uint32 r;
+ int j;
+
+ /*
+ * pg_jrand48 returns a 32-bit integer. Fill the next 4 bytes from it.
+ */
+ r = (uint32) pg_jrand48(BackendRandomShmem->seed);
+
+ for (j = 0; j < 4 && dst < end; j++)
+ {
+ *(dst++) = (char) (r & 0xFF);
+ r >>= 8;
+ }
+ }
+ LWLockRelease(BackendRandomLock);
+
+ return true;
+}
+
+
+#endif /* HAVE_STRONG_RANDOM */