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/rand.h>
54 
55 void
57 {
58  /*
59  * Make sure processes do not share OpenSSL randomness state. This is no
60  * longer required in OpenSSL 1.1.1 and later versions, but until we drop
61  * support for version < 1.1.1 we need to do this.
62  */
63  RAND_poll();
64 }
65 
66 bool
67 pg_strong_random(void *buf, size_t len)
68 {
69  int i;
70 
71  /*
72  * Check that OpenSSL's CSPRNG has been sufficiently seeded, and if not
73  * add more seed data using RAND_poll(). With some older versions of
74  * OpenSSL, it may be necessary to call RAND_poll() a number of times. If
75  * RAND_poll() fails to generate seed data within the given amount of
76  * retries, subsequent RAND_bytes() calls will fail, but we allow that to
77  * happen to let pg_strong_random() callers handle that with appropriate
78  * error handling.
79  */
80 #define NUM_RAND_POLL_RETRIES 8
81 
82  for (i = 0; i < NUM_RAND_POLL_RETRIES; i++)
83  {
84  if (RAND_status() == 1)
85  {
86  /* The CSPRNG is sufficiently seeded */
87  break;
88  }
89 
90  RAND_poll();
91  }
92 
93  if (RAND_bytes(buf, len) == 1)
94  return true;
95  return false;
96 }
97 
98 #elif WIN32
99 
100 #include <wincrypt.h>
101 /*
102  * Cache a global crypto provider that only gets freed when the process
103  * exits, in case we need random numbers more than once.
104  */
105 static HCRYPTPROV hProvider = 0;
106 
107 void
109 {
110  /* No initialization needed on WIN32 */
111 }
112 
113 bool
114 pg_strong_random(void *buf, size_t len)
115 {
116  if (hProvider == 0)
117  {
118  if (!CryptAcquireContext(&hProvider,
119  NULL,
120  MS_DEF_PROV,
121  PROV_RSA_FULL,
122  CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
123  {
124  /*
125  * On failure, set back to 0 in case the value was for some reason
126  * modified.
127  */
128  hProvider = 0;
129  }
130  }
131  /* Re-check in case we just retrieved the provider */
132  if (hProvider != 0)
133  {
134  if (CryptGenRandom(hProvider, len, buf))
135  return true;
136  }
137  return false;
138 }
139 
140 #else /* not USE_OPENSSL or WIN32 */
141 
142 /*
143  * Without OpenSSL or Win32 support, just read /dev/urandom ourselves.
144  */
145 
146 void
148 {
149  /* No initialization needed */
150 }
151 
152 bool
153 pg_strong_random(void *buf, size_t len)
154 {
155  int f;
156  char *p = buf;
157  ssize_t res;
158 
159  f = open("/dev/urandom", O_RDONLY, 0);
160  if (f == -1)
161  return false;
162 
163  while (len)
164  {
165  res = read(f, p, len);
166  if (res <= 0)
167  {
168  if (errno == EINTR)
169  continue; /* interrupted by signal, just retry */
170 
171  close(f);
172  return false;
173  }
174 
175  p += res;
176  len -= res;
177  }
178 
179  close(f);
180  return true;
181 }
182 #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