Age Owner Branch data TLA Line data Source code
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
1751 magnus@hagander.net 58 :CBC 19108 : pg_strong_random_init(void)
59 : : {
60 : : /* No initialization needed */
61 : 19108 : }
62 : :
63 : : bool
64 : 59833 : 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 : :
2605 dean.a.rasheed@gmail 79 [ + - ]: 59833 : for (i = 0; i < NUM_RAND_POLL_RETRIES; i++)
80 : : {
81 [ + - ]: 59833 : if (RAND_status() == 1)
82 : : {
83 : : /* The CSPRNG is sufficiently seeded */
84 : 59833 : break;
85 : : }
86 : :
1869 noah@leadboat.com 87 :UBC 0 : RAND_poll();
88 : : }
89 : :
3197 heikki.linnakangas@i 90 [ + - ]:CBC 59833 : if (RAND_bytes(buf, len) == 1)
91 : 59833 : return true;
3197 heikki.linnakangas@i 92 :UBC 0 : 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 : : {
146 : : /* No initialization needed */
147 : : }
148 : :
149 : : bool
150 : : pg_strong_random(void *buf, size_t len)
151 : : {
152 : : int f;
153 : : char *p = buf;
154 : : ssize_t res;
155 : :
156 : : f = open("/dev/urandom", O_RDONLY, 0);
157 : : if (f == -1)
158 : : return false;
159 : :
160 : : while (len)
161 : : {
162 : : res = read(f, p, len);
163 : : if (res <= 0)
164 : : {
165 : : if (errno == EINTR)
166 : : continue; /* interrupted by signal, just retry */
167 : :
168 : : close(f);
169 : : return false;
170 : : }
171 : :
172 : : p += res;
173 : : len -= res;
174 : : }
175 : :
176 : : close(f);
177 : : return true;
178 : : }
179 : : #endif
|