]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[500-strengthen-option-def-parser] Added OptionDefParser code value sanity checks
authorFrancis Dupont <fdupont@isc.org>
Tue, 26 Feb 2019 20:57:53 +0000 (21:57 +0100)
committerFrancis Dupont <fdupont@isc.org>
Wed, 10 Apr 2019 21:07:46 +0000 (17:07 -0400)
src/bin/dhcp4/json_config_parser.cc
src/bin/dhcp6/json_config_parser.cc
src/lib/dhcpsrv/parsers/client_class_def_parser.cc
src/lib/dhcpsrv/parsers/dhcp_parsers.cc
src/lib/dhcpsrv/parsers/dhcp_parsers.h
src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc

index cd0bbb372cb455dced6d62d18139e9070f7c623b..049f06ab7e5cfa17b1ef1f2508c91e6a31b37fc8 100644 (file)
@@ -357,7 +357,7 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
         // We need definitions first
         ConstElementPtr option_defs = mutable_cfg->get("option-def");
         if (option_defs) {
-            OptionDefListParser parser;
+            OptionDefListParser parser(AF_INET);
             CfgOptionDefPtr cfg_option_def = srv_cfg->getCfgOptionDef();
             parser.parse(cfg_option_def, option_defs);
         }
@@ -667,5 +667,101 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
     return (answer);
 }
 
+void databaseConfigFetch(const SrvConfigPtr& srv_cfg) {
+
+    ConfigBackendDHCPv4Mgr& mgr = ConfigBackendDHCPv4Mgr::instance();
+
+    // Close any existing CB databasess, then open all in srv_cfg (if any)
+    if (!databaseConfigConnect(srv_cfg)) {
+        // There are no CB databases so we're done
+        return;
+    }
+
+    LOG_INFO(dhcp4_logger, DHCP4_CONFIG_FETCH);
+
+    // For now we find data based on first backend that has it.
+    BackendSelector backend_selector(BackendSelector::Type::UNSPEC);
+
+    // Use the server_tag if set, otherwise use ALL.
+    std::string server_tag = srv_cfg->getServerTag();
+    ServerSelector& server_selector = (server_tag.empty()? ServerSelector::ALL()
+                                                         : ServerSelector::ONE(server_tag));
+    // Create the external config into which we'll fetch backend config data.
+    SrvConfigPtr external_cfg = CfgMgr::instance().createExternalCfg();
+
+    // First let's fetch the globals and add them to external config.
+    data::StampedValueCollection globals;
+    globals = mgr.getPool()->getAllGlobalParameters4(backend_selector, server_selector);
+    addGlobalsToConfig(external_cfg, globals);
+
+    // Now we fetch the option definitions and add them.
+    OptionDefContainer option_defs = mgr.getPool()->getAllOptionDefs4(backend_selector,
+                                                                      server_selector);
+    for (auto option_def = option_defs.begin(); option_def != option_defs.end(); ++option_def) {
+        external_cfg->getCfgOptionDef()->add((*option_def), (*option_def)->getOptionSpaceName());
+    }
+
+    // Next fetch the options. They are returned as a container of OptionDescriptors.
+    OptionContainer options = mgr.getPool()->getAllOptions4(backend_selector, server_selector);
+    for (auto option = options.begin(); option != options.end(); ++option) {
+        external_cfg->getCfgOption()->add((*option), (*option).space_name_);
+    }
+
+    // Now fetch the shared networks.
+    SharedNetwork4Collection networks = mgr.getPool()->getAllSharedNetworks4(backend_selector,
+                                                                             server_selector);
+    for (auto network = networks.begin(); network != networks.end(); ++network) {
+        external_cfg->getCfgSharedNetworks4()->add((*network));
+    }
+
+    // Next we fetch subnets.
+    Subnet4Collection subnets = mgr.getPool()->getAllSubnets4(backend_selector, server_selector);
+    for (auto subnet = subnets.begin(); subnet != subnets.end(); ++subnet) {
+        external_cfg->getCfgSubnets4()->add((*subnet));
+    }
+
+    // Now we merge the fecthed configuration into the staging configuration.
+    CfgMgr::instance().mergeIntoStagingCfg(external_cfg->getSequence());
+    LOG_INFO(dhcp4_logger, DHCP4_CONFIG_MERGED);
+}
+
+bool databaseConfigConnect(const SrvConfigPtr& srv_cfg) {
+    // We need to get rid of any existing backends.  These would be any
+    // opened by previous configuration cycle.
+    ConfigBackendDHCPv4Mgr& mgr = ConfigBackendDHCPv4Mgr::instance();
+    mgr.delAllBackends();
+
+    // Fetch the config-control info.
+    ConstConfigControlInfoPtr config_ctl = srv_cfg->getConfigControlInfo();
+    if (!config_ctl || config_ctl->getConfigDatabases().empty()) {
+        // No config dbs, nothing to do.
+        return (false);
+    }
+
+    // Iterate over the configured DBs and instantiate them.
+    for (auto db : config_ctl->getConfigDatabases()) {
+        LOG_INFO(dhcp4_logger, DHCP4_OPEN_CONFIG_DB)
+                 .arg(db.redactedAccessString());
+        mgr.addBackend(db.getAccessString());
+    }
+
+    // Let the caller know we have opened DBs.
+    return (true);
+}
+
+
+void addGlobalsToConfig(SrvConfigPtr external_cfg, data::StampedValueCollection& cb_globals) {
+    const auto& index = cb_globals.get<StampedValueNameIndexTag>();
+    for (auto cb_global = index.begin(); cb_global != index.end(); ++cb_global) {
+
+        if ((*cb_global)->amNull()) {
+            continue;
+        }
+
+        external_cfg->addConfiguredGlobal((*cb_global)->getName(),
+                                          (*cb_global)->getElementValue());
+    }
+}
+
 }; // end of isc::dhcp namespace
 }; // end of isc namespace
