/src/c-ares/src/lib/util/ares_rand.c
Line | Count | Source |
1 | | /* MIT License |
2 | | * |
3 | | * Copyright (c) 2023 Brad House |
4 | | * |
5 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
6 | | * of this software and associated documentation files (the "Software"), to deal |
7 | | * in the Software without restriction, including without limitation the rights |
8 | | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
9 | | * copies of the Software, and to permit persons to whom the Software is |
10 | | * furnished to do so, subject to the following conditions: |
11 | | * |
12 | | * The above copyright notice and this permission notice (including the next |
13 | | * paragraph) shall be included in all copies or substantial portions of the |
14 | | * Software. |
15 | | * |
16 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
19 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
21 | | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
22 | | * SOFTWARE. |
23 | | * |
24 | | * SPDX-License-Identifier: MIT |
25 | | */ |
26 | | |
27 | | #include "ares_private.h" |
28 | | #include <stdlib.h> |
29 | | |
30 | | /* Older MacOS versions require including AvailabilityMacros.h before |
31 | | * sys/random.h */ |
32 | | #ifdef HAVE_AVAILABILITYMACROS_H |
33 | | # include <AvailabilityMacros.h> |
34 | | #endif |
35 | | |
36 | | #ifdef HAVE_SYS_RANDOM_H |
37 | | # include <sys/random.h> |
38 | | #endif |
39 | | |
40 | | |
41 | | typedef enum { |
42 | | ARES_RAND_OS = 1 << 0, /* OS-provided such as RtlGenRandom or arc4random */ |
43 | | ARES_RAND_FILE = 1 << 1, /* OS file-backed random number generator */ |
44 | | ARES_RAND_RC4 = 1 << 2 /* Internal RC4 based PRNG */ |
45 | | } ares_rand_backend; |
46 | | |
47 | 4.18k | #define ARES_RC4_KEY_LEN 32 /* 256 bits */ |
48 | | |
49 | | typedef struct ares_rand_rc4 { |
50 | | unsigned char S[256]; |
51 | | size_t i; |
52 | | size_t j; |
53 | | } ares_rand_rc4; |
54 | | |
55 | | static unsigned int ares_u32_from_ptr(void *addr) |
56 | 0 | { |
57 | 0 | /* LCOV_EXCL_START: FallbackCode */ |
58 | 0 | if (ares_is_64bit()) { |
59 | 0 | return (unsigned int)((((ares_uint64_t)addr >> 32) & 0xFFFFFFFF) ^ |
60 | 0 | ((ares_uint64_t)addr & 0xFFFFFFFF)); |
61 | 0 | } |
62 | 0 | return (unsigned int)((size_t)addr & 0xFFFFFFFF); |
63 | 0 | /* LCOV_EXCL_STOP */ |
64 | 0 | } |
65 | | |
66 | | /* initialize an rc4 key as the last possible fallback. */ |
67 | | static void ares_rc4_generate_key(ares_rand_rc4 *rc4_state, unsigned char *key, |
68 | | size_t key_len) |
69 | 4.18k | { |
70 | | /* LCOV_EXCL_START: FallbackCode */ |
71 | 4.18k | size_t i; |
72 | 4.18k | size_t len = 0; |
73 | 4.18k | unsigned int data; |
74 | 4.18k | ares_timeval_t tv; |
75 | | |
76 | 4.18k | if (key_len != ARES_RC4_KEY_LEN) { |
77 | 0 | return; |
78 | 0 | } |
79 | | |
80 | 4.18k | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
81 | | /* For fuzzing, random should be deterministic */ |
82 | 4.18k | srand(0); |
83 | | #else |
84 | | /* Randomness is hard to come by. Maybe the system randomizes heap and stack |
85 | | * addresses. Maybe the current timestamp give us some randomness. Use |
86 | | * rc4_state (heap), &i (stack), and ares_tvnow() |
87 | | */ |
88 | | data = ares_u32_from_ptr(rc4_state); |
89 | | memcpy(key + len, &data, sizeof(data)); |
90 | | len += sizeof(data); |
91 | | |
92 | | data = ares_u32_from_ptr(&i); |
93 | | memcpy(key + len, &data, sizeof(data)); |
94 | | len += sizeof(data); |
95 | | |
96 | | ares_tvnow(&tv); |
97 | | data = (unsigned int)((tv.sec ^ tv.usec) & 0xFFFFFFFF); |
98 | | memcpy(key + len, &data, sizeof(data)); |
99 | | len += sizeof(data); |
100 | | |
101 | | srand(ares_u32_from_ptr(rc4_state) ^ ares_u32_from_ptr(&i) ^ |
102 | | (unsigned int)((tv.sec ^ tv.usec) & 0xFFFFFFFF)); |
103 | | #endif |
104 | | |
105 | 138k | for (i = len; i < key_len; i++) { |
106 | 133k | key[i] = (unsigned char)(rand() % 256); /* LCOV_EXCL_LINE */ |
107 | 133k | } |
108 | | /* LCOV_EXCL_STOP */ |
109 | 4.18k | } |
110 | | |
111 | | #define ARES_SWAP_BYTE(a, b) \ |
112 | 2.14M | do { \ |
113 | 2.14M | unsigned char swapByte = *(a); \ |
114 | 2.14M | *(a) = *(b); \ |
115 | 2.14M | *(b) = swapByte; \ |
116 | 2.14M | } while (0) |
117 | | |
118 | | static void ares_rc4_init(ares_rand_rc4 *rc4_state) |
119 | 4.18k | { |
120 | | /* LCOV_EXCL_START: FallbackCode */ |
121 | 4.18k | unsigned char key[ARES_RC4_KEY_LEN]; |
122 | 4.18k | size_t i; |
123 | 4.18k | size_t j; |
124 | | |
125 | 4.18k | ares_rc4_generate_key(rc4_state, key, sizeof(key)); |
126 | | |
127 | 1.07M | for (i = 0; i < sizeof(rc4_state->S); i++) { |
128 | 1.07M | rc4_state->S[i] = i & 0xFF; |
129 | 1.07M | } |
130 | | |
131 | 1.07M | for (i = 0, j = 0; i < 256; i++) { |
132 | 1.07M | j = (j + rc4_state->S[i] + key[i % sizeof(key)]) % 256; |
133 | 1.07M | ARES_SWAP_BYTE(&rc4_state->S[i], &rc4_state->S[j]); |
134 | 1.07M | } |
135 | | |
136 | 4.18k | rc4_state->i = 0; |
137 | 4.18k | rc4_state->j = 0; |
138 | | /* LCOV_EXCL_STOP */ |
139 | 4.18k | } |
140 | | |
141 | | /* Just outputs the key schedule, no need to XOR with any data since we have |
142 | | * none */ |
143 | | static void ares_rc4_prng(ares_rand_rc4 *rc4_state, unsigned char *buf, |
144 | | size_t len) |
145 | 4.18k | { |
146 | | /* LCOV_EXCL_START: FallbackCode */ |
147 | 4.18k | unsigned char *S = rc4_state->S; |
148 | 4.18k | size_t i = rc4_state->i; |
149 | 4.18k | size_t j = rc4_state->j; |
150 | 4.18k | size_t cnt; |
151 | | |
152 | 1.07M | for (cnt = 0; cnt < len; cnt++) { |
153 | 1.07M | i = (i + 1) % 256; |
154 | 1.07M | j = (j + S[i]) % 256; |
155 | | |
156 | 1.07M | ARES_SWAP_BYTE(&S[i], &S[j]); |
157 | 1.07M | buf[cnt] = S[(S[i] + S[j]) % 256]; |
158 | 1.07M | } |
159 | | |
160 | 4.18k | rc4_state->i = i; |
161 | 4.18k | rc4_state->j = j; |
162 | | /* LCOV_EXCL_STOP */ |
163 | 4.18k | } |
164 | | |
165 | | struct ares_rand_state { |
166 | | ares_rand_backend type; |
167 | | ares_rand_backend bad_backends; |
168 | | |
169 | | union { |
170 | | FILE *rand_file; |
171 | | ares_rand_rc4 rc4; |
172 | | } state; |
173 | | |
174 | | /* Since except for RC4, random data will likely result in a syscall, lets |
175 | | * pre-pull 256 bytes at a time. Every query will pull 2 bytes off this so |
176 | | * that means we should only need a syscall every 128 queries. 256bytes |
177 | | * appears to be a sweet spot that may be able to be served without |
178 | | * interruption */ |
179 | | unsigned char cache[256]; |
180 | | size_t cache_remaining; |
181 | | }; |
182 | | |
183 | | /* Define RtlGenRandom = SystemFunction036. This is in advapi32.dll. There is |
184 | | * no need to dynamically load this, other software used widely does not. |
185 | | * http://blogs.msdn.com/michael_howard/archive/2005/01/14/353379.aspx |
186 | | * https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom |
187 | | */ |
188 | | #ifdef _WIN32 |
189 | | BOOLEAN WINAPI SystemFunction036(PVOID RandomBuffer, ULONG RandomBufferLength); |
190 | | # ifndef RtlGenRandom |
191 | | # define RtlGenRandom(a, b) SystemFunction036(a, b) |
192 | | # endif |
193 | | #endif |
194 | | |
195 | | |
196 | | static ares_bool_t ares_init_rand_engine(ares_rand_state *state) |
197 | 4.18k | { |
198 | 4.18k | state->cache_remaining = 0; |
199 | | |
200 | 4.18k | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
201 | | /* For fuzzing, random should be deterministic */ |
202 | 4.18k | state->bad_backends |= ARES_RAND_OS | ARES_RAND_FILE; |
203 | 4.18k | #endif |
204 | | |
205 | 4.18k | #if defined(HAVE_ARC4RANDOM_BUF) || defined(HAVE_GETRANDOM) || defined(_WIN32) |
206 | 4.18k | if (!(state->bad_backends & ARES_RAND_OS)) { |
207 | 0 | state->type = ARES_RAND_OS; |
208 | 0 | return ARES_TRUE; |
209 | 0 | } |
210 | 4.18k | #endif |
211 | | |
212 | 4.18k | #if defined(CARES_RANDOM_FILE) |
213 | | /* LCOV_EXCL_START: FallbackCode */ |
214 | 4.18k | if (!(state->bad_backends & ARES_RAND_FILE)) { |
215 | 0 | state->type = ARES_RAND_FILE; |
216 | 0 | state->state.rand_file = fopen(CARES_RANDOM_FILE, "rb"); |
217 | 0 | if (state->state.rand_file) { |
218 | 0 | setvbuf(state->state.rand_file, NULL, _IONBF, 0); |
219 | 0 | return ARES_TRUE; |
220 | 0 | } |
221 | 0 | } |
222 | | /* LCOV_EXCL_STOP */ |
223 | | |
224 | | /* Fall-Thru on failure to RC4 */ |
225 | 4.18k | #endif |
226 | | |
227 | | /* LCOV_EXCL_START: FallbackCode */ |
228 | 4.18k | state->type = ARES_RAND_RC4; |
229 | 4.18k | ares_rc4_init(&state->state.rc4); |
230 | | /* LCOV_EXCL_STOP */ |
231 | | |
232 | | /* Currently cannot fail */ |
233 | 4.18k | return ARES_TRUE; /* LCOV_EXCL_LINE: UntestablePath */ |
234 | 4.18k | } |
235 | | |
236 | | ares_rand_state *ares_init_rand_state(void) |
237 | 4.18k | { |
238 | 4.18k | ares_rand_state *state = NULL; |
239 | | |
240 | 4.18k | state = ares_malloc_zero(sizeof(*state)); |
241 | 4.18k | if (!state) { |
242 | 0 | return NULL; |
243 | 0 | } |
244 | | |
245 | 4.18k | if (!ares_init_rand_engine(state)) { |
246 | 0 | ares_free(state); /* LCOV_EXCL_LINE: UntestablePath */ |
247 | 0 | return NULL; /* LCOV_EXCL_LINE: UntestablePath */ |
248 | 0 | } |
249 | | |
250 | 4.18k | return state; |
251 | 4.18k | } |
252 | | |
253 | | static void ares_clear_rand_state(ares_rand_state *state) |
254 | 4.18k | { |
255 | 4.18k | if (!state) { |
256 | 0 | return; /* LCOV_EXCL_LINE: DefensiveCoding */ |
257 | 0 | } |
258 | | |
259 | 4.18k | switch (state->type) { |
260 | 0 | case ARES_RAND_OS: |
261 | 0 | break; |
262 | | /* LCOV_EXCL_START: FallbackCode */ |
263 | 0 | case ARES_RAND_FILE: |
264 | 0 | fclose(state->state.rand_file); |
265 | 0 | break; |
266 | 4.18k | case ARES_RAND_RC4: |
267 | 4.18k | break; |
268 | | /* LCOV_EXCL_STOP */ |
269 | 4.18k | } |
270 | 4.18k | } |
271 | | |
272 | | static void ares_reinit_rand(ares_rand_state *state) |
273 | 0 | { |
274 | | /* LCOV_EXCL_START: UntestablePath */ |
275 | 0 | ares_clear_rand_state(state); |
276 | 0 | ares_init_rand_engine(state); |
277 | | /* LCOV_EXCL_STOP */ |
278 | 0 | } |
279 | | |
280 | | void ares_destroy_rand_state(ares_rand_state *state) |
281 | 4.18k | { |
282 | 4.18k | if (!state) { |
283 | 0 | return; |
284 | 0 | } |
285 | | |
286 | 4.18k | ares_clear_rand_state(state); |
287 | 4.18k | ares_free(state); |
288 | 4.18k | } |
289 | | |
290 | | static void ares_rand_bytes_fetch(ares_rand_state *state, unsigned char *buf, |
291 | | size_t len) |
292 | 4.18k | { |
293 | 4.18k | while (1) { |
294 | 4.18k | size_t bytes_read = 0; |
295 | | |
296 | 4.18k | switch (state->type) { |
297 | 0 | case ARES_RAND_OS: |
298 | | #ifdef _WIN32 |
299 | | RtlGenRandom(buf, (ULONG)len); |
300 | | return; |
301 | | #elif defined(HAVE_ARC4RANDOM_BUF) |
302 | | arc4random_buf(buf, len); |
303 | | return; |
304 | | #elif defined(HAVE_GETRANDOM) |
305 | 0 | while (1) { |
306 | 0 | size_t n = len - bytes_read; |
307 | | /* getrandom() on Linux always succeeds and is never |
308 | | * interrupted by a signal when requesting <= 256 bytes. |
309 | | */ |
310 | 0 | ssize_t rv = getrandom(buf + bytes_read, n > 256 ? 256 : n, 0); |
311 | 0 | if (rv <= 0) { |
312 | | /* We need to fall back to another backend */ |
313 | 0 | if (errno == ENOSYS) { |
314 | 0 | state->bad_backends |= ARES_RAND_OS; |
315 | 0 | break; |
316 | 0 | } |
317 | 0 | continue; /* Just retry. */ |
318 | 0 | } |
319 | | |
320 | 0 | bytes_read += (size_t)rv; |
321 | 0 | if (bytes_read == len) { |
322 | 0 | return; |
323 | 0 | } |
324 | 0 | } |
325 | 0 | break; |
326 | | #else |
327 | | /* Shouldn't be possible to be here */ |
328 | | break; |
329 | | #endif |
330 | | |
331 | | /* LCOV_EXCL_START: FallbackCode */ |
332 | | |
333 | 0 | case ARES_RAND_FILE: |
334 | 0 | while (1) { |
335 | 0 | size_t rv = fread(buf + bytes_read, 1, len - bytes_read, |
336 | 0 | state->state.rand_file); |
337 | 0 | if (rv == 0) { |
338 | 0 | break; /* critical error, will reinit rand state */ |
339 | 0 | } |
340 | | |
341 | 0 | bytes_read += rv; |
342 | 0 | if (bytes_read == len) { |
343 | 0 | return; |
344 | 0 | } |
345 | 0 | } |
346 | 0 | break; |
347 | | |
348 | 4.18k | case ARES_RAND_RC4: |
349 | 4.18k | ares_rc4_prng(&state->state.rc4, buf, len); |
350 | 4.18k | return; |
351 | | |
352 | | /* LCOV_EXCL_STOP */ |
353 | 4.18k | } |
354 | | |
355 | | /* If we didn't return before we got here, that means we had a critical rand |
356 | | * failure and need to reinitialized */ |
357 | 0 | ares_reinit_rand(state); /* LCOV_EXCL_LINE: UntestablePath */ |
358 | 0 | } |
359 | 4.18k | } |
360 | | |
361 | | void ares_rand_bytes(ares_rand_state *state, unsigned char *buf, size_t len) |
362 | 4.59k | { |
363 | | /* See if we need to refill the cache to serve the request, but if len is |
364 | | * excessive, we're not going to update our cache or serve from cache */ |
365 | 4.59k | if (len > state->cache_remaining && len < sizeof(state->cache)) { |
366 | 4.18k | size_t fetch_size = sizeof(state->cache) - state->cache_remaining; |
367 | 4.18k | ares_rand_bytes_fetch(state, state->cache, fetch_size); |
368 | 4.18k | state->cache_remaining = sizeof(state->cache); |
369 | 4.18k | } |
370 | | |
371 | | /* Serve from cache */ |
372 | 4.59k | if (len <= state->cache_remaining) { |
373 | 4.59k | size_t offset = sizeof(state->cache) - state->cache_remaining; |
374 | 4.59k | memcpy(buf, state->cache + offset, len); |
375 | 4.59k | state->cache_remaining -= len; |
376 | 4.59k | return; |
377 | 4.59k | } |
378 | | |
379 | | /* Serve direct due to excess size of request */ |
380 | 0 | ares_rand_bytes_fetch(state, buf, len); |
381 | 0 | } |
382 | | |
383 | | unsigned short ares_generate_new_id(ares_rand_state *state) |
384 | 0 | { |
385 | 0 | unsigned short r = 0; |
386 | |
|
387 | 0 | ares_rand_bytes(state, (unsigned char *)&r, sizeof(r)); |
388 | 0 | return r; |
389 | 0 | } |