>From d969c8c48febae6ea27035fceccc160778ebb093 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
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 <sys/types.h>
+#include <unistd.h>
+#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

