Fix NullPointerException that would occur when getting a non-dnssec referral from...
[captive-validator.git] / src / com / verisign / tat / dnssec / CaptiveValidator.java
1 /***************************** -*- Java -*- ********************************\
2  *                                                                         *
3  *   Copyright (c) 2009 VeriSign, Inc. All rights reserved.                *
4  *                                                                         *
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           *
20  * documentation.                                                          *
21  *                                                                         *
22 \***************************************************************************/
23
24 package com.verisign.tat.dnssec;
25
26 import org.apache.log4j.Logger;
27
28 import org.xbill.DNS.*;
29 import org.xbill.DNS.utils.base64;
30
31 import java.io.IOException;
32
33 import java.util.*;
34
35 /**
36  * This resolver module implements a "captive" DNSSEC validator. The captive
37  * validator does not have direct access to the Internet and DNS system --
38  * instead it attempts to validate DNS messages using only configured context.
39  * This is useful for determining if responses coming from a given authoritative
40  * server will validate independent of the normal chain of trust.
41  */
42 public class CaptiveValidator {
43     // A data structure holding all all of our trusted keys.
44     private TrustAnchorStore mTrustedKeys;
45
46     // The local validation utilities.
47     private ValUtils mValUtils;
48
49     // The local verification utility.
50     private DnsSecVerifier mVerifier;
51     private Logger log = Logger.getLogger(this.getClass());
52
53     private List<String>   mErrorList;
54
55     public CaptiveValidator() {
56         mVerifier        = new DnsSecVerifier();
57         mValUtils        = new ValUtils(mVerifier);
58         mTrustedKeys     = new TrustAnchorStore();
59         mErrorList       = new ArrayList<String>();
60     }
61
62     // ---------------- Module Initialization -------------------
63
64     /**
65      * Add a set of trusted keys from a file. The file should be in DNS master
66      * zone file format. Only DNSKEY records will be added.
67      * 
68      * @param filename
69      *            The file contains the trusted keys.
70      * @throws IOException
71      */
72     @SuppressWarnings("unchecked")
73     public void addTrustedKeysFromFile(String filename) throws IOException {
74         // First read in the whole trust anchor file.
75         Master master = new Master(filename, Name.root, 0);
76         ArrayList<Record> records = new ArrayList<Record>();
77         Record r = null;
78
79         while ((r = master.nextRecord()) != null) {
80             records.add(r);
81         }
82
83         // Record.compareTo() should sort them into DNSSEC canonical order.
84         // Don't care about canonical order per se, but do want them to be
85         // formable into RRsets.
86         Collections.sort(records);
87
88         SRRset cur_rrset = new SRRset();
89
90         for (Record rec : records) {
91             // Skip RR types that cannot be used as trusted keys. I.e.,
92             // everything not a key :)
93             if (rec.getType() != Type.DNSKEY) {
94                 continue;
95             }
96
97             // If our cur_rrset is empty, we can just add it.
98             if (cur_rrset.size() == 0) {
99                 cur_rrset.addRR(rec);
100
101                 continue;
102             }
103
104             // If this record matches our current RRset, we can just add it.
105             if (cur_rrset.getName().equals(rec.getName())
106                     && (cur_rrset.getType() == rec.getType())
107                     && (cur_rrset.getDClass() == rec.getDClass())) {
108                 cur_rrset.addRR(rec);
109
110                 continue;
111             }
112
113             // Otherwise, we add the rrset to our set of trust anchors.
114             mTrustedKeys.store(cur_rrset);
115             cur_rrset = new SRRset();
116             cur_rrset.addRR(rec);
117         }
118
119         // add the last rrset (if it was not empty)
120         if (cur_rrset.size() > 0) {
121             mTrustedKeys.store(cur_rrset);
122         }
123     }
124
125     public void addTrustedKeysFromResponse(Message m) {
126         RRset[] rrsets = m.getSectionRRsets(Section.ANSWER);
127
128         for (int i = 0; i < rrsets.length; ++i) {
129             if (rrsets[i].getType() == Type.DNSKEY) {
130                 SRRset srrset = new SRRset(rrsets[i]);
131                 mTrustedKeys.store(srrset);
132             }
133         }
134     }
135
136     // ----------------- Validation Support ----------------------
137
138     /**
139      * This routine normalizes a response. This includes removing "irrelevant"
140      * records from the answer and additional sections and (re)synthesizing
141      * CNAMEs from DNAMEs, if present.
142      * 
143      * @param response
144      */
145     private SMessage normalize(SMessage m) {
146         if (m == null) {
147             return m;
148         }
149
150         if ((m.getRcode() != Rcode.NOERROR) && (m.getRcode() != Rcode.NXDOMAIN)) {
151             return m;
152         }
153
154         Name qname = m.getQuestion().getName();
155         int qtype = m.getQuestion().getType();
156
157         Name sname = qname;
158
159         // For the ANSWER section, remove all "irrelevant" records and add
160         // synthesized CNAMEs from DNAMEs
161         // This will strip out-of-order CNAMEs as well.
162         List<SRRset> rrset_list = m.getSectionList(Section.ANSWER);
163         Set<Name> additional_names = new HashSet<Name>();
164
165         for (ListIterator<SRRset> i = rrset_list.listIterator(); i.hasNext();) {
166             SRRset rrset = i.next();
167             int type = rrset.getType();
168             Name n = rrset.getName();
169
170             // Handle DNAME synthesis; DNAME synthesis does not occur at the
171             // DNAME name itself.
172             if ((type == Type.DNAME) && ValUtils.strictSubdomain(sname, n)) {
173                 if (rrset.size() > 1) {
174                     log.debug("Found DNAME rrset with size > 1: " + rrset);
175                     m.setStatus(SecurityStatus.INVALID);
176
177                     return m;
178                 }
179
180                 DNAMERecord dname = (DNAMERecord) rrset.first();
181
182                 try {
183                     Name cname_alias = sname.fromDNAME(dname);
184
185                     // Note that synthesized CNAMEs should have a TTL of zero.
186                     CNAMERecord cname = new CNAMERecord(sname, dname
187                             .getDClass(), 0, cname_alias);
188                     SRRset cname_rrset = new SRRset();
189                     cname_rrset.addRR(cname);
190                     i.add(cname_rrset);
191
192                     sname = cname_alias;
193                 } catch (NameTooLongException e) {
194                     log.debug("not adding synthesized CNAME -- "
195                             + "generated name is too long", e);
196                 }
197
198                 continue;
199             }
200
201             // The only records in the ANSWER section not allowed to
202             if (!n.equals(sname)) {
203                 log.debug("normalize: removing irrelevant rrset: " + rrset);
204                 i.remove();
205
206                 continue;
207             }
208
209             // Follow the CNAME chain.
210             if (type == Type.CNAME) {
211                 if (rrset.size() > 1) {
212                     mErrorList.add("Found CNAME rrset with size > 1: " + rrset);
213                     m.setStatus(SecurityStatus.INVALID);
214
215                     return m;
216                 }
217
218                 CNAMERecord cname = (CNAMERecord) rrset.first();
219                 sname = cname.getAlias();
220
221                 continue;
222             }
223
224             // Otherwise, make sure that the RRset matches the qtype.
225             if ((qtype != Type.ANY) && (qtype != type)) {
226                 log.debug("normalize: removing irrelevant rrset: " + rrset);
227                 i.remove();
228             }
229
230             // Otherwise, fetch the additional names from the relevant rrset.
231             rrsetAdditionalNames(additional_names, rrset);
232         }
233
234         // Get additional names from AUTHORITY
235         rrset_list = m.getSectionList(Section.AUTHORITY);
236
237         for (SRRset rrset : rrset_list) {
238             rrsetAdditionalNames(additional_names, rrset);
239         }
240
241         // For each record in the additional section, remove it if it is an
242         // address record and not in the collection of additional names found in
243         // ANSWER and AUTHORITY.
244         rrset_list = m.getSectionList(Section.ADDITIONAL);
245
246         for (Iterator<SRRset> i = rrset_list.iterator(); i.hasNext();) {
247             SRRset rrset = i.next();
248             int type = rrset.getType();
249
250             if (((type == Type.A) || (type == Type.AAAA))
251                     && !additional_names.contains(rrset.getName())) {
252                 i.remove();
253             }
254         }
255
256         return m;
257     }
258
259     /**
260      * Extract additional names from the records in an rrset.
261      * 
262      * @param additional_names
263      *            The set to add the additional names to, if any.
264      * @param rrset
265      *            The rrset to extract from.
266      */
267     private void rrsetAdditionalNames(Set<Name> additional_names, SRRset rrset) {
268         if (rrset == null) {
269             return;
270         }
271
272         for (Iterator<Record> i = rrset.rrs(); i.hasNext();) {
273             Record r = i.next();
274             Name add_name = r.getAdditionalName();
275
276             if (add_name != null) {
277                 additional_names.add(add_name);
278             }
279         }
280     }
281
282     private SRRset findKeys(SMessage message) {
283         Name qname = message.getQName();
284         int qclass = message.getQClass();
285
286         return mTrustedKeys.find(qname, qclass);
287     }
288
289     /**
290      * Check to see if a given response needs to go through the validation
291      * process. Typical reasons for this routine to return false are: CD bit was
292      * on in the original request, the response was already validated, or the
293      * response is a kind of message that is unvalidatable (i.e., SERVFAIL,
294      * REFUSED, etc.)
295      * 
296      * @param message
297      *            The message to check.
298      * @param origRequest
299      *            The original request received from the client.
300      * 
301      * @return true if the response could use validation (although this does not
302      *         mean we can actually validate this response).
303      */
304     private boolean needsValidation(SMessage message) {
305         int rcode = message.getRcode();
306
307         if ((rcode != Rcode.NOERROR) && (rcode != Rcode.NXDOMAIN)) {
308             log.debug("cannot validate non-answer.");
309             log.trace("non-answer: " + message);
310
311             return false;
312         }
313
314         if (!mTrustedKeys.isBelowTrustAnchor(message.getQName(), message
315                 .getQClass())) {
316             return false;
317         }
318
319         return true;
320     }
321
322     /**
323      * Given a "positive" response -- a response that contains an answer to the
324      * question, and no CNAME chain, validate this response. This generally
325      * consists of verifying the answer RRset and the authority RRsets.
326      * 
327      * Note that by the time this method is called, the process of finding the
328      * trusted DNSKEY rrset that signs this response must already have been
329      * completed.
330      * 
331      * @param response
332      *            The response to validate.
333      * @param request
334      *            The request that generated this response.
335      * @param key_rrset
336      *            The trusted DNSKEY rrset that matches the signer of the
337      *            answer.
338      */
339     private void validatePositiveResponse(SMessage message, SRRset key_rrset) {
340         Name qname = message.getQName();
341         int qtype = message.getQType();
342
343         SMessage m = message;
344
345         // validate the ANSWER section - this will be the answer itself
346         SRRset[] rrsets = m.getSectionRRsets(Section.ANSWER);
347
348         Name wc = null;
349         boolean wcNSEC_ok = false;
350         boolean dname = false;
351         List<NSEC3Record> nsec3s = null;
352
353         for (int i = 0; i < rrsets.length; i++) {
354             // Skip the CNAME following a (validated) DNAME.
355             // Because of the normalization routines in NameserverClient, there
356             // will always be an unsigned CNAME following a DNAME (unless
357             // qtype=DNAME).
358             if (dname && (rrsets[i].getType() == Type.CNAME)) {
359                 dname = false;
360
361                 continue;
362             }
363
364             // Verify the answer rrset.
365             int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
366
367             // If the (answer) rrset failed to validate, then this message is
368             // BAD.
369             if (status != SecurityStatus.SECURE) {
370                 mErrorList.add("Positive response has failed ANSWER rrset: " +
371                          rrsets[i]);
372                 m.setStatus(SecurityStatus.BOGUS);
373
374                 return;
375             }
376
377             // Check to see if the rrset is the result of a wildcard expansion.
378             // If so, an additional check will need to be made in the authority
379             // section.
380             wc = ValUtils.rrsetWildcard(rrsets[i]);
381
382             // Notice a DNAME that should be followed by an unsigned CNAME.
383             if ((qtype != Type.DNAME) && (rrsets[i].getType() == Type.DNAME)) {
384                 dname = true;
385             }
386         }
387
388         // validate the AUTHORITY section as well - this will generally be the
389         // NS rrset (which could be missing, no problem)
390         rrsets = m.getSectionRRsets(Section.AUTHORITY);
391
392         for (int i = 0; i < rrsets.length; i++) {
393             int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
394
395             // If anything in the authority section fails to be secure, we have
396             // a bad message.
397             if (status != SecurityStatus.SECURE) {
398                 mErrorList.add("Positive response has failed AUTHORITY rrset: " +
399                          rrsets[i]);
400                 m.setStatus(SecurityStatus.BOGUS);
401
402                 return;
403             }
404
405             // If this is a positive wildcard response, and we have a (just
406             // verified) NSEC record, try to use it to 1) prove that qname
407             // doesn't exist and 2) that the correct wildcard was used.
408             if ((wc != null) && (rrsets[i].getType() == Type.NSEC)) {
409                 NSECRecord nsec = (NSECRecord) rrsets[i].first();
410
411                 if (ValUtils.nsecProvesNameError(nsec, qname, key_rrset
412                         .getName())) {
413                     Name nsec_wc = ValUtils.nsecWildcard(qname, nsec);
414
415                     if (!wc.equals(nsec_wc)) {
416                         mErrorList.add("Positive wildcard response wasn't generated by the correct wildcard");
417                         m.setStatus(SecurityStatus.BOGUS);
418
419                         return;
420                     }
421
422                     wcNSEC_ok = true;
423                 }
424             }
425
426             // Otherwise, if this is a positive wildcard response and we have
427             // NSEC3 records, collect them.
428             if ((wc != null) && (rrsets[i].getType() == Type.NSEC3)) {
429                 if (nsec3s == null) {
430                     nsec3s = new ArrayList<NSEC3Record>();
431                 }
432
433                 nsec3s.add((NSEC3Record) rrsets[i].first());
434             }
435         }
436
437         // If this was a positive wildcard response that we haven't already
438         // proven, and we have NSEC3 records, try to prove it using the NSEC3
439         // records.
440         if ((wc != null) && !wcNSEC_ok && (nsec3s != null)) {
441             if (NSEC3ValUtils.proveWildcard(nsec3s, qname, key_rrset.getName(),
442                         wc, mErrorList)) {
443                 wcNSEC_ok = true;
444             }
445         }
446
447         // If after all this, we still haven't proven the positive wildcard
448         // response, fail.
449         if ((wc != null) && !wcNSEC_ok) {
450             // log.debug("positive response was wildcard expansion and "
451             // + "did not prove original data did not exist");
452             m.setStatus(SecurityStatus.BOGUS);
453
454             return;
455         }
456
457         log.trace("Successfully validated positive response");
458         m.setStatus(SecurityStatus.SECURE);
459     }
460
461     private void validateReferral(SMessage message, SRRset key_rrset) {
462         SMessage m = message;
463
464         if (m.getCount(Section.ANSWER) > 0) {
465             m.setStatus(SecurityStatus.INVALID);
466
467             return;
468         }
469
470         // validate the AUTHORITY section.
471         SRRset[] rrsets = m.getSectionRRsets(Section.AUTHORITY);
472
473         boolean secure_delegation = false;
474         Name delegation = null;
475         Name nsec3zone = null;
476         NSECRecord nsec = null;
477         List<NSEC3Record> nsec3s = null;
478
479         // validate the AUTHORITY section as well - this will generally be the
480         // NS rrset, plus proof of a secure delegation or not
481         rrsets = m.getSectionRRsets(Section.AUTHORITY);
482
483         for (int i = 0; i < rrsets.length; i++) {
484             int type = rrsets[i].getType();
485
486             // The NS RRset won't be signed, but everything else should be.
487             if (type != Type.NS) {
488                 int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
489
490                 // If anything in the authority section fails to be secure, we
491                 // have
492                 // a bad message.
493                 if (status != SecurityStatus.SECURE) {
494                     mErrorList.add("Positive response has failed AUTHORITY rrset: " +
495                              rrsets[i]);
496                     m.setStatus(SecurityStatus.BOGUS);
497
498                     return;
499                 }
500             }
501
502             switch (type) {
503             case Type.DS:
504                 secure_delegation = true;
505
506                 break;
507
508             case Type.NS:
509                 delegation = rrsets[i].getName();
510
511                 break;
512
513             case Type.NSEC:
514                 nsec = (NSECRecord) rrsets[i].first();
515
516                 break;
517
518             case Type.NSEC3:
519
520                 if (nsec3s == null) {
521                     nsec3s = new ArrayList<NSEC3Record>();
522                 }
523
524                 NSEC3Record nsec3 = (NSEC3Record) rrsets[i].first();
525                 nsec3s.add(nsec3);
526                 nsec3zone = rrsets[i].getSignerName(); // this is a hack of
527                 // sorts.
528
529                 break;
530
531             default:
532                 log.warn("Encountered unexpected type in a REFERRAL response: "
533                         + Type.string(type));
534
535                 break;
536             }
537         }
538
539         // At this point, all validatable RRsets have been validated.
540         // Now to check to see if we have a valid combination of things.
541         if (delegation == null) {
542             // somehow we have a referral without an NS rrset.
543             mErrorList.add("Apparent referral does not contain NS RRset");
544             m.setStatus(SecurityStatus.BOGUS);
545
546             return;
547         }
548
549         if (secure_delegation) {
550             if ((nsec != null) || ((nsec3s != null) && (nsec3s.size() > 0))) {
551                 // we found both a DS rrset *and* NSEC/NSEC3 rrsets!
552                 mErrorList.add("Referral contains both DS and NSEC/NSEC3 RRsets");
553                 m.setStatus(SecurityStatus.BOGUS);
554
555                 return;
556             }
557
558             // otherwise, we are done.
559             m.setStatus(SecurityStatus.SECURE);
560
561             return;
562         }
563
564         // Note: not going to care if both NSEC and NSEC3 rrsets were present.
565         if (nsec != null) {
566             byte status = ValUtils.nsecProvesNoDS(nsec, delegation);
567
568             if (status != SecurityStatus.SECURE) {
569                 // The NSEC *must* prove that there was no DS record. The
570                 // INSECURE state here is still bogus.
571                 mErrorList.add("Referral does not contain a NSEC record proving no DS");
572                 m.setStatus(SecurityStatus.BOGUS);
573
574                 return;
575             }
576
577             m.setStatus(SecurityStatus.SECURE);
578
579             return;
580         }
581
582         if (nsec3s != null && nsec3s.size() > 0) {
583             byte status = NSEC3ValUtils.proveNoDS(nsec3s, delegation, nsec3zone, mErrorList);
584
585             if (status != SecurityStatus.SECURE) {
586                 // the NSEC3 RRs MUST prove no DS, so the INDETERMINATE state is
587                 // actually bogus
588                 mErrorList.add("Referral does not contain NSEC3 record(s) proving no DS");
589                 m.setStatus(SecurityStatus.BOGUS);
590
591                 return;
592             }
593
594             m.setStatus(SecurityStatus.SECURE);
595
596             return;
597         }
598
599         // failed to find proof either way.
600         mErrorList.add("Referral does not contain proof of no DS");
601         m.setStatus(SecurityStatus.BOGUS);
602     }
603
604     // FIXME: write CNAME validation code.
605     private void validateCNAMEResponse(SMessage message, SRRset key_rrset) {}
606
607     /**
608      * Given an "ANY" response -- a response that contains an answer to a
609      * qtype==ANY question, with answers. This consists of simply verifying all
610      * present answer/auth RRsets, with no checking that all types are present.
611      * 
612      * NOTE: it may be possible to get parent-side delegation point records
613      * here, which won't all be signed. Right now, this routine relies on the
614      * upstream iterative resolver to not return these responses -- instead
615      * treating them as referrals.
616      * 
617      * NOTE: RFC 4035 is silent on this issue, so this may change upon
618      * clarification.
619      * 
620      * Note that by the time this method is called, the process of finding the
621      * trusted DNSKEY rrset that signs this response must already have been
622      * completed.
623      * 
624      * @param message
625      *            The response to validate.
626      * @param key_rrset
627      *            The trusted DNSKEY rrset that matches the signer of the
628      *            answer.
629      */
630     private void validateAnyResponse(SMessage message, SRRset key_rrset) {
631         int qtype = message.getQType();
632
633         if (qtype != Type.ANY) {
634             throw new IllegalArgumentException(
635                     "ANY validation called on non-ANY response.");
636         }
637
638         SMessage m = message;
639
640         // validate the ANSWER section.
641         SRRset[] rrsets = m.getSectionRRsets(Section.ANSWER);
642
643         for (int i = 0; i < rrsets.length; i++) {
644             int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
645
646             // If the (answer) rrset failed to validate, then this message is
647             // BAD.
648             if (status != SecurityStatus.SECURE) {
649                 mErrorList.add("Positive response has failed ANSWER rrset: " +
650                          rrsets[i]);
651                 m.setStatus(SecurityStatus.BOGUS);
652
653                 return;
654             }
655         }
656
657         // validate the AUTHORITY section as well - this will be the NS rrset
658         // (which could be missing, no problem)
659         rrsets = m.getSectionRRsets(Section.AUTHORITY);
660
661         for (int i = 0; i < rrsets.length; i++) {
662             int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
663
664             // If anything in the authority section fails to be secure, we have
665             // a bad message.
666             if (status != SecurityStatus.SECURE) {
667                 mErrorList.add("Positive response has failed AUTHORITY rrset: " +
668                          rrsets[i]);
669                 m.setStatus(SecurityStatus.BOGUS);
670
671                 return;
672             }
673         }
674
675         log.trace("Successfully validated positive ANY response");
676         m.setStatus(SecurityStatus.SECURE);
677     }
678
679     /**
680      * Validate a NOERROR/NODATA signed response -- a response that has a
681      * NOERROR Rcode but no ANSWER section RRsets. This consists of verifying
682      * the authority section rrsets and making certain that the authority
683      * section NSEC/NSEC3s proves that the qname does exist and the qtype
684      * doesn't.
685      * 
686      * Note that by the time this method is called, the process of finding the
687      * trusted DNSKEY rrset that signs this response must already have been
688      * completed.
689      * 
690      * @param response
691      *            The response to validate.
692      * @param request
693      *            The request that generated this response.
694      * @param key_rrset
695      *            The trusted DNSKEY rrset that signs this response.
696      */
697     private void validateNodataResponse(SMessage message, SRRset key_rrset, List<String> errorList) {
698         Name     qname = message.getQName();
699         int      qtype = message.getQType();
700
701         SMessage m = message;
702
703         // Since we are here, there must be nothing in the ANSWER section to
704         // validate. (Note: CNAME/DNAME responses will not directly get here --
705         // instead they are broken down into individual CNAME/DNAME/final answer
706         // responses.)
707
708         // validate the AUTHORITY section
709         SRRset[] rrsets = m.getSectionRRsets(Section.AUTHORITY);
710
711         boolean hasValidNSEC = false; // If true, then the NODATA has been
712         // proven.
713
714         Name ce = null; // for wildcard NODATA responses. This is the proven
715         // closest encloser.
716
717         NSECRecord wc = null; // for wildcard NODATA responses. This is the
718         // wildcard NSEC.
719
720         List<NSEC3Record> nsec3s = null; // A collection of NSEC3 RRs found in
721         // the authority
722         // section.
723
724         Name nsec3Signer = null; // The RRSIG signer field for the NSEC3 RRs.
725
726         for (int i = 0; i < rrsets.length; i++) {
727             int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
728
729             if (status != SecurityStatus.SECURE) {
730                 mErrorList.add("NODATA response has failed AUTHORITY rrset: " +
731                          rrsets[i]);
732                 m.setStatus(SecurityStatus.BOGUS);
733
734                 return;
735             }
736
737             // If we encounter an NSEC record, try to use it to prove NODATA.
738             // This needs to handle the ENT NODATA case.
739             if (rrsets[i].getType() == Type.NSEC) {
740                 NSECRecord nsec = (NSECRecord) rrsets[i].first();
741
742                 if (ValUtils.nsecProvesNodata(nsec, qname, qtype)) {
743                     hasValidNSEC = true;
744
745                     if (nsec.getName().isWild()) {
746                         wc = nsec;
747                     }
748                 } else if (ValUtils.nsecProvesNameError(nsec, qname, rrsets[i]
749                         .getSignerName())) {
750                     ce = ValUtils.closestEncloser(qname, nsec);
751                 }
752             }
753
754             // Collect any NSEC3 records present.
755             if (rrsets[i].getType() == Type.NSEC3) {
756                 if (nsec3s == null) {
757                     nsec3s = new ArrayList<NSEC3Record>();
758                 }
759
760                 nsec3s.add((NSEC3Record) rrsets[i].first());
761                 nsec3Signer = rrsets[i].getSignerName();
762             }
763         }
764
765         // check to see if we have a wildcard NODATA proof.
766
767         // The wildcard NODATA is 1 NSEC proving that qname does not exists (and
768         // also proving what the closest encloser is), and 1 NSEC showing the
769         // matching wildcard, which must be *.closest_encloser.
770         if ((ce != null) || (wc != null)) {
771             try {
772                 Name wc_name = new Name("*", ce);
773
774                 if (!wc_name.equals(wc.getName())) {
775                     hasValidNSEC = false;
776                 }
777             } catch (TextParseException e) {
778                 log.error(e);
779             }
780         }
781
782         NSEC3ValUtils.stripUnknownAlgNSEC3s(nsec3s);
783
784         if (!hasValidNSEC && (nsec3s != null) && (nsec3s.size() > 0)) {
785             // try to prove NODATA with our NSEC3 record(s)
786             hasValidNSEC = NSEC3ValUtils.proveNodata(nsec3s, qname, qtype,
787                     nsec3Signer, errorList);
788         }
789
790         if (!hasValidNSEC) {
791             log.debug("NODATA response failed to prove NODATA "
792                     + "status with NSEC/NSEC3");
793             log.trace("Failed NODATA:\n" + m);
794             mErrorList.add("NODATA response failed to prove NODATA status with NSEC/NSEC3");
795             m.setStatus(SecurityStatus.BOGUS);
796
797             return;
798         }
799
800         log.trace("successfully validated NODATA response.");
801         m.setStatus(SecurityStatus.SECURE);
802     }
803
804     /**
805      * Validate a NAMEERROR signed response -- a response that has a NXDOMAIN
806      * Rcode. This consists of verifying the authority section rrsets and making
807      * certain that the authority section NSEC proves that the qname doesn't
808      * exist and the covering wildcard also doesn't exist..
809      * 
810      * Note that by the time this method is called, the process of finding the
811      * trusted DNSKEY rrset that signs this response must already have been
812      * completed.
813      * 
814      * @param response
815      *            The response to validate.
816      * @param request
817      *            The request that generated this response.
818      * @param key_rrset
819      *            The trusted DNSKEY rrset that signs this response.
820      */
821     private void validateNameErrorResponse(SMessage message, SRRset key_rrset) {
822         Name qname = message.getQName();
823
824         SMessage m = message;
825
826         if (message.getCount(Section.ANSWER) > 0) {
827             log.warn(
828                 "NameError response contained records in the ANSWER SECTION");
829             mErrorList.add("NameError response contained records in the ANSWER SECTION");
830             message.setStatus(SecurityStatus.INVALID);
831
832             return;
833         }
834
835         // Validate the authority section -- all RRsets in the authority section
836         // must be signed and valid.
837         // In addition, the NSEC record(s) must prove the NXDOMAIN condition.
838         boolean hasValidNSEC = false;
839         boolean hasValidWCNSEC = false;
840         SRRset[] rrsets = m.getSectionRRsets(Section.AUTHORITY);
841         List<NSEC3Record> nsec3s = null;
842         Name nsec3Signer = null;
843
844         for (int i = 0; i < rrsets.length; i++) {
845             int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
846
847             if (status != SecurityStatus.SECURE) {
848                 mErrorList.add("NameError response has failed AUTHORITY rrset: " +
849                          rrsets[i]);
850                 m.setStatus(SecurityStatus.BOGUS);
851
852                 return;
853             }
854
855             if (rrsets[i].getType() == Type.NSEC) {
856                 NSECRecord nsec = (NSECRecord) rrsets[i].first();
857
858                 if (ValUtils.nsecProvesNameError(nsec, qname, rrsets[i]
859                         .getSignerName())) {
860                     hasValidNSEC = true;
861                 }
862
863                 if (ValUtils.nsecProvesNoWC(nsec, qname, rrsets[i]
864                         .getSignerName())) {
865                     hasValidWCNSEC = true;
866                 }
867             }
868
869             if (rrsets[i].getType() == Type.NSEC3) {
870                 if (nsec3s == null) {
871                     nsec3s = new ArrayList<NSEC3Record>();
872                 }
873
874                 nsec3s.add((NSEC3Record) rrsets[i].first());
875                 nsec3Signer = rrsets[i].getSignerName();
876             }
877         }
878
879         NSEC3ValUtils.stripUnknownAlgNSEC3s(nsec3s);
880
881         if ((nsec3s != null) && (nsec3s.size() > 0)) {
882             log.debug("Validating nxdomain: using NSEC3 records");
883
884             // Attempt to prove name error with nsec3 records.
885             if (NSEC3ValUtils.allNSEC3sIgnoreable(nsec3s, key_rrset, mVerifier)) {
886                 // log.debug("all NSEC3s were validated but ignored.");
887                 m.setStatus(SecurityStatus.INSECURE);
888
889                 return;
890             }
891
892             hasValidNSEC       = NSEC3ValUtils.proveNameError(nsec3s, qname,
893                     nsec3Signer, mErrorList);
894
895             // Note that we assume that the NSEC3ValUtils proofs encompass the
896             // wildcard part of the proof.
897             hasValidWCNSEC = hasValidNSEC;
898         }
899
900         // If the message fails to prove either condition, it is bogus.
901         if (!hasValidNSEC) {
902             mErrorList.add("NameError response has failed to prove qname does not exist");
903             m.setStatus(SecurityStatus.BOGUS);
904
905             return;
906         }
907
908         if (!hasValidWCNSEC) {
909             mErrorList.add("NameError response has failed to prove covering wildcard does not exist");
910             m.setStatus(SecurityStatus.BOGUS);
911
912             return;
913         }
914
915         // Otherwise, we consider the message secure.
916         log.trace("successfully validated NAME ERROR response.");
917         m.setStatus(SecurityStatus.SECURE);
918     }
919
920     public byte validateMessage(SMessage message, Name zone) {
921         mErrorList.clear();
922         if (!zone.isAbsolute()) {
923             try {
924                 zone = Name.concatenate(zone, Name.root);
925             } catch (NameTooLongException e) {
926                 log.error(e);
927
928                 return SecurityStatus.UNCHECKED;
929             }
930         }
931
932         // FIXME: it is unclear if we should actually normalize our responses
933         // Instead, maybe we should just fail if they are not normal?
934         message = normalize(message);
935
936         if (!needsValidation(message)) {
937             return SecurityStatus.UNCHECKED;
938         }
939
940         SRRset key_rrset = findKeys(message);
941
942         if (key_rrset == null) {
943             mErrorList.add("Failed to find matching DNSKEYs for the response");
944             return SecurityStatus.BOGUS;
945         }
946
947         ValUtils.ResponseType subtype = ValUtils.classifyResponse(message, zone);
948
949         switch (subtype) {
950         case POSITIVE:
951             log.trace("Validating a positive response");
952             validatePositiveResponse(message, key_rrset);
953
954             break;
955
956         case REFERRAL:
957             validateReferral(message, key_rrset);
958
959             break;
960
961         case NODATA:
962             log.trace("Validating a NODATA response");
963             validateNodataResponse(message, key_rrset, mErrorList);
964
965             break;
966
967         case NAMEERROR:
968             log.trace("Validating a NXDOMAIN response");
969             validateNameErrorResponse(message, key_rrset);
970
971             break;
972
973         case CNAME:
974             log.trace("Validating a CNAME response");
975             validateCNAMEResponse(message, key_rrset);
976
977             break;
978
979         case ANY:
980             log.trace("Validating a positive ANY response");
981             validateAnyResponse(message, key_rrset);
982
983             break;
984
985         default:
986             log.error("unhandled response subtype: " + subtype);
987         }
988
989         return message.getSecurityStatus().getStatus();
990     }
991
992     public byte validateMessage(Message message, String zone)
993             throws TextParseException {
994         SMessage sm = new SMessage(message);
995         Name z = Name.fromString(zone);
996
997         return validateMessage(sm, z);
998     }
999
1000     public byte validateMessage(byte[] messagebytes, String zone)
1001             throws IOException {
1002         Message message = new Message(messagebytes);
1003         return validateMessage(message, zone);
1004     }
1005
1006     public byte validateMessage(String b64messagebytes, String zone)
1007             throws IOException {
1008         byte[] messagebytes = base64.fromString(b64messagebytes);
1009         return validateMessage(messagebytes, zone);
1010     }
1011
1012     public List<String> listTrustedKeys() {
1013         return mTrustedKeys.listTrustAnchors();
1014     }
1015
1016     public List<String> getErrorList() {
1017         return mErrorList;
1018     }
1019  }