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 | */ |
39 | static HCRYPTPROV hProvider = 0; |
40 | #endif |
41 | |
42 | #if defined(USE_DEV_URANDOM) |
43 | /* |
44 | * Read (random) bytes from a file. |
45 | */ |
46 | static bool |
47 | random_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 | */ |
99 | bool |
100 | pg_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 | |