#include <libknot/descriptor.h>
#include <libknot/rrtype/rdname.h>
+#include <libknot/rrtype/dnskey.h>
#include "lib/layer/iterate.h"
#include "lib/resolve.h"
#include "lib/nsrep.h"
#include "lib/module.h"
-#define DEBUG_MSG(fmt...) QRDEBUG(kr_rplan_current(&req->rplan), "vldr", fmt)
+#define DEBUG_MSG(fmt...) QRDEBUG(qry, "vldr", fmt)
/* Set resolution context and parameters. */
static int begin(knot_layer_t *ctx, void *module_param)
return KNOT_STATE_PRODUCE;
}
+#if 0
static int secure_query(knot_layer_t *ctx, knot_pkt_t *pkt)
{
assert(pkt && ctx);
#endif
return ctx->state;
}
+#endif
+
+static int validate_records(struct kr_query *qry, knot_pkt_t *answer)
+{
+#warning TODO: validate RRSIGS (records with ZSK, keys with KSK), return FAIL if failed
+ if (!qry->zone_cut.key) {
+ DEBUG_MSG("<= no DNSKEY, can't validate\n");
+ }
+
+ DEBUG_MSG("!! validation not implemented\n");
+ return kr_error(ENOSYS);
+}
+
+static int validate_proof(struct kr_query *qry, knot_pkt_t *answer)
+{
+#warning TODO: validate NSECx proof, RRSIGs will be checked later if it matches
+ return kr_ok();
+}
+
+static int validate_keyset(struct kr_query *qry, knot_pkt_t *answer)
+{
+ /* Merge DNSKEY records from answer */
+ const knot_pktsection_t *an = knot_pkt_section(answer, KNOT_ANSWER);
+ for (unsigned i = 0; i < an->count; ++i) {
+ const knot_rrset_t *rr = knot_pkt_rr(an, i);
+ if (rr->type == KNOT_RRTYPE_DNSKEY) {
+ DEBUG_MSG("+= DNSKEY flags: %hu algo: %x\n",
+ knot_dnskey_flags(&rr->rrs, 0),
+ 0xff & knot_dnskey_alg(&rr->rrs, 0));
+#warning TODO: merge with zone cut 'key' RRSet
+ }
+ }
+ /* Check if there's a key for current TA. */
+#warning TODO: check if there is a DNSKEY we can trust (matching current TA)
+ return kr_ok();
+}
+
+static int update_delegation(struct kr_query *qry, knot_pkt_t *answer)
+{
+ DEBUG_MSG("<= referral, checking DS\n");
+#warning TODO: delegation, check DS record presence
+ return kr_ok();
+}
static int validate(knot_layer_t *ctx, knot_pkt_t *pkt)
{
+ int ret = 0;
struct kr_request *req = ctx->data;
- struct kr_query *query = kr_rplan_current(&req->rplan);
+ struct kr_query *qry = kr_rplan_current(&req->rplan);
if (ctx->state & KNOT_STATE_FAIL) {
return ctx->state;
}
-#warning TODO: check if we have DNSKEY in qry->zone_cut and validate RRSIGS/proof, return FAIL if failed
-#warning TODO: we must also validate incoming DNSKEY records against the current zone cut TA
-#warning FLOW: first answer that comes here must have the DNSKEY that we can validate using TA
- DEBUG_MSG("checking query, dnskey: %d, secured: %d\n",
- knot_pkt_qtype(pkt) == KNOT_RRTYPE_DNSKEY,
- knot_pkt_has_dnssec(pkt));
+
+ /* Pass-through if user doesn't want secure answer. */
+ if (!(req->flags & KR_REQ_DNSSEC)) {
+ return ctx->state;
+ }
+
+ /* Server didn't copy back DO=1, this is okay if it doesn't have DS => insecure.
+ * If it has DS, it must be secured, fail it as bogus. */
+ if (!knot_pkt_has_dnssec(pkt)) {
+ DEBUG_MSG("<= asked with DO=1, got insecure response\n");
+#warning TODO: fail and retry if it has TA, otherwise flag as INSECURE and continue
+ return KNOT_STATE_FAIL;
+ }
+
+ /* Validate non-existence proof if not positive answer. */
+ if (knot_wire_get_rcode(pkt->wire) == KNOT_RCODE_NXDOMAIN) {
+ ret = validate_proof(qry, pkt);
+ if (ret != 0) {
+ DEBUG_MSG("<= bad NXDOMAIN proof\n");
+ qry->flags |= QUERY_DNSSEC_BOGUS;
+ return KNOT_STATE_FAIL;
+ }
+ }
+
+ /* Check if this is a DNSKEY answer, check trust chain and store. */
+ uint16_t qtype = knot_pkt_qtype(pkt);
+ if (qtype == KNOT_RRTYPE_DNSKEY) {
+ ret = validate_keyset(qry, pkt);
+ if (ret != 0) {
+ DEBUG_MSG("<= bad keys, broken trust chain\n");
+ qry->flags |= QUERY_DNSSEC_BOGUS;
+ return KNOT_STATE_FAIL;
+ }
+ /* Update trust anchor. */
+ } else if (qtype == KNOT_RRTYPE_NS) {
+ update_delegation(qry, pkt);
+ }
+
+ /* Validate all records, fail as bogus if it doesn't match. */
+ ret = validate_records(qry, pkt);
+ if (ret != 0) {
+ DEBUG_MSG("<= couldn't validate RRSIGs\n");
+ qry->flags |= QUERY_DNSSEC_BOGUS;
+ return KNOT_STATE_FAIL;
+ }
+
+ DEBUG_MSG("<= answer valid, OK\n");
return ctx->state;
}
if (state == KNOT_STATE_FAIL) {
kr_nsrep_update_rtt(&qry->ns, KR_NS_TIMEOUT, ctx->cache_rtt);
invalidate_ns(rplan, qry);
+ qry->flags &= ~QUERY_RESOLVED;
/* Track RTT for iterative answers */
} else if (!(qry->flags & QUERY_CACHED)) {
struct timeval now;
}
knot_overlay_reset(&request->overlay);
+
+ /* Do not finish with bogus answer. */
+ if (qry->flags & QUERY_DNSSEC_BOGUS) {
+ return KNOT_STATE_FAIL;
+ }
return kr_rplan_empty(&request->rplan) ? KNOT_STATE_DONE : KNOT_STATE_PRODUCE;
}
}
/* Try to fetch missing DNSKEY. */
if (want_secured && !qry->zone_cut.key && qry->stype != KNOT_RRTYPE_DNSKEY) {
- /* TODO -- Fetch all missing DNSKEYS and DS records. */
- /* TODO -- Fetch DS at parent side of a zone cut. Fetch NS at the child side of the zone cut. */
- /* TODO -- Handle holes (sequences with missing delegation). */
struct kr_query *next = kr_rplan_push(rplan, qry, qry->zone_cut.name, KNOT_CLASS_IN, KNOT_RRTYPE_DNSKEY);
if (!next) {
return kr_error(ENOMEM);
}
+ next->flags |= QUERY_AWAIT_CUT;
return KNOT_STATE_PRODUCE;
}
/* Update minimized QNAME if zone cut changed */
DEBUG_MSG("finished: %d, mempool: %zu B\n", state, (size_t) mp_total_size(request->pool.ctx));
#endif
/* Finalize answer */
- if (answer_finalize(request->answer) != 0) {
+ if (answer_finalize(request, state) != 0) {
state = KNOT_STATE_FAIL;
}
/* Error during procesing, internal failure */
#include "lib/nsrep.h"
#define QUERY_FLAGS(X) \
- X(NO_MINIMIZE, 1 << 0) /**< Don't minimize QNAME. */ \
- X(NO_THROTTLE, 1 << 1) /**< No query/slow NS throttling. */ \
- X(TCP , 1 << 2) /**< Use TCP for this query. */ \
- X(RESOLVED , 1 << 3) /**< Query is resolved. */ \
- X(AWAIT_IPV4 , 1 << 4) /**< Query is waiting for A address. */ \
- X(AWAIT_IPV6 , 1 << 5) /**< Query is waiting for AAAA address. */ \
- X(AWAIT_CUT , 1 << 6) /**< Query is waiting for zone cut lookup */ \
- X(SAFEMODE , 1 << 7) /**< Don't use fancy stuff (EDNS...) */ \
- X(CACHED , 1 << 8) /**< Query response is cached. */ \
- X(EXPIRING , 1 << 9) /**< Query response is cached, but expiring. */
+ X(NO_MINIMIZE , 1 << 0) /**< Don't minimize QNAME. */ \
+ X(NO_THROTTLE , 1 << 1) /**< No query/slow NS throttling. */ \
+ X(TCP , 1 << 2) /**< Use TCP for this query. */ \
+ X(RESOLVED , 1 << 3) /**< Query is resolved. */ \
+ X(AWAIT_IPV4 , 1 << 4) /**< Query is waiting for A address. */ \
+ X(AWAIT_IPV6 , 1 << 5) /**< Query is waiting for AAAA address. */ \
+ X(AWAIT_CUT , 1 << 6) /**< Query is waiting for zone cut lookup */ \
+ X(SAFEMODE , 1 << 7) /**< Don't use fancy stuff (EDNS...) */ \
+ X(CACHED , 1 << 8) /**< Query response is cached. */ \
+ X(EXPIRING , 1 << 9) /**< Query response is cached, but expiring. */ \
+ X(DNSSEC_BOGUS , 1 << 10) /**< Query response is DNSSEC bogus. */ \
/** Query flags */
enum kr_query_flag {
}
}
+#warning TODO: set root trust anchor from config, or hardcode for now
return kr_ok();
}
/* Start at QNAME parent. */
while (txn) {
+#warning TODO: find closest trust anchor here, then find NS
bool has_key = !secured || fetch_dnskey(ctx, cut, name, txn, timestamp) == 0;
if (has_key && fetch_ns(ctx, cut, name, txn, timestamp) == 0) {
update_cut_name(cut, name);
mm_ctx_t *pool; /**< Memory pool. */
map_t nsset; /**< Map of nameserver => address_set. */
knot_rrset_t* key; /**< Zone cut DNSKEY. */
+ knot_rrset_t* trust_anchor; /**< Current trust anchor. */
};
/**
/* Load basic modules. */
array_init(global_modules);
- const char *load_modules[3] = {"iterate", "rrcache", "pktcache"};
- for (unsigned i = 0; i < 3; ++i) {
+ const char *load_modules[4] = {"iterate", "validate", "rrcache", "pktcache" };
+ for (unsigned i = 0; i < 4; ++i) {
struct kr_module *mod = malloc(sizeof(*mod));
kr_module_load(mod, load_modules[i], NULL);
array_push(global_modules, mod);
--- /dev/null
+; config options
+server:
+ trust-anchor: ". 3600 IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5"
+ val-override-date: "1436477112"
+
+stub-zone:
+ name: "."
+ stub-addr: 198.41.0.4 # a.root-servers.net.
+CONFIG_END
+
+SCENARIO_BEGIN Test basic validation of NS cz. (two levels)
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 198.41.0.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. 518400 IN NS a.root-servers.net.
+. 518400 IN NS b.root-servers.net.
+. 518400 IN NS c.root-servers.net.
+. 518400 IN NS d.root-servers.net.
+. 518400 IN NS e.root-servers.net.
+. 518400 IN NS f.root-servers.net.
+. 518400 IN NS g.root-servers.net.
+. 518400 IN NS h.root-servers.net.
+. 518400 IN NS i.root-servers.net.
+. 518400 IN NS j.root-servers.net.
+. 518400 IN NS k.root-servers.net.
+. 518400 IN NS l.root-servers.net.
+. 518400 IN NS m.root-servers.net.
+. 518400 IN RRSIG NS 8 0 518400 20150719170000 20150709160000 1518 . qoRtgQy1XMDlvBufwjClMyMJRXlcHEl7+Z9mn8BRqZJiAmpYbk+Ku1Z2 omfnUsHX9fLhyLuIRS/FKN4/WPVmcVOxZ09EgZ9hFBH/pn2LOlhQ6abP OIrpy9slr/i9DGZ2YXirKIUWUKUfpcLv0O+g7DZZcqnmhGCikNtXgJ76 njw=
+SECTION ADDITIONAL
+a.root-servers.net. 518400 IN A 198.41.0.4
+ENTRY_END
+
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+. IN DNSKEY
+SECTION ANSWER
+. 172800 IN DNSKEY 256 3 8 AwEAAZyIkCwEYeG29NV+4cOdKE4DPng/4BqJeoOhKqzJbl+LR33TPWsr wBRfmAi9wvR/Qc6IV4MFMXjmkclXns+atIQZ9uQV3YAvKv/cVuO7Mneu MssIQixaMw+jp73R7zIUNMbLBgJRQXI57Rl+pvXBAkgHndVwv+aJkf7y GEuE9Dtj
+. 172800 IN DNSKEY 256 3 8 AwEAAa67bQck1JjopOOFc+iMISFcp/osWrEst2wbKbuQSUWu77QC9UHL ipiHgWN7JlqVAEjKITZz49hhkLmOpmLK55pTq+RD2kwoyNWk9cvpc+tS nIxT7i93O+3oVeLYjMWrkDAz7K45rObbHDuSBwYZKrcSIUCZnCpNMUtn PFl/04cb
+. 172800 IN DNSKEY 257 3 8 AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjF FVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoX bfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaD X6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpz W5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relS Qageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulq QxA+Uk1ihz0=
+. 172800 IN RRSIG DNSKEY 8 0 172800 20150715235959 20150701000000 19036 . PCLEqe8X433LWIWcrP5jC3Ejjws7XST8CFpiccRKXuB9YGMi3AngOXf4 4wiXG1WXLNb+5Rj/na6/4znyTd3sb21T9syHol9kaVMSXzIGg07yZ0hk 62BVdwKOPphtivSwYYjZYxXXXMR/X1MZNwD6a63Tz2Q+zMnS897CTo3M gMsU8qUUwPAqS52rP82Bf0L3Uhr7KWwfHrfH9CN4kAxi8ha2EWSaHNCo jTHUXnaGDDOtUQUSvrgI8vlBRjncr45ktmUh7a8OF2AxoPlfd/FTp6Dy 1f3B90G8fGml0LU/dQVOr3PWMRmmELhY/QQpL+FptAnxIVPeg7jJsZ9A P3OrmA==
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+cz. IN NS
+SECTION AUTHORITY
+cz. 172800 IN NS a.ns.nic.cz.
+cz. 86400 IN DS 54576 10 2 397E50C85EDE9CDE33F363A9E66FD1B216D788F8DD438A57A423A386 869C8F06
+cz. 86400 IN RRSIG DS 8 1 86400 20150719170000 20150709160000 1518 . nzjFR1npBJ8enQ4jXm9DZ8S8FheWzMps9xourERlhaal8buDVwyGnWWW wy9z3d+a3hXtYCL+rOJLm9tz+GVRQuZRk6Yp//5ckTGn0HwymIIXw3nU LEfGafsgBIQZiW7eyMq/zwZMjOSQ9KRMomjh1clUxQsuxUrkw/mJMMZI 0R0=
+SECTION ADDITIONAL
+a.ns.nic.cz. 172800 IN A 194.0.12.1
+ENTRY_END
+RANGE_END
+
+; a.ns.nic.cz.
+RANGE_BEGIN 0 100
+ ADDRESS 194.0.12.1
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA NOERROR
+SECTION QUESTION
+cz. IN NS
+SECTION ANSWER
+cz. 18000 IN NS a.ns.nic.cz.
+cz. 18000 IN NS b.ns.nic.cz.
+cz. 18000 IN NS c.ns.nic.cz.
+cz. 18000 IN NS d.ns.nic.cz.
+cz. 18000 IN RRSIG NS 10 1 18000 20150722014826 20150708113847 39788 cz. fXbYxeKOypz1mouiC3PTYSUv16rGy93f1GNRwIvNkJQHrDAXImLX6JoS aESL9cm671WJ4d8MgjrCcdaeILyFPnsb2zoSn96sreABtk3zZz54xk23 dzQmXIMSwXDHIpfdF2Adsh+JOblOYuLrgepyG59IRKdPS0UVyDUlIfia slQ=
+SECTION ADDITIONAL
+a.ns.nic.cz. 18000 IN A 194.0.12.1
+b.ns.nic.cz. 18000 IN A 194.0.12.1
+c.ns.nic.cz. 18000 IN A 194.0.12.1
+d.ns.nic.cz. 18000 IN A 194.0.12.1
+ENTRY_END
+
+RANGE_END
+
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+cz. IN NS
+ENTRY_END
+
+; recursion happens here.
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AD NOERROR
+SECTION QUESTION
+cz. IN NS
+SECTION ANSWER
+cz. 18000 IN NS a.ns.nic.cz.
+cz. 18000 IN NS b.ns.nic.cz.
+cz. 18000 IN NS c.ns.nic.cz.
+cz. 18000 IN NS d.ns.nic.cz.
+cz. 18000 IN RRSIG NS 10 1 18000 20150722014826 20150708113847 39788 cz. fXbYxeKOypz1mouiC3PTYSUv16rGy93f1GNRwIvNkJQHrDAXImLX6JoS aESL9cm671WJ4d8MgjrCcdaeILyFPnsb2zoSn96sreABtk3zZz54xk23 dzQmXIMSwXDHIpfdF2Adsh+JOblOYuLrgepyG59IRKdPS0UVyDUlIfia slQ=
+ENTRY_END
+
+SCENARIO_END