Browse Source

[3432] Fixed d2::TSIGKeyInfo::remake to use correct dns::TSIGKey constructor

The dns::TSIGKey constructor that was being by d2:TSIGKeyINfo expects an
ordinary string value for secret and then encodes it to base64. Since
TSIGKeyInfo configuration value for secret is already expected to be base64
encoded this was causing it to be encoded again resulting in verification
errors when tested against Bind9.

Changed TSIGKeyInfo::remake to use the appropriate TSIGKey constructor.
Thomas Markwalder 11 years ago
parent
commit
5819b2620f

+ 9 - 3
src/bin/d2/d2_config.cc

@@ -72,9 +72,15 @@ TSIGKeyInfo::stringToAlgorithmName(const std::string& algorithm_id) {
 void
 TSIGKeyInfo::remakeKey() {
     try {
-        tsig_key_.reset(new dns::TSIGKey(dns::Name(name_),
-                                         stringToAlgorithmName(algorithm_),
-                                         secret_.c_str(), secret_.size()));
+        // Since our secret value is base64 encoded already, we need to
+        // build the input string for the appropriate TSIGKey constructor.
+        // If secret isn't a valid base64 value, the constructor will throw.
+        std::ostringstream stream;
+        stream << dns::Name(name_).toText() << ":"
+               << secret_ << ":"
+               << stringToAlgorithmName(algorithm_);
+
+        tsig_key_.reset(new dns::TSIGKey(stream.str()));
     } catch (const std::exception& ex) {
         isc_throw(D2CfgError, "Cannot make TSIGKey: " << ex.what());
     }

+ 15 - 3
src/bin/d2/d2_config.h

@@ -175,10 +175,22 @@ public:
     /// -# "SHA384"
     /// -# "SHA512"
     ///
-    /// @param secret the secret component of this key
+    /// @param secret the base-64 encoded secret component for this key
+    /// such as one would typically see in a key file for the entry "Key"
+    /// in the example below:
+    /// @code
+    ///   Private-key-format: v1.3
+    ///   Algorithm: 157 (HMAC_MD5)
+    ///   Key: LSWXnfkKZjdPJI5QxlpnfQ==
+    ///   Bits: AAA=
+    ///   Created: 20140515143700
+    ///   Publish: 20140515143700
+    ///   Activate: 20140515143700
+    /// @endcode
+    ///
     /// @throw D2CfgError if values supplied are invalid:
     /// name cannot be blank, algorithm must be a supported value,
-    /// secret cannot be blank
+    /// secret must be a non-blank, base64 encoded string.
     TSIGKeyInfo(const std::string& name, const std::string& algorithm,
                 const std::string& secret);
 
@@ -250,7 +262,7 @@ private:
     /// @brief The string ID of the algorithm that should be used for this key.
     std::string algorithm_;
 
-    /// @brief The secret value component of this key.
+    /// @brief The base64 encoded string secret value component of this key.
     std::string secret_;
 
     /// @brief The actual TSIG key.

+ 48 - 34
src/bin/d2/tests/d2_cfg_mgr_unittests.cc

@@ -17,6 +17,7 @@
 #include <d2/d2_cfg_mgr.h>
 #include <d_test_stubs.h>
 #include <test_data_files_config.h>
+#include <util/encode/base64.h>
 
 #include <boost/foreach.hpp>
 #include <gtest/gtest.h>
@@ -117,8 +118,8 @@ bool checkServer(DnsServerInfoPtr server, const char* hostname,
 ///
 /// @return returns true if there is a match across the board, otherwise it
 /// returns false.
-bool checkKey(TSIGKeyInfoPtr key, const char* name,
-                 const char *algorithm, const char* secret)
+bool checkKey(TSIGKeyInfoPtr key, const std::string& name,
+                 const std::string& algorithm, const std::string& secret)
 {
     // Return value, assume its a match.
     bool result = true;
@@ -255,14 +256,12 @@ public:
 /// 1. Name cannot be blank.
 /// 2. Algorithm cannot be blank.
 /// 3. Secret cannot be blank.
-/// @TODO TSIG keys are not fully functional. Only basic validation is
-/// currently supported. This test will need to expand as they evolve.
 TEST_F(TSIGKeyInfoTest, invalidEntry) {
     // Config with a blank name entry.
     std::string config = "{"
                          " \"name\": \"\" , "
                          " \"algorithm\": \"MD5\" , "
-                         " \"secret\": \"0123456789\" "
+                         "   \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" "
                          "}";
     ASSERT_TRUE(fromJSON(config));
 
@@ -273,7 +272,7 @@ TEST_F(TSIGKeyInfoTest, invalidEntry) {
     config = "{"
                          " \"name\": \"d2_key_one\" , "
                          " \"algorithm\": \"\" , "
-                         " \"secret\": \"0123456789\" "
+                         "   \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" "
                          "}";
 
     ASSERT_TRUE(fromJSON(config));
@@ -285,7 +284,7 @@ TEST_F(TSIGKeyInfoTest, invalidEntry) {
     config = "{"
                          " \"name\": \"d2_key_one\" , "
                          " \"algorithm\": \"bogus\" , "
-                         " \"secret\": \"0123456789\" "
+                         "   \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" "
                          "}";
 
     ASSERT_TRUE(fromJSON(config));
@@ -293,7 +292,6 @@ TEST_F(TSIGKeyInfoTest, invalidEntry) {
     // Verify that build fails on blank algorithm.
     EXPECT_THROW(parser_->build(config_set_), D2CfgError);
 
-
     // Config with a blank secret entry.
     config = "{"
                          " \"name\": \"d2_key_one\" , "
@@ -305,6 +303,18 @@ TEST_F(TSIGKeyInfoTest, invalidEntry) {
 
     // Verify that build fails blank secret
     EXPECT_THROW(parser_->build(config_set_), D2CfgError);
+
+    // Config with an invalid secret entry.
+    config = "{"
+                         " \"name\": \"d2_key_one\" , "
+                         " \"algorithm\": \"MD5\" , "
+                         " \"secret\": \"bogus\" "
+                         "}";
+
+    ASSERT_TRUE(fromJSON(config));
+
+    // Verify that build fails an invalid secret
+    EXPECT_THROW(parser_->build(config_set_), D2CfgError);
 }
 
 /// @brief Verifies that TSIGKeyInfo parsing creates a proper TSIGKeyInfo
@@ -314,12 +324,13 @@ TEST_F(TSIGKeyInfoTest, validEntry) {
     std::string config = "{"
                          " \"name\": \"d2_key_one\" , "
                          " \"algorithm\": \"MD5\" , "
-                         " \"secret\": \"0123456789\" "
+                         " \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
                          "}";
     ASSERT_TRUE(fromJSON(config));
 
     // Verify that it builds and commits without throwing.
-    ASSERT_NO_THROW(parser_->build(config_set_));
+    //ASSERT_NO_THROW(parser_->build(config_set_));
+    (parser_->build(config_set_));
     ASSERT_NO_THROW(parser_->commit());
 
     // Verify the correct number of keys are present
@@ -332,7 +343,8 @@ TEST_F(TSIGKeyInfoTest, validEntry) {
     TSIGKeyInfoPtr& key = gotit->second;
 
     // Verify the key contents.
-    EXPECT_TRUE(checkKey(key, "d2_key_one", "MD5", "0123456789"));
+    EXPECT_TRUE(checkKey(key, "d2_key_one", "MD5",
+                         "dGhpcyBrZXkgd2lsbCBtYXRjaA=="));
 }
 
 /// @brief Verifies that attempting to parse an invalid list of TSIGKeyInfo
@@ -343,15 +355,16 @@ TEST_F(TSIGKeyInfoTest, invalidTSIGKeyList) {
 
                          " { \"name\": \"key1\" , "
                          "   \"algorithm\": \"MD5\" ,"
-                         "   \"secret\": \"secret11\" "
+                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
                          " },"
+                         // this entry has an invalid algorithm
                          " { \"name\": \"key2\" , "
                          "   \"algorithm\": \"\" ,"
-                         "   \"secret\": \"secret12\" "
+                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
                          " },"
                          " { \"name\": \"key3\" , "
                          "   \"algorithm\": \"MD5\" ,"
-                         "   \"secret\": \"secret13\" "
+                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
                          " }"
                          " ]";
 
@@ -373,15 +386,15 @@ TEST_F(TSIGKeyInfoTest, duplicateTSIGKey) {
 
                          " { \"name\": \"key1\" , "
                          "   \"algorithm\": \"MD5\" ,"
-                         "   \"secret\": \"secret11\" "
+                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
                          " },"
                          " { \"name\": \"key2\" , "
                          "   \"algorithm\": \"MD5\" ,"
-                         "   \"secret\": \"secret12\" "
+                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
                          " },"
                          " { \"name\": \"key1\" , "
                          "   \"algorithm\": \"MD5\" ,"
-                         "   \"secret\": \"secret13\" "
+                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
                          " }"
                          " ]";
 
@@ -403,27 +416,27 @@ TEST_F(TSIGKeyInfoTest, validTSIGKeyList) {
 
                          " { \"name\": \"key1\" , "
                          "   \"algorithm\": \"MD5\" ,"
-                         "   \"secret\": \"secret1\" "
+                         "  \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
                          " },"
                          " { \"name\": \"key2\" , "
                          "   \"algorithm\": \"SHA1\" ,"
-                         "   \"secret\": \"secret2\" "
+                         "  \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
                          " },"
                          " { \"name\": \"key3\" , "
                          "   \"algorithm\": \"SHA256\" ,"
-                         "   \"secret\": \"secret3\" "
+                         "  \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
                          " },"
                          " { \"name\": \"key4\" , "
                          "   \"algorithm\": \"SHA224\" ,"
-                         "   \"secret\": \"secret4\" "
+                         "  \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
                          " },"
                          " { \"name\": \"key5\" , "
                          "   \"algorithm\": \"SHA384\" ,"
-                         "   \"secret\": \"secret5\" "
+                         "  \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
                          " },"
                          " { \"name\": \"key6\" , "
                          "   \"algorithm\": \"SHA512\" ,"
-                         "   \"secret\": \"secret6\" "
+                         "   \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
                          " }"
                          " ]";
 
@@ -436,6 +449,7 @@ TEST_F(TSIGKeyInfoTest, validTSIGKeyList) {
     ASSERT_NO_THROW(parser->build(config_set_));
     ASSERT_NO_THROW(parser->commit());
 
+    std::string ref_secret = "dGhpcyBrZXkgd2lsbCBtYXRjaA==";
     // Verify the correct number of keys are present
     int count =  keys_->size();
     ASSERT_EQ(6, count);
@@ -446,7 +460,7 @@ TEST_F(TSIGKeyInfoTest, validTSIGKeyList) {
     TSIGKeyInfoPtr& key = gotit->second;
 
     // Verify the key contents.
-    EXPECT_TRUE(checkKey(key, "key1", TSIGKeyInfo::MD5_STR, "secret1"));
+    EXPECT_TRUE(checkKey(key, "key1", TSIGKeyInfo::MD5_STR, ref_secret));
 
     // Find the 2nd key and retrieve it.
     gotit = keys_->find("key2");
@@ -454,7 +468,7 @@ TEST_F(TSIGKeyInfoTest, validTSIGKeyList) {
     key = gotit->second;
 
     // Verify the key contents.
-    EXPECT_TRUE(checkKey(key, "key2", TSIGKeyInfo::SHA1_STR, "secret2"));
+    EXPECT_TRUE(checkKey(key, "key2", TSIGKeyInfo::SHA1_STR, ref_secret));
 
     // Find the 3rd key and retrieve it.
     gotit = keys_->find("key3");
@@ -462,7 +476,7 @@ TEST_F(TSIGKeyInfoTest, validTSIGKeyList) {
     key = gotit->second;
 
     // Verify the key contents.
-    EXPECT_TRUE(checkKey(key, "key3", TSIGKeyInfo::SHA256_STR, "secret3"));
+    EXPECT_TRUE(checkKey(key, "key3", TSIGKeyInfo::SHA256_STR, ref_secret));
 
     // Find the 4th key and retrieve it.
     gotit = keys_->find("key4");
@@ -470,7 +484,7 @@ TEST_F(TSIGKeyInfoTest, validTSIGKeyList) {
     key = gotit->second;
 
     // Verify the key contents.
-    EXPECT_TRUE(checkKey(key, "key4", TSIGKeyInfo::SHA224_STR, "secret4"));
+    EXPECT_TRUE(checkKey(key, "key4", TSIGKeyInfo::SHA224_STR, ref_secret));
 
     // Find the 5th key and retrieve it.
     gotit = keys_->find("key5");
@@ -478,7 +492,7 @@ TEST_F(TSIGKeyInfoTest, validTSIGKeyList) {
     key = gotit->second;
 
     // Verify the key contents.
-    EXPECT_TRUE(checkKey(key, "key5", TSIGKeyInfo::SHA384_STR, "secret5"));
+    EXPECT_TRUE(checkKey(key, "key5", TSIGKeyInfo::SHA384_STR, ref_secret));
 
     // Find the 6th key and retrieve it.
     gotit = keys_->find("key6");
@@ -486,7 +500,7 @@ TEST_F(TSIGKeyInfoTest, validTSIGKeyList) {
     key = gotit->second;
 
     // Verify the key contents.
-    EXPECT_TRUE(checkKey(key, "key6", TSIGKeyInfo::SHA512_STR, "secret6"));
+    EXPECT_TRUE(checkKey(key, "key6", TSIGKeyInfo::SHA512_STR, ref_secret));
 }
 
 /// @brief Tests the enforcement of data validation when parsing DnsServerInfos.
@@ -722,7 +736,7 @@ TEST_F(DdnsDomainTest, ddnsDomainParsing) {
     ASSERT_TRUE(fromJSON(config));
 
     // Add a TSIG key to the test key map, so key validation will pass.
-    addKey("d2_key.tmark.org", "md5", "0123456789");
+    addKey("d2_key.tmark.org", "md5", "GWG/Xfbju4O2iXGqkSu4PQ==");
 
     // Verify that the domain configuration builds and commits without error.
     ASSERT_NO_THROW(parser_->build(config_set_));
@@ -800,8 +814,8 @@ TEST_F(DdnsDomainTest, DdnsDomainListParsing) {
     ASSERT_TRUE(fromJSON(config));
 
     // Add keys to key map so key validation passes.
-    addKey("d2_key.tmark.org", "MD5", "secret1");
-    addKey("d2_key.billcat.net", "MD5", "secret2");
+    addKey("d2_key.tmark.org", "MD5", "GWG/Xfbju4O2iXGqkSu4PQ==");
+    addKey("d2_key.billcat.net", "MD5", "GWG/Xfbju4O2iXGqkSu4PQ==");
 
     // Create the list parser
     isc::dhcp::ParserPtr list_parser;
@@ -941,12 +955,12 @@ TEST_F(D2CfgMgrTest, fullConfig) {
                         "{"
                         "  \"name\": \"d2_key.tmark.org\" , "
                         "  \"algorithm\": \"md5\" , "
-                        "  \"secret\": \"ssh-dont-tell\"  "
+                        "   \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" "
                         "},"
                         "{"
                         "  \"name\": \"d2_key.billcat.net\" , "
                         "  \"algorithm\": \"md5\" , "
-                        "  \"secret\": \"ollie-ollie-in-free\"  "
+                        "   \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" "
                         "}"
                         "],"
                         "\"forward_ddns\" : {"

+ 1 - 1
src/bin/d2/tests/d2_process_unittests.cc

@@ -41,7 +41,7 @@ const char* bad_ip_d2_config = "{ "
                         "\"tsig_keys\": ["
                         "{ \"name\": \"d2_key.tmark.org\" , "
                         "   \"algorithm\": \"md5\" ,"
-                        "   \"secret\": \"0123456989\" "
+                        "   \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" "
                         "} ],"
                         "\"forward_ddns\" : {"
                         "\"ddns_domains\": [ "

+ 1 - 0
src/bin/d2/tests/d2_update_message_unittests.cc

@@ -596,6 +596,7 @@ TEST_F(D2UpdateMessageTest, validTSIG) {
                                     TSIGKey(Name("right.com"),
                                             TSIGKey::HMACMD5_NAME(),
                                             secret.c_str(), secret.size())));
+
     TSIGKeyPtr wrong_key;
     secret = "this key will not match";
     ASSERT_NO_THROW(wrong_key.reset(new

+ 1 - 1
src/bin/d2/tests/d_test_stubs.cc

@@ -28,7 +28,7 @@ const char* valid_d2_config = "{ "
                         "\"tsig_keys\": ["
                         "{ \"name\": \"d2_key.tmark.org\" , "
                         "   \"algorithm\": \"md5\" ,"
-                        "   \"secret\": \"0123456989\" "
+                        "   \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" "
                         "} ],"
                         "\"forward_ddns\" : {"
                         "\"ddns_domains\": [ "

+ 12 - 2
src/bin/d2/tests/nc_test_utils.cc

@@ -18,6 +18,7 @@
 #include <nc_test_utils.h>
 #include <asio.hpp>
 #include <asiolink/udp_endpoint.h>
+#include <util/encode/base64.h>
 
 #include <gtest/gtest.h>
 
@@ -432,8 +433,17 @@ TSIGKeyInfoPtr makeTSIGKeyInfo(const std::string& key_name,
         if (!secret.empty()) {
             key_info.reset(new TSIGKeyInfo(key_name, algorithm, secret));
         } else {
-            // if no secret, then just use the key_name for it
-            key_info.reset(new TSIGKeyInfo(key_name, algorithm, key_name));
+            // Since secret was left blank, we'll convert key_name into a
+            // base64 encoded string and use that.
+            const uint8_t* bytes = reinterpret_cast<const uint8_t*>
+                                                   (key_name.c_str());
+            size_t len = key_name.size();
+            const vector<uint8_t> key_name_v(bytes, bytes + len);
+            std::string key_name64
+                = isc::util::encode::encodeBase64(key_name_v);
+
+            // Now, make the TSIGKeyInfo with a real base64 secret.
+            key_info.reset(new TSIGKeyInfo(key_name, algorithm, key_name64));
         }
     }
 

+ 2 - 1
src/bin/d2/tests/nc_test_utils.h

@@ -365,7 +365,8 @@ extern DdnsDomainPtr makeDomain(const std::string& zone_name,
 /// @brief Creates a TSIGKeyInfo
 ///
 /// @param key_name name of the key
-/// @param secret key secret data.  If blank, the key_name will be used.
+/// @param secret key secret data as a base64 encoded string. If blank,
+/// then the secret value will be generated from key_name.
 /// @param algorithm algorithm to use. Defaults to MD5.
 /// @return a TSIGKeyInfoPtr for the newly created key.  If key_name is blank
 /// the pointer will be empty.

+ 1 - 1
src/bin/d2/tests/nc_trans_unittests.cc

@@ -1009,7 +1009,7 @@ TEST_F(NameChangeTransactionTest, sendUpdate) {
 /// @brief Tests that an unsigned response to a signed request is an error
 TEST_F(NameChangeTransactionTest, tsigUnsignedResponse) {
     NameChangeStubPtr name_change;
-    ASSERT_NO_THROW(name_change = makeCannedTransaction("key_one"));
+    /*ASSERT_NO_THROW*/(name_change = makeCannedTransaction("key_one"));
     ASSERT_NO_THROW(name_change->initDictionaries());
     ASSERT_TRUE(name_change->selectFwdServer());