mirror of https://github.com/getdnsapi/getdns.git
getdns_validate_dnssec validate replies in turn
This commit is contained in:
parent
ae580575d0
commit
d47c533b64
181
src/dnssec.c
181
src/dnssec.c
|
@ -1172,6 +1172,10 @@ static int chain_head_validate_with_ta(chain_head *head, getdns_rrset *ta)
|
|||
!= GETDNS_DNSSEC_SECURE)
|
||||
return s;
|
||||
|
||||
debug_sec_print_rrset("Trusted keys to evaluate head: ", &head->rrset);
|
||||
DEBUG_SEC("rrset_has_rrs(&head->rrset) -> %d\n", rrset_has_rrs(&head->rrset));
|
||||
DEBUG_SEC("a_key_signed_rrset(keys, &head->rrset) -> %d\n", a_key_signed_rrset(keys, &head->rrset));
|
||||
DEBUG_SEC("(key_proves_nonexistance(keys, &head->rrset) -> %d\n", key_proves_nonexistance(keys, &head->rrset));
|
||||
if (rrset_has_rrs(&head->rrset)) {
|
||||
if (a_key_signed_rrset(keys, &head->rrset))
|
||||
return GETDNS_DNSSEC_SECURE;
|
||||
|
@ -1214,12 +1218,15 @@ static int chain_head_validate(chain_head *head, rrset_iter *tas)
|
|||
|
||||
static int chain_validate_dnssec(chain_head *chain, rrset_iter *tas)
|
||||
{
|
||||
int s = GETDNS_DNSSEC_INDETERMINATE;
|
||||
int s = GETDNS_DNSSEC_INDETERMINATE, t;
|
||||
chain_head *head;
|
||||
|
||||
/* The netreq status is the worst for any head */
|
||||
for (head = chain; head; head = head->next) {
|
||||
switch (chain_head_validate(head, tas)) {
|
||||
t = chain_head_validate(head, tas);
|
||||
debug_sec_print_rrset("head ", &head->rrset);
|
||||
DEBUG_SEC("1. validated to: %d -> %d\n", t, s);
|
||||
switch (t) {
|
||||
case GETDNS_DNSSEC_SECURE:
|
||||
if (s == GETDNS_DNSSEC_INDETERMINATE)
|
||||
s = GETDNS_DNSSEC_SECURE;
|
||||
|
@ -1237,6 +1244,7 @@ static int chain_validate_dnssec(chain_head *chain, rrset_iter *tas)
|
|||
default:
|
||||
break;
|
||||
}
|
||||
DEBUG_SEC("2. validated to: %d -> %d\n", t, s);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
@ -1868,6 +1876,80 @@ void priv_getdns_get_validation_chain(getdns_dns_req *dnsreq)
|
|||
priv_getdns_call_user_callback(dnsreq,
|
||||
create_getdns_response(dnsreq));
|
||||
}
|
||||
static int wire_validate_dnssec(uint8_t *to_val, size_t to_val_len,
|
||||
uint8_t *support, size_t support_len, uint8_t *tas, size_t tas_len,
|
||||
struct mem_funcs *mf)
|
||||
{
|
||||
chain_head *chain, *head, *next_head;
|
||||
chain_node *node;
|
||||
|
||||
uint8_t qname_spc[256], *qname = NULL;
|
||||
size_t qname_len = sizeof(qname_spc);
|
||||
uint16_t qtype = 0, qclass = GETDNS_RRCLASS_IN;
|
||||
|
||||
priv_getdns_rr_iter rr_spc, *rr;
|
||||
rrset_iter tas_iter;
|
||||
|
||||
int s;
|
||||
|
||||
|
||||
if (to_val_len < GLDNS_HEADER_SIZE)
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
|
||||
#if defined(SEC_DEBUG) && SEC_DEBUG
|
||||
char *str = gldns_wire2str_pkt(to_val, to_val_len);
|
||||
DEBUG_SEC("to validate: %s\n", str);
|
||||
free(str);
|
||||
#endif
|
||||
|
||||
if (GLDNS_RCODE_WIRE(to_val) != GETDNS_RCODE_NOERROR &&
|
||||
GLDNS_RCODE_WIRE(to_val) != GETDNS_RCODE_NXDOMAIN)
|
||||
return GETDNS_DNSSEC_INSECURE;
|
||||
|
||||
if (GLDNS_QDCOUNT(to_val) == 0 && GLDNS_ANCOUNT(to_val) == 0)
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
|
||||
chain = NULL;
|
||||
/* First create a chain (head + nodes) for each rr in the answer and
|
||||
* authority section of the fake to_val packet.
|
||||
*/
|
||||
add_pkt2val_chain(mf, &chain, to_val, to_val_len, NULL);
|
||||
|
||||
/* For each question in the question section add a chain head.
|
||||
*/
|
||||
if ( (rr = priv_getdns_rr_iter_init(&rr_spc, to_val, to_val_len))
|
||||
&& priv_getdns_rr_iter_section(rr) == GLDNS_SECTION_QUESTION
|
||||
&& (qname = priv_getdns_owner_if_or_as_decompressed(
|
||||
rr, qname_spc, &qname_len))
|
||||
&& rr->nxt >= rr->rr_type + 4) {
|
||||
|
||||
qtype = gldns_read_uint16(rr->rr_type);
|
||||
qclass = gldns_read_uint16(rr->rr_type + 2);
|
||||
|
||||
add_question2val_chain(mf, &chain, to_val, to_val_len,
|
||||
qname, qtype, qclass, NULL);
|
||||
}
|
||||
|
||||
/* Now equip the nodes with the support records wireformat */
|
||||
for (head = chain; head; head = head->next) {
|
||||
for (node = head->parent; node; node = node->parent) {
|
||||
|
||||
node->dnskey.pkt = support;
|
||||
node->dnskey.pkt_len = support_len;
|
||||
node->ds.pkt = support;
|
||||
node->ds.pkt_len = support_len;
|
||||
}
|
||||
}
|
||||
s = chain_validate_dnssec(
|
||||
chain, rrset_iter_init(&tas_iter, tas, tas_len));
|
||||
|
||||
/* Cleanup the chain */
|
||||
for (head = chain; head; head = next_head) {
|
||||
next_head = head->next;
|
||||
GETDNS_FREE(*mf, head);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
* getdns_validate_dnssec
|
||||
|
@ -1886,18 +1968,11 @@ getdns_validate_dnssec(getdns_list *records_to_validate,
|
|||
support_len = sizeof(support_buf),
|
||||
tas_len = sizeof(tas_buf);
|
||||
|
||||
getdns_return_t r = GETDNS_RETURN_MEMORY_ERROR;
|
||||
int r = GETDNS_RETURN_MEMORY_ERROR;
|
||||
struct mem_funcs *mf;
|
||||
|
||||
chain_head *chain, *head;
|
||||
chain_node *node;
|
||||
|
||||
uint8_t qname_spc[256], *qname = NULL;
|
||||
size_t qname_len = sizeof(qname_spc);
|
||||
uint16_t qtype = 0, qclass = GETDNS_RRCLASS_IN;
|
||||
|
||||
priv_getdns_rr_iter rr_spc, *rr;
|
||||
rrset_iter tas_iter;
|
||||
size_t i;
|
||||
getdns_dict *reply;
|
||||
|
||||
#if defined(SEC_DEBUG) && SEC_DEBUG
|
||||
fflush(stdout);
|
||||
|
@ -1909,76 +1984,62 @@ getdns_validate_dnssec(getdns_list *records_to_validate,
|
|||
|
||||
/* First convert everything to wire format
|
||||
*/
|
||||
if (!(to_val = _getdns_list2wire(records_to_validate,
|
||||
to_val_buf, &to_val_len, mf)))
|
||||
return GETDNS_RETURN_MEMORY_ERROR;
|
||||
|
||||
if (!(support = _getdns_list2wire(support_records,
|
||||
support_buf, &support_len, mf)))
|
||||
goto exit_free_to_val;
|
||||
return GETDNS_RETURN_MEMORY_ERROR;
|
||||
|
||||
if (!(tas = _getdns_list2wire(trust_anchors,
|
||||
tas_buf, &tas_len, mf)))
|
||||
goto exit_free_support;
|
||||
|
||||
if (GLDNS_QDCOUNT(to_val) == 0 && GLDNS_ANCOUNT(to_val) == 0) {
|
||||
r = GETDNS_RETURN_GENERIC_ERROR;
|
||||
if (!(to_val = _getdns_list2wire(records_to_validate,
|
||||
to_val_buf, &to_val_len, mf)))
|
||||
goto exit_free_tas;
|
||||
}
|
||||
|
||||
chain = NULL;
|
||||
/* First create a chain (head + nodes) for each rr in the answer and
|
||||
* authority section of the fake to_val packet.
|
||||
*/
|
||||
add_pkt2val_chain(mf, &chain, to_val, to_val_len, NULL);
|
||||
if ((r = wire_validate_dnssec(
|
||||
to_val, to_val_len, support, support_len, tas, tas_len, mf)) !=
|
||||
GETDNS_RETURN_GENERIC_ERROR)
|
||||
goto exit_free_to_val;
|
||||
|
||||
/* When records_to_validate contained replies, like the replies_tree
|
||||
* list in a response dict, the returned wireformat packet may contain
|
||||
* multiple questions in the question section. For each reply one
|
||||
* question.
|
||||
*
|
||||
* For each question in the question section add a chain head.
|
||||
*/
|
||||
for ( rr = priv_getdns_rr_iter_init(&rr_spc, to_val, to_val_len)
|
||||
; rr && priv_getdns_rr_iter_section(rr) == GLDNS_SECTION_QUESTION
|
||||
; rr = priv_getdns_rr_iter_next(rr) ) {
|
||||
for (i = 0; !getdns_list_get_dict(records_to_validate,i,&reply); i++) {
|
||||
|
||||
if ((qname = priv_getdns_owner_if_or_as_decompressed(
|
||||
rr, qname_spc, &qname_len))
|
||||
&& rr->nxt >= rr->rr_type + 4) {
|
||||
DEBUG_SEC("REPLY %zu, r: %d\n", i, r);
|
||||
if (to_val != to_val_buf)
|
||||
GETDNS_FREE(*mf, to_val);
|
||||
to_val_len = sizeof(to_val_buf);
|
||||
|
||||
qtype = gldns_read_uint16(rr->rr_type);
|
||||
qclass = gldns_read_uint16(rr->rr_type + 2);
|
||||
if (!(to_val = _getdns_reply2wire(
|
||||
reply, to_val_buf, &to_val_len, mf)))
|
||||
continue;
|
||||
|
||||
add_question2val_chain(mf, &chain, to_val, to_val_len,
|
||||
qname, qtype, qclass, NULL);
|
||||
switch (wire_validate_dnssec(
|
||||
to_val, to_val_len, support, support_len, tas, tas_len, mf)) {
|
||||
case GETDNS_DNSSEC_SECURE:
|
||||
if (r == GETDNS_RETURN_GENERIC_ERROR)
|
||||
r = GETDNS_DNSSEC_SECURE;
|
||||
break;
|
||||
case GETDNS_DNSSEC_INSECURE:
|
||||
if (r != GETDNS_DNSSEC_BOGUS)
|
||||
r = GETDNS_DNSSEC_INSECURE;
|
||||
break;
|
||||
case GETDNS_DNSSEC_BOGUS:
|
||||
r = GETDNS_DNSSEC_BOGUS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
DEBUG_SEC("REPLY %zu, r: %d\n", i, r);
|
||||
|
||||
/* Now equip the nodes with the support records wireformat */
|
||||
for (head = chain; head; head = head->next) {
|
||||
for (node = head->parent; node; node = node->parent) {
|
||||
|
||||
node->dnskey.pkt = support;
|
||||
node->dnskey.pkt_len = support_len;
|
||||
node->ds.pkt = support;
|
||||
node->ds.pkt_len = support_len;
|
||||
}
|
||||
}
|
||||
/* Now equip the nodes with the support records wireformat */
|
||||
|
||||
r = (getdns_return_t)chain_validate_dnssec(
|
||||
chain, rrset_iter_init(&tas_iter, tas, tas_len));
|
||||
|
||||
exit_free_to_val:
|
||||
if (to_val != to_val_buf)
|
||||
GETDNS_FREE(*mf, to_val);
|
||||
exit_free_tas:
|
||||
if (tas != tas_buf)
|
||||
GETDNS_FREE(*mf, tas);
|
||||
exit_free_support:
|
||||
if (support != support_buf)
|
||||
GETDNS_FREE(*mf, support);
|
||||
exit_free_to_val:
|
||||
if (to_val != to_val_buf)
|
||||
GETDNS_FREE(*mf, to_val);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -990,13 +990,66 @@ priv_getdns_validate_dname(const char* dname) {
|
|||
} /* priv_getdns_validate_dname */
|
||||
|
||||
|
||||
static void _getdns_reply2wire_buf(gldns_buffer *buf, getdns_dict *reply)
|
||||
{
|
||||
getdns_dict *rr_dict, *q_dict, *h_dict;
|
||||
getdns_list *section;
|
||||
size_t i, pkt_start, ancount, nscount;
|
||||
uint32_t qtype, qclass = GETDNS_RRCLASS_IN, rcode = GETDNS_RCODE_NOERROR;
|
||||
getdns_bindata *qname;
|
||||
|
||||
|
||||
pkt_start = gldns_buffer_position(buf);
|
||||
/* Empty header */
|
||||
gldns_buffer_write_u32(buf, 0);
|
||||
gldns_buffer_write_u32(buf, 0);
|
||||
gldns_buffer_write_u32(buf, 0);
|
||||
|
||||
if ( !getdns_dict_get_dict(reply, "question", &q_dict)
|
||||
&& !getdns_dict_get_int(q_dict, "qtype", &qtype)
|
||||
&& !getdns_dict_get_bindata(q_dict, "qname", &qname)) {
|
||||
|
||||
gldns_buffer_write(buf, qname->data, qname->size);
|
||||
gldns_buffer_write_u16(buf, (uint16_t)qtype);
|
||||
gldns_buffer_write_u16(buf, (uint16_t)qclass);
|
||||
gldns_buffer_write_u16_at(
|
||||
buf, pkt_start + GLDNS_QDCOUNT_OFF, 1);
|
||||
}
|
||||
|
||||
if ( !getdns_dict_get_dict(reply, "header", &h_dict)
|
||||
&& !getdns_dict_get_int(h_dict, "rcode", &rcode)) {
|
||||
|
||||
GLDNS_RCODE_SET(gldns_buffer_at(buf, pkt_start), rcode);
|
||||
}
|
||||
if (!getdns_dict_get_list(reply, "answer", §ion)) {
|
||||
for ( i = 0, ancount = 0
|
||||
; !getdns_list_get_dict(section, i, &rr_dict)
|
||||
; i++ ) {
|
||||
|
||||
if (!priv_getdns_rr_dict2wire(rr_dict, buf))
|
||||
ancount++;
|
||||
}
|
||||
gldns_buffer_write_u16_at(
|
||||
buf, pkt_start + GLDNS_ANCOUNT_OFF, ancount);
|
||||
}
|
||||
if (!getdns_dict_get_list(reply, "authority", §ion)) {
|
||||
for ( i = 0, nscount = 0
|
||||
; !getdns_list_get_dict(section, i, &rr_dict)
|
||||
; i++ ) {
|
||||
|
||||
if (!priv_getdns_rr_dict2wire(rr_dict, buf))
|
||||
nscount++;
|
||||
}
|
||||
gldns_buffer_write_u16_at(
|
||||
buf, pkt_start + GLDNS_NSCOUNT_OFF, nscount);
|
||||
}
|
||||
}
|
||||
|
||||
static void _getdns_list2wire_buf(gldns_buffer *buf, getdns_list *l)
|
||||
{
|
||||
getdns_dict *rr_dict, *q_dict;
|
||||
getdns_list *section;
|
||||
getdns_return_t r;
|
||||
size_t i, j, pkt_start, ancount, qdcount;
|
||||
uint32_t qtype, qclass;
|
||||
getdns_dict *rr_dict;
|
||||
size_t i, pkt_start, ancount;
|
||||
uint32_t qtype, qclass = GETDNS_RRCLASS_IN;
|
||||
getdns_bindata *qname;
|
||||
|
||||
pkt_start = gldns_buffer_position(buf);
|
||||
|
@ -1005,25 +1058,10 @@ static void _getdns_list2wire_buf(gldns_buffer *buf, getdns_list *l)
|
|||
gldns_buffer_write_u32(buf, 0);
|
||||
gldns_buffer_write_u32(buf, 0);
|
||||
|
||||
for ( i = 0, qdcount = 0
|
||||
; (r = getdns_list_get_dict(l, i, &rr_dict))
|
||||
!= GETDNS_RETURN_NO_SUCH_LIST_ITEM
|
||||
for ( i = 0
|
||||
; !getdns_list_get_dict(l, i, &rr_dict)
|
||||
; i++ ) {
|
||||
|
||||
if (r) {
|
||||
if (r == GETDNS_RETURN_WRONG_TYPE_REQUESTED)
|
||||
continue;
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (getdns_dict_get_dict(rr_dict, "question", &q_dict)
|
||||
== GETDNS_RETURN_GOOD) {
|
||||
|
||||
/* rr_dict was actually a reply
|
||||
* with a question section/rr_dict
|
||||
*/
|
||||
rr_dict = q_dict;
|
||||
}
|
||||
if (getdns_dict_get_int(rr_dict, "qtype", &qtype) ||
|
||||
getdns_dict_get_bindata(rr_dict, "qname", &qname))
|
||||
continue;
|
||||
|
@ -1031,68 +1069,15 @@ static void _getdns_list2wire_buf(gldns_buffer *buf, getdns_list *l)
|
|||
gldns_buffer_write(buf, qname->data, qname->size);
|
||||
gldns_buffer_write_u16(buf, (uint16_t)qtype);
|
||||
gldns_buffer_write_u16(buf, (uint16_t)qclass);
|
||||
qdcount++;
|
||||
gldns_buffer_write_u16_at(buf, pkt_start+GLDNS_QDCOUNT_OFF, 1);
|
||||
break;
|
||||
}
|
||||
gldns_buffer_write_u16_at(buf, pkt_start+GLDNS_QDCOUNT_OFF, qdcount);
|
||||
for ( i = 0, ancount = 0
|
||||
; (r = getdns_list_get_dict(l, i, &rr_dict))
|
||||
!= GETDNS_RETURN_NO_SUCH_LIST_ITEM
|
||||
; !getdns_list_get_dict(l, i, &rr_dict)
|
||||
; i++ ) {
|
||||
|
||||
if (r) {
|
||||
if (r == GETDNS_RETURN_WRONG_TYPE_REQUESTED)
|
||||
continue;
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (priv_getdns_rr_dict2wire(rr_dict, buf)
|
||||
== GETDNS_RETURN_GOOD) {
|
||||
|
||||
if (!priv_getdns_rr_dict2wire(rr_dict, buf))
|
||||
ancount++;
|
||||
continue;
|
||||
}
|
||||
if (getdns_dict_get_list(rr_dict, "answer", §ion)
|
||||
== GETDNS_RETURN_GOOD) {
|
||||
|
||||
for ( j = 0
|
||||
; (r = getdns_list_get_dict(section, j, &q_dict))
|
||||
!= GETDNS_RETURN_NO_SUCH_LIST_ITEM
|
||||
; j++ ) {
|
||||
|
||||
if (r) {
|
||||
if (r ==
|
||||
GETDNS_RETURN_WRONG_TYPE_REQUESTED)
|
||||
continue;
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (priv_getdns_rr_dict2wire(q_dict, buf)
|
||||
== GETDNS_RETURN_GOOD)
|
||||
|
||||
ancount++;
|
||||
}
|
||||
}
|
||||
if (getdns_dict_get_list(rr_dict, "authority", §ion)
|
||||
== GETDNS_RETURN_GOOD) {
|
||||
|
||||
for ( j = 0
|
||||
; (r = getdns_list_get_dict(section, j, &q_dict))
|
||||
!= GETDNS_RETURN_NO_SUCH_LIST_ITEM
|
||||
; j++ ) {
|
||||
|
||||
if (r) {
|
||||
if (r ==
|
||||
GETDNS_RETURN_WRONG_TYPE_REQUESTED)
|
||||
continue;
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (priv_getdns_rr_dict2wire(q_dict, buf)
|
||||
== GETDNS_RETURN_GOOD)
|
||||
|
||||
ancount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
gldns_buffer_write_u16_at(buf, pkt_start+GLDNS_ANCOUNT_OFF, ancount);
|
||||
}
|
||||
|
@ -1106,9 +1091,10 @@ uint8_t *_getdns_list2wire(
|
|||
gldns_buffer_init_frm_data(&gbuf, buf, *buf_len);
|
||||
_getdns_list2wire_buf(&gbuf, l);
|
||||
|
||||
if ((sz = gldns_buffer_position(&gbuf)) <= *buf_len)
|
||||
if ((sz = gldns_buffer_position(&gbuf)) <= *buf_len) {
|
||||
*buf_len = sz;
|
||||
return buf;
|
||||
|
||||
}
|
||||
if (!(buf = GETDNS_XMALLOC(*mf, uint8_t, (*buf_len = sz))))
|
||||
return NULL;
|
||||
|
||||
|
@ -1117,6 +1103,27 @@ uint8_t *_getdns_list2wire(
|
|||
return buf;
|
||||
}
|
||||
|
||||
uint8_t *_getdns_reply2wire(
|
||||
getdns_dict *r, uint8_t *buf, size_t *buf_len, struct mem_funcs *mf)
|
||||
{
|
||||
gldns_buffer gbuf;
|
||||
size_t sz;
|
||||
|
||||
gldns_buffer_init_frm_data(&gbuf, buf, *buf_len);
|
||||
_getdns_reply2wire_buf(&gbuf, r);
|
||||
|
||||
if ((sz = gldns_buffer_position(&gbuf)) <= *buf_len) {
|
||||
*buf_len = sz;
|
||||
return buf;
|
||||
}
|
||||
if (!(buf = GETDNS_XMALLOC(*mf, uint8_t, (*buf_len = sz))))
|
||||
return NULL;
|
||||
|
||||
gldns_buffer_init_frm_data(&gbuf, buf, sz);
|
||||
_getdns_reply2wire_buf(&gbuf, r);
|
||||
return buf;
|
||||
}
|
||||
|
||||
void _getdns_wire2list(uint8_t *pkt, size_t pkt_len, getdns_list *l)
|
||||
{
|
||||
priv_getdns_rr_iter rr_spc, *rr;
|
||||
|
|
|
@ -135,6 +135,9 @@ int priv_getdns_dname_equal(const uint8_t *s1, const uint8_t *s2);
|
|||
uint8_t *_getdns_list2wire(
|
||||
getdns_list *l, uint8_t *buf, size_t *buf_len, struct mem_funcs *mf);
|
||||
|
||||
uint8_t *_getdns_reply2wire(
|
||||
getdns_dict *r, uint8_t *buf, size_t *buf_len, struct mem_funcs *mf);
|
||||
|
||||
void _getdns_wire2list(uint8_t *pkt, size_t pkt_len, getdns_list *l);
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue