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