Validate received TSIG reply

This commit is contained in:
Willem Toorop 2015-12-22 01:03:31 +01:00
parent 6c1e00fc3f
commit 8a8a017fc5
4 changed files with 191 additions and 1 deletions

View File

@ -995,6 +995,7 @@ getdns_pp_dict(gldns_buffer * buf, size_t indent,
if (!json && if (!json &&
(strcmp(item->node.key, "answer_type") == 0 || (strcmp(item->node.key, "answer_type") == 0 ||
strcmp(item->node.key, "dnssec_status") == 0 || strcmp(item->node.key, "dnssec_status") == 0 ||
strcmp(item->node.key, "tsig_status") == 0 ||
strcmp(item->node.key, "status") == 0 || strcmp(item->node.key, "status") == 0 ||
strcmp(item->node.key, "append_name") == 0 || strcmp(item->node.key, "append_name") == 0 ||
strcmp(item->node.key, "follow_redirects") == 0 || strcmp(item->node.key, "follow_redirects") == 0 ||

View File

@ -134,6 +134,7 @@ network_req_init(getdns_network_req *net_req, getdns_dns_req *owner,
net_req->owner = owner; net_req->owner = owner;
net_req->dnssec_status = GETDNS_DNSSEC_INDETERMINATE; net_req->dnssec_status = GETDNS_DNSSEC_INDETERMINATE;
net_req->tsig_status = GETDNS_DNSSEC_INDETERMINATE;
net_req->upstream = NULL; net_req->upstream = NULL;
net_req->fd = -1; net_req->fd = -1;
@ -308,6 +309,9 @@ _getdns_network_req_add_tsig(getdns_network_req *req)
unsigned int md_len = EVP_MAX_MD_SIZE; unsigned int md_len = EVP_MAX_MD_SIZE;
const EVP_MD *digester; const EVP_MD *digester;
/* Should only be called when in stub mode */
assert(req->query);
if (upstream->tsig_alg == GETDNS_NO_TSIG || !upstream->tsig_dname_len) if (upstream->tsig_alg == GETDNS_NO_TSIG || !upstream->tsig_dname_len)
return req->response - req->query; return req->response - req->query;
@ -384,11 +388,177 @@ _getdns_network_req_add_tsig(getdns_network_req *req)
return req->response - req->query; return req->response - req->query;
DEBUG_STUB("Sending with TSIG, mac length: %d\n", (int)md_len); DEBUG_STUB("Sending with TSIG, mac length: %d\n", (int)md_len);
req->tsig_status = GETDNS_DNSSEC_INSECURE;
gldns_write_uint16(req->query + 10, arcount + 1); gldns_write_uint16(req->query + 10, arcount + 1);
req->response = gldns_buffer_current(&gbuf); req->response = gldns_buffer_current(&gbuf);
return req->response - req->query; return req->response - req->query;
} }
void
_getdns_network_validate_tsig(getdns_network_req *req)
{
_getdns_rr_iter rr_spc, *rr;
_getdns_rdf_iter rdf_spc, *rdf;
uint8_t *request_mac;
uint16_t request_mac_len;
uint8_t tsig_vars[MAXIMUM_TSIG_SPACE];
gldns_buffer gbuf;
uint8_t *dname;
size_t dname_len;
uint8_t *response_mac;
uint16_t response_mac_len;
uint8_t other_len;
uint8_t result_mac[EVP_MAX_MD_SIZE];
unsigned int result_mac_len = EVP_MAX_MD_SIZE;
uint16_t original_id;
const EVP_MD *digester;
HMAC_CTX ctx;
DEBUG_STUB("Validate TSIG\n");
for ( rr = _getdns_rr_iter_init(&rr_spc, req->query,
(req->response - req->query))
; rr
; rr = _getdns_rr_iter_next(rr)) {
if (_getdns_rr_iter_section(rr) == GLDNS_SECTION_ADDITIONAL &&
gldns_read_uint16(rr->rr_type) == GETDNS_RRTYPE_TSIG)
break;
}
if (!rr || !(rdf = _getdns_rdf_iter_init_at(&rdf_spc, rr, 3)))
return; /* No good TSIG sent, so nothing expected on reply */
request_mac_len = gldns_read_uint16(rdf->pos);
if (request_mac_len != rdf->nxt - rdf->pos - 2)
return;
DEBUG_STUB("Request MAC found length: %d\n", (int)(request_mac_len));
request_mac = rdf->pos + 2;
/* Now we expect a TSIG on the response! */
req->tsig_status = GETDNS_DNSSEC_BOGUS;
for ( rr = _getdns_rr_iter_init(
&rr_spc, req->response, req->response_len)
; rr
; rr = _getdns_rr_iter_next(rr)) {
if (_getdns_rr_iter_section(rr) == GLDNS_SECTION_ADDITIONAL &&
gldns_read_uint16(rr->rr_type) == GETDNS_RRTYPE_TSIG)
break;
}
if (!rr || !(rdf = _getdns_rdf_iter_init(&rdf_spc, rr)))
return;
gldns_buffer_init_frm_data(&gbuf, tsig_vars, MAXIMUM_TSIG_SPACE);
dname_len = gldns_buffer_remaining(&gbuf);
if (!(dname = _getdns_owner_if_or_as_decompressed(
rr, gldns_buffer_current(&gbuf), &dname_len)))
return;
if (dname == gldns_buffer_current(&gbuf))
gldns_buffer_skip(&gbuf, dname_len);
else
gldns_buffer_write(&gbuf, dname, dname_len);
gldns_buffer_write(&gbuf, rr->rr_type + 2, 2); /* Class */
gldns_buffer_write(&gbuf, rr->rr_type + 4, 4); /* TTL */
dname_len = gldns_buffer_remaining(&gbuf);
if (!(dname = _getdns_rdf_if_or_as_decompressed(
rdf, gldns_buffer_current(&gbuf), &dname_len)))
return;
if (dname == gldns_buffer_current(&gbuf))
gldns_buffer_skip(&gbuf, dname_len);
else
gldns_buffer_write(&gbuf, dname, dname_len);
if (!(rdf = _getdns_rdf_iter_next(rdf)) ||
rdf->nxt - rdf->pos != 6)
return;
gldns_buffer_write(&gbuf, rdf->pos, 6); /* Time Signed */
if (!(rdf = _getdns_rdf_iter_next(rdf)) ||
rdf->nxt - rdf->pos != 2)
return;
gldns_buffer_write(&gbuf, rdf->pos, 2); /* Fudge */
if (!(rdf = _getdns_rdf_iter_next(rdf))) /* mac */
return;
response_mac_len = gldns_read_uint16(rdf->pos);
if (response_mac_len != rdf->nxt - rdf->pos - 2)
return;
DEBUG_STUB("Response MAC found length: %d\n", (int)(response_mac_len));
response_mac = rdf->pos + 2;
if (!(rdf = _getdns_rdf_iter_next(rdf)) ||
rdf->nxt -rdf->pos != 2) /* Original ID */
return;
original_id = gldns_read_uint16(rdf->pos);
if (!(rdf = _getdns_rdf_iter_next(rdf)) ||
rdf->nxt - rdf->pos != 2)
return;
gldns_buffer_write(&gbuf, rdf->pos, 2); /* Error */
if (!(rdf = _getdns_rdf_iter_next(rdf))) /* Other */
return;
gldns_buffer_write_u16(&gbuf, 0); /* Other len */
other_len = gldns_read_uint16(rdf->pos);
if (other_len != rdf->nxt - rdf->pos - 2)
return;
if (other_len)
gldns_buffer_write(&gbuf, rdf->pos, other_len);
/* TSIG found */
DEBUG_STUB("TSIG found, original ID: %d\n", (int)original_id);
gldns_write_uint16(req->response + 10,
gldns_read_uint16(req->response + 10) - 1);
gldns_write_uint16(req->response, original_id);
switch (req->upstream->tsig_alg) {
#ifdef HAVE_EVP_MD5
case GETDNS_HMAC_MD5 : digester = EVP_md5() ; break;
#endif
#ifdef HAVE_EVP_SHA1
case GETDNS_HMAC_SHA1 : digester = EVP_sha1() ; break;
#endif
#ifdef HAVE_EVP_SHA224
case GETDNS_HMAC_SHA224: digester = EVP_sha224(); break;
#endif
#ifdef HAVE_EVP_SHA256
case GETDNS_HMAC_SHA256: digester = EVP_sha256(); break;
#endif
#ifdef HAVE_EVP_SHA384
case GETDNS_HMAC_SHA384: digester = EVP_sha384(); break;
#endif
#ifdef HAVE_EVP_SHA512
case GETDNS_HMAC_SHA512: digester = EVP_sha512(); break;
#endif
default : return;
}
HMAC_CTX_init(&ctx);
(void) HMAC_Init_ex(&ctx, req->upstream->tsig_key,
req->upstream->tsig_size, digester, NULL);
(void) HMAC_Update(&ctx, request_mac - 2, request_mac_len + 2);
(void) HMAC_Update(&ctx, req->response, rr->pos - req->response);
(void) HMAC_Update(&ctx, tsig_vars, gldns_buffer_position(&gbuf));
HMAC_Final(&ctx, result_mac, &result_mac_len);
DEBUG_STUB("Result MAC length: %d\n", (int)(result_mac_len));
if (result_mac_len == response_mac_len &&
memcmp(result_mac, response_mac, result_mac_len) == 0)
req->tsig_status = GETDNS_DNSSEC_SECURE;
HMAC_CTX_cleanup(&ctx);
gldns_write_uint16(req->response, gldns_read_uint16(req->query));
gldns_write_uint16(req->response + 10,
gldns_read_uint16(req->response + 10) + 1);
}
void void
_getdns_dns_req_free(getdns_dns_req * req) _getdns_dns_req_free(getdns_dns_req * req)
{ {

View File

@ -209,6 +209,14 @@ typedef struct getdns_network_req
/* dnssec status */ /* dnssec status */
int dnssec_status; int dnssec_status;
/* tsig status:
* GETDNS_DNSSEC_INDETERMINATE means "No TSIG processing"
* GETDNS_DNSSEC_INSECURE means "TSIG sent, validate reply"
* GETDNS_DNSSEC_SECURE means "Validated"
* GETDNS_DNSSEC_BOGUS means "Validation failed"
*/
int tsig_status;
/* For stub resolving */ /* For stub resolving */
struct getdns_upstream *upstream; struct getdns_upstream *upstream;
int fd; int fd;
@ -382,5 +390,7 @@ void _getdns_network_req_clear_upstream_options(getdns_network_req * req);
/* Adds TSIG signature (if needed) and returns query length */ /* Adds TSIG signature (if needed) and returns query length */
size_t _getdns_network_req_add_tsig(getdns_network_req *req); size_t _getdns_network_req_add_tsig(getdns_network_req *req);
void _getdns_network_validate_tsig(getdns_network_req *req);
#endif #endif
/* types-internal.h */ /* types-internal.h */

View File

@ -802,6 +802,9 @@ _getdns_create_getdns_response(getdns_dns_req *completed_request)
if (! netreq->response_len) if (! netreq->response_len)
continue; continue;
if (netreq->tsig_status == GETDNS_DNSSEC_INSECURE)
_getdns_network_validate_tsig(netreq);
nreplies++; nreplies++;
if (netreq->dnssec_status == GETDNS_DNSSEC_SECURE) if (netreq->dnssec_status == GETDNS_DNSSEC_SECURE)
nsecure++; nsecure++;
@ -820,6 +823,8 @@ _getdns_create_getdns_response(getdns_dns_req *completed_request)
else if (completed_request->dnssec_return_only_secure else if (completed_request->dnssec_return_only_secure
&& netreq->dnssec_status != GETDNS_DNSSEC_SECURE) && netreq->dnssec_status != GETDNS_DNSSEC_SECURE)
continue; continue;
else if (netreq->tsig_status == GETDNS_DNSSEC_BOGUS)
continue;
} }
if (!(reply = _getdns_create_reply_dict(context, if (!(reply = _getdns_create_reply_dict(context,
netreq, just_addrs, &rrsigs_in_answer))) netreq, just_addrs, &rrsigs_in_answer)))
@ -847,7 +852,11 @@ _getdns_create_getdns_response(getdns_dns_req *completed_request)
netreq->dnssec_status)) netreq->dnssec_status))
goto error; goto error;
} }
if (netreq->tsig_status != GETDNS_DNSSEC_INDETERMINATE) {
if (getdns_dict_set_int(reply, "tsig_status",
netreq->tsig_status))
goto error;
}
if (_getdns_list_append_dict(replies_tree, reply)) { if (_getdns_list_append_dict(replies_tree, reply)) {
getdns_dict_destroy(reply); getdns_dict_destroy(reply);
goto error; goto error;