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  * Copyright (c) 1996-2017, PostgreSQL Global Development Group
10  *
11  * IDENTIFICATION
12  *	  src/port/pg_strong_random.c
13  *
14  *-------------------------------------------------------------------------
15  */
16 
17 #ifndef FRONTEND
18 #include "postgres.h"
19 #else
20 #include "postgres_fe.h"
21 #endif
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 WIN32
31 #include <wincrypt.h>
32 #endif
33 
34 #ifdef WIN32
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
random_from_file(char * filename,void * buf,size_t len)47 random_from_file(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
pg_strong_random(void * buf,size_t len)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.
112 	 */
113 #define NUM_RAND_POLL_RETRIES 8
114 
115 	for (i = 0; i < NUM_RAND_POLL_RETRIES; i++)
116 	{
117 		if (RAND_status() == 1)
118 		{
119 			/* The CSPRNG is sufficiently seeded */
120 			break;
121 		}
122 
123 		if (RAND_poll() == 0)
124 		{
125 			/*
126 			 * RAND_poll() failed to generate any seed data, which means that
127 			 * RAND_bytes() will probably fail.  For now, just fall through
128 			 * and let that happen.  XXX: maybe we could seed it some other
129 			 * way.
130 			 */
131 			break;
132 		}
133 	}
134 
135 	if (RAND_bytes(buf, len) == 1)
136 		return true;
137 	return false;
138 
139 	/*
140 	 * Windows has CryptoAPI for strong cryptographic numbers.
141 	 */
142 #elif defined(USE_WIN32_RANDOM)
143 	if (hProvider == 0)
144 	{
145 		if (!CryptAcquireContext(&hProvider,
146 								 NULL,
147 								 MS_DEF_PROV,
148 								 PROV_RSA_FULL,
149 								 CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
150 		{
151 			/*
152 			 * On failure, set back to 0 in case the value was for some reason
153 			 * modified.
154 			 */
155 			hProvider = 0;
156 		}
157 	}
158 	/* Re-check in case we just retrieved the provider */
159 	if (hProvider != 0)
160 	{
161 		if (CryptGenRandom(hProvider, len, buf))
162 			return true;
163 	}
164 	return false;
165 
166 	/*
167 	 * Read /dev/urandom ourselves.
168 	 */
169 #elif defined(USE_DEV_URANDOM)
170 	if (random_from_file("/dev/urandom", buf, len))
171 		return true;
172 	return false;
173 
174 #else
175 	/* The autoconf script should not have allowed this */
176 #error no source of random numbers configured
177 #endif
178 }
179