1135446Strhodes/* 2254402Serwin * Copyright (C) 2004-2013 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 2000-2003 Internet Software Consortium. 4135446Strhodes * 5174187Sdougb * Permission to use, copy, modify, and/or distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18234010Sdougb/* $Id$ */ 19135446Strhodes 20135446Strhodes#include <config.h> 21135446Strhodes 22193149Sdougb#include <isc/base32.h> 23135446Strhodes#include <isc/mem.h> 24135446Strhodes#include <isc/print.h> 25193149Sdougb#include <isc/sha2.h> 26135446Strhodes#include <isc/string.h> 27135446Strhodes#include <isc/task.h> 28135446Strhodes#include <isc/util.h> 29135446Strhodes 30135446Strhodes#include <dns/db.h> 31224092Sdougb#include <dns/dnssec.h> 32135446Strhodes#include <dns/ds.h> 33135446Strhodes#include <dns/events.h> 34135446Strhodes#include <dns/keytable.h> 35224092Sdougb#include <dns/keyvalues.h> 36135446Strhodes#include <dns/log.h> 37135446Strhodes#include <dns/message.h> 38135446Strhodes#include <dns/ncache.h> 39135446Strhodes#include <dns/nsec.h> 40193149Sdougb#include <dns/nsec3.h> 41135446Strhodes#include <dns/rdata.h> 42135446Strhodes#include <dns/rdataset.h> 43135446Strhodes#include <dns/rdatatype.h> 44135446Strhodes#include <dns/resolver.h> 45135446Strhodes#include <dns/result.h> 46135446Strhodes#include <dns/validator.h> 47135446Strhodes#include <dns/view.h> 48135446Strhodes 49165071Sdougb/*! \file 50165071Sdougb * \brief 51165071Sdougb * Basic processing sequences. 52165071Sdougb * 53165071Sdougb * \li When called with rdataset and sigrdataset: 54165071Sdougb * validator_start -> validate -> proveunsecure -> startfinddlvsep -> 55165071Sdougb * dlv_validator_start -> validator_start -> validate -> proveunsecure 56165071Sdougb * 57165071Sdougb * validator_start -> validate -> nsecvalidate (secure wildcard answer) 58186462Sdougb * 59165071Sdougb * \li When called with rdataset, sigrdataset and with DNS_VALIDATOR_DLV: 60165071Sdougb * validator_start -> startfinddlvsep -> dlv_validator_start -> 61165071Sdougb * validator_start -> validate -> proveunsecure 62165071Sdougb * 63165071Sdougb * \li When called with rdataset: 64165071Sdougb * validator_start -> proveunsecure -> startfinddlvsep -> 65165071Sdougb * dlv_validator_start -> validator_start -> proveunsecure 66165071Sdougb * 67165071Sdougb * \li When called with rdataset and with DNS_VALIDATOR_DLV: 68165071Sdougb * validator_start -> startfinddlvsep -> dlv_validator_start -> 69165071Sdougb * validator_start -> proveunsecure 70165071Sdougb * 71165071Sdougb * \li When called without a rdataset: 72165071Sdougb * validator_start -> nsecvalidate -> proveunsecure -> startfinddlvsep -> 73165071Sdougb * dlv_validator_start -> validator_start -> nsecvalidate -> proveunsecure 74165071Sdougb * 75170222Sdougb * Note: there isn't a case for DNS_VALIDATOR_DLV here as we want nsecvalidate() 76170222Sdougb * to always validate the authority section even when it does not contain 77170222Sdougb * signatures. 78165071Sdougb * 79165071Sdougb * validator_start: determines what type of validation to do. 80165071Sdougb * validate: attempts to perform a positive validation. 81165071Sdougb * proveunsecure: attempts to prove the answer comes from a unsecure zone. 82165071Sdougb * nsecvalidate: attempts to prove a negative response. 83165071Sdougb * startfinddlvsep: starts the DLV record lookup. 84165071Sdougb * dlv_validator_start: resets state and restarts the lookup using the 85165071Sdougb * DLV RRset found by startfinddlvsep. 86165071Sdougb */ 87165071Sdougb 88135446Strhodes#define VALIDATOR_MAGIC ISC_MAGIC('V', 'a', 'l', '?') 89135446Strhodes#define VALID_VALIDATOR(v) ISC_MAGIC_VALID(v, VALIDATOR_MAGIC) 90135446Strhodes 91165071Sdougb#define VALATTR_SHUTDOWN 0x0001 /*%< Shutting down. */ 92193149Sdougb#define VALATTR_CANCELED 0x0002 /*%< Canceled. */ 93165071Sdougb#define VALATTR_TRIEDVERIFY 0x0004 /*%< We have found a key and 94165071Sdougb * have attempted a verify. */ 95165071Sdougb#define VALATTR_INSECURITY 0x0010 /*%< Attempting proveunsecure. */ 96165071Sdougb#define VALATTR_DLVTRIED 0x0020 /*%< Looked for a DLV record. */ 97135446Strhodes 98165071Sdougb/*! 99165071Sdougb * NSEC proofs to be looked for. 100165071Sdougb */ 101193149Sdougb#define VALATTR_NEEDNOQNAME 0x00000100 102193149Sdougb#define VALATTR_NEEDNOWILDCARD 0x00000200 103193149Sdougb#define VALATTR_NEEDNODATA 0x00000400 104135446Strhodes 105165071Sdougb/*! 106165071Sdougb * NSEC proofs that have been found. 107165071Sdougb */ 108193149Sdougb#define VALATTR_FOUNDNOQNAME 0x00001000 109193149Sdougb#define VALATTR_FOUNDNOWILDCARD 0x00002000 110193149Sdougb#define VALATTR_FOUNDNODATA 0x00004000 111193149Sdougb#define VALATTR_FOUNDCLOSEST 0x00008000 112135446Strhodes 113193149Sdougb/* 114193149Sdougb * 115193149Sdougb */ 116193149Sdougb#define VALATTR_FOUNDOPTOUT 0x00010000 117193149Sdougb#define VALATTR_FOUNDUNKNOWN 0x00020000 118193149Sdougb 119135446Strhodes#define NEEDNODATA(val) ((val->attributes & VALATTR_NEEDNODATA) != 0) 120135446Strhodes#define NEEDNOQNAME(val) ((val->attributes & VALATTR_NEEDNOQNAME) != 0) 121135446Strhodes#define NEEDNOWILDCARD(val) ((val->attributes & VALATTR_NEEDNOWILDCARD) != 0) 122135446Strhodes#define DLVTRIED(val) ((val->attributes & VALATTR_DLVTRIED) != 0) 123214586Sdougb#define FOUNDNODATA(val) ((val->attributes & VALATTR_FOUNDNODATA) != 0) 124214586Sdougb#define FOUNDNOQNAME(val) ((val->attributes & VALATTR_FOUNDNOQNAME) != 0) 125214586Sdougb#define FOUNDNOWILDCARD(val) ((val->attributes & VALATTR_FOUNDNOWILDCARD) != 0) 126214586Sdougb#define FOUNDCLOSEST(val) ((val->attributes & VALATTR_FOUNDCLOSEST) != 0) 127214586Sdougb#define FOUNDOPTOUT(val) ((val->attributes & VALATTR_FOUNDOPTOUT) != 0) 128135446Strhodes 129135446Strhodes#define SHUTDOWN(v) (((v)->attributes & VALATTR_SHUTDOWN) != 0) 130174187Sdougb#define CANCELED(v) (((v)->attributes & VALATTR_CANCELED) != 0) 131135446Strhodes 132223812Sdougb#define NEGATIVE(r) (((r)->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) 133223812Sdougb 134135446Strhodesstatic void 135135446Strhodesdestroy(dns_validator_t *val); 136135446Strhodes 137135446Strhodesstatic isc_result_t 138135446Strhodesget_dst_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo, 139135446Strhodes dns_rdataset_t *rdataset); 140135446Strhodes 141135446Strhodesstatic isc_result_t 142135446Strhodesvalidate(dns_validator_t *val, isc_boolean_t resume); 143135446Strhodes 144135446Strhodesstatic isc_result_t 145135446Strhodesvalidatezonekey(dns_validator_t *val); 146135446Strhodes 147135446Strhodesstatic isc_result_t 148135446Strhodesnsecvalidate(dns_validator_t *val, isc_boolean_t resume); 149135446Strhodes 150135446Strhodesstatic isc_result_t 151186462Sdougbproveunsecure(dns_validator_t *val, isc_boolean_t have_ds, 152186462Sdougb isc_boolean_t resume); 153135446Strhodes 154135446Strhodesstatic void 155135446Strhodesvalidator_logv(dns_validator_t *val, isc_logcategory_t *category, 156135446Strhodes isc_logmodule_t *module, int level, const char *fmt, va_list ap) 157135446Strhodes ISC_FORMAT_PRINTF(5, 0); 158135446Strhodes 159135446Strhodesstatic void 160254402Serwinvalidator_log(void *val, int level, const char *fmt, ...) 161135446Strhodes ISC_FORMAT_PRINTF(3, 4); 162135446Strhodes 163135446Strhodesstatic void 164135446Strhodesvalidator_logcreate(dns_validator_t *val, 165135446Strhodes dns_name_t *name, dns_rdatatype_t type, 166135446Strhodes const char *caller, const char *operation); 167135446Strhodes 168135446Strhodesstatic isc_result_t 169135446Strhodesdlv_validatezonekey(dns_validator_t *val); 170135446Strhodes 171165071Sdougbstatic void 172153816Sdougbdlv_validator_start(dns_validator_t *val); 173153816Sdougb 174153816Sdougbstatic isc_result_t 175135446Strhodesfinddlvsep(dns_validator_t *val, isc_boolean_t resume); 176135446Strhodes 177165071Sdougbstatic isc_result_t 178165071Sdougbstartfinddlvsep(dns_validator_t *val, dns_name_t *unsecure); 179165071Sdougb 180165071Sdougb/*% 181165071Sdougb * Mark the RRsets as a answer. 182165071Sdougb */ 183153816Sdougbstatic inline void 184214586Sdougbmarkanswer(dns_validator_t *val, const char *where) { 185214586Sdougb validator_log(val, ISC_LOG_DEBUG(3), "marking as answer (%s)", where); 186165071Sdougb if (val->event->rdataset != NULL) 187205292Sdougb dns_rdataset_settrust(val->event->rdataset, dns_trust_answer); 188165071Sdougb if (val->event->sigrdataset != NULL) 189205292Sdougb dns_rdataset_settrust(val->event->sigrdataset, 190205292Sdougb dns_trust_answer); 191153816Sdougb} 192153816Sdougb 193205292Sdougbstatic inline void 194205292Sdougbmarksecure(dns_validatorevent_t *event) { 195205292Sdougb dns_rdataset_settrust(event->rdataset, dns_trust_secure); 196214586Sdougb if (event->sigrdataset != NULL) 197214586Sdougb dns_rdataset_settrust(event->sigrdataset, dns_trust_secure); 198254402Serwin event->secure = ISC_TRUE; 199205292Sdougb} 200205292Sdougb 201135446Strhodesstatic void 202135446Strhodesvalidator_done(dns_validator_t *val, isc_result_t result) { 203135446Strhodes isc_task_t *task; 204135446Strhodes 205135446Strhodes if (val->event == NULL) 206135446Strhodes return; 207135446Strhodes 208135446Strhodes /* 209135446Strhodes * Caller must be holding the lock. 210135446Strhodes */ 211135446Strhodes 212135446Strhodes val->event->result = result; 213135446Strhodes task = val->event->ev_sender; 214135446Strhodes val->event->ev_sender = val; 215135446Strhodes val->event->ev_type = DNS_EVENT_VALIDATORDONE; 216135446Strhodes val->event->ev_action = val->action; 217135446Strhodes val->event->ev_arg = val->arg; 218135446Strhodes isc_task_sendanddetach(&task, (isc_event_t **)&val->event); 219135446Strhodes} 220135446Strhodes 221135446Strhodesstatic inline isc_boolean_t 222135446Strhodesexit_check(dns_validator_t *val) { 223135446Strhodes /* 224135446Strhodes * Caller must be holding the lock. 225135446Strhodes */ 226135446Strhodes if (!SHUTDOWN(val)) 227135446Strhodes return (ISC_FALSE); 228135446Strhodes 229135446Strhodes INSIST(val->event == NULL); 230135446Strhodes 231135446Strhodes if (val->fetch != NULL || val->subvalidator != NULL) 232135446Strhodes return (ISC_FALSE); 233135446Strhodes 234135446Strhodes return (ISC_TRUE); 235135446Strhodes} 236135446Strhodes 237190227Sdougb/* 238190227Sdougb * Check that we have atleast one supported algorithm in the DLV RRset. 239190227Sdougb */ 240190227Sdougbstatic inline isc_boolean_t 241190227Sdougbdlv_algorithm_supported(dns_validator_t *val) { 242190227Sdougb dns_rdata_t rdata = DNS_RDATA_INIT; 243190227Sdougb dns_rdata_dlv_t dlv; 244190227Sdougb isc_result_t result; 245190227Sdougb 246190227Sdougb for (result = dns_rdataset_first(&val->dlv); 247190227Sdougb result == ISC_R_SUCCESS; 248190227Sdougb result = dns_rdataset_next(&val->dlv)) { 249190227Sdougb dns_rdata_reset(&rdata); 250190227Sdougb dns_rdataset_current(&val->dlv, &rdata); 251190227Sdougb result = dns_rdata_tostruct(&rdata, &dlv, NULL); 252190227Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 253190227Sdougb 254190227Sdougb if (!dns_resolver_algorithm_supported(val->view->resolver, 255190227Sdougb val->event->name, 256190227Sdougb dlv.algorithm)) 257190227Sdougb continue; 258190227Sdougb 259224092Sdougb#ifdef HAVE_OPENSSL_GOST 260190227Sdougb if (dlv.digest_type != DNS_DSDIGEST_SHA256 && 261224092Sdougb dlv.digest_type != DNS_DSDIGEST_SHA1 && 262224092Sdougb dlv.digest_type != DNS_DSDIGEST_GOST) 263224092Sdougb continue; 264224092Sdougb#else 265224092Sdougb if (dlv.digest_type != DNS_DSDIGEST_SHA256 && 266190227Sdougb dlv.digest_type != DNS_DSDIGEST_SHA1) 267190227Sdougb continue; 268224092Sdougb#endif 269190227Sdougb 270224092Sdougb 271190227Sdougb return (ISC_TRUE); 272190227Sdougb } 273190227Sdougb return (ISC_FALSE); 274190227Sdougb} 275190227Sdougb 276165071Sdougb/*% 277165071Sdougb * Look in the NSEC record returned from a DS query to see if there is 278165071Sdougb * a NS RRset at this name. If it is found we are at a delegation point. 279165071Sdougb */ 280135446Strhodesstatic isc_boolean_t 281135446Strhodesisdelegation(dns_name_t *name, dns_rdataset_t *rdataset, 282135446Strhodes isc_result_t dbresult) 283135446Strhodes{ 284193149Sdougb dns_fixedname_t fixed; 285193149Sdougb dns_label_t hashlabel; 286193149Sdougb dns_name_t nsec3name; 287193149Sdougb dns_rdata_nsec3_t nsec3; 288193149Sdougb dns_rdata_t rdata = DNS_RDATA_INIT; 289135446Strhodes dns_rdataset_t set; 290193149Sdougb int order; 291193149Sdougb int scope; 292135446Strhodes isc_boolean_t found; 293193149Sdougb isc_buffer_t buffer; 294135446Strhodes isc_result_t result; 295193149Sdougb unsigned char hash[NSEC3_MAX_HASH_LENGTH]; 296193149Sdougb unsigned char owner[NSEC3_MAX_HASH_LENGTH]; 297193149Sdougb unsigned int length; 298135446Strhodes 299135446Strhodes REQUIRE(dbresult == DNS_R_NXRRSET || dbresult == DNS_R_NCACHENXRRSET); 300135446Strhodes 301135446Strhodes dns_rdataset_init(&set); 302135446Strhodes if (dbresult == DNS_R_NXRRSET) 303135446Strhodes dns_rdataset_clone(rdataset, &set); 304135446Strhodes else { 305135446Strhodes result = dns_ncache_getrdataset(rdataset, name, 306135446Strhodes dns_rdatatype_nsec, &set); 307193149Sdougb if (result == ISC_R_NOTFOUND) 308193149Sdougb goto trynsec3; 309135446Strhodes if (result != ISC_R_SUCCESS) 310135446Strhodes return (ISC_FALSE); 311135446Strhodes } 312135446Strhodes 313135446Strhodes INSIST(set.type == dns_rdatatype_nsec); 314135446Strhodes 315135446Strhodes found = ISC_FALSE; 316135446Strhodes result = dns_rdataset_first(&set); 317135446Strhodes if (result == ISC_R_SUCCESS) { 318135446Strhodes dns_rdataset_current(&set, &rdata); 319135446Strhodes found = dns_nsec_typepresent(&rdata, dns_rdatatype_ns); 320193149Sdougb dns_rdata_reset(&rdata); 321135446Strhodes } 322135446Strhodes dns_rdataset_disassociate(&set); 323135446Strhodes return (found); 324193149Sdougb 325193149Sdougb trynsec3: 326193149Sdougb /* 327193149Sdougb * Iterate over the ncache entry. 328193149Sdougb */ 329193149Sdougb found = ISC_FALSE; 330193149Sdougb dns_name_init(&nsec3name, NULL); 331193149Sdougb dns_fixedname_init(&fixed); 332193149Sdougb dns_name_downcase(name, dns_fixedname_name(&fixed), NULL); 333193149Sdougb name = dns_fixedname_name(&fixed); 334193149Sdougb for (result = dns_rdataset_first(rdataset); 335193149Sdougb result == ISC_R_SUCCESS; 336193149Sdougb result = dns_rdataset_next(rdataset)) 337193149Sdougb { 338193149Sdougb dns_ncache_current(rdataset, &nsec3name, &set); 339193149Sdougb if (set.type != dns_rdatatype_nsec3) { 340193149Sdougb dns_rdataset_disassociate(&set); 341193149Sdougb continue; 342193149Sdougb } 343193149Sdougb dns_name_getlabel(&nsec3name, 0, &hashlabel); 344193149Sdougb isc_region_consume(&hashlabel, 1); 345193149Sdougb isc_buffer_init(&buffer, owner, sizeof(owner)); 346193149Sdougb result = isc_base32hex_decoderegion(&hashlabel, &buffer); 347193149Sdougb if (result != ISC_R_SUCCESS) { 348193149Sdougb dns_rdataset_disassociate(&set); 349193149Sdougb continue; 350193149Sdougb } 351193149Sdougb for (result = dns_rdataset_first(&set); 352193149Sdougb result == ISC_R_SUCCESS; 353193149Sdougb result = dns_rdataset_next(&set)) 354193149Sdougb { 355193149Sdougb dns_rdata_reset(&rdata); 356193149Sdougb dns_rdataset_current(&set, &rdata); 357193149Sdougb (void)dns_rdata_tostruct(&rdata, &nsec3, NULL); 358193149Sdougb if (nsec3.hash != 1) 359193149Sdougb continue; 360193149Sdougb length = isc_iterated_hash(hash, nsec3.hash, 361193149Sdougb nsec3.iterations, nsec3.salt, 362193149Sdougb nsec3.salt_length, 363193149Sdougb name->ndata, name->length); 364193149Sdougb if (length != isc_buffer_usedlength(&buffer)) 365193149Sdougb continue; 366193149Sdougb order = memcmp(hash, owner, length); 367193149Sdougb if (order == 0) { 368193149Sdougb found = dns_nsec3_typepresent(&rdata, 369193149Sdougb dns_rdatatype_ns); 370193149Sdougb dns_rdataset_disassociate(&set); 371193149Sdougb return (found); 372193149Sdougb } 373193149Sdougb if ((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) == 0) 374193149Sdougb continue; 375193149Sdougb /* 376193149Sdougb * Does this optout span cover the name? 377193149Sdougb */ 378193149Sdougb scope = memcmp(owner, nsec3.next, nsec3.next_length); 379193149Sdougb if ((scope < 0 && order > 0 && 380193149Sdougb memcmp(hash, nsec3.next, length) < 0) || 381193149Sdougb (scope >= 0 && (order > 0 || 382193149Sdougb memcmp(hash, nsec3.next, length) < 0))) 383193149Sdougb { 384193149Sdougb dns_rdataset_disassociate(&set); 385193149Sdougb return (ISC_TRUE); 386193149Sdougb } 387193149Sdougb } 388193149Sdougb dns_rdataset_disassociate(&set); 389193149Sdougb } 390193149Sdougb return (found); 391135446Strhodes} 392135446Strhodes 393165071Sdougb/*% 394224092Sdougb * We have been asked to look for a key. 395165071Sdougb * If found resume the validation process. 396165071Sdougb * If not found fail the validation process. 397165071Sdougb */ 398135446Strhodesstatic void 399135446Strhodesfetch_callback_validator(isc_task_t *task, isc_event_t *event) { 400135446Strhodes dns_fetchevent_t *devent; 401135446Strhodes dns_validator_t *val; 402135446Strhodes dns_rdataset_t *rdataset; 403135446Strhodes isc_boolean_t want_destroy; 404135446Strhodes isc_result_t result; 405135446Strhodes isc_result_t eresult; 406216175Sdougb isc_result_t saved_result; 407254402Serwin dns_fetch_t *fetch; 408135446Strhodes 409135446Strhodes UNUSED(task); 410135446Strhodes INSIST(event->ev_type == DNS_EVENT_FETCHDONE); 411135446Strhodes devent = (dns_fetchevent_t *)event; 412135446Strhodes val = devent->ev_arg; 413135446Strhodes rdataset = &val->frdataset; 414135446Strhodes eresult = devent->result; 415135446Strhodes 416153816Sdougb /* Free resources which are not of interest. */ 417153816Sdougb if (devent->node != NULL) 418153816Sdougb dns_db_detachnode(devent->db, &devent->node); 419153816Sdougb if (devent->db != NULL) 420153816Sdougb dns_db_detach(&devent->db); 421153816Sdougb if (dns_rdataset_isassociated(&val->fsigrdataset)) 422153816Sdougb dns_rdataset_disassociate(&val->fsigrdataset); 423135446Strhodes isc_event_free(&event); 424135446Strhodes 425135446Strhodes INSIST(val->event != NULL); 426135446Strhodes 427135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), "in fetch_callback_validator"); 428135446Strhodes LOCK(&val->lock); 429254402Serwin fetch = val->fetch; 430254402Serwin val->fetch = NULL; 431174187Sdougb if (CANCELED(val)) { 432174187Sdougb validator_done(val, ISC_R_CANCELED); 433174187Sdougb } else if (eresult == ISC_R_SUCCESS) { 434135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 435222395Sdougb "keyset with trust %s", 436222395Sdougb dns_trust_totext(rdataset->trust)); 437135446Strhodes /* 438135446Strhodes * Only extract the dst key if the keyset is secure. 439135446Strhodes */ 440135446Strhodes if (rdataset->trust >= dns_trust_secure) { 441135446Strhodes result = get_dst_key(val, val->siginfo, rdataset); 442135446Strhodes if (result == ISC_R_SUCCESS) 443135446Strhodes val->keyset = &val->frdataset; 444135446Strhodes } 445135446Strhodes result = validate(val, ISC_TRUE); 446216175Sdougb if (result == DNS_R_NOVALIDSIG && 447216175Sdougb (val->attributes & VALATTR_TRIEDVERIFY) == 0) 448216175Sdougb { 449216175Sdougb saved_result = result; 450216175Sdougb validator_log(val, ISC_LOG_DEBUG(3), 451216175Sdougb "falling back to insecurity proof"); 452216175Sdougb val->attributes |= VALATTR_INSECURITY; 453216175Sdougb result = proveunsecure(val, ISC_FALSE, ISC_FALSE); 454216175Sdougb if (result == DNS_R_NOTINSECURE) 455216175Sdougb result = saved_result; 456216175Sdougb } 457135446Strhodes if (result != DNS_R_WAIT) 458135446Strhodes validator_done(val, result); 459135446Strhodes } else { 460135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 461135446Strhodes "fetch_callback_validator: got %s", 462135446Strhodes isc_result_totext(eresult)); 463135446Strhodes if (eresult == ISC_R_CANCELED) 464135446Strhodes validator_done(val, eresult); 465135446Strhodes else 466205292Sdougb validator_done(val, DNS_R_BROKENCHAIN); 467135446Strhodes } 468135446Strhodes want_destroy = exit_check(val); 469135446Strhodes UNLOCK(&val->lock); 470254402Serwin if (fetch != NULL) 471254402Serwin dns_resolver_destroyfetch(&fetch); 472135446Strhodes if (want_destroy) 473135446Strhodes destroy(val); 474135446Strhodes} 475135446Strhodes 476165071Sdougb/*% 477165071Sdougb * We were asked to look for a DS record as part of following a key chain 478165071Sdougb * upwards. If found resume the validation process. If not found fail the 479165071Sdougb * validation process. 480165071Sdougb */ 481135446Strhodesstatic void 482135446Strhodesdsfetched(isc_task_t *task, isc_event_t *event) { 483135446Strhodes dns_fetchevent_t *devent; 484135446Strhodes dns_validator_t *val; 485135446Strhodes dns_rdataset_t *rdataset; 486135446Strhodes isc_boolean_t want_destroy; 487135446Strhodes isc_result_t result; 488135446Strhodes isc_result_t eresult; 489254402Serwin dns_fetch_t *fetch; 490135446Strhodes 491135446Strhodes UNUSED(task); 492135446Strhodes INSIST(event->ev_type == DNS_EVENT_FETCHDONE); 493135446Strhodes devent = (dns_fetchevent_t *)event; 494135446Strhodes val = devent->ev_arg; 495135446Strhodes rdataset = &val->frdataset; 496135446Strhodes eresult = devent->result; 497135446Strhodes 498153816Sdougb /* Free resources which are not of interest. */ 499153816Sdougb if (devent->node != NULL) 500153816Sdougb dns_db_detachnode(devent->db, &devent->node); 501153816Sdougb if (devent->db != NULL) 502153816Sdougb dns_db_detach(&devent->db); 503153816Sdougb if (dns_rdataset_isassociated(&val->fsigrdataset)) 504153816Sdougb dns_rdataset_disassociate(&val->fsigrdataset); 505135446Strhodes isc_event_free(&event); 506135446Strhodes 507135446Strhodes INSIST(val->event != NULL); 508135446Strhodes 509135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), "in dsfetched"); 510135446Strhodes LOCK(&val->lock); 511254402Serwin fetch = val->fetch; 512254402Serwin val->fetch = NULL; 513174187Sdougb if (CANCELED(val)) { 514174187Sdougb validator_done(val, ISC_R_CANCELED); 515174187Sdougb } else if (eresult == ISC_R_SUCCESS) { 516135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 517222395Sdougb "dsset with trust %s", 518222395Sdougb dns_trust_totext(rdataset->trust)); 519135446Strhodes val->dsset = &val->frdataset; 520135446Strhodes result = validatezonekey(val); 521135446Strhodes if (result != DNS_R_WAIT) 522135446Strhodes validator_done(val, result); 523225361Sdougb } else if (eresult == DNS_R_CNAME || 524225361Sdougb eresult == DNS_R_NXRRSET || 525174187Sdougb eresult == DNS_R_NCACHENXRRSET || 526174187Sdougb eresult == DNS_R_SERVFAIL) /* RFC 1034 parent? */ 527135446Strhodes { 528135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 529174187Sdougb "falling back to insecurity proof (%s)", 530174187Sdougb dns_result_totext(eresult)); 531135446Strhodes val->attributes |= VALATTR_INSECURITY; 532186462Sdougb result = proveunsecure(val, ISC_FALSE, ISC_FALSE); 533135446Strhodes if (result != DNS_R_WAIT) 534135446Strhodes validator_done(val, result); 535135446Strhodes } else { 536135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 537135446Strhodes "dsfetched: got %s", 538135446Strhodes isc_result_totext(eresult)); 539135446Strhodes if (eresult == ISC_R_CANCELED) 540135446Strhodes validator_done(val, eresult); 541135446Strhodes else 542205292Sdougb validator_done(val, DNS_R_BROKENCHAIN); 543135446Strhodes } 544135446Strhodes want_destroy = exit_check(val); 545135446Strhodes UNLOCK(&val->lock); 546254402Serwin if (fetch != NULL) 547254402Serwin dns_resolver_destroyfetch(&fetch); 548135446Strhodes if (want_destroy) 549135446Strhodes destroy(val); 550135446Strhodes} 551135446Strhodes 552165071Sdougb/*% 553165071Sdougb * We were asked to look for the DS record as part of proving that a 554165071Sdougb * name is unsecure. 555165071Sdougb * 556165071Sdougb * If the DS record doesn't exist and the query name corresponds to 557165071Sdougb * a delegation point we are transitioning from a secure zone to a 558165071Sdougb * unsecure zone. 559165071Sdougb * 560165071Sdougb * If the DS record exists it will be secure. We can continue looking 561165071Sdougb * for the break point in the chain of trust. 562135446Strhodes */ 563135446Strhodesstatic void 564135446Strhodesdsfetched2(isc_task_t *task, isc_event_t *event) { 565135446Strhodes dns_fetchevent_t *devent; 566135446Strhodes dns_validator_t *val; 567135446Strhodes dns_name_t *tname; 568135446Strhodes isc_boolean_t want_destroy; 569135446Strhodes isc_result_t result; 570135446Strhodes isc_result_t eresult; 571254402Serwin dns_fetch_t *fetch; 572135446Strhodes 573135446Strhodes UNUSED(task); 574135446Strhodes INSIST(event->ev_type == DNS_EVENT_FETCHDONE); 575135446Strhodes devent = (dns_fetchevent_t *)event; 576135446Strhodes val = devent->ev_arg; 577135446Strhodes eresult = devent->result; 578135446Strhodes 579153816Sdougb /* Free resources which are not of interest. */ 580153816Sdougb if (devent->node != NULL) 581153816Sdougb dns_db_detachnode(devent->db, &devent->node); 582153816Sdougb if (devent->db != NULL) 583153816Sdougb dns_db_detach(&devent->db); 584153816Sdougb if (dns_rdataset_isassociated(&val->fsigrdataset)) 585153816Sdougb dns_rdataset_disassociate(&val->fsigrdataset); 586135446Strhodes 587135446Strhodes INSIST(val->event != NULL); 588135446Strhodes 589165071Sdougb validator_log(val, ISC_LOG_DEBUG(3), "in dsfetched2: %s", 590165071Sdougb dns_result_totext(eresult)); 591135446Strhodes LOCK(&val->lock); 592254402Serwin fetch = val->fetch; 593254402Serwin val->fetch = NULL; 594174187Sdougb if (CANCELED(val)) { 595174187Sdougb validator_done(val, ISC_R_CANCELED); 596225361Sdougb } else if (eresult == DNS_R_CNAME || 597225361Sdougb eresult == DNS_R_NXRRSET || 598225361Sdougb eresult == DNS_R_NCACHENXRRSET) 599225361Sdougb { 600135446Strhodes /* 601135446Strhodes * There is no DS. If this is a delegation, we're done. 602135446Strhodes */ 603135446Strhodes tname = dns_fixedname_name(&devent->foundname); 604225361Sdougb if (eresult != DNS_R_CNAME && 605225361Sdougb isdelegation(tname, &val->frdataset, eresult)) { 606135446Strhodes if (val->mustbesecure) { 607135446Strhodes validator_log(val, ISC_LOG_WARNING, 608224092Sdougb "must be secure failure, no DS" 609224092Sdougb " and this is a delegation"); 610135446Strhodes validator_done(val, DNS_R_MUSTBESECURE); 611165071Sdougb } else if (val->view->dlv == NULL || DLVTRIED(val)) { 612214586Sdougb markanswer(val, "dsfetched2"); 613135446Strhodes validator_done(val, ISC_R_SUCCESS); 614165071Sdougb } else { 615165071Sdougb result = startfinddlvsep(val, tname); 616165071Sdougb if (result != DNS_R_WAIT) 617165071Sdougb validator_done(val, result); 618135446Strhodes } 619135446Strhodes } else { 620186462Sdougb result = proveunsecure(val, ISC_FALSE, ISC_TRUE); 621135446Strhodes if (result != DNS_R_WAIT) 622135446Strhodes validator_done(val, result); 623135446Strhodes } 624135446Strhodes } else if (eresult == ISC_R_SUCCESS || 625135446Strhodes eresult == DNS_R_NXDOMAIN || 626135446Strhodes eresult == DNS_R_NCACHENXDOMAIN) 627135446Strhodes { 628135446Strhodes /* 629186462Sdougb * There is a DS which may or may not be a zone cut. 630165071Sdougb * In either case we are still in a secure zone resume 631165071Sdougb * validation. 632135446Strhodes */ 633186462Sdougb result = proveunsecure(val, ISC_TF(eresult == ISC_R_SUCCESS), 634186462Sdougb ISC_TRUE); 635135446Strhodes if (result != DNS_R_WAIT) 636135446Strhodes validator_done(val, result); 637135446Strhodes } else { 638135446Strhodes if (eresult == ISC_R_CANCELED) 639135446Strhodes validator_done(val, eresult); 640135446Strhodes else 641135446Strhodes validator_done(val, DNS_R_NOVALIDDS); 642135446Strhodes } 643135446Strhodes isc_event_free(&event); 644135446Strhodes want_destroy = exit_check(val); 645135446Strhodes UNLOCK(&val->lock); 646254402Serwin if (fetch != NULL) 647254402Serwin dns_resolver_destroyfetch(&fetch); 648135446Strhodes if (want_destroy) 649135446Strhodes destroy(val); 650135446Strhodes} 651135446Strhodes 652165071Sdougb/*% 653165071Sdougb * Callback from when a DNSKEY RRset has been validated. 654165071Sdougb * 655165071Sdougb * Resumes the stalled validation process. 656165071Sdougb */ 657135446Strhodesstatic void 658135446Strhodeskeyvalidated(isc_task_t *task, isc_event_t *event) { 659135446Strhodes dns_validatorevent_t *devent; 660135446Strhodes dns_validator_t *val; 661135446Strhodes isc_boolean_t want_destroy; 662135446Strhodes isc_result_t result; 663135446Strhodes isc_result_t eresult; 664216175Sdougb isc_result_t saved_result; 665135446Strhodes 666135446Strhodes UNUSED(task); 667135446Strhodes INSIST(event->ev_type == DNS_EVENT_VALIDATORDONE); 668135446Strhodes 669135446Strhodes devent = (dns_validatorevent_t *)event; 670135446Strhodes val = devent->ev_arg; 671135446Strhodes eresult = devent->result; 672135446Strhodes 673135446Strhodes isc_event_free(&event); 674135446Strhodes dns_validator_destroy(&val->subvalidator); 675135446Strhodes 676135446Strhodes INSIST(val->event != NULL); 677135446Strhodes 678135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), "in keyvalidated"); 679135446Strhodes LOCK(&val->lock); 680174187Sdougb if (CANCELED(val)) { 681174187Sdougb validator_done(val, ISC_R_CANCELED); 682174187Sdougb } else if (eresult == ISC_R_SUCCESS) { 683135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 684222395Sdougb "keyset with trust %s", 685222395Sdougb dns_trust_totext(val->frdataset.trust)); 686135446Strhodes /* 687135446Strhodes * Only extract the dst key if the keyset is secure. 688135446Strhodes */ 689135446Strhodes if (val->frdataset.trust >= dns_trust_secure) 690135446Strhodes (void) get_dst_key(val, val->siginfo, &val->frdataset); 691135446Strhodes result = validate(val, ISC_TRUE); 692216175Sdougb if (result == DNS_R_NOVALIDSIG && 693216175Sdougb (val->attributes & VALATTR_TRIEDVERIFY) == 0) 694216175Sdougb { 695216175Sdougb saved_result = result; 696216175Sdougb validator_log(val, ISC_LOG_DEBUG(3), 697216175Sdougb "falling back to insecurity proof"); 698216175Sdougb val->attributes |= VALATTR_INSECURITY; 699216175Sdougb result = proveunsecure(val, ISC_FALSE, ISC_FALSE); 700216175Sdougb if (result == DNS_R_NOTINSECURE) 701216175Sdougb result = saved_result; 702216175Sdougb } 703135446Strhodes if (result != DNS_R_WAIT) 704135446Strhodes validator_done(val, result); 705135446Strhodes } else { 706205292Sdougb if (eresult != DNS_R_BROKENCHAIN) { 707205292Sdougb if (dns_rdataset_isassociated(&val->frdataset)) 708205292Sdougb dns_rdataset_expire(&val->frdataset); 709205292Sdougb if (dns_rdataset_isassociated(&val->fsigrdataset)) 710205292Sdougb dns_rdataset_expire(&val->fsigrdataset); 711205292Sdougb } 712135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 713135446Strhodes "keyvalidated: got %s", 714135446Strhodes isc_result_totext(eresult)); 715205292Sdougb validator_done(val, DNS_R_BROKENCHAIN); 716135446Strhodes } 717135446Strhodes want_destroy = exit_check(val); 718135446Strhodes UNLOCK(&val->lock); 719135446Strhodes if (want_destroy) 720135446Strhodes destroy(val); 721135446Strhodes} 722135446Strhodes 723165071Sdougb/*% 724165071Sdougb * Callback when the DS record has been validated. 725165071Sdougb * 726165071Sdougb * Resumes validation of the zone key or the unsecure zone proof. 727165071Sdougb */ 728135446Strhodesstatic void 729135446Strhodesdsvalidated(isc_task_t *task, isc_event_t *event) { 730135446Strhodes dns_validatorevent_t *devent; 731135446Strhodes dns_validator_t *val; 732135446Strhodes isc_boolean_t want_destroy; 733135446Strhodes isc_result_t result; 734135446Strhodes isc_result_t eresult; 735135446Strhodes 736135446Strhodes UNUSED(task); 737135446Strhodes INSIST(event->ev_type == DNS_EVENT_VALIDATORDONE); 738135446Strhodes 739135446Strhodes devent = (dns_validatorevent_t *)event; 740135446Strhodes val = devent->ev_arg; 741135446Strhodes eresult = devent->result; 742135446Strhodes 743135446Strhodes isc_event_free(&event); 744135446Strhodes dns_validator_destroy(&val->subvalidator); 745135446Strhodes 746135446Strhodes INSIST(val->event != NULL); 747135446Strhodes 748135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), "in dsvalidated"); 749135446Strhodes LOCK(&val->lock); 750174187Sdougb if (CANCELED(val)) { 751174187Sdougb validator_done(val, ISC_R_CANCELED); 752174187Sdougb } else if (eresult == ISC_R_SUCCESS) { 753214586Sdougb isc_boolean_t have_dsset; 754214586Sdougb dns_name_t *name; 755135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 756222395Sdougb "%s with trust %s", 757214586Sdougb val->frdataset.type == dns_rdatatype_ds ? 758214586Sdougb "dsset" : "ds non-existance", 759222395Sdougb dns_trust_totext(val->frdataset.trust)); 760214586Sdougb have_dsset = ISC_TF(val->frdataset.type == dns_rdatatype_ds); 761214586Sdougb name = dns_fixedname_name(&val->fname); 762214586Sdougb if ((val->attributes & VALATTR_INSECURITY) != 0 && 763214586Sdougb val->frdataset.covers == dns_rdatatype_ds && 764223812Sdougb NEGATIVE(&val->frdataset) && 765214586Sdougb isdelegation(name, &val->frdataset, DNS_R_NCACHENXRRSET)) { 766214586Sdougb if (val->mustbesecure) { 767214586Sdougb validator_log(val, ISC_LOG_WARNING, 768214586Sdougb "must be secure failure, no DS " 769214586Sdougb "and this is a delegation"); 770214586Sdougb result = DNS_R_MUSTBESECURE; 771214586Sdougb } else if (val->view->dlv == NULL || DLVTRIED(val)) { 772214586Sdougb markanswer(val, "dsvalidated"); 773214586Sdougb result = ISC_R_SUCCESS;; 774214586Sdougb } else 775214586Sdougb result = startfinddlvsep(val, name); 776214586Sdougb } else if ((val->attributes & VALATTR_INSECURITY) != 0) { 777214586Sdougb result = proveunsecure(val, have_dsset, ISC_TRUE); 778214586Sdougb } else 779135446Strhodes result = validatezonekey(val); 780135446Strhodes if (result != DNS_R_WAIT) 781135446Strhodes validator_done(val, result); 782135446Strhodes } else { 783205292Sdougb if (eresult != DNS_R_BROKENCHAIN) { 784205292Sdougb if (dns_rdataset_isassociated(&val->frdataset)) 785205292Sdougb dns_rdataset_expire(&val->frdataset); 786205292Sdougb if (dns_rdataset_isassociated(&val->fsigrdataset)) 787205292Sdougb dns_rdataset_expire(&val->fsigrdataset); 788205292Sdougb } 789135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 790135446Strhodes "dsvalidated: got %s", 791135446Strhodes isc_result_totext(eresult)); 792205292Sdougb validator_done(val, DNS_R_BROKENCHAIN); 793135446Strhodes } 794135446Strhodes want_destroy = exit_check(val); 795135446Strhodes UNLOCK(&val->lock); 796135446Strhodes if (want_destroy) 797135446Strhodes destroy(val); 798135446Strhodes} 799135446Strhodes 800165071Sdougb/*% 801225361Sdougb * Callback when the CNAME record has been validated. 802225361Sdougb * 803225361Sdougb * Resumes validation of the unsecure zone proof. 804225361Sdougb */ 805225361Sdougbstatic void 806225361Sdougbcnamevalidated(isc_task_t *task, isc_event_t *event) { 807225361Sdougb dns_validatorevent_t *devent; 808225361Sdougb dns_validator_t *val; 809225361Sdougb isc_boolean_t want_destroy; 810225361Sdougb isc_result_t result; 811225361Sdougb isc_result_t eresult; 812225361Sdougb 813225361Sdougb UNUSED(task); 814225361Sdougb INSIST(event->ev_type == DNS_EVENT_VALIDATORDONE); 815225361Sdougb 816225361Sdougb devent = (dns_validatorevent_t *)event; 817225361Sdougb val = devent->ev_arg; 818225361Sdougb eresult = devent->result; 819225361Sdougb 820225361Sdougb isc_event_free(&event); 821225361Sdougb dns_validator_destroy(&val->subvalidator); 822225361Sdougb 823225361Sdougb INSIST(val->event != NULL); 824225361Sdougb INSIST((val->attributes & VALATTR_INSECURITY) != 0); 825225361Sdougb 826225361Sdougb validator_log(val, ISC_LOG_DEBUG(3), "in cnamevalidated"); 827225361Sdougb LOCK(&val->lock); 828225361Sdougb if (CANCELED(val)) { 829225361Sdougb validator_done(val, ISC_R_CANCELED); 830225361Sdougb } else if (eresult == ISC_R_SUCCESS) { 831225361Sdougb validator_log(val, ISC_LOG_DEBUG(3), "cname with trust %s", 832225361Sdougb dns_trust_totext(val->frdataset.trust)); 833225361Sdougb result = proveunsecure(val, ISC_FALSE, ISC_TRUE); 834225361Sdougb if (result != DNS_R_WAIT) 835225361Sdougb validator_done(val, result); 836225361Sdougb } else { 837225361Sdougb if (eresult != DNS_R_BROKENCHAIN) { 838225361Sdougb if (dns_rdataset_isassociated(&val->frdataset)) 839225361Sdougb dns_rdataset_expire(&val->frdataset); 840225361Sdougb if (dns_rdataset_isassociated(&val->fsigrdataset)) 841225361Sdougb dns_rdataset_expire(&val->fsigrdataset); 842225361Sdougb } 843225361Sdougb validator_log(val, ISC_LOG_DEBUG(3), 844225361Sdougb "cnamevalidated: got %s", 845225361Sdougb isc_result_totext(eresult)); 846225361Sdougb validator_done(val, DNS_R_BROKENCHAIN); 847225361Sdougb } 848225361Sdougb want_destroy = exit_check(val); 849225361Sdougb UNLOCK(&val->lock); 850225361Sdougb if (want_destroy) 851225361Sdougb destroy(val); 852225361Sdougb} 853225361Sdougb 854225361Sdougb/*% 855165071Sdougb * Callback for when NSEC records have been validated. 856165071Sdougb * 857193149Sdougb * Looks for NOQNAME, NODATA and OPTOUT proofs. 858165071Sdougb * 859165071Sdougb * Resumes nsecvalidate. 860165071Sdougb */ 861135446Strhodesstatic void 862135446Strhodesauthvalidated(isc_task_t *task, isc_event_t *event) { 863135446Strhodes dns_validatorevent_t *devent; 864135446Strhodes dns_validator_t *val; 865153816Sdougb dns_rdataset_t *rdataset; 866135446Strhodes isc_boolean_t want_destroy; 867135446Strhodes isc_result_t result; 868135446Strhodes isc_boolean_t exists, data; 869135446Strhodes 870135446Strhodes UNUSED(task); 871135446Strhodes INSIST(event->ev_type == DNS_EVENT_VALIDATORDONE); 872135446Strhodes 873135446Strhodes devent = (dns_validatorevent_t *)event; 874135446Strhodes rdataset = devent->rdataset; 875135446Strhodes val = devent->ev_arg; 876135446Strhodes result = devent->result; 877135446Strhodes dns_validator_destroy(&val->subvalidator); 878135446Strhodes 879135446Strhodes INSIST(val->event != NULL); 880135446Strhodes 881135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), "in authvalidated"); 882135446Strhodes LOCK(&val->lock); 883174187Sdougb if (CANCELED(val)) { 884174187Sdougb validator_done(val, ISC_R_CANCELED); 885174187Sdougb } else if (result != ISC_R_SUCCESS) { 886135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 887135446Strhodes "authvalidated: got %s", 888135446Strhodes isc_result_totext(result)); 889205292Sdougb if (result == DNS_R_BROKENCHAIN) 890205292Sdougb val->authfail++; 891135446Strhodes if (result == ISC_R_CANCELED) 892135446Strhodes validator_done(val, result); 893135446Strhodes else { 894135446Strhodes result = nsecvalidate(val, ISC_TRUE); 895135446Strhodes if (result != DNS_R_WAIT) 896135446Strhodes validator_done(val, result); 897135446Strhodes } 898135446Strhodes } else { 899135446Strhodes dns_name_t **proofs = val->event->proofs; 900170222Sdougb dns_name_t *wild = dns_fixedname_name(&val->wild); 901186462Sdougb 902135446Strhodes if (rdataset->trust == dns_trust_secure) 903135446Strhodes val->seensig = ISC_TRUE; 904135446Strhodes 905143731Sdougb if (rdataset->type == dns_rdatatype_nsec && 906135446Strhodes rdataset->trust == dns_trust_secure && 907214586Sdougb (NEEDNODATA(val) || NEEDNOQNAME(val)) && 908214586Sdougb !FOUNDNODATA(val) && !FOUNDNOQNAME(val) && 909254402Serwin dns_nsec_noexistnodata(val->event->type, val->event->name, 910254402Serwin devent->name, rdataset, &exists, 911254402Serwin &data, wild, validator_log, val) 912135446Strhodes == ISC_R_SUCCESS) 913170222Sdougb { 914135446Strhodes if (exists && !data) { 915135446Strhodes val->attributes |= VALATTR_FOUNDNODATA; 916135446Strhodes if (NEEDNODATA(val)) 917135446Strhodes proofs[DNS_VALIDATOR_NODATAPROOF] = 918135446Strhodes devent->name; 919135446Strhodes } 920135446Strhodes if (!exists) { 921135446Strhodes val->attributes |= VALATTR_FOUNDNOQNAME; 922193149Sdougb val->attributes |= VALATTR_FOUNDCLOSEST; 923193149Sdougb /* 924193149Sdougb * The NSEC noqname proof also contains 925193149Sdougb * the closest encloser. 926193149Sdougb 927193149Sdougb */ 928135446Strhodes if (NEEDNOQNAME(val)) 929135446Strhodes proofs[DNS_VALIDATOR_NOQNAMEPROOF] = 930135446Strhodes devent->name; 931135446Strhodes } 932135446Strhodes } 933193149Sdougb 934135446Strhodes result = nsecvalidate(val, ISC_TRUE); 935135446Strhodes if (result != DNS_R_WAIT) 936135446Strhodes validator_done(val, result); 937135446Strhodes } 938135446Strhodes want_destroy = exit_check(val); 939135446Strhodes UNLOCK(&val->lock); 940135446Strhodes if (want_destroy) 941135446Strhodes destroy(val); 942135446Strhodes 943135446Strhodes /* 944135446Strhodes * Free stuff from the event. 945135446Strhodes */ 946135446Strhodes isc_event_free(&event); 947135446Strhodes} 948135446Strhodes 949165071Sdougb/*% 950165071Sdougb * Looks for the requested name and type in the view (zones and cache). 951165071Sdougb * 952165071Sdougb * When looking for a DLV record also checks to make sure the NSEC record 953165071Sdougb * returns covers the query name as part of aggressive negative caching. 954165071Sdougb * 955165071Sdougb * Returns: 956165071Sdougb * \li ISC_R_SUCCESS 957165071Sdougb * \li ISC_R_NOTFOUND 958165071Sdougb * \li DNS_R_NCACHENXDOMAIN 959165071Sdougb * \li DNS_R_NCACHENXRRSET 960165071Sdougb * \li DNS_R_NXRRSET 961165071Sdougb * \li DNS_R_NXDOMAIN 962205292Sdougb * \li DNS_R_BROKENCHAIN 963165071Sdougb */ 964135446Strhodesstatic inline isc_result_t 965135446Strhodesview_find(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type) { 966135446Strhodes dns_fixedname_t fixedname; 967135446Strhodes dns_name_t *foundname; 968135446Strhodes dns_rdata_nsec_t nsec; 969135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 970135446Strhodes isc_result_t result; 971135446Strhodes unsigned int options; 972205292Sdougb isc_time_t now; 973135446Strhodes char buf1[DNS_NAME_FORMATSIZE]; 974135446Strhodes char buf2[DNS_NAME_FORMATSIZE]; 975135446Strhodes char buf3[DNS_NAME_FORMATSIZE]; 976205292Sdougb char namebuf[DNS_NAME_FORMATSIZE]; 977205292Sdougb char typebuf[DNS_RDATATYPE_FORMATSIZE]; 978135446Strhodes 979135446Strhodes if (dns_rdataset_isassociated(&val->frdataset)) 980135446Strhodes dns_rdataset_disassociate(&val->frdataset); 981135446Strhodes if (dns_rdataset_isassociated(&val->fsigrdataset)) 982135446Strhodes dns_rdataset_disassociate(&val->fsigrdataset); 983135446Strhodes 984205292Sdougb if (isc_time_now(&now) == ISC_R_SUCCESS && 985205292Sdougb dns_resolver_getbadcache(val->view->resolver, name, type, &now)) { 986205292Sdougb 987205292Sdougb dns_name_format(name, namebuf, sizeof(namebuf)); 988205292Sdougb dns_rdatatype_format(type, typebuf, sizeof(typebuf)); 989205292Sdougb validator_log(val, ISC_LOG_INFO, "bad cache hit (%s/%s)", 990205292Sdougb namebuf, typebuf); 991205292Sdougb return (DNS_R_BROKENCHAIN); 992205292Sdougb } 993205292Sdougb 994135446Strhodes options = DNS_DBFIND_PENDINGOK; 995135446Strhodes if (type == dns_rdatatype_dlv) 996135446Strhodes options |= DNS_DBFIND_COVERINGNSEC; 997135446Strhodes dns_fixedname_init(&fixedname); 998135446Strhodes foundname = dns_fixedname_name(&fixedname); 999135446Strhodes result = dns_view_find(val->view, name, type, 0, options, 1000135446Strhodes ISC_FALSE, NULL, NULL, foundname, 1001135446Strhodes &val->frdataset, &val->fsigrdataset); 1002205292Sdougb 1003135446Strhodes if (result == DNS_R_NXDOMAIN) { 1004135446Strhodes if (dns_rdataset_isassociated(&val->frdataset)) 1005135446Strhodes dns_rdataset_disassociate(&val->frdataset); 1006135446Strhodes if (dns_rdataset_isassociated(&val->fsigrdataset)) 1007135446Strhodes dns_rdataset_disassociate(&val->fsigrdataset); 1008135446Strhodes } else if (result == DNS_R_COVERINGNSEC) { 1009135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), "DNS_R_COVERINGNSEC"); 1010135446Strhodes /* 1011135446Strhodes * Check if the returned NSEC covers the name. 1012135446Strhodes */ 1013135446Strhodes INSIST(type == dns_rdatatype_dlv); 1014135446Strhodes if (val->frdataset.trust != dns_trust_secure) { 1015135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 1016222395Sdougb "covering nsec: trust %s", 1017222395Sdougb dns_trust_totext(val->frdataset.trust)); 1018135446Strhodes goto notfound; 1019135446Strhodes } 1020135446Strhodes result = dns_rdataset_first(&val->frdataset); 1021135446Strhodes if (result != ISC_R_SUCCESS) 1022135446Strhodes goto notfound; 1023135446Strhodes dns_rdataset_current(&val->frdataset, &rdata); 1024135446Strhodes if (dns_nsec_typepresent(&rdata, dns_rdatatype_ns) && 1025135446Strhodes !dns_nsec_typepresent(&rdata, dns_rdatatype_soa)) { 1026135446Strhodes /* Parent NSEC record. */ 1027135446Strhodes if (dns_name_issubdomain(name, foundname)) { 1028135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 1029135446Strhodes "covering nsec: for parent"); 1030135446Strhodes goto notfound; 1031135446Strhodes } 1032135446Strhodes } 1033135446Strhodes result = dns_rdata_tostruct(&rdata, &nsec, NULL); 1034135446Strhodes if (result != ISC_R_SUCCESS) 1035135446Strhodes goto notfound; 1036135446Strhodes if (dns_name_compare(foundname, &nsec.next) >= 0) { 1037135446Strhodes /* End of zone chain. */ 1038135446Strhodes if (!dns_name_issubdomain(name, &nsec.next)) { 1039135446Strhodes /* 1040186462Sdougb * XXXMPA We could look for a parent NSEC 1041135446Strhodes * at nsec.next and if found retest with 1042135446Strhodes * this NSEC. 1043135446Strhodes */ 1044135446Strhodes dns_rdata_freestruct(&nsec); 1045135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 1046135446Strhodes "covering nsec: not in zone"); 1047135446Strhodes goto notfound; 1048135446Strhodes } 1049135446Strhodes } else if (dns_name_compare(name, &nsec.next) >= 0) { 1050135446Strhodes /* 1051135446Strhodes * XXXMPA We could check if this NSEC is at a zone 1052135446Strhodes * apex and if the qname is not below it and look for 1053135446Strhodes * a parent NSEC with the same name. This requires 1054135446Strhodes * that we can cache both NSEC records which we 1055135446Strhodes * currently don't support. 1056135446Strhodes */ 1057135446Strhodes dns_rdata_freestruct(&nsec); 1058135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 1059135446Strhodes "covering nsec: not in range"); 1060135446Strhodes goto notfound; 1061135446Strhodes } 1062135446Strhodes if (isc_log_wouldlog(dns_lctx,ISC_LOG_DEBUG(3))) { 1063135446Strhodes dns_name_format(name, buf1, sizeof buf1); 1064135446Strhodes dns_name_format(foundname, buf2, sizeof buf2); 1065135446Strhodes dns_name_format(&nsec.next, buf3, sizeof buf3); 1066135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 1067135446Strhodes "covering nsec found: '%s' '%s' '%s'", 1068135446Strhodes buf1, buf2, buf3); 1069135446Strhodes } 1070135446Strhodes if (dns_rdataset_isassociated(&val->frdataset)) 1071135446Strhodes dns_rdataset_disassociate(&val->frdataset); 1072135446Strhodes if (dns_rdataset_isassociated(&val->fsigrdataset)) 1073135446Strhodes dns_rdataset_disassociate(&val->fsigrdataset); 1074135446Strhodes dns_rdata_freestruct(&nsec); 1075135446Strhodes result = DNS_R_NCACHENXDOMAIN; 1076135446Strhodes } else if (result != ISC_R_SUCCESS && 1077186462Sdougb result != DNS_R_NCACHENXDOMAIN && 1078186462Sdougb result != DNS_R_NCACHENXRRSET && 1079186462Sdougb result != DNS_R_EMPTYNAME && 1080186462Sdougb result != DNS_R_NXRRSET && 1081186462Sdougb result != ISC_R_NOTFOUND) { 1082135446Strhodes goto notfound; 1083135446Strhodes } 1084135446Strhodes return (result); 1085135446Strhodes 1086135446Strhodes notfound: 1087135446Strhodes if (dns_rdataset_isassociated(&val->frdataset)) 1088135446Strhodes dns_rdataset_disassociate(&val->frdataset); 1089135446Strhodes if (dns_rdataset_isassociated(&val->fsigrdataset)) 1090135446Strhodes dns_rdataset_disassociate(&val->fsigrdataset); 1091135446Strhodes return (ISC_R_NOTFOUND); 1092135446Strhodes} 1093135446Strhodes 1094165071Sdougb/*% 1095165071Sdougb * Checks to make sure we are not going to loop. As we use a SHARED fetch 1096165071Sdougb * the validation process will stall if looping was to occur. 1097165071Sdougb */ 1098135446Strhodesstatic inline isc_boolean_t 1099193149Sdougbcheck_deadlock(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type, 1100193149Sdougb dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset) 1101193149Sdougb{ 1102135446Strhodes dns_validator_t *parent; 1103135446Strhodes 1104165071Sdougb for (parent = val; parent != NULL; parent = parent->parent) { 1105135446Strhodes if (parent->event != NULL && 1106135446Strhodes parent->event->type == type && 1107193149Sdougb dns_name_equal(parent->event->name, name) && 1108193149Sdougb /* 1109193149Sdougb * As NSEC3 records are meta data you sometimes 1110193149Sdougb * need to prove a NSEC3 record which says that 1111193149Sdougb * itself doesn't exist. 1112193149Sdougb */ 1113193149Sdougb (parent->event->type != dns_rdatatype_nsec3 || 1114193149Sdougb rdataset == NULL || sigrdataset == NULL || 1115193149Sdougb parent->event->message == NULL || 1116193149Sdougb parent->event->rdataset != NULL || 1117193149Sdougb parent->event->sigrdataset != NULL)) 1118135446Strhodes { 1119135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 1120135446Strhodes "continuing validation would lead to " 1121135446Strhodes "deadlock: aborting validation"); 1122135446Strhodes return (ISC_TRUE); 1123135446Strhodes } 1124135446Strhodes } 1125135446Strhodes return (ISC_FALSE); 1126135446Strhodes} 1127135446Strhodes 1128165071Sdougb/*% 1129165071Sdougb * Start a fetch for the requested name and type. 1130165071Sdougb */ 1131135446Strhodesstatic inline isc_result_t 1132135446Strhodescreate_fetch(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type, 1133135446Strhodes isc_taskaction_t callback, const char *caller) 1134135446Strhodes{ 1135135446Strhodes if (dns_rdataset_isassociated(&val->frdataset)) 1136135446Strhodes dns_rdataset_disassociate(&val->frdataset); 1137135446Strhodes if (dns_rdataset_isassociated(&val->fsigrdataset)) 1138135446Strhodes dns_rdataset_disassociate(&val->fsigrdataset); 1139135446Strhodes 1140224092Sdougb if (check_deadlock(val, name, type, NULL, NULL)) { 1141224092Sdougb validator_log(val, ISC_LOG_DEBUG(3), 1142224092Sdougb "deadlock found (create_fetch)"); 1143135446Strhodes return (DNS_R_NOVALIDSIG); 1144224092Sdougb } 1145135446Strhodes 1146135446Strhodes validator_logcreate(val, name, type, caller, "fetch"); 1147135446Strhodes return (dns_resolver_createfetch(val->view->resolver, name, type, 1148135446Strhodes NULL, NULL, NULL, 0, 1149135446Strhodes val->event->ev_sender, 1150135446Strhodes callback, val, 1151135446Strhodes &val->frdataset, 1152135446Strhodes &val->fsigrdataset, 1153135446Strhodes &val->fetch)); 1154135446Strhodes} 1155135446Strhodes 1156165071Sdougb/*% 1157165071Sdougb * Start a subvalidation process. 1158165071Sdougb */ 1159135446Strhodesstatic inline isc_result_t 1160135446Strhodescreate_validator(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type, 1161135446Strhodes dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, 1162135446Strhodes isc_taskaction_t action, const char *caller) 1163135446Strhodes{ 1164135446Strhodes isc_result_t result; 1165135446Strhodes 1166224092Sdougb if (check_deadlock(val, name, type, rdataset, sigrdataset)) { 1167224092Sdougb validator_log(val, ISC_LOG_DEBUG(3), 1168224092Sdougb "deadlock found (create_validator)"); 1169135446Strhodes return (DNS_R_NOVALIDSIG); 1170224092Sdougb } 1171135446Strhodes 1172135446Strhodes validator_logcreate(val, name, type, caller, "validator"); 1173135446Strhodes result = dns_validator_create(val->view, name, type, 1174135446Strhodes rdataset, sigrdataset, NULL, 0, 1175135446Strhodes val->task, action, val, 1176135446Strhodes &val->subvalidator); 1177153816Sdougb if (result == ISC_R_SUCCESS) { 1178135446Strhodes val->subvalidator->parent = val; 1179153816Sdougb val->subvalidator->depth = val->depth + 1; 1180153816Sdougb } 1181135446Strhodes return (result); 1182135446Strhodes} 1183135446Strhodes 1184165071Sdougb/*% 1185135446Strhodes * Try to find a key that could have signed 'siginfo' among those 1186135446Strhodes * in 'rdataset'. If found, build a dst_key_t for it and point 1187135446Strhodes * val->key at it. 1188135446Strhodes * 1189135446Strhodes * If val->key is non-NULL, this returns the next matching key. 1190135446Strhodes */ 1191135446Strhodesstatic isc_result_t 1192135446Strhodesget_dst_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo, 1193135446Strhodes dns_rdataset_t *rdataset) 1194135446Strhodes{ 1195135446Strhodes isc_result_t result; 1196135446Strhodes isc_buffer_t b; 1197135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 1198135446Strhodes dst_key_t *oldkey = val->key; 1199135446Strhodes isc_boolean_t foundold; 1200135446Strhodes 1201135446Strhodes if (oldkey == NULL) 1202135446Strhodes foundold = ISC_TRUE; 1203135446Strhodes else { 1204135446Strhodes foundold = ISC_FALSE; 1205135446Strhodes val->key = NULL; 1206135446Strhodes } 1207135446Strhodes 1208135446Strhodes result = dns_rdataset_first(rdataset); 1209135446Strhodes if (result != ISC_R_SUCCESS) 1210135446Strhodes goto failure; 1211135446Strhodes do { 1212135446Strhodes dns_rdataset_current(rdataset, &rdata); 1213135446Strhodes 1214135446Strhodes isc_buffer_init(&b, rdata.data, rdata.length); 1215135446Strhodes isc_buffer_add(&b, rdata.length); 1216135446Strhodes INSIST(val->key == NULL); 1217135446Strhodes result = dst_key_fromdns(&siginfo->signer, rdata.rdclass, &b, 1218135446Strhodes val->view->mctx, &val->key); 1219135446Strhodes if (result != ISC_R_SUCCESS) 1220135446Strhodes goto failure; 1221135446Strhodes if (siginfo->algorithm == 1222135446Strhodes (dns_secalg_t)dst_key_alg(val->key) && 1223135446Strhodes siginfo->keyid == 1224135446Strhodes (dns_keytag_t)dst_key_id(val->key) && 1225135446Strhodes dst_key_iszonekey(val->key)) 1226135446Strhodes { 1227135446Strhodes if (foundold) 1228135446Strhodes /* 1229135446Strhodes * This is the key we're looking for. 1230135446Strhodes */ 1231135446Strhodes return (ISC_R_SUCCESS); 1232135446Strhodes else if (dst_key_compare(oldkey, val->key) == ISC_TRUE) 1233135446Strhodes { 1234135446Strhodes foundold = ISC_TRUE; 1235135446Strhodes dst_key_free(&oldkey); 1236135446Strhodes } 1237135446Strhodes } 1238135446Strhodes dst_key_free(&val->key); 1239135446Strhodes dns_rdata_reset(&rdata); 1240135446Strhodes result = dns_rdataset_next(rdataset); 1241135446Strhodes } while (result == ISC_R_SUCCESS); 1242135446Strhodes if (result == ISC_R_NOMORE) 1243135446Strhodes result = ISC_R_NOTFOUND; 1244135446Strhodes 1245135446Strhodes failure: 1246135446Strhodes if (oldkey != NULL) 1247135446Strhodes dst_key_free(&oldkey); 1248135446Strhodes 1249135446Strhodes return (result); 1250135446Strhodes} 1251135446Strhodes 1252165071Sdougb/*% 1253193149Sdougb * Get the key that generated this signature. 1254165071Sdougb */ 1255135446Strhodesstatic isc_result_t 1256135446Strhodesget_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo) { 1257135446Strhodes isc_result_t result; 1258135446Strhodes unsigned int nlabels; 1259135446Strhodes int order; 1260135446Strhodes dns_namereln_t namereln; 1261135446Strhodes 1262135446Strhodes /* 1263135446Strhodes * Is the signer name appropriate for this signature? 1264135446Strhodes * 1265135446Strhodes * The signer name must be at the same level as the owner name 1266193149Sdougb * or closer to the DNS root. 1267135446Strhodes */ 1268135446Strhodes namereln = dns_name_fullcompare(val->event->name, &siginfo->signer, 1269135446Strhodes &order, &nlabels); 1270135446Strhodes if (namereln != dns_namereln_subdomain && 1271135446Strhodes namereln != dns_namereln_equal) 1272135446Strhodes return (DNS_R_CONTINUE); 1273135446Strhodes 1274135446Strhodes if (namereln == dns_namereln_equal) { 1275135446Strhodes /* 1276135446Strhodes * If this is a self-signed keyset, it must not be a zone key 1277135446Strhodes * (since get_key is not called from validatezonekey). 1278135446Strhodes */ 1279135446Strhodes if (val->event->rdataset->type == dns_rdatatype_dnskey) 1280135446Strhodes return (DNS_R_CONTINUE); 1281135446Strhodes 1282135446Strhodes /* 1283135446Strhodes * Records appearing in the parent zone at delegation 1284135446Strhodes * points cannot be self-signed. 1285135446Strhodes */ 1286135446Strhodes if (dns_rdatatype_atparent(val->event->rdataset->type)) 1287135446Strhodes return (DNS_R_CONTINUE); 1288193149Sdougb } else { 1289193149Sdougb /* 1290193149Sdougb * SOA and NS RRsets can only be signed by a key with 1291193149Sdougb * the same name. 1292193149Sdougb */ 1293193149Sdougb if (val->event->rdataset->type == dns_rdatatype_soa || 1294193149Sdougb val->event->rdataset->type == dns_rdatatype_ns) { 1295193149Sdougb const char *typename; 1296193149Sdougb 1297193149Sdougb if (val->event->rdataset->type == dns_rdatatype_soa) 1298193149Sdougb typename = "SOA"; 1299193149Sdougb else 1300193149Sdougb typename = "NS"; 1301193149Sdougb validator_log(val, ISC_LOG_DEBUG(3), 1302193149Sdougb "%s signer mismatch", typename); 1303193149Sdougb return (DNS_R_CONTINUE); 1304193149Sdougb } 1305135446Strhodes } 1306135446Strhodes 1307135446Strhodes /* 1308135446Strhodes * Do we know about this key? 1309135446Strhodes */ 1310135446Strhodes result = view_find(val, &siginfo->signer, dns_rdatatype_dnskey); 1311135446Strhodes if (result == ISC_R_SUCCESS) { 1312135446Strhodes /* 1313135446Strhodes * We have an rrset for the given keyname. 1314135446Strhodes */ 1315135446Strhodes val->keyset = &val->frdataset; 1316214586Sdougb if ((DNS_TRUST_PENDING(val->frdataset.trust) || 1317214586Sdougb DNS_TRUST_ANSWER(val->frdataset.trust)) && 1318135446Strhodes dns_rdataset_isassociated(&val->fsigrdataset)) 1319135446Strhodes { 1320135446Strhodes /* 1321214586Sdougb * We know the key but haven't validated it yet or 1322214586Sdougb * we have a key of trust answer but a DS/DLV 1323214586Sdougb * record for the zone may have been added. 1324135446Strhodes */ 1325135446Strhodes result = create_validator(val, &siginfo->signer, 1326135446Strhodes dns_rdatatype_dnskey, 1327135446Strhodes &val->frdataset, 1328135446Strhodes &val->fsigrdataset, 1329135446Strhodes keyvalidated, 1330135446Strhodes "get_key"); 1331135446Strhodes if (result != ISC_R_SUCCESS) 1332135446Strhodes return (result); 1333135446Strhodes return (DNS_R_WAIT); 1334199958Sdougb } else if (DNS_TRUST_PENDING(val->frdataset.trust)) { 1335135446Strhodes /* 1336135446Strhodes * Having a pending key with no signature means that 1337135446Strhodes * something is broken. 1338135446Strhodes */ 1339135446Strhodes result = DNS_R_CONTINUE; 1340135446Strhodes } else if (val->frdataset.trust < dns_trust_secure) { 1341135446Strhodes /* 1342135446Strhodes * The key is legitimately insecure. There's no 1343135446Strhodes * point in even attempting verification. 1344135446Strhodes */ 1345135446Strhodes val->key = NULL; 1346135446Strhodes result = ISC_R_SUCCESS; 1347135446Strhodes } else { 1348135446Strhodes /* 1349135446Strhodes * See if we've got the key used in the signature. 1350135446Strhodes */ 1351135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 1352222395Sdougb "keyset with trust %s", 1353222395Sdougb dns_trust_totext(val->frdataset.trust)); 1354135446Strhodes result = get_dst_key(val, siginfo, val->keyset); 1355135446Strhodes if (result != ISC_R_SUCCESS) { 1356135446Strhodes /* 1357135446Strhodes * Either the key we're looking for is not 1358135446Strhodes * in the rrset, or something bad happened. 1359135446Strhodes * Give up. 1360135446Strhodes */ 1361135446Strhodes result = DNS_R_CONTINUE; 1362135446Strhodes } 1363135446Strhodes } 1364135446Strhodes } else if (result == ISC_R_NOTFOUND) { 1365135446Strhodes /* 1366135446Strhodes * We don't know anything about this key. 1367135446Strhodes */ 1368205292Sdougb result = create_fetch(val, &siginfo->signer, 1369205292Sdougb dns_rdatatype_dnskey, 1370135446Strhodes fetch_callback_validator, "get_key"); 1371135446Strhodes if (result != ISC_R_SUCCESS) 1372135446Strhodes return (result); 1373135446Strhodes return (DNS_R_WAIT); 1374135446Strhodes } else if (result == DNS_R_NCACHENXDOMAIN || 1375135446Strhodes result == DNS_R_NCACHENXRRSET || 1376186462Sdougb result == DNS_R_EMPTYNAME || 1377135446Strhodes result == DNS_R_NXDOMAIN || 1378135446Strhodes result == DNS_R_NXRRSET) 1379135446Strhodes { 1380135446Strhodes /* 1381135446Strhodes * This key doesn't exist. 1382135446Strhodes */ 1383135446Strhodes result = DNS_R_CONTINUE; 1384205292Sdougb } else if (result == DNS_R_BROKENCHAIN) 1385205292Sdougb return (result); 1386135446Strhodes 1387135446Strhodes if (dns_rdataset_isassociated(&val->frdataset) && 1388135446Strhodes val->keyset != &val->frdataset) 1389135446Strhodes dns_rdataset_disassociate(&val->frdataset); 1390135446Strhodes if (dns_rdataset_isassociated(&val->fsigrdataset)) 1391135446Strhodes dns_rdataset_disassociate(&val->fsigrdataset); 1392135446Strhodes 1393135446Strhodes return (result); 1394135446Strhodes} 1395135446Strhodes 1396135446Strhodesstatic dns_keytag_t 1397135446Strhodescompute_keytag(dns_rdata_t *rdata, dns_rdata_dnskey_t *key) { 1398135446Strhodes isc_region_t r; 1399135446Strhodes 1400135446Strhodes dns_rdata_toregion(rdata, &r); 1401135446Strhodes return (dst_region_computeid(&r, key->algorithm)); 1402135446Strhodes} 1403135446Strhodes 1404165071Sdougb/*% 1405135446Strhodes * Is this keyset self-signed? 1406135446Strhodes */ 1407135446Strhodesstatic isc_boolean_t 1408135446Strhodesisselfsigned(dns_validator_t *val) { 1409135446Strhodes dns_rdataset_t *rdataset, *sigrdataset; 1410135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 1411135446Strhodes dns_rdata_t sigrdata = DNS_RDATA_INIT; 1412135446Strhodes dns_rdata_dnskey_t key; 1413135446Strhodes dns_rdata_rrsig_t sig; 1414135446Strhodes dns_keytag_t keytag; 1415224092Sdougb dns_name_t *name; 1416135446Strhodes isc_result_t result; 1417224092Sdougb dst_key_t *dstkey; 1418224092Sdougb isc_mem_t *mctx; 1419224092Sdougb isc_boolean_t answer = ISC_FALSE; 1420135446Strhodes 1421135446Strhodes rdataset = val->event->rdataset; 1422135446Strhodes sigrdataset = val->event->sigrdataset; 1423224092Sdougb name = val->event->name; 1424224092Sdougb mctx = val->view->mctx; 1425135446Strhodes 1426254402Serwin if (rdataset->type == dns_rdatatype_cname || 1427254402Serwin rdataset->type == dns_rdatatype_dname) 1428254402Serwin return (answer); 1429254402Serwin 1430135446Strhodes INSIST(rdataset->type == dns_rdatatype_dnskey); 1431135446Strhodes 1432135446Strhodes for (result = dns_rdataset_first(rdataset); 1433135446Strhodes result == ISC_R_SUCCESS; 1434135446Strhodes result = dns_rdataset_next(rdataset)) 1435135446Strhodes { 1436135446Strhodes dns_rdata_reset(&rdata); 1437135446Strhodes dns_rdataset_current(rdataset, &rdata); 1438186462Sdougb result = dns_rdata_tostruct(&rdata, &key, NULL); 1439186462Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 1440135446Strhodes keytag = compute_keytag(&rdata, &key); 1441135446Strhodes for (result = dns_rdataset_first(sigrdataset); 1442135446Strhodes result == ISC_R_SUCCESS; 1443135446Strhodes result = dns_rdataset_next(sigrdataset)) 1444135446Strhodes { 1445135446Strhodes dns_rdata_reset(&sigrdata); 1446135446Strhodes dns_rdataset_current(sigrdataset, &sigrdata); 1447186462Sdougb result = dns_rdata_tostruct(&sigrdata, &sig, NULL); 1448186462Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 1449135446Strhodes 1450224092Sdougb if (sig.algorithm != key.algorithm || 1451224092Sdougb sig.keyid != keytag || 1452224092Sdougb !dns_name_equal(name, &sig.signer)) 1453224092Sdougb continue; 1454224092Sdougb 1455224092Sdougb dstkey = NULL; 1456224092Sdougb result = dns_dnssec_keyfromrdata(name, &rdata, mctx, 1457224092Sdougb &dstkey); 1458224092Sdougb if (result != ISC_R_SUCCESS) 1459224092Sdougb continue; 1460224092Sdougb 1461254897Serwin result = dns_dnssec_verify3(name, rdataset, dstkey, 1462254897Serwin ISC_TRUE, 1463254897Serwin val->view->maxbits, 1464285258Sdelphij mctx, &sigrdata, NULL); 1465224092Sdougb dst_key_free(&dstkey); 1466224092Sdougb if (result != ISC_R_SUCCESS) 1467224092Sdougb continue; 1468224092Sdougb if ((key.flags & DNS_KEYFLAG_REVOKE) == 0) { 1469224092Sdougb answer = ISC_TRUE; 1470224092Sdougb continue; 1471224092Sdougb } 1472224092Sdougb dns_view_untrust(val->view, name, &key, mctx); 1473135446Strhodes } 1474135446Strhodes } 1475224092Sdougb return (answer); 1476135446Strhodes} 1477135446Strhodes 1478165071Sdougb/*% 1479165071Sdougb * Attempt to verify the rdataset using the given key and rdata (RRSIG). 1480165071Sdougb * The signature was good and from a wildcard record and the QNAME does 1481165071Sdougb * not match the wildcard we need to look for a NOQNAME proof. 1482165071Sdougb * 1483165071Sdougb * Returns: 1484165071Sdougb * \li ISC_R_SUCCESS if the verification succeeds. 1485165071Sdougb * \li Others if the verification fails. 1486165071Sdougb */ 1487135446Strhodesstatic isc_result_t 1488165071Sdougbverify(dns_validator_t *val, dst_key_t *key, dns_rdata_t *rdata, 1489165071Sdougb isc_uint16_t keyid) 1490165071Sdougb{ 1491135446Strhodes isc_result_t result; 1492135446Strhodes dns_fixedname_t fixed; 1493170222Sdougb isc_boolean_t ignore = ISC_FALSE; 1494234010Sdougb dns_name_t *wild; 1495135446Strhodes 1496135446Strhodes val->attributes |= VALATTR_TRIEDVERIFY; 1497135446Strhodes dns_fixedname_init(&fixed); 1498234010Sdougb wild = dns_fixedname_name(&fixed); 1499170222Sdougb again: 1500254897Serwin result = dns_dnssec_verify3(val->event->name, val->event->rdataset, 1501254897Serwin key, ignore, val->view->maxbits, 1502254897Serwin val->view->mctx, rdata, wild); 1503234010Sdougb if ((result == DNS_R_SIGEXPIRED || result == DNS_R_SIGFUTURE) && 1504234010Sdougb val->view->acceptexpired) 1505234010Sdougb { 1506170222Sdougb ignore = ISC_TRUE; 1507170222Sdougb goto again; 1508170222Sdougb } 1509170222Sdougb if (ignore && (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD)) 1510170222Sdougb validator_log(val, ISC_LOG_INFO, 1511170222Sdougb "accepted expired %sRRSIG (keyid=%u)", 1512170222Sdougb (result == DNS_R_FROMWILDCARD) ? 1513170222Sdougb "wildcard " : "", keyid); 1514234010Sdougb else if (result == DNS_R_SIGEXPIRED || result == DNS_R_SIGFUTURE) 1515234010Sdougb validator_log(val, ISC_LOG_INFO, 1516234010Sdougb "verify failed due to bad signature (keyid=%u): " 1517234010Sdougb "%s", keyid, isc_result_totext(result)); 1518170222Sdougb else 1519170222Sdougb validator_log(val, ISC_LOG_DEBUG(3), 1520170222Sdougb "verify rdataset (keyid=%u): %s", 1521170222Sdougb keyid, isc_result_totext(result)); 1522135446Strhodes if (result == DNS_R_FROMWILDCARD) { 1523234010Sdougb if (!dns_name_equal(val->event->name, wild)) { 1524234010Sdougb dns_name_t *closest; 1525234010Sdougb unsigned int labels; 1526234010Sdougb 1527234010Sdougb /* 1528234010Sdougb * Compute the closest encloser in case we need it 1529234010Sdougb * for the NSEC3 NOQNAME proof. 1530234010Sdougb */ 1531234010Sdougb closest = dns_fixedname_name(&val->closest); 1532234010Sdougb dns_name_copy(wild, closest, NULL); 1533234010Sdougb labels = dns_name_countlabels(closest) - 1; 1534234010Sdougb dns_name_getlabelsequence(closest, 1, labels, closest); 1535135446Strhodes val->attributes |= VALATTR_NEEDNOQNAME; 1536234010Sdougb } 1537135446Strhodes result = ISC_R_SUCCESS; 1538135446Strhodes } 1539135446Strhodes return (result); 1540135446Strhodes} 1541135446Strhodes 1542165071Sdougb/*% 1543135446Strhodes * Attempts positive response validation of a normal RRset. 1544135446Strhodes * 1545135446Strhodes * Returns: 1546165071Sdougb * \li ISC_R_SUCCESS Validation completed successfully 1547165071Sdougb * \li DNS_R_WAIT Validation has started but is waiting 1548135446Strhodes * for an event. 1549165071Sdougb * \li Other return codes are possible and all indicate failure. 1550135446Strhodes */ 1551135446Strhodesstatic isc_result_t 1552135446Strhodesvalidate(dns_validator_t *val, isc_boolean_t resume) { 1553135446Strhodes isc_result_t result; 1554135446Strhodes dns_validatorevent_t *event; 1555135446Strhodes dns_rdata_t rdata = DNS_RDATA_INIT; 1556135446Strhodes 1557135446Strhodes /* 1558135446Strhodes * Caller must be holding the validator lock. 1559135446Strhodes */ 1560135446Strhodes 1561135446Strhodes event = val->event; 1562135446Strhodes 1563135446Strhodes if (resume) { 1564135446Strhodes /* 1565135446Strhodes * We already have a sigrdataset. 1566135446Strhodes */ 1567135446Strhodes result = ISC_R_SUCCESS; 1568135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), "resuming validate"); 1569135446Strhodes } else { 1570135446Strhodes result = dns_rdataset_first(event->sigrdataset); 1571135446Strhodes } 1572135446Strhodes 1573135446Strhodes for (; 1574135446Strhodes result == ISC_R_SUCCESS; 1575135446Strhodes result = dns_rdataset_next(event->sigrdataset)) 1576135446Strhodes { 1577135446Strhodes dns_rdata_reset(&rdata); 1578135446Strhodes dns_rdataset_current(event->sigrdataset, &rdata); 1579135446Strhodes if (val->siginfo == NULL) { 1580135446Strhodes val->siginfo = isc_mem_get(val->view->mctx, 1581135446Strhodes sizeof(*val->siginfo)); 1582135446Strhodes if (val->siginfo == NULL) 1583135446Strhodes return (ISC_R_NOMEMORY); 1584135446Strhodes } 1585135446Strhodes result = dns_rdata_tostruct(&rdata, val->siginfo, NULL); 1586135446Strhodes if (result != ISC_R_SUCCESS) 1587135446Strhodes return (result); 1588135446Strhodes 1589135446Strhodes /* 1590135446Strhodes * At this point we could check that the signature algorithm 1591135446Strhodes * was known and "sufficiently good". 1592135446Strhodes */ 1593135446Strhodes if (!dns_resolver_algorithm_supported(val->view->resolver, 1594216175Sdougb event->name, 1595216175Sdougb val->siginfo->algorithm)) { 1596216175Sdougb resume = ISC_FALSE; 1597135446Strhodes continue; 1598216175Sdougb } 1599135446Strhodes 1600135446Strhodes if (!resume) { 1601135446Strhodes result = get_key(val, val->siginfo); 1602135446Strhodes if (result == DNS_R_CONTINUE) 1603135446Strhodes continue; /* Try the next SIG RR. */ 1604135446Strhodes if (result != ISC_R_SUCCESS) 1605135446Strhodes return (result); 1606135446Strhodes } 1607135446Strhodes 1608135446Strhodes /* 1609216175Sdougb * There isn't a secure DNSKEY for this signature so move 1610216175Sdougb * onto the next RRSIG. 1611135446Strhodes */ 1612135446Strhodes if (val->key == NULL) { 1613216175Sdougb resume = ISC_FALSE; 1614216175Sdougb continue; 1615135446Strhodes } 1616135446Strhodes 1617135446Strhodes do { 1618165071Sdougb result = verify(val, val->key, &rdata, 1619165071Sdougb val->siginfo->keyid); 1620135446Strhodes if (result == ISC_R_SUCCESS) 1621135446Strhodes break; 1622135446Strhodes if (val->keynode != NULL) { 1623135446Strhodes dns_keynode_t *nextnode = NULL; 1624135446Strhodes result = dns_keytable_findnextkeynode( 1625135446Strhodes val->keytable, 1626135446Strhodes val->keynode, 1627135446Strhodes &nextnode); 1628135446Strhodes dns_keytable_detachkeynode(val->keytable, 1629135446Strhodes &val->keynode); 1630135446Strhodes val->keynode = nextnode; 1631135446Strhodes if (result != ISC_R_SUCCESS) { 1632135446Strhodes val->key = NULL; 1633135446Strhodes break; 1634135446Strhodes } 1635135446Strhodes val->key = dns_keynode_key(val->keynode); 1636224092Sdougb if (val->key == NULL) 1637224092Sdougb break; 1638135446Strhodes } else { 1639135446Strhodes if (get_dst_key(val, val->siginfo, val->keyset) 1640135446Strhodes != ISC_R_SUCCESS) 1641135446Strhodes break; 1642135446Strhodes } 1643135446Strhodes } while (1); 1644135446Strhodes if (result != ISC_R_SUCCESS) 1645135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 1646135446Strhodes "failed to verify rdataset"); 1647135446Strhodes else { 1648135446Strhodes isc_stdtime_t now; 1649135446Strhodes 1650135446Strhodes isc_stdtime_get(&now); 1651245163Serwin dns_rdataset_trimttl(event->rdataset, 1652245163Serwin event->sigrdataset, 1653245163Serwin val->siginfo, now, 1654245163Serwin val->view->acceptexpired); 1655135446Strhodes } 1656135446Strhodes 1657135446Strhodes if (val->keynode != NULL) 1658135446Strhodes dns_keytable_detachkeynode(val->keytable, 1659135446Strhodes &val->keynode); 1660135446Strhodes else { 1661135446Strhodes if (val->key != NULL) 1662135446Strhodes dst_key_free(&val->key); 1663135446Strhodes if (val->keyset != NULL) { 1664135446Strhodes dns_rdataset_disassociate(val->keyset); 1665135446Strhodes val->keyset = NULL; 1666135446Strhodes } 1667135446Strhodes } 1668135446Strhodes val->key = NULL; 1669214586Sdougb if (NEEDNOQNAME(val)) { 1670135446Strhodes if (val->event->message == NULL) { 1671135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 1672135446Strhodes "no message available for noqname proof"); 1673135446Strhodes return (DNS_R_NOVALIDSIG); 1674135446Strhodes } 1675135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 1676135446Strhodes "looking for noqname proof"); 1677135446Strhodes return (nsecvalidate(val, ISC_FALSE)); 1678135446Strhodes } else if (result == ISC_R_SUCCESS) { 1679205292Sdougb marksecure(event); 1680135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 1681224092Sdougb "marking as secure, " 1682224092Sdougb "noqname proof not needed"); 1683135446Strhodes return (result); 1684135446Strhodes } else { 1685135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 1686135446Strhodes "verify failure: %s", 1687135446Strhodes isc_result_totext(result)); 1688135446Strhodes resume = ISC_FALSE; 1689135446Strhodes } 1690135446Strhodes } 1691135446Strhodes if (result != ISC_R_NOMORE) { 1692135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 1693135446Strhodes "failed to iterate signatures: %s", 1694135446Strhodes isc_result_totext(result)); 1695135446Strhodes return (result); 1696135446Strhodes } 1697135446Strhodes 1698135446Strhodes validator_log(val, ISC_LOG_INFO, "no valid signature found"); 1699135446Strhodes return (DNS_R_NOVALIDSIG); 1700135446Strhodes} 1701135446Strhodes 1702165071Sdougb/*% 1703224092Sdougb * Check whether this DNSKEY (keyrdata) signed the DNSKEY RRset 1704224092Sdougb * (val->event->rdataset). 1705224092Sdougb */ 1706224092Sdougbstatic isc_result_t 1707224092Sdougbcheckkey(dns_validator_t *val, dns_rdata_t *keyrdata, isc_uint16_t keyid, 1708224092Sdougb dns_secalg_t algorithm) 1709224092Sdougb{ 1710224092Sdougb dns_rdata_rrsig_t sig; 1711224092Sdougb dst_key_t *dstkey = NULL; 1712224092Sdougb isc_result_t result; 1713224092Sdougb 1714224092Sdougb for (result = dns_rdataset_first(val->event->sigrdataset); 1715224092Sdougb result == ISC_R_SUCCESS; 1716224092Sdougb result = dns_rdataset_next(val->event->sigrdataset)) 1717224092Sdougb { 1718224092Sdougb dns_rdata_t rdata = DNS_RDATA_INIT; 1719224092Sdougb 1720224092Sdougb dns_rdataset_current(val->event->sigrdataset, &rdata); 1721224092Sdougb result = dns_rdata_tostruct(&rdata, &sig, NULL); 1722224092Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 1723224092Sdougb if (keyid != sig.keyid || algorithm != sig.algorithm) 1724224092Sdougb continue; 1725224092Sdougb if (dstkey == NULL) { 1726224092Sdougb result = dns_dnssec_keyfromrdata(val->event->name, 1727224092Sdougb keyrdata, 1728224092Sdougb val->view->mctx, 1729224092Sdougb &dstkey); 1730224092Sdougb if (result != ISC_R_SUCCESS) 1731224092Sdougb /* 1732224092Sdougb * This really shouldn't happen, but... 1733224092Sdougb */ 1734224092Sdougb continue; 1735224092Sdougb } 1736224092Sdougb result = verify(val, dstkey, &rdata, sig.keyid); 1737224092Sdougb if (result == ISC_R_SUCCESS) 1738224092Sdougb break; 1739224092Sdougb } 1740224092Sdougb if (dstkey != NULL) 1741224092Sdougb dst_key_free(&dstkey); 1742224092Sdougb return (result); 1743224092Sdougb} 1744224092Sdougb 1745224092Sdougb/*% 1746224092Sdougb * Find the DNSKEY that corresponds to the DS. 1747224092Sdougb */ 1748224092Sdougbstatic isc_result_t 1749224092Sdougbkeyfromds(dns_validator_t *val, dns_rdataset_t *rdataset, dns_rdata_t *dsrdata, 1750224092Sdougb isc_uint8_t digest, isc_uint16_t keyid, dns_secalg_t algorithm, 1751224092Sdougb dns_rdata_t *keyrdata) 1752224092Sdougb{ 1753224092Sdougb dns_keytag_t keytag; 1754224092Sdougb dns_rdata_dnskey_t key; 1755224092Sdougb isc_result_t result; 1756224092Sdougb unsigned char dsbuf[DNS_DS_BUFFERSIZE]; 1757224092Sdougb 1758224092Sdougb for (result = dns_rdataset_first(rdataset); 1759224092Sdougb result == ISC_R_SUCCESS; 1760224092Sdougb result = dns_rdataset_next(rdataset)) 1761224092Sdougb { 1762224092Sdougb dns_rdata_t newdsrdata = DNS_RDATA_INIT; 1763224092Sdougb 1764224092Sdougb dns_rdata_reset(keyrdata); 1765224092Sdougb dns_rdataset_current(rdataset, keyrdata); 1766224092Sdougb result = dns_rdata_tostruct(keyrdata, &key, NULL); 1767224092Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 1768224092Sdougb keytag = compute_keytag(keyrdata, &key); 1769224092Sdougb if (keyid != keytag || algorithm != key.algorithm) 1770224092Sdougb continue; 1771224092Sdougb dns_rdata_reset(&newdsrdata); 1772224092Sdougb result = dns_ds_buildrdata(val->event->name, keyrdata, digest, 1773224092Sdougb dsbuf, &newdsrdata); 1774224092Sdougb if (result != ISC_R_SUCCESS) { 1775224092Sdougb validator_log(val, ISC_LOG_DEBUG(3), 1776224092Sdougb "dns_ds_buildrdata() -> %s", 1777224092Sdougb dns_result_totext(result)); 1778224092Sdougb continue; 1779224092Sdougb } 1780224092Sdougb if (dns_rdata_compare(dsrdata, &newdsrdata) == 0) 1781224092Sdougb break; 1782224092Sdougb } 1783224092Sdougb return (result); 1784224092Sdougb} 1785224092Sdougb 1786224092Sdougb/*% 1787165071Sdougb * Validate the DNSKEY RRset by looking for a DNSKEY that matches a 1788165071Sdougb * DLV record and that also verifies the DNSKEY RRset. 1789165071Sdougb */ 1790135446Strhodesstatic isc_result_t 1791135446Strhodesdlv_validatezonekey(dns_validator_t *val) { 1792135446Strhodes dns_rdata_dlv_t dlv; 1793135446Strhodes dns_rdata_t dlvrdata = DNS_RDATA_INIT; 1794135446Strhodes dns_rdata_t keyrdata = DNS_RDATA_INIT; 1795135446Strhodes dns_rdataset_t trdataset; 1796135446Strhodes isc_boolean_t supported_algorithm; 1797135446Strhodes isc_result_t result; 1798224092Sdougb char digest_types[256]; 1799135446Strhodes 1800153816Sdougb validator_log(val, ISC_LOG_DEBUG(3), "dlv_validatezonekey"); 1801165071Sdougb 1802135446Strhodes /* 1803135446Strhodes * Look through the DLV record and find the keys that can sign the 1804135446Strhodes * key set and the matching signature. For each such key, attempt 1805135446Strhodes * verification. 1806135446Strhodes */ 1807135446Strhodes supported_algorithm = ISC_FALSE; 1808135446Strhodes 1809170222Sdougb /* 1810170222Sdougb * If DNS_DSDIGEST_SHA256 is present we are required to prefer 1811170222Sdougb * it over DNS_DSDIGEST_SHA1. This in practice means that we 1812170222Sdougb * need to ignore DNS_DSDIGEST_SHA1 if a DNS_DSDIGEST_SHA256 1813170222Sdougb * is present. 1814170222Sdougb */ 1815224092Sdougb memset(digest_types, 1, sizeof(digest_types)); 1816153816Sdougb for (result = dns_rdataset_first(&val->dlv); 1817135446Strhodes result == ISC_R_SUCCESS; 1818170222Sdougb result = dns_rdataset_next(&val->dlv)) { 1819170222Sdougb dns_rdata_reset(&dlvrdata); 1820170222Sdougb dns_rdataset_current(&val->dlv, &dlvrdata); 1821186462Sdougb result = dns_rdata_tostruct(&dlvrdata, &dlv, NULL); 1822186462Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 1823170222Sdougb 1824170222Sdougb if (!dns_resolver_algorithm_supported(val->view->resolver, 1825170222Sdougb val->event->name, 1826170222Sdougb dlv.algorithm)) 1827170222Sdougb continue; 1828170222Sdougb 1829174187Sdougb if (dlv.digest_type == DNS_DSDIGEST_SHA256 && 1830174187Sdougb dlv.length == ISC_SHA256_DIGESTLENGTH) { 1831224092Sdougb digest_types[DNS_DSDIGEST_SHA1] = 0; 1832170222Sdougb break; 1833170222Sdougb } 1834170222Sdougb } 1835170222Sdougb 1836170222Sdougb for (result = dns_rdataset_first(&val->dlv); 1837170222Sdougb result == ISC_R_SUCCESS; 1838153816Sdougb result = dns_rdataset_next(&val->dlv)) 1839135446Strhodes { 1840135446Strhodes dns_rdata_reset(&dlvrdata); 1841153816Sdougb dns_rdataset_current(&val->dlv, &dlvrdata); 1842186462Sdougb result = dns_rdata_tostruct(&dlvrdata, &dlv, NULL); 1843186462Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 1844135446Strhodes 1845170222Sdougb if (!dns_resolver_digest_supported(val->view->resolver, 1846170222Sdougb dlv.digest_type)) 1847170222Sdougb continue; 1848186462Sdougb 1849224092Sdougb if (digest_types[dlv.digest_type] == 0) 1850170222Sdougb continue; 1851170222Sdougb 1852170222Sdougb if (!dns_resolver_algorithm_supported(val->view->resolver, 1853135446Strhodes val->event->name, 1854135446Strhodes dlv.algorithm)) 1855135446Strhodes continue; 1856135446Strhodes 1857135446Strhodes supported_algorithm = ISC_TRUE; 1858135446Strhodes 1859135446Strhodes dns_rdataset_init(&trdataset); 1860135446Strhodes dns_rdataset_clone(val->event->rdataset, &trdataset); 1861135446Strhodes 1862224092Sdougb /* 1863224092Sdougb * Convert to DLV to DS and find matching DNSKEY. 1864224092Sdougb */ 1865224092Sdougb dlvrdata.type = dns_rdatatype_ds; 1866224092Sdougb result = keyfromds(val, &trdataset, &dlvrdata, 1867224092Sdougb dlv.digest_type, dlv.key_tag, 1868224092Sdougb dlv.algorithm, &keyrdata); 1869135446Strhodes if (result != ISC_R_SUCCESS) { 1870193149Sdougb dns_rdataset_disassociate(&trdataset); 1871135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 1872135446Strhodes "no DNSKEY matching DLV"); 1873135446Strhodes continue; 1874135446Strhodes } 1875224092Sdougb 1876153816Sdougb validator_log(val, ISC_LOG_DEBUG(3), 1877153816Sdougb "Found matching DLV record: checking for signature"); 1878224092Sdougb /* 1879224092Sdougb * Check that this DNSKEY signed the DNSKEY rrset. 1880224092Sdougb */ 1881224092Sdougb result = checkkey(val, &keyrdata, dlv.key_tag, dlv.algorithm); 1882153816Sdougb 1883135446Strhodes dns_rdataset_disassociate(&trdataset); 1884135446Strhodes if (result == ISC_R_SUCCESS) 1885135446Strhodes break; 1886135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 1887135446Strhodes "no RRSIG matching DLV key"); 1888135446Strhodes } 1889135446Strhodes if (result == ISC_R_SUCCESS) { 1890205292Sdougb marksecure(val->event); 1891224092Sdougb validator_log(val, ISC_LOG_DEBUG(3), "marking as secure (dlv)"); 1892135446Strhodes return (result); 1893135446Strhodes } else if (result == ISC_R_NOMORE && !supported_algorithm) { 1894135446Strhodes if (val->mustbesecure) { 1895135446Strhodes validator_log(val, ISC_LOG_WARNING, 1896224092Sdougb "must be secure failure," 1897224092Sdougb "no supported algorithm/digest (dlv)"); 1898135446Strhodes return (DNS_R_MUSTBESECURE); 1899135446Strhodes } 1900135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 1901153816Sdougb "no supported algorithm/digest (dlv)"); 1902214586Sdougb markanswer(val, "dlv_validatezonekey (2)"); 1903135446Strhodes return (ISC_R_SUCCESS); 1904135446Strhodes } else 1905135446Strhodes return (DNS_R_NOVALIDSIG); 1906135446Strhodes} 1907135446Strhodes 1908165071Sdougb/*% 1909224092Sdougb * Attempts positive response validation of an RRset containing zone keys 1910224092Sdougb * (i.e. a DNSKEY rrset). 1911135446Strhodes * 1912135446Strhodes * Returns: 1913165071Sdougb * \li ISC_R_SUCCESS Validation completed successfully 1914165071Sdougb * \li DNS_R_WAIT Validation has started but is waiting 1915135446Strhodes * for an event. 1916165071Sdougb * \li Other return codes are possible and all indicate failure. 1917135446Strhodes */ 1918135446Strhodesstatic isc_result_t 1919135446Strhodesvalidatezonekey(dns_validator_t *val) { 1920135446Strhodes isc_result_t result; 1921135446Strhodes dns_validatorevent_t *event; 1922135446Strhodes dns_rdataset_t trdataset; 1923135446Strhodes dns_rdata_t dsrdata = DNS_RDATA_INIT; 1924135446Strhodes dns_rdata_t keyrdata = DNS_RDATA_INIT; 1925135446Strhodes dns_rdata_t sigrdata = DNS_RDATA_INIT; 1926165071Sdougb char namebuf[DNS_NAME_FORMATSIZE]; 1927135446Strhodes dns_rdata_ds_t ds; 1928135446Strhodes dns_rdata_rrsig_t sig; 1929135446Strhodes dst_key_t *dstkey; 1930135446Strhodes isc_boolean_t supported_algorithm; 1931165071Sdougb isc_boolean_t atsep = ISC_FALSE; 1932224092Sdougb char digest_types[256]; 1933135446Strhodes 1934135446Strhodes /* 1935135446Strhodes * Caller must be holding the validator lock. 1936135446Strhodes */ 1937135446Strhodes 1938135446Strhodes event = val->event; 1939135446Strhodes 1940153816Sdougb if (val->havedlvsep && val->dlv.trust >= dns_trust_secure && 1941153816Sdougb dns_name_equal(event->name, dns_fixedname_name(&val->dlvsep))) 1942153816Sdougb return (dlv_validatezonekey(val)); 1943153816Sdougb 1944135446Strhodes if (val->dsset == NULL) { 1945218384Sdougb 1946135446Strhodes /* 1947218384Sdougb * We have a dlv sep. Skip looking up the SEP from 1948218384Sdougb * {trusted,managed}-keys. If the dlv sep is for the 1949218384Sdougb * root then it will have been handled above so we don't 1950218384Sdougb * need to check whether val->event->name is "." prior to 1951218384Sdougb * looking up the DS. 1952218384Sdougb */ 1953218384Sdougb if (val->havedlvsep) 1954218384Sdougb goto find_ds; 1955218384Sdougb 1956218384Sdougb /* 1957135446Strhodes * First, see if this key was signed by a trusted key. 1958135446Strhodes */ 1959135446Strhodes for (result = dns_rdataset_first(val->event->sigrdataset); 1960135446Strhodes result == ISC_R_SUCCESS; 1961135446Strhodes result = dns_rdataset_next(val->event->sigrdataset)) 1962135446Strhodes { 1963214586Sdougb dns_keynode_t *keynode = NULL; 1964214586Sdougb dns_fixedname_t fixed; 1965214586Sdougb dns_name_t *found; 1966135446Strhodes 1967214586Sdougb dns_fixedname_init(&fixed); 1968214586Sdougb found = dns_fixedname_name(&fixed); 1969135446Strhodes dns_rdata_reset(&sigrdata); 1970135446Strhodes dns_rdataset_current(val->event->sigrdataset, 1971135446Strhodes &sigrdata); 1972186462Sdougb result = dns_rdata_tostruct(&sigrdata, &sig, NULL); 1973186462Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 1974193149Sdougb 1975193149Sdougb if (!dns_name_equal(val->event->name, &sig.signer)) 1976193149Sdougb continue; 1977193149Sdougb 1978135446Strhodes result = dns_keytable_findkeynode(val->keytable, 1979135446Strhodes val->event->name, 1980135446Strhodes sig.algorithm, 1981224092Sdougb sig.keyid, &keynode); 1982214586Sdougb if (result == ISC_R_NOTFOUND && 1983214586Sdougb dns_keytable_finddeepestmatch(val->keytable, 1984214586Sdougb val->event->name, found) != ISC_R_SUCCESS) { 1985214586Sdougb if (val->mustbesecure) { 1986214586Sdougb validator_log(val, ISC_LOG_WARNING, 1987218384Sdougb "must be secure failure, " 1988218384Sdougb "not beneath secure root"); 1989214586Sdougb return (DNS_R_MUSTBESECURE); 1990214586Sdougb } else 1991214586Sdougb validator_log(val, ISC_LOG_DEBUG(3), 1992218384Sdougb "not beneath secure root"); 1993218384Sdougb if (val->view->dlv == NULL) { 1994214586Sdougb markanswer(val, "validatezonekey (1)"); 1995214586Sdougb return (ISC_R_SUCCESS); 1996214586Sdougb } 1997214586Sdougb return (startfinddlvsep(val, dns_rootname)); 1998214586Sdougb } 1999165071Sdougb if (result == DNS_R_PARTIALMATCH || 2000165071Sdougb result == ISC_R_SUCCESS) 2001165071Sdougb atsep = ISC_TRUE; 2002135446Strhodes while (result == ISC_R_SUCCESS) { 2003214586Sdougb dns_keynode_t *nextnode = NULL; 2004135446Strhodes dstkey = dns_keynode_key(keynode); 2005224092Sdougb if (dstkey == NULL) { 2006224092Sdougb dns_keytable_detachkeynode( 2007224092Sdougb val->keytable, 2008224092Sdougb &keynode); 2009224092Sdougb break; 2010224092Sdougb } 2011165071Sdougb result = verify(val, dstkey, &sigrdata, 2012165071Sdougb sig.keyid); 2013135446Strhodes if (result == ISC_R_SUCCESS) { 2014224092Sdougb dns_keytable_detachkeynode( 2015224092Sdougb val->keytable, 2016224092Sdougb &keynode); 2017135446Strhodes break; 2018135446Strhodes } 2019135446Strhodes result = dns_keytable_findnextkeynode( 2020135446Strhodes val->keytable, 2021135446Strhodes keynode, 2022135446Strhodes &nextnode); 2023135446Strhodes dns_keytable_detachkeynode(val->keytable, 2024135446Strhodes &keynode); 2025135446Strhodes keynode = nextnode; 2026135446Strhodes } 2027135446Strhodes if (result == ISC_R_SUCCESS) { 2028205292Sdougb marksecure(event); 2029135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 2030135446Strhodes "signed by trusted key; " 2031135446Strhodes "marking as secure"); 2032135446Strhodes return (result); 2033135446Strhodes } 2034135446Strhodes } 2035135446Strhodes 2036165071Sdougb if (atsep) { 2037165071Sdougb /* 2038165071Sdougb * We have not found a key to verify this DNSKEY 2039165071Sdougb * RRset. As this is a SEP we have to assume that 2040165071Sdougb * the RRset is invalid. 2041165071Sdougb */ 2042165071Sdougb dns_name_format(val->event->name, namebuf, 2043186462Sdougb sizeof(namebuf)); 2044205292Sdougb validator_log(val, ISC_LOG_NOTICE, 2045165071Sdougb "unable to find a DNSKEY which verifies " 2046224092Sdougb "the DNSKEY RRset and also matches a " 2047224092Sdougb "trusted key for '%s'", 2048165071Sdougb namebuf); 2049205292Sdougb validator_log(val, ISC_LOG_NOTICE, 2050205292Sdougb "please check the 'trusted-keys' for " 2051205292Sdougb "'%s' in named.conf.", namebuf); 2052165071Sdougb return (DNS_R_NOVALIDKEY); 2053165071Sdougb } 2054165071Sdougb 2055135446Strhodes /* 2056218384Sdougb * If this is the root name and there was no trusted key, 2057218384Sdougb * give up, since there's no DS at the root. 2058218384Sdougb */ 2059218384Sdougb if (dns_name_equal(event->name, dns_rootname)) { 2060218384Sdougb if ((val->attributes & VALATTR_TRIEDVERIFY) != 0) { 2061218384Sdougb validator_log(val, ISC_LOG_DEBUG(3), 2062218384Sdougb "root key failed to validate"); 2063218384Sdougb return (DNS_R_NOVALIDSIG); 2064218384Sdougb } else { 2065218384Sdougb validator_log(val, ISC_LOG_DEBUG(3), 2066218384Sdougb "no trusted root key"); 2067218384Sdougb return (DNS_R_NOVALIDDS); 2068218384Sdougb } 2069218384Sdougb } 2070218384Sdougb find_ds: 2071218384Sdougb /* 2072135446Strhodes * Otherwise, try to find the DS record. 2073135446Strhodes */ 2074135446Strhodes result = view_find(val, val->event->name, dns_rdatatype_ds); 2075135446Strhodes if (result == ISC_R_SUCCESS) { 2076135446Strhodes /* 2077135446Strhodes * We have DS records. 2078135446Strhodes */ 2079135446Strhodes val->dsset = &val->frdataset; 2080214586Sdougb if ((DNS_TRUST_PENDING(val->frdataset.trust) || 2081214586Sdougb DNS_TRUST_ANSWER(val->frdataset.trust)) && 2082135446Strhodes dns_rdataset_isassociated(&val->fsigrdataset)) 2083135446Strhodes { 2084135446Strhodes result = create_validator(val, 2085135446Strhodes val->event->name, 2086135446Strhodes dns_rdatatype_ds, 2087135446Strhodes &val->frdataset, 2088135446Strhodes &val->fsigrdataset, 2089135446Strhodes dsvalidated, 2090135446Strhodes "validatezonekey"); 2091135446Strhodes if (result != ISC_R_SUCCESS) 2092135446Strhodes return (result); 2093135446Strhodes return (DNS_R_WAIT); 2094199958Sdougb } else if (DNS_TRUST_PENDING(val->frdataset.trust)) { 2095135446Strhodes /* 2096135446Strhodes * There should never be an unsigned DS. 2097135446Strhodes */ 2098135446Strhodes dns_rdataset_disassociate(&val->frdataset); 2099135446Strhodes validator_log(val, ISC_LOG_DEBUG(2), 2100135446Strhodes "unsigned DS record"); 2101135446Strhodes return (DNS_R_NOVALIDSIG); 2102225361Sdougb } else { 2103135446Strhodes result = ISC_R_SUCCESS; 2104225361Sdougb POST(result); 2105225361Sdougb } 2106135446Strhodes } else if (result == ISC_R_NOTFOUND) { 2107135446Strhodes /* 2108135446Strhodes * We don't have the DS. Find it. 2109135446Strhodes */ 2110135446Strhodes result = create_fetch(val, val->event->name, 2111135446Strhodes dns_rdatatype_ds, dsfetched, 2112135446Strhodes "validatezonekey"); 2113135446Strhodes if (result != ISC_R_SUCCESS) 2114135446Strhodes return (result); 2115135446Strhodes return (DNS_R_WAIT); 2116225361Sdougb } else if (result == DNS_R_NCACHENXDOMAIN || 2117135446Strhodes result == DNS_R_NCACHENXRRSET || 2118186462Sdougb result == DNS_R_EMPTYNAME || 2119135446Strhodes result == DNS_R_NXDOMAIN || 2120225361Sdougb result == DNS_R_NXRRSET || 2121225361Sdougb result == DNS_R_CNAME) 2122135446Strhodes { 2123135446Strhodes /* 2124135446Strhodes * The DS does not exist. 2125135446Strhodes */ 2126135446Strhodes if (dns_rdataset_isassociated(&val->frdataset)) 2127135446Strhodes dns_rdataset_disassociate(&val->frdataset); 2128135446Strhodes if (dns_rdataset_isassociated(&val->fsigrdataset)) 2129135446Strhodes dns_rdataset_disassociate(&val->fsigrdataset); 2130135446Strhodes validator_log(val, ISC_LOG_DEBUG(2), "no DS record"); 2131135446Strhodes return (DNS_R_NOVALIDSIG); 2132205292Sdougb } else if (result == DNS_R_BROKENCHAIN) 2133205292Sdougb return (result); 2134135446Strhodes } 2135135446Strhodes 2136135446Strhodes /* 2137135446Strhodes * We have a DS set. 2138135446Strhodes */ 2139135446Strhodes INSIST(val->dsset != NULL); 2140135446Strhodes 2141135446Strhodes if (val->dsset->trust < dns_trust_secure) { 2142135446Strhodes if (val->mustbesecure) { 2143135446Strhodes validator_log(val, ISC_LOG_WARNING, 2144224092Sdougb "must be secure failure," 2145224092Sdougb " insecure DS"); 2146135446Strhodes return (DNS_R_MUSTBESECURE); 2147135446Strhodes } 2148222395Sdougb if (val->view->dlv == NULL || DLVTRIED(val)) { 2149222395Sdougb markanswer(val, "validatezonekey (2)"); 2150222395Sdougb return (ISC_R_SUCCESS); 2151222395Sdougb } 2152222395Sdougb return (startfinddlvsep(val, val->event->name)); 2153135446Strhodes } 2154135446Strhodes 2155135446Strhodes /* 2156135446Strhodes * Look through the DS record and find the keys that can sign the 2157135446Strhodes * key set and the matching signature. For each such key, attempt 2158135446Strhodes * verification. 2159135446Strhodes */ 2160135446Strhodes 2161135446Strhodes supported_algorithm = ISC_FALSE; 2162135446Strhodes 2163170222Sdougb /* 2164170222Sdougb * If DNS_DSDIGEST_SHA256 is present we are required to prefer 2165170222Sdougb * it over DNS_DSDIGEST_SHA1. This in practice means that we 2166170222Sdougb * need to ignore DNS_DSDIGEST_SHA1 if a DNS_DSDIGEST_SHA256 2167170222Sdougb * is present. 2168170222Sdougb */ 2169224092Sdougb memset(digest_types, 1, sizeof(digest_types)); 2170135446Strhodes for (result = dns_rdataset_first(val->dsset); 2171135446Strhodes result == ISC_R_SUCCESS; 2172170222Sdougb result = dns_rdataset_next(val->dsset)) { 2173170222Sdougb dns_rdata_reset(&dsrdata); 2174170222Sdougb dns_rdataset_current(val->dsset, &dsrdata); 2175186462Sdougb result = dns_rdata_tostruct(&dsrdata, &ds, NULL); 2176186462Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 2177170222Sdougb 2178170222Sdougb if (!dns_resolver_algorithm_supported(val->view->resolver, 2179170222Sdougb val->event->name, 2180170222Sdougb ds.algorithm)) 2181170222Sdougb continue; 2182170222Sdougb 2183174187Sdougb if (ds.digest_type == DNS_DSDIGEST_SHA256 && 2184174187Sdougb ds.length == ISC_SHA256_DIGESTLENGTH) { 2185224092Sdougb digest_types[DNS_DSDIGEST_SHA1] = 0; 2186170222Sdougb break; 2187170222Sdougb } 2188170222Sdougb } 2189170222Sdougb 2190170222Sdougb for (result = dns_rdataset_first(val->dsset); 2191170222Sdougb result == ISC_R_SUCCESS; 2192135446Strhodes result = dns_rdataset_next(val->dsset)) 2193135446Strhodes { 2194135446Strhodes dns_rdata_reset(&dsrdata); 2195135446Strhodes dns_rdataset_current(val->dsset, &dsrdata); 2196186462Sdougb result = dns_rdata_tostruct(&dsrdata, &ds, NULL); 2197186462Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 2198135446Strhodes 2199170222Sdougb if (!dns_resolver_digest_supported(val->view->resolver, 2200170222Sdougb ds.digest_type)) 2201153816Sdougb continue; 2202170222Sdougb 2203224092Sdougb if (digest_types[ds.digest_type] == 0) 2204170222Sdougb continue; 2205170222Sdougb 2206135446Strhodes if (!dns_resolver_algorithm_supported(val->view->resolver, 2207135446Strhodes val->event->name, 2208135446Strhodes ds.algorithm)) 2209135446Strhodes continue; 2210135446Strhodes 2211135446Strhodes supported_algorithm = ISC_TRUE; 2212135446Strhodes 2213135446Strhodes dns_rdataset_init(&trdataset); 2214135446Strhodes dns_rdataset_clone(val->event->rdataset, &trdataset); 2215135446Strhodes 2216165071Sdougb /* 2217224092Sdougb * Find matching DNSKEY from DS. 2218165071Sdougb */ 2219224092Sdougb result = keyfromds(val, &trdataset, &dsrdata, ds.digest_type, 2220224092Sdougb ds.key_tag, ds.algorithm, &keyrdata); 2221135446Strhodes if (result != ISC_R_SUCCESS) { 2222193149Sdougb dns_rdataset_disassociate(&trdataset); 2223135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 2224135446Strhodes "no DNSKEY matching DS"); 2225135446Strhodes continue; 2226135446Strhodes } 2227186462Sdougb 2228224092Sdougb /* 2229224092Sdougb * Check that this DNSKEY signed the DNSKEY rrset. 2230224092Sdougb */ 2231224092Sdougb result = checkkey(val, &keyrdata, ds.key_tag, ds.algorithm); 2232224092Sdougb 2233135446Strhodes dns_rdataset_disassociate(&trdataset); 2234135446Strhodes if (result == ISC_R_SUCCESS) 2235135446Strhodes break; 2236135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 2237135446Strhodes "no RRSIG matching DS key"); 2238135446Strhodes } 2239135446Strhodes if (result == ISC_R_SUCCESS) { 2240205292Sdougb marksecure(event); 2241224092Sdougb validator_log(val, ISC_LOG_DEBUG(3), "marking as secure (DS)"); 2242135446Strhodes return (result); 2243135446Strhodes } else if (result == ISC_R_NOMORE && !supported_algorithm) { 2244135446Strhodes if (val->mustbesecure) { 2245135446Strhodes validator_log(val, ISC_LOG_WARNING, 2246224092Sdougb "must be secure failure, " 2247224092Sdougb "no supported algorithm/digest (DS)"); 2248135446Strhodes return (DNS_R_MUSTBESECURE); 2249135446Strhodes } 2250135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 2251153816Sdougb "no supported algorithm/digest (DS)"); 2252214586Sdougb markanswer(val, "validatezonekey (3)"); 2253135446Strhodes return (ISC_R_SUCCESS); 2254224092Sdougb } else { 2255224092Sdougb validator_log(val, ISC_LOG_INFO, 2256224092Sdougb "no valid signature found (DS)"); 2257135446Strhodes return (DNS_R_NOVALIDSIG); 2258224092Sdougb } 2259135446Strhodes} 2260135446Strhodes 2261165071Sdougb/*% 2262135446Strhodes * Starts a positive response validation. 2263135446Strhodes * 2264135446Strhodes * Returns: 2265165071Sdougb * \li ISC_R_SUCCESS Validation completed successfully 2266165071Sdougb * \li DNS_R_WAIT Validation has started but is waiting 2267135446Strhodes * for an event. 2268165071Sdougb * \li Other return codes are possible and all indicate failure. 2269135446Strhodes */ 2270135446Strhodesstatic isc_result_t 2271135446Strhodesstart_positive_validation(dns_validator_t *val) { 2272135446Strhodes /* 2273135446Strhodes * If this is not a key, go straight into validate(). 2274135446Strhodes */ 2275135446Strhodes if (val->event->type != dns_rdatatype_dnskey || !isselfsigned(val)) 2276135446Strhodes return (validate(val, ISC_FALSE)); 2277135446Strhodes 2278135446Strhodes return (validatezonekey(val)); 2279135446Strhodes} 2280135446Strhodes 2281165071Sdougb/*% 2282214586Sdougb * val_rdataset_first and val_rdataset_next provide iteration methods 2283214586Sdougb * that hide whether we are iterating across a message or a negative 2284214586Sdougb * cache rdataset. 2285214586Sdougb */ 2286214586Sdougbstatic isc_result_t 2287214586Sdougbval_rdataset_first(dns_validator_t *val, dns_name_t **namep, 2288214586Sdougb dns_rdataset_t **rdatasetp) 2289214586Sdougb{ 2290214586Sdougb dns_message_t *message = val->event->message; 2291214586Sdougb isc_result_t result; 2292214586Sdougb 2293214586Sdougb REQUIRE(rdatasetp != NULL); 2294214586Sdougb REQUIRE(namep != NULL); 2295214586Sdougb if (message == NULL) { 2296214586Sdougb REQUIRE(*rdatasetp != NULL); 2297214586Sdougb REQUIRE(*namep != NULL); 2298214586Sdougb } else { 2299214586Sdougb REQUIRE(*rdatasetp == NULL); 2300214586Sdougb REQUIRE(*namep == NULL); 2301214586Sdougb } 2302214586Sdougb 2303214586Sdougb if (message != NULL) { 2304214586Sdougb result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); 2305214586Sdougb if (result != ISC_R_SUCCESS) 2306214586Sdougb return (result); 2307214586Sdougb dns_message_currentname(message, DNS_SECTION_AUTHORITY, namep); 2308214586Sdougb *rdatasetp = ISC_LIST_HEAD((*namep)->list); 2309214586Sdougb INSIST(*rdatasetp != NULL); 2310214586Sdougb } else { 2311214586Sdougb result = dns_rdataset_first(val->event->rdataset); 2312214586Sdougb if (result == ISC_R_SUCCESS) 2313214586Sdougb dns_ncache_current(val->event->rdataset, *namep, 2314214586Sdougb *rdatasetp); 2315214586Sdougb } 2316214586Sdougb return (result); 2317214586Sdougb} 2318214586Sdougb 2319214586Sdougbstatic isc_result_t 2320214586Sdougbval_rdataset_next(dns_validator_t *val, dns_name_t **namep, 2321214586Sdougb dns_rdataset_t **rdatasetp) 2322214586Sdougb{ 2323214586Sdougb dns_message_t *message = val->event->message; 2324214586Sdougb isc_result_t result = ISC_R_SUCCESS; 2325214586Sdougb 2326214586Sdougb REQUIRE(rdatasetp != NULL && *rdatasetp != NULL); 2327214586Sdougb REQUIRE(namep != NULL && *namep != NULL); 2328214586Sdougb 2329214586Sdougb if (message != NULL) { 2330214586Sdougb dns_rdataset_t *rdataset = *rdatasetp; 2331214586Sdougb rdataset = ISC_LIST_NEXT(rdataset, link); 2332214586Sdougb if (rdataset == NULL) { 2333214586Sdougb *namep = NULL; 2334214586Sdougb result = dns_message_nextname(message, 2335214586Sdougb DNS_SECTION_AUTHORITY); 2336214586Sdougb if (result == ISC_R_SUCCESS) { 2337214586Sdougb dns_message_currentname(message, 2338214586Sdougb DNS_SECTION_AUTHORITY, 2339214586Sdougb namep); 2340214586Sdougb rdataset = ISC_LIST_HEAD((*namep)->list); 2341214586Sdougb INSIST(rdataset != NULL); 2342214586Sdougb } 2343214586Sdougb } 2344214586Sdougb *rdatasetp = rdataset; 2345214586Sdougb } else { 2346214586Sdougb dns_rdataset_disassociate(*rdatasetp); 2347214586Sdougb result = dns_rdataset_next(val->event->rdataset); 2348214586Sdougb if (result == ISC_R_SUCCESS) 2349214586Sdougb dns_ncache_current(val->event->rdataset, *namep, 2350214586Sdougb *rdatasetp); 2351214586Sdougb } 2352214586Sdougb return (result); 2353214586Sdougb} 2354214586Sdougb 2355214586Sdougb/*% 2356165071Sdougb * Look for NODATA at the wildcard and NOWILDCARD proofs in the 2357165071Sdougb * previously validated NSEC records. As these proofs are mutually 2358165071Sdougb * exclusive we stop when one is found. 2359165071Sdougb * 2360165071Sdougb * Returns 2361186462Sdougb * \li ISC_R_SUCCESS 2362165071Sdougb */ 2363135446Strhodesstatic isc_result_t 2364193149Sdougbcheckwildcard(dns_validator_t *val, dns_rdatatype_t type, dns_name_t *zonename) 2365193149Sdougb{ 2366214586Sdougb dns_name_t *name, *wild, tname; 2367135446Strhodes isc_result_t result; 2368135446Strhodes isc_boolean_t exists, data; 2369135446Strhodes char namebuf[DNS_NAME_FORMATSIZE]; 2370214586Sdougb dns_rdataset_t *rdataset, trdataset; 2371135446Strhodes 2372214586Sdougb dns_name_init(&tname, NULL); 2373214586Sdougb dns_rdataset_init(&trdataset); 2374135446Strhodes wild = dns_fixedname_name(&val->wild); 2375193149Sdougb 2376193149Sdougb if (dns_name_countlabels(wild) == 0) { 2377193149Sdougb validator_log(val, ISC_LOG_DEBUG(3), 2378193149Sdougb "in checkwildcard: no wildcard to check"); 2379193149Sdougb return (ISC_R_SUCCESS); 2380193149Sdougb } 2381193149Sdougb 2382135446Strhodes dns_name_format(wild, namebuf, sizeof(namebuf)); 2383135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), "in checkwildcard: %s", namebuf); 2384135446Strhodes 2385214586Sdougb if (val->event->message == NULL) { 2386214586Sdougb name = &tname; 2387214586Sdougb rdataset = &trdataset; 2388214586Sdougb } else { 2389214586Sdougb name = NULL; 2390214586Sdougb rdataset = NULL; 2391214586Sdougb } 2392214586Sdougb 2393214586Sdougb for (result = val_rdataset_first(val, &name, &rdataset); 2394135446Strhodes result == ISC_R_SUCCESS; 2395214586Sdougb result = val_rdataset_next(val, &name, &rdataset)) 2396135446Strhodes { 2397214586Sdougb if (rdataset->type != type || 2398214586Sdougb rdataset->trust != dns_trust_secure) 2399214586Sdougb continue; 2400135446Strhodes 2401214586Sdougb if (rdataset->type == dns_rdatatype_nsec && 2402214586Sdougb (NEEDNODATA(val) || NEEDNOWILDCARD(val)) && 2403214586Sdougb !FOUNDNODATA(val) && !FOUNDNOWILDCARD(val) && 2404254402Serwin dns_nsec_noexistnodata(val->event->type, wild, name, 2405254402Serwin rdataset, &exists, &data, NULL, 2406254402Serwin validator_log, val) 2407214586Sdougb == ISC_R_SUCCESS) 2408214586Sdougb { 2409214586Sdougb dns_name_t **proofs = val->event->proofs; 2410214586Sdougb if (exists && !data) 2411214586Sdougb val->attributes |= VALATTR_FOUNDNODATA; 2412214586Sdougb if (exists && !data && NEEDNODATA(val)) 2413214586Sdougb proofs[DNS_VALIDATOR_NODATAPROOF] = 2414214586Sdougb name; 2415214586Sdougb if (!exists) 2416214586Sdougb val->attributes |= 2417214586Sdougb VALATTR_FOUNDNOWILDCARD; 2418214586Sdougb if (!exists && NEEDNOQNAME(val)) 2419214586Sdougb proofs[DNS_VALIDATOR_NOWILDCARDPROOF] = 2420214586Sdougb name; 2421214586Sdougb if (dns_rdataset_isassociated(&trdataset)) 2422214586Sdougb dns_rdataset_disassociate(&trdataset); 2423214586Sdougb return (ISC_R_SUCCESS); 2424214586Sdougb } 2425135446Strhodes 2426214586Sdougb if (rdataset->type == dns_rdatatype_nsec3 && 2427214586Sdougb (NEEDNODATA(val) || NEEDNOWILDCARD(val)) && 2428214586Sdougb !FOUNDNODATA(val) && !FOUNDNOWILDCARD(val) && 2429254402Serwin dns_nsec3_noexistnodata(val->event->type, wild, name, 2430254402Serwin rdataset, zonename, &exists, &data, 2431254402Serwin NULL, NULL, NULL, NULL, NULL, NULL, 2432254402Serwin validator_log, val) 2433254402Serwin == ISC_R_SUCCESS) 2434135446Strhodes { 2435214586Sdougb dns_name_t **proofs = val->event->proofs; 2436214586Sdougb if (exists && !data) 2437214586Sdougb val->attributes |= VALATTR_FOUNDNODATA; 2438214586Sdougb if (exists && !data && NEEDNODATA(val)) 2439214586Sdougb proofs[DNS_VALIDATOR_NODATAPROOF] = 2440214586Sdougb name; 2441214586Sdougb if (!exists) 2442214586Sdougb val->attributes |= 2443214586Sdougb VALATTR_FOUNDNOWILDCARD; 2444214586Sdougb if (!exists && NEEDNOQNAME(val)) 2445214586Sdougb proofs[DNS_VALIDATOR_NOWILDCARDPROOF] = 2446214586Sdougb name; 2447214586Sdougb if (dns_rdataset_isassociated(&trdataset)) 2448214586Sdougb dns_rdataset_disassociate(&trdataset); 2449214586Sdougb return (ISC_R_SUCCESS); 2450135446Strhodes } 2451135446Strhodes } 2452135446Strhodes if (result == ISC_R_NOMORE) 2453135446Strhodes result = ISC_R_SUCCESS; 2454214586Sdougb if (dns_rdataset_isassociated(&trdataset)) 2455214586Sdougb dns_rdataset_disassociate(&trdataset); 2456135446Strhodes return (result); 2457135446Strhodes} 2458135446Strhodes 2459193149Sdougbstatic isc_result_t 2460193149Sdougbfindnsec3proofs(dns_validator_t *val) { 2461214586Sdougb dns_name_t *name, tname; 2462193149Sdougb isc_result_t result; 2463193149Sdougb isc_boolean_t exists, data, optout, unknown; 2464234010Sdougb isc_boolean_t setclosest, setnearest, *setclosestp; 2465193149Sdougb dns_fixedname_t fclosest, fnearest, fzonename; 2466234010Sdougb dns_name_t *closest, *nearest, *zonename, *closestp; 2467193149Sdougb dns_name_t **proofs = val->event->proofs; 2468214586Sdougb dns_rdataset_t *rdataset, trdataset; 2469193149Sdougb 2470214586Sdougb dns_name_init(&tname, NULL); 2471214586Sdougb dns_rdataset_init(&trdataset); 2472193149Sdougb dns_fixedname_init(&fclosest); 2473193149Sdougb dns_fixedname_init(&fnearest); 2474193149Sdougb dns_fixedname_init(&fzonename); 2475193149Sdougb closest = dns_fixedname_name(&fclosest); 2476193149Sdougb nearest = dns_fixedname_name(&fnearest); 2477193149Sdougb zonename = dns_fixedname_name(&fzonename); 2478193149Sdougb 2479214586Sdougb if (val->event->message == NULL) { 2480214586Sdougb name = &tname; 2481214586Sdougb rdataset = &trdataset; 2482214586Sdougb } else { 2483214586Sdougb name = NULL; 2484214586Sdougb rdataset = NULL; 2485214586Sdougb } 2486214586Sdougb 2487214586Sdougb for (result = val_rdataset_first(val, &name, &rdataset); 2488193149Sdougb result == ISC_R_SUCCESS; 2489214586Sdougb result = val_rdataset_next(val, &name, &rdataset)) 2490193149Sdougb { 2491214586Sdougb if (rdataset->type != dns_rdatatype_nsec3 || 2492214586Sdougb rdataset->trust != dns_trust_secure) 2493214586Sdougb continue; 2494193149Sdougb 2495254402Serwin result = dns_nsec3_noexistnodata(val->event->type, 2496254402Serwin val->event->name, name, 2497254402Serwin rdataset, zonename, NULL, 2498254402Serwin NULL, NULL, NULL, NULL, NULL, 2499254402Serwin NULL, NULL, validator_log, 2500254402Serwin val); 2501214586Sdougb if (result != ISC_R_IGNORE && result != ISC_R_SUCCESS) { 2502214586Sdougb if (dns_rdataset_isassociated(&trdataset)) 2503214586Sdougb dns_rdataset_disassociate(&trdataset); 2504214586Sdougb return (result); 2505193149Sdougb } 2506193149Sdougb } 2507193149Sdougb if (result != ISC_R_NOMORE) 2508193149Sdougb result = ISC_R_SUCCESS; 2509225361Sdougb POST(result); 2510193149Sdougb 2511193149Sdougb if (dns_name_countlabels(zonename) == 0) 2512193149Sdougb return (ISC_R_SUCCESS); 2513193149Sdougb 2514234010Sdougb /* 2515234010Sdougb * If the val->closest is set then we want to use it otherwise 2516234010Sdougb * we need to discover it. 2517234010Sdougb */ 2518234010Sdougb if (dns_name_countlabels(dns_fixedname_name(&val->closest)) != 0) { 2519234010Sdougb char namebuf[DNS_NAME_FORMATSIZE]; 2520234010Sdougb 2521234010Sdougb dns_name_format(dns_fixedname_name(&val->closest), 2522234010Sdougb namebuf, sizeof(namebuf)); 2523234010Sdougb validator_log(val, ISC_LOG_DEBUG(3), "closest encloser from " 2524234010Sdougb "wildcard signature '%s'", namebuf); 2525234010Sdougb dns_name_copy(dns_fixedname_name(&val->closest), closest, NULL); 2526234010Sdougb closestp = NULL; 2527234010Sdougb setclosestp = NULL; 2528234010Sdougb } else { 2529234010Sdougb closestp = closest; 2530234010Sdougb setclosestp = &setclosest; 2531234010Sdougb } 2532234010Sdougb 2533214586Sdougb for (result = val_rdataset_first(val, &name, &rdataset); 2534193149Sdougb result == ISC_R_SUCCESS; 2535214586Sdougb result = val_rdataset_next(val, &name, &rdataset)) 2536193149Sdougb { 2537214586Sdougb if (rdataset->type != dns_rdatatype_nsec3 || 2538214586Sdougb rdataset->trust != dns_trust_secure) 2539214586Sdougb continue; 2540193149Sdougb 2541214586Sdougb /* 2542214586Sdougb * We process all NSEC3 records to find the closest 2543214586Sdougb * encloser and nearest name to the closest encloser. 2544214586Sdougb */ 2545214586Sdougb setclosest = setnearest = ISC_FALSE; 2546214586Sdougb optout = ISC_FALSE; 2547214586Sdougb unknown = ISC_FALSE; 2548254402Serwin result = dns_nsec3_noexistnodata(val->event->type, 2549254402Serwin val->event->name, 2550254402Serwin name, rdataset, zonename, 2551254402Serwin &exists, &data, &optout, 2552254402Serwin &unknown, setclosestp, 2553254402Serwin &setnearest, closestp, 2554254402Serwin nearest, validator_log, val); 2555214586Sdougb if (unknown) 2556214586Sdougb val->attributes |= VALATTR_FOUNDUNKNOWN; 2557214586Sdougb if (result != ISC_R_SUCCESS) 2558214586Sdougb continue; 2559254402Serwin if (setclosest) 2560254402Serwin proofs[DNS_VALIDATOR_CLOSESTENCLOSER] = name; 2561214586Sdougb if (exists && !data && NEEDNODATA(val)) { 2562214586Sdougb val->attributes |= VALATTR_FOUNDNODATA; 2563214586Sdougb proofs[DNS_VALIDATOR_NODATAPROOF] = name; 2564193149Sdougb } 2565214586Sdougb if (!exists && setnearest) { 2566214586Sdougb val->attributes |= VALATTR_FOUNDNOQNAME; 2567214586Sdougb proofs[DNS_VALIDATOR_NOQNAMEPROOF] = name; 2568214586Sdougb if (optout) 2569214586Sdougb val->attributes |= VALATTR_FOUNDOPTOUT; 2570214586Sdougb } 2571193149Sdougb } 2572214586Sdougb if (result == ISC_R_NOMORE) 2573193149Sdougb result = ISC_R_SUCCESS; 2574193149Sdougb 2575193149Sdougb /* 2576193149Sdougb * To know we have a valid noqname and optout proofs we need to also 2577193149Sdougb * have a valid closest encloser. Otherwise we could still be looking 2578193149Sdougb * at proofs from the parent zone. 2579193149Sdougb */ 2580193149Sdougb if (dns_name_countlabels(closest) > 0 && 2581193149Sdougb dns_name_countlabels(nearest) == 2582193149Sdougb dns_name_countlabels(closest) + 1 && 2583193149Sdougb dns_name_issubdomain(nearest, closest)) 2584193149Sdougb { 2585193149Sdougb val->attributes |= VALATTR_FOUNDCLOSEST; 2586193149Sdougb result = dns_name_concatenate(dns_wildcardname, closest, 2587193149Sdougb dns_fixedname_name(&val->wild), 2588193149Sdougb NULL); 2589193149Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 2590193149Sdougb } else { 2591193149Sdougb val->attributes &= ~VALATTR_FOUNDNOQNAME; 2592193149Sdougb val->attributes &= ~VALATTR_FOUNDOPTOUT; 2593193149Sdougb proofs[DNS_VALIDATOR_NOQNAMEPROOF] = NULL; 2594193149Sdougb } 2595193149Sdougb 2596193149Sdougb /* 2597193149Sdougb * Do we need to check for the wildcard? 2598193149Sdougb */ 2599214586Sdougb if (FOUNDNOQNAME(val) && FOUNDCLOSEST(val) && 2600214586Sdougb ((NEEDNODATA(val) && !FOUNDNODATA(val)) || NEEDNOWILDCARD(val))) { 2601193149Sdougb result = checkwildcard(val, dns_rdatatype_nsec3, zonename); 2602193149Sdougb if (result != ISC_R_SUCCESS) 2603193149Sdougb return (result); 2604193149Sdougb } 2605193149Sdougb return (result); 2606193149Sdougb} 2607193149Sdougb 2608165071Sdougb/*% 2609214586Sdougb * Validate the authority section records. 2610165071Sdougb */ 2611135446Strhodesstatic isc_result_t 2612214586Sdougbvalidate_authority(dns_validator_t *val, isc_boolean_t resume) { 2613135446Strhodes dns_name_t *name; 2614135446Strhodes dns_message_t *message = val->event->message; 2615135446Strhodes isc_result_t result; 2616135446Strhodes 2617135446Strhodes if (!resume) 2618135446Strhodes result = dns_message_firstname(message, DNS_SECTION_AUTHORITY); 2619214586Sdougb else 2620135446Strhodes result = ISC_R_SUCCESS; 2621135446Strhodes 2622135446Strhodes for (; 2623135446Strhodes result == ISC_R_SUCCESS; 2624135446Strhodes result = dns_message_nextname(message, DNS_SECTION_AUTHORITY)) 2625135446Strhodes { 2626135446Strhodes dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL; 2627135446Strhodes 2628135446Strhodes name = NULL; 2629135446Strhodes dns_message_currentname(message, DNS_SECTION_AUTHORITY, &name); 2630135446Strhodes if (resume) { 2631135446Strhodes rdataset = ISC_LIST_NEXT(val->currentset, link); 2632135446Strhodes val->currentset = NULL; 2633135446Strhodes resume = ISC_FALSE; 2634135446Strhodes } else 2635135446Strhodes rdataset = ISC_LIST_HEAD(name->list); 2636135446Strhodes 2637135446Strhodes for (; 2638135446Strhodes rdataset != NULL; 2639135446Strhodes rdataset = ISC_LIST_NEXT(rdataset, link)) 2640135446Strhodes { 2641135446Strhodes if (rdataset->type == dns_rdatatype_rrsig) 2642135446Strhodes continue; 2643135446Strhodes 2644135446Strhodes for (sigrdataset = ISC_LIST_HEAD(name->list); 2645135446Strhodes sigrdataset != NULL; 2646135446Strhodes sigrdataset = ISC_LIST_NEXT(sigrdataset, 2647135446Strhodes link)) 2648135446Strhodes { 2649135446Strhodes if (sigrdataset->type == dns_rdatatype_rrsig && 2650135446Strhodes sigrdataset->covers == rdataset->type) 2651135446Strhodes break; 2652135446Strhodes } 2653135446Strhodes /* 2654135446Strhodes * If a signed zone is missing the zone key, bad 2655135446Strhodes * things could happen. A query for data in the zone 2656135446Strhodes * would lead to a query for the zone key, which 2657135446Strhodes * would return a negative answer, which would contain 2658135446Strhodes * an SOA and an NSEC signed by the missing key, which 2659135446Strhodes * would trigger another query for the DNSKEY (since 2660135446Strhodes * the first one is still in progress), and go into an 2661135446Strhodes * infinite loop. Avoid that. 2662135446Strhodes */ 2663135446Strhodes if (val->event->type == dns_rdatatype_dnskey && 2664225361Sdougb rdataset->type == dns_rdatatype_nsec && 2665135446Strhodes dns_name_equal(name, val->event->name)) 2666135446Strhodes { 2667135446Strhodes dns_rdata_t nsec = DNS_RDATA_INIT; 2668135446Strhodes 2669135446Strhodes result = dns_rdataset_first(rdataset); 2670135446Strhodes if (result != ISC_R_SUCCESS) 2671135446Strhodes return (result); 2672135446Strhodes dns_rdataset_current(rdataset, &nsec); 2673135446Strhodes if (dns_nsec_typepresent(&nsec, 2674135446Strhodes dns_rdatatype_soa)) 2675135446Strhodes continue; 2676135446Strhodes } 2677135446Strhodes val->currentset = rdataset; 2678135446Strhodes result = create_validator(val, name, rdataset->type, 2679135446Strhodes rdataset, sigrdataset, 2680135446Strhodes authvalidated, 2681214586Sdougb "validate_authority"); 2682135446Strhodes if (result != ISC_R_SUCCESS) 2683135446Strhodes return (result); 2684205292Sdougb val->authcount++; 2685135446Strhodes return (DNS_R_WAIT); 2686214586Sdougb } 2687214586Sdougb } 2688214586Sdougb if (result == ISC_R_NOMORE) 2689214586Sdougb result = ISC_R_SUCCESS; 2690214586Sdougb return (result); 2691214586Sdougb} 2692135446Strhodes 2693214586Sdougb/*% 2694214586Sdougb * Validate the ncache elements. 2695214586Sdougb */ 2696214586Sdougbstatic isc_result_t 2697214586Sdougbvalidate_ncache(dns_validator_t *val, isc_boolean_t resume) { 2698214586Sdougb dns_name_t *name; 2699214586Sdougb isc_result_t result; 2700214586Sdougb 2701214586Sdougb if (!resume) 2702214586Sdougb result = dns_rdataset_first(val->event->rdataset); 2703214586Sdougb else 2704214586Sdougb result = dns_rdataset_next(val->event->rdataset); 2705214586Sdougb 2706214586Sdougb for (; 2707214586Sdougb result == ISC_R_SUCCESS; 2708214586Sdougb result = dns_rdataset_next(val->event->rdataset)) 2709214586Sdougb { 2710214586Sdougb dns_rdataset_t *rdataset, *sigrdataset = NULL; 2711214586Sdougb 2712214586Sdougb if (dns_rdataset_isassociated(&val->frdataset)) 2713214586Sdougb dns_rdataset_disassociate(&val->frdataset); 2714214586Sdougb if (dns_rdataset_isassociated(&val->fsigrdataset)) 2715214586Sdougb dns_rdataset_disassociate(&val->fsigrdataset); 2716214586Sdougb 2717214586Sdougb dns_fixedname_init(&val->fname); 2718214586Sdougb name = dns_fixedname_name(&val->fname); 2719214586Sdougb rdataset = &val->frdataset; 2720214586Sdougb dns_ncache_current(val->event->rdataset, name, rdataset); 2721214586Sdougb 2722214586Sdougb if (val->frdataset.type == dns_rdatatype_rrsig) 2723214586Sdougb continue; 2724214586Sdougb 2725214586Sdougb result = dns_ncache_getsigrdataset(val->event->rdataset, name, 2726214586Sdougb rdataset->type, 2727214586Sdougb &val->fsigrdataset); 2728214586Sdougb if (result == ISC_R_SUCCESS) 2729214586Sdougb sigrdataset = &val->fsigrdataset; 2730214586Sdougb 2731214586Sdougb /* 2732214586Sdougb * If a signed zone is missing the zone key, bad 2733214586Sdougb * things could happen. A query for data in the zone 2734214586Sdougb * would lead to a query for the zone key, which 2735214586Sdougb * would return a negative answer, which would contain 2736214586Sdougb * an SOA and an NSEC signed by the missing key, which 2737214586Sdougb * would trigger another query for the DNSKEY (since 2738214586Sdougb * the first one is still in progress), and go into an 2739214586Sdougb * infinite loop. Avoid that. 2740214586Sdougb */ 2741214586Sdougb if (val->event->type == dns_rdatatype_dnskey && 2742225361Sdougb rdataset->type == dns_rdatatype_nsec && 2743214586Sdougb dns_name_equal(name, val->event->name)) 2744214586Sdougb { 2745214586Sdougb dns_rdata_t nsec = DNS_RDATA_INIT; 2746214586Sdougb 2747214586Sdougb result = dns_rdataset_first(rdataset); 2748214586Sdougb if (result != ISC_R_SUCCESS) 2749214586Sdougb return (result); 2750214586Sdougb dns_rdataset_current(rdataset, &nsec); 2751214586Sdougb if (dns_nsec_typepresent(&nsec, 2752214586Sdougb dns_rdatatype_soa)) 2753214586Sdougb continue; 2754135446Strhodes } 2755214586Sdougb val->currentset = rdataset; 2756214586Sdougb result = create_validator(val, name, rdataset->type, 2757214586Sdougb rdataset, sigrdataset, 2758214586Sdougb authvalidated, 2759214586Sdougb "validate_ncache"); 2760214586Sdougb if (result != ISC_R_SUCCESS) 2761214586Sdougb return (result); 2762214586Sdougb val->authcount++; 2763214586Sdougb return (DNS_R_WAIT); 2764135446Strhodes } 2765135446Strhodes if (result == ISC_R_NOMORE) 2766135446Strhodes result = ISC_R_SUCCESS; 2767214586Sdougb return (result); 2768214586Sdougb} 2769214586Sdougb 2770214586Sdougb/*% 2771214586Sdougb * Prove a negative answer is good or that there is a NOQNAME when the 2772214586Sdougb * answer is from a wildcard. 2773214586Sdougb * 2774214586Sdougb * Loop through the authority section looking for NODATA, NOWILDCARD 2775214586Sdougb * and NOQNAME proofs in the NSEC records by calling authvalidated(). 2776214586Sdougb * 2777214586Sdougb * If the required proofs are found we are done. 2778214586Sdougb * 2779214586Sdougb * If the proofs are not found attempt to prove this is a unsecure 2780214586Sdougb * response. 2781214586Sdougb */ 2782214586Sdougbstatic isc_result_t 2783214586Sdougbnsecvalidate(dns_validator_t *val, isc_boolean_t resume) { 2784214586Sdougb isc_result_t result; 2785214586Sdougb 2786214586Sdougb if (resume) 2787214586Sdougb validator_log(val, ISC_LOG_DEBUG(3), "resuming nsecvalidate"); 2788214586Sdougb 2789214586Sdougb if (val->event->message == NULL) 2790214586Sdougb result = validate_ncache(val, resume); 2791214586Sdougb else 2792214586Sdougb result = validate_authority(val, resume); 2793214586Sdougb 2794135446Strhodes if (result != ISC_R_SUCCESS) 2795135446Strhodes return (result); 2796135446Strhodes 2797135446Strhodes /* 2798165071Sdougb * Do we only need to check for NOQNAME? To get here we must have 2799165071Sdougb * had a secure wildcard answer. 2800135446Strhodes */ 2801214586Sdougb if (!NEEDNODATA(val) && !NEEDNOWILDCARD(val) && NEEDNOQNAME(val)) { 2802214586Sdougb if (!FOUNDNOQNAME(val)) 2803193149Sdougb findnsec3proofs(val); 2804214586Sdougb if (FOUNDNOQNAME(val) && FOUNDCLOSEST(val)) { 2805135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 2806224092Sdougb "marking as secure, noqname proof found"); 2807205292Sdougb marksecure(val->event); 2808135446Strhodes return (ISC_R_SUCCESS); 2809214586Sdougb } else if (FOUNDOPTOUT(val) && 2810193149Sdougb dns_name_countlabels(dns_fixedname_name(&val->wild)) 2811193149Sdougb != 0) { 2812193149Sdougb validator_log(val, ISC_LOG_DEBUG(3), 2813193149Sdougb "optout proof found"); 2814193149Sdougb val->event->optout = ISC_TRUE; 2815214586Sdougb markanswer(val, "nsecvalidate (1)"); 2816193149Sdougb return (ISC_R_SUCCESS); 2817193149Sdougb } else if ((val->attributes & VALATTR_FOUNDUNKNOWN) != 0) { 2818193149Sdougb validator_log(val, ISC_LOG_DEBUG(3), 2819193149Sdougb "unknown NSEC3 hash algorithm found"); 2820214586Sdougb markanswer(val, "nsecvalidate (2)"); 2821193149Sdougb return (ISC_R_SUCCESS); 2822135446Strhodes } 2823135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 2824135446Strhodes "noqname proof not found"); 2825135446Strhodes return (DNS_R_NOVALIDNSEC); 2826135446Strhodes } 2827135446Strhodes 2828214586Sdougb if (!FOUNDNOQNAME(val) && !FOUNDNODATA(val)) 2829193149Sdougb findnsec3proofs(val); 2830193149Sdougb 2831135446Strhodes /* 2832135446Strhodes * Do we need to check for the wildcard? 2833135446Strhodes */ 2834214586Sdougb if (FOUNDNOQNAME(val) && FOUNDCLOSEST(val) && 2835214586Sdougb ((NEEDNODATA(val) && !FOUNDNODATA(val)) || NEEDNOWILDCARD(val))) { 2836193149Sdougb result = checkwildcard(val, dns_rdatatype_nsec, NULL); 2837135446Strhodes if (result != ISC_R_SUCCESS) 2838135446Strhodes return (result); 2839135446Strhodes } 2840135446Strhodes 2841214586Sdougb if ((NEEDNODATA(val) && (FOUNDNODATA(val) || FOUNDOPTOUT(val))) || 2842214586Sdougb (NEEDNOQNAME(val) && FOUNDNOQNAME(val) && 2843214586Sdougb NEEDNOWILDCARD(val) && FOUNDNOWILDCARD(val) && 2844214586Sdougb FOUNDCLOSEST(val))) { 2845193149Sdougb if ((val->attributes & VALATTR_FOUNDOPTOUT) != 0) 2846193149Sdougb val->event->optout = ISC_TRUE; 2847135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 2848165071Sdougb "nonexistence proof(s) found"); 2849214586Sdougb if (val->event->message == NULL) 2850214586Sdougb marksecure(val->event); 2851254402Serwin else 2852254402Serwin val->event->secure = ISC_TRUE; 2853135446Strhodes return (ISC_R_SUCCESS); 2854135446Strhodes } 2855165071Sdougb 2856208337Sdougb if (val->authfail != 0 && val->authcount == val->authfail) 2857205292Sdougb return (DNS_R_BROKENCHAIN); 2858165071Sdougb validator_log(val, ISC_LOG_DEBUG(3), 2859165071Sdougb "nonexistence proof(s) not found"); 2860165071Sdougb val->attributes |= VALATTR_INSECURITY; 2861186462Sdougb return (proveunsecure(val, ISC_FALSE, ISC_FALSE)); 2862135446Strhodes} 2863135446Strhodes 2864135446Strhodesstatic isc_boolean_t 2865153816Sdougbcheck_ds(dns_validator_t *val, dns_name_t *name, dns_rdataset_t *rdataset) { 2866135446Strhodes dns_rdata_t dsrdata = DNS_RDATA_INIT; 2867135446Strhodes dns_rdata_ds_t ds; 2868135446Strhodes isc_result_t result; 2869135446Strhodes 2870135446Strhodes for (result = dns_rdataset_first(rdataset); 2871135446Strhodes result == ISC_R_SUCCESS; 2872135446Strhodes result = dns_rdataset_next(rdataset)) { 2873135446Strhodes dns_rdataset_current(rdataset, &dsrdata); 2874186462Sdougb result = dns_rdata_tostruct(&dsrdata, &ds, NULL); 2875186462Sdougb RUNTIME_CHECK(result == ISC_R_SUCCESS); 2876135446Strhodes 2877170222Sdougb if (dns_resolver_digest_supported(val->view->resolver, 2878170222Sdougb ds.digest_type) && 2879153816Sdougb dns_resolver_algorithm_supported(val->view->resolver, 2880153816Sdougb name, ds.algorithm)) { 2881153816Sdougb dns_rdata_reset(&dsrdata); 2882135446Strhodes return (ISC_TRUE); 2883153816Sdougb } 2884135446Strhodes dns_rdata_reset(&dsrdata); 2885135446Strhodes } 2886135446Strhodes return (ISC_FALSE); 2887135446Strhodes} 2888135446Strhodes 2889205292Sdougbstatic void 2890205292Sdougbdlvvalidated(isc_task_t *task, isc_event_t *event) { 2891205292Sdougb dns_validatorevent_t *devent; 2892205292Sdougb dns_validator_t *val; 2893205292Sdougb isc_result_t eresult; 2894205292Sdougb isc_boolean_t want_destroy; 2895205292Sdougb 2896205292Sdougb UNUSED(task); 2897205292Sdougb INSIST(event->ev_type == DNS_EVENT_VALIDATORDONE); 2898205292Sdougb 2899205292Sdougb devent = (dns_validatorevent_t *)event; 2900205292Sdougb val = devent->ev_arg; 2901205292Sdougb eresult = devent->result; 2902205292Sdougb 2903205292Sdougb isc_event_free(&event); 2904205292Sdougb dns_validator_destroy(&val->subvalidator); 2905205292Sdougb 2906205292Sdougb INSIST(val->event != NULL); 2907205292Sdougb 2908205292Sdougb validator_log(val, ISC_LOG_DEBUG(3), "in dlvvalidated"); 2909205292Sdougb LOCK(&val->lock); 2910205292Sdougb if (CANCELED(val)) { 2911205292Sdougb validator_done(val, ISC_R_CANCELED); 2912205292Sdougb } else if (eresult == ISC_R_SUCCESS) { 2913205292Sdougb validator_log(val, ISC_LOG_DEBUG(3), 2914222395Sdougb "dlvset with trust %s", 2915222395Sdougb dns_trust_totext(val->frdataset.trust)); 2916205292Sdougb dns_rdataset_clone(&val->frdataset, &val->dlv); 2917205292Sdougb val->havedlvsep = ISC_TRUE; 2918205292Sdougb if (dlv_algorithm_supported(val)) 2919205292Sdougb dlv_validator_start(val); 2920205292Sdougb else { 2921214586Sdougb markanswer(val, "dlvvalidated"); 2922205292Sdougb validator_done(val, ISC_R_SUCCESS); 2923205292Sdougb } 2924205292Sdougb } else { 2925205292Sdougb if (eresult != DNS_R_BROKENCHAIN) { 2926205292Sdougb if (dns_rdataset_isassociated(&val->frdataset)) 2927205292Sdougb dns_rdataset_expire(&val->frdataset); 2928205292Sdougb if (dns_rdataset_isassociated(&val->fsigrdataset)) 2929205292Sdougb dns_rdataset_expire(&val->fsigrdataset); 2930205292Sdougb } 2931205292Sdougb validator_log(val, ISC_LOG_DEBUG(3), 2932205292Sdougb "dlvvalidated: got %s", 2933205292Sdougb isc_result_totext(eresult)); 2934205292Sdougb validator_done(val, DNS_R_BROKENCHAIN); 2935205292Sdougb } 2936205292Sdougb want_destroy = exit_check(val); 2937205292Sdougb UNLOCK(&val->lock); 2938205292Sdougb if (want_destroy) 2939205292Sdougb destroy(val); 2940205292Sdougb} 2941205292Sdougb 2942165071Sdougb/*% 2943165071Sdougb * Callback from fetching a DLV record. 2944186462Sdougb * 2945165071Sdougb * Resumes the DLV lookup process. 2946165071Sdougb */ 2947135446Strhodesstatic void 2948153816Sdougbdlvfetched(isc_task_t *task, isc_event_t *event) { 2949153816Sdougb char namebuf[DNS_NAME_FORMATSIZE]; 2950135446Strhodes dns_fetchevent_t *devent; 2951135446Strhodes dns_validator_t *val; 2952135446Strhodes isc_boolean_t want_destroy; 2953135446Strhodes isc_result_t eresult; 2954135446Strhodes isc_result_t result; 2955254402Serwin dns_fetch_t *fetch; 2956135446Strhodes 2957135446Strhodes UNUSED(task); 2958135446Strhodes INSIST(event->ev_type == DNS_EVENT_FETCHDONE); 2959135446Strhodes devent = (dns_fetchevent_t *)event; 2960135446Strhodes val = devent->ev_arg; 2961135446Strhodes eresult = devent->result; 2962153816Sdougb 2963153816Sdougb /* Free resources which are not of interest. */ 2964153816Sdougb if (devent->node != NULL) 2965153816Sdougb dns_db_detachnode(devent->db, &devent->node); 2966153816Sdougb if (devent->db != NULL) 2967153816Sdougb dns_db_detach(&devent->db); 2968153816Sdougb if (dns_rdataset_isassociated(&val->fsigrdataset)) 2969153816Sdougb dns_rdataset_disassociate(&val->fsigrdataset); 2970135446Strhodes isc_event_free(&event); 2971153816Sdougb 2972135446Strhodes INSIST(val->event != NULL); 2973153816Sdougb validator_log(val, ISC_LOG_DEBUG(3), "in dlvfetched: %s", 2974135446Strhodes dns_result_totext(eresult)); 2975135446Strhodes 2976135446Strhodes LOCK(&val->lock); 2977254402Serwin fetch = val->fetch; 2978254402Serwin val->fetch = NULL; 2979135446Strhodes if (eresult == ISC_R_SUCCESS) { 2980153816Sdougb dns_name_format(dns_fixedname_name(&val->dlvsep), namebuf, 2981153816Sdougb sizeof(namebuf)); 2982153816Sdougb dns_rdataset_clone(&val->frdataset, &val->dlv); 2983135446Strhodes val->havedlvsep = ISC_TRUE; 2984190227Sdougb if (dlv_algorithm_supported(val)) { 2985190227Sdougb validator_log(val, ISC_LOG_DEBUG(3), "DLV %s found", 2986190227Sdougb namebuf); 2987190227Sdougb dlv_validator_start(val); 2988190227Sdougb } else { 2989190227Sdougb validator_log(val, ISC_LOG_DEBUG(3), 2990190227Sdougb "DLV %s found with no supported algorithms", 2991190227Sdougb namebuf); 2992214586Sdougb markanswer(val, "dlvfetched (1)"); 2993190227Sdougb validator_done(val, ISC_R_SUCCESS); 2994190227Sdougb } 2995135446Strhodes } else if (eresult == DNS_R_NXRRSET || 2996135446Strhodes eresult == DNS_R_NXDOMAIN || 2997135446Strhodes eresult == DNS_R_NCACHENXRRSET || 2998135446Strhodes eresult == DNS_R_NCACHENXDOMAIN) { 2999190227Sdougb result = finddlvsep(val, ISC_TRUE); 3000135446Strhodes if (result == ISC_R_SUCCESS) { 3001190227Sdougb if (dlv_algorithm_supported(val)) { 3002190227Sdougb dns_name_format(dns_fixedname_name(&val->dlvsep), 3003190227Sdougb namebuf, sizeof(namebuf)); 3004190227Sdougb validator_log(val, ISC_LOG_DEBUG(3), 3005190227Sdougb "DLV %s found", namebuf); 3006190227Sdougb dlv_validator_start(val); 3007190227Sdougb } else { 3008190227Sdougb validator_log(val, ISC_LOG_DEBUG(3), 3009190227Sdougb "DLV %s found with no supported " 3010190227Sdougb "algorithms", namebuf); 3011214586Sdougb markanswer(val, "dlvfetched (2)"); 3012190227Sdougb validator_done(val, ISC_R_SUCCESS); 3013190227Sdougb } 3014135446Strhodes } else if (result == ISC_R_NOTFOUND) { 3015153816Sdougb validator_log(val, ISC_LOG_DEBUG(3), "DLV not found"); 3016214586Sdougb markanswer(val, "dlvfetched (3)"); 3017135446Strhodes validator_done(val, ISC_R_SUCCESS); 3018153816Sdougb } else { 3019153816Sdougb validator_log(val, ISC_LOG_DEBUG(3), "DLV lookup: %s", 3020153816Sdougb dns_result_totext(result)); 3021153816Sdougb if (result != DNS_R_WAIT) 3022153816Sdougb validator_done(val, result); 3023153816Sdougb } 3024153816Sdougb } else { 3025153816Sdougb validator_log(val, ISC_LOG_DEBUG(3), "DLV lookup: %s", 3026153816Sdougb dns_result_totext(eresult)); 3027165071Sdougb validator_done(val, eresult); 3028135446Strhodes } 3029135446Strhodes want_destroy = exit_check(val); 3030135446Strhodes UNLOCK(&val->lock); 3031254402Serwin if (fetch != NULL) 3032254402Serwin dns_resolver_destroyfetch(&fetch); 3033135446Strhodes if (want_destroy) 3034135446Strhodes destroy(val); 3035135446Strhodes} 3036135446Strhodes 3037165071Sdougb/*% 3038193149Sdougb * Start the DLV lookup process. 3039186462Sdougb * 3040165071Sdougb * Returns 3041165071Sdougb * \li ISC_R_SUCCESS 3042165071Sdougb * \li DNS_R_WAIT 3043165071Sdougb * \li Others on validation failures. 3044165071Sdougb */ 3045135446Strhodesstatic isc_result_t 3046153816Sdougbstartfinddlvsep(dns_validator_t *val, dns_name_t *unsecure) { 3047153816Sdougb char namebuf[DNS_NAME_FORMATSIZE]; 3048153816Sdougb isc_result_t result; 3049153816Sdougb 3050153816Sdougb INSIST(!DLVTRIED(val)); 3051153816Sdougb 3052153816Sdougb val->attributes |= VALATTR_DLVTRIED; 3053153816Sdougb 3054153816Sdougb dns_name_format(unsecure, namebuf, sizeof(namebuf)); 3055153816Sdougb validator_log(val, ISC_LOG_DEBUG(3), 3056153816Sdougb "plain DNSSEC returns unsecure (%s): looking for DLV", 3057153816Sdougb namebuf); 3058153816Sdougb 3059153816Sdougb if (dns_name_issubdomain(val->event->name, val->view->dlv)) { 3060224092Sdougb validator_log(val, ISC_LOG_WARNING, "must be secure failure, " 3061224092Sdougb " %s is under DLV (startfinddlvsep)", namebuf); 3062153816Sdougb return (DNS_R_MUSTBESECURE); 3063153816Sdougb } 3064153816Sdougb 3065153816Sdougb val->dlvlabels = dns_name_countlabels(unsecure) - 1; 3066153816Sdougb result = finddlvsep(val, ISC_FALSE); 3067153816Sdougb if (result == ISC_R_NOTFOUND) { 3068153816Sdougb validator_log(val, ISC_LOG_DEBUG(3), "DLV not found"); 3069214586Sdougb markanswer(val, "startfinddlvsep (1)"); 3070153816Sdougb return (ISC_R_SUCCESS); 3071153816Sdougb } 3072153816Sdougb if (result != ISC_R_SUCCESS) { 3073153816Sdougb validator_log(val, ISC_LOG_DEBUG(3), "DLV lookup: %s", 3074153816Sdougb dns_result_totext(result)); 3075153816Sdougb return (result); 3076153816Sdougb } 3077153816Sdougb dns_name_format(dns_fixedname_name(&val->dlvsep), namebuf, 3078153816Sdougb sizeof(namebuf)); 3079190227Sdougb if (dlv_algorithm_supported(val)) { 3080190227Sdougb validator_log(val, ISC_LOG_DEBUG(3), "DLV %s found", namebuf); 3081190227Sdougb dlv_validator_start(val); 3082190227Sdougb return (DNS_R_WAIT); 3083193149Sdougb } 3084190227Sdougb validator_log(val, ISC_LOG_DEBUG(3), "DLV %s found with no supported " 3085190227Sdougb "algorithms", namebuf); 3086214586Sdougb markanswer(val, "startfinddlvsep (2)"); 3087190227Sdougb validator_done(val, ISC_R_SUCCESS); 3088190227Sdougb return (ISC_R_SUCCESS); 3089153816Sdougb} 3090153816Sdougb 3091165071Sdougb/*% 3092165071Sdougb * Continue the DLV lookup process. 3093165071Sdougb * 3094165071Sdougb * Returns 3095165071Sdougb * \li ISC_R_SUCCESS 3096165071Sdougb * \li ISC_R_NOTFOUND 3097165071Sdougb * \li DNS_R_WAIT 3098165071Sdougb * \li Others on validation failure. 3099165071Sdougb */ 3100153816Sdougbstatic isc_result_t 3101135446Strhodesfinddlvsep(dns_validator_t *val, isc_boolean_t resume) { 3102153816Sdougb char namebuf[DNS_NAME_FORMATSIZE]; 3103135446Strhodes dns_fixedname_t dlvfixed; 3104135446Strhodes dns_name_t *dlvname; 3105135446Strhodes dns_name_t *dlvsep; 3106135446Strhodes dns_name_t noroot; 3107135446Strhodes isc_result_t result; 3108135446Strhodes unsigned int labels; 3109165071Sdougb 3110153816Sdougb INSIST(val->view->dlv != NULL); 3111135446Strhodes 3112135446Strhodes if (!resume) { 3113153816Sdougb if (dns_name_issubdomain(val->event->name, val->view->dlv)) { 3114224092Sdougb dns_name_format(val->event->name, namebuf, 3115224092Sdougb sizeof(namebuf)); 3116153816Sdougb validator_log(val, ISC_LOG_WARNING, 3117224092Sdougb "must be secure failure, " 3118224092Sdougb "%s is under DLV (finddlvsep)", namebuf); 3119153816Sdougb return (DNS_R_MUSTBESECURE); 3120153816Sdougb } 3121153816Sdougb 3122135446Strhodes dns_fixedname_init(&val->dlvsep); 3123135446Strhodes dlvsep = dns_fixedname_name(&val->dlvsep); 3124135446Strhodes dns_name_copy(val->event->name, dlvsep, NULL); 3125174187Sdougb /* 3126174187Sdougb * If this is a response to a DS query, we need to look in 3127174187Sdougb * the parent zone for the trust anchor. 3128174187Sdougb */ 3129153816Sdougb if (val->event->type == dns_rdatatype_ds) { 3130153816Sdougb labels = dns_name_countlabels(dlvsep); 3131153816Sdougb if (labels == 0) 3132153816Sdougb return (ISC_R_NOTFOUND); 3133153816Sdougb dns_name_getlabelsequence(dlvsep, 1, labels - 1, 3134153816Sdougb dlvsep); 3135153816Sdougb } 3136135446Strhodes } else { 3137135446Strhodes dlvsep = dns_fixedname_name(&val->dlvsep); 3138135446Strhodes labels = dns_name_countlabels(dlvsep); 3139135446Strhodes dns_name_getlabelsequence(dlvsep, 1, labels - 1, dlvsep); 3140135446Strhodes } 3141135446Strhodes dns_name_init(&noroot, NULL); 3142135446Strhodes dns_fixedname_init(&dlvfixed); 3143135446Strhodes dlvname = dns_fixedname_name(&dlvfixed); 3144135446Strhodes labels = dns_name_countlabels(dlvsep); 3145153816Sdougb if (labels == 0) 3146153816Sdougb return (ISC_R_NOTFOUND); 3147135446Strhodes dns_name_getlabelsequence(dlvsep, 0, labels - 1, &noroot); 3148135446Strhodes result = dns_name_concatenate(&noroot, val->view->dlv, dlvname, NULL); 3149135446Strhodes while (result == ISC_R_NOSPACE) { 3150135446Strhodes labels = dns_name_countlabels(dlvsep); 3151135446Strhodes dns_name_getlabelsequence(dlvsep, 1, labels - 1, dlvsep); 3152135446Strhodes dns_name_getlabelsequence(dlvsep, 0, labels - 2, &noroot); 3153135446Strhodes result = dns_name_concatenate(&noroot, val->view->dlv, 3154135446Strhodes dlvname, NULL); 3155135446Strhodes } 3156135446Strhodes if (result != ISC_R_SUCCESS) { 3157135446Strhodes validator_log(val, ISC_LOG_DEBUG(2), "DLV concatenate failed"); 3158135446Strhodes return (DNS_R_NOVALIDSIG); 3159135446Strhodes } 3160135446Strhodes 3161153816Sdougb while (dns_name_countlabels(dlvname) >= 3162153816Sdougb dns_name_countlabels(val->view->dlv) + val->dlvlabels) { 3163153816Sdougb dns_name_format(dlvname, namebuf, sizeof(namebuf)); 3164153816Sdougb validator_log(val, ISC_LOG_DEBUG(3), "looking for DLV %s", 3165153816Sdougb namebuf); 3166135446Strhodes result = view_find(val, dlvname, dns_rdatatype_dlv); 3167135446Strhodes if (result == ISC_R_SUCCESS) { 3168205292Sdougb if (DNS_TRUST_PENDING(val->frdataset.trust) && 3169205292Sdougb dns_rdataset_isassociated(&val->fsigrdataset)) 3170205292Sdougb { 3171205292Sdougb dns_fixedname_init(&val->fname); 3172205292Sdougb dns_name_copy(dlvname, 3173205292Sdougb dns_fixedname_name(&val->fname), 3174205292Sdougb NULL); 3175205292Sdougb result = create_validator(val, 3176205292Sdougb dns_fixedname_name(&val->fname), 3177205292Sdougb dns_rdatatype_dlv, 3178205292Sdougb &val->frdataset, 3179205292Sdougb &val->fsigrdataset, 3180205292Sdougb dlvvalidated, 3181205292Sdougb "finddlvsep"); 3182205292Sdougb if (result != ISC_R_SUCCESS) 3183205292Sdougb return (result); 3184205292Sdougb return (DNS_R_WAIT); 3185205292Sdougb } 3186224092Sdougb if (val->frdataset.trust < dns_trust_secure) { 3187224092Sdougb validator_log(val, ISC_LOG_DEBUG(3), 3188224092Sdougb "DLV not validated"); 3189135446Strhodes return (DNS_R_NOVALIDSIG); 3190224092Sdougb } 3191135446Strhodes val->havedlvsep = ISC_TRUE; 3192153816Sdougb dns_rdataset_clone(&val->frdataset, &val->dlv); 3193135446Strhodes return (ISC_R_SUCCESS); 3194135446Strhodes } 3195135446Strhodes if (result == ISC_R_NOTFOUND) { 3196153816Sdougb result = create_fetch(val, dlvname, dns_rdatatype_dlv, 3197153816Sdougb dlvfetched, "finddlvsep"); 3198153816Sdougb if (result != ISC_R_SUCCESS) 3199153816Sdougb return (result); 3200153816Sdougb return (DNS_R_WAIT); 3201135446Strhodes } 3202135446Strhodes if (result != DNS_R_NXRRSET && 3203135446Strhodes result != DNS_R_NXDOMAIN && 3204186462Sdougb result != DNS_R_EMPTYNAME && 3205135446Strhodes result != DNS_R_NCACHENXRRSET && 3206153816Sdougb result != DNS_R_NCACHENXDOMAIN) 3207135446Strhodes return (result); 3208135446Strhodes /* 3209135446Strhodes * Strip first labels from both dlvsep and dlvname. 3210135446Strhodes */ 3211135446Strhodes labels = dns_name_countlabels(dlvsep); 3212153816Sdougb if (labels == 0) 3213153816Sdougb break; 3214135446Strhodes dns_name_getlabelsequence(dlvsep, 1, labels - 1, dlvsep); 3215135446Strhodes labels = dns_name_countlabels(dlvname); 3216135446Strhodes dns_name_getlabelsequence(dlvname, 1, labels - 1, dlvname); 3217135446Strhodes } 3218135446Strhodes return (ISC_R_NOTFOUND); 3219135446Strhodes} 3220135446Strhodes 3221165071Sdougb/*% 3222153816Sdougb * proveunsecure walks down from the SEP looking for a break in the 3223165071Sdougb * chain of trust. That occurs when we can prove the DS record does 3224153816Sdougb * not exist at a delegation point or the DS exists at a delegation 3225153816Sdougb * but we don't support the algorithm/digest. 3226165071Sdougb * 3227165071Sdougb * If DLV is active and we look for a DLV record at or below the 3228165071Sdougb * point we go insecure. If found we restart the validation process. 3229165071Sdougb * If not found or DLV isn't active we mark the response as a answer. 3230165071Sdougb * 3231165071Sdougb * Returns: 3232165071Sdougb * \li ISC_R_SUCCESS val->event->name is in a unsecure zone 3233165071Sdougb * \li DNS_R_WAIT validation is in progress. 3234165071Sdougb * \li DNS_R_MUSTBESECURE val->event->name is supposed to be secure 3235165071Sdougb * (policy) but we proved that it is unsecure. 3236165071Sdougb * \li DNS_R_NOVALIDSIG 3237165071Sdougb * \li DNS_R_NOVALIDNSEC 3238165071Sdougb * \li DNS_R_NOTINSECURE 3239205292Sdougb * \li DNS_R_BROKENCHAIN 3240153816Sdougb */ 3241135446Strhodesstatic isc_result_t 3242186462Sdougbproveunsecure(dns_validator_t *val, isc_boolean_t have_ds, isc_boolean_t resume) 3243186462Sdougb{ 3244135446Strhodes isc_result_t result; 3245153816Sdougb dns_fixedname_t fixedsecroot; 3246153816Sdougb dns_name_t *secroot; 3247135446Strhodes dns_name_t *tname; 3248153816Sdougb char namebuf[DNS_NAME_FORMATSIZE]; 3249193149Sdougb dns_name_t *found; 3250193149Sdougb dns_fixedname_t fixedfound; 3251135446Strhodes 3252153816Sdougb dns_fixedname_init(&fixedsecroot); 3253153816Sdougb secroot = dns_fixedname_name(&fixedsecroot); 3254193149Sdougb dns_fixedname_init(&fixedfound); 3255193149Sdougb found = dns_fixedname_name(&fixedfound); 3256153816Sdougb if (val->havedlvsep) 3257153816Sdougb dns_name_copy(dns_fixedname_name(&val->dlvsep), secroot, NULL); 3258153816Sdougb else { 3259202961Sdougb unsigned int labels; 3260174187Sdougb dns_name_copy(val->event->name, secroot, NULL); 3261174187Sdougb /* 3262174187Sdougb * If this is a response to a DS query, we need to look in 3263174187Sdougb * the parent zone for the trust anchor. 3264174187Sdougb */ 3265202961Sdougb 3266202961Sdougb labels = dns_name_countlabels(secroot); 3267202961Sdougb if (val->event->type == dns_rdatatype_ds && labels > 1U) 3268202961Sdougb dns_name_getlabelsequence(secroot, 1, labels - 1, 3269202961Sdougb secroot); 3270153816Sdougb result = dns_keytable_finddeepestmatch(val->keytable, 3271174187Sdougb secroot, secroot); 3272153816Sdougb if (result == ISC_R_NOTFOUND) { 3273143731Sdougb if (val->mustbesecure) { 3274143731Sdougb validator_log(val, ISC_LOG_WARNING, 3275224092Sdougb "must be secure failure, " 3276224092Sdougb "not beneath secure root"); 3277143731Sdougb result = DNS_R_MUSTBESECURE; 3278143731Sdougb goto out; 3279224092Sdougb } else 3280224092Sdougb validator_log(val, ISC_LOG_DEBUG(3), 3281224092Sdougb "not beneath secure root"); 3282153816Sdougb if (val->view->dlv == NULL || DLVTRIED(val)) { 3283214586Sdougb markanswer(val, "proveunsecure (1)"); 3284153816Sdougb return (ISC_R_SUCCESS); 3285153816Sdougb } 3286153816Sdougb return (startfinddlvsep(val, dns_rootname)); 3287153816Sdougb } else if (result != ISC_R_SUCCESS) 3288153816Sdougb return (result); 3289135446Strhodes } 3290135446Strhodes 3291135446Strhodes if (!resume) { 3292153816Sdougb /* 3293153816Sdougb * We are looking for breaks below the SEP so add a label. 3294153816Sdougb */ 3295153816Sdougb val->labels = dns_name_countlabels(secroot) + 1; 3296135446Strhodes } else { 3297135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), "resuming proveunsecure"); 3298186462Sdougb /* 3299186462Sdougb * If we have a DS rdataset and it is secure then check if 3300186462Sdougb * the DS rdataset has a supported algorithm combination. 3301224092Sdougb * If not this is an insecure delegation as far as this 3302186462Sdougb * resolver is concerned. Fall back to DLV if available. 3303186462Sdougb */ 3304186462Sdougb if (have_ds && val->frdataset.trust >= dns_trust_secure && 3305153816Sdougb !check_ds(val, dns_fixedname_name(&val->fname), 3306153816Sdougb &val->frdataset)) { 3307153816Sdougb dns_name_format(dns_fixedname_name(&val->fname), 3308153816Sdougb namebuf, sizeof(namebuf)); 3309186462Sdougb if ((val->view->dlv == NULL || DLVTRIED(val)) && 3310186462Sdougb val->mustbesecure) { 3311135446Strhodes validator_log(val, ISC_LOG_WARNING, 3312224092Sdougb "must be secure failure at '%s', " 3313224092Sdougb "can't fall back to DLV", 3314153816Sdougb namebuf); 3315135446Strhodes result = DNS_R_MUSTBESECURE; 3316135446Strhodes goto out; 3317135446Strhodes } 3318135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 3319153816Sdougb "no supported algorithm/digest (%s/DS)", 3320153816Sdougb namebuf); 3321153816Sdougb if (val->view->dlv == NULL || DLVTRIED(val)) { 3322214586Sdougb markanswer(val, "proveunsecure (2)"); 3323153816Sdougb result = ISC_R_SUCCESS; 3324153816Sdougb goto out; 3325153816Sdougb } 3326234010Sdougb return(startfinddlvsep(val, 3327234010Sdougb dns_fixedname_name(&val->fname))); 3328135446Strhodes } 3329135446Strhodes val->labels++; 3330135446Strhodes } 3331135446Strhodes 3332135446Strhodes for (; 3333135446Strhodes val->labels <= dns_name_countlabels(val->event->name); 3334135446Strhodes val->labels++) 3335135446Strhodes { 3336135446Strhodes 3337135446Strhodes dns_fixedname_init(&val->fname); 3338135446Strhodes tname = dns_fixedname_name(&val->fname); 3339135446Strhodes if (val->labels == dns_name_countlabels(val->event->name)) 3340135446Strhodes dns_name_copy(val->event->name, tname, NULL); 3341135446Strhodes else 3342135446Strhodes dns_name_split(val->event->name, val->labels, 3343135446Strhodes NULL, tname); 3344135446Strhodes 3345135446Strhodes dns_name_format(tname, namebuf, sizeof(namebuf)); 3346135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 3347135446Strhodes "checking existence of DS at '%s'", 3348135446Strhodes namebuf); 3349135446Strhodes 3350135446Strhodes result = view_find(val, tname, dns_rdatatype_ds); 3351135446Strhodes if (result == DNS_R_NXRRSET || result == DNS_R_NCACHENXRRSET) { 3352135446Strhodes /* 3353135446Strhodes * There is no DS. If this is a delegation, 3354224092Sdougb * we may be done. 3355135446Strhodes */ 3356214586Sdougb /* 3357214586Sdougb * If we have "trust == answer" then this namespace 3358214586Sdougb * has switched from insecure to should be secure. 3359214586Sdougb */ 3360214586Sdougb if (DNS_TRUST_PENDING(val->frdataset.trust) || 3361214586Sdougb DNS_TRUST_ANSWER(val->frdataset.trust)) { 3362214586Sdougb result = create_validator(val, tname, 3363214586Sdougb dns_rdatatype_ds, 3364214586Sdougb &val->frdataset, 3365214586Sdougb NULL, dsvalidated, 3366214586Sdougb "proveunsecure"); 3367224092Sdougb if (result != ISC_R_SUCCESS) 3368170222Sdougb goto out; 3369170222Sdougb return (DNS_R_WAIT); 3370170222Sdougb } 3371193149Sdougb /* 3372193149Sdougb * Zones using NSEC3 don't return a NSEC RRset so 3373193149Sdougb * we need to use dns_view_findzonecut2 to find 3374193149Sdougb * the zone cut. 3375193149Sdougb */ 3376193149Sdougb if (result == DNS_R_NXRRSET && 3377193149Sdougb !dns_rdataset_isassociated(&val->frdataset) && 3378193149Sdougb dns_view_findzonecut2(val->view, tname, found, 3379224092Sdougb 0, 0, ISC_FALSE, ISC_FALSE, 3380224092Sdougb NULL, NULL) == ISC_R_SUCCESS && 3381193149Sdougb dns_name_equal(tname, found)) { 3382193149Sdougb if (val->mustbesecure) { 3383193149Sdougb validator_log(val, ISC_LOG_WARNING, 3384224092Sdougb "must be secure failure, " 3385224092Sdougb "no DS at zone cut"); 3386193149Sdougb return (DNS_R_MUSTBESECURE); 3387193149Sdougb } 3388193149Sdougb if (val->view->dlv == NULL || DLVTRIED(val)) { 3389214586Sdougb markanswer(val, "proveunsecure (3)"); 3390193149Sdougb return (ISC_R_SUCCESS); 3391193149Sdougb } 3392193149Sdougb return (startfinddlvsep(val, tname)); 3393193149Sdougb } 3394135446Strhodes if (val->frdataset.trust < dns_trust_secure) { 3395135446Strhodes /* 3396135446Strhodes * This shouldn't happen, since the negative 3397135446Strhodes * response should have been validated. Since 3398135446Strhodes * there's no way of validating existing 3399135446Strhodes * negative response blobs, give up. 3400135446Strhodes */ 3401224092Sdougb validator_log(val, ISC_LOG_WARNING, 3402224092Sdougb "can't validate existing " 3403224092Sdougb "negative responses (no DS)"); 3404135446Strhodes result = DNS_R_NOVALIDSIG; 3405135446Strhodes goto out; 3406135446Strhodes } 3407135446Strhodes if (isdelegation(tname, &val->frdataset, result)) { 3408135446Strhodes if (val->mustbesecure) { 3409135446Strhodes validator_log(val, ISC_LOG_WARNING, 3410224092Sdougb "must be secure failure, " 3411224092Sdougb "%s is a delegation", 3412224092Sdougb namebuf); 3413135446Strhodes return (DNS_R_MUSTBESECURE); 3414135446Strhodes } 3415153816Sdougb if (val->view->dlv == NULL || DLVTRIED(val)) { 3416214586Sdougb markanswer(val, "proveunsecure (4)"); 3417153816Sdougb return (ISC_R_SUCCESS); 3418153816Sdougb } 3419153816Sdougb return (startfinddlvsep(val, tname)); 3420135446Strhodes } 3421135446Strhodes continue; 3422225361Sdougb } else if (result == DNS_R_CNAME) { 3423225361Sdougb if (DNS_TRUST_PENDING(val->frdataset.trust) || 3424225361Sdougb DNS_TRUST_ANSWER(val->frdataset.trust)) { 3425225361Sdougb result = create_validator(val, tname, 3426225361Sdougb dns_rdatatype_cname, 3427225361Sdougb &val->frdataset, 3428225361Sdougb NULL, cnamevalidated, 3429225361Sdougb "proveunsecure " 3430225361Sdougb "(cname)"); 3431225361Sdougb if (result != ISC_R_SUCCESS) 3432225361Sdougb goto out; 3433225361Sdougb return (DNS_R_WAIT); 3434225361Sdougb } 3435225361Sdougb continue; 3436135446Strhodes } else if (result == ISC_R_SUCCESS) { 3437135446Strhodes /* 3438135446Strhodes * There is a DS here. Verify that it's secure and 3439135446Strhodes * continue. 3440135446Strhodes */ 3441135446Strhodes if (val->frdataset.trust >= dns_trust_secure) { 3442153816Sdougb if (!check_ds(val, tname, &val->frdataset)) { 3443135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 3444153816Sdougb "no supported algorithm/" 3445153816Sdougb "digest (%s/DS)", namebuf); 3446135446Strhodes if (val->mustbesecure) { 3447135446Strhodes validator_log(val, 3448135446Strhodes ISC_LOG_WARNING, 3449224092Sdougb "must be secure failure, " 3450224092Sdougb "no supported algorithm/" 3451224092Sdougb "digest (%s/DS)", 3452224092Sdougb namebuf); 3453135446Strhodes result = DNS_R_MUSTBESECURE; 3454135446Strhodes goto out; 3455135446Strhodes } 3456153816Sdougb if (val->view->dlv == NULL || 3457153816Sdougb DLVTRIED(val)) { 3458214586Sdougb markanswer(val, 3459214586Sdougb "proveunsecure (5)"); 3460153816Sdougb result = ISC_R_SUCCESS; 3461153816Sdougb goto out; 3462153816Sdougb } 3463234010Sdougb return(startfinddlvsep(val, tname)); 3464135446Strhodes } 3465135446Strhodes continue; 3466135446Strhodes } 3467135446Strhodes else if (!dns_rdataset_isassociated(&val->fsigrdataset)) 3468135446Strhodes { 3469224092Sdougb validator_log(val, ISC_LOG_DEBUG(3), 3470224092Sdougb "DS is unsigned"); 3471135446Strhodes result = DNS_R_NOVALIDSIG; 3472135446Strhodes goto out; 3473135446Strhodes } 3474214586Sdougb /* 3475214586Sdougb * Validate / re-validate answer. 3476214586Sdougb */ 3477135446Strhodes result = create_validator(val, tname, dns_rdatatype_ds, 3478135446Strhodes &val->frdataset, 3479135446Strhodes &val->fsigrdataset, 3480135446Strhodes dsvalidated, 3481135446Strhodes "proveunsecure"); 3482135446Strhodes if (result != ISC_R_SUCCESS) 3483135446Strhodes goto out; 3484135446Strhodes return (DNS_R_WAIT); 3485135446Strhodes } else if (result == DNS_R_NXDOMAIN || 3486165071Sdougb result == DNS_R_NCACHENXDOMAIN) { 3487135446Strhodes /* 3488135446Strhodes * This is not a zone cut. Assuming things are 3489135446Strhodes * as expected, continue. 3490135446Strhodes */ 3491135446Strhodes if (!dns_rdataset_isassociated(&val->frdataset)) { 3492135446Strhodes /* 3493135446Strhodes * There should be an NSEC here, since we 3494135446Strhodes * are still in a secure zone. 3495135446Strhodes */ 3496135446Strhodes result = DNS_R_NOVALIDNSEC; 3497135446Strhodes goto out; 3498216175Sdougb } else if (DNS_TRUST_PENDING(val->frdataset.trust) || 3499216175Sdougb DNS_TRUST_ANSWER(val->frdataset.trust)) { 3500216175Sdougb /* 3501216175Sdougb * If we have "trust == answer" then this namespace 3502216175Sdougb * has switched from insecure to should be secure. 3503216175Sdougb */ 3504216175Sdougb result = create_validator(val, tname, 3505216175Sdougb dns_rdatatype_ds, 3506216175Sdougb &val->frdataset, 3507216175Sdougb NULL, dsvalidated, 3508216175Sdougb "proveunsecure"); 3509216175Sdougb if (result != ISC_R_SUCCESS) 3510216175Sdougb goto out; 3511216175Sdougb return (DNS_R_WAIT); 3512135446Strhodes } else if (val->frdataset.trust < dns_trust_secure) { 3513135446Strhodes /* 3514135446Strhodes * This shouldn't happen, since the negative 3515135446Strhodes * response should have been validated. Since 3516135446Strhodes * there's no way of validating existing 3517135446Strhodes * negative response blobs, give up. 3518135446Strhodes */ 3519224092Sdougb validator_log(val, ISC_LOG_WARNING, 3520224092Sdougb "can't validate existing " 3521224092Sdougb "negative responses " 3522224092Sdougb "(not a zone cut)"); 3523135446Strhodes result = DNS_R_NOVALIDSIG; 3524135446Strhodes goto out; 3525135446Strhodes } 3526135446Strhodes continue; 3527135446Strhodes } else if (result == ISC_R_NOTFOUND) { 3528135446Strhodes /* 3529135446Strhodes * We don't know anything about the DS. Find it. 3530135446Strhodes */ 3531135446Strhodes result = create_fetch(val, tname, dns_rdatatype_ds, 3532135446Strhodes dsfetched2, "proveunsecure"); 3533135446Strhodes if (result != ISC_R_SUCCESS) 3534135446Strhodes goto out; 3535135446Strhodes return (DNS_R_WAIT); 3536205292Sdougb } else if (result == DNS_R_BROKENCHAIN) 3537205292Sdougb return (result); 3538135446Strhodes } 3539193149Sdougb 3540214586Sdougb /* Couldn't complete insecurity proof */ 3541135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), "insecurity proof failed"); 3542224092Sdougb return (DNS_R_NOTINSECURE); 3543135446Strhodes 3544135446Strhodes out: 3545135446Strhodes if (dns_rdataset_isassociated(&val->frdataset)) 3546135446Strhodes dns_rdataset_disassociate(&val->frdataset); 3547135446Strhodes if (dns_rdataset_isassociated(&val->fsigrdataset)) 3548135446Strhodes dns_rdataset_disassociate(&val->fsigrdataset); 3549135446Strhodes return (result); 3550135446Strhodes} 3551135446Strhodes 3552165071Sdougb/*% 3553165071Sdougb * Reset state and revalidate the answer using DLV. 3554165071Sdougb */ 3555165071Sdougbstatic void 3556153816Sdougbdlv_validator_start(dns_validator_t *val) { 3557153816Sdougb isc_event_t *event; 3558153816Sdougb 3559153816Sdougb validator_log(val, ISC_LOG_DEBUG(3), "dlv_validator_start"); 3560153816Sdougb 3561153816Sdougb /* 3562153816Sdougb * Reset state and try again. 3563153816Sdougb */ 3564153816Sdougb val->attributes &= VALATTR_DLVTRIED; 3565153816Sdougb val->options &= ~DNS_VALIDATOR_DLV; 3566153816Sdougb 3567153816Sdougb event = (isc_event_t *)val->event; 3568153816Sdougb isc_task_send(val->task, &event); 3569153816Sdougb} 3570153816Sdougb 3571165071Sdougb/*% 3572165071Sdougb * Start the validation process. 3573165071Sdougb * 3574193149Sdougb * Attempt to validate the answer based on the category it appears to 3575165071Sdougb * fall in. 3576165071Sdougb * \li 1. secure positive answer. 3577165071Sdougb * \li 2. unsecure positive answer. 3578165071Sdougb * \li 3. a negative answer (secure or unsecure). 3579165071Sdougb * 3580165071Sdougb * Note a answer that appears to be a secure positive answer may actually 3581224092Sdougb * be an unsecure positive answer. 3582165071Sdougb */ 3583135446Strhodesstatic void 3584135446Strhodesvalidator_start(isc_task_t *task, isc_event_t *event) { 3585135446Strhodes dns_validator_t *val; 3586135446Strhodes dns_validatorevent_t *vevent; 3587135446Strhodes isc_boolean_t want_destroy = ISC_FALSE; 3588135446Strhodes isc_result_t result = ISC_R_FAILURE; 3589135446Strhodes 3590135446Strhodes UNUSED(task); 3591135446Strhodes REQUIRE(event->ev_type == DNS_EVENT_VALIDATORSTART); 3592135446Strhodes vevent = (dns_validatorevent_t *)event; 3593135446Strhodes val = vevent->validator; 3594135446Strhodes 3595193149Sdougb /* If the validator has been canceled, val->event == NULL */ 3596135446Strhodes if (val->event == NULL) 3597135446Strhodes return; 3598135446Strhodes 3599153816Sdougb if (DLVTRIED(val)) 3600153816Sdougb validator_log(val, ISC_LOG_DEBUG(3), "restarting using DLV"); 3601153816Sdougb else 3602153816Sdougb validator_log(val, ISC_LOG_DEBUG(3), "starting"); 3603135446Strhodes 3604135446Strhodes LOCK(&val->lock); 3605135446Strhodes 3606170222Sdougb if ((val->options & DNS_VALIDATOR_DLV) != 0 && 3607170222Sdougb val->event->rdataset != NULL) { 3608153816Sdougb validator_log(val, ISC_LOG_DEBUG(3), "looking for DLV"); 3609153816Sdougb result = startfinddlvsep(val, dns_rootname); 3610153816Sdougb } else if (val->event->rdataset != NULL && 3611153816Sdougb val->event->sigrdataset != NULL) { 3612135446Strhodes isc_result_t saved_result; 3613135446Strhodes 3614135446Strhodes /* 3615135446Strhodes * This looks like a simple validation. We say "looks like" 3616135446Strhodes * because it might end up requiring an insecurity proof. 3617135446Strhodes */ 3618135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 3619135446Strhodes "attempting positive response validation"); 3620135446Strhodes 3621135446Strhodes INSIST(dns_rdataset_isassociated(val->event->rdataset)); 3622135446Strhodes INSIST(dns_rdataset_isassociated(val->event->sigrdataset)); 3623135446Strhodes result = start_positive_validation(val); 3624135446Strhodes if (result == DNS_R_NOVALIDSIG && 3625135446Strhodes (val->attributes & VALATTR_TRIEDVERIFY) == 0) 3626135446Strhodes { 3627135446Strhodes saved_result = result; 3628135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 3629135446Strhodes "falling back to insecurity proof"); 3630135446Strhodes val->attributes |= VALATTR_INSECURITY; 3631186462Sdougb result = proveunsecure(val, ISC_FALSE, ISC_FALSE); 3632135446Strhodes if (result == DNS_R_NOTINSECURE) 3633135446Strhodes result = saved_result; 3634135446Strhodes } 3635214586Sdougb } else if (val->event->rdataset != NULL && 3636214586Sdougb val->event->rdataset->type != 0) { 3637135446Strhodes /* 3638135446Strhodes * This is either an unsecure subdomain or a response from 3639135446Strhodes * a broken server. 3640135446Strhodes */ 3641135446Strhodes INSIST(dns_rdataset_isassociated(val->event->rdataset)); 3642135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 3643135446Strhodes "attempting insecurity proof"); 3644135446Strhodes 3645135446Strhodes val->attributes |= VALATTR_INSECURITY; 3646186462Sdougb result = proveunsecure(val, ISC_FALSE, ISC_FALSE); 3647224092Sdougb if (result == DNS_R_NOTINSECURE) 3648224092Sdougb validator_log(val, ISC_LOG_INFO, 3649224092Sdougb "got insecure response; " 3650224092Sdougb "parent indicates it should be secure"); 3651135446Strhodes } else if (val->event->rdataset == NULL && 3652135446Strhodes val->event->sigrdataset == NULL) 3653135446Strhodes { 3654135446Strhodes /* 3655135446Strhodes * This is a nonexistence validation. 3656135446Strhodes */ 3657135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), 3658135446Strhodes "attempting negative response validation"); 3659135446Strhodes 3660135446Strhodes if (val->event->message->rcode == dns_rcode_nxdomain) { 3661135446Strhodes val->attributes |= VALATTR_NEEDNOQNAME; 3662135446Strhodes val->attributes |= VALATTR_NEEDNOWILDCARD; 3663135446Strhodes } else 3664135446Strhodes val->attributes |= VALATTR_NEEDNODATA; 3665135446Strhodes result = nsecvalidate(val, ISC_FALSE); 3666214586Sdougb } else if (val->event->rdataset != NULL && 3667223812Sdougb NEGATIVE(val->event->rdataset)) 3668214586Sdougb { 3669214586Sdougb /* 3670214586Sdougb * This is a nonexistence validation. 3671214586Sdougb */ 3672214586Sdougb validator_log(val, ISC_LOG_DEBUG(3), 3673214586Sdougb "attempting negative response validation"); 3674214586Sdougb 3675214586Sdougb if (val->event->rdataset->covers == dns_rdatatype_any) { 3676214586Sdougb val->attributes |= VALATTR_NEEDNOQNAME; 3677214586Sdougb val->attributes |= VALATTR_NEEDNOWILDCARD; 3678214586Sdougb } else 3679214586Sdougb val->attributes |= VALATTR_NEEDNODATA; 3680214586Sdougb result = nsecvalidate(val, ISC_FALSE); 3681135446Strhodes } else { 3682135446Strhodes /* 3683135446Strhodes * This shouldn't happen. 3684135446Strhodes */ 3685135446Strhodes INSIST(0); 3686135446Strhodes } 3687135446Strhodes 3688135446Strhodes if (result != DNS_R_WAIT) { 3689135446Strhodes want_destroy = exit_check(val); 3690135446Strhodes validator_done(val, result); 3691135446Strhodes } 3692135446Strhodes 3693135446Strhodes UNLOCK(&val->lock); 3694135446Strhodes if (want_destroy) 3695135446Strhodes destroy(val); 3696135446Strhodes} 3697135446Strhodes 3698135446Strhodesisc_result_t 3699135446Strhodesdns_validator_create(dns_view_t *view, dns_name_t *name, dns_rdatatype_t type, 3700135446Strhodes dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, 3701135446Strhodes dns_message_t *message, unsigned int options, 3702135446Strhodes isc_task_t *task, isc_taskaction_t action, void *arg, 3703135446Strhodes dns_validator_t **validatorp) 3704135446Strhodes{ 3705225361Sdougb isc_result_t result = ISC_R_FAILURE; 3706135446Strhodes dns_validator_t *val; 3707225361Sdougb isc_task_t *tclone = NULL; 3708135446Strhodes dns_validatorevent_t *event; 3709135446Strhodes 3710135446Strhodes REQUIRE(name != NULL); 3711135446Strhodes REQUIRE(rdataset != NULL || 3712135446Strhodes (rdataset == NULL && sigrdataset == NULL && message != NULL)); 3713135446Strhodes REQUIRE(validatorp != NULL && *validatorp == NULL); 3714135446Strhodes 3715135446Strhodes val = isc_mem_get(view->mctx, sizeof(*val)); 3716135446Strhodes if (val == NULL) 3717135446Strhodes return (ISC_R_NOMEMORY); 3718135446Strhodes val->view = NULL; 3719135446Strhodes dns_view_weakattach(view, &val->view); 3720224092Sdougb 3721135446Strhodes event = (dns_validatorevent_t *) 3722135446Strhodes isc_event_allocate(view->mctx, task, 3723135446Strhodes DNS_EVENT_VALIDATORSTART, 3724135446Strhodes validator_start, NULL, 3725135446Strhodes sizeof(dns_validatorevent_t)); 3726135446Strhodes if (event == NULL) { 3727135446Strhodes result = ISC_R_NOMEMORY; 3728135446Strhodes goto cleanup_val; 3729135446Strhodes } 3730135446Strhodes isc_task_attach(task, &tclone); 3731135446Strhodes event->validator = val; 3732135446Strhodes event->result = ISC_R_FAILURE; 3733135446Strhodes event->name = name; 3734135446Strhodes event->type = type; 3735135446Strhodes event->rdataset = rdataset; 3736135446Strhodes event->sigrdataset = sigrdataset; 3737135446Strhodes event->message = message; 3738135446Strhodes memset(event->proofs, 0, sizeof(event->proofs)); 3739193149Sdougb event->optout = ISC_FALSE; 3740254402Serwin event->secure = ISC_FALSE; 3741135446Strhodes result = isc_mutex_init(&val->lock); 3742135446Strhodes if (result != ISC_R_SUCCESS) 3743135446Strhodes goto cleanup_event; 3744135446Strhodes val->event = event; 3745135446Strhodes val->options = options; 3746135446Strhodes val->attributes = 0; 3747135446Strhodes val->fetch = NULL; 3748135446Strhodes val->subvalidator = NULL; 3749135446Strhodes val->parent = NULL; 3750224092Sdougb 3751135446Strhodes val->keytable = NULL; 3752224092Sdougb result = dns_view_getsecroots(val->view, &val->keytable); 3753224092Sdougb if (result != ISC_R_SUCCESS) 3754262706Serwin goto cleanup_mutex; 3755135446Strhodes val->keynode = NULL; 3756135446Strhodes val->key = NULL; 3757135446Strhodes val->siginfo = NULL; 3758135446Strhodes val->task = task; 3759135446Strhodes val->action = action; 3760135446Strhodes val->arg = arg; 3761135446Strhodes val->labels = 0; 3762135446Strhodes val->currentset = NULL; 3763135446Strhodes val->keyset = NULL; 3764135446Strhodes val->dsset = NULL; 3765153816Sdougb dns_rdataset_init(&val->dlv); 3766135446Strhodes val->seensig = ISC_FALSE; 3767135446Strhodes val->havedlvsep = ISC_FALSE; 3768153816Sdougb val->depth = 0; 3769205292Sdougb val->authcount = 0; 3770205292Sdougb val->authfail = 0; 3771135446Strhodes val->mustbesecure = dns_resolver_getmustbesecure(view->resolver, name); 3772135446Strhodes dns_rdataset_init(&val->frdataset); 3773135446Strhodes dns_rdataset_init(&val->fsigrdataset); 3774135446Strhodes dns_fixedname_init(&val->wild); 3775193149Sdougb dns_fixedname_init(&val->nearest); 3776193149Sdougb dns_fixedname_init(&val->closest); 3777135446Strhodes ISC_LINK_INIT(val, link); 3778135446Strhodes val->magic = VALIDATOR_MAGIC; 3779135446Strhodes 3780166332Sdougb if ((options & DNS_VALIDATOR_DEFER) == 0) 3781166332Sdougb isc_task_send(task, ISC_EVENT_PTR(&event)); 3782135446Strhodes 3783135446Strhodes *validatorp = val; 3784135446Strhodes 3785135446Strhodes return (ISC_R_SUCCESS); 3786135446Strhodes 3787262706Serwin cleanup_mutex: 3788262706Serwin DESTROYLOCK(&val->lock); 3789262706Serwin 3790135446Strhodes cleanup_event: 3791135446Strhodes isc_task_detach(&tclone); 3792165071Sdougb isc_event_free(ISC_EVENT_PTR(&event)); 3793135446Strhodes 3794135446Strhodes cleanup_val: 3795135446Strhodes dns_view_weakdetach(&val->view); 3796135446Strhodes isc_mem_put(view->mctx, val, sizeof(*val)); 3797135446Strhodes 3798135446Strhodes return (result); 3799135446Strhodes} 3800135446Strhodes 3801135446Strhodesvoid 3802166332Sdougbdns_validator_send(dns_validator_t *validator) { 3803166332Sdougb isc_event_t *event; 3804166332Sdougb REQUIRE(VALID_VALIDATOR(validator)); 3805166332Sdougb 3806166332Sdougb LOCK(&validator->lock); 3807166332Sdougb 3808166332Sdougb INSIST((validator->options & DNS_VALIDATOR_DEFER) != 0); 3809166332Sdougb event = (isc_event_t *)validator->event; 3810166332Sdougb validator->options &= ~DNS_VALIDATOR_DEFER; 3811166332Sdougb UNLOCK(&validator->lock); 3812166332Sdougb 3813166332Sdougb isc_task_send(validator->task, ISC_EVENT_PTR(&event)); 3814166332Sdougb} 3815166332Sdougb 3816166332Sdougbvoid 3817135446Strhodesdns_validator_cancel(dns_validator_t *validator) { 3818254402Serwin dns_fetch_t *fetch = NULL; 3819254402Serwin 3820135446Strhodes REQUIRE(VALID_VALIDATOR(validator)); 3821135446Strhodes 3822135446Strhodes LOCK(&validator->lock); 3823135446Strhodes 3824135446Strhodes validator_log(validator, ISC_LOG_DEBUG(3), "dns_validator_cancel"); 3825135446Strhodes 3826218384Sdougb if ((validator->attributes & VALATTR_CANCELED) == 0) { 3827218384Sdougb validator->attributes |= VALATTR_CANCELED; 3828218384Sdougb if (validator->event != NULL) { 3829254402Serwin fetch = validator->fetch; 3830254402Serwin validator->fetch = NULL; 3831135446Strhodes 3832218384Sdougb if (validator->subvalidator != NULL) 3833218384Sdougb dns_validator_cancel(validator->subvalidator); 3834218384Sdougb if ((validator->options & DNS_VALIDATOR_DEFER) != 0) { 3835218384Sdougb validator->options &= ~DNS_VALIDATOR_DEFER; 3836218384Sdougb validator_done(validator, ISC_R_CANCELED); 3837218384Sdougb } 3838166332Sdougb } 3839135446Strhodes } 3840135446Strhodes UNLOCK(&validator->lock); 3841254402Serwin 3842254402Serwin /* Need to cancel and destroy the fetch outside validator lock */ 3843254402Serwin if (fetch != NULL) { 3844254402Serwin dns_resolver_cancelfetch(fetch); 3845254402Serwin dns_resolver_destroyfetch(&fetch); 3846254402Serwin } 3847135446Strhodes} 3848135446Strhodes 3849135446Strhodesstatic void 3850135446Strhodesdestroy(dns_validator_t *val) { 3851135446Strhodes isc_mem_t *mctx; 3852135446Strhodes 3853135446Strhodes REQUIRE(SHUTDOWN(val)); 3854135446Strhodes REQUIRE(val->event == NULL); 3855135446Strhodes REQUIRE(val->fetch == NULL); 3856135446Strhodes 3857135446Strhodes if (val->keynode != NULL) 3858135446Strhodes dns_keytable_detachkeynode(val->keytable, &val->keynode); 3859135446Strhodes else if (val->key != NULL) 3860135446Strhodes dst_key_free(&val->key); 3861135446Strhodes if (val->keytable != NULL) 3862135446Strhodes dns_keytable_detach(&val->keytable); 3863135446Strhodes if (val->subvalidator != NULL) 3864135446Strhodes dns_validator_destroy(&val->subvalidator); 3865153816Sdougb if (val->havedlvsep) 3866153816Sdougb dns_rdataset_disassociate(&val->dlv); 3867153816Sdougb if (dns_rdataset_isassociated(&val->frdataset)) 3868153816Sdougb dns_rdataset_disassociate(&val->frdataset); 3869153816Sdougb if (dns_rdataset_isassociated(&val->fsigrdataset)) 3870153816Sdougb dns_rdataset_disassociate(&val->fsigrdataset); 3871135446Strhodes mctx = val->view->mctx; 3872135446Strhodes if (val->siginfo != NULL) 3873135446Strhodes isc_mem_put(mctx, val->siginfo, sizeof(*val->siginfo)); 3874135446Strhodes DESTROYLOCK(&val->lock); 3875135446Strhodes dns_view_weakdetach(&val->view); 3876135446Strhodes val->magic = 0; 3877135446Strhodes isc_mem_put(mctx, val, sizeof(*val)); 3878135446Strhodes} 3879135446Strhodes 3880135446Strhodesvoid 3881135446Strhodesdns_validator_destroy(dns_validator_t **validatorp) { 3882135446Strhodes dns_validator_t *val; 3883135446Strhodes isc_boolean_t want_destroy = ISC_FALSE; 3884135446Strhodes 3885135446Strhodes REQUIRE(validatorp != NULL); 3886135446Strhodes val = *validatorp; 3887135446Strhodes REQUIRE(VALID_VALIDATOR(val)); 3888135446Strhodes 3889135446Strhodes LOCK(&val->lock); 3890135446Strhodes 3891135446Strhodes val->attributes |= VALATTR_SHUTDOWN; 3892135446Strhodes validator_log(val, ISC_LOG_DEBUG(3), "dns_validator_destroy"); 3893135446Strhodes 3894135446Strhodes want_destroy = exit_check(val); 3895135446Strhodes 3896135446Strhodes UNLOCK(&val->lock); 3897135446Strhodes 3898135446Strhodes if (want_destroy) 3899135446Strhodes destroy(val); 3900135446Strhodes 3901135446Strhodes *validatorp = NULL; 3902135446Strhodes} 3903135446Strhodes 3904135446Strhodesstatic void 3905135446Strhodesvalidator_logv(dns_validator_t *val, isc_logcategory_t *category, 3906135446Strhodes isc_logmodule_t *module, int level, const char *fmt, va_list ap) 3907135446Strhodes{ 3908135446Strhodes char msgbuf[2048]; 3909153816Sdougb static const char spaces[] = " *"; 3910153816Sdougb int depth = val->depth * 2; 3911135446Strhodes 3912135446Strhodes vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); 3913135446Strhodes 3914153816Sdougb if ((unsigned int) depth >= sizeof spaces) 3915153816Sdougb depth = sizeof spaces - 1; 3916153816Sdougb 3917135446Strhodes if (val->event != NULL && val->event->name != NULL) { 3918135446Strhodes char namebuf[DNS_NAME_FORMATSIZE]; 3919135446Strhodes char typebuf[DNS_RDATATYPE_FORMATSIZE]; 3920135446Strhodes 3921135446Strhodes dns_name_format(val->event->name, namebuf, sizeof(namebuf)); 3922135446Strhodes dns_rdatatype_format(val->event->type, typebuf, 3923135446Strhodes sizeof(typebuf)); 3924135446Strhodes isc_log_write(dns_lctx, category, module, level, 3925153816Sdougb "%.*svalidating @%p: %s %s: %s", depth, spaces, 3926153816Sdougb val, namebuf, typebuf, msgbuf); 3927135446Strhodes } else { 3928135446Strhodes isc_log_write(dns_lctx, category, module, level, 3929153816Sdougb "%.*svalidator @%p: %s", depth, spaces, 3930153816Sdougb val, msgbuf); 3931135446Strhodes } 3932135446Strhodes} 3933135446Strhodes 3934135446Strhodesstatic void 3935254402Serwinvalidator_log(void *val, int level, const char *fmt, ...) { 3936135446Strhodes va_list ap; 3937135446Strhodes 3938135446Strhodes if (! isc_log_wouldlog(dns_lctx, level)) 3939135446Strhodes return; 3940135446Strhodes 3941135446Strhodes va_start(ap, fmt); 3942135446Strhodes 3943135446Strhodes validator_logv(val, DNS_LOGCATEGORY_DNSSEC, 3944135446Strhodes DNS_LOGMODULE_VALIDATOR, level, fmt, ap); 3945135446Strhodes va_end(ap); 3946135446Strhodes} 3947135446Strhodes 3948135446Strhodesstatic void 3949135446Strhodesvalidator_logcreate(dns_validator_t *val, 3950135446Strhodes dns_name_t *name, dns_rdatatype_t type, 3951135446Strhodes const char *caller, const char *operation) 3952135446Strhodes{ 3953135446Strhodes char namestr[DNS_NAME_FORMATSIZE]; 3954135446Strhodes char typestr[DNS_RDATATYPE_FORMATSIZE]; 3955135446Strhodes 3956135446Strhodes dns_name_format(name, namestr, sizeof(namestr)); 3957135446Strhodes dns_rdatatype_format(type, typestr, sizeof(typestr)); 3958135446Strhodes validator_log(val, ISC_LOG_DEBUG(9), "%s: creating %s for %s %s", 3959135446Strhodes caller, operation, namestr, typestr); 3960135446Strhodes} 3961