From 0f410fdf8391dce1ae2ce9e7794cc18de3732e2e Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Wed, 19 Oct 2016 11:56:47 +0900
Subject: [PATCH 3/9] Implement last-resort method in pg_strong_random

This method will be used on nix platforms in the case where any of the
previous methods failed. It uses pid(), gettimeofday() and random() to
grab some entropy, even if this is predictible, and processes the
obtained value through a SHA-256 hash.
---
 src/port/pg_strong_random.c | 76 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 76 insertions(+)

diff --git a/src/port/pg_strong_random.c b/src/port/pg_strong_random.c
index a404111..17c217a 100644
--- a/src/port/pg_strong_random.c
+++ b/src/port/pg_strong_random.c
@@ -18,6 +18,8 @@
 #include "postgres_fe.h"
 #endif
 
+#include "common/sha2.h"
+
 #include <fcntl.h>
 #include <unistd.h>
 
@@ -29,6 +31,9 @@
 #endif
 
 static bool random_from_file(char *filename, void *buf, size_t len);
+#ifndef WIN32
+static bool random_from_std(void *dst, size_t len);
+#endif
 
 #ifdef WIN32
 /*
@@ -72,6 +77,67 @@ random_from_file(char *filename, void *buf, size_t len)
 	return true;
 }
 
+#ifndef WIN32
+/*
+ * This is not available on Windows, and the cryptography standards available
+ * there are enough anyway to generate randomness.
+ */
+#include <sys/time.h>
+#include <time.h>
+
+/*
+ * Generate some random data using environment data as entropy
+ * through SHA-256.  All the other methods failed, this can be used
+ * as a last resort method even if it is predictible, and rather
+ * slow compared to the rest.
+ */
+static bool
+random_from_std(void *dst, size_t len)
+{
+	pid_t	pid;
+	size_t	remaining = len;
+
+	/* process ID */
+	pid = getpid();
+
+	/*
+	 * Compute a random value by generating successive chunks worth
+	 * of PG_SHA256_DIGEST_LENGTH bytes.
+	 */
+	while (remaining != 0)
+	{
+		int				x;
+		struct timeval	tv;
+		pg_sha256_ctx	ctx;
+		uint8			sha_buf[PG_SHA256_DIGEST_LENGTH];
+
+		pg_sha256_init(&ctx);
+		pg_sha256_update(&ctx, (uint8 *) &pid, sizeof(pid));
+
+		/* time */
+		gettimeofday(&tv, NULL);
+		pg_sha256_update(&ctx, (uint8 *) &tv, sizeof(tv));
+
+		/* pointless, but should not hurt */
+		x = random();
+		pg_sha256_update(&ctx, (uint8 *) &x, sizeof(x));
+		pg_sha256_final(&ctx, sha_buf);
+
+		/* copy the chunk generated in the result */
+		memcpy(dst, sha_buf, Min(remaining, PG_SHA256_DIGEST_LENGTH));
+
+		/* and prepare to loop further */
+		if (remaining >= PG_SHA256_DIGEST_LENGTH)
+			remaining -= PG_SHA256_DIGEST_LENGTH;
+		else
+			remaining = 0;
+		dst = (uint8 *) dst + PG_SHA256_DIGEST_LENGTH;
+	}
+
+	return true;
+}
+#endif
+
 /*
  * pg_strong_random
  *
@@ -86,6 +152,8 @@ random_from_file(char *filename, void *buf, size_t len)
  * 2. Windows' CryptGenRandom() function
  * 3. /dev/urandom
  * 4. /dev/random
+ * 5. Use the internal, predictible method computing a value through SHA-256
+ *	  with system-related entropy.
  *
  * Returns true on success, and false if none of the sources
  * were available. NB: It is important to check the return value!
@@ -143,6 +211,14 @@ pg_strong_random(void *buf, size_t len)
 	if (random_from_file("/dev/random", buf, len))
 		return true;
 
+#ifndef WIN32
+	/*
+	 * As final method, generate some randomness using the in-house method.
+	 */
+	if (random_from_std(buf, len))
+		return true;
+#endif
+
 	/* None of the sources were available. */
 	return false;
 }
-- 
2.10.2

