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