summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTatsuo Ishii2025-05-20 00:47:02 +0000
committerTatsuo Ishii2025-05-20 00:47:02 +0000
commit7abdd48ece6630eb2c376e865d39500e49b33d8c (patch)
treee4716659090a3f2194db57da5aa5d62f6b37e7c4
parentbc0d9ca49d7afabcbfc8e000a40075a9fac1adf6 (diff)
Replace PostmasterRandom() with pg_strong_random().
Our PostmasterRandmon() was imported from PostgreSQL long time ago (in 2016). In the same year PostgreSQL replaced PostmasterRandmon() with pg_strong_random()(src/port/pg_strong_random.c). This commit follows it. pg_strong_random() looks better than PostmasterRandmon(), since it's more secure and portable. Moreover no initialization is necessary. Reviewed-by: Martijn van Duren <pgpool@list.imperialat.at> Discussion: [pgpool-hackers: 4588] Shuffle random functions and use better random numbers https://www.pgpool.net/pipermail/pgpool-hackers/2025-May/004589.html
-rw-r--r--src/Makefile.am3
-rw-r--r--src/auth/pool_auth.c65
-rw-r--r--src/include/pool.h4
-rw-r--r--src/utils/pg_strong_random.c185
4 files changed, 198 insertions, 59 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index e32c41269..efc11cb68 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -65,7 +65,8 @@ pgpool_SOURCES = main/main.c \
utils/statistics.c \
utils/pool_health_check_stats.c \
utils/psqlscan.l \
- utils/pgstrcasecmp.c
+ utils/pgstrcasecmp.c \
+ utils/pg_strong_random.c
utils/psqlscan.c: utils/psqlscan.l
$(LEX) -o'utils/psqlscan.c' $<
diff --git a/src/auth/pool_auth.c b/src/auth/pool_auth.c
index 7551a567e..be9f33434 100644
--- a/src/auth/pool_auth.c
+++ b/src/auth/pool_auth.c
@@ -68,7 +68,6 @@ static int read_password_packet(POOL_CONNECTION * frontend, int protoMajor, char
static int send_password_packet(POOL_CONNECTION * backend, int protoMajor, char *password);
static int send_auth_ok(POOL_CONNECTION * frontend, int protoMajor);
static void sendAuthRequest(POOL_CONNECTION * frontend, int protoMajor, int32 auth_req_type, char *extradata, int extralen);
-static long PostmasterRandom(void);
static int pg_SASL_continue(POOL_CONNECTION * backend, char *payload, int payloadlen, void *sasl_state, bool final);
static void *pg_SASL_init(POOL_CONNECTION * backend, char *payload, int payloadlen, char *username, char *storedPassword);
@@ -1771,27 +1770,16 @@ send_auth_ok(POOL_CONNECTION * frontend, int protoMajor)
return 0;
}
-
+/*
+ * Generate random bytes
+ */
void
pool_random(void *buf, size_t len)
{
- int ret = 0;
-#ifdef USE_SSL
- ret = RAND_bytes(buf, len);
-#endif
- /* if RND_bytes fails or not present use the old technique */
- if (ret == 0)
- {
- int i;
- char *ptr = buf;
-
- for (i = 0; i < len; i++)
- {
- long rand = PostmasterRandom();
-
- ptr[i] = (rand % 255) + 1;
- }
- }
+ if (!pg_strong_random(buf, len))
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("could not generate random bytes")));
}
/*
@@ -1803,45 +1791,6 @@ pool_random_salt(char *md5Salt)
pool_random(md5Salt, 4);
}
-/*
- * PostmasterRandom
- */
-static long
-PostmasterRandom(void)
-{
- extern struct timeval random_start_time;
- static unsigned int random_seed = 0;
-
- /*
- * Select a random seed at the time of first receiving a request.
- */
- if (random_seed == 0)
- {
- do
- {
- struct timeval random_stop_time;
-
- 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);
-
- srandom(random_seed);
- }
-
- return random();
-}
-
-
static bool
do_SCRAM(POOL_CONNECTION * frontend, POOL_CONNECTION * backend, int protoMajor, int message_length,
char *username, char *storedPassword, PasswordType passwordType)
diff --git a/src/include/pool.h b/src/include/pool.h
index 95570c08e..c9b4dc27e 100644
--- a/src/include/pool.h
+++ b/src/include/pool.h
@@ -653,4 +653,8 @@ extern size_t strlcpy(char *dst, const char *src, size_t siz);
extern void do_worker_child(void);
extern int get_query_result(POOL_CONNECTION_POOL_SLOT * *slots, int backend_id, char *query, POOL_SELECT_RESULT * *res);
+/* utils/pg_strong_random.c */
+void pg_strong_random_init(void);
+bool pg_strong_random(void *buf, size_t len);
+
#endif /* POOL_H */
diff --git a/src/utils/pg_strong_random.c b/src/utils/pg_strong_random.c
new file mode 100644
index 000000000..2017139b1
--- /dev/null
+++ b/src/utils/pg_strong_random.c
@@ -0,0 +1,185 @@
+/*
+ * This file was imported from PostgreSQL source code
+ * (src/port/pg_strong_random.c).
+ */
+
+/*-------------------------------------------------------------------------
+ *
+ * pg_strong_random.c
+ * generate a cryptographically secure random number
+ *
+ * Our definition of "strong" is that it's suitable for generating random
+ * salts and query cancellation keys, during authentication.
+ *
+ * Note: this code is run quite early in postmaster and backend startup;
+ * therefore, even when built for backend, it cannot rely on backend
+ * infrastructure such as elog() or palloc().
+ *
+ * Portions Copyright (c) 2025, PgPool Global Development Group
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/port/pg_strong_random.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "pool.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+/*
+ * pg_strong_random & pg_strong_random_init
+ *
+ * Generate requested number of random bytes. The returned bytes are
+ * cryptographically secure, suitable for use e.g. in authentication.
+ *
+ * Before pg_strong_random is called in any process, the generator must first
+ * be initialized by calling pg_strong_random_init(). Initialization is a no-
+ * op for all supported randomness sources, it is kept to maintain backwards
+ * compatibility with extensions.
+ *
+ * We rely on system facilities for actually generating the numbers.
+ * We support a number of sources:
+ *
+ * 1. OpenSSL's RAND_bytes()
+ * 2. Windows' CryptGenRandom() function
+ * 3. /dev/urandom
+ *
+ * Returns true on success, and false if none of the sources
+ * were available. NB: It is important to check the return value!
+ * Proceeding with key generation when no random data was available
+ * would lead to predictable keys and security issues.
+ */
+
+
+
+#ifdef USE_SSL
+
+#include <openssl/rand.h>
+
+void
+pg_strong_random_init(void)
+{
+ /* No initialization needed */
+}
+
+bool
+pg_strong_random(void *buf, size_t len)
+{
+ int i;
+
+ /*
+ * Check that OpenSSL's CSPRNG has been sufficiently seeded, and if not
+ * add more seed data using RAND_poll(). With some older versions of
+ * OpenSSL, it may be necessary to call RAND_poll() a number of times. If
+ * RAND_poll() fails to generate seed data within the given amount of
+ * retries, subsequent RAND_bytes() calls will fail, but we allow that to
+ * happen to let pg_strong_random() callers handle that with appropriate
+ * error handling.
+ */
+#define NUM_RAND_POLL_RETRIES 8
+
+ for (i = 0; i < NUM_RAND_POLL_RETRIES; i++)
+ {
+ if (RAND_status() == 1)
+ {
+ /* The CSPRNG is sufficiently seeded */
+ break;
+ }
+
+ RAND_poll();
+ }
+
+ if (RAND_bytes(buf, len) == 1)
+ return true;
+ return false;
+}
+
+#elif WIN32
+
+#include <wincrypt.h>
+/*
+ * Cache a global crypto provider that only gets freed when the process
+ * exits, in case we need random numbers more than once.
+ */
+static HCRYPTPROV hProvider = 0;
+
+void
+pg_strong_random_init(void)
+{
+ /* No initialization needed on WIN32 */
+}
+
+bool
+pg_strong_random(void *buf, size_t len)
+{
+ if (hProvider == 0)
+ {
+ if (!CryptAcquireContext(&hProvider,
+ NULL,
+ MS_DEF_PROV,
+ PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
+ {
+ /*
+ * On failure, set back to 0 in case the value was for some reason
+ * modified.
+ */
+ hProvider = 0;
+ }
+ }
+ /* Re-check in case we just retrieved the provider */
+ if (hProvider != 0)
+ {
+ if (CryptGenRandom(hProvider, len, buf))
+ return true;
+ }
+ return false;
+}
+
+#else /* not USE_OPENSSL or WIN32 */
+
+/*
+ * Without OpenSSL or Win32 support, just read /dev/urandom ourselves.
+ */
+
+void
+pg_strong_random_init(void)
+{
+ /* No initialization needed */
+}
+
+bool
+pg_strong_random(void *buf, size_t len)
+{
+ int f;
+ char *p = buf;
+ ssize_t res;
+
+ f = open("/dev/urandom", O_RDONLY, 0);
+ if (f == -1)
+ return false;
+
+ while (len)
+ {
+ res = read(f, p, len);
+ if (res <= 0)
+ {
+ if (errno == EINTR)
+ continue; /* interrupted by signal, just retry */
+
+ close(f);
+ return false;
+ }
+
+ p += res;
+ len -= res;
+ }
+
+ close(f);
+ return true;
+}
+#endif