#include <testutils/test_to_element.h>
#include <boost/lexical_cast.hpp>
+#include <fstream>
using namespace isc;
using namespace isc::data;
/// @param name is the value to compare against key's name_.
/// @param algorithm is the string value to compare against key's algorithm.
/// @param secret is the value to compare against key's secret.
+/// @param secret_file is the file name where the secret can be found.
+/// @param digestbits is the minimum truncated length in bits.
///
/// @return returns true if there is a match across the board, otherwise it
/// returns false.
bool checkKey(TSIGKeyInfoPtr key, const std::string& name,
const std::string& algorithm, const std::string& secret,
- uint32_t digestbits = 0) {
+ std::string secret_file, uint32_t digestbits = 0) {
// Return value, assume its a match.
return (((key) &&
(key->getName() == name) &&
- (key->getAlgorithm() == algorithm) &&
+ (key->getAlgorithm() == algorithm) &&
(key->getDigestbits() == digestbits) &&
- (key->getSecret() == secret) &&
+ (key->getSecret() == secret) &&
+ (key->getSecretFile() == secret_file) &&
(key->getTSIGKey())));
}
/// ensure any D2 object(s) that were created by a prior invocation are
/// destroyed. This permits parsing to be conducted more than once
/// in the same test.
- virtual void reset(){};
+ virtual void reset() {
+ }
/// @brief Adds default values to the given element tree
///
public:
/// @brief Constructor
TSIGKeyInfoParserTest()
- : D2SimpleParserTest(D2ParserContext::PARSER_TSIG_KEY) {
+ : D2SimpleParserTest(D2ParserContext::PARSER_TSIG_KEY),
+ test_file_name_(TEST_DATA_BUILDDIR "/sf-test") {
}
/// @brief Free up the keys created by parsing
/// @brief Destructor
virtual ~TSIGKeyInfoParserTest() {
+ static_cast<void>(remove(test_file_name_.c_str()));
reset();
};
/// @brief Retains the TSIGKeyInfo created by a successful parsing
TSIGKeyInfoPtr key_;
-};
+ /// @brief Secret file name.
+ std::string test_file_name_;
+};
/// @brief Test fixture class for testing TSIGKeyInfo list parsing.
class TSIGKeyInfoListParserTest : public D2SimpleParserTest {
/// 1. Name cannot be blank.
/// 2. Algorithm cannot be blank.
/// 3. Secret cannot be blank.
+/// 4. Secret file cannot be invalid when specified.
TEST_F(TSIGKeyInfoParserTest, invalidEntry) {
// Name cannot be blank.
"}";
PARSE_FAIL(config, "Cannot make D2TsigKey: non-zero bits left over"
" bogus (<string>:1:1)");
-}
+ // Secret file must exist.
+ config = "{"
+ " \"name\": \"d2_key_one\" , "
+ " \"algorithm\": \"HMAC-MD5\" , "
+ " \"digest-bits\": 120 , "
+ " \"secret-file\": \"/does/not/exist\" "
+ "}";
+ PARSE_FAIL(config, "tsig-key : Expected a file at path "
+ "'/does/not/exist' (<string>:1:91)");
+
+ // Secret file must be a regular file.
+ config = "{"
+ " \"name\": \"d2_key_one\" , "
+ " \"algorithm\": \"HMAC-MD5\" , "
+ " \"digest-bits\": 120 , "
+ " \"secret-file\": \"/\" "
+ "}";
+ PARSE_FAIL(config, "tsig-key : Expected '/' to be a regular file "
+ "(<string>:1:91)");
+
+ // Secret file must not be empty.
+ std::ofstream fs(test_file_name_.c_str(),
+ std::ofstream::out | std::ofstream::trunc);
+ ASSERT_TRUE(fs.is_open());
+ fs.close();
+
+ config = "{"
+ " \"name\": \"d2_key_one\" , "
+ " \"algorithm\": \"HMAC-MD5\" , "
+ " \"digest-bits\": 120 , "
+ " \"secret-file\": \"";
+ config += test_file_name_ + "\" }";
+ std::string expected = "tsig-key : Expected '";
+ expected += test_file_name_ + "' to not be empty (<string>:1:91)";
+ PARSE_FAIL(config, expected);
+
+ // Secret file content must be valid.
+ fs = std::ofstream(test_file_name_.c_str(),
+ std::ofstream::out | std::ofstream::trunc);
+ ASSERT_TRUE(fs.is_open());
+ fs << "bogus";
+ fs.close();
+
+ config = "{"
+ " \"name\": \"d2_key_one\" , "
+ " \"algorithm\": \"HMAC-MD5\" , "
+ " \"digest-bits\": 120 , "
+ " \"secret-file\": \"";
+ config += test_file_name_ + "\" }";
+ PARSE_FAIL(config, "Cannot make D2TsigKey: non-zero bits left over"
+ " bogus (<string>:1:1)");
+}
/// @brief Verifies that TSIGKeyInfo parsing creates a proper TSIGKeyInfo
/// when given a valid combination of entries.
// Verify the key contents.
EXPECT_TRUE(checkKey(key_, "d2_key_one", "HMAC-MD5",
- "dGhpcyBrZXkgd2lsbCBtYXRjaA==", 120));
+ "dGhpcyBrZXkgd2lsbCBtYXRjaA==", "", 120));
+
+ // Verify unparsing.
+ runToElementTest<TSIGKeyInfo>(config, *key_);
+}
+
+/// @brief Verifies that TSIGKeyInfo parsing creates a proper TSIGKeyInfo
+/// when given a valid secret file.
+TEST_F(TSIGKeyInfoParserTest, validSecretFile) {
+ // Valid entries for TSIG key, all items are required.
+ std::string config = "{"
+ " \"name\": \"d2_key_one\" , "
+ " \"algorithm\": \"HMAC-MD5\" , "
+ " \"digest-bits\": 120 , "
+ " \"secret-file\": \"";
+ config += test_file_name_ + "\" }";
+ // Create and fill the secret file.
+ std::ofstream fs(test_file_name_.c_str(),
+ std::ofstream::out | std::ofstream::trunc);
+ ASSERT_TRUE(fs.is_open());
+ fs << "dGhpcyBrZXkgd2lsbCBtYXRjaA==";
+ fs.close();
+ // Verify that it parses.
+ PARSE_OK(config);
+ ASSERT_TRUE(key_);
+
+ // Verify the key contents.
+ EXPECT_TRUE(checkKey(key_, "d2_key_one", "HMAC-MD5",
+ "dGhpcyBrZXkgd2lsbCBtYXRjaA==",
+ test_file_name_, 120));
// Verify unparsing.
runToElementTest<TSIGKeyInfo>(config, *key_);
// Verify the key contents.
EXPECT_TRUE(checkKey(key, "key1", TSIGKeyInfo::HMAC_MD5_STR,
- ref_secret, 80));
+ ref_secret, "", 80));
// Find the 2nd key and retrieve it.
gotit = keys_->find("key2");
// Verify the key contents.
EXPECT_TRUE(checkKey(key, "key2", TSIGKeyInfo::HMAC_SHA1_STR,
- ref_secret, 80));
+ ref_secret, "", 80));
// Find the 3rd key and retrieve it.
gotit = keys_->find("key3");
// Verify the key contents.
EXPECT_TRUE(checkKey(key, "key3", TSIGKeyInfo::HMAC_SHA256_STR,
- ref_secret, 128));
+ ref_secret, "", 128));
// Find the 4th key and retrieve it.
gotit = keys_->find("key4");
// Verify the key contents.
EXPECT_TRUE(checkKey(key, "key4", TSIGKeyInfo::HMAC_SHA224_STR,
- ref_secret, 112));
+ ref_secret, "", 112));
// Find the 5th key and retrieve it.
gotit = keys_->find("key5");
// Verify the key contents.
EXPECT_TRUE(checkKey(key, "key5", TSIGKeyInfo::HMAC_SHA384_STR,
- ref_secret, 192));
+ ref_secret, "", 192));
// Find the 6th key and retrieve it.
gotit = keys_->find("key6");
// Verify the key contents.
EXPECT_TRUE(checkKey(key, "key6", TSIGKeyInfo::HMAC_SHA512_STR,
- ref_secret, 256));
+ ref_secret, "", 256));
}
/// @brief Tests the enforcement of data validation when parsing DnsServerInfos.
-// Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2017-2024 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
const bool generate_action = false;
#endif
+/// @brief the test secret file name.
+std::string test_file_name(TEST_DATA_BUILDDIR "/sf-test");
+
/// @brief Read a file into a string
std::string
readFile(const std::string& file_path) {
first_lib->set("library", Element::create(lib_path));
}
+/// @brief Replace the test secret file name.
+void testFileNameReplacer(ConstElementPtr d2_cfg) {
+ ConstElementPtr tsig_keys = d2_cfg->get("tsig-keys");
+ if (!tsig_keys || (tsig_keys->size() != 4)) {
+ return;
+ }
+ ElementPtr third_key = tsig_keys->getNonConst(2);
+ if (third_key->contains("secret")) {
+ return;
+ }
+ third_key->set("secret-file", Element::create(test_file_name));
+}
+
+/// @brief Create and fill the secret file.
+void fillSecretFile() {
+ std::ofstream fs(test_file_name.c_str(),
+ std::ofstream::out | std::ofstream::trunc);
+ ASSERT_TRUE(fs.is_open());
+ fs << "Gwk53fvy3CmbupoI9TgigA==";
+ fs.close();
+}
+
}
/// Test fixture class
Daemon::setVerbose(false);
// Create fresh context.
resetConfiguration();
+ // Fill test secret file.
+ fillSecretFile();
}
~D2GetConfigTest() {
+ static_cast<void>(remove(test_file_name.c_str()));
resetConfiguration();
}
// update hooks-libraries
pathReplacer(d2);
+ // update tsig-keys.
+ testFileNameReplacer(d2);
+
// try DHCPDDNS configure
ConstElementPtr status;
try {
ASSERT_NO_THROW(d2 = jsonj->get("DhcpDdns"));
ASSERT_TRUE(d2);
pathReplacer(d2);
+ testFileNameReplacer(d2);
// check that unparsed and expected values match
EXPECT_TRUE(isEquivalent(unparsed, jsonj));
// check on pretty prints too
#include <config.h>
+#include <asiolink/io_error.h>
#include <d2srv/d2_log.h>
#include <d2srv/d2_cfg_mgr.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <exceptions/exceptions.h>
-#include <asiolink/io_error.h>
+#include <util/filesystem.h>
#include <boost/scoped_ptr.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <sstream>
#include <string>
-using namespace isc::process;
using namespace isc::data;
+using namespace isc::process;
namespace isc {
namespace d2 {
const char* TSIGKeyInfo::HMAC_SHA512_STR = "HMAC-SHA512";
TSIGKeyInfo::TSIGKeyInfo(const std::string& name, const std::string& algorithm,
- const std::string& secret, uint32_t digestbits)
- :name_(name), algorithm_(algorithm), secret_(secret),
- digestbits_(digestbits), tsig_key_() {
+ const std::string& secret, std::string secret_file,
+ uint32_t digestbits)
+ : name_(name), algorithm_(algorithm), secret_(secret),
+ secret_file_(secret_file), digestbits_(digestbits), tsig_key_() {
remakeKey();
}
result->set("name", Element::create(name_));
// Set algorithm
result->set("algorithm", Element::create(algorithm_));
- // Set secret
- result->set("secret", Element::create(secret_));
+ // Set secret[-file]
+ if (!secret_file_.empty()) {
+ result->set("secret-file", Element::create(secret_file_));
+ } else {
+ result->set("secret", Element::create(secret_));
+ }
// Set digest-bits
result->set("digest-bits",
Element::create(static_cast<int64_t>(digestbits_)));
std::string name = getString(key_config, "name");
std::string algorithm = getString(key_config, "algorithm");
uint32_t digestbits = getInteger(key_config, "digest-bits");
- std::string secret = getString(key_config, "secret");
+ std::string secret_file;
+ std::string secret;
+ if (key_config->contains("secret-file")) {
+ secret_file = getString(key_config, "secret-file");
+ try {
+ secret = util::file::getContent(secret_file);
+ if (secret.empty()) {
+ isc_throw(BadValue, "Expected '" << secret_file
+ << "' to not be empty");
+ }
+ } catch (const std::exception& ex) {
+ isc_throw(D2CfgError, "tsig-key : " << ex.what()
+ << " (" << getPosition("secret-file", key_config)
+ << ")");
+ }
+ } else {
+ secret = getString(key_config, "secret");
+ }
ConstElementPtr user_context = key_config->get("user-context");
// Algorithm must be valid.
// with an invalid secret content.
TSIGKeyInfoPtr key_info;
try {
- key_info.reset(new TSIGKeyInfo(name, algorithm, secret, digestbits));
+ key_info.reset(new TSIGKeyInfo(name, algorithm, secret,
+ secret_file, digestbits));
} catch (const std::exception& ex) {
isc_throw(D2CfgError, ex.what() << " ("
<< key_config->getPosition() << ")");