mirror of https://github.com/getdnsapi/getdns.git
Validate received TSIG reply
This commit is contained in:
parent
6c1e00fc3f
commit
8a8a017fc5
|
@ -995,6 +995,7 @@ getdns_pp_dict(gldns_buffer * buf, size_t indent,
|
|||
if (!json &&
|
||||
(strcmp(item->node.key, "answer_type") == 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, "append_name") == 0 ||
|
||||
strcmp(item->node.key, "follow_redirects") == 0 ||
|
||||
|
|
|
@ -134,6 +134,7 @@ network_req_init(getdns_network_req *net_req, getdns_dns_req *owner,
|
|||
net_req->owner = owner;
|
||||
|
||||
net_req->dnssec_status = GETDNS_DNSSEC_INDETERMINATE;
|
||||
net_req->tsig_status = GETDNS_DNSSEC_INDETERMINATE;
|
||||
|
||||
net_req->upstream = NULL;
|
||||
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;
|
||||
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)
|
||||
return req->response - req->query;
|
||||
|
||||
|
@ -384,11 +388,177 @@ _getdns_network_req_add_tsig(getdns_network_req *req)
|
|||
return req->response - req->query;
|
||||
|
||||
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);
|
||||
req->response = gldns_buffer_current(&gbuf);
|
||||
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
|
||||
_getdns_dns_req_free(getdns_dns_req * req)
|
||||
{
|
||||
|
|
|
@ -209,6 +209,14 @@ typedef struct getdns_network_req
|
|||
/* 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 */
|
||||
struct getdns_upstream *upstream;
|
||||
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 */
|
||||
size_t _getdns_network_req_add_tsig(getdns_network_req *req);
|
||||
|
||||
void _getdns_network_validate_tsig(getdns_network_req *req);
|
||||
|
||||
#endif
|
||||
/* types-internal.h */
|
||||
|
|
|
@ -802,6 +802,9 @@ _getdns_create_getdns_response(getdns_dns_req *completed_request)
|
|||
if (! netreq->response_len)
|
||||
continue;
|
||||
|
||||
if (netreq->tsig_status == GETDNS_DNSSEC_INSECURE)
|
||||
_getdns_network_validate_tsig(netreq);
|
||||
|
||||
nreplies++;
|
||||
if (netreq->dnssec_status == GETDNS_DNSSEC_SECURE)
|
||||
nsecure++;
|
||||
|
@ -820,6 +823,8 @@ _getdns_create_getdns_response(getdns_dns_req *completed_request)
|
|||
else if (completed_request->dnssec_return_only_secure
|
||||
&& netreq->dnssec_status != GETDNS_DNSSEC_SECURE)
|
||||
continue;
|
||||
else if (netreq->tsig_status == GETDNS_DNSSEC_BOGUS)
|
||||
continue;
|
||||
}
|
||||
if (!(reply = _getdns_create_reply_dict(context,
|
||||
netreq, just_addrs, &rrsigs_in_answer)))
|
||||
|
@ -847,7 +852,11 @@ _getdns_create_getdns_response(getdns_dns_req *completed_request)
|
|||
netreq->dnssec_status))
|
||||
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)) {
|
||||
getdns_dict_destroy(reply);
|
||||
goto error;
|
||||
|
|
Loading…
Reference in New Issue