From d969c8c48febae6ea27035fceccc160778ebb093 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Wed, 7 Dec 2016 17:12:52 +0200 Subject: [PATCH 09/11] Random number fixes. * Check return value of pg_backend_random() and pg_strong_random() * Add a fallback implementation for libpq, for !HAVE_STRONG_RANDOM * Rename SCRAM_NONCE_LEN to SCRAM_RAW_NONCE_LEN, for clarity (unrelated to the other changes in this commit, really) --- src/backend/libpq/auth-scram.c | 22 ++++++++++--- src/include/common/scram-common.h | 4 +-- src/interfaces/libpq/Makefile | 13 ++++++-- src/interfaces/libpq/fe-auth-scram.c | 62 +++++++++++++++++++++++++++++++++--- 4 files changed, 87 insertions(+), 14 deletions(-) diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c index cda663b..6b129af 100644 --- a/src/backend/libpq/auth-scram.c +++ b/src/backend/libpq/auth-scram.c @@ -426,7 +426,13 @@ scram_build_verifier(const char *username, const char *password, if (iterations <= 0) iterations = SCRAM_ITERATIONS_DEFAULT; - pg_backend_random(salt, SCRAM_SALT_LEN); + if (!pg_backend_random(salt, SCRAM_SALT_LEN)) + { + ereport(LOG, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("could not generate random salt"))); + return NULL; + } encoded_salt = palloc(pg_b64_enc_len(SCRAM_SALT_LEN) + 1); encoded_len = pg_b64_encode(salt, SCRAM_SALT_LEN, encoded_salt); @@ -809,13 +815,19 @@ build_server_first_message(scram_state *state) * For convenience, however, we don't use the whole range available, rather, * we generate some random bytes, and base64 encode them. */ - char raw_nonce[SCRAM_NONCE_LEN]; + char raw_nonce[SCRAM_RAW_NONCE_LEN]; int encoded_len; - pg_backend_random(raw_nonce, SCRAM_NONCE_LEN); + if (!pg_backend_random(raw_nonce, SCRAM_RAW_NONCE_LEN)) + { + ereport(LOG, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("could not generate random nonce"))); + return NULL; + } - state->server_nonce = palloc(pg_b64_enc_len(SCRAM_NONCE_LEN) + 1); - encoded_len = pg_b64_encode(raw_nonce, SCRAM_NONCE_LEN, state->server_nonce); + state->server_nonce = palloc(pg_b64_enc_len(SCRAM_RAW_NONCE_LEN) + 1); + encoded_len = pg_b64_encode(raw_nonce, SCRAM_RAW_NONCE_LEN, state->server_nonce); if (encoded_len < 0) return NULL; diff --git a/src/include/common/scram-common.h b/src/include/common/scram-common.h index 34c6527..024059b 100644 --- a/src/include/common/scram-common.h +++ b/src/include/common/scram-common.h @@ -26,10 +26,10 @@ * in "raw" number of bytes, the actual nonces sent over the wire are * encoded using only ASCII-printable characters. */ -#define SCRAM_NONCE_LEN 10 +#define SCRAM_RAW_NONCE_LEN 10 /* length of salt when generating new verifiers */ -#define SCRAM_SALT_LEN SCRAM_NONCE_LEN +#define SCRAM_SALT_LEN 10 /* number of bytes used when sending iteration number during exchange */ #define SCRAM_ITERATION_LEN 10 diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile index 460b0a1..4599b03 100644 --- a/src/interfaces/libpq/Makefile +++ b/src/interfaces/libpq/Makefile @@ -35,10 +35,17 @@ OBJS= fe-auth.o fe-auth-scram.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-l fe-protocol2.o fe-protocol3.o pqexpbuffer.o fe-secure.o \ libpq-events.o # libpgport C files we always use -OBJS += chklocale.o inet_net_ntop.o noblock.o pg_strong_random.o \ - pgstrcasecmp.o pqsignal.o thread.o +OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o pqsignal.o \ + thread.o # libpgport C files that are needed if identified by configure OBJS += $(filter crypt.o getaddrinfo.o getpeereid.o inet_aton.o open.o system.o snprintf.o strerror.o strlcpy.o win32error.o win32setlocale.o, $(LIBOBJS)) + +ifeq ($(enable_strong_random), yes) +OBJS += pg_strong_random.o +else +OBJS += erand48.o +endif + # src/backend/utils/mb OBJS += encnames.o wchar.o # src/common @@ -95,7 +102,7 @@ backend_src = $(top_srcdir)/src/backend # For some libpgport modules, this only happens if configure decides # the module is needed (see filter hack in OBJS, above). -chklocale.c crypt.c getaddrinfo.c getpeereid.c inet_aton.c inet_net_ntop.c noblock.c open.c system.c pgsleep.c pg_strong_random.c pgstrcasecmp.c pqsignal.c snprintf.c strerror.c strlcpy.c thread.c win32error.c win32setlocale.c: % : $(top_srcdir)/src/port/% +chklocale.c crypt.c erand48.c getaddrinfo.c getpeereid.c inet_aton.c inet_net_ntop.c noblock.c open.c system.c pgsleep.c pg_strong_random.c pgstrcasecmp.c pqsignal.c snprintf.c strerror.c strlcpy.c thread.c win32error.c win32setlocale.c: % : $(top_srcdir)/src/port/% rm -f $@ && $(LN_S) $< . ip.c md5.c: % : $(top_srcdir)/src/common/% diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c index 12884e5..12943ff 100644 --- a/src/interfaces/libpq/fe-auth-scram.c +++ b/src/interfaces/libpq/fe-auth-scram.c @@ -18,6 +18,12 @@ #include "common/scram-common.h" #include "fe-auth.h" +/* These are needed for getpid(), in the fallback implementation */ +#ifndef HAVE_STRONG_RANDOM +#include +#include +#endif + /* * Status of exchange messages used for SCRAM authentication via the * SASL protocol. @@ -250,13 +256,61 @@ read_attr_value(char **input, char attr, PQExpBuffer errorMessage) return begin; } +static bool +pg_frontend_random(char *dst, int len) +{ +#ifdef HAVE_STRONG_RANDOM + return pg_strong_random(dst, len); +#else + int i; + char *end = dst + len; + + static unsigned short seed[3]; + static int mypid = 0; + + pglock_thread(); + + if (mypid != getpid()) + { + struct timeval now; + + gettimeofday(&now, NULL); + + seed[0] = now.tv_sec ^ getpid(); + seed[1] = (unsigned short) (now.tv_usec); + seed[2] = (unsigned short) (now.tv_usec >> 16); + } + + 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(seed); + + for (j = 0; j < 4 && dst < end; j++) + { + *(dst++) = (char) (r & 0xFF); + r >>= 8; + } + } + + pgunlock_thread(); + + return true; +#endif +} + /* * Build the first exchange message sent by the client. */ static char * build_client_first_message(fe_scram_state *state, PQExpBuffer errormessage) { - char raw_nonce[SCRAM_NONCE_LEN + 1]; + char raw_nonce[SCRAM_RAW_NONCE_LEN + 1]; char *buf; char buflen; int n; @@ -268,14 +322,14 @@ build_client_first_message(fe_scram_state *state, PQExpBuffer errormessage) * Generate a "raw" nonce. This is converted to ASCII-printable form by * base64-encoding it. */ - if (!pg_strong_random(raw_nonce, SCRAM_NONCE_LEN)) + if (!pg_frontend_random(raw_nonce, SCRAM_RAW_NONCE_LEN)) { printfPQExpBuffer(errormessage, libpq_gettext("failed to generate nonce\n")); return NULL; } /* Generate message */ - buflen = 5 + strlen(state->username) + 3 + pg_b64_enc_len(SCRAM_NONCE_LEN) + 1; + buflen = 5 + strlen(state->username) + 3 + pg_b64_enc_len(SCRAM_RAW_NONCE_LEN) + 1; buf = malloc(buflen); if (buf == NULL) { @@ -283,7 +337,7 @@ build_client_first_message(fe_scram_state *state, PQExpBuffer errormessage) return NULL; } n = snprintf(buf, buflen, "n,,n=%s,r=", state->username); - encoded_len = pg_b64_encode(raw_nonce, SCRAM_NONCE_LEN, buf + n); + encoded_len = pg_b64_encode(raw_nonce, SCRAM_RAW_NONCE_LEN, buf + n); buf[n + encoded_len] = '\0'; state->client_first_message_bare = strdup(buf + 3); -- 2.10.2