#include <libknot/descriptor.h>
#include <libknot/rrtype/rdname.h>
+#include <libknot/rrtype/rrsig.h>
#include "lib/layer/iterate.h"
#include "lib/resolve.h"
return state;
}
+static const knot_dname_t *signature_authority(knot_pkt_t *pkt)
+{
+ /* Can't find signer for RRSIGs, bail out. */
+ if (knot_pkt_qtype(pkt) == KNOT_RRTYPE_RRSIG) {
+ return NULL;
+ }
+ for (knot_section_t i = KNOT_ANSWER; i <= KNOT_AUTHORITY; ++i) {
+ const knot_pktsection_t *sec = knot_pkt_section(pkt, i);
+ for (unsigned k = 0; k < sec->count; ++k) {
+ const knot_rrset_t *rr = knot_pkt_rr(sec, k);
+ if (rr->type == KNOT_RRTYPE_RRSIG) {
+ return knot_rrsig_signer_name(&rr->rrs, 0);
+ }
+ }
+ }
+ return NULL;
+}
+
static int process_authority(knot_pkt_t *pkt, struct kr_request *req)
{
int result = KNOT_STATE_CONSUME;
case KNOT_STATE_FAIL: return state; break;
default: /* continue */ break;
}
- } else if (rr->type == KNOT_RRTYPE_SOA) {
+ } else if (rr->type == KNOT_RRTYPE_SOA && knot_dname_is_sub(rr->owner, qry->zone_cut.name)) {
/* SOA below cut in authority indicates different authority, but same NS set. */
- if (knot_dname_is_sub(rr->owner, qry->zone_cut.name)) {
- qry->zone_cut.name = knot_dname_copy(rr->owner, &req->pool);
- if (qry->flags & QUERY_DNSSEC_WANT) { /* Treat as a referral */
- result = KNOT_STATE_DONE;
- break;
- }
- }
+ qry->zone_cut.name = knot_dname_copy(rr->owner, &req->pool);
}
}
+ /* Track difference between current TA and signer name.
+ * This indicates that the NS is auth for both parent-child, and we must update DS/DNSKEY to validate it.
+ * @todo: This has to be checked here before we put the data into packet, there is no "DEFER" or "PAUSE" action yet.
+ */
+ const bool track_pc_change = (!(qry->flags & QUERY_CACHED) && (qry->flags & QUERY_DNSSEC_WANT));
+ const knot_dname_t *ta_name = qry->zone_cut.trust_anchor ? qry->zone_cut.trust_anchor->owner : NULL;
+ const knot_dname_t *signer = signature_authority(pkt);
+ if (track_pc_change && ta_name && signer && !knot_dname_is_equal(ta_name, signer)) {
+ DEBUG_MSG(">< cut changed, needs revalidation\n");
+ if (knot_dname_is_sub(signer, qry->zone_cut.name)) {
+ qry->zone_cut.name = knot_dname_copy(signer, &req->pool);
+ } else if (!knot_dname_is_equal(signer, qry->zone_cut.name)) {
+ /* Key signer is above the current cut, so we can't validate it. This happens when
+ a server is authoritative for both grandparent, parent and child zone.
+ Ascend to parent cut, and refetch authority for signer. */
+ if (qry->zone_cut.parent) {
+ memcpy(&qry->zone_cut, qry->zone_cut.parent, sizeof(qry->zone_cut));
+ } else {
+ qry->flags |= QUERY_AWAIT_CUT;
+ }
+ qry->zone_cut.name = knot_dname_copy(signer, &req->pool);
+ } /* else zone cut matches, but DS/DNSKEY doesn't => refetch. */
+ knot_wire_set_tc(pkt->wire);
+ result = KNOT_STATE_NOOP;
+ }
+
/* CONSUME => Unhelpful referral.
- * DONE => Zone cut updated. */
+ * DONE => Zone cut updated.
+ * NOOP => Ignore this answer. */
return result;
}
case KNOT_STATE_DONE: /* Referral */
DEBUG_MSG("<= referral response, follow\n");
break;
+ case KNOT_STATE_NOOP: /* Deferred, bail out. */
+ state = KNOT_STATE_CONSUME;
+ break;
default:
break;
}
updated_key = true;
}
}
- if (!qry->zone_cut.key) {
- return kr_error(EBADMSG);
- }
/* Check if there's a key for current TA. */
if (updated_key && !(qry->flags & QUERY_CACHED)) {
return kr_ok();
}
-static const knot_dname_t *section_first_signer_name(knot_pkt_t *pkt, knot_section_t section_id)
-{
- const knot_dname_t *sname = NULL;
- const knot_pktsection_t *sec = knot_pkt_section(pkt, section_id);
- if (!sec) {
- return sname;
- }
-
- for (unsigned i = 0; i < sec->count; ++i) {
- const knot_rrset_t *rr = knot_pkt_rr(sec, i);
- if (rr->type != KNOT_RRTYPE_RRSIG) {
- continue;
- }
-
- sname = knot_rrsig_signer_name(&rr->rrs, 0);
- break;
- }
-
- return sname;
-}
-
-static const knot_dname_t *first_rrsig_signer_name(knot_pkt_t *answer)
-{
- const knot_dname_t *ans_sname = section_first_signer_name(answer, KNOT_ANSWER);
- const knot_dname_t *auth_sname = section_first_signer_name(answer, KNOT_AUTHORITY);
-
- if (!ans_sname) {
- return auth_sname;
- } else if (!auth_sname) {
- return ans_sname;
- } else if (knot_dname_is_equal(ans_sname, auth_sname)) {
- return ans_sname;
- } else {
- return NULL;
- }
-}
-
static knot_rrset_t *update_ds(struct kr_zonecut *cut, const knot_pktsection_t *sec)
{
/* Aggregate DS records (if using multiple keys) */
}
/* Answer for RRSIG may not set DO=1, but all records MUST still validate. */
bool use_signatures = (knot_pkt_qtype(pkt) != KNOT_RRTYPE_RRSIG);
- /* @todo do not cache RRSIG answers until RFC2181 credibility is implemented */
- if (!use_signatures) {
- knot_wire_set_rcode(pkt->wire, KNOT_RCODE_SERVFAIL); /* Prevent caching */
- }
if (!(qry->flags & QUERY_CACHED) && !knot_pkt_has_dnssec(pkt) && !use_signatures) {
DEBUG_MSG(qry, "<= got insecure response\n");
qry->flags |= QUERY_DNSSEC_BOGUS;
return KNOT_STATE_FAIL;
}
- /* Check if we descended to a NS, which is authoritative for both parent-child, this means that
- * the signatures are made using a different key than we have.
- * Although we can't "pause" the response procesing and fetch the keys, we can
- * say "do not cache this answer, and try again". This way, the resolver will realise
- * that the keys are missing and will schedule a subrequest before retrying.
- */
- const knot_dname_t *key_own = qry->zone_cut.trust_anchor ? qry->zone_cut.trust_anchor->owner : NULL;
- const knot_dname_t *sig_name = first_rrsig_signer_name(pkt);
- if (use_signatures && sig_name && key_own && !knot_dname_is_equal(key_own, sig_name)) {
- DEBUG_MSG(qry, ">< cut changed, needs revalidation\n");
- if (knot_dname_is_sub(sig_name, qry->zone_cut.name)) {
- qry->zone_cut.name = knot_dname_copy(sig_name, &req->pool);
- } else if (!knot_dname_is_equal(sig_name, qry->zone_cut.name)) {
- /* Key signer is above the current cut, so we can't validate it. This happens when
- a server is authoritative for both grandparent, parent and child zone.
- Ascend to parent cut, and refetch authority for signer. */
- if (qry->zone_cut.parent) {
- memcpy(&qry->zone_cut, qry->zone_cut.parent, sizeof(qry->zone_cut));
- } else {
- qry->flags |= QUERY_AWAIT_CUT;
- }
- qry->zone_cut.name = knot_dname_copy(sig_name, &req->pool);
- } /* else zone cut matches, but DS/DNSKEY doesn't => refetch. */
- knot_wire_set_rcode(pkt->wire, KNOT_RCODE_SERVFAIL); /* Prevent caching */
- qry->flags &= ~QUERY_RESOLVED;
- return KNOT_STATE_CONSUME;
- }
-
/* Check if this is a DNSKEY answer, check trust chain and store. */
uint8_t pkt_rcode = knot_wire_get_rcode(pkt->wire);
uint16_t qtype = knot_pkt_qtype(pkt);