1 /***************************** -*- Java -*- ********************************\
3 * Copyright (c) 2009 VeriSign, Inc. All rights reserved. *
5 * This software is provided solely in connection with the terms of the *
6 * license agreement. Any other use without the prior express written *
7 * permission of VeriSign is completely prohibited. The software and *
8 * documentation are "Commercial Items", as that term is defined in 48 *
9 * C.F.R. section 2.101, consisting of "Commercial Computer Software" and *
10 * "Commercial Computer Software Documentation" as such terms are defined *
11 * in 48 C.F.R. section 252.227-7014(a)(5) and 48 C.F.R. section *
12 * 252.227-7014(a)(1), and used in 48 C.F.R. section 12.212 and 48 C.F.R. *
13 * section 227.7202, as applicable. Pursuant to the above and other *
14 * relevant sections of the Code of Federal Regulations, as applicable, *
15 * VeriSign's publications, commercial computer software, and commercial *
16 * computer software documentation are distributed and licensed to United *
17 * States Government end users with only those rights as granted to all *
18 * other end users, according to the terms and conditions contained in the *
19 * license agreement(s) that accompany the products and software *
22 \***************************************************************************/
24 package com.verisign.tat.dnssec;
26 import org.apache.log4j.Logger;
28 import org.xbill.DNS.*;
29 import org.xbill.DNS.utils.base64;
31 import java.io.IOException;
36 * This resolver module implements a "captive" DNSSEC validator. The
37 * captive validator does not have direct access to the Internet and
38 * DNS system -- instead it attempts to validate DNS messages using
39 * only configured context. This is useful for determining if
40 * responses coming from a given authoritative server will validate
41 * independent of the normal chain of trust.
43 public class CaptiveValidator {
44 // A data structure holding all all of our trusted keys.
45 private TrustAnchorStore mTrustedKeys;
47 // The local validation utilities.
48 private ValUtils mValUtils;
50 // The local verification utility.
51 private DnsSecVerifier mVerifier;
52 private Logger log = Logger.getLogger(this.getClass());
54 // The list of validation errors found.
55 private List<String> mErrorList;
57 public CaptiveValidator() {
58 mVerifier = new DnsSecVerifier();
59 mValUtils = new ValUtils(mVerifier);
60 mTrustedKeys = new TrustAnchorStore();
61 mErrorList = new ArrayList<String>();
64 // ---------------- Module Initialization -------------------
67 * Add a set of trusted keys from a file. The file should be in
68 * DNS master zone file format. Only DNSKEY records will be added.
71 * The file contains the trusted keys.
74 @SuppressWarnings("unchecked")
75 public void addTrustedKeysFromFile(String filename) throws IOException {
76 // First read in the whole trust anchor file.
77 Master master = new Master(filename, Name.root, 0);
78 ArrayList<Record> records = new ArrayList<Record>();
81 while ((r = master.nextRecord()) != null) {
85 // Record.compareTo() should sort them into DNSSEC canonical
86 // order. Don't care about canonical order per se, but do
87 // want them to be formable into RRsets.
88 Collections.sort(records);
90 SRRset cur_rrset = new SRRset();
92 for (Record rec : records) {
93 // Skip RR types that cannot be used as trusted
94 // keys. I.e., everything not a key :)
95 if (rec.getType() != Type.DNSKEY) {
99 // If our cur_rrset is empty, we can just add it.
100 if (cur_rrset.size() == 0) {
101 cur_rrset.addRR(rec);
106 // If this record matches our current RRset, we can just
108 if (cur_rrset.getName().equals(rec.getName()) &&
109 (cur_rrset.getType() == rec.getType()) && (cur_rrset.getDClass() == rec.getDClass())) {
111 cur_rrset.addRR(rec);
115 // Otherwise, we add the rrset to our set of trust anchors.
116 mTrustedKeys.store(cur_rrset);
117 cur_rrset = new SRRset();
118 cur_rrset.addRR(rec);
121 // add the last rrset (if it was not empty)
122 if (cur_rrset.size() > 0) {
123 mTrustedKeys.store(cur_rrset);
127 public void addTrustedKeysFromResponse(Message m) {
128 RRset[] rrsets = m.getSectionRRsets(Section.ANSWER);
130 for (int i = 0; i < rrsets.length; ++i) {
131 if (rrsets[i].getType() == Type.DNSKEY) {
132 SRRset srrset = new SRRset(rrsets[i]);
133 mTrustedKeys.store(srrset);
138 // ----------------- Validation Support ----------------------
141 * This routine normalizes a response. This includes removing
142 * "irrelevant" records from the answer and additional sections
143 * and (re)synthesizing CNAMEs from DNAMEs, if present.
147 private SMessage normalize(SMessage m) {
152 if ((m.getRcode() != Rcode.NOERROR) && (m.getRcode() != Rcode.NXDOMAIN)) {
156 Name qname = m.getQuestion().getName();
157 int qtype = m.getQuestion().getType();
161 // For the ANSWER section, remove all "irrelevant" records and add
162 // synthesized CNAMEs from DNAMEs
163 // This will strip out-of-order CNAMEs as well.
164 List<SRRset> rrset_list = m.getSectionList(Section.ANSWER);
165 Set<Name> additional_names = new HashSet<Name>();
167 for (ListIterator<SRRset> i = rrset_list.listIterator(); i.hasNext();) {
168 SRRset rrset = i.next();
169 int type = rrset.getType();
170 Name n = rrset.getName();
172 // Handle DNAME synthesis; DNAME synthesis does not occur
173 // at the DNAME name itself.
174 if ((type == Type.DNAME) && ValUtils.strictSubdomain(sname, n)) {
175 if (rrset.size() > 1) {
176 log.debug("Found DNAME rrset with size > 1: " + rrset);
177 m.setStatus(SecurityStatus.INVALID);
182 DNAMERecord dname = (DNAMERecord) rrset.first();
185 Name cname_alias = sname.fromDNAME(dname);
187 // Note that synthesized CNAMEs should have a TTL of zero.
188 CNAMERecord cname = new CNAMERecord(sname, dname.getDClass(), 0, cname_alias);
189 SRRset cname_rrset = new SRRset();
190 cname_rrset.addRR(cname);
194 } catch (NameTooLongException e) {
195 log.debug("not adding synthesized CNAME -- " +
196 "generated name is too long", e);
202 // The only records in the ANSWER section not allowed to
203 if (!n.equals(sname)) {
204 log.debug("normalize: removing irrelevant rrset: " + rrset);
210 // Follow the CNAME chain.
211 if (type == Type.CNAME) {
212 if (rrset.size() > 1) {
213 mErrorList.add("Found CNAME rrset with size > 1: " + rrset);
214 m.setStatus(SecurityStatus.INVALID);
219 CNAMERecord cname = (CNAMERecord) rrset.first();
220 sname = cname.getAlias();
225 // Otherwise, make sure that the RRset matches the qtype.
226 if ((qtype != Type.ANY) && (qtype != type)) {
227 log.debug("normalize: removing irrelevant rrset: " + rrset);
231 // Otherwise, fetch the additional names from the relevant rrset.
232 rrsetAdditionalNames(additional_names, rrset);
235 // Get additional names from AUTHORITY
236 rrset_list = m.getSectionList(Section.AUTHORITY);
238 for (SRRset rrset : rrset_list) {
239 rrsetAdditionalNames(additional_names, rrset);
242 // For each record in the additional section, remove it if it is an
243 // address record and not in the collection of additional names found in
244 // ANSWER and AUTHORITY.
245 rrset_list = m.getSectionList(Section.ADDITIONAL);
247 for (Iterator<SRRset> i = rrset_list.iterator(); i.hasNext();) {
248 SRRset rrset = i.next();
249 int type = rrset.getType();
251 if (((type == Type.A) || (type == Type.AAAA)) &&
252 !additional_names.contains(rrset.getName())) {
262 * Extract additional names from the records in an rrset.
264 * @param additional_names
265 * The set to add the additional names to, if any.
267 * The rrset to extract from.
269 private void rrsetAdditionalNames(Set<Name> additional_names, SRRset rrset) {
274 for (Iterator<Record> i = rrset.rrs(); i.hasNext();) {
276 Name add_name = r.getAdditionalName();
278 if (add_name != null) {
279 additional_names.add(add_name);
284 private SRRset findKeys(SMessage message) {
285 Name qname = message.getQName();
286 int qclass = message.getQClass();
288 return mTrustedKeys.find(qname, qclass);
292 * Check to see if a given response needs to go through the
293 * validation process. Typical reasons for this routine to return
294 * false are: CD bit was on in the original request, the response
295 * was already validated, or the response is a kind of message
296 * that is unvalidatable (i.e., SERVFAIL, REFUSED, etc.)
299 * The message to check.
301 * The original request received from the client.
303 * @return true if the response could use validation (although this does not
304 * mean we can actually validate this response).
306 private boolean needsValidation(SMessage message) {
307 int rcode = message.getRcode();
309 if ((rcode != Rcode.NOERROR) && (rcode != Rcode.NXDOMAIN)) {
310 log.debug("cannot validate non-answer.");
311 log.trace("non-answer: " + message);
316 if (!mTrustedKeys.isBelowTrustAnchor(message.getQName(), message.getQClass())) {
324 * Given a "positive" response -- a response that contains an
325 * answer to the question, and no CNAME chain, validate this
326 * response. This generally consists of verifying the answer RRset
327 * and the authority RRsets.
329 * Note that by the time this method is called, the process of
330 * finding the trusted DNSKEY rrset that signs this response must
331 * already have been completed.
334 * The response to validate.
336 * The request that generated this response.
338 * The trusted DNSKEY rrset that matches the signer of the
341 private void validatePositiveResponse(SMessage message, SRRset key_rrset) {
342 Name qname = message.getQName();
343 int qtype = message.getQType();
345 SMessage m = message;
347 // validate the ANSWER section - this will be the answer itself
348 SRRset[] rrsets = m.getSectionRRsets(Section.ANSWER);
351 boolean wcNSEC_ok = false;
352 boolean dname = false;
353 List<NSEC3Record> nsec3s = null;
355 for (int i = 0; i < rrsets.length; i++) {
356 // Skip the CNAME following a (validated) DNAME.
357 // Because of the normalization routines in NameserverClient, there
358 // will always be an unsigned CNAME following a DNAME (unless
360 if (dname && (rrsets[i].getType() == Type.CNAME)) {
365 // Verify the answer rrset.
366 int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
368 // If the (answer) rrset failed to validate, then this
370 if (status != SecurityStatus.SECURE) {
371 mErrorList.add("Positive response has failed ANSWER rrset: " +
373 m.setStatus(SecurityStatus.BOGUS);
378 // Check to see if the rrset is the result of a wildcard expansion.
379 // If so, an additional check will need to be made in the authority
381 wc = ValUtils.rrsetWildcard(rrsets[i]);
383 // Notice a DNAME that should be followed by an unsigned CNAME.
384 if ((qtype != Type.DNAME) && (rrsets[i].getType() == Type.DNAME)) {
389 // validate the AUTHORITY section as well - this will
390 // generally be the NS rrset (which could be missing, no
392 rrsets = m.getSectionRRsets(Section.AUTHORITY);
394 for (int i = 0; i < rrsets.length; i++) {
395 int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
397 // If anything in the authority section fails to be
398 // secure, we have a bad message.
399 if (status != SecurityStatus.SECURE) {
400 mErrorList.add("Positive response has failed AUTHORITY rrset: " +
402 m.setStatus(SecurityStatus.BOGUS);
407 // If this is a positive wildcard response, and we have a
408 // (just verified) NSEC record, try to use it to 1) prove
409 // that qname doesn't exist and 2) that the correct
410 // wildcard was used.
411 if ((wc != null) && (rrsets[i].getType() == Type.NSEC)) {
412 NSECRecord nsec = (NSECRecord) rrsets[i].first();
414 if (ValUtils.nsecProvesNameError(nsec, qname, key_rrset.getName())) {
415 Name nsec_wc = ValUtils.nsecWildcard(qname, nsec);
417 if (!wc.equals(nsec_wc)) {
418 mErrorList.add("Positive wildcard response wasn't generated by the correct wildcard");
419 m.setStatus(SecurityStatus.BOGUS);
428 // Otherwise, if this is a positive wildcard response and we have
429 // NSEC3 records, collect them.
430 if ((wc != null) && (rrsets[i].getType() == Type.NSEC3)) {
431 if (nsec3s == null) {
432 nsec3s = new ArrayList<NSEC3Record>();
435 nsec3s.add((NSEC3Record) rrsets[i].first());
439 // If this was a positive wildcard response that we haven't
440 // already proven, and we have NSEC3 records, try to prove it
441 // using the NSEC3 records.
442 if ((wc != null) && !wcNSEC_ok && (nsec3s != null)) {
443 if (NSEC3ValUtils.proveWildcard(nsec3s, qname, key_rrset.getName(),
449 // If after all this, we still haven't proven the positive
450 // wildcard response, fail.
451 if ((wc != null) && !wcNSEC_ok) {
452 // log.debug("positive response was wildcard expansion and " +
453 // "did not prove original data did not exist");
454 m.setStatus(SecurityStatus.BOGUS);
459 log.trace("Successfully validated positive response");
460 m.setStatus(SecurityStatus.SECURE);
463 /** Given a "referral" type response (RCODE=NOERROR, ANSWER=0,
464 * AUTH=NS records under the zone we thought we were talking to,
465 * etc.), validate it. This consists of validating the DS or
466 * NSEC/NSEC3 RRsets and noting that the response does indeed look
471 private void validateReferral(SMessage message, SRRset key_rrset) {
472 SMessage m = message;
474 if (m.getCount(Section.ANSWER) > 0) {
475 m.setStatus(SecurityStatus.INVALID);
480 // validate the AUTHORITY section.
481 SRRset[] rrsets = m.getSectionRRsets(Section.AUTHORITY);
483 boolean secure_delegation = false;
484 Name delegation = null;
485 Name nsec3zone = null;
486 NSECRecord nsec = null;
487 List<NSEC3Record> nsec3s = null;
489 // validate the AUTHORITY section as well - this will generally be the
490 // NS rrset, plus proof of a secure delegation or not
491 rrsets = m.getSectionRRsets(Section.AUTHORITY);
493 for (int i = 0; i < rrsets.length; i++) {
494 int type = rrsets[i].getType();
496 // The NS RRset won't be signed, but everything else
497 // should be. FIXME: if we have an unexpected type here
498 // with a bad signature, we will fail when we otherwise
499 // might just have warned about the odd record. Consider
500 // checking the types first, then validating.
501 if (type != Type.NS) {
502 int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
504 // If anything in the authority section fails to be
505 // secure, we have a bad message.
506 if (status != SecurityStatus.SECURE) {
507 mErrorList.add("Referral response has failed AUTHORITY rrset: " +
509 m.setStatus(SecurityStatus.BOGUS);
517 secure_delegation = true;
521 delegation = rrsets[i].getName();
525 nsec = (NSECRecord) rrsets[i].first();
529 if (nsec3s == null) {
530 nsec3s = new ArrayList<NSEC3Record>();
533 NSEC3Record nsec3 = (NSEC3Record) rrsets[i].first();
535 // this is a hack, really.
536 nsec3zone = rrsets[i].getSignerName();
541 log.warn("Encountered unexpected type in a REFERRAL response: "
542 + Type.string(type));
548 // At this point, all validatable RRsets have been validated.
549 // Now to check to see if we have a valid combination of
551 if (delegation == null) {
552 // somehow we have a referral without an NS rrset.
553 mErrorList.add("Apparent referral does not contain NS RRset");
554 m.setStatus(SecurityStatus.BOGUS);
559 if (secure_delegation) {
560 if ((nsec != null) || ((nsec3s != null) && (nsec3s.size() > 0))) {
561 // we found both a DS rrset *and* NSEC/NSEC3 rrsets!
562 mErrorList.add("Referral contains both DS and NSEC/NSEC3 RRsets");
563 m.setStatus(SecurityStatus.BOGUS);
568 // otherwise, we are done.
569 m.setStatus(SecurityStatus.SECURE);
574 // Note: not going to care if both NSEC and NSEC3 rrsets were present.
576 byte status = ValUtils.nsecProvesNoDS(nsec, delegation);
578 if (status != SecurityStatus.SECURE) {
579 // The NSEC *must* prove that there was no DS
580 // record. The INSECURE state here is still bogus.
581 mErrorList.add("Referral does not contain a NSEC record proving no DS");
582 m.setStatus(SecurityStatus.BOGUS);
587 m.setStatus(SecurityStatus.SECURE);
592 if (nsec3s != null && nsec3s.size() > 0) {
593 byte status = NSEC3ValUtils.proveNoDS(nsec3s, delegation, nsec3zone, mErrorList);
595 if (status != SecurityStatus.SECURE) {
596 // the NSEC3 RRs MUST prove no DS, so the INDETERMINATE state is
598 mErrorList.add("Referral does not contain NSEC3 record(s) proving no DS");
599 m.setStatus(SecurityStatus.BOGUS);
604 m.setStatus(SecurityStatus.SECURE);
609 // failed to find proof either way.
610 mErrorList.add("Referral does not contain proof of no DS");
611 m.setStatus(SecurityStatus.BOGUS);
614 // FIXME: write CNAME validation code.
615 private void validateCNAMEResponse(SMessage message, SRRset key_rrset) {}
618 * Given an "ANY" response -- a response that contains an answer
619 * to a qtype==ANY question, with answers. This consists of simply
620 * verifying all present answer/auth RRsets, with no checking that
621 * all types are present.
623 * NOTE: it may be possible to get parent-side delegation point
624 * records here, which won't all be signed. Right now, this
625 * routine relies on the upstream iterative resolver to not return
626 * these responses -- instead treating them as referrals.
628 * NOTE: RFC 4035 is silent on this issue, so this may change upon
631 * Note that by the time this method is called, the process of
632 * finding the trusted DNSKEY rrset that signs this response must
633 * already have been completed.
636 * The response to validate.
638 * The trusted DNSKEY rrset that matches the signer of the
641 private void validateAnyResponse(SMessage message, SRRset key_rrset) {
642 int qtype = message.getQType();
644 if (qtype != Type.ANY) {
645 throw new IllegalArgumentException("ANY validation called on non-ANY response.");
648 SMessage m = message;
650 // validate the ANSWER section.
651 SRRset[] rrsets = m.getSectionRRsets(Section.ANSWER);
653 for (int i = 0; i < rrsets.length; i++) {
654 int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
656 // If the (answer) rrset failed to validate, then this message is
658 if (status != SecurityStatus.SECURE) {
659 mErrorList.add("Positive response has failed ANSWER rrset: " +
661 m.setStatus(SecurityStatus.BOGUS);
667 // validate the AUTHORITY section as well - this will be the NS rrset
668 // (which could be missing, no problem)
669 rrsets = m.getSectionRRsets(Section.AUTHORITY);
671 for (int i = 0; i < rrsets.length; i++) {
672 int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
674 // If anything in the authority section fails to be secure, we have
676 if (status != SecurityStatus.SECURE) {
677 mErrorList.add("Positive response has failed AUTHORITY rrset: " +
679 m.setStatus(SecurityStatus.BOGUS);
685 log.trace("Successfully validated positive ANY response");
686 m.setStatus(SecurityStatus.SECURE);
690 * Validate a NOERROR/NODATA signed response -- a response that
691 * has a NOERROR Rcode but no ANSWER section RRsets. This consists
692 * of verifying the authority section rrsets and making certain
693 * that the authority section NSEC/NSEC3s proves that the qname
694 * does exist and the qtype doesn't.
696 * Note that by the time this method is called, the process of
697 * finding the trusted DNSKEY rrset that signs this response must
698 * already have been completed.
701 * The response to validate.
703 * The request that generated this response.
705 * The trusted DNSKEY rrset that signs this response.
707 private void validateNodataResponse(SMessage message,
709 List<String> errorList) {
710 Name qname = message.getQName();
711 int qtype = message.getQType();
713 SMessage m = message;
715 // Since we are here, there must be nothing in the ANSWER
716 // section to validate.
718 // validate the AUTHORITY section
719 SRRset[] rrsets = m.getSectionRRsets(Section.AUTHORITY);
721 // If true, then the NODATA has been proven.
722 boolean hasValidNSEC = false;
724 // for wildcard NODATA responses. This is the proven closest
728 // for wildcard NODATA responses. This is the wildcard NSEC.
729 NSECRecord wc = null;
731 // A collection of NSEC3 RRs found in the authority section.
732 List<NSEC3Record> nsec3s = null;
734 // The RRSIG signer field for the NSEC3 RRs.
735 Name nsec3Signer = null;
737 for (int i = 0; i < rrsets.length; i++) {
738 int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
740 if (status != SecurityStatus.SECURE) {
741 mErrorList.add("NODATA response has failed AUTHORITY rrset: " +
743 m.setStatus(SecurityStatus.BOGUS);
748 // If we encounter an NSEC record, try to use it to prove NODATA.
749 // This needs to handle the ENT NODATA case.
750 if (rrsets[i].getType() == Type.NSEC) {
751 NSECRecord nsec = (NSECRecord) rrsets[i].first();
753 if (ValUtils.nsecProvesNodata(nsec, qname, qtype)) {
756 if (nsec.getName().isWild()) {
759 } else if (ValUtils.nsecProvesNameError(nsec, qname, rrsets[i].getSignerName())) {
760 ce = ValUtils.closestEncloser(qname, nsec);
764 // Collect any NSEC3 records present.
765 if (rrsets[i].getType() == Type.NSEC3) {
766 if (nsec3s == null) {
767 nsec3s = new ArrayList<NSEC3Record>();
770 nsec3s.add((NSEC3Record) rrsets[i].first());
771 nsec3Signer = rrsets[i].getSignerName();
775 // check to see if we have a wildcard NODATA proof.
777 // The wildcard NODATA is 1 NSEC proving that qname does not
778 // exists (and also proving what the closest encloser is), and
779 // 1 NSEC showing the matching wildcard, which must be
780 // *.closest_encloser.
781 if ((ce != null) || (wc != null)) {
783 Name wc_name = new Name("*", ce);
785 if (!wc_name.equals(wc.getName())) {
786 hasValidNSEC = false;
788 } catch (TextParseException e) {
793 NSEC3ValUtils.stripUnknownAlgNSEC3s(nsec3s);
795 if (!hasValidNSEC && (nsec3s != null) && (nsec3s.size() > 0)) {
796 // try to prove NODATA with our NSEC3 record(s)
797 hasValidNSEC = NSEC3ValUtils.proveNodata(nsec3s, qname, qtype,
798 nsec3Signer, errorList);
802 log.debug("NODATA response failed to prove NODATA " +
803 "status with NSEC/NSEC3");
804 log.trace("Failed NODATA:\n" + m);
805 mErrorList.add("NODATA response failed to prove NODATA status with NSEC/NSEC3");
806 m.setStatus(SecurityStatus.BOGUS);
811 log.trace("successfully validated NODATA response.");
812 m.setStatus(SecurityStatus.SECURE);
816 * Validate a NAMEERROR signed response -- a response that has a
817 * NXDOMAIN Rcode. This consists of verifying the authority
818 * section rrsets and making certain that the authority section
819 * NSEC proves that the qname doesn't exist and the covering
820 * wildcard also doesn't exist..
822 * Note that by the time this method is called, the process of
823 * finding the trusted DNSKEY rrset that signs this response must
824 * already have been completed.
827 * The response to validate.
829 * The request that generated this response.
831 * The trusted DNSKEY rrset that signs this response.
833 private void validateNameErrorResponse(SMessage message, SRRset key_rrset) {
834 Name qname = message.getQName();
836 SMessage m = message;
838 if (message.getCount(Section.ANSWER) > 0) {
839 log.warn("NameError response contained records in the ANSWER SECTION");
840 mErrorList.add("NameError response contained records in the ANSWER SECTION");
841 message.setStatus(SecurityStatus.INVALID);
846 // Validate the authority section -- all RRsets in the authority section
847 // must be signed and valid.
848 // In addition, the NSEC record(s) must prove the NXDOMAIN condition.
849 boolean hasValidNSEC = false;
850 boolean hasValidWCNSEC = false;
851 SRRset[] rrsets = m.getSectionRRsets(Section.AUTHORITY);
852 List<NSEC3Record> nsec3s = null;
853 Name nsec3Signer = null;
855 for (int i = 0; i < rrsets.length; i++) {
856 int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
858 if (status != SecurityStatus.SECURE) {
859 mErrorList.add("NameError response has failed AUTHORITY rrset: " +
861 m.setStatus(SecurityStatus.BOGUS);
866 if (rrsets[i].getType() == Type.NSEC) {
867 NSECRecord nsec = (NSECRecord) rrsets[i].first();
869 if (ValUtils.nsecProvesNameError(nsec, qname, rrsets[i].getSignerName())) {
873 if (ValUtils.nsecProvesNoWC(nsec, qname, rrsets[i].getSignerName())) {
874 hasValidWCNSEC = true;
878 if (rrsets[i].getType() == Type.NSEC3) {
879 if (nsec3s == null) {
880 nsec3s = new ArrayList<NSEC3Record>();
883 nsec3s.add((NSEC3Record) rrsets[i].first());
884 nsec3Signer = rrsets[i].getSignerName();
888 NSEC3ValUtils.stripUnknownAlgNSEC3s(nsec3s);
890 if ((nsec3s != null) && (nsec3s.size() > 0)) {
891 log.debug("Validating nxdomain: using NSEC3 records");
893 // Attempt to prove name error with nsec3 records.
894 if (NSEC3ValUtils.allNSEC3sIgnoreable(nsec3s, key_rrset, mVerifier)) {
895 // log.debug("all NSEC3s were validated but ignored.");
896 m.setStatus(SecurityStatus.INSECURE);
901 hasValidNSEC = NSEC3ValUtils.proveNameError(nsec3s, qname, nsec3Signer, mErrorList);
903 // Note that we assume that the NSEC3ValUtils proofs
904 // encompass the wildcard part of the proof.
905 hasValidWCNSEC = hasValidNSEC;
908 // If the message fails to prove either condition, it is bogus.
910 mErrorList.add("NameError response has failed to prove qname does not exist");
911 m.setStatus(SecurityStatus.BOGUS);
916 if (!hasValidWCNSEC) {
917 mErrorList.add("NameError response has failed to prove covering wildcard does not exist");
918 m.setStatus(SecurityStatus.BOGUS);
923 // Otherwise, we consider the message secure.
924 log.trace("successfully validated NAME ERROR response.");
925 m.setStatus(SecurityStatus.SECURE);
928 public byte validateMessage(SMessage message, Name zone) {
930 if (!zone.isAbsolute()) {
932 zone = Name.concatenate(zone, Name.root);
933 } catch (NameTooLongException e) {
936 return SecurityStatus.UNCHECKED;
940 // FIXME: it is unclear if we should actually normalize our
941 // responses Instead, maybe we should just fail if they are
943 message = normalize(message);
945 if (!needsValidation(message)) {
946 return SecurityStatus.UNCHECKED;
949 SRRset key_rrset = findKeys(message);
951 if (key_rrset == null) {
952 mErrorList.add("Failed to find matching DNSKEYs for the response");
953 return SecurityStatus.BOGUS;
956 ValUtils.ResponseType subtype = ValUtils.classifyResponse(message, zone);
960 log.trace("Validating a positive response");
961 validatePositiveResponse(message, key_rrset);
965 validateReferral(message, key_rrset);
969 log.trace("Validating a NODATA response");
970 validateNodataResponse(message, key_rrset, mErrorList);
974 log.trace("Validating a NXDOMAIN response");
975 validateNameErrorResponse(message, key_rrset);
979 log.trace("Validating a CNAME response");
980 validateCNAMEResponse(message, key_rrset);
984 log.trace("Validating a positive ANY response");
985 validateAnyResponse(message, key_rrset);
989 log.error("unhandled response subtype: " + subtype);
992 return message.getSecurityStatus().getStatus();
995 public byte validateMessage(Message message, String zone)
996 throws TextParseException {
997 SMessage sm = new SMessage(message);
998 Name z = Name.fromString(zone);
1000 return validateMessage(sm, z);
1003 public byte validateMessage(byte[] messagebytes, String zone)
1004 throws IOException {
1005 Message message = new Message(messagebytes);
1006 return validateMessage(message, zone);
1009 public byte validateMessage(String b64messagebytes, String zone)
1010 throws IOException {
1011 byte[] messagebytes = base64.fromString(b64messagebytes);
1012 return validateMessage(messagebytes, zone);
1015 public List<String> listTrustedKeys() {
1016 return mTrustedKeys.listTrustAnchors();
1019 public List<String> getErrorList() {