/src/postgres/src/port/pg_strong_random.c
Line | Count | Source (jump to first uncovered line) |
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-2025, 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 | | /* |
28 | | * pg_strong_random & pg_strong_random_init |
29 | | * |
30 | | * Generate requested number of random bytes. The returned bytes are |
31 | | * cryptographically secure, suitable for use e.g. in authentication. |
32 | | * |
33 | | * Before pg_strong_random is called in any process, the generator must first |
34 | | * be initialized by calling pg_strong_random_init(). Initialization is a no- |
35 | | * op for all supported randomness sources, it is kept to maintain backwards |
36 | | * compatibility with extensions. |
37 | | * |
38 | | * We rely on system facilities for actually generating the numbers. |
39 | | * We support a number of sources: |
40 | | * |
41 | | * 1. OpenSSL's RAND_bytes() |
42 | | * 2. Windows' CryptGenRandom() function |
43 | | * 3. /dev/urandom |
44 | | * |
45 | | * Returns true on success, and false if none of the sources |
46 | | * were available. NB: It is important to check the return value! |
47 | | * Proceeding with key generation when no random data was available |
48 | | * would lead to predictable keys and security issues. |
49 | | */ |
50 | | |
51 | | |
52 | | |
53 | | #ifdef USE_OPENSSL |
54 | | |
55 | | #include <openssl/rand.h> |
56 | | |
57 | | void |
58 | | pg_strong_random_init(void) |
59 | | { |
60 | | /* No initialization needed */ |
61 | | } |
62 | | |
63 | | bool |
64 | | pg_strong_random(void *buf, size_t len) |
65 | | { |
66 | | int i; |
67 | | |
68 | | /* |
69 | | * Check that OpenSSL's CSPRNG has been sufficiently seeded, and if not |
70 | | * add more seed data using RAND_poll(). With some older versions of |
71 | | * OpenSSL, it may be necessary to call RAND_poll() a number of times. If |
72 | | * RAND_poll() fails to generate seed data within the given amount of |
73 | | * retries, subsequent RAND_bytes() calls will fail, but we allow that to |
74 | | * happen to let pg_strong_random() callers handle that with appropriate |
75 | | * error handling. |
76 | | */ |
77 | | #define NUM_RAND_POLL_RETRIES 8 |
78 | | |
79 | | for (i = 0; i < NUM_RAND_POLL_RETRIES; i++) |
80 | | { |
81 | | if (RAND_status() == 1) |
82 | | { |
83 | | /* The CSPRNG is sufficiently seeded */ |
84 | | break; |
85 | | } |
86 | | |
87 | | RAND_poll(); |
88 | | } |
89 | | |
90 | | if (RAND_bytes(buf, len) == 1) |
91 | | return true; |
92 | | return false; |
93 | | } |
94 | | |
95 | | #elif WIN32 |
96 | | |
97 | | #include <wincrypt.h> |
98 | | /* |
99 | | * Cache a global crypto provider that only gets freed when the process |
100 | | * exits, in case we need random numbers more than once. |
101 | | */ |
102 | | static HCRYPTPROV hProvider = 0; |
103 | | |
104 | | void |
105 | | pg_strong_random_init(void) |
106 | | { |
107 | | /* No initialization needed on WIN32 */ |
108 | | } |
109 | | |
110 | | bool |
111 | | pg_strong_random(void *buf, size_t len) |
112 | | { |
113 | | if (hProvider == 0) |
114 | | { |
115 | | if (!CryptAcquireContext(&hProvider, |
116 | | NULL, |
117 | | MS_DEF_PROV, |
118 | | PROV_RSA_FULL, |
119 | | CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) |
120 | | { |
121 | | /* |
122 | | * On failure, set back to 0 in case the value was for some reason |
123 | | * modified. |
124 | | */ |
125 | | hProvider = 0; |
126 | | } |
127 | | } |
128 | | /* Re-check in case we just retrieved the provider */ |
129 | | if (hProvider != 0) |
130 | | { |
131 | | if (CryptGenRandom(hProvider, len, buf)) |
132 | | return true; |
133 | | } |
134 | | return false; |
135 | | } |
136 | | |
137 | | #else /* not USE_OPENSSL or WIN32 */ |
138 | | |
139 | | /* |
140 | | * Without OpenSSL or Win32 support, just read /dev/urandom ourselves. |
141 | | */ |
142 | | |
143 | | void |
144 | | pg_strong_random_init(void) |
145 | 0 | { |
146 | | /* No initialization needed */ |
147 | 0 | } |
148 | | |
149 | | bool |
150 | | pg_strong_random(void *buf, size_t len) |
151 | 0 | { |
152 | 0 | int f; |
153 | 0 | char *p = buf; |
154 | 0 | ssize_t res; |
155 | |
|
156 | 0 | f = open("/dev/urandom", O_RDONLY, 0); |
157 | 0 | if (f == -1) |
158 | 0 | return false; |
159 | | |
160 | 0 | while (len) |
161 | 0 | { |
162 | 0 | res = read(f, p, len); |
163 | 0 | if (res <= 0) |
164 | 0 | { |
165 | 0 | if (errno == EINTR) |
166 | 0 | continue; /* interrupted by signal, just retry */ |
167 | | |
168 | 0 | close(f); |
169 | 0 | return false; |
170 | 0 | } |
171 | | |
172 | 0 | p += res; |
173 | 0 | len -= res; |
174 | 0 | } |
175 | | |
176 | 0 | close(f); |
177 | 0 | return true; |
178 | 0 | } |
179 | | #endif |