rand: allow fallback from OS (#661)

getrandom() can fail with ENOSYS if the libc supports the function but the kernel does not.

Fixes Bug: #660 
Fix By: Brad House (@bradh352)
pull/663/head
Brad House 1 year ago committed by GitHub
parent 290e9e951c
commit 1c122c3ab8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      RELEASE-NOTES
  2. 44
      src/lib/ares_rand.c

@ -24,6 +24,8 @@ Bug Fixes:
o Some projects that depend on c-ares expect invalid parameter option values o Some projects that depend on c-ares expect invalid parameter option values
passed into ares_init_options() to simply be ignored. This behavior has passed into ares_init_options() to simply be ignored. This behavior has
been restored. [7] been restored. [7]
o On linux getrandom() can fail if the kernel doesn't support the syscall,
fall back to another random source. [8]
Thanks go to these friendly people for their efforts and contributions: Thanks go to these friendly people for their efforts and contributions:
Brad House (@bradh352) Brad House (@bradh352)
@ -38,5 +40,6 @@ References to bug reports and discussions on issues:
[5] = https://github.com/c-ares/c-ares/pull/653 [5] = https://github.com/c-ares/c-ares/pull/653
[6] = https://github.com/c-ares/c-ares/pull/655 [6] = https://github.com/c-ares/c-ares/pull/655
[7] = https://github.com/c-ares/c-ares/commit/c982bf4 [7] = https://github.com/c-ares/c-ares/commit/c982bf4
[8] = https://github.com/c-ares/c-ares/pull/661

@ -29,22 +29,14 @@
#include "ares_private.h" #include "ares_private.h"
#include <stdlib.h> #include <stdlib.h>
#if !defined(HAVE_ARC4RANDOM_BUF) && !defined(HAVE_GETRANDOM) && \
!defined(_WIN32)
# define ARES_NEEDS_RC4 1
#endif
typedef enum { typedef enum {
ARES_RAND_OS = 1, /* OS-provided such as RtlGenRandom or arc4random */ ARES_RAND_OS = 1 << 0, /* OS-provided such as RtlGenRandom or arc4random */
ARES_RAND_FILE = 2, /* OS file-backed random number generator */ ARES_RAND_FILE = 1 << 1, /* OS file-backed random number generator */
#ifdef ARES_NEEDS_RC4 ARES_RAND_RC4 = 1 << 2, /* Internal RC4 based PRNG */
ARES_RAND_RC4 = 3 /* Internal RC4 based PRNG */
#endif
} ares_rand_backend; } ares_rand_backend;
/* Don't build RC4 code if it goes unused as it will generate dead code
* warnings */
#ifdef ARES_NEEDS_RC4
#define ARES_RC4_KEY_LEN 32 /* 256 bits */ #define ARES_RC4_KEY_LEN 32 /* 256 bits */
typedef struct ares_rand_rc4 { typedef struct ares_rand_rc4 {
@ -143,17 +135,14 @@ static void ares_rc4_prng(ares_rand_rc4 *rc4_state, unsigned char *buf,
rc4_state->j = j; rc4_state->j = j;
} }
#endif /* ARES_NEEDS_RC4 */
struct ares_rand_state { struct ares_rand_state {
ares_rand_backend type; ares_rand_backend type;
ares_rand_backend bad_backends;
union { union {
FILE *rand_file; FILE *rand_file;
#ifdef ARES_NEEDS_RC4
ares_rand_rc4 rc4; ares_rand_rc4 rc4;
#endif
} state; } state;
/* Since except for RC4, random data will likely result in a syscall, lets /* Since except for RC4, random data will likely result in a syscall, lets
@ -180,35 +169,39 @@ BOOLEAN WINAPI SystemFunction036(PVOID RandomBuffer, ULONG RandomBufferLength);
static ares_bool_t ares__init_rand_engine(ares_rand_state *state) static ares_bool_t ares__init_rand_engine(ares_rand_state *state)
{ {
memset(state, 0, sizeof(*state)); state->cache_remaining = 0;
#if defined(HAVE_ARC4RANDOM_BUF) || defined(HAVE_GETRANDOM) || defined(_WIN32) #if defined(HAVE_ARC4RANDOM_BUF) || defined(HAVE_GETRANDOM) || defined(_WIN32)
if (!(state->bad_backends & ARES_RAND_OS)) {
state->type = ARES_RAND_OS; state->type = ARES_RAND_OS;
return ARES_TRUE; return ARES_TRUE;
#elif defined(CARES_RANDOM_FILE) }
#endif
#if defined(CARES_RANDOM_FILE)
if (!(state->bad_backends & ARES_RAND_FILE)) {
state->type = ARES_RAND_FILE; state->type = ARES_RAND_FILE;
state->state.rand_file = fopen(CARES_RANDOM_FILE, "rb"); state->state.rand_file = fopen(CARES_RANDOM_FILE, "rb");
if (state->state.rand_file) { if (state->state.rand_file) {
setvbuf(state->state.rand_file, NULL, _IONBF, 0); setvbuf(state->state.rand_file, NULL, _IONBF, 0);
return ARES_TRUE; return ARES_TRUE;
} }
}
/* Fall-Thru on failure to RC4 */ /* Fall-Thru on failure to RC4 */
#endif #endif
#ifdef ARES_NEEDS_RC4
state->type = ARES_RAND_RC4; state->type = ARES_RAND_RC4;
ares_rc4_init(&state->state.rc4); ares_rc4_init(&state->state.rc4);
/* Currently cannot fail */ /* Currently cannot fail */
return ARES_TRUE; return ARES_TRUE;
#endif
} }
ares_rand_state *ares__init_rand_state(void) ares_rand_state *ares__init_rand_state(void)
{ {
ares_rand_state *state = NULL; ares_rand_state *state = NULL;
state = ares_malloc(sizeof(*state)); state = ares_malloc_zero(sizeof(*state));
if (!state) { if (!state) {
return NULL; return NULL;
} }
@ -233,10 +226,8 @@ static void ares__clear_rand_state(ares_rand_state *state)
case ARES_RAND_FILE: case ARES_RAND_FILE:
fclose(state->state.rand_file); fclose(state->state.rand_file);
break; break;
#ifdef ARES_NEEDS_RC4
case ARES_RAND_RC4: case ARES_RAND_RC4:
break; break;
#endif
} }
} }
@ -278,6 +269,11 @@ static void ares__rand_bytes_fetch(ares_rand_state *state, unsigned char *buf,
*/ */
ssize_t rv = getrandom(buf + bytes_read, n > 256 ? 256 : n, 0); ssize_t rv = getrandom(buf + bytes_read, n > 256 ? 256 : n, 0);
if (rv <= 0) { if (rv <= 0) {
/* We need to fall back to another backend */
if (errno == ENOSYS) {
state->bad_backends |= ARES_RAND_OS;
break;
}
continue; /* Just retry. */ continue; /* Just retry. */
} }
@ -307,11 +303,9 @@ static void ares__rand_bytes_fetch(ares_rand_state *state, unsigned char *buf,
} }
break; break;
#ifdef ARES_NEEDS_RC4
case ARES_RAND_RC4: case ARES_RAND_RC4:
ares_rc4_prng(&state->state.rc4, buf, len); ares_rc4_prng(&state->state.rc4, buf, len);
return; return;
#endif
} }
/* If we didn't return before we got here, that means we had a critical rand /* If we didn't return before we got here, that means we had a critical rand

Loading…
Cancel
Save