]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[1580] Merge branch 'master' into trac1580 to reduce conflicts with results
authorJINMEI Tatuya <jinmei@isc.org>
Thu, 16 Feb 2012 06:50:36 +0000 (22:50 -0800)
committerJINMEI Tatuya <jinmei@isc.org>
Thu, 16 Feb 2012 06:50:36 +0000 (22:50 -0800)
of other NSEC3 tickets.

1  2 
src/bin/auth/query.cc
src/bin/auth/query.h
src/bin/auth/tests/query_unittest.cc

Simple merge
Simple merge
index 6f98bda577e56da0b023c6574ee7f03a2573274f,c5d8a7971359ed1d2b9bf491323104c191b88200..eea46a0a2c3d4024efbf1392ca0e870693ee627f
@@@ -282,9 -329,18 +329,20 @@@ public
              "q00jkcevqvmu85r014c7dkba38o0ji5r";
          hash_map_[Name("nxdomain3.example.com")] =
              "009mhaveqvm6t7vbl5lop2u3t2rp3tom";
 +        hash_map_[Name("*.example.com")] =
 +            "r53bq7cc2uvmubfu5ocmm6pers9tk9en";
+         hash_map_[Name("unsigned-delegation.example.com")] =
+             "q81r598950igr1eqvc60aedlq66425b5"; // a bit larger than H(www)
+         hash_map_[Name("*.uwild.example.com")] =
+             "b4um86eghhds6nea196smvmlo4ors995";
+         hash_map_[Name("unsigned-delegation-optout.example.com")] =
+             "vld46lphhasfapj8og1pglgiasa5o5gt";
+         // For closest encloser proof for www1.uwild.example.com:
+         hash_map_[Name("uwild.example.com")] =
+             "t644ebqk9bibcna874givr6joj62mlhv";
+         hash_map_[Name("www1.uwild.example.com")] =
+             "q04jkcevqvmu85r014c7dkba38o0ji6r"; // a bit larger than H(www)
      }
      virtual isc::dns::Name getOrigin() const { return (origin_); }
      virtual isc::dns::RRClass getClass() const { return (rrclass_); }
@@@ -1656,58 -1830,335 +1832,353 @@@ TEST_F(QueryTest, findNSEC3) 
  
      // Similar to the previous case, but next closer name is different
      // (is the parent) of the non existent name.
-     nsec3Check(true, expected_closest_labels,
-                string(nsec3_apex_txt) + string(nsec3_www_txt),
-                mock_finder->findNSEC3(Name("nx.domain.example.com"), true));
+     {
+         SCOPED_TRACE("nxdomain, next closer != qname");
+         nsec3Check(true, expected_closest_labels,
+                    string(nsec3_apex_txt) + string(nsec3_www_txt),
+                    mock_finder->findNSEC3(Name("nx.domain.example.com"),
+                                           true));
+     }
  
      // In the rest of test we check hash comparison for wrap around cases.
