From: Francis Dupont Date: Tue, 26 Feb 2019 20:57:53 +0000 (+0100) Subject: [500-strengthen-option-def-parser] Added OptionDefParser code value sanity checks X-Git-Tag: Kea-1.6.0-beta~269 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=85b4bef45ca1eca969102c460033ddb468d68a86;p=thirdparty%2Fkea.git [500-strengthen-option-def-parser] Added OptionDefParser code value sanity checks --- diff --git a/src/bin/dhcp4/json_config_parser.cc b/src/bin/dhcp4/json_config_parser.cc index cd0bbb372c..049f06ab7e 100644 --- a/src/bin/dhcp4/json_config_parser.cc +++ b/src/bin/dhcp4/json_config_parser.cc @@ -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(); + 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 diff --git a/src/bin/dhcp6/json_config_parser.cc b/src/bin/dhcp6/json_config_parser.cc index 89d9e3d1b2..c15c2ce7bc 100644 --- a/src/bin/dhcp6/json_config_parser.cc +++ b/src/bin/dhcp6/json_config_parser.cc @@ -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); } diff --git a/src/lib/dhcpsrv/parsers/client_class_def_parser.cc b/src/lib/dhcpsrv/parsers/client_class_def_parser.cc index a9a8c92dd6..7177a4b7c6 100644 --- a/src/lib/dhcpsrv/parsers/client_class_def_parser.cc +++ b/src/lib/dhcpsrv/parsers/client_class_def_parser.cc @@ -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. diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc index bcffcca7dc..ea8ad59ea3 100644 --- a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc +++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc @@ -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 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::max()) { + isc_throw(DhcpConfigError, "invalid option code '" << code64 + << "', it must not be greater than '" + << static_cast(std::numeric_limits::max()) + << "' (" << getPosition("code", option_def) << ")"); + } else if (address_family_ == AF_INET6 && + code64 > std::numeric_limits::max()) { + isc_throw(DhcpConfigError, "invalid option code '" << code64 + << "', it must not be greater than '" + << std::numeric_limits::max() + << "' (" << getPosition("code", option_def) << ")"); + } + uint32_t code = static_cast(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; diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.h b/src/lib/dhcpsrv/parsers/dhcp_parsers.h index cdba49fa98..8333c4b749 100644 --- a/src/lib/dhcpsrv/parsers/dhcp_parsers.h +++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.h @@ -228,6 +228,11 @@ typedef std::pair 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: diff --git a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc index 2c5b57bbed..949c7f7070 100644 --- a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc +++ b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc @@ -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\"" " } ]"