diff options
Diffstat (limited to 'src/backend')
| -rw-r--r-- | src/backend/libpq/auth.c | 62 | ||||
| -rw-r--r-- | src/backend/libpq/crypt.c | 10 | ||||
| -rw-r--r-- | src/backend/postmaster/postmaster.c | 146 | ||||
| -rw-r--r-- | src/backend/storage/ipc/ipci.c | 3 | ||||
| -rw-r--r-- | src/backend/storage/lmgr/lwlocknames.txt | 1 | ||||
| -rw-r--r-- | src/backend/utils/init/globals.c | 2 | ||||
| -rw-r--r-- | src/backend/utils/misc/Makefile | 5 | ||||
| -rw-r--r-- | src/backend/utils/misc/backend_random.c | 158 |
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 */ |
