1/*-------------------------------------------------------------------------
2 *
3 * pg_strong_random.c
4 * generate a cryptographically secure random number
5 *
6 * Our definition of "strong" is that it's suitable for generating random
7 * salts and query cancellation keys, during authentication.
8 *
9 * Note: this code is run quite early in postmaster and backend startup;
10 * therefore, even when built for backend, it cannot rely on backend
11 * infrastructure such as elog() or palloc().
12 *
13 * Copyright (c) 1996-2019, PostgreSQL Global Development Group
14 *
15 * IDENTIFICATION
16 * src/port/pg_strong_random.c
17 *
18 *-------------------------------------------------------------------------
19 */
20
21#include "c.h"
22
23#include <fcntl.h>
24#include <unistd.h>
25#include <sys/time.h>
26
27#ifdef USE_OPENSSL
28#include <openssl/rand.h>
29#endif
30#ifdef WIN32
31#include <wincrypt.h>
32#endif
33
34#ifdef WIN32
35/*
36 * Cache a global crypto provider that only gets freed when the process
37 * exits, in case we need random numbers more than once.
38 */
39static HCRYPTPROV hProvider = 0;
40#endif
41
42#if defined(USE_DEV_URANDOM)
43/*
44 * Read (random) bytes from a file.
45 */
46static bool
47random_from_file(const char *filename, void *buf, size_t len)
48{
49 int f;
50 char *p = buf;
51 ssize_t res;
52
53 f = open(filename, O_RDONLY, 0);
54 if (f == -1)
55 return false;
56
57 while (len)
58 {
59 res = read(f, p, len);
60 if (res <= 0)
61 {
62 if (errno == EINTR)
63 continue; /* interrupted by signal, just retry */
64
65 close(f);
66 return false;
67 }
68
69 p += res;
70 len -= res;
71 }
72
73 close(f);
74 return true;
75}
76#endif
77
78/*
79 * pg_strong_random
80 *
81 * Generate requested number of random bytes. The returned bytes are
82 * cryptographically secure, suitable for use e.g. in authentication.
83 *
84 * We rely on system facilities for actually generating the numbers.
85 * We support a number of sources:
86 *
87 * 1. OpenSSL's RAND_bytes()
88 * 2. Windows' CryptGenRandom() function
89 * 3. /dev/urandom
90 *
91 * The configure script will choose which one to use, and set
92 * a USE_*_RANDOM flag accordingly.
93 *
94 * Returns true on success, and false if none of the sources
95 * were available. NB: It is important to check the return value!
96 * Proceeding with key generation when no random data was available
97 * would lead to predictable keys and security issues.
98 */
99bool
100pg_strong_random(void *buf, size_t len)
101{
102 /*
103 * When built with OpenSSL, use OpenSSL's RAND_bytes function.
104 */
105#if defined(USE_OPENSSL_RANDOM)
106 int i;
107
108 /*
109 * Check that OpenSSL's CSPRNG has been sufficiently seeded, and if not
110 * add more seed data using RAND_poll(). With some older versions of
111 * OpenSSL, it may be necessary to call RAND_poll() a number of times.
112 */
113#define NUM_RAND_POLL_RETRIES 8
114
115 for (i = 0; i < NUM_RAND_POLL_RETRIES; i++)
116 {
117 if (RAND_status() == 1)
118 {
119 /* The CSPRNG is sufficiently seeded */
120 break;
121 }
122
123 if (RAND_poll() == 0)
124 {
125 /*
126 * RAND_poll() failed to generate any seed data, which means that
127 * RAND_bytes() will probably fail. For now, just fall through
128 * and let that happen. XXX: maybe we could seed it some other
129 * way.
130 */
131 break;
132 }
133 }
134
135 if (RAND_bytes(buf, len) == 1)
136 return true;
137 return false;
138
139 /*
140 * Windows has CryptoAPI for strong cryptographic numbers.
141 */
142#elif defined(USE_WIN32_RANDOM)
143 if (hProvider == 0)
144 {
145 if (!CryptAcquireContext(&hProvider,
146 NULL,
147 MS_DEF_PROV,
148 PROV_RSA_FULL,
149 CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
150 {
151 /*
152 * On failure, set back to 0 in case the value was for some reason
153 * modified.
154 */
155 hProvider = 0;
156 }
157 }
158 /* Re-check in case we just retrieved the provider */
159 if (hProvider != 0)
160 {
161 if (CryptGenRandom(hProvider, len, buf))
162 return true;
163 }
164 return false;
165
166 /*
167 * Read /dev/urandom ourselves.
168 */
169#elif defined(USE_DEV_URANDOM)
170 if (random_from_file("/dev/urandom", buf, len))
171 return true;
172 return false;
173
174#else
175 /* The autoconf script should not have allowed this */
176#error no source of random numbers configured
177#endif
178}
179