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