4747. [func] Synthesis of responses from DNSSEC-verified records.

Stage 3 - synthesize NODATA responses. [RT #40138]
This commit is contained in:
Mark Andrews
2017-10-03 11:16:37 +11:00
parent af3f476e77
commit c85b467dc0
6 changed files with 246 additions and 55 deletions

View File

@@ -8341,6 +8341,87 @@ log_noexistnodata(void *val, int level, const char *fmt, ...) {
va_end(ap);
}
/*
* Synthesize a NODATA response from the SOA and covering NSEC in cache.
*/
static isc_result_t
query_synthnodata(query_ctx_t *qctx, const dns_name_t *signer,
dns_rdataset_t **soardatasetp,
dns_rdataset_t **sigsoardatasetp)
{
dns_name_t *name = NULL;
dns_ttl_t ttl;
isc_buffer_t *dbuf, b;
isc_result_t result;
dns_rdataset_t *clone = NULL, *sigclone = NULL;
/*
* Detemine the correct TTL to use for the SOA and RRSIG
*/
ttl = ISC_MIN(qctx->rdataset->ttl, qctx->sigrdataset->ttl);
ttl = ISC_MIN(ttl, (*soardatasetp)->ttl);
ttl = ISC_MIN(ttl, (*sigsoardatasetp)->ttl);
(*soardatasetp)->ttl = (*sigsoardatasetp)->ttl = ttl;
/*
* We want the SOA record to be first, so save the
* NODATA proof's name now or else discard it.
*/
if (WANTDNSSEC(qctx->client)) {
query_keepname(qctx->client, qctx->fname, qctx->dbuf);
} else {
query_releasename(qctx->client, &qctx->fname);
}
dbuf = query_getnamebuf(qctx->client);
if (dbuf == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
name = query_newname(qctx->client, dbuf, &b);
if (name == NULL) {
result = ISC_R_NOMEMORY;
goto cleanup;
}
dns_name_clone(signer, name);
/*
* Add SOA record. Omit the RRSIG if DNSSEC was not requested.
*/
if (!WANTDNSSEC(qctx->client)) {
sigsoardatasetp = NULL;
}
query_addrrset(qctx->client, &name, soardatasetp, sigsoardatasetp,
dbuf, DNS_SECTION_AUTHORITY);
if (WANTDNSSEC(qctx->client)) {
/*
* Add NODATA proof.
*/
query_addrrset(qctx->client, &qctx->fname,
&qctx->rdataset, &qctx->sigrdataset,
NULL, DNS_SECTION_AUTHORITY);
}
result = ISC_R_SUCCESS;
inc_stats(qctx->client, ns_statscounter_nodatasynth);
cleanup:
if (name != NULL) {
query_releasename(qctx->client, &name);
}
if (clone != NULL) {
query_putrdataset(qctx->client, &clone);
}
if (sigclone != NULL) {
query_putrdataset(qctx->client, &sigclone);
}
return (result);
}
/*
* Synthesize a wildcard answer using the contents of 'rdataset'.
* qctx contains the NODATA proof.
@@ -8405,7 +8486,7 @@ query_synthwildcard(query_ctx_t *qctx, dns_rdataset_t *rdataset,
if (WANTDNSSEC(qctx->client)) {
/*
* Add NODATA proof.
* Add NOQNAME proof.
*/
query_addrrset(qctx->client, &qctx->fname,
&qctx->rdataset, &qctx->sigrdataset,
@@ -8429,7 +8510,7 @@ cleanup:
}
/*
* Add a synthesised CNAME record from the wildard RRset (rdataset)
* Add a synthesized CNAME record from the wildard RRset (rdataset)
* and NODATA proof by calling query_synthwildcard then setup to
* follow the CNAME.
*/
@@ -8487,7 +8568,7 @@ query_synthcnamewildcard(query_ctx_t *qctx, dns_rdataset_t *rdataset,
}
/*
* Synthesise a NXDOMAIN response from qctx (which contains the
* Synthesize a NXDOMAIN response from qctx (which contains the
* NODATA proof), nowild + rdataset + sigrdataset (which contains
* the NOWILDCARD proof) and signer + soardatasetp + sigsoardatasetp
* which contain the SOA record + RRSIG for the negative answer.
@@ -8553,7 +8634,7 @@ query_synthnxdomain(query_ctx_t *qctx,
if (WANTDNSSEC(qctx->client)) {
/*
* Add NODATA proof.
* Add NOQNAME proof.
*/
query_addrrset(qctx->client, &qctx->fname,
&qctx->rdataset, &qctx->sigrdataset,
@@ -8607,22 +8688,51 @@ cleanup:
return (result);
}
/*
* Check that all signer names in sigrdataset match the expected signer.
*/
static isc_result_t
checksignames(dns_name_t *signer, dns_rdataset_t *sigrdataset) {
isc_result_t result;
for (result = dns_rdataset_first(sigrdataset);
result == ISC_R_SUCCESS;
result = dns_rdataset_next(sigrdataset)) {
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_rdata_rrsig_t rrsig;
dns_rdataset_current(sigrdataset, &rdata);
result = dns_rdata_tostruct(&rdata, &rrsig, NULL);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
if (dns_name_countlabels(signer) == 0) {
dns_name_copy(&rrsig.signer, signer, NULL);
} else if (!dns_name_equal(signer, &rrsig.signer)) {
return (ISC_R_FAILURE);
}
}
return (ISC_R_SUCCESS);
}
/*%
* Handle covering NSEC responses.
*
* Verify the NSEC record is apropriate for the QNAME, if not
* Verify the NSEC record is apropriate for the QNAME; if not,
* redo the initial query without DNS_DBFIND_COVERINGNSEC.
*
* Compute the wildcard record and check if the wildcard name
* exists or not. If we can't determine this redo the initial
* query without DNS_DBFIND_COVERINGNSEC.
* If the covering NSEC proves that the name exists but not the type,
* synthesize a NODATA response.
*
* If the wildcard name does not exist compute the SOA name and look
* that up. If the SOA record does not exist redo the initial query
* without DNS_DBFIND_COVERINGNSEC. If the SOA record exists constructed
* a NXDOMAIN response from the found records.
* If the name doesn't exist, compute the wildcard record and check whether
* the wildcard name exists or not. If we can't determine this, redo the
* initial query without DNS_DBFIND_COVERINGNSEC.
*
* If the wildcard name does exist perform a lookup for the requested
* If the wildcard name does not exist, compute the SOA name and look that
* up. If the SOA record does not exist, redo the initial query without
* DNS_DBFIND_COVERINGNSEC. If the SOA record exists, synthesize an
* NXDOMAIN response from the found records.
*
* If the wildcard name does exist, perform a lookup for the requested
* type at the wildcard name.
*/
static isc_result_t
@@ -8673,21 +8783,10 @@ query_coveringnsec(query_ctx_t *qctx) {
/*
* All signer names must be the same to accept.
*/
for (result = dns_rdataset_first(qctx->sigrdataset);
result == ISC_R_SUCCESS;
result = dns_rdataset_next(qctx->sigrdataset))
{
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_rdata_rrsig_t rrsig;
dns_rdataset_current(qctx->sigrdataset, &rdata);
result = dns_rdata_tostruct(&rdata, &rrsig, NULL);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
if (dns_name_countlabels(signer) == 0) {
dns_name_copy(&rrsig.signer, signer, NULL);
} else if (!dns_name_equal(signer, &rrsig.signer)) {
goto cleanup;
}
result = checksignames(signer, qctx->sigrdataset);
if (result != ISC_R_SUCCESS) {
result = ISC_R_SUCCESS;
goto cleanup;
}
/*
@@ -8698,7 +8797,56 @@ query_coveringnsec(query_ctx_t *qctx) {
&exists, &data, wild,
log_noexistnodata, qctx);
if (result != ISC_R_SUCCESS || exists) {
if (result != ISC_R_SUCCESS || (exists && data)) {
goto cleanup;
}
if (exists) {
if (qctx->type == dns_rdatatype_any) { /* XXX not yet */
goto cleanup;
}
#ifdef ALLOW_FILTER_AAAA
if (qctx->client->filter_aaaa != dns_aaaa_ok &&
(qctx->type == dns_rdatatype_a ||
qctx->type == dns_rdatatype_aaaa)) /* XXX not yet */
{
goto cleanup;
}
#endif
if (!ISC_LIST_EMPTY(qctx->client->view->dns64) &&
(qctx->type == dns_rdatatype_a ||
qctx->type == dns_rdatatype_aaaa)) /* XXX not yet */
{
goto cleanup;
}
if (!qctx->resuming && !STALE(qctx->rdataset) &&
qctx->rdataset->ttl == 0 && RECURSIONOK(qctx->client))
{
goto cleanup;
}
soardataset = query_newrdataset(qctx->client);
sigsoardataset = query_newrdataset(qctx->client);
if (soardataset == NULL || sigsoardataset == NULL) {
goto cleanup;
}
/*
* Look for SOA record to construct NODATA response.
*/
dns_db_attach(qctx->db, &db);
result = dns_db_findext(db, signer, qctx->version,
dns_rdatatype_soa, dboptions,
qctx->client->now, &node,
fname, &cm, &ci, soardataset,
sigsoardataset);
if (result != ISC_R_SUCCESS) {
goto cleanup;
}
(void)query_synthnodata(qctx, signer,
&soardataset, &sigsoardataset);
done = ISC_TRUE;
goto cleanup;
}
@@ -8768,8 +8916,7 @@ query_coveringnsec(query_ctx_t *qctx) {
done = ISC_TRUE;
goto cleanup;
case DNS_R_CNAME: /* wild card cname */
(void)query_synthcnamewildcard(qctx, &rdataset,
&sigrdataset);
(void)query_synthcnamewildcard(qctx, &rdataset, &sigrdataset);
done = ISC_TRUE;
goto cleanup;
case DNS_R_NCACHENXRRSET: /* wild card nodata */
@@ -8789,26 +8936,19 @@ query_coveringnsec(query_ctx_t *qctx) {
}
/*
* All signer names must be the same to accept.
* Must be signed to accept.
*/
if (!dns_rdataset_isassociated(&sigrdataset)) {
goto cleanup;
}
for (result = dns_rdataset_first(&sigrdataset);
result == ISC_R_SUCCESS;
result = dns_rdataset_next(&sigrdataset)) {
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_rdata_rrsig_t rrsig;
dns_rdataset_current(&sigrdataset, &rdata);
result = dns_rdata_tostruct(&rdata, &rrsig, NULL);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
if (dns_name_countlabels(signer) == 0) {
dns_name_copy(&rrsig.signer, signer, NULL);
} else if (!dns_name_equal(signer, &rrsig.signer)) {
goto cleanup;
}
/*
* Check signer signer names again.
*/
result = checksignames(signer, &sigrdataset);
if (result != ISC_R_SUCCESS) {
result = ISC_R_SUCCESS;
goto cleanup;
}
if (node != NULL) {