From 8fc9f64df9fec3a4c872d7b873f1dd5907487b7e Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Tue, 13 Mar 2018 12:11:48 +1100 Subject: [PATCH] detect and processes root-key-sentinel labels. --- lib/ns/include/ns/query.h | 4 + lib/ns/query.c | 177 +++++++++++++++++++++++++++++++++++++- 2 files changed, 180 insertions(+), 1 deletion(-) diff --git a/lib/ns/include/ns/query.h b/lib/ns/include/ns/query.h index 87c0a638ed..365cb8c754 100644 --- a/lib/ns/include/ns/query.h +++ b/lib/ns/include/ns/query.h @@ -75,6 +75,10 @@ struct ns_query { isc_boolean_t authoritative; isc_boolean_t is_zone; } redirect; + + dns_keytag_t root_key_sentinel_keyid; + isc_boolean_t root_key_sentinel_is_ta; + isc_boolean_t root_key_sentinel_not_ta; }; #define NS_QUERYATTR_RECURSIONOK 0x0001 diff --git a/lib/ns/query.c b/lib/ns/query.c index b352cdbf55..275b230432 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -700,6 +701,9 @@ query_reset(ns_client_t *client, isc_boolean_t everything) { client->query.isreferral = ISC_FALSE; client->query.dns64_options = 0; client->query.dns64_ttl = ISC_UINT32_MAX; + client->query.root_key_sentinel_keyid = 0; + client->query.root_key_sentinel_is_ta = ISC_FALSE; + client->query.root_key_sentinel_not_ta = ISC_FALSE; } static void @@ -5118,6 +5122,68 @@ query_setup(ns_client_t *client, dns_rdatatype_t qtype) { return (ns__query_start(&qctx)); } +static isc_boolean_t +get_root_key_sentinel_id(query_ctx_t *qctx, const char *ndata) { + unsigned int v = 0; + int i; + + for (i = 0; i < 5; i++) { + if (ndata[i] < '0' || ndata[i] > '9') { + return (ISC_FALSE); + } + v *= 10; + v += ndata[i] - '0'; + } + if (v > 65535U) { + return (ISC_FALSE); + } + qctx->client->query.root_key_sentinel_keyid = v; + return (ISC_TRUE); +} + +/*% + * Find out if the query is for a root key sentinel and if so, record the type + * of root key sentinel query and the key id that is being checked for. + * + * The code is assuming a zero padded decimal field of width 5. + */ +static void +root_key_sentinel_detect(query_ctx_t *qctx) { + const char *ndata = (const char *)qctx->client->query.qname->ndata; + + if (qctx->client->query.qname->length > 30 && ndata[0] == 29 && + strncasecmp(ndata + 1, "root-key-sentinel-is-ta-", 24) == 0) + { + if (!get_root_key_sentinel_id(qctx, ndata + 25)) { + return; + } + qctx->client->query.root_key_sentinel_is_ta = ISC_TRUE; + /* + * Simplify processing by disabling agressive + * negative caching. + */ + qctx->findcoveringnsec = ISC_FALSE; + ns_client_log(qctx->client, NS_LOGCATEGORY_TAT, + NS_LOGMODULE_QUERY, ISC_LOG_INFO, + "root-key-sentinel-is-ta query label found"); + } else if (qctx->client->query.qname->length > 31 && ndata[0] == 30 && + strncasecmp(ndata + 1, "root-key-sentinel-not-ta-", 25) == 0) + { + if (!get_root_key_sentinel_id(qctx, ndata + 26)) { + return; + } + qctx->client->query.root_key_sentinel_not_ta = ISC_TRUE; + /* + * Simplify processing by disabling agressive + * negative caching. + */ + qctx->findcoveringnsec = ISC_FALSE; + ns_client_log(qctx->client, NS_LOGCATEGORY_TAT, + NS_LOGMODULE_QUERY, ISC_LOG_INFO, + "root-key-sentinel-not-ta query label found"); + } +} + /*% * Starting point for a client query or a chaining query. * @@ -5158,6 +5224,17 @@ ns__query_start(query_ctx_t *qctx) { return (query_done(qctx)); } + /* + * Setup for root key sentinel processing. + */ + if (qctx->client->query.restarts == 0 && + (qctx->qtype == dns_rdatatype_a || + qctx->qtype == dns_rdatatype_aaaa) && + (qctx->client->message->flags & DNS_MESSAGEFLAG_CD) == 0) + { + root_key_sentinel_detect(qctx); + } + /* * First we must find the right database. */ @@ -6339,6 +6416,91 @@ query_rpzcname(query_ctx_t *qctx, dns_name_t *cname) { return (ISC_R_SUCCESS); } +/*% + * Check the configured trust anchors for a root zone trust anchor + * with a key id that matches qctx->client->query.root_key_sentinel_keyid. + * + * Return ISC_TRUE when found, otherwise return ISC_FALSE. + */ +static isc_boolean_t +has_ta(query_ctx_t *qctx) { + dns_keytable_t *keytable = NULL; + dns_keynode_t *keynode = NULL; + isc_result_t result; + + result = dns_view_getsecroots(qctx->client->view, &keytable); + if (result != ISC_R_SUCCESS) { + return (ISC_FALSE); + } + + result = dns_keytable_find(keytable, dns_rootname, &keynode); + while (result == ISC_R_SUCCESS) { + dns_keynode_t *nextnode = NULL; + dns_keytag_t keyid = dst_key_id(dns_keynode_key(keynode)); + if (keyid == qctx->client->query.root_key_sentinel_keyid) { + dns_keytable_detachkeynode(keytable, &keynode); + dns_keytable_detach(&keytable); + return (ISC_TRUE); + } + result = dns_keytable_nextkeynode(keytable, keynode, &nextnode); + dns_keytable_detachkeynode(keytable, &keynode); + keynode = nextnode; + } + dns_keytable_detach(&keytable); + + return (ISC_FALSE); +} + +/*% + * Check if a root key sentinel SERVFAIL should be returned. + */ +static isc_boolean_t +root_key_sentinel_return_servfail(query_ctx_t *qctx, isc_result_t result) { + /* + * Are we looking at a "root-key-sentinel" query? + */ + if (!qctx->client->query.root_key_sentinel_is_ta && + !qctx->client->query.root_key_sentinel_not_ta) + { + return (ISC_FALSE); + } + + /* + * We only care about the query if 'result' indicates we have a cached + * answer. + */ + switch (result) { + case ISC_R_SUCCESS: + case DNS_R_CNAME: + case DNS_R_DNAME: + case DNS_R_NCACHENXDOMAIN: + case DNS_R_NCACHENXRRSET: + break; + default: + return (ISC_FALSE); + } + + /* + * Do we meet the specified conditions to return SERVFAIL? + */ + if (!qctx->is_zone && + qctx->rdataset->trust == dns_trust_secure && + ((qctx->client->query.root_key_sentinel_is_ta && !has_ta(qctx)) || + (qctx->client->query.root_key_sentinel_not_ta && has_ta(qctx)))) + { + return (ISC_TRUE); + } + + /* + * As special processing may only be triggered by the original QNAME, + * disable it after following a CNAME/DNAME. + */ + qctx->client->query.root_key_sentinel_is_ta = ISC_FALSE; + qctx->client->query.root_key_sentinel_not_ta = ISC_FALSE; + + return (ISC_FALSE); +} + /*% * Continue after doing a database lookup or returning from * recursion, and call out to the next function depending on the @@ -6362,6 +6524,19 @@ query_gotanswer(query_ctx_t *qctx, isc_result_t result) { return (query_done(qctx)); } + /* + * If required, handle special "root-key-sentinel-is-ta-" and + * "root-key-sentinel-not-ta-" labels by returning SERVFAIL. + */ + if (root_key_sentinel_return_servfail(qctx, result)) { + /* + * Don't record this response in the SERVFAIL cache. + */ + qctx->client->attributes |= NS_CLIENTATTR_NOSETFC; + QUERY_ERROR(qctx, DNS_R_SERVFAIL); + return (query_done(qctx)); + } + switch (result) { case ISC_R_SUCCESS: return (query_prepresponse(qctx)); @@ -10855,7 +11030,7 @@ ns_query_start(ns_client_t *client) { client->query.attributes &= ~NS_QUERYATTR_SECURE; /* - * Set NS_CLIENTATTR_WANTDNSSEC if the client has set AD in the query. + * Set NS_CLIENTATTR_WANTAD if the client has set AD in the query. * This allows AD to be returned on queries without DO set. */ if ((message->flags & DNS_MESSAGEFLAG_AD) != 0)