index 89d9e3d1b267752a21ed0900c7e5f23c779deb67..c15c2ce7bc214d20bb466361a91bef5be32976db 100644 (file)
@@ -464,7 +464,7 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
         // We need definitions first
         ConstElementPtr option_defs = mutable_cfg->get("option-def");
         if (option_defs) {
-            OptionDefListParser parser;
+            OptionDefListParser parser(AF_INET6);
             CfgOptionDefPtr cfg_option_def = srv_config->getCfgOptionDef();
             parser.parse(cfg_option_def, option_defs);
         }
index a9a8c92dd6bb37dbf691d8523ef070ef874e16fc..7177a4b7c6dc5fbcc94d8ce5640d697d70f9d510 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2015-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2019 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
@@ -108,10 +108,10 @@ ClientClassDefParser::parse(ClientClassDictionaryPtr& class_dictionary,
                 SimpleParser4::OPTION4_DEF_DEFAULTS :
                 SimpleParser6::OPTION6_DEF_DEFAULTS);
 
-        OptionDefParser parser;
+        OptionDefParser parser(family);
         BOOST_FOREACH(ConstElementPtr option_def, option_defs->listValue()) {
             OptionDefinitionTuple def;
-                
+
             def = parser.parse(option_def);
             // Verify if the defition is for an option which are
             // in a deferred processing list.
index bcffcca7dc91abfcdbef42f2dcb440d51368052a..ea8ad59ea3705345f776c07e91d5755e39c1ab25 100644 (file)
@@ -111,12 +111,16 @@ OptionDataParser::findOptionDefinition(const std::string& option_space,
 
 // ******************************** OptionDefParser ****************************
 
+OptionDefParser::OptionDefParser(const uint16_t address_family)
+    : address_family_(address_family) {
+}
+
 std::pair<isc::dhcp::OptionDefinitionPtr, std::string>
 OptionDefParser::parse(ConstElementPtr option_def) {
 
     // Get mandatory parameters.
     std::string name = getString(option_def, "name");
-    uint32_t code = getInteger(option_def, "code");
+    int64_t code64 = getInteger(option_def, "code");
     std::string type = getString(option_def, "type");
 
     // Get optional parameters. Whoever called this parser, should have
@@ -127,6 +131,29 @@ OptionDefParser::parse(ConstElementPtr option_def) {
     std::string encapsulates = getString(option_def, "encapsulate");
     ConstElementPtr user_context = option_def->get("user-context");
 
+    // Check code value.
+    if (code64 < 0) {
+        isc_throw(DhcpConfigError, "option code must not be negative "
+                  "(" << getPosition("code", option_def) << ")");
+    } else if (code64 == 0) {
+        isc_throw(DhcpConfigError, "option code must not be zero "
+                  "(" << getPosition("code", option_def) << ")");
+    } else if (address_family_ == AF_INET &&
+               code64 > std::numeric_limits<uint8_t>::max()) {
+        isc_throw(DhcpConfigError, "invalid option code '" << code64
+                  << "', it must not be greater than '"
+                  << static_cast<int>(std::numeric_limits<uint8_t>::max())
+                  << "' (" << getPosition("code", option_def) << ")");
+    } else if (address_family_ == AF_INET6 &&
+               code64 > std::numeric_limits<uint16_t>::max()) {
+        isc_throw(DhcpConfigError, "invalid option code '" << code64
+                  << "', it must not be greater than '"
+                  << std::numeric_limits<uint16_t>::max()
+                  << "' (" << getPosition("code", option_def) << ")");
+    }
+    uint32_t code = static_cast<uint32_t>(code64);
+
+    // Validate space name.
     if (!OptionSpace::validateName(space)) {
         isc_throw(DhcpConfigError, "invalid option space name '"
                   << space << "' ("
@@ -198,6 +225,11 @@ OptionDefParser::parse(ConstElementPtr option_def) {
 }
 
 // ******************************** OptionDefListParser ************************
+
+OptionDefListParser::OptionDefListParser(const uint16_t address_family)
+    : address_family_(address_family) {
+}
+
 void
 OptionDefListParser::parse(CfgOptionDefPtr storage, ConstElementPtr option_def_list) {
     if (!option_def_list) {
@@ -207,7 +239,7 @@ OptionDefListParser::parse(CfgOptionDefPtr storage, ConstElementPtr option_def_l
                   << option_def_list->getPosition() << ")");
     }
 
-    OptionDefParser parser;
+    OptionDefParser parser(address_family_);
     BOOST_FOREACH(ConstElementPtr option_def, option_def_list->listValue()) {
         OptionDefinitionTuple def;
 
index cdba49fa988c0b98121a1079f1d6954c22b128b7..8333c4b74962c0e1a80c567c54d9bd748148d438 100644 (file)
@@ -228,6 +228,11 @@ typedef std::pair<isc::dhcp::OptionDefinitionPtr, std::string> OptionDefinitionT
 /// This parser creates an instance of a single option definition.
 class OptionDefParser : public isc::data::SimpleParser {
 public:
+    /// @brief Constructor.
+    ///
+    /// @param address_family Address family: @c AF_INET or @c AF_INET6.
+    OptionDefParser(const uint16_t address_family);
+
     /// @brief Parses an entry that describes single option definition.
     ///
     /// @param option_def a configuration entry to be parsed.
@@ -236,6 +241,10 @@ public:
     /// @throw DhcpConfigError if parsing was unsuccessful.
     OptionDefinitionTuple
     parse(isc::data::ConstElementPtr option_def);
+
+private:
+    /// @brief Address family: @c AF_INET or @c AF_INET6.
+    uint16_t address_family_;
 };
 
 /// @brief Parser for a list of option definitions.
@@ -246,6 +255,11 @@ public:
 /// is put into the provided storage.
 class OptionDefListParser : public isc::data::SimpleParser {
 public:
+    /// @brief Constructor.
+    ///
+    /// @param address_family Address family: @c AF_INET or @c AF_INET6.
+    OptionDefListParser(const uint16_t address_family);
+
     /// @brief Parses a list of option definitions, create them and store in cfg
     ///
     /// This method iterates over def_list, which is a JSON list of option definitions,
@@ -255,6 +269,10 @@ public:
     /// @param def_list JSON list describing option definitions
     /// @param cfg parsed option definitions will be stored here
     void parse(CfgOptionDefPtr cfg, isc::data::ConstElementPtr def_list);
+
+private:
+    /// @brief Address family: @c AF_INET or @c AF_INET6.
+    uint16_t address_family_;
 };
 
 /// @brief a collection of pools
@@ -421,7 +439,7 @@ public:
     /// @throw isc::dhcp::DhcpConfigError if the address string is not a valid
     /// IP address, is an address of the wrong family, or is already in the
     /// relay address list
-    void addAddress(const std::string& name, const std::string& address_str, 
+    void addAddress(const std::string& name, const std::string& address_str,
                     isc::data::ConstElementPtr relay_elem,
                     const isc::dhcp::Network::RelayInfoPtr& relay_info);
 private:
index 2c5b57bbedb582206060c83d80e1244b833217a7..949c7f7070306e4830ff73b290152d8200073fb6 100644 (file)
@@ -233,7 +233,7 @@ public:
             if (def_config != values_map.end()) {
 
                 CfgOptionDefPtr cfg_def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef();
-                OptionDefListParser def_list_parser;
+                OptionDefListParser def_list_parser(family_);
                 def_list_parser.parse(cfg_def, def_config->second);
             }
 
@@ -717,6 +717,73 @@ TEST_F(ParseConfigTest, defaultSpaceOptionDefTest) {
     cfg.runCfgOptionsTest(family_, config);
 }
 
+/// @brief Check parsing of option definitions using invalid code fails.
+TEST_F(ParseConfigTest, badCodeOptionDefTest) {
+
+    {
+
+        SCOPED_TRACE("negative code");
+        std::string config =
+            "{ \"option-def\": [ {"
+            "      \"name\": \"negative\","
+            "      \"code\": -1,"
+            "      \"type\": \"ipv6-address\","
+            "      \"space\": \"isc\""
+            "  } ]"
+            "}";
+
+        int rcode = parseConfiguration(config, true);
+        ASSERT_NE(0, rcode);
+    }
+
+    {
+        SCOPED_TRACE("zero code");
+        std::string config =
+            "{ \"option-def\": [ {"
+            "      \"name\": \"zero\","
+            "      \"code\": 0,"
+            "      \"type\": \"ipv6-address\","
+            "      \"space\": \"isc\""
+            "  } ]"
+            "}";
+
+        int rcode = parseConfiguration(config, true);
+        ASSERT_NE(0, rcode);
+    }
+
+    {
+        SCOPED_TRACE("out of range code (v6)");
+        std::string config =
+            "{ \"option-def\": [ {"
+            "      \"name\": \"hundred-thousands\","
+            "      \"code\": 100000,"
+            "      \"type\": \"ipv6-address\","
+            "      \"space\": \"isc\""
+            "  } ]"
+            "}";
+
+        int rcode = parseConfiguration(config, true);
+        ASSERT_NE(0, rcode);
+    }
+
+    {
+        SCOPED_TRACE("out of range code (v4)");
+        family_ = AF_INET;     // Switch to DHCPv4.
+
+        std::string config =
+            "{ \"option-def\": [ {"
+            "      \"name\": \"thousand\","
+            "      \"code\": 1000,"
+            "      \"type\": \"ip-address\","
+            "      \"space\": \"isc\""
+            "  } ]"
+            "}";
+
+        int rcode = parseConfiguration(config, false);
+        ASSERT_NE(0, rcode);
+    }
+}
+
 /// @brief Check parsing of option definitions using invalid space fails.
 TEST_F(ParseConfigTest, badSpaceOptionDefTest) {
 
@@ -724,7 +791,7 @@ TEST_F(ParseConfigTest, badSpaceOptionDefTest) {
     std::string config =
         "{ \"option-def\": [ {"
         "      \"name\": \"foo\","
-        "      \"code\": 100000,"
+        "      \"code\": 100,"
         "      \"type\": \"ipv6-address\","
         "      \"space\": \"-1\""
         "  } ]"