mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-09-02 15:05:16 +00:00
[500-strengthen-option-def-parser] Added OptionDefParser code value sanity checks
This commit is contained in:
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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,7 +108,7 @@ 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;
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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\""
|
||||
" } ]"
|
||||
|
Reference in New Issue
Block a user