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