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-2020, 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 USE_WIN32_RANDOM
31 #include <wincrypt.h>
32 #endif
33 
34 #ifdef USE_WIN32_RANDOM
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. If
112  * RAND_poll() fails to generate seed data within the given amount of
113  * retries, subsequent RAND_bytes() calls will fail, but we allow that to
114  * happen to let pg_strong_random() callers handle that with appropriate
115  * error handling.
116  */
117 #define NUM_RAND_POLL_RETRIES 8
118 
119  for (i = 0; i < NUM_RAND_POLL_RETRIES; i++)
120  {
121  if (RAND_status() == 1)
122  {
123  /* The CSPRNG is sufficiently seeded */
124  break;
125  }
126 
127  RAND_poll();
128  }
129 
130  if (RAND_bytes(buf, len) == 1)
131  return true;
132  return false;
133 
134  /*
135  * Windows has CryptoAPI for strong cryptographic numbers.
136  */
137 #elif defined(USE_WIN32_RANDOM)
138  if (hProvider == 0)
139  {
140  if (!CryptAcquireContext(&hProvider,
141  NULL,
142  MS_DEF_PROV,
143  PROV_RSA_FULL,
144  CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
145  {
146  /*
147  * On failure, set back to 0 in case the value was for some reason
148  * modified.
149  */
150  hProvider = 0;
151  }
152  }
153  /* Re-check in case we just retrieved the provider */
154  if (hProvider != 0)
155  {
156  if (CryptGenRandom(hProvider, len, buf))
157  return true;
158  }
159  return false;
160 
161  /*
162  * Read /dev/urandom ourselves.
163  */
164 #elif defined(USE_DEV_URANDOM)
165  if (random_from_file("/dev/urandom", buf, len))
166  return true;
167  return false;
168 
169 #else
170  /* The autoconf script should not have allowed this */
171 #error no source of random numbers configured
172 #endif
173 }
bool pg_strong_random(void *buf, size_t len)
static char * buf
Definition: pg_test_fsync.c:67
static char * filename
Definition: pg_dumpall.c:90
int i
#define close(a)
Definition: win32.h:12
#define EINTR
Definition: win32_port.h:323
#define read(a, b, c)
Definition: win32.h:13