2
0
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:
Francis Dupont
2019-02-26 21:57:53 +01:00
parent 64067fb393
commit 85b4bef45c
6 changed files with 223 additions and 10 deletions

View 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

View 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);
}

View 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,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;

View 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;

View 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

View 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\""
" } ]"