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