Merge pull request from GHSA-8r8p-23f3-64c2

* segment random number generation into own file

* abstract random code to make it more modular so we can have multiple backends

* rand: add support for arc4random_buf() and also direct CARES_RANDOM_FILE reading

* autotools: fix detection of arc4random_buf

* rework initial rc4 seed for PRNG as last fallback

* rc4: more proper implementation, simplified for clarity

* clarifications
pull/522/head
Brad House 2 years ago committed by GitHub
parent a2b8a4d434
commit 823df3b989
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      CMakeLists.txt
  2. 1
      configure.ac
  3. 85
      m4/cares-functions.m4
  4. 1
      src/lib/Makefile.inc
  5. 3
      src/lib/ares_config.h.cmake
  6. 3
      src/lib/ares_destroy.c
  7. 89
      src/lib/ares_init.c
  8. 19
      src/lib/ares_private.h
  9. 36
      src/lib/ares_query.c
  10. 274
      src/lib/ares_rand.c

@ -393,6 +393,8 @@ CHECK_SYMBOL_EXISTS (strncasecmp "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_STRNCAS
CHECK_SYMBOL_EXISTS (strncmpi "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_STRNCMPI)
CHECK_SYMBOL_EXISTS (strnicmp "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_STRNICMP)
CHECK_SYMBOL_EXISTS (writev "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_WRITEV)
CHECK_SYMBOL_EXISTS (arc4random_buf "${CMAKE_EXTRA_INCLUDE_FILES}" HAVE_ARC4RANDOM_BUF)
# On Android, the system headers may define __system_property_get(), but excluded
# from libc. We need to perform a link test instead of a header/symbol test.

@ -683,6 +683,7 @@ CARES_CHECK_FUNC_STRNCASECMP
CARES_CHECK_FUNC_STRNCMPI
CARES_CHECK_FUNC_STRNICMP
CARES_CHECK_FUNC_WRITEV
CARES_CHECK_FUNC_ARC4RANDOM_BUF
dnl check for AF_INET6

@ -3753,3 +3753,88 @@ AC_DEFUN([CARES_CHECK_FUNC_WRITEV], [
ac_cv_func_writev="no"
fi
])
dnl CARES_CHECK_FUNC_ARC4RANDOM_BUF
dnl -------------------------------------------------
dnl Verify if arc4random_buf is available, prototyped, and
dnl can be compiled. If all of these are true, and
dnl usage has not been previously disallowed with
dnl shell variable cares_disallow_arc4random_buf, then
dnl HAVE_ARC4RANDOM_BUF will be defined.
AC_DEFUN([CARES_CHECK_FUNC_ARC4RANDOM_BUF], [
AC_REQUIRE([CARES_INCLUDES_STDLIB])dnl
#
tst_links_arc4random_buf="unknown"
tst_proto_arc4random_buf="unknown"
tst_compi_arc4random_buf="unknown"
tst_allow_arc4random_buf="unknown"
#
AC_MSG_CHECKING([if arc4random_buf can be linked])
AC_LINK_IFELSE([
AC_LANG_FUNC_LINK_TRY([arc4random_buf])
],[
AC_MSG_RESULT([yes])
tst_links_arc4random_buf="yes"
],[
AC_MSG_RESULT([no])
tst_links_arc4random_buf="no"
])
#
if test "$tst_links_arc4random_buf" = "yes"; then
AC_MSG_CHECKING([if arc4random_buf is prototyped])
AC_EGREP_CPP([arc4random_buf],[
$cares_includes_stdlib
],[
AC_MSG_RESULT([yes])
tst_proto_arc4random_buf="yes"
],[
AC_MSG_RESULT([no])
tst_proto_arc4random_buf="no"
])
fi
#
if test "$tst_proto_arc4random_buf" = "yes"; then
AC_MSG_CHECKING([if arc4random_buf is compilable])
AC_COMPILE_IFELSE([
AC_LANG_PROGRAM([[
$cares_includes_stdlib
]],[[
arc4random_buf(NULL, 0);
return 1;
]])
],[
AC_MSG_RESULT([yes])
tst_compi_arc4random_buf="yes"
],[
AC_MSG_RESULT([no])
tst_compi_arc4random_buf="no"
])
fi
#
if test "$tst_compi_arc4random_buf" = "yes"; then
AC_MSG_CHECKING([if arc4random_buf usage allowed])
if test "x$cares_disallow_arc4random_buf" != "xyes"; then
AC_MSG_RESULT([yes])
tst_allow_arc4random_buf="yes"
else
AC_MSG_RESULT([no])
tst_allow_arc4random_buf="no"
fi
fi
#
AC_MSG_CHECKING([if arc4random_buf might be used])
if test "$tst_links_arc4random_buf" = "yes" &&
test "$tst_proto_arc4random_buf" = "yes" &&
test "$tst_compi_arc4random_buf" = "yes" &&
test "$tst_allow_arc4random_buf" = "yes"; then
AC_MSG_RESULT([yes])
AC_DEFINE_UNQUOTED(HAVE_ARC4RANDOM_BUF, 1,
[Define to 1 if you have the arc4random_buf function.])
ac_cv_func_arc4random_buf="yes"
else
AC_MSG_RESULT([no])
ac_cv_func_arc4random_buf="no"
fi
])

@ -45,6 +45,7 @@ CSOURCES = ares__addrinfo2hostent.c \
ares_platform.c \
ares_process.c \
ares_query.c \
ares_rand.c \
ares_search.c \
ares_send.c \
ares_strcasecmp.c \

@ -346,6 +346,9 @@
/* Define to 1 if you need the memory.h header file even with stdlib.h */
#cmakedefine NEED_MEMORY_H
/* Define if have arc4random_buf() */
#cmakedefine HAVE_ARC4RANDOM_BUF
/* a suitable file/device to read random data from */
#cmakedefine CARES_RANDOM_FILE "@CARES_RANDOM_FILE@"

@ -95,6 +95,9 @@ void ares_destroy(ares_channel channel)
if (channel->hosts_path)
ares_free(channel->hosts_path);
if (channel->rand_state)
ares__destroy_rand_state(channel->rand_state);
ares_free(channel);
}

@ -61,17 +61,6 @@
#undef WIN32 /* Redefined in MingW/MSVC headers */
#endif
/* Define RtlGenRandom = SystemFunction036. This is in advapi32.dll. There is
* no need to dynamically load this, other software used widely does not.
* http://blogs.msdn.com/michael_howard/archive/2005/01/14/353379.aspx
* https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom
*/
#ifdef _WIN32
BOOLEAN WINAPI SystemFunction036(PVOID RandomBuffer, ULONG RandomBufferLength);
# ifndef RtlGenRandom
# define RtlGenRandom(a,b) SystemFunction036(a,b)
# endif
#endif
static int init_by_options(ares_channel channel,
const struct ares_options *options,
@ -87,7 +76,6 @@ static int config_nameserver(struct server_state **servers, int *nservers,
static int set_search(ares_channel channel, const char *str);
static int set_options(ares_channel channel, const char *str);
static const char *try_option(const char *p, const char *q, const char *opt);
static int init_id_key(rc4_key* key,int key_data_len);
static int config_sortlist(struct apattern **sortlist, int *nsort,
const char *str);
@ -165,6 +153,7 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options,
channel->sock_func_cb_data = NULL;
channel->resolvconf_path = NULL;
channel->hosts_path = NULL;
channel->rand_state = NULL;
channel->last_server = 0;
channel->last_timeout_processed = (time_t)now.tv_sec;
@ -218,9 +207,13 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options,
/* Generate random key */
if (status == ARES_SUCCESS) {
status = init_id_key(&channel->id_key, ARES_ID_KEY_LEN);
channel->rand_state = ares__init_rand_state();
if (channel->rand_state == NULL) {
status = ARES_ENOMEM;
}
if (status == ARES_SUCCESS)
channel->next_id = ares__generate_new_id(&channel->id_key);
channel->next_id = ares__generate_new_id(channel->rand_state);
else
DEBUGF(fprintf(stderr, "Error: init_id_key failed: %s\n",
ares_strerror(status)));
@ -242,6 +235,8 @@ done:
ares_free(channel->resolvconf_path);
if(channel->hosts_path)
ares_free(channel->hosts_path);
if (channel->rand_state)
ares__destroy_rand_state(channel->rand_state);
ares_free(channel);
return status;
}
@ -2182,72 +2177,6 @@ static int sortlist_alloc(struct apattern **sortlist, int *nsort,
}
/* initialize an rc4 key. If possible a cryptographically secure random key
is generated using a suitable function otherwise the code defaults to
cross-platform albeit less secure mechanism using rand
*/
static void randomize_key(unsigned char* key,int key_data_len)
{
int randomized = 0;
int counter=0;
#ifdef WIN32
BOOLEAN res;
res = RtlGenRandom(key, key_data_len);
if (res)
randomized = 1;
#else /* !WIN32 */
# ifdef CARES_RANDOM_FILE
FILE *f = fopen(CARES_RANDOM_FILE, "rb");
if(f) {
setvbuf(f, NULL, _IONBF, 0);
counter = aresx_uztosi(fread(key, 1, key_data_len, f));
fclose(f);
}
# endif
#endif /* WIN32 */
if (!randomized) {
for (;counter<key_data_len;counter++)
key[counter]=(unsigned char)(rand() % 256); /* LCOV_EXCL_LINE */
}
}
static int init_id_key(rc4_key* key,int key_data_len)
{
unsigned char index1;
unsigned char index2;
unsigned char* state;
short counter;
unsigned char *key_data_ptr = 0;
key_data_ptr = ares_malloc(key_data_len);
if (!key_data_ptr)
return ARES_ENOMEM;
memset(key_data_ptr, 0, key_data_len);
state = &key->state[0];
for(counter = 0; counter < 256; counter++)
/* unnecessary AND but it keeps some compilers happier */
state[counter] = (unsigned char)(counter & 0xff);
randomize_key(key->state,key_data_len);
key->x = 0;
key->y = 0;
index1 = 0;
index2 = 0;
for(counter = 0; counter < 256; counter++)
{
index2 = (unsigned char)((key_data_ptr[index1] + state[counter] +
index2) % 256);
ARES_SWAP_BYTE(&state[counter], &state[index2]);
index1 = (unsigned char)((index1 + 1) % key_data_len);
}
ares_free(key_data_ptr);
return ARES_SUCCESS;
}
void ares_set_local_ip4(ares_channel channel, unsigned int local_ip)
{
channel->local_ip4 = local_ip;

@ -101,8 +101,6 @@ W32_FUNC const char *_w32_GetHostsFile (void);
#endif
#define ARES_ID_KEY_LEN 31
#include "ares_ipv6.h"
#include "ares_llist.h"
@ -262,12 +260,8 @@ struct apattern {
unsigned short type;
};
typedef struct rc4_key
{
unsigned char state[256];
unsigned char x;
unsigned char y;
} rc4_key;
struct ares_rand_state;
typedef struct ares_rand_state ares_rand_state;
struct ares_channeldata {
/* Configuration data */
@ -302,8 +296,8 @@ struct ares_channeldata {
/* ID to use for next query */
unsigned short next_id;
/* key to use when generating new ids */
rc4_key id_key;
/* random state to use when generating new ids */
ares_rand_state *rand_state;
/* Generation number to use for the next TCP socket open/close */
int tcp_connection_generation;
@ -362,7 +356,10 @@ void ares__close_sockets(ares_channel channel, struct server_state *server);
int ares__get_hostent(FILE *fp, int family, struct hostent **host);
int ares__read_line(FILE *fp, char **buf, size_t *bufsize);
void ares__free_query(struct query *query);
unsigned short ares__generate_new_id(rc4_key* key);
ares_rand_state *ares__init_rand_state(void);
void ares__destroy_rand_state(ares_rand_state *state);
unsigned short ares__generate_new_id(ares_rand_state *state);
struct timeval ares__tvnow(void);
int ares__expand_name_validated(const unsigned char *encoded,
const unsigned char *abuf,

@ -33,32 +33,6 @@ struct qquery {
static void qcallback(void *arg, int status, int timeouts, unsigned char *abuf, int alen);
static void rc4(rc4_key* key, unsigned char *buffer_ptr, int buffer_len)
{
unsigned char x;
unsigned char y;
unsigned char* state;
unsigned char xorIndex;
int counter;
x = key->x;
y = key->y;
state = &key->state[0];
for(counter = 0; counter < buffer_len; counter ++)
{
x = (unsigned char)((x + 1) % 256);
y = (unsigned char)((state[x] + y) % 256);
ARES_SWAP_BYTE(&state[x], &state[y]);
xorIndex = (unsigned char)((state[x] + state[y]) % 256);
buffer_ptr[counter] = (unsigned char)(buffer_ptr[counter]^state[xorIndex]);
}
key->x = x;
key->y = y;
}
static struct query* find_query_by_id(ares_channel channel, unsigned short id)
{
unsigned short qid;
@ -78,7 +52,6 @@ static struct query* find_query_by_id(ares_channel channel, unsigned short id)
return NULL;
}
/* a unique query id is generated using an rc4 key. Since the id may already
be used by a running query (as infrequent as it may be), a lookup is
performed per id generation. In practice this search should happen only
@ -89,19 +62,12 @@ static unsigned short generate_unique_id(ares_channel channel)
unsigned short id;
do {
id = ares__generate_new_id(&channel->id_key);
id = ares__generate_new_id(channel->rand_state);
} while (find_query_by_id(channel, id));
return (unsigned short)id;
}
unsigned short ares__generate_new_id(rc4_key* key)
{
unsigned short r=0;
rc4(key, (unsigned char *)&r, sizeof(r));
return r;
}
void ares_query(ares_channel channel, const char *name, int dnsclass,
int type, ares_callback callback, void *arg)
{

@ -0,0 +1,274 @@
/* Copyright 1998 by the Massachusetts Institute of Technology.
* Copyright (C) 2007-2013 by Daniel Stenberg
*
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting
* documentation, and that the name of M.I.T. not be used in
* advertising or publicity pertaining to distribution of the
* software without specific, written prior permission.
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is"
* without express or implied warranty.
*/
#include "ares_setup.h"
#include "ares.h"
#include "ares_private.h"
#include "ares_nowarn.h"
#include <stdlib.h>
typedef enum {
ARES_RAND_OS = 1, /* OS-provided such as RtlGenRandom or arc4random */
ARES_RAND_FILE = 2, /* OS file-backed random number generator */
ARES_RAND_RC4 = 3 /* Internal RC4 based PRNG */
} ares_rand_backend;
typedef struct ares_rand_rc4
{
unsigned char S[256];
size_t i;
size_t j;
} ares_rand_rc4;
struct ares_rand_state
{
ares_rand_backend type;
union {
FILE *rand_file;
ares_rand_rc4 rc4;
} state;
};
/* Define RtlGenRandom = SystemFunction036. This is in advapi32.dll. There is
* no need to dynamically load this, other software used widely does not.
* http://blogs.msdn.com/michael_howard/archive/2005/01/14/353379.aspx
* https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom
*/
#ifdef _WIN32
BOOLEAN WINAPI SystemFunction036(PVOID RandomBuffer, ULONG RandomBufferLength);
# ifndef RtlGenRandom
# define RtlGenRandom(a,b) SystemFunction036(a,b)
# endif
#endif
#define ARES_RC4_KEY_LEN 32 /* 256 bits */
static unsigned int ares_u32_from_ptr(void *addr)
{
if (sizeof(void *) == 8) {
return (unsigned int)((((size_t)addr >> 32) & 0xFFFFFFFF) | ((size_t)addr & 0xFFFFFFFF));
}
return (unsigned int)((size_t)addr & 0xFFFFFFFF);
}
/* initialize an rc4 key as the last possible fallback. */
static void ares_rc4_generate_key(ares_rand_rc4 *rc4_state, unsigned char *key, size_t key_len)
{
size_t i;
size_t len = 0;
unsigned int data;
struct timeval tv;
if (key_len != ARES_RC4_KEY_LEN)
return;
/* Randomness is hard to come by. Maybe the system randomizes heap and stack addresses.
* Maybe the current timestamp give us some randomness.
* Use rc4_state (heap), &i (stack), and ares__tvnow()
*/
data = ares_u32_from_ptr(rc4_state);
memcpy(key + len, &data, sizeof(data));
len += sizeof(data);
data = ares_u32_from_ptr(&i);
memcpy(key + len, &data, sizeof(data));
len += sizeof(data);
tv = ares__tvnow();
data = (unsigned int)((tv.tv_sec | tv.tv_usec) & 0xFFFFFFFF);
memcpy(key + len, &data, sizeof(data));
len += sizeof(data);
srand(ares_u32_from_ptr(rc4_state) | ares_u32_from_ptr(&i) | (unsigned int)((tv.tv_sec | tv.tv_usec) & 0xFFFFFFFF));
for (i=len; i<key_len; i++) {
key[i]=(unsigned char)(rand() % 256); /* LCOV_EXCL_LINE */
}
}
static void ares_rc4_init(ares_rand_rc4 *rc4_state)
{
unsigned char key[ARES_RC4_KEY_LEN];
size_t i;
size_t j;
ares_rc4_generate_key(rc4_state, key, sizeof(key));
for (i = 0; i < sizeof(rc4_state->S); i++) {
rc4_state->S[i] = i & 0xFF;
}
for(i = 0, j = 0; i < 256; i++) {
j = (j + rc4_state->S[i] + key[i % sizeof(key)]) % 256;
ARES_SWAP_BYTE(&rc4_state->S[i], &rc4_state->S[j]);
}
rc4_state->i = 0;
rc4_state->j = 0;
}
/* Just outputs the key schedule, no need to XOR with any data since we have none */
static void ares_rc4_prng(ares_rand_rc4 *rc4_state, unsigned char *buf, int len)
{
unsigned char *S = rc4_state->S;
size_t i = rc4_state->i;
size_t j = rc4_state->j;
size_t cnt;
for (cnt=0; cnt<len; cnt++) {
i = (i + 1) % 256;
j = (j + S[i]) % 256;
ARES_SWAP_BYTE(&S[i], &S[j]);
buf[cnt] = S[(S[i] + S[j]) % 256];
}
rc4_state->i = i;
rc4_state->j = j;
}
static int ares__init_rand_engine(ares_rand_state *state)
{
memset(state, 0, sizeof(*state));
#if defined(HAVE_ARC4RANDOM_BUF) || defined(_WIN32)
state->type = ARES_RAND_OS;
return 1;
#elif defined(CARES_RANDOM_FILE)
state->type = ARES_RAND_FILE;
state->state.rand_file = fopen(CARES_RANDOM_FILE, "rb");
if (state->state.rand_file) {
setvbuf(state->state.rand_file, NULL, _IONBF, 0);
return 1;
}
/* Fall-Thru on failure to RC4 */
#endif
state->type = ARES_RAND_RC4;
ares_rc4_init(&state->state.rc4);
/* Currently cannot fail */
return 1;
}
ares_rand_state *ares__init_rand_state()
{
ares_rand_state *state = NULL;
state = ares_malloc(sizeof(*state));
if (!state)
return NULL;
if (!ares__init_rand_engine(state)) {
ares_free(state);
return NULL;
}
return state;
}
static void ares__clear_rand_state(ares_rand_state *state)
{
if (!state)
return;
switch (state->type) {
case ARES_RAND_OS:
break;
case ARES_RAND_FILE:
fclose(state->state.rand_file);
break;
case ARES_RAND_RC4:
break;
}
}
static void ares__reinit_rand(ares_rand_state *state)
{
ares__clear_rand_state(state);
ares__init_rand_engine(state);
}
void ares__destroy_rand_state(ares_rand_state *state)
{
if (!state)
return;
ares__clear_rand_state(state);
ares_free(state);
}
static void ares__rand_bytes(ares_rand_state *state, unsigned char *buf, size_t len)
{
while (1) {
size_t rv;
size_t bytes_read = 0;
switch (state->type) {
case ARES_RAND_OS:
#ifdef _WIN32
RtlGenRandom(buf, len);
return;
#elif defined(HAVE_ARC4RANDOM_BUF)
arc4random_buf(buf, len);
return;
#else
/* Shouldn't be possible to be here */
break;
#endif
case ARES_RAND_FILE:
while (1) {
size_t rv = fread(buf + bytes_read, 1, len - bytes_read, state->state.rand_file);
if (rv == 0)
break; /* critical error, will reinit rand state */
bytes_read += rv;
if (bytes_read == len)
return;
}
break;
case ARES_RAND_RC4:
ares_rc4_prng(&state->state.rc4, buf, len);
return;
}
/* If we didn't return before we got here, that means we had a critical rand
* failure and need to reinitialized */
ares__reinit_rand(state);
}
}
unsigned short ares__generate_new_id(ares_rand_state *state)
{
unsigned short r=0;
ares__rand_bytes(state, (unsigned char *)&r, sizeof(r));
return r;
}
Loading…
Cancel
Save