summaryrefslogtreecommitdiff
path: root/src/port/pg_strong_random.c
blob: a404111d745952da725db3eb2aeb1395e357f981 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*-------------------------------------------------------------------------
 *
 * pg_strong_random.c
 *	  pg_strong_random() function to return a strong random number
 *
 * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *	  src/port/pg_strong_random.c
 *
 *-------------------------------------------------------------------------
 */

#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif

#include <fcntl.h>
#include <unistd.h>

#ifdef USE_SSL
#include <openssl/rand.h>
#endif
#ifdef WIN32
#include <Wincrypt.h>
#endif

static bool random_from_file(char *filename, void *buf, size_t len);

#ifdef WIN32
/*
 * Cache a global crypto provider that only gets freed when the process
 * exits, in case we need random numbers more than once.
 */
static HCRYPTPROV hProvider = 0;
#endif

/*
 * Read (random) bytes from a file.
 */
static bool
random_from_file(char *filename, void *buf, size_t len)
{
	int			f;
	char	   *p = buf;
	ssize_t		res;

	f = open(filename, O_RDONLY, 0);
	if (f == -1)
		return false;

	while (len)
	{
		res = read(f, p, len);
		if (res <= 0)
		{
			if (errno == EINTR)
				continue;		/* interrupted by signal, just retry */

			close(f);
			return false;
		}

		p += res;
		len -= res;
	}

	close(f);
	return true;
}

/*
 * pg_strong_random
 *
 * Generate requested number of random bytes. The bytes are
 * cryptographically strong random, suitable for use e.g. in key
 * generation.
 *
 * The bytes can be acquired from a number of sources, depending
 * on what's available. We try the following, in this order:
 *
 * 1. OpenSSL's RAND_bytes()
 * 2. Windows' CryptGenRandom() function
 * 3. /dev/urandom
 * 4. /dev/random
 *
 * Returns true on success, and false if none of the sources
 * were available. NB: It is important to check the return value!
 * Proceeding with key generation when no random data was available
 * would lead to predictable keys and security issues.
 */
bool
pg_strong_random(void *buf, size_t len)
{
#ifdef USE_SSL

	/*
	 * When built with OpenSSL, first try the random generation function from
	 * there.
	 */
	if (RAND_bytes(buf, len) == 1)
		return true;
#endif

#ifdef WIN32

	/*
	 * Windows has CryptoAPI for strong cryptographic numbers.
	 */
	if (hProvider == 0)
	{
		if (!CryptAcquireContext(&hProvider,
								 NULL,
								 MS_DEF_PROV,
								 PROV_RSA_FULL,
								 CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
		{
			/*
			 * On failure, set back to 0 in case the value was for some reason
			 * modified.
			 */
			hProvider = 0;
		}
	}

	/* Re-check in case we just retrieved the provider */
	if (hProvider != 0)
	{
		if (CryptGenRandom(hProvider, len, buf))
			return true;
	}
#endif

	/*
	 * If there is no OpenSSL and no CryptoAPI (or they didn't work), then
	 * fall back on reading /dev/urandom or even /dev/random.
	 */
	if (random_from_file("/dev/urandom", buf, len))
		return true;
	if (random_from_file("/dev/random", buf, len))
		return true;

	/* None of the sources were available. */
	return false;
}