• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifdef HAVE_ARC4RANDOM
36 /* Some platforms might have the prototype missing (ubuntu + libressl) */
37 uint32_t arc4random(void);
38 #endif
39 
40 #include <curl/curl.h>
41 #include "urldata.h"
42 #include "vtls/vtls.h"
43 #include "sendf.h"
44 #include "timeval.h"
45 #include "rand.h"
46 #include "escape.h"
47 
48 /* The last 3 #include files should be in this order */
49 #include "curl_printf.h"
50 #include "curl_memory.h"
51 #include "memdebug.h"
52 
53 #ifdef WIN32
54 
55 #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600
56 #  define HAVE_WIN_BCRYPTGENRANDOM
57 #  include <bcrypt.h>
58 #  ifdef _MSC_VER
59 #    pragma comment(lib, "bcrypt.lib")
60 #  endif
61 #  ifndef BCRYPT_USE_SYSTEM_PREFERRED_RNG
62 #  define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002
63 #  endif
64 #  ifndef STATUS_SUCCESS
65 #  define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
66 #  endif
67 #elif defined(USE_WIN32_CRYPTO)
68 #  include <wincrypt.h>
69 #  ifdef _MSC_VER
70 #    pragma comment(lib, "advapi32.lib")
71 #  endif
72 #endif
73 
Curl_win32_random(unsigned char * entropy,size_t length)74 CURLcode Curl_win32_random(unsigned char *entropy, size_t length)
75 {
76   memset(entropy, 0, length);
77 
78 #if defined(HAVE_WIN_BCRYPTGENRANDOM)
79   if(BCryptGenRandom(NULL, entropy, (ULONG)length,
80                      BCRYPT_USE_SYSTEM_PREFERRED_RNG) != STATUS_SUCCESS)
81     return CURLE_FAILED_INIT;
82 
83   return CURLE_OK;
84 #elif defined(USE_WIN32_CRYPTO)
85   {
86     HCRYPTPROV hCryptProv = 0;
87 
88     if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
89                             CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
90       return CURLE_FAILED_INIT;
91 
92     if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) {
93       CryptReleaseContext(hCryptProv, 0UL);
94       return CURLE_FAILED_INIT;
95     }
96 
97     CryptReleaseContext(hCryptProv, 0UL);
98   }
99   return CURLE_OK;
100 #else
101   return CURLE_NOT_BUILT_IN;
102 #endif
103 }
104 #endif
105 
randit(struct Curl_easy * data,unsigned int * rnd)106 static CURLcode randit(struct Curl_easy *data, unsigned int *rnd)
107 {
108   unsigned int r;
109   CURLcode result = CURLE_OK;
110   static unsigned int randseed;
111   static bool seeded = FALSE;
112 
113 #ifdef CURLDEBUG
114   char *force_entropy = getenv("CURL_ENTROPY");
115   if(force_entropy) {
116     if(!seeded) {
117       unsigned int seed = 0;
118       size_t elen = strlen(force_entropy);
119       size_t clen = sizeof(seed);
120       size_t min = elen < clen ? elen : clen;
121       memcpy((char *)&seed, force_entropy, min);
122       randseed = ntohl(seed);
123       seeded = TRUE;
124     }
125     else
126       randseed++;
127     *rnd = randseed;
128     return CURLE_OK;
129   }
130 #endif
131 
132   /* data may be NULL! */
133   result = Curl_ssl_random(data, (unsigned char *)rnd, sizeof(*rnd));
134   if(result != CURLE_NOT_BUILT_IN)
135     /* only if there is no random function in the TLS backend do the non crypto
136        version, otherwise return result */
137     return result;
138 
139   /* ---- non-cryptographic version following ---- */
140 
141 #ifdef WIN32
142   if(!seeded) {
143     result = Curl_win32_random((unsigned char *)rnd, sizeof(*rnd));
144     if(result != CURLE_NOT_BUILT_IN)
145       return result;
146   }
147 #endif
148 
149 #ifdef HAVE_ARC4RANDOM
150   *rnd = (unsigned int)arc4random();
151   return CURLE_OK;
152 #endif
153 
154 #if defined(RANDOM_FILE) && !defined(WIN32)
155   if(!seeded) {
156     /* if there's a random file to read a seed from, use it */
157     int fd = open(RANDOM_FILE, O_RDONLY);
158     if(fd > -1) {
159       /* read random data into the randseed variable */
160       ssize_t nread = read(fd, &randseed, sizeof(randseed));
161       if(nread == sizeof(randseed))
162         seeded = TRUE;
163       close(fd);
164     }
165   }
166 #endif
167 
168   if(!seeded) {
169     struct curltime now = Curl_now();
170     infof(data, "WARNING: using weak random seed");
171     randseed += (unsigned int)now.tv_usec + (unsigned int)now.tv_sec;
172     randseed = randseed * 1103515245 + 12345;
173     randseed = randseed * 1103515245 + 12345;
174     randseed = randseed * 1103515245 + 12345;
175     seeded = TRUE;
176   }
177 
178   /* Return an unsigned 32-bit pseudo-random number. */
179   r = randseed = randseed * 1103515245 + 12345;
180   *rnd = (r << 16) | ((r >> 16) & 0xFFFF);
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 > 0);
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     return CURLE_BAD_FUNCTION_ARGUMENT;
247 
248   num--; /* save one for null-termination */
249 
250   result = Curl_rand(data, buffer, num/2);
251   if(result)
252     return result;
253 
254   Curl_hexencode(buffer, num/2, rnd, num + 1);
255   return result;
256 }
257 
258 /*
259  * Curl_rand_alnum() fills the 'rnd' buffer with a given 'num' size with random
260  * alphanumerical chars PLUS a null-terminating byte.
261  */
262 
263 static const char alnum[] =
264   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
265 
Curl_rand_alnum(struct Curl_easy * data,unsigned char * rnd,size_t num)266 CURLcode Curl_rand_alnum(struct Curl_easy *data, unsigned char *rnd,
267                          size_t num)
268 {
269   CURLcode result = CURLE_OK;
270   const int alnumspace = sizeof(alnum) - 1;
271   unsigned int r;
272   DEBUGASSERT(num > 1);
273 
274   num--; /* save one for null-termination */
275 
276   while(num) {
277     do {
278       result = randit(data, &r);
279       if(result)
280         return result;
281     } while(r >= (UINT_MAX - UINT_MAX % alnumspace));
282 
283     *rnd++ = alnum[r % alnumspace];
284     num--;
285   }
286   *rnd = 0;
287 
288   return result;
289 }
290