1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2022, Daniel Stenberg, <[email protected]>, 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#ifdef HAVE_FCNTL_H
28#include <fcntl.h>
29#endif
30
31#include <curl/curl.h>
32#include "vtls/vtls.h"
33#include "sendf.h"
34#include "rand.h"
35
36/* The last 3 #include files should be in this order */
37#include "curl_printf.h"
38#include "curl_memory.h"
39#include "memdebug.h"
40
41#ifdef WIN32
42
43#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
44# define HAVE_MINGW_ORIGINAL
45#endif
46
47#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 && \
48 !defined(HAVE_MINGW_ORIGINAL)
49# define HAVE_WIN_BCRYPTGENRANDOM
50# include <bcrypt.h>
51# ifdef _MSC_VER
52# pragma comment(lib, "bcrypt.lib")
53# endif
54# ifndef BCRYPT_USE_SYSTEM_PREFERRED_RNG
55# define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002
56# endif
57# ifndef STATUS_SUCCESS
58# define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
59# endif
60#elif defined(USE_WIN32_CRYPTO)
61# include <wincrypt.h>
62# ifdef _MSC_VER
63# pragma comment(lib, "advapi32.lib")
64# endif
65#endif
66
67CURLcode Curl_win32_random(unsigned char *entropy, size_t length)
68{
69 memset(entropy, 0, length);
70
71#if defined(HAVE_WIN_BCRYPTGENRANDOM)
72 if(BCryptGenRandom(NULL, entropy, (ULONG)length,
73 BCRYPT_USE_SYSTEM_PREFERRED_RNG) != STATUS_SUCCESS)
74 return CURLE_FAILED_INIT;
75
76 return CURLE_OK;
77#elif defined(USE_WIN32_CRYPTO)
78 {
79 HCRYPTPROV hCryptProv = 0;
80
81 if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
82 CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
83 return CURLE_FAILED_INIT;
84
85 if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) {
86 CryptReleaseContext(hCryptProv, 0UL);
87 return CURLE_FAILED_INIT;
88 }
89
90 CryptReleaseContext(hCryptProv, 0UL);
91 }
92 return CURLE_OK;
93#else
94 return CURLE_NOT_BUILT_IN;
95#endif
96}
97#endif
98
99static CURLcode randit(struct Curl_easy *data, unsigned int *rnd)
100{
101 unsigned int r;
102 CURLcode result = CURLE_OK;
103 static unsigned int randseed;
104 static bool seeded = FALSE;
105
106#ifdef CURLDEBUG
107 char *force_entropy = getenv("CURL_ENTROPY");
108 if(force_entropy) {
109 if(!seeded) {
110 unsigned int seed = 0;
111 size_t elen = strlen(force_entropy);
112 size_t clen = sizeof(seed);
113 size_t min = elen < clen ? elen : clen;
114 memcpy((char *)&seed, force_entropy, min);
115 randseed = ntohl(seed);
116 seeded = TRUE;
117 }
118 else
119 randseed++;
120 *rnd = randseed;
121 return CURLE_OK;
122 }
123#endif
124
125 /* data may be NULL! */
126 result = Curl_ssl_random(data, (unsigned char *)rnd, sizeof(*rnd));
127 if(result != CURLE_NOT_BUILT_IN)
128 /* only if there is no random function in the TLS backend do the non crypto
129 version, otherwise return result */
130 return result;
131
132 /* ---- non-cryptographic version following ---- */
133
134#ifdef WIN32
135 if(!seeded) {
136 result = Curl_win32_random((unsigned char *)rnd, sizeof(*rnd));
137 if(result != CURLE_NOT_BUILT_IN)
138 return result;
139 }
140#endif
141
142#if defined(RANDOM_FILE) && !defined(WIN32)
143 if(!seeded) {
144 /* if there's a random file to read a seed from, use it */
145 int fd = open(RANDOM_FILE, O_RDONLY);
146 if(fd > -1) {
147 /* read random data into the randseed variable */
148 ssize_t nread = read(fd, &randseed, sizeof(randseed));
149 if(nread == sizeof(randseed))
150 seeded = TRUE;
151 close(fd);
152 }
153 }
154#endif
155
156 if(!seeded) {
157 struct curltime now = Curl_now();
158 infof(data, "WARNING: using weak random seed");
159 randseed += (unsigned int)now.tv_usec + (unsigned int)now.tv_sec;
160 randseed = randseed * 1103515245 + 12345;
161 randseed = randseed * 1103515245 + 12345;
162 randseed = randseed * 1103515245 + 12345;
163 seeded = TRUE;
164 }
165
166 /* Return an unsigned 32-bit pseudo-random number. */
167 r = randseed = randseed * 1103515245 + 12345;
168 *rnd = (r << 16) | ((r >> 16) & 0xFFFF);
169 return CURLE_OK;
170}
171
172/*
173 * Curl_rand() stores 'num' number of random unsigned integers in the buffer
174 * 'rndptr' points to.
175 *
176 * If libcurl is built without TLS support or with a TLS backend that lacks a
177 * proper random API (rustls, Gskit or mbedTLS), this function will use "weak"
178 * random.
179 *
180 * When built *with* TLS support and a backend that offers strong random, it
181 * will return error if it cannot provide strong random values.
182 *
183 * NOTE: 'data' may be passed in as NULL when coming from external API without
184 * easy handle!
185 *
186 */
187
188CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num)
189{
190 CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT;
191
192 DEBUGASSERT(num > 0);
193
194 while(num) {
195 unsigned int r;
196 size_t left = num < sizeof(unsigned int) ? num : sizeof(unsigned int);
197
198 result = randit(data, &r);
199 if(result)
200 return result;
201
202 while(left) {
203 *rnd++ = (unsigned char)(r & 0xFF);
204 r >>= 8;
205 --num;
206 --left;
207 }
208 }
209
210 return result;
211}
212
213/*
214 * Curl_rand_hex() fills the 'rnd' buffer with a given 'num' size with random
215 * hexadecimal digits PLUS a zero terminating byte. It must be an odd number
216 * size.
217 */
218
219CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd,
220 size_t num)
221{
222 CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT;
223 const char *hex = "0123456789abcdef";
224 unsigned char buffer[128];
225 unsigned char *bufp = buffer;
226 DEBUGASSERT(num > 1);
227
228#ifdef __clang_analyzer__
229 /* This silences a scan-build warning about accessing this buffer with
230 uninitialized memory. */
231 memset(buffer, 0, sizeof(buffer));
232#endif
233
234 if((num/2 >= sizeof(buffer)) || !(num&1))
235 /* make sure it fits in the local buffer and that it is an odd number! */
236 return CURLE_BAD_FUNCTION_ARGUMENT;
237
238 num--; /* save one for zero termination */
239
240 result = Curl_rand(data, buffer, num/2);
241 if(result)
242 return result;
243
244 while(num) {
245 /* clang-tidy warns on this line without this comment: */
246 /* NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult) */
247 *rnd++ = hex[(*bufp & 0xF0)>>4];
248 *rnd++ = hex[*bufp & 0x0F];
249 bufp++;
250 num -= 2;
251 }
252 *rnd = 0;
253
254 return result;
255}
256