05b8d651bc05b441392e80dafd640f28fe25785c
[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.  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     /**
615      * Given a "CNAME" response (i.e., a response that contains at
616      * least one CNAME, and qtype != CNAME).  This largely consists of
617      * validating each CNAME RRset until the CNAME chain goes "out of
618      * zone".  Note that out-of-order CNAME chains will have been
619      * cleaned up via normalize().  When traversing the CNAME chain,
620      * we detect if the CNAME were generated from a wildcard, and we
621      * detect when the chain goes "out-of-zone".  If the chain doesn't
622      * go out-of-zone, we then determine if the CNAME response was
623      * positive or negative (i.e., did it end with a non-CNAME
624      * RRset?).  For each in-zone wildcard generated CNAME, we check
625      * for a proof that the alias (the owner of each cname) doesn't
626      * exist.  If the response is negative (i.e., remains in-zone and
627      * results in no RRset, we do a NODATA or NXDOMAIN proof based on
628      * the actual RCODE.
629      *
630      * Note that once the CNAME chain goes out of zone, any further
631      * CNAMEs are not DNSSEC validated (we would need more trusted
632      * keysets for that), so this isn't useful in all cases (i.e., for
633      * testing a nameserver, like BIND, which generates CNAME chains
634      * across zones.)
635      *
636      * Note that by the time this method is called, the process of
637      * finding the trusted DNSKEY rrset that signs this reponse must
638      * already have been completed.
639      */
640     private void validateCNAMEResponse(SMessage message, SRRset key_rrset)
641     {
642         Name qname = message.getQName();
643         int  qtype = message.getQType();
644
645         Name       sname     = qname; // this is the "current" name in the chain
646         boolean    dname     = false; // a flag indicating that prev iteration was a dname
647         boolean    inZone    = true; // a flag telling us if we ended up in zone.
648         boolean    positive  = false; // a flag telling us if we ended with a positive answer
649         List<Name> wildcards = new Vector<Name>();
650         Name       wc        = null;
651         Name       zone      = key_rrset.getName();
652
653         SRRset[] rrsets = message.getSectionRRsets(Section.ANSWER);
654
655         // Validate the ANSWER section RRsets.
656         for (int i = 0; i < rrsets.length; i++) {
657
658             int  rtype = rrsets[i].getType();
659             Name rname = rrsets[i].getName();
660
661             // Follow the CNAME chain
662             if (rtype == Type.CNAME) {
663                 // If we've gotten off track...  Note: this should be
664                 // impossible with normalization in effect.
665                 if (!sname.equals(rname)) {
666                     mErrorList.add("CNAME chain is broken: expected owner name of " +
667                                    sname + " got: " + rname);
668                     message.setStatus(SecurityStatus.BOGUS);
669                     return;
670                 }
671
672                 sname = ((CNAMERecord) rrsets[i].first()).getAlias();
673
674                 // Check to see if the CNAME was generated by a
675                 // wildcard.  We store the generated name instead of
676                 // the wildcard value, as we need to prove that the
677                 // wildcard wasn't blocked.
678                 wc = ValUtils.rrsetWildcard(rrsets[i]);
679                 if (wc != null) {
680                     wildcards.add(sname);
681                 }
682             }
683
684             // Note when we see a DNAME.
685             if (rtype == Type.DNAME) {
686                 dname = true;
687                 wc = ValUtils.rrsetWildcard(rrsets[i]);
688                 if (wc != null) {
689                     mErrorList.add("Illegal wildcard DNAME found: " + rrsets[i]);
690                 }
691             }
692
693             // Skip validation of CNAMEs following DNAMEs.  The
694             // normalization step will have synthesized an unsigned
695             // CNAME RRset.
696             if (dname && rtype == Type.CNAME) {
697                 dname = false;
698                 continue;
699             }
700
701             if (rtype == qtype) {
702                 positive = true;
703             }
704
705             // Once we've gone off the reservation, avoid further
706             // validation.
707             if (! rname.subdomain(zone)) {
708                 inZone = false;
709                 break;
710             }
711
712             int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
713
714             if (status != SecurityStatus.SECURE) {
715                 mErrorList.add("CNAME response has a failed ANSWER rrset: " +
716                                rrsets[i]);
717                 message.setStatus(SecurityStatus.BOGUS);
718
719                 return;
720             }
721         }
722
723
724         // Validate the AUTHORITY section.
725         rrsets = message.getSectionRRsets(Section.ANSWER);
726         for (int i = 0; i < rrsets.length; i++) {
727
728
729         }
730
731         log.trace("Successfully validated CNAME response");
732         message.setStatus(SecurityStatus.SECURE);
733     }
734
735     /**
736      * Given an "ANY" response -- a response that contains an answer
737      * to a qtype==ANY question, with answers. This consists of simply
738      * verifying all present answer/auth RRsets, with no checking that
739      * all types are present.
740      *
741      * NOTE: it may be possible to get parent-side delegation point
742      * records here, which won't all be signed. Right now, this
743      * routine relies on the upstream iterative resolver to not return
744      * these responses -- instead treating them as referrals.
745      *
746      * NOTE: RFC 4035 is silent on this issue, so this may change upon
747      * clarification.
748      *
749      * Note that by the time this method is called, the process of
750      * finding the trusted DNSKEY rrset that signs this response must
751      * already have been completed.
752      *
753      * @param message
754      *            The response to validate.
755      * @param key_rrset
756      *            The trusted DNSKEY rrset that matches the signer of the
757      *            answer.
758      */
759     private void validateAnyResponse(SMessage message, SRRset key_rrset) {
760         int qtype = message.getQType();
761
762         if (qtype != Type.ANY) {
763             throw new IllegalArgumentException("ANY validation called on non-ANY response.");
764         }
765
766         SMessage m = message;
767
768         // validate the ANSWER section.
769         SRRset[] rrsets = m.getSectionRRsets(Section.ANSWER);
770
771         for (int i = 0; i < rrsets.length; i++) {
772             int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
773
774             // If the (answer) rrset failed to validate, then this message is
775             // BAD.
776             if (status != SecurityStatus.SECURE) {
777                 mErrorList.add("Positive response has failed ANSWER rrset: " +
778                                rrsets[i]);
779                 m.setStatus(SecurityStatus.BOGUS);
780
781                 return;
782             }
783         }
784
785         // validate the AUTHORITY section as well - this will be the NS rrset
786         // (which could be missing, no problem)
787         rrsets = m.getSectionRRsets(Section.AUTHORITY);
788
789         for (int i = 0; i < rrsets.length; i++) {
790             int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
791
792             // If anything in the authority section fails to be secure, we have
793             // a bad message.
794             if (status != SecurityStatus.SECURE) {
795                 mErrorList.add("Positive response has failed AUTHORITY rrset: " +
796                                rrsets[i]);
797                 m.setStatus(SecurityStatus.BOGUS);
798
799                 return;
800             }
801         }
802
803         log.trace("Successfully validated positive ANY response");
804         m.setStatus(SecurityStatus.SECURE);
805     }
806
807     /**
808      * Validate a NOERROR/NODATA signed response -- a response that
809      * has a NOERROR Rcode but no ANSWER section RRsets. This consists
810      * of verifying the authority section rrsets and making certain
811      * that the authority section NSEC/NSEC3s proves that the qname
812      * does exist and the qtype doesn't.
813      *
814      * Note that by the time this method is called, the process of
815      * finding the trusted DNSKEY rrset that signs this response must
816      * already have been completed.
817      *
818      * @param response
819      *            The response to validate.
820      * @param request
821      *            The request that generated this response.
822      * @param key_rrset
823      *            The trusted DNSKEY rrset that signs this response.
824      */
825     private void validateNodataResponse(SMessage     message,
826                                         SRRset       key_rrset,
827                                         List<String> errorList) {
828         Name qname = message.getQName();
829         int  qtype = message.getQType();
830
831         SMessage m = message;
832
833         // Since we are here, there must be nothing in the ANSWER
834         // section to validate.
835
836         // validate the AUTHORITY section
837         SRRset[] rrsets = m.getSectionRRsets(Section.AUTHORITY);
838
839         // If true, then the NODATA has been proven.
840         boolean hasValidNSEC = false;
841
842         // for wildcard NODATA responses. This is the proven closest
843         // encloser.
844         Name ce = null;
845
846         // for wildcard NODATA responses. This is the wildcard NSEC.
847         NSECRecord wc = null;
848
849         // A collection of NSEC3 RRs found in the authority section.
850         List<NSEC3Record> nsec3s = null;
851
852         // The RRSIG signer field for the NSEC3 RRs.
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("NODATA response has failed AUTHORITY rrset: " +
860                                rrsets[i]);
861                 m.setStatus(SecurityStatus.BOGUS);
862
863                 return;
864             }
865
866             // If we encounter an NSEC record, try to use it to prove NODATA.
867             // This needs to handle the ENT NODATA case.
868             if (rrsets[i].getType() == Type.NSEC) {
869                 NSECRecord nsec = (NSECRecord) rrsets[i].first();
870
871                 if (ValUtils.nsecProvesNodata(nsec, qname, qtype)) {
872                     hasValidNSEC = true;
873
874                     if (nsec.getName().isWild()) {
875                         wc = nsec;
876                     }
877                 } else if (ValUtils.nsecProvesNameError(nsec, qname, rrsets[i].getSignerName())) {
878                     ce = ValUtils.closestEncloser(qname, nsec);
879                 }
880             }
881
882             // Collect any NSEC3 records present.
883             if (rrsets[i].getType() == Type.NSEC3) {
884                 if (nsec3s == null) {
885                     nsec3s = new ArrayList<NSEC3Record>();
886                 }
887
888                 nsec3s.add((NSEC3Record) rrsets[i].first());
889                 nsec3Signer = rrsets[i].getSignerName();
890             }
891         }
892
893         // check to see if we have a wildcard NODATA proof.
894
895         // The wildcard NODATA is 1 NSEC proving that qname does not
896         // exists (and also proving what the closest encloser is), and
897         // 1 NSEC showing the matching wildcard, which must be
898         // *.closest_encloser.
899         if ((ce != null) || (wc != null)) {
900             try {
901                 Name wc_name = new Name("*", ce);
902
903                 if (!wc_name.equals(wc.getName())) {
904                     hasValidNSEC = false;
905                 }
906             } catch (TextParseException e) {
907                 log.error(e);
908             }
909         }
910
911         NSEC3ValUtils.stripUnknownAlgNSEC3s(nsec3s);
912
913         if (!hasValidNSEC && (nsec3s != null) && (nsec3s.size() > 0)) {
914             // try to prove NODATA with our NSEC3 record(s)
915             hasValidNSEC = NSEC3ValUtils.proveNodata(nsec3s, qname, qtype,
916                                                      nsec3Signer, errorList);
917         }
918
919         if (!hasValidNSEC) {
920             log.debug("NODATA response failed to prove NODATA " +
921                       "status with NSEC/NSEC3");
922             log.trace("Failed NODATA:\n" + m);
923             mErrorList.add("NODATA response failed to prove NODATA status with NSEC/NSEC3");
924             m.setStatus(SecurityStatus.BOGUS);
925
926             return;
927         }
928
929         log.trace("successfully validated NODATA response.");
930         m.setStatus(SecurityStatus.SECURE);
931     }
932
933     /**
934      * Validate a NAMEERROR signed response -- a response that has a
935      * NXDOMAIN Rcode. This consists of verifying the authority
936      * section rrsets and making certain that the authority section
937      * NSEC proves that the qname doesn't exist and the covering
938      * wildcard also doesn't exist..
939      *
940      * Note that by the time this method is called, the process of
941      * finding the trusted DNSKEY rrset that signs this response must
942      * already have been completed.
943      *
944      * @param response
945      *            The response to validate.
946      * @param request
947      *            The request that generated this response.
948      * @param key_rrset
949      *            The trusted DNSKEY rrset that signs this response.
950      */
951     private void validateNameErrorResponse(SMessage message, SRRset key_rrset) {
952         Name qname = message.getQName();
953
954         SMessage m = message;
955
956         if (message.getCount(Section.ANSWER) > 0) {
957             log.warn("NameError response contained records in the ANSWER SECTION");
958             mErrorList.add("NameError response contained records in the ANSWER SECTION");
959             message.setStatus(SecurityStatus.INVALID);
960
961             return;
962         }
963
964         // Validate the authority section -- all RRsets in the authority section
965         // must be signed and valid.
966         // In addition, the NSEC record(s) must prove the NXDOMAIN condition.
967         boolean           hasValidNSEC   = false;
968         boolean           hasValidWCNSEC = false;
969         SRRset[]          rrsets         = m.getSectionRRsets(Section.AUTHORITY);
970         List<NSEC3Record> nsec3s         = null;
971         Name              nsec3Signer    = null;
972
973         for (int i = 0; i < rrsets.length; i++) {
974             int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
975
976             if (status != SecurityStatus.SECURE) {
977                 mErrorList.add("NameError response has failed AUTHORITY rrset: " +
978                                rrsets[i]);
979                 m.setStatus(SecurityStatus.BOGUS);
980
981                 return;
982             }
983
984             if (rrsets[i].getType() == Type.NSEC) {
985                 NSECRecord nsec = (NSECRecord) rrsets[i].first();
986
987                 if (ValUtils.nsecProvesNameError(nsec, qname, rrsets[i].getSignerName())) {
988                     hasValidNSEC = true;
989                 }
990
991                 if (ValUtils.nsecProvesNoWC(nsec, qname, rrsets[i].getSignerName())) {
992                     hasValidWCNSEC = true;
993                 }
994             }
995
996             if (rrsets[i].getType() == Type.NSEC3) {
997                 if (nsec3s == null) {
998                     nsec3s = new ArrayList<NSEC3Record>();
999                 }
1000
1001                 nsec3s.add((NSEC3Record) rrsets[i].first());
1002                 nsec3Signer = rrsets[i].getSignerName();
1003             }
1004         }
1005
1006         NSEC3ValUtils.stripUnknownAlgNSEC3s(nsec3s);
1007
1008         if ((nsec3s != null) && (nsec3s.size() > 0)) {
1009             log.debug("Validating nxdomain: using NSEC3 records");
1010
1011             // Attempt to prove name error with nsec3 records.
1012             if (NSEC3ValUtils.allNSEC3sIgnoreable(nsec3s, key_rrset, mVerifier)) {
1013                 // log.debug("all NSEC3s were validated but ignored.");
1014                 m.setStatus(SecurityStatus.INSECURE);
1015
1016                 return;
1017             }
1018
1019             hasValidNSEC = NSEC3ValUtils.proveNameError(nsec3s, qname, nsec3Signer, mErrorList);
1020
1021             // Note that we assume that the NSEC3ValUtils proofs
1022             // encompass the wildcard part of the proof.
1023             hasValidWCNSEC = hasValidNSEC;
1024         }
1025
1026         // If the message fails to prove either condition, it is bogus.
1027         if (!hasValidNSEC) {
1028             mErrorList.add("NameError response has failed to prove qname does not exist");
1029             m.setStatus(SecurityStatus.BOGUS);
1030
1031             return;
1032         }
1033
1034         if (!hasValidWCNSEC) {
1035             mErrorList.add("NameError response has failed to prove covering wildcard does not exist");
1036             m.setStatus(SecurityStatus.BOGUS);
1037
1038             return;
1039         }
1040
1041         // Otherwise, we consider the message secure.
1042         log.trace("successfully validated NAME ERROR response.");
1043         m.setStatus(SecurityStatus.SECURE);
1044     }
1045
1046     public byte validateMessage(SMessage message, Name zone) {
1047         mErrorList.clear();
1048         if (!zone.isAbsolute()) {
1049             try {
1050                 zone = Name.concatenate(zone, Name.root);
1051             } catch (NameTooLongException e) {
1052                 log.error(e);
1053
1054                 return SecurityStatus.UNCHECKED;
1055             }
1056         }
1057
1058         // It is unclear if we should actually normalize our
1059         // responses Instead, maybe we should just fail if they are
1060         // not normal?
1061         message = normalize(message);
1062
1063         if (!needsValidation(message)) {
1064             return SecurityStatus.UNCHECKED;
1065         }
1066
1067         SRRset key_rrset = findKeys(message);
1068
1069         if (key_rrset == null) {
1070             mErrorList.add("Failed to find matching DNSKEYs for the response");
1071             return SecurityStatus.BOGUS;
1072         }
1073
1074         ValUtils.ResponseType subtype = ValUtils.classifyResponse(message, zone);
1075
1076         switch (subtype) {
1077         case POSITIVE:
1078             log.trace("Validating a positive response");
1079             validatePositiveResponse(message, key_rrset);
1080             break;
1081
1082         case REFERRAL:
1083             validateReferral(message, key_rrset);
1084             break;
1085
1086         case NODATA:
1087             log.trace("Validating a NODATA response");
1088             validateNodataResponse(message, key_rrset, mErrorList);
1089             break;
1090
1091         case NAMEERROR:
1092             log.trace("Validating a NXDOMAIN response");
1093             validateNameErrorResponse(message, key_rrset);
1094             break;
1095
1096         case CNAME:
1097             log.trace("Validating a CNAME response");
1098             validateCNAMEResponse(message, key_rrset);
1099             break;
1100
1101         case ANY:
1102             log.trace("Validating a positive ANY response");
1103             validateAnyResponse(message, key_rrset);
1104             break;
1105
1106         default:
1107             log.error("unhandled response subtype: " + subtype);
1108         }
1109
1110         return message.getSecurityStatus().getStatus();
1111     }
1112
1113     public byte validateMessage(Message message, String zone)
1114         throws TextParseException {
1115         SMessage sm = new SMessage(message);
1116         Name     z  = Name.fromString(zone);
1117
1118         return validateMessage(sm, z);
1119     }
1120
1121     public byte validateMessage(byte[] messagebytes, String zone)
1122         throws IOException {
1123         Message message = new Message(messagebytes);
1124         return validateMessage(message, zone);
1125     }
1126
1127     public byte validateMessage(String b64messagebytes, String zone)
1128         throws IOException {
1129         byte[] messagebytes = base64.fromString(b64messagebytes);
1130         return validateMessage(messagebytes, zone);
1131     }
1132
1133     public List<String> listTrustedKeys() {
1134         return mTrustedKeys.listTrustAnchors();
1135     }
1136
1137     public List<String> getErrorList() {
1138         return mErrorList;
1139     }
1140  }