]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2707] Added config-hash-get with SHA-256
authorFrancis Dupont <fdupont@isc.org>
Mon, 5 Jun 2023 16:13:22 +0000 (18:13 +0200)
committerTomek Mrugalski <tomek@isc.org>
Thu, 22 Jun 2023 14:23:38 +0000 (16:23 +0200)
src/bin/agent/tests/ca_controller_unittests.cc
src/bin/d2/tests/d2_command_unittest.cc
src/bin/dhcp4/ctrl_dhcp4_srv.h
src/bin/dhcp6/ctrl_dhcp6_srv.cc
src/bin/dhcp6/ctrl_dhcp6_srv.h
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc

index f0f88480273508fca9a3815c70f31f9ad02668a0..2513d2dc617c6e1fd33f01a6c1c22b1178795c1f 100644 (file)
@@ -480,6 +480,7 @@ TEST_F(CtrlAgentControllerTest, registeredCommands) {
     // Check that the following command are really available.
     checkCommandRegistered("build-report");
     checkCommandRegistered("config-get");
+    checkCommandRegistered("config-hash-get");
     checkCommandRegistered("config-reload");
     checkCommandRegistered("config-set");
     checkCommandRegistered("config-test");
index c92cbc351883882319d08a97072752aedf680cb0..564b5cd4e9ad1f0bb7cd6d131d5d58841ff6c93d 100644 (file)
@@ -534,6 +534,7 @@ TEST_F(CtrlChannelD2Test, commandsRegistration) {
     EXPECT_TRUE(command_list.find("\"list-commands\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"build-report\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"config-get\"") != string::npos);
+    EXPECT_TRUE(command_list.find("\"config-hash-get\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"config-reload\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"config-set\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"config-test\"") != string::npos);
@@ -627,6 +628,7 @@ TEST_F(CtrlChannelD2Test, listCommands) {
     // We expect the server to report at least the following commands:
     checkListCommands(rsp, "build-report");
     checkListCommands(rsp, "config-get");
+    checkListCommands(rsp, "config-hash-get");
     checkListCommands(rsp, "config-reload");
     checkListCommands(rsp, "config-set");
     checkListCommands(rsp, "config-test");
@@ -704,6 +706,33 @@ TEST_F(CtrlChannelD2Test, configGet) {
     EXPECT_TRUE(cfg->get("DhcpDdns"));
 }
 
+// Tests if the server returns the hash of its configuration using
+// config-hash-get.
+TEST_F(CtrlChannelD2Test, configHashGet) {
+    EXPECT_NO_THROW(createUnixChannelServer());
+    string response;
+
+    sendUnixCommand("{ \"command\": \"config-hash-get\" }", response);
+    ConstElementPtr rsp;
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    int status;
+    ConstElementPtr args = parseAnswer(status, rsp);
+    EXPECT_EQ(CONTROL_RESULT_SUCCESS, status);
+
+    // Ok, now roughly check if the response seems legit.
+    ASSERT_TRUE(args);
+    ASSERT_EQ(Element::map, args->getType());
+    ConstElementPtr hash = args->get("hash");
+    ASSERT_TRUE(hash);
+    ASSERT_EQ(Element::string, hash->getType());
+    // SHA-256 -> 64 hex digits.
+    EXPECT_EQ(64, hash->stringValue().size());
+}
+
 // Verify that the "config-test" command will do what we expect.
 TEST_F(CtrlChannelD2Test, configTest) {
 
index b18f9dd9141028ab8b760259f16ce9574319c6da..e4631db6991a0f13b237aa6fa72dfb7459fd6209 100644 (file)
@@ -85,6 +85,7 @@ public:
     /// - config-reload
     /// - config-set
     /// - config-get
+    /// - config-hash-get
     /// - config-test
     /// - dhcp-disable
     /// - dhcp-enable
index 3dfb9b4cb502bc88c879cb110ad44c1707b51ca2..13c978e5ac922099dd4928d862ef13c93d6030b2 100644 (file)
@@ -9,6 +9,7 @@
 #include <cc/command_interpreter.h>
 #include <cc/data.h>
 #include <config/command_mgr.h>
+#include <cryptolink/crypto_hash.h>
 #include <dhcp/libdhcp++.h>
 #include <dhcp6/ctrl_dhcp6_srv.h>
 #include <dhcp6/dhcp6_log.h>
@@ -25,6 +26,7 @@
 #include <hooks/hooks_manager.h>
 #include <process/cfgrpt/config_report.h>
 #include <stats/stats_mgr.h>
+#include <util/encode/hex.h>
 #include <util/multi_threading_mgr.h>
 
 #include <signal.h>
@@ -292,6 +294,27 @@ ControlledDhcpv6Srv::commandConfigGetHandler(const string&,
     return (createAnswer(CONTROL_RESULT_SUCCESS, config));
 }
 
+ConstElementPtr
+ControlledDhcpv6Srv::commandConfigHashGetHandler(const string&,
+                                                 ConstElementPtr /*args*/) {
+    ConstElementPtr config = CfgMgr::instance().getCurrentCfg()->toElement();
+    // Assume that config is never null.
+    string config_txt = config->str();
+    OutputBuffer hash_data(0);
+    isc::cryptolink::digest(config_txt.c_str(),
+                            config_txt.size(),
+                            isc::cryptolink::HashAlgorithm::SHA256,
+                            hash_data);
+    vector<uint8_t> hash;
+    hash.resize(hash_data.getLength());
+    if (hash.size() > 0) {
+        memmove(&hash[0], hash_data.getData(), hash.size());
+    }
+    ElementPtr params = Element::createMap();
+    params->set("hash", Element::create(encode::encodeHex(hash)));
+    return (createAnswer(CONTROL_RESULT_SUCCESS, params));
+}
+
 ConstElementPtr
 ControlledDhcpv6Srv::commandConfigWriteHandler(const string&,
                                                ConstElementPtr args) {
@@ -842,6 +865,9 @@ ControlledDhcpv6Srv::processCommand(const string& command,
         } else if (command == "config-get") {
             return (srv->commandConfigGetHandler(command, args));
 
+        } else if (command == "config-hash-get") {
+            return (srv->commandConfigHashGetHandler(command, args));
+
         } else if (command == "config-test") {
             return (srv->commandConfigTestHandler(command, args));
 
@@ -1162,6 +1188,9 @@ ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t server_port /*= DHCP6_SERVER_P
     CommandMgr::instance().registerCommand("config-get",
         std::bind(&ControlledDhcpv6Srv::commandConfigGetHandler, this, ph::_1, ph::_2));
 
+    CommandMgr::instance().registerCommand("config-hash-get",
+        std::bind(&ControlledDhcpv6Srv::commandConfigHashGetHandler, this, ph::_1, ph::_2));
+
     CommandMgr::instance().registerCommand("config-reload",
         std::bind(&ControlledDhcpv6Srv::commandConfigReloadHandler, this, ph::_1, ph::_2));
 
@@ -1258,6 +1287,7 @@ ControlledDhcpv6Srv::~ControlledDhcpv6Srv() {
         CommandMgr::instance().deregisterCommand("build-report");
         CommandMgr::instance().deregisterCommand("config-backend-pull");
         CommandMgr::instance().deregisterCommand("config-get");
+        CommandMgr::instance().deregisterCommand("config-hash-get");
         CommandMgr::instance().deregisterCommand("config-reload");
         CommandMgr::instance().deregisterCommand("config-set");
         CommandMgr::instance().deregisterCommand("config-test");
index 5271ace747f0abbd38688f8d3350c7809dc1e2d3..756c7d6e8c64b172c4d411d04c49a7a772330c77 100644 (file)
@@ -85,6 +85,7 @@ public:
     /// - config-reload
     /// - config-set
     /// - config-get
+    /// - config-hash-get
     /// - config-test
     /// - dhcp-disable
     /// - dhcp-enable
@@ -193,9 +194,9 @@ private:
     commandConfigReloadHandler(const std::string& command,
                                isc::data::ConstElementPtr args);
 
-    /// @brief handler for processing 'get-config' command
+    /// @brief handler for processing 'config-get' command
     ///
-    /// This handler processes get-config command, which retrieves
+    /// This handler processes config-get command, which retrieves
     /// the current configuration and returns it in response.
     ///
     /// @param command (ignored)
@@ -205,9 +206,21 @@ private:
     commandConfigGetHandler(const std::string& command,
                             isc::data::ConstElementPtr args);
 
-    /// @brief handler for processing 'write-config' command
+    /// @brief handler for processing 'config-hash-get' command
     ///
-    /// This handle processes write-config command, which writes the
+    /// This handler processes config-hash-get command, which retrieves
+    /// the hash of the current configuration and returns it in response.
+    ///
+    /// @param command (ignored)
+    /// @param args (ignored)
+    /// @return hash of current configuration wrapped in a response
+    isc::data::ConstElementPtr
+    commandConfigHashGetHandler(const std::string& command,
+                                isc::data::ConstElementPtr args);
+
+    /// @brief handler for processing 'config-write' command
+    ///
+    /// This handle processes config-write command, which writes the
     /// current configuration to disk. This command takes one optional
     /// parameter called filename. If specified, the current configuration
     /// will be written to that file. If not specified, the file used during
index d1ed5217130b3396f6be0258e5b46b1eedb41f4b..a0cd3fce0e292cd7ff5d6f974cdffa7f4def00c0 100644 (file)
@@ -576,6 +576,7 @@ TEST_F(CtrlDhcpv6SrvTest, commandsRegistration) {
     EXPECT_TRUE(command_list.find("\"build-report\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"config-backend-pull\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"config-get\"") != string::npos);
+    EXPECT_TRUE(command_list.find("\"config-hash-get\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"config-set\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"config-write\"") != string::npos);
     EXPECT_TRUE(command_list.find("\"leases-reclaim\"") != string::npos);
@@ -829,6 +830,33 @@ TEST_F(CtrlChannelDhcpv6SrvTest, configGet) {
     EXPECT_TRUE(cfg->get("Dhcp6")->get("loggers"));
 }
 
+// Tests if the server returns the hash of its configuration using
+// config-hash-get.
+TEST_F(CtrlChannelDhcpv6SrvTest, configHashGet) {
+    createUnixChannelServer();
+    std::string response;
+
+    sendUnixCommand("{ \"command\": \"config-hash-get\" }", response);
+    ConstElementPtr rsp;
+
+    // The response should be a valid JSON.
+    EXPECT_NO_THROW(rsp = Element::fromJSON(response));
+    ASSERT_TRUE(rsp);
+
+    int status;
+    ConstElementPtr args = parseAnswer(status, rsp);
+    EXPECT_EQ(CONTROL_RESULT_SUCCESS, status);
+
+    // Ok, now roughly check if the response seems legit.
+    ASSERT_TRUE(args);
+    ASSERT_EQ(Element::map, args->getType());
+    ConstElementPtr hash = args->get("hash");
+    ASSERT_TRUE(hash);
+    ASSERT_EQ(Element::string, hash->getType());
+    // SHA-256 -> 64 hex digits.
+    EXPECT_EQ(64, hash->stringValue().size());
+}
+
 // Verify that the "config-test" command will do what we expect.
 TEST_F(CtrlChannelDhcpv6SrvTest, configTest) {
     createUnixChannelServer();
@@ -1447,6 +1475,7 @@ TEST_F(CtrlChannelDhcpv6SrvTest, listCommands) {
     checkListCommands(rsp, "build-report");
     checkListCommands(rsp, "config-backend-pull");
     checkListCommands(rsp, "config-get");
+    checkListCommands(rsp, "config-hash-get");
     checkListCommands(rsp, "config-reload");
     checkListCommands(rsp, "config-set");
     checkListCommands(rsp, "config-test");