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