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