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 70CURLcode 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 102static 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 200CURLcode 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 231CURLcode 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 265static const char alnum[] = 266 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 267 268CURLcode 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