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