Skip to content

Commit

Permalink
[5646] Dedicated checks for truncated client-id, server-id added
Browse files Browse the repository at this point in the history
  • Loading branch information
tomaszmrugalski committed Jun 11, 2018
1 parent 3ec666b commit a4545bb
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 1 deletion.
25 changes: 24 additions & 1 deletion src/bin/dhcp6/dhcp6_srv.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1260,18 +1260,23 @@ Dhcpv6Srv::sanityCheck(const Pkt6Ptr& pkt, RequirementLevel clientid,
RequirementLevel serverid) {
OptionCollection client_ids = pkt->getOptions(D6O_CLIENTID);
switch (clientid) {
case MANDATORY:
case MANDATORY: {
if (client_ids.size() != 1) {
isc_throw(RFCViolation, "Exactly 1 client-id option expected in "
<< pkt->getName() << ", but " << client_ids.size()
<< " received");
}
sanityCheckDUID(client_ids.begin()->second, "client-id");
break;
}
case OPTIONAL:
if (client_ids.size() > 1) {
isc_throw(RFCViolation, "Too many (" << client_ids.size()
<< ") client-id options received in " << pkt->getName());
}
if (!client_ids.empty()) {
sanityCheckDUID(client_ids.begin()->second, "client-id");
}
break;

case FORBIDDEN:
Expand All @@ -1294,13 +1299,31 @@ Dhcpv6Srv::sanityCheck(const Pkt6Ptr& pkt, RequirementLevel clientid,
<< server_ids.size() << "), exactly 1 expected in message "
<< pkt->getName());
}
sanityCheckDUID(server_ids.begin()->second, "server-id");
break;

case OPTIONAL:
if (server_ids.size() > 1) {
isc_throw(RFCViolation, "Too many (" << server_ids.size()
<< ") server-id options received in " << pkt->getName());
}
if (!server_ids.empty()) {
sanityCheckDUID(server_ids.begin()->second, "server-id");
}
}
}

void Dhcpv6Srv::sanityCheckDUID(const OptionPtr& opt, const std::string& opt_name) {
if (!opt) {
isc_throw(RFCViolation, "Unable to find expected option " << opt_name);
}

// The client-id or server-id has to have at least 3 bytes of useful data:
// two for duid type and one more for actual duid value.
uint16_t len = opt->len() - opt->getHeaderLen();
if (len < 3) {
isc_throw(RFCViolation, "Received empty or truncated " << opt_name << " option: "
<< len << " byte(s) only");
}
}

Expand Down
7 changes: 7 additions & 0 deletions src/bin/dhcp6/dhcp6_srv.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,13 @@ class Dhcpv6Srv : public Daemon {
void sanityCheck(const Pkt6Ptr& pkt, RequirementLevel clientid,
RequirementLevel serverid);

/// @brief verifies if received DUID option (client-id or server-id) is sane
///
/// @param opt option to be checked
/// @param opt_name text name to be printed
/// @throw RFCViolation if any issues are detected
void sanityCheckDUID(const OptionPtr& opt, const std::string& opt_name);

/// @brief Processes incoming Solicit and returns response.
///
/// Processes received Solicit message and verifies that its sender
Expand Down
50 changes: 50 additions & 0 deletions src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,56 @@ TEST_F(Dhcpv6SrvTest, sanityCheck) {
EXPECT_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::MANDATORY),
RFCViolation);
}

// This test verifies if the sanityCheck() really checks options content,
// especially truncated client-id.
TEST_F(Dhcpv6SrvTest, truncatedClientId) {
NakedDhcpv6Srv srv(0);

Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));

// Case 1: completely empty (size 0)
pkt->addOption(generateClientId(0));
EXPECT_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::FORBIDDEN),
RFCViolation);

// Case 2: too short (at the very least 3 bytes are needed)
pkt->delOption(D6O_CLIENTID);
pkt->addOption(generateClientId(2));
EXPECT_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::FORBIDDEN),
RFCViolation);

// Case 3: the shortest DUID possible (3 bytes) is ok:
pkt->delOption(D6O_CLIENTID);
pkt->addOption(generateClientId(3));
EXPECT_NO_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::MANDATORY, Dhcpv6Srv::FORBIDDEN));
}

// This test verifies if the sanityCheck() really checks options content,
// especially truncated server-id.
TEST_F(Dhcpv6SrvTest, truncatedServerId) {
NakedDhcpv6Srv srv(0);

Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));

// Case 1: completely empty (size 0)
pkt->addOption(generateServerId(0));
EXPECT_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::FORBIDDEN, Dhcpv6Srv::MANDATORY),
RFCViolation);

// Case 2: too short (at the very least 3 bytes are needed)
pkt->delOption(D6O_SERVERID);
pkt->addOption(generateServerId(2));
EXPECT_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::FORBIDDEN, Dhcpv6Srv::MANDATORY),
RFCViolation);

// Case 3: the shortest DUID possible (3 bytes) is ok:
pkt->delOption(D6O_SERVERID);
pkt->addOption(generateServerId(3));
EXPECT_NO_THROW(srv.sanityCheck(pkt, Dhcpv6Srv::FORBIDDEN, Dhcpv6Srv::MANDATORY));
}


// Check that the server is testing if server identifier received in the
// query, matches server identifier used by the server.
TEST_F(Dhcpv6SrvTest, testServerID) {
Expand Down
29 changes: 29 additions & 0 deletions src/bin/dhcp6/tests/dhcp6_test_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,12 @@ class NakedDhcpv6SrvTest : public BaseServerTest {
// Generate client-id option
isc::dhcp::OptionPtr generateClientId(size_t duid_size = 32) {

if (duid_size == 0) {
return (isc::dhcp::OptionPtr
(new isc::dhcp::Option(isc::dhcp::Option::V6, D6O_CLIENTID)));

}

isc::dhcp::OptionBuffer clnt_duid(duid_size);
for (size_t i = 0; i < duid_size; i++) {
clnt_duid[i] = 100 + i;
Expand All @@ -325,6 +331,29 @@ class NakedDhcpv6SrvTest : public BaseServerTest {
clnt_duid.begin() + duid_size)));
}

/// Generate server-id option
/// @param duid_size size of the duid
isc::dhcp::OptionPtr generateServerId(size_t duid_size = 32) {

if (duid_size == 0) {
return (isc::dhcp::OptionPtr
(new isc::dhcp::Option(isc::dhcp::Option::V6, D6O_SERVERID)));

}

isc::dhcp::OptionBuffer clnt_duid(duid_size);
for (size_t i = 0; i < duid_size; i++) {
clnt_duid[i] = 100 + i;
}

duid_ = isc::dhcp::DuidPtr(new isc::dhcp::DUID(clnt_duid));

return (isc::dhcp::OptionPtr
(new isc::dhcp::Option(isc::dhcp::Option::V6, D6O_SERVERID,
clnt_duid.begin(),
clnt_duid.begin() + duid_size)));
}

// Checks if server response (ADVERTISE or REPLY) includes proper
// server-id.
void checkServerId(const isc::dhcp::Pkt6Ptr& rsp,
Expand Down

0 comments on commit a4545bb

Please sign in to comment.