Setting & getting of tsig info per upstream

This commit is contained in:
Willem Toorop 2015-12-21 12:22:59 +01:00
parent bc2ec7cee3
commit 98dc4018c3
4 changed files with 264 additions and 59 deletions

View File

@ -54,6 +54,7 @@
#include "dnssec.h"
#include "stub.h"
#include "list.h"
#include "dict.h"
#define GETDNS_PORT_ZERO 0
#define GETDNS_PORT_DNS 53
@ -167,7 +168,7 @@ static inline void canonicalize_dname(uint8_t *dname)
{
uint8_t *next_label;
while (*dname) {
while (*dname && !(*dname & 0xC0)) {
next_label = dname + *dname + 1;
dname += 1;
while (dname < next_label) {
@ -599,6 +600,64 @@ net_req_query_id_cmp(const void *id1, const void *id2)
return (intptr_t)id1 - (intptr_t)id2;
}
static getdns_tsig_info tsig_info[] = {
{ GETDNS_NO_TSIG, NULL, 0, NULL, 0, 0, 0 }
, { GETDNS_HMAC_MD5 , "hmac-md5.sig-alg.reg.int", 24
, (uint8_t *)"\x08hmac-md5\x07sig-alg\x03reg\x03int", 26, 10, 16 }
, { GETDNS_NO_TSIG, NULL, 0, NULL, 0, 0, 0 }
, { GETDNS_HMAC_SHA1 , "hmac-sha1" , 9
, (uint8_t *)"\x09hmac-sha1" , 11, 10, 20 }
, { GETDNS_HMAC_SHA224, "hmac-sha224", 11
, (uint8_t *)"\x0bhmac-sha224", 13, 14, 28 }
, { GETDNS_HMAC_SHA224, "hmac-sha256", 11
, (uint8_t *)"\x0bhmac-sha256", 13, 16, 32 }
, { GETDNS_HMAC_SHA224, "hmac-sha384", 11
, (uint8_t *)"\x0bhmac-sha383", 13, 24, 48 }
, { GETDNS_HMAC_SHA224, "hmac-sha512", 11
, (uint8_t *)"\x0bhmac-sha512", 13, 32, 64 }
, { GETDNS_HMAC_MD5 , "hmac-md5" , 8
, (uint8_t *)"\x08hmac-md5" , 10, 10, 16 }
};
const getdns_tsig_info *_getdns_get_tsig_info(getdns_tsig_algo tsig_alg)
{
return tsig_alg > sizeof(tsig_info) - 1
|| tsig_info[tsig_alg].alg == GETDNS_NO_TSIG ? NULL
: &tsig_info[tsig_alg];
}
static const getdns_tsig_algo _getdns_get_tsig_algo(getdns_bindata *algo)
{
getdns_tsig_info *i;
if (!algo || algo->size == 0)
return GETDNS_NO_TSIG;
if (algo->data[algo->size-1] != 0) {
/* Unterminated string */
for (i = tsig_info; i < tsig_info + sizeof(tsig_info); i++)
if (algo->size == i->strlen_name &&
strncasecmp((const char *)algo->data, i->name,
i->strlen_name) == 0)
return i->alg;
} else if (!_getdns_bindata_is_dname(algo)) {
/* Terminated string */
for (i = tsig_info; i < tsig_info + sizeof(tsig_info); i++)
if (algo->size - 1 == i->strlen_name &&
strncasecmp((const char *)algo->data, i->name,
i->strlen_name) == 0)
return i->alg;
} else {
/* fqdn, canonical_dname_compare is now safe to use! */
for (i = tsig_info; i < tsig_info + sizeof(tsig_info); i++)
if (canonical_dname_compare(algo->data, i->dname) == 0)
return i->alg;
}
return GETDNS_NO_TSIG;
}
static void
upstream_init(getdns_upstream *upstream,
getdns_upstreams *parent, struct addrinfo *ai)
@ -635,6 +694,10 @@ upstream_init(getdns_upstream *upstream,
upstream->has_prev_client_cookie = 0;
upstream->has_server_cookie = 0;
upstream->tsig_alg = GETDNS_NO_TSIG;
upstream->tsig_dname_len = 0;
upstream->tsig_size = 0;
/* Tracking of network requests on this socket */
_getdns_rbtree_init(&upstream->netreq_by_query_id,
net_req_query_id_cmp);
@ -1715,15 +1778,21 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context,
upstreams = upstreams_create(
context, count * GETDNS_UPSTREAM_TRANSPORTS);
for (i = 0; i < count; i++) {
getdns_dict *dict;
getdns_dict *dict;
getdns_bindata *address_type;
getdns_bindata *address_data;
getdns_bindata *tls_auth_name;
struct sockaddr_storage addr;
getdns_bindata *scope_id;
getdns_bindata *scope_id;
getdns_upstream *upstream;
getdns_bindata *tsig_alg_name, *tsig_name, *tsig_key;
getdns_tsig_algo tsig_alg;
char tsig_name_str[1024];
uint8_t tsig_dname_spc[256], *tsig_dname;
size_t tsig_dname_len;
if ((r = getdns_list_get_dict(upstream_list, i, &dict)))
goto error;
@ -1760,6 +1829,63 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context,
eos[scope_id->size] = 0;
}
tsig_alg_name = tsig_name = tsig_key = NULL;
tsig_dname = NULL;
tsig_dname_len = 0;
if (getdns_dict_get_bindata(dict,
"tsig_algorithm", &tsig_alg_name) == GETDNS_RETURN_GOOD)
tsig_alg = _getdns_get_tsig_algo(tsig_alg_name);
else
tsig_alg = GETDNS_HMAC_MD5;
if (getdns_dict_get_bindata(dict, "tsig_name", &tsig_name))
tsig_alg = GETDNS_NO_TSIG; /* No name, no TSIG */
else if (tsig_name->size == 0)
tsig_alg = GETDNS_NO_TSIG;
else if (tsig_name->data[tsig_name->size - 1] != 0) {
/* Unterminated string */
if (tsig_name->size >= sizeof(tsig_name_str) - 1)
tsig_alg = GETDNS_NO_TSIG;
else {
(void) memcpy(tsig_name_str, tsig_name->data
, tsig_name->size);
tsig_name_str[tsig_name->size] = 0;
tsig_dname_len = sizeof(tsig_dname_spc);
if (gldns_str2wire_dname_buf(tsig_name_str,
tsig_dname_spc, &tsig_dname_len))
tsig_alg = GETDNS_NO_TSIG;
else
tsig_dname = tsig_dname_spc;
}
} else if (!_getdns_bindata_is_dname(tsig_name)) {
/* Terminated string */
tsig_dname_len = sizeof(tsig_dname_spc);
if (gldns_str2wire_dname_buf(tsig_name_str,
tsig_dname_spc, &tsig_dname_len))
tsig_alg = GETDNS_NO_TSIG;
else
tsig_dname = tsig_dname_spc;
} else if (tsig_name->size > sizeof(tsig_dname_spc))
tsig_alg = GETDNS_NO_TSIG;
else {
/* fqdn */
tsig_dname = memcpy(tsig_dname_spc, tsig_name->data
, tsig_name->size);
tsig_dname_len = tsig_name->size;
}
if (getdns_dict_get_bindata(dict, "tsig_secret", &tsig_key))
tsig_alg = GETDNS_NO_TSIG; /* No key, no TSIG */
/* Don't check TSIG length contraints here.
* Let the upstream decide what is secure enough.
*/
/* Loop to create upstreams as needed*/
for (size_t j = 0; j < GETDNS_UPSTREAM_TRANSPORTS; j++) {
uint32_t port;
@ -1798,6 +1924,25 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context,
upstream->tls_auth_name[tls_auth_name->size] = '\0';
}
}
if ((upstream->tsig_alg = tsig_alg)) {
if (tsig_name) {
(void) memcpy(upstream->tsig_dname,
tsig_dname, tsig_dname_len);
upstream->tsig_dname_len =
tsig_dname_len;
} else
upstream->tsig_dname_len = 0;
if (tsig_key) {
(void) memcpy(upstream->tsig_key,
tsig_key->data, tsig_key->size);
upstream->tsig_size = tsig_key->size;
} else
upstream->tsig_size = 0;
} else {
upstream->tsig_dname_len = 0;
upstream->tsig_size = 0;
}
upstreams->count++;
freeaddrinfo(ai);
}
@ -2542,9 +2687,12 @@ upstream_port(getdns_upstream *upstream)
}
static getdns_dict*
_get_context_settings(getdns_context* context) {
_get_context_settings(getdns_context* context)
{
getdns_return_t r = GETDNS_RETURN_GOOD;
getdns_dict* result = getdns_dict_create_with_context(context);
getdns_list *upstreams;
if (!result) {
return NULL;
}
@ -2562,34 +2710,8 @@ _get_context_settings(getdns_context* context) {
r |= getdns_dict_set_int(result, "append_name", context->append_name);
/* list fields */
if (context->suffix) r |= getdns_dict_set_list(result, "suffix", context->suffix);
if (context->upstreams && context->upstreams->count > 0) {
size_t i;
getdns_upstream *upstream;
getdns_list *upstreams =
getdns_list_create_with_context(context);
for (i = 0; i < context->upstreams->count;) {
size_t j;
getdns_dict *d;
upstream = &context->upstreams->upstreams[i];
d = sockaddr_dict(context,
(struct sockaddr *)&upstream->addr);
for ( j = 1, i++
; j < GETDNS_UPSTREAM_TRANSPORTS &&
i < context->upstreams->count
; j++, i++) {
upstream = &context->upstreams->upstreams[i];
if (upstream->transport != GETDNS_TRANSPORT_TLS)
continue;
if (upstream_port(upstream) != getdns_port_array[j])
continue;
(void) getdns_dict_set_int(d, "tls_port",
(uint32_t) upstream_port(upstream));
}
r |= _getdns_list_append_dict(upstreams, d);
getdns_dict_destroy(d);
}
if (!getdns_context_get_upstream_recursive_servers(context, &upstreams)) {
r |= getdns_dict_set_list(result, "upstream_recursive_servers",
upstreams);
getdns_list_destroy(upstreams);
@ -2956,43 +3078,88 @@ getdns_context_get_dnssec_allowed_skew(getdns_context *context,
getdns_return_t
getdns_context_get_upstream_recursive_servers(getdns_context *context,
getdns_list **upstream_list) {
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
RETURN_IF_NULL(upstream_list, GETDNS_RETURN_INVALID_PARAMETER);
*upstream_list = NULL;
if (context->upstreams && context->upstreams->count > 0) {
getdns_return_t r = GETDNS_RETURN_GOOD;
size_t i;
getdns_upstream *upstream;
getdns_list *upstreams = getdns_list_create();
for (i = 0; i < context->upstreams->count;) {
getdns_list **upstreams_r)
{
size_t i;
getdns_list *upstreams;
getdns_return_t r;
if (!context || !upstreams_r)
return GETDNS_RETURN_INVALID_PARAMETER;
if (!(upstreams = getdns_list_create_with_context(context)))
return GETDNS_RETURN_MEMORY_ERROR;
if (!context->upstreams || context->upstreams->count == 0) {
*upstreams_r = upstreams;
return GETDNS_RETURN_GOOD;
}
r = GETDNS_RETURN_GOOD;
i = 0;
while (!r && i < context->upstreams->count) {
size_t j;
getdns_dict *d;
upstream = &context->upstreams->upstreams[i];
d = sockaddr_dict(context, (struct sockaddr *)&upstream->addr);
getdns_upstream *upstream = &context->upstreams->upstreams[i];
getdns_bindata bindata;
const getdns_tsig_info *tsig_info;
if (!(d =
sockaddr_dict(context, (struct sockaddr*)&upstream->addr))) {
r = GETDNS_RETURN_MEMORY_ERROR;
break;
}
if (upstream->tsig_alg) {
tsig_info = _getdns_get_tsig_info(upstream->tsig_alg);
bindata.data = tsig_info->dname;
bindata.size = tsig_info->dname_len;
if ((r = getdns_dict_set_bindata(
d, "tsig_algorithm", &bindata)))
break;
if (upstream->tsig_dname_len) {
bindata.data = upstream->tsig_dname;
bindata.size = upstream->tsig_dname_len;
if ((r = getdns_dict_set_bindata(
d, "tsig_name", &bindata)))
break;
}
if (upstream->tsig_size) {
bindata.data = upstream->tsig_key;
bindata.size = upstream->tsig_size;
if ((r = getdns_dict_set_bindata(
d, "tsig_secret", &bindata)))
break;
}
}
for ( j = 1, i++
; j < GETDNS_UPSTREAM_TRANSPORTS &&
i < context->upstreams->count
; j++, i++) {
upstream = &context->upstreams->upstreams[i];
if (upstream->transport != GETDNS_TRANSPORT_TLS)
continue;
if (upstream_port(upstream) != getdns_port_array[j])
continue;
(void) getdns_dict_set_int(d, "tls_port",
(uint32_t) upstream_port(upstream));
if (upstream->transport == GETDNS_TRANSPORT_UDP &&
upstream_port(upstream) != getdns_port_array[j] &&
(r = getdns_dict_set_int(d, "port",
(uint32_t)upstream_port(upstream))))
break;
if (upstream->transport == GETDNS_TRANSPORT_TLS &&
upstream_port(upstream) != getdns_port_array[j] &&
(r = getdns_dict_set_int(d, "tls_port",
(uint32_t)upstream_port(upstream))))
break;
}
r |= _getdns_list_append_dict(upstreams, d);
if (!r)
r = _getdns_list_append_dict(upstreams, d);
getdns_dict_destroy(d);
}
if (r != GETDNS_RETURN_GOOD) {
getdns_list_destroy(upstreams);
return GETDNS_RETURN_MEMORY_ERROR;
}
*upstream_list = upstreams;
}
return GETDNS_RETURN_GOOD;
if (r)
getdns_list_destroy(upstreams);
else
*upstreams_r = upstreams;
return r;
}
getdns_return_t

View File

@ -79,6 +79,29 @@ typedef enum getdns_tls_hs_state {
GETDNS_HS_FAILED
} getdns_tls_hs_state_t;
typedef enum getdns_tsig_algo {
GETDNS_NO_TSIG = 0, /* Do not use tsig */
GETDNS_HMAC_MD5 = 1, /* 128 bits */
GETDNS_GSS_TSIG = 2, /* Not supported */
GETDNS_HMAC_SHA1 = 3, /* 160 bits */
GETDNS_HMAC_SHA224 = 4,
GETDNS_HMAC_SHA256 = 5,
GETDNS_HMAC_SHA384 = 6,
GETDNS_HMAC_SHA512 = 7
} getdns_tsig_algo;
typedef struct getdns_tsig_info {
getdns_tsig_algo alg;
const char *name;
size_t strlen_name;
const uint8_t *dname;
size_t dname_len;
size_t min_size; /* in # octets */
size_t max_size; /* Actual size in # octets */
} getdns_tsig_info;
const getdns_tsig_info *_getdns_get_tsig_info(getdns_tsig_algo tsig_alg);
typedef struct getdns_upstream {
/* backpointer to containing upstreams structure */
struct getdns_upstreams *upstreams;
@ -120,6 +143,13 @@ typedef struct getdns_upstream {
unsigned has_server_cookie : 1;
unsigned server_cookie_len : 5;
/* TSIG */
uint8_t tsig_dname[256];
size_t tsig_dname_len;
size_t tsig_size;
uint8_t tsig_key[256];
getdns_tsig_algo tsig_alg;
} getdns_upstream;
typedef struct getdns_upstreams {

View File

@ -656,12 +656,15 @@ getdns_indent(size_t indent)
return spaces + 80 - (indent < 80 ? indent : 0);
} /* getdns_indent */
static int
int
_getdns_bindata_is_dname(getdns_bindata *bindata)
{
size_t i = 0, n_labels = 0;
while (i < bindata->size && bindata->data[i]) {
if (bindata->data[i] & 0xC0) /* Compression pointer! */
return 0;
i += ((size_t)bindata->data[i]) + 1;
n_labels++;
}

View File

@ -71,6 +71,11 @@ getdns_return_t _getdns_dict_find(
getdns_return_t _getdns_dict_find_and_add(
getdns_dict *dict, const char *key, getdns_item **item);
/* Return 1 (true) if bindata can be interpreted as an
* uncompressed dname.
*/
int _getdns_bindata_is_dname(getdns_bindata *bindata);
#endif
/* dict.h */