1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25 #include "curl_setup.h"
26
27 #include <limits.h>
28
29 #ifdef HAVE_FCNTL_H
30 #include <fcntl.h>
31 #endif
32 #ifdef HAVE_ARPA_INET_H
33 #include <arpa/inet.h>
34 #endif
35
36 #include <curl/curl.h>
37 #include "urldata.h"
38 #include "vtls/vtls.h"
39 #include "sendf.h"
40 #include "timeval.h"
41 #include "rand.h"
42 #include "escape.h"
43
44 /* The last 3 #include files should be in this order */
45 #include "curl_printf.h"
46 #include "curl_memory.h"
47 #include "memdebug.h"
48
49 #ifdef _WIN32
50
51 #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600
52 # define HAVE_WIN_BCRYPTGENRANDOM
53 # include <bcrypt.h>
54 # ifdef _MSC_VER
55 # pragma comment(lib, "bcrypt.lib")
56 # endif
57 # ifndef BCRYPT_USE_SYSTEM_PREFERRED_RNG
58 # define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002
59 # endif
60 # ifndef STATUS_SUCCESS
61 # define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
62 # endif
63 #elif defined(USE_WIN32_CRYPTO)
64 # include <wincrypt.h>
65 # ifdef _MSC_VER
66 # pragma comment(lib, "advapi32.lib")
67 # endif
68 #endif
69
Curl_win32_random(unsigned char * entropy,size_t length)70 CURLcode Curl_win32_random(unsigned char *entropy, size_t length)
71 {
72 memset(entropy, 0, length);
73
74 #if defined(HAVE_WIN_BCRYPTGENRANDOM)
75 if(BCryptGenRandom(NULL, entropy, (ULONG)length,
76 BCRYPT_USE_SYSTEM_PREFERRED_RNG) != STATUS_SUCCESS)
77 return CURLE_FAILED_INIT;
78
79 return CURLE_OK;
80 #elif defined(USE_WIN32_CRYPTO)
81 {
82 HCRYPTPROV hCryptProv = 0;
83
84 if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
85 CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
86 return CURLE_FAILED_INIT;
87
88 if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) {
89 CryptReleaseContext(hCryptProv, 0UL);
90 return CURLE_FAILED_INIT;
91 }
92
93 CryptReleaseContext(hCryptProv, 0UL);
94 }
95 return CURLE_OK;
96 #else
97 return CURLE_NOT_BUILT_IN;
98 #endif
99 }
100 #endif
101
randit(struct Curl_easy * data,unsigned int * rnd)102 static CURLcode randit(struct Curl_easy *data, unsigned int *rnd)
103 {
104 CURLcode result = CURLE_OK;
105 static unsigned int randseed;
106 static bool seeded = FALSE;
107
108 #ifdef CURLDEBUG
109 char *force_entropy = getenv("CURL_ENTROPY");
110 if(force_entropy) {
111 if(!seeded) {
112 unsigned int seed = 0;
113 size_t elen = strlen(force_entropy);
114 size_t clen = sizeof(seed);
115 size_t min = elen < clen ? elen : clen;
116 memcpy((char *)&seed, force_entropy, min);
117 randseed = ntohl(seed);
118 seeded = TRUE;
119 }
120 else
121 randseed++;
122 *rnd = randseed;
123 return CURLE_OK;
124 }
125 #endif
126
127 /* data may be NULL! */
128 result = Curl_ssl_random(data, (unsigned char *)rnd, sizeof(*rnd));
129 if(result != CURLE_NOT_BUILT_IN)
130 /* only if there is no random function in the TLS backend do the non crypto
131 version, otherwise return result */
132 return result;
133
134 /* ---- non-cryptographic version following ---- */
135
136 #ifdef _WIN32
137 if(!seeded) {
138 result = Curl_win32_random((unsigned char *)rnd, sizeof(*rnd));
139 if(result != CURLE_NOT_BUILT_IN)
140 return result;
141 }
142 #endif
143
144 #if defined(HAVE_ARC4RANDOM) && !defined(USE_OPENSSL)
145 if(!seeded) {
146 *rnd = (unsigned int)arc4random();
147 return CURLE_OK;
148 }
149 #endif
150
151 #if defined(RANDOM_FILE) && !defined(_WIN32)
152 if(!seeded) {
153 /* if there's a random file to read a seed from, use it */
154 int fd = open(RANDOM_FILE, O_RDONLY);
155 if(fd > -1) {
156 /* read random data into the randseed variable */
157 ssize_t nread = read(fd, &randseed, sizeof(randseed));
158 if(nread == sizeof(randseed))
159 seeded = TRUE;
160 close(fd);
161 }
162 }
163 #endif
164
165 if(!seeded) {
166 struct curltime now = Curl_now();
167 infof(data, "WARNING: using weak random seed");
168 randseed += (unsigned int)now.tv_usec + (unsigned int)now.tv_sec;
169 randseed = randseed * 1103515245 + 12345;
170 randseed = randseed * 1103515245 + 12345;
171 randseed = randseed * 1103515245 + 12345;
172 seeded = TRUE;
173 }
174
175 {
176 unsigned int r;
177 /* Return an unsigned 32-bit pseudo-random number. */
178 r = randseed = randseed * 1103515245 + 12345;
179 *rnd = (r << 16) | ((r >> 16) & 0xFFFF);
180 }
181 return CURLE_OK;
182 }
183
184 /*
185 * Curl_rand() stores 'num' number of random unsigned characters in the buffer
186 * 'rnd' points to.
187 *
188 * If libcurl is built without TLS support or with a TLS backend that lacks a
189 * proper random API (rustls or mbedTLS), this function will use "weak"
190 * random.
191 *
192 * When built *with* TLS support and a backend that offers strong random, it
193 * will return error if it cannot provide strong random values.
194 *
195 * NOTE: 'data' may be passed in as NULL when coming from external API without
196 * easy handle!
197 *
198 */
199
Curl_rand(struct Curl_easy * data,unsigned char * rnd,size_t num)200 CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num)
201 {
202 CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT;
203
204 DEBUGASSERT(num);
205
206 while(num) {
207 unsigned int r;
208 size_t left = num < sizeof(unsigned int) ? num : sizeof(unsigned int);
209
210 result = randit(data, &r);
211 if(result)
212 return result;
213
214 while(left) {
215 *rnd++ = (unsigned char)(r & 0xFF);
216 r >>= 8;
217 --num;
218 --left;
219 }
220 }
221
222 return result;
223 }
224
225 /*
226 * Curl_rand_hex() fills the 'rnd' buffer with a given 'num' size with random
227 * hexadecimal digits PLUS a null-terminating byte. It must be an odd number
228 * size.
229 */
230
Curl_rand_hex(struct Curl_easy * data,unsigned char * rnd,size_t num)231 CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd,
232 size_t num)
233 {
234 CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT;
235 unsigned char buffer[128];
236 DEBUGASSERT(num > 1);
237
238 #ifdef __clang_analyzer__
239 /* This silences a scan-build warning about accessing this buffer with
240 uninitialized memory. */
241 memset(buffer, 0, sizeof(buffer));
242 #endif
243
244 if((num/2 >= sizeof(buffer)) || !(num&1)) {
245 /* make sure it fits in the local buffer and that it is an odd number! */
246 DEBUGF(infof(data, "invalid buffer size with Curl_rand_hex"));
247 return CURLE_BAD_FUNCTION_ARGUMENT;
248 }
249
250 num--; /* save one for null-termination */
251
252 result = Curl_rand(data, buffer, num/2);
253 if(result)
254 return result;
255
256 Curl_hexencode(buffer, num/2, rnd, num + 1);
257 return result;
258 }
259
260 /*
261 * Curl_rand_alnum() fills the 'rnd' buffer with a given 'num' size with random
262 * alphanumerical chars PLUS a null-terminating byte.
263 */
264
265 static const char alnum[] =
266 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
267
Curl_rand_alnum(struct Curl_easy * data,unsigned char * rnd,size_t num)268 CURLcode Curl_rand_alnum(struct Curl_easy *data, unsigned char *rnd,
269 size_t num)
270 {
271 CURLcode result = CURLE_OK;
272 const int alnumspace = sizeof(alnum) - 1;
273 unsigned int r;
274 DEBUGASSERT(num > 1);
275
276 num--; /* save one for null-termination */
277
278 while(num) {
279 do {
280 result = randit(data, &r);
281 if(result)
282 return result;
283 } while(r >= (UINT_MAX - UINT_MAX % alnumspace));
284
285 *rnd++ = alnum[r % alnumspace];
286 num--;
287 }
288 *rnd = 0;
289
290 return result;
291 }
292