f32b863ea037705f2a036e75d0bd244f45709da0
[captive-validator.git] / src / com / versign / tat / dnssec / CaptiveValidator.java
1 /*
2  * Copyright (c) 2009 VeriSign, Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  */
28
29 package com.versign.tat.dnssec;
30
31 import java.io.IOException;
32 import java.util.*;
33
34 import org.xbill.DNS.*;
35
36 /**
37  * This resolver module implements a "captive" DNSSEC validator. The captive
38  * validator does not have direct access to the Internet and DNS system --
39  * instead it attempts to validate DNS messages using only configured context.
40  * This is useful for determining if responses coming from a given authoritative
41  * server will validate independent of the normal chain of trust.
42  */
43
44 public class CaptiveValidator {
45
46     // A data structure holding all all of our trusted keys.
47     private TrustAnchorStore mTrustedKeys;
48
49     // The local validation utilities.
50     private ValUtils         mValUtils;
51
52     // The local verification utility.
53     private DnsSecVerifier   mVerifier;
54
55     public CaptiveValidator() {
56         mVerifier = new DnsSecVerifier();
57         mValUtils = new ValUtils(mVerifier);
58         mTrustedKeys = new TrustAnchorStore();
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         for (Record rec : records) {
89             // Skip RR types that cannot be used as trusted keys. I.e.,
90             // everything not a key :)
91             if (rec.getType() != Type.DNSKEY) continue;
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                 continue;
97             }
98             // If this record matches our current RRset, we can just add it.
99             if (cur_rrset.getName().equals(rec.getName())
100                 && cur_rrset.getType() == rec.getType()
101                 && cur_rrset.getDClass() == rec.getDClass()) {
102                 cur_rrset.addRR(rec);
103                 continue;
104             }
105
106             // Otherwise, we add the rrset to our set of trust anchors.
107             mTrustedKeys.store(cur_rrset);
108             cur_rrset = new SRRset();
109             cur_rrset.addRR(rec);
110         }
111
112         // add the last rrset (if it was not empty)
113         if (cur_rrset.size() > 0) {
114             mTrustedKeys.store(cur_rrset);
115         }
116     }
117     
118     public void addTrustedKeysFromResponse(Message m) {
119         RRset[] rrsets = m.getSectionRRsets(Section.ANSWER);
120         for (int i = 0; i < rrsets.length; ++i) {
121             if (rrsets[i].getType() == Type.DNSKEY) {
122                 SRRset srrset = new SRRset(rrsets[i]);
123                 mTrustedKeys.store(srrset);
124             }
125         }
126     }
127
128     // ----------------- Validation Support ----------------------
129
130     /**
131      * This routine normalizes a response. This includes removing "irrelevant"
132      * records from the answer and additional sections and (re)synthesizing
133      * CNAMEs from DNAMEs, if present.
134      * 
135      * @param response
136      */
137     private SMessage normalize(SMessage m) {
138         if (m == null) return m;
139
140         if (m.getRcode() != Rcode.NOERROR && m.getRcode() != Rcode.NXDOMAIN) {
141             return m;
142         }
143
144         Name qname = m.getQuestion().getName();
145         int qtype = m.getQuestion().getType();
146
147         Name sname = qname;
148
149         // For the ANSWER section, remove all "irrelevant" records and add
150         // synthesized CNAMEs from DNAMEs
151         // This will strip out-of-order CNAMEs as well.
152         List<SRRset> rrset_list = m.getSectionList(Section.ANSWER);
153         Set<Name> additional_names = new HashSet<Name>();
154
155         for (ListIterator<SRRset> i = rrset_list.listIterator(); i.hasNext();) {
156             SRRset rrset = i.next();
157             int type = rrset.getType();
158             Name n = rrset.getName();
159
160             // Handle DNAME synthesis; DNAME synthesis does not occur at the
161             // DNAME name itself.
162             if (type == Type.DNAME && ValUtils.strictSubdomain(sname, n)) {
163                 if (rrset.size() > 1) {
164                     // log.debug("Found DNAME rrset with size > 1: " + rrset);
165                     // return Util.errorMessage(m, Rcode.SERVFAIL);
166                     return null; // FIXME
167                 }
168                 DNAMERecord dname = (DNAMERecord) rrset.first();
169                 try {
170                     Name cname_alias = sname.fromDNAME(dname);
171                     // Note that synthesized CNAMEs should have a TTL of zero.
172
173                     CNAMERecord cname = new CNAMERecord(sname,
174                             dname.getDClass(), 0, cname_alias);
175                     SRRset cname_rrset = new SRRset();
176                     cname_rrset.addRR(cname);
177                     i.add(cname_rrset);
178
179                     sname = cname_alias;
180                 } catch (NameTooLongException e) {
181 //                    log.debug("not adding synthesized CNAME -- "
182 //                              + "generated name is too long", e);
183                 }
184                 continue;
185             }
186
187             // The only records in the ANSWER section not allowed to
188             if (!n.equals(sname)) {
189 //                log.debug("normalize: removing irrelevant rrset: " + rrset);
190                 i.remove();
191                 continue;
192             }
193
194             // Follow the CNAME chain.
195             if (type == Type.CNAME) {
196                 if (rrset.size() > 1) {
197 //                    log.debug("Found CNAME rrset with size > 1: " + rrset);
198 //                    return Util.errorMessage(m, Rcode.SERVFAIL);
199                     return null; // FIXME
200                 }
201
202                 CNAMERecord cname = (CNAMERecord) rrset.first();
203                 sname = cname.getAlias();
204                 continue;
205             }
206
207             // Otherwise, make sure that the RRset matches the qtype.
208             if (qtype != Type.ANY && qtype != type) {
209 //                log.debug("normalize: removing irrelevant rrset: " + rrset);
210                 i.remove();
211             }
212
213             // Otherwise, fetch the additional names from the relevant rrset.
214             rrsetAdditionalNames(additional_names, rrset);
215         }
216
217         // Get additional names from AUTHORITY
218         rrset_list = m.getSectionList(Section.AUTHORITY);
219         for (SRRset rrset : rrset_list) {
220             rrsetAdditionalNames(additional_names, rrset);
221         }
222
223         // For each record in the additional section, remove it if it is an
224         // address record and not in the collection of additional names found in
225         // ANSWER and AUTHORITY.
226         rrset_list = m.getSectionList(Section.ADDITIONAL);
227         for (Iterator<SRRset> i = rrset_list.iterator(); i.hasNext();) {
228             SRRset rrset = i.next();
229             int type = rrset.getType();
230             if ((type == Type.A || type == Type.AAAA)
231                 && !additional_names.contains(rrset.getName())) {
232                 i.remove();
233             }
234             // FIXME: what about other types?
235         }
236
237         return m;
238     }
239     
240     /**
241      * Extract additional names from the records in an rrset.
242      * 
243      * @param additional_names
244      *            The set to add the additional names to, if any.
245      * @param rrset
246      *            The rrset to extract from.
247      */
248     private void rrsetAdditionalNames(Set<Name> additional_names, SRRset rrset) {
249         if (rrset == null) return;
250
251         for (Iterator<Record> i = rrset.rrs(); i.hasNext();) {
252             Record r = i.next();
253             Name add_name = r.getAdditionalName();
254             if (add_name != null) {
255                 additional_names.add(add_name);
256             }
257         }
258     }
259     
260     private SRRset findKeys(SMessage message) {
261         Name qname = message.getQName();
262         int qclass = message.getQClass();
263
264         return mTrustedKeys.find(qname, qclass);
265     }
266
267     /**
268      * Check to see if a given response needs to go through the validation
269      * process. Typical reasons for this routine to return false are: CD bit was
270      * on in the original request, the response was already validated, or the
271      * response is a kind of message that is unvalidatable (i.e., SERVFAIL,
272      * REFUSED, etc.)
273      * 
274      * @param message
275      *            The message to check.
276      * @param origRequest
277      *            The original request received from the client.
278      * 
279      * @return true if the response could use validation (although this does not
280      *         mean we can actually validate this response).
281      */
282     private boolean needsValidation(SMessage message) {
283         int rcode = message.getRcode();
284
285         if (rcode != Rcode.NOERROR && rcode != Rcode.NXDOMAIN) {
286             // log.debug("cannot validate non-answer.");
287             // log.trace("non-answer: " + response);
288             return false;
289         }
290         
291         if (!mTrustedKeys.isBelowTrustAnchor(message.getQName(), message.getQClass())) {
292             return false;
293         }
294         return true;
295     }
296
297     /**
298      * Given a "positive" response -- a response that contains an answer to the
299      * question, and no CNAME chain, validate this response. This generally
300      * consists of verifying the answer RRset and the authority RRsets.
301      * 
302      * Note that by the time this method is called, the process of finding the
303      * trusted DNSKEY rrset that signs this response must already have been
304      * completed.
305      * 
306      * @param response
307      *            The response to validate.
308      * @param request
309      *            The request that generated this response.
310      * @param key_rrset
311      *            The trusted DNSKEY rrset that matches the signer of the
312      *            answer.
313      */
314     private void validatePositiveResponse(SMessage message, SRRset key_rrset) {
315         Name qname = message.getQName();
316         int qtype = message.getQType();
317
318         SMessage m = message;
319
320         // validate the ANSWER section - this will be the answer itself
321         SRRset[] rrsets = m.getSectionRRsets(Section.ANSWER);
322
323         Name wc = null;
324         boolean wcNSEC_ok = false;
325         boolean dname = false;
326         List<NSEC3Record> nsec3s = null;
327
328         for (int i = 0; i < rrsets.length; i++) {
329             // Skip the CNAME following a (validated) DNAME.
330             // Because of the normalization routines in NameserverClient, there
331             // will always be an unsigned CNAME following a DNAME (unless
332             // qtype=DNAME).
333             if (dname && rrsets[i].getType() == Type.CNAME) {
334                 dname = false;
335                 continue;
336             }
337
338             // Verify the answer rrset.
339             int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
340             // If the (answer) rrset failed to validate, then this message is
341             // BAD.
342             if (status != SecurityStatus.SECURE) {
343                 // log.debug("Positive response has failed ANSWER rrset: "
344                 // + rrsets[i]);
345                 m.setStatus(SecurityStatus.BOGUS);
346                 return;
347             }
348             // Check to see if the rrset is the result of a wildcard expansion.
349             // If so, an additional check will need to be made in the authority
350             // section.
351             wc = ValUtils.rrsetWildcard(rrsets[i]);
352
353             // Notice a DNAME that should be followed by an unsigned CNAME.
354             if (qtype != Type.DNAME && rrsets[i].getType() == Type.DNAME) {
355                 dname = true;
356             }
357         }
358
359         // validate the AUTHORITY section as well - this will generally be the
360         // NS rrset (which could be missing, no problem)
361         rrsets = m.getSectionRRsets(Section.AUTHORITY);
362         for (int i = 0; i < rrsets.length; i++) {
363             int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
364             // If anything in the authority section fails to be secure, we have
365             // a
366             // bad message.
367             if (status != SecurityStatus.SECURE) {
368                 // log.debug("Positive response has failed AUTHORITY rrset: "
369                 // + rrsets[i]);
370                 m.setStatus(SecurityStatus.BOGUS);
371                 return;
372             }
373
374             // If this is a positive wildcard response, and we have a (just
375             // verified) NSEC record, try to use it to 1) prove that qname
376             // doesn't exist and 2) that the correct wildcard was used.
377             if (wc != null && rrsets[i].getType() == Type.NSEC) {
378                 NSECRecord nsec = (NSECRecord) rrsets[i].first();
379
380                 if (ValUtils.nsecProvesNameError(nsec, qname,
381                                                  key_rrset.getName())) {
382                     Name nsec_wc = ValUtils.nsecWildcard(qname, nsec);
383                     if (!wc.equals(nsec_wc)) {
384                         // log.debug("Positive wildcard response wasn't generated "
385                         // + "by the correct wildcard");
386                         m.setStatus(SecurityStatus.BOGUS);
387                         return;
388                     }
389                     wcNSEC_ok = true;
390                 }
391             }
392
393             // Otherwise, if this is a positive wildcard response and we have
394             // NSEC3 records, collect them.
395             if (wc != null && rrsets[i].getType() == Type.NSEC3) {
396                 if (nsec3s == null) nsec3s = new ArrayList<NSEC3Record>();
397                 nsec3s.add((NSEC3Record) rrsets[i].first());
398             }
399         }
400
401         // If this was a positive wildcard response that we haven't already
402         // proven, and we have NSEC3 records, try to prove it using the NSEC3
403         // records.
404         if (wc != null && !wcNSEC_ok && nsec3s != null) {
405             if (NSEC3ValUtils.proveWildcard(nsec3s, qname, key_rrset.getName(),
406                                             wc)) {
407                 wcNSEC_ok = true;
408             }
409         }
410
411         // If after all this, we still haven't proven the positive wildcard
412         // response, fail.
413         if (wc != null && !wcNSEC_ok) {
414             // log.debug("positive response was wildcard expansion and "
415             // + "did not prove original data did not exist");
416             m.setStatus(SecurityStatus.BOGUS);
417             return;
418         }
419
420         // log.trace("Successfully validated positive response");
421         m.setStatus(SecurityStatus.SECURE);
422     }
423
424     private void validateReferral(SMessage message, SRRset key_rrset) {
425         SMessage m = message;
426
427         if (m.getCount(Section.ANSWER) > 0) {
428             // FIXME: fail somehow.
429         }
430
431         // validate the AUTHORITY section.
432         SRRset[] rrsets = m.getSectionRRsets(Section.AUTHORITY);
433
434         boolean secure_delegation = false;
435         Name delegation = null;
436         Name nsec3zone  = null;
437         NSECRecord nsec = null;
438         List<NSEC3Record> nsec3s = null;
439
440         // validate the AUTHORITY section as well - this will generally be the
441         // NS rrset, plus proof of a secure delegation or not
442         rrsets = m.getSectionRRsets(Section.AUTHORITY);
443         for (int i = 0; i < rrsets.length; i++) {
444             int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
445             // If anything in the authority section fails to be secure, we have
446             // a bad message.
447             if (status != SecurityStatus.SECURE) {
448                 // log.debug("Positive response has failed AUTHORITY rrset: "
449                 // + rrsets[i]);
450                 m.setStatus(SecurityStatus.BOGUS);
451                 return;
452             }
453
454             int type = rrsets[i].getType();
455             switch (type) {
456             case Type.DS:
457                 secure_delegation = true;
458                 break;
459             case Type.NS:
460                 delegation = rrsets[i].getName();
461                 break;
462             case Type.NSEC:
463                 nsec = (NSECRecord) rrsets[i].first();
464                 break;
465             case Type.NSEC3:
466                 if (nsec3s == null) nsec3s = new ArrayList<NSEC3Record>();
467                 NSEC3Record nsec3 = (NSEC3Record) rrsets[i].first();
468                 nsec3s.add(nsec3);
469                 nsec3zone = rrsets[i].getSignerName(); // this is a hack of sorts.
470                 break;
471             default:
472                 // FIXME: should probably whine if we see something else.
473                 break;
474             }
475         }
476         
477         // At this point, all validatable RRsets have been validated.
478         // Now to check to see if we have a valid combination of things.
479         if (delegation == null) {
480             // somehow we have a referral without an NS rrset.
481             m.setStatus(SecurityStatus.BOGUS);
482             return;
483         }
484         
485         if (secure_delegation) {
486             if (nsec != null || nsec3s.size() > 0) {
487                 // we found both a DS rrset *and* NSEC/NSEC3 rrsets! 
488                 m.setStatus(SecurityStatus.BOGUS);
489                 return;
490             }
491             // otherwise, we are done.
492             m.setStatus(SecurityStatus.SECURE);
493             return;
494         }
495
496         // Note: not going to care if both NSEC and NSEC3 rrsets were present.
497         if (nsec != null) {
498             byte status = ValUtils.nsecProvesNoDS(nsec, delegation);
499             if (status != SecurityStatus.SECURE) {
500                 // The NSEC *must* prove that there was no DS record.  The INSECURE state here is still bogus.
501                 m.setStatus(SecurityStatus.BOGUS);
502                 return;
503             }
504             m.setStatus(SecurityStatus.SECURE);
505             return;
506         }
507         
508         if (nsec3s.size() > 0) {
509             byte status = NSEC3ValUtils.proveNoDS(nsec3s, delegation, nsec3zone);
510             if (status != SecurityStatus.SECURE) {
511                 // the NSEC3 RRs MUST prove no DS, so the INDETERMINATE state is actually bogus
512                 m.setStatus(SecurityStatus.BOGUS);
513                 return;
514             }
515             m.setStatus(SecurityStatus.SECURE);
516             return;            
517         }
518
519         // failed to find proof either way.
520         m.setStatus(SecurityStatus.BOGUS);
521     }
522
523     private void validateCNAMEResponse(SMessage message, SRRset key_rrset) {
524
525     }
526
527     /**
528      * Given an "ANY" response -- a response that contains an answer to a
529      * qtype==ANY question, with answers. This consists of simply verifying all
530      * present answer/auth RRsets, with no checking that all types are present.
531      * 
532      * NOTE: it may be possible to get parent-side delegation point records
533      * here, which won't all be signed. Right now, this routine relies on the
534      * upstream iterative resolver to not return these responses -- instead
535      * treating them as referrals.
536      * 
537      * NOTE: RFC 4035 is silent on this issue, so this may change upon
538      * clarification.
539      * 
540      * Note that by the time this method is called, the process of finding the
541      * trusted DNSKEY rrset that signs this response must already have been
542      * completed.
543      * 
544      * @param message
545      *            The response to validate.
546      * @param key_rrset
547      *            The trusted DNSKEY rrset that matches the signer of the
548      *            answer.
549      */
550     private void validateAnyResponse(SMessage message, SRRset key_rrset) {
551         int qtype = message.getQType();
552
553         if (qtype != Type.ANY)
554             throw new IllegalArgumentException(
555                     "ANY validation called on non-ANY response.");
556
557         SMessage m = message;
558
559         // validate the ANSWER section.
560         SRRset[] rrsets = m.getSectionRRsets(Section.ANSWER);
561         for (int i = 0; i < rrsets.length; i++) {
562             int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
563             // If the (answer) rrset failed to validate, then this message is
564             // BAD.
565             if (status != SecurityStatus.SECURE) {
566                 // log.debug("Positive response has failed ANSWER rrset: "
567                 // + rrsets[i]);
568                 m.setStatus(SecurityStatus.BOGUS);
569                 return;
570             }
571         }
572
573         // validate the AUTHORITY section as well - this will be the NS rrset
574         // (which could be missing, no problem)
575         rrsets = m.getSectionRRsets(Section.AUTHORITY);
576         for (int i = 0; i < rrsets.length; i++) {
577             int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
578             // If anything in the authority section fails to be secure, we have
579             // a
580             // bad message.
581             if (status != SecurityStatus.SECURE) {
582                 // log.debug("Positive response has failed AUTHORITY rrset: "
583                 // + rrsets[i]);
584                 m.setStatus(SecurityStatus.BOGUS);
585                 return;
586             }
587         }
588
589         // log.trace("Successfully validated positive ANY response");
590         m.setStatus(SecurityStatus.SECURE);
591     }
592
593     /**
594      * Validate a NOERROR/NODATA signed response -- a response that has a
595      * NOERROR Rcode but no ANSWER section RRsets. This consists of verifying
596      * the authority section rrsets and making certain that the authority
597      * section NSEC/NSEC3s proves that the qname does exist and the qtype
598      * doesn't.
599      * 
600      * Note that by the time this method is called, the process of finding the
601      * trusted DNSKEY rrset that signs this response must already have been
602      * completed.
603      * 
604      * @param response
605      *            The response to validate.
606      * @param request
607      *            The request that generated this response.
608      * @param key_rrset
609      *            The trusted DNSKEY rrset that signs this response.
610      */
611     private void validateNodataResponse(SMessage message, SRRset key_rrset) {
612         Name qname = message.getQName();
613         int qtype = message.getQType();
614
615         SMessage m = message;
616
617         // Since we are here, there must be nothing in the ANSWER section to
618         // validate. (Note: CNAME/DNAME responses will not directly get here --
619         // instead they are broken down into individual CNAME/DNAME/final answer
620         // responses.)
621
622         // validate the AUTHORITY section
623         SRRset[] rrsets = m.getSectionRRsets(Section.AUTHORITY);
624
625         boolean hasValidNSEC = false; // If true, then the NODATA has been
626         // proven.
627         Name ce = null; // for wildcard NODATA responses. This is the proven
628         // closest encloser.
629         NSECRecord wc = null; // for wildcard NODATA responses. This is the
630         // wildcard NSEC.
631         List<NSEC3Record> nsec3s = null; // A collection of NSEC3 RRs found in the authority
632         // section.
633         Name nsec3Signer = null; // The RRSIG signer field for the NSEC3 RRs.
634
635         for (int i = 0; i < rrsets.length; i++) {
636             int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
637             if (status != SecurityStatus.SECURE) {
638                 // log.debug("NODATA response has failed AUTHORITY rrset: "
639                 // + rrsets[i]);
640                 m.setStatus(SecurityStatus.BOGUS);
641                 return;
642             }
643
644             // If we encounter an NSEC record, try to use it to prove NODATA.
645             // This needs to handle the ENT NODATA case.
646             if (rrsets[i].getType() == Type.NSEC) {
647                 NSECRecord nsec = (NSECRecord) rrsets[i].first();
648                 if (ValUtils.nsecProvesNodata(nsec, qname, qtype)) {
649                     hasValidNSEC = true;
650                     if (nsec.getName().isWild()) wc = nsec;
651                 } else if (ValUtils.nsecProvesNameError(
652                                                         nsec,
653                                                         qname,
654                                                         rrsets[i].getSignerName())) {
655                     ce = ValUtils.closestEncloser(qname, nsec);
656                 }
657             }
658
659             // Collect any NSEC3 records present.
660             if (rrsets[i].getType() == Type.NSEC3) {
661                 if (nsec3s == null) nsec3s = new ArrayList<NSEC3Record>();
662                 nsec3s.add((NSEC3Record) rrsets[i].first());
663                 nsec3Signer = rrsets[i].getSignerName();
664             }
665         }
666
667         // check to see if we have a wildcard NODATA proof.
668
669         // The wildcard NODATA is 1 NSEC proving that qname does not exists (and
670         // also proving what the closest encloser is), and 1 NSEC showing the
671         // matching wildcard, which must be *.closest_encloser.
672         if (ce != null || wc != null) {
673             try {
674                 Name wc_name = new Name("*", ce);
675                 if (!wc_name.equals(wc.getName())) {
676                     hasValidNSEC = false;
677                 }
678             } catch (TextParseException e) {
679                 // log.error(e);
680             }
681         }
682
683         NSEC3ValUtils.stripUnknownAlgNSEC3s(nsec3s);
684
685         if (!hasValidNSEC && nsec3s != null && nsec3s.size() > 0) {
686             // try to prove NODATA with our NSEC3 record(s)
687             hasValidNSEC = NSEC3ValUtils.proveNodata(nsec3s, qname, qtype,
688                                                      nsec3Signer);
689         }
690
691         if (!hasValidNSEC) {
692             // log.debug("NODATA response failed to prove NODATA "
693             // + "status with NSEC/NSEC3");
694             // log.trace("Failed NODATA:\n" + m);
695             m.setStatus(SecurityStatus.BOGUS);
696             return;
697         }
698         // log.trace("successfully validated NODATA response.");
699         m.setStatus(SecurityStatus.SECURE);
700     }
701
702     /**
703      * Validate a NAMEERROR signed response -- a response that has a NXDOMAIN
704      * Rcode. This consists of verifying the authority section rrsets and making
705      * certain that the authority section NSEC proves that the qname doesn't
706      * exist and the covering wildcard also doesn't exist..
707      * 
708      * Note that by the time this method is called, the process of finding the
709      * trusted DNSKEY rrset that signs this response must already have been
710      * completed.
711      * 
712      * @param response
713      *            The response to validate.
714      * @param request
715      *            The request that generated this response.
716      * @param key_rrset
717      *            The trusted DNSKEY rrset that signs this response.
718      */
719     private void validateNameErrorResponse(SMessage message, SRRset key_rrset) {
720         Name qname = message.getQName();
721
722         SMessage m = message;
723
724         // FIXME: should we check to see if there is anything in the answer
725         // section? if so, what should the result be?
726
727         // Validate the authority section -- all RRsets in the authority section
728         // must be signed and valid.
729         // In addition, the NSEC record(s) must prove the NXDOMAIN condition.
730
731         boolean hasValidNSEC = false;
732         boolean hasValidWCNSEC = false;
733         SRRset[] rrsets = m.getSectionRRsets(Section.AUTHORITY);
734         List<NSEC3Record> nsec3s = null;
735         Name nsec3Signer = null;
736
737         for (int i = 0; i < rrsets.length; i++) {
738             int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
739             if (status != SecurityStatus.SECURE) {
740                 // log.debug("NameError response has failed AUTHORITY rrset: "
741                 // + rrsets[i]);
742                 m.setStatus(SecurityStatus.BOGUS);
743                 return;
744             }
745             if (rrsets[i].getType() == Type.NSEC) {
746                 NSECRecord nsec = (NSECRecord) rrsets[i].first();
747
748                 if (ValUtils.nsecProvesNameError(nsec, qname,
749                                                  rrsets[i].getSignerName())) {
750                     hasValidNSEC = true;
751                 }
752                 if (ValUtils.nsecProvesNoWC(nsec, qname,
753                                             rrsets[i].getSignerName())) {
754                     hasValidWCNSEC = true;
755                 }
756             }
757             if (rrsets[i].getType() == Type.NSEC3) {
758                 if (nsec3s == null) nsec3s = new ArrayList<NSEC3Record>();
759                 nsec3s.add((NSEC3Record) rrsets[i].first());
760                 nsec3Signer = rrsets[i].getSignerName();
761             }
762         }
763
764         NSEC3ValUtils.stripUnknownAlgNSEC3s(nsec3s);
765
766         if (nsec3s != null && nsec3s.size() > 0) {
767             // log.debug("Validating nxdomain: using NSEC3 records");
768             // Attempt to prove name error with nsec3 records.
769
770             if (NSEC3ValUtils.allNSEC3sIgnoreable(nsec3s, key_rrset, mVerifier)) {
771                 // log.debug("all NSEC3s were validated but ignored.");
772                 m.setStatus(SecurityStatus.INSECURE);
773                 return;
774             }
775
776             hasValidNSEC = NSEC3ValUtils.proveNameError(nsec3s, qname,
777                                                         nsec3Signer);
778
779             // Note that we assume that the NSEC3ValUtils proofs encompass the
780             // wildcard part of the proof.
781             hasValidWCNSEC = hasValidNSEC;
782         }
783
784         // If the message fails to prove either condition, it is bogus.
785         if (!hasValidNSEC) {
786             // log.debug("NameError response has failed to prove: "
787             // + "qname does not exist");
788             m.setStatus(SecurityStatus.BOGUS);
789             return;
790         }
791
792         if (!hasValidWCNSEC) {
793             // log.debug("NameError response has failed to prove: "
794             // + "covering wildcard does not exist");
795             m.setStatus(SecurityStatus.BOGUS);
796             return;
797         }
798
799         // Otherwise, we consider the message secure.
800         // log.trace("successfully validated NAME ERROR response.");
801         m.setStatus(SecurityStatus.SECURE);
802     }
803
804     public byte validateMessage(SMessage message, Name zone) {
805
806         // FIXME: it is unclear if we should actually normalize our responses
807         // Instead, maybe we should just fail if they are not normal?
808         message = normalize(message);
809         
810         if (! needsValidation(message)) {
811             return SecurityStatus.UNCHECKED;
812         }
813         
814         SRRset key_rrset = findKeys(message);
815         if (key_rrset == null) {
816             return SecurityStatus.BOGUS;
817         }
818
819         ValUtils.ResponseType subtype = ValUtils.classifyResponse(message, zone);
820
821         switch (subtype) {
822         case POSITIVE:
823             // log.trace("Validating a positive response");
824             validatePositiveResponse(message, key_rrset);
825             break;
826         case REFERRAL:
827             validateReferral(message, key_rrset);
828             break;
829         case NODATA:
830             // log.trace("Validating a NODATA response");
831             validateNodataResponse(message, key_rrset);
832             break;
833         case NAMEERROR:
834             // log.trace("Validating a NXDOMAIN response");
835             validateNameErrorResponse(message, key_rrset);
836             break;
837         case CNAME:
838             // log.trace("Validating a CNAME response");
839             // forward on to the special CNAME state for this.
840             // state.state = ValEventState.CNAME_STATE;
841             validateCNAMEResponse(message, key_rrset);
842             break;
843         case ANY:
844             // log.trace("Validating a positive ANY response");
845             validateAnyResponse(message, key_rrset);
846             break;
847         default:
848             // log.error("unhandled response subtype: " + subtype);
849         }
850
851         return message.getSecurityStatus().getStatus();
852
853     }
854 }