Coverage Report

Created: 2025-08-12 06:43

/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