-     nsec3Check(false, 4, nsec3_apex_txt,
-                mock_finder->findNSEC3(Name("nxdomain2.example.com"), false));
-     nsec3Check(false, 4, nsec3_www_txt,
-                mock_finder->findNSEC3(Name("nxdomain3.example.com"), false));
+     {
+         SCOPED_TRACE("largest");
+         nsec3Check(false, 4, nsec3_apex_txt,
+                    mock_finder->findNSEC3(Name("nxdomain2.example.com"),
+                                           false));
+     }
+     {
+         SCOPED_TRACE("smallest");
+         nsec3Check(false, 4, nsec3_www_txt,
+                    mock_finder->findNSEC3(Name("nxdomain3.example.com"),
+                                           false));
+     }
+ }
+ // This tests that the DS is returned above the delegation point as
+ // an authoritative answer, not a delegation. This is as described in
+ // RFC 4035, section 3.1.4.1.
+ // This mock finder is used for some DS-query tests to support the cases
+ // where the query is expected to be handled in a different zone than our
+ // main test zone, example.com.  Only limited methods are expected to called
+ // (and for limited purposes) on this class object in these tests, which
+ // are overridden below.
+ class AlternateZoneFinder : public MockZoneFinder {
+ public:
+     // This zone is expected not to have a DS by default and return NXRRSET
+     // for a DS query.  If have_ds is set to true on construction, it will
+     // return a faked DS answer.
+     AlternateZoneFinder(const Name& origin, bool have_ds = false) :
+         MockZoneFinder(), origin_(origin), have_ds_(have_ds)
+     {}
+     virtual isc::dns::Name getOrigin() const { return (origin_); }
+     virtual FindResult find(const isc::dns::Name&,
+                             const isc::dns::RRType& type,
+                             const FindOptions)
+     {
+         if (type == RRType::SOA()) {
+             RRsetPtr soa = textToRRset(origin_.toText() + " 3600 IN SOA . . "
+                                        "0 0 0 0 0\n", origin_);
+             soa->addRRsig(RdataPtr(new generic::RRSIG(
+                                        getCommonRRSIGText("SOA"))));
+             return (FindResult(SUCCESS, soa));
+         }
+         if (type == RRType::NS()) {
+             RRsetPtr ns = textToRRset(origin_.toText() + " 3600 IN NS " +
+                                       Name("ns").concatenate(origin_).toText());
+             ns->addRRsig(RdataPtr(new generic::RRSIG(
+                                       getCommonRRSIGText("NS"))));
+             return (FindResult(SUCCESS, ns));
+         }
+         if (type == RRType::DS()) {
+             if (have_ds_) {
+                 RRsetPtr ds = textToRRset(origin_.toText() +
+                                           " 3600 IN DS 57855 5 1 " +
+                                           "49FD46E6C4B45C55D4AC69CBD"
+                                           "3CD34AC1AFE51DE");
+                 ds->addRRsig(RdataPtr(new generic::RRSIG(
+                                           getCommonRRSIGText("DS"))));
+                 return (FindResult(SUCCESS, ds));
+             } else {
+                 RRsetPtr nsec = textToRRset(origin_.toText() +
+                                             " 3600 IN NSEC " +
+                                             origin_.toText() +
+                                             " SOA NSEC RRSIG");
+                 nsec->addRRsig(RdataPtr(new generic::RRSIG(
+                                             getCommonRRSIGText("NSEC"))));
+                 return (FindResult(NXRRSET, nsec, RESULT_NSEC_SIGNED));
+             }
+         }
+         // Returning NXDOMAIN is not correct, but doesn't matter for our tests.
+         return (FindResult(NXDOMAIN, ConstRRsetPtr()));
+     }
+ private:
+     const Name origin_;
+     const bool have_ds_;
+ };
+ TEST_F(QueryTest, dsAboveDelegation) {
+     // Pretending to have authority for the child zone, too.
+     memory_client.addZone(ZoneFinderPtr(new AlternateZoneFinder(
+                                             Name("delegation.example.com"))));
+     // The following will succeed only if the search goes to the parent
+     // zone, not the child one we added above.
+     EXPECT_NO_THROW(Query(memory_client, Name("delegation.example.com"),
+                           RRType::DS(), response, true).process());
+     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 4, 6,
+                   (string(delegation_ds_txt) + "\n" +
+                    "delegation.example.com. 3600 IN RRSIG " +
+                    getCommonRRSIGText("DS")).c_str(),
+                   (string(zone_ns_txt) + "\n" +
+                    "example.com. 3600 IN RRSIG " +
+                    getCommonRRSIGText("NS")).c_str(),
+                   ns_addrs_and_sig_txt.c_str());
+ }
+ TEST_F(QueryTest, dsAboveDelegationNoData) {
+     // Similar to the previous case, but the query is for an unsigned zone
+     // (which doesn't have a DS at the parent).  The response should be a
+     // "no data" error.  The query should still be handled at the parent.
+     memory_client.addZone(ZoneFinderPtr(
+                               new AlternateZoneFinder(
+                                   Name("unsigned-delegation.example.com"))));
+     // The following will succeed only if the search goes to the parent
+     // zone, not the child one we added above.
+     EXPECT_NO_THROW(Query(memory_client,
+                           Name("unsigned-delegation.example.com"),
+                           RRType::DS(), response, true).process());
+     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
+                   (string(soa_txt) +
+                    string("example.com. 3600 IN RRSIG ") +
+                    getCommonRRSIGText("SOA") + "\n" +
+                    string(unsigned_delegation_nsec_txt) +
+                    "unsigned-delegation.example.com. 3600 IN RRSIG " +
+                    getCommonRRSIGText("NSEC")).c_str(),
+                   NULL, mock_finder->getOrigin());
+ }
+ // This one checks that type-DS query results in a "no data" response
+ // when it happens to be sent to the child zone, as described in RFC 4035,
+ // section 3.1.4.1. The example is inspired by the B.8. example from the RFC.
+ TEST_F(QueryTest, dsBelowDelegation) {
+     EXPECT_NO_THROW(Query(memory_client, Name("example.com"),
+                           RRType::DS(), response, true).process());
+     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
+                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+                    getCommonRRSIGText("SOA") + "\n" +
+                    string(nsec_apex_txt) + "\n" +
+                    string("example.com. 3600 IN RRSIG ") +
+                    getCommonRRSIGText("NSEC")).c_str(), NULL,
+                   mock_finder->getOrigin());
+ }
+ // Similar to the previous case, but even more pathological: the DS somehow
+ // exists in the child zone.  The Query module should still return SOA.
+ // In our implementation NSEC/NSEC3 isn't attached in this case.
+ TEST_F(QueryTest, dsBelowDelegationWithDS) {
+     mock_finder->addRecord(zone_ds_txt); // add the DS to the child's apex
+     EXPECT_NO_THROW(Query(memory_client, Name("example.com"),
+                           RRType::DS(), response, true).process());
+     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 2, 0, NULL,
+                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+                    getCommonRRSIGText("SOA")).c_str(), NULL,
+                   mock_finder->getOrigin());
+ }
+ // DS query received at a completely irrelevant (neither parent nor child)
+ // server.  It should just like the "noZone" test case, but DS query involves
+ // special processing, so we test it explicitly.
+ TEST_F(QueryTest, dsNoZone) {
+     Query(memory_client, Name("example"), RRType::DS(), response,
+           true).process();
+     responseCheck(response, Rcode::REFUSED(), 0, 0, 0, 0, NULL, NULL, NULL);
+ }
+ // DS query for a "grandchild" zone.  This should result in normal
+ // delegation (unless this server also has authority of the grandchild zone).
+ TEST_F(QueryTest, dsAtGrandParent) {
+     Query(memory_client, Name("grand.delegation.example.com"), RRType::DS(),
+           response, true).process();
+     responseCheck(response, Rcode::NOERROR(), 0, 0, 6, 6, NULL,
+                   (string(delegation_txt) + string(delegation_ds_txt) +
+                    "delegation.example.com. 3600 IN RRSIG " +
+                    getCommonRRSIGText("DS")).c_str(),
+                   ns_addrs_and_sig_txt.c_str());
+ }
+ // DS query sent to a "grandparent" server that also has authority for the
+ // child zone.  In this case the query should be handled in the child
+ // side and should result in no data with SOA.  Note that the server doesn't
+ // have authority for the "parent".  Unlike the dsAboveDelegation test case
+ // the query should be handled in the child zone, not in the grandparent.
+ TEST_F(QueryTest, dsAtGrandParentAndChild) {
+     // Pretending to have authority for the child zone, too.
+     const Name childname("grand.delegation.example.com");
+     memory_client.addZone(ZoneFinderPtr(
+                               new AlternateZoneFinder(childname)));
+     Query(memory_client, childname, RRType::DS(), response, true).process();
+     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
+                   (childname.toText() + " 3600 IN SOA . . 0 0 0 0 0\n" +
+                    childname.toText() + " 3600 IN RRSIG " +
+                    getCommonRRSIGText("SOA") + "\n" +
+                    childname.toText() + " 3600 IN NSEC " +
+                    childname.toText() + " SOA NSEC RRSIG\n" +
+                    childname.toText() + " 3600 IN RRSIG " +
+                    getCommonRRSIGText("NSEC")).c_str(), NULL, childname);
+ }
+ // DS query for the root name (quite pathological).  Since there's no "parent",
+ // the query will be handled in the root zone anyway, and should (normally)
+ // result in no data.
+ TEST_F(QueryTest, dsAtRoot) {
+     // Pretend to be a root server.
+     memory_client.addZone(ZoneFinderPtr(
+                               new AlternateZoneFinder(Name::ROOT_NAME())));
+     Query(memory_client, Name::ROOT_NAME(), RRType::DS(), response,
+           true).process();
+     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
+                   (string(". 3600 IN SOA . . 0 0 0 0 0\n") +
+                    ". 3600 IN RRSIG " + getCommonRRSIGText("SOA") + "\n" +
+                    ". 3600 IN NSEC " + ". SOA NSEC RRSIG\n" +
+                    ". 3600 IN RRSIG " +
+                    getCommonRRSIGText("NSEC")).c_str(), NULL);
+ }
+ // Even more pathological case: A faked root zone actually has its own DS
+ // query.  How we respond wouldn't matter much in practice, but check if
+ // it behaves as it's intended.  This implementation should return the DS.
+ TEST_F(QueryTest, dsAtRootWithDS) {
+     memory_client.addZone(ZoneFinderPtr(
+                               new AlternateZoneFinder(Name::ROOT_NAME(),
+                                                       true)));
+     Query(memory_client, Name::ROOT_NAME(), RRType::DS(), response,
+           true).process();
+     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 2, 2, 0,
+                   (string(". 3600 IN DS 57855 5 1 49FD46E6C4B45C55D4AC69CBD"
+                           "3CD34AC1AFE51DE\n") +
+                    ". 3600 IN RRSIG " + getCommonRRSIGText("DS")).c_str(),
+                   (string(". 3600 IN NS ns.\n") +
+                    ". 3600 IN RRSIG " + getCommonRRSIGText("NS")).c_str(),
+                   NULL);
+ }
+ // Check the signature is present when an NXRRSET is returned
+ TEST_F(QueryTest, nxrrsetWithNSEC3) {
+     mock_finder->setNSEC3Flag(true);
+     // NXRRSET with DNSSEC proof.  We should have SOA, NSEC3 that proves the
+     // NXRRSET and their RRSIGs.
+     Query(memory_client, Name("www.example.com"), RRType::TXT(), response,
+           true).process();
+     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
+                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+                    getCommonRRSIGText("SOA") + "\n" +
+                    string(nsec3_www_txt) + "\n" +
+                    mock_finder->hash_map_[Name("www.example.com.")] +
+                    ".example.com. 3600 IN RRSIG " +
+                    getCommonRRSIGText("NSEC3") + "\n").c_str(),
+                   NULL, mock_finder->getOrigin());
+ }
+ // Check the exception is correctly raised when the NSEC3 thing isn't in the
+ // zone
+ TEST_F(QueryTest, nxrrsetMissingNSEC3) {
+     mock_finder->setNSEC3Flag(true);
+     // We just need it to return false for "matched". This indicates
+     // there's no exact match for NSEC3 on www.example.com.
+     ZoneFinder::FindNSEC3Result nsec3(false, 0, ConstRRsetPtr(),
+                                       ConstRRsetPtr());
+     mock_finder->setNSEC3Result(&nsec3);
+     EXPECT_THROW(Query(memory_client, Name("www.example.com"), RRType::TXT(),
+                        response, true).process(), Query::BadNSEC3);
+ }
+ TEST_F(QueryTest, nxrrsetWithNSEC3_ds_exact) {
+     mock_finder->addRecord(unsigned_delegation_nsec3_txt);
+     mock_finder->setNSEC3Flag(true);
+     // This delegation has no DS, but does have a matching NSEC3 record
+     // (See RFC5155 section 7.2.4)
+     Query(memory_client, Name("unsigned-delegation.example.com."),
+           RRType::DS(), response, true).process();
+     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 4, 0, NULL,
+                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+                    getCommonRRSIGText("SOA") + "\n" +
+                    string(unsigned_delegation_nsec3_txt) + "\n" +
+                    mock_finder->
+                         hash_map_[Name("unsigned-delegation.example.com.")] +
+                    ".example.com. 3600 IN RRSIG " +
+                    getCommonRRSIGText("NSEC3") + "\n").c_str(),
+                   NULL, mock_finder->getOrigin());
+ }
+ TEST_F(QueryTest, nxrrsetWithNSEC3_ds_no_exact) {
+     mock_finder->addRecord(unsigned_delegation_nsec3_txt);
+     mock_finder->setNSEC3Flag(true);
+     // This delegation has no DS, and no directly matching NSEC3 record
+     // So the response should contain closest encloser proof (and the
+     // 'next closer' should have opt-out set, though that is not
+     // actually checked)
+     // (See RFC5155 section 7.2.4)
+     Query(memory_client, Name("unsigned-delegation-optout.example.com."),
+           RRType::DS(), response, true).process();
+     responseCheck(response, Rcode::NOERROR(), AA_FLAG, 0, 6, 0, NULL,
+                   (string(soa_txt) + string("example.com. 3600 IN RRSIG ") +
+                    getCommonRRSIGText("SOA") + "\n" +
+                    string(nsec3_apex_txt) + "\n" +
+                    mock_finder->hash_map_[Name("example.com.")] +
+                    ".example.com. 3600 IN RRSIG " +
+                    getCommonRRSIGText("NSEC3") + "\n" +
+                    string(unsigned_delegation_nsec3_txt) + "\n" +
+                    mock_finder->
+                         hash_map_[Name("unsigned-delegation.example.com.")] +
+                    ".example.com. 3600 IN RRSIG " +
+                    getCommonRRSIGText("NSEC3") + "\n").c_str(),
+                   NULL, mock_finder->getOrigin());
  }
  
 -// The following are tentative tests until we really add tests for the
 -// query logic for these cases.  At that point it's probably better to
 -// clean them up.
 -TEST_F(QueryTest, nxdomainWithNSEC3) {
 +TEST_F(QueryTest, nxdomainWithBadNextNSEC3Proof) {
      mock_finder->setNSEC3Flag(true);
 -    ZoneFinder::FindResult result = mock_finder->find(
 -        Name("nxdomain.example.com"), RRType::A(), ZoneFinder::FIND_DNSSEC);
 -    EXPECT_EQ(ZoneFinder::NXDOMAIN, result.code);
 -    EXPECT_FALSE(result.rrset);
 -    EXPECT_TRUE(result.isNSEC3Signed());
 -    EXPECT_FALSE(result.isWildcard());
 +    ZoneFinder::FindNSEC3Result nsec3(true, 0, textToRRset(nsec3_apex_txt),
 +                                      ConstRRsetPtr());
 +    mock_finder->setNSEC3Result(&nsec3);
 +
 +    EXPECT_THROW(Query(memory_client, Name("nxdomain.example.com"),
 +                       RRType::TXT(), response, true).process(),
 +                 Query::BadNSEC3);
  }
  
- TEST_F(QueryTest, nxrrsetWithNSEC3) {
-     mock_finder->setNSEC3Flag(true);
-     ZoneFinder::FindResult result = mock_finder->find(
-         Name("www.example.com"), RRType::TXT(), ZoneFinder::FIND_DNSSEC);
-     EXPECT_EQ(ZoneFinder::NXRRSET, result.code);
-     EXPECT_FALSE(result.rrset);
-     EXPECT_TRUE(result.isNSEC3Signed());
-     EXPECT_FALSE(result.isWildcard());
- }
 +TEST_F(QueryTest, nxdomainWithNSEC3Proof) {
 +    mock_finder->setNSEC3Flag(true);
 +    Query(memory_client, Name("nxdomain.example.com"), qtype,
 +              response, true).process();
 +    responseCheck(response, Rcode::NXDOMAIN(), AA_FLAG, 0, 6, 0,
 +                  NULL, (string(soa_txt) +
 +                         string("example.com. 3600 IN RRSIG ") +
 +                         getCommonRRSIGText("SOA") + "\n" +
 +                         string(nsec3_apex_txt) + "\n" +
 +                         string("0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example.com. 3600 IN RRSIG ") +
 +                         getCommonRRSIGText("NSEC3") + "\n" +
 +                         string(nsec3_www_txt) + "\n" +
 +                         string("q04jkcevqvmu85r014c7dkba38o0ji5r.example.com. 3600 IN RRSIG ") +
 +                         getCommonRRSIGText("NSEC3")).c_str(),
 +                  NULL, mock_finder->getOrigin());
 +}
 +
 +// The following are tentative tests until we really add tests for the
 +// query logic for these cases.  At that point it's probably better to
 +// clean them up.
  TEST_F(QueryTest, emptyNameWithNSEC3) {
      mock_finder->setNSEC3Flag(true);
      ZoneFinder::FindResult result = mock_finder->find(