mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-31 14:05:33 +00:00
[2723] The code now gracefully refuses too short DUIDs and client-ids
This commit is contained in:
@@ -28,15 +28,20 @@ namespace dhcp {
|
||||
DUID::DUID(const std::vector<uint8_t>& duid) {
|
||||
if (duid.size() > MAX_DUID_LEN) {
|
||||
isc_throw(OutOfRange, "DUID too large");
|
||||
} else {
|
||||
duid_ = duid;
|
||||
}
|
||||
if (duid.empty()) {
|
||||
isc_throw(OutOfRange, "Empty DUIDs are not allowed");
|
||||
}
|
||||
duid_ = duid;
|
||||
}
|
||||
|
||||
DUID::DUID(const uint8_t* data, size_t len) {
|
||||
if (len > MAX_DUID_LEN) {
|
||||
isc_throw(OutOfRange, "DUID too large");
|
||||
}
|
||||
if (len == 0) {
|
||||
isc_throw(OutOfRange, "Empty DUIDs/Client-ids not allowed");
|
||||
}
|
||||
|
||||
duid_ = std::vector<uint8_t>(data, data + len);
|
||||
}
|
||||
@@ -83,11 +88,19 @@ bool DUID::operator!=(const DUID& other) const {
|
||||
// Constructor based on vector<uint8_t>
|
||||
ClientId::ClientId(const std::vector<uint8_t>& clientid)
|
||||
: DUID(clientid) {
|
||||
if (clientid.size() < MIN_CLIENT_ID_LEN) {
|
||||
isc_throw(OutOfRange, "client-id is too short (" << clientid.size()
|
||||
<< "), at least 2 is required");
|
||||
}
|
||||
}
|
||||
|
||||
// Constructor based on C-style data
|
||||
ClientId::ClientId(const uint8_t *clientid, size_t len)
|
||||
: DUID(clientid, len) {
|
||||
if (len < MIN_CLIENT_ID_LEN) {
|
||||
isc_throw(OutOfRange, "client-id is too short (" << len
|
||||
<< "), at least 2 is required");
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a copy of client-id data
|
||||
|
@@ -35,6 +35,11 @@ class DUID {
|
||||
/// As defined in RFC3315, section 9.1
|
||||
static const size_t MAX_DUID_LEN = 128;
|
||||
|
||||
/// @brief minimum duid size
|
||||
/// There's no explicit minimal DUID size specified in RFC3315,
|
||||
/// so let's use absolute minimum
|
||||
static const size_t MIN_DUID_LEN = 1;
|
||||
|
||||
/// @brief specifies DUID type
|
||||
typedef enum {
|
||||
DUID_UNKNOWN = 0, ///< invalid/unknown type
|
||||
@@ -88,6 +93,13 @@ typedef boost::shared_ptr<DUID> DuidPtr;
|
||||
/// a client-id
|
||||
class ClientId : public DUID {
|
||||
public:
|
||||
|
||||
/// @brief Minimum size of a client ID
|
||||
///
|
||||
/// Excerpt from RFC2132, section 9.14.
|
||||
/// The code for this option is 61, and its minimum length is 2.
|
||||
static const size_t MIN_CLIENT_ID_LEN = 2;
|
||||
|
||||
/// @brief Maximum size of a client ID
|
||||
///
|
||||
/// This is the same as the maximum size of the underlying DUID.
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2011-2013 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -37,6 +37,15 @@ using boost::scoped_ptr;
|
||||
|
||||
namespace {
|
||||
|
||||
// This is a workaround for strange linking problems with gtest:
|
||||
// libdhcp___unittests-duid_unittest.o: In function `Compare<long unsigned int, long unsigned int>':
|
||||
// ~/gtest-1.6.0/include/gtest/gtest.h:1353: undefined reference to `isc::dhcp::ClientId::MAX_CLIENT_ID_LE'N
|
||||
// collect2: ld returned 1 exit status
|
||||
|
||||
const size_t MAX_DUID_LEN = DUID::MAX_DUID_LEN;
|
||||
const size_t MAX_CLIENT_ID_LEN = DUID::MAX_DUID_LEN;
|
||||
|
||||
|
||||
// This test verifies if the constructors are working as expected
|
||||
// and process passed parameters.
|
||||
TEST(DuidTest, constructor) {
|
||||
@@ -61,21 +70,20 @@ TEST(DuidTest, constructor) {
|
||||
// This test verifies if DUID size restrictions are implemented
|
||||
// properly.
|
||||
TEST(DuidTest, size) {
|
||||
const int MAX_DUID_SIZE = 128;
|
||||
uint8_t data[MAX_DUID_SIZE + 1];
|
||||
uint8_t data[MAX_DUID_LEN + 1];
|
||||
vector<uint8_t> data2;
|
||||
for (uint8_t i = 0; i < MAX_DUID_SIZE + 1; ++i) {
|
||||
for (uint8_t i = 0; i < MAX_DUID_LEN + 1; ++i) {
|
||||
data[i] = i;
|
||||
if (i < MAX_DUID_SIZE)
|
||||
if (i < MAX_DUID_LEN)
|
||||
data2.push_back(i);
|
||||
}
|
||||
ASSERT_EQ(data2.size(), MAX_DUID_SIZE);
|
||||
ASSERT_EQ(data2.size(), MAX_DUID_LEN);
|
||||
|
||||
scoped_ptr<DUID> duidmaxsize1(new DUID(data, MAX_DUID_SIZE));
|
||||
scoped_ptr<DUID> duidmaxsize1(new DUID(data, MAX_DUID_LEN));
|
||||
scoped_ptr<DUID> duidmaxsize2(new DUID(data2));
|
||||
|
||||
EXPECT_THROW(
|
||||
scoped_ptr<DUID> toolarge1(new DUID(data, MAX_DUID_SIZE + 1)),
|
||||
scoped_ptr<DUID> toolarge1(new DUID(data, MAX_DUID_LEN + 1)),
|
||||
OutOfRange);
|
||||
|
||||
// that's one too much
|
||||
@@ -84,6 +92,16 @@ TEST(DuidTest, size) {
|
||||
EXPECT_THROW(
|
||||
scoped_ptr<DUID> toolarge2(new DUID(data2)),
|
||||
OutOfRange);
|
||||
|
||||
// empty duids are not allowed
|
||||
vector<uint8_t> empty;
|
||||
EXPECT_THROW(
|
||||
scoped_ptr<DUID> emptyDuid(new DUID(empty)),
|
||||
OutOfRange);
|
||||
|
||||
EXPECT_THROW(
|
||||
scoped_ptr<DUID> emptyDuid2(new DUID(data, 0)),
|
||||
OutOfRange);
|
||||
}
|
||||
|
||||
// This test verifies if the implementation supports all defined
|
||||
@@ -157,6 +175,51 @@ TEST(ClientIdTest, constructor) {
|
||||
EXPECT_TRUE(data2 == vecdata);
|
||||
}
|
||||
|
||||
// Check that client-id sizes are reasonable
|
||||
TEST(ClientIdTest, size) {
|
||||
uint8_t data[MAX_CLIENT_ID_LEN + 1];
|
||||
vector<uint8_t> data2;
|
||||
for (uint8_t i = 0; i < MAX_CLIENT_ID_LEN + 1; ++i) {
|
||||
data[i] = i;
|
||||
if (i < MAX_CLIENT_ID_LEN)
|
||||
data2.push_back(i);
|
||||
}
|
||||
ASSERT_EQ(data2.size(), MAX_CLIENT_ID_LEN);
|
||||
|
||||
scoped_ptr<ClientId> duidmaxsize1(new ClientId(data, MAX_CLIENT_ID_LEN));
|
||||
scoped_ptr<ClientId> duidmaxsize2(new ClientId(data2));
|
||||
|
||||
EXPECT_THROW(
|
||||
scoped_ptr<ClientId> toolarge1(new ClientId(data, MAX_CLIENT_ID_LEN + 1)),
|
||||
OutOfRange);
|
||||
|
||||
// that's one too much
|
||||
data2.push_back(128);
|
||||
|
||||
EXPECT_THROW(
|
||||
scoped_ptr<ClientId> toolarge2(new ClientId(data2)),
|
||||
OutOfRange);
|
||||
|
||||
// empty client-ids are not allowed
|
||||
vector<uint8_t> empty;
|
||||
EXPECT_THROW(
|
||||
scoped_ptr<ClientId> empty_client_id1(new ClientId(empty)),
|
||||
OutOfRange);
|
||||
|
||||
EXPECT_THROW(
|
||||
scoped_ptr<ClientId> empty_client_id2(new ClientId(data, 0)),
|
||||
OutOfRange);
|
||||
|
||||
// client-id must be at least 2 bytes long
|
||||
vector<uint8_t> shorty(1,17); // just a single byte with value 17
|
||||
EXPECT_THROW(
|
||||
scoped_ptr<ClientId> too_short_client_id1(new ClientId(shorty)),
|
||||
OutOfRange);
|
||||
EXPECT_THROW(
|
||||
scoped_ptr<ClientId> too_short_client_id1(new ClientId(data, 1)),
|
||||
OutOfRange);
|
||||
}
|
||||
|
||||
// This test checks if the comparison operators are sane.
|
||||
TEST(ClientIdTest, operators) {
|
||||
uint8_t data1[] = {0, 1, 2, 3, 4, 5, 6};
|
||||
|
@@ -177,6 +177,14 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
|
||||
isc_throw(InvalidOperation, "No allocator selected");
|
||||
}
|
||||
|
||||
if (!subnet) {
|
||||
isc_throw(InvalidOperation, "Subnet is required for allocation");
|
||||
}
|
||||
|
||||
if (!duid) {
|
||||
isc_throw(InvalidOperation, "DUID is mandatory for allocation");
|
||||
}
|
||||
|
||||
// check if there's existing lease for that subnet/duid/iaid combination.
|
||||
Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(*duid, iaid, subnet->getID());
|
||||
if (existing) {
|
||||
@@ -284,8 +292,16 @@ AllocEngine::allocateAddress4(const SubnetPtr& subnet,
|
||||
isc_throw(InvalidOperation, "No allocator selected");
|
||||
}
|
||||
|
||||
if (!subnet) {
|
||||
isc_throw(InvalidOperation, "Can't allocate IPv4 address without subnet");
|
||||
}
|
||||
|
||||
if (!hwaddr) {
|
||||
isc_throw(InvalidOperation, "HWAddr must be defined");
|
||||
}
|
||||
|
||||
// Check if there's existing lease for that subnet/clientid/hwaddr combination.
|
||||
Lease4Ptr existing = LeaseMgrFactory::instance().getLease4(hwaddr->hwaddr_, subnet->getID());
|
||||
Lease4Ptr existing = LeaseMgrFactory::instance().getLease4(*hwaddr, subnet->getID());
|
||||
if (existing) {
|
||||
// We have a lease already. This is a returning client, probably after
|
||||
// its reboot.
|
||||
|
@@ -160,7 +160,15 @@ public:
|
||||
EXPECT_EQ(subnet_->getT2(), lease->t2_);
|
||||
EXPECT_TRUE(false == lease->fqdn_fwd_);
|
||||
EXPECT_TRUE(false == lease->fqdn_rev_);
|
||||
EXPECT_TRUE(*lease->client_id_ == *clientid_);
|
||||
if (lease->client_id_ && !clientid_) {
|
||||
ADD_FAILURE() << "Lease4 has a client-id, while it should have none.";
|
||||
}
|
||||
if (!lease->client_id_ && clientid_) {
|
||||
ADD_FAILURE() << "Lease4 has no client-id, but it was expected to have one.";
|
||||
}
|
||||
if (lease->client_id_ && clientid_) {
|
||||
EXPECT_TRUE(*lease->client_id_ == *clientid_);
|
||||
}
|
||||
EXPECT_TRUE(lease->hwaddr_ == hwaddr_->hwaddr_);
|
||||
// @todo: check cltt
|
||||
}
|
||||
@@ -329,6 +337,24 @@ TEST_F(AllocEngine6Test, allocBogusHint6) {
|
||||
detailCompareLease(lease, from_mgr);
|
||||
}
|
||||
|
||||
// This test checks that NULL values are handled properly
|
||||
TEST_F(AllocEngine6Test, allocateAddress6Nulls) {
|
||||
boost::scoped_ptr<AllocEngine> engine;
|
||||
ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
|
||||
ASSERT_TRUE(engine);
|
||||
|
||||
// Allocations without subnet are not allowed
|
||||
Lease6Ptr lease = engine->allocateAddress6(Subnet6Ptr(), duid_, iaid_,
|
||||
IOAddress("::"), false);
|
||||
ASSERT_FALSE(lease);
|
||||
|
||||
// Allocations without DUID are not allowed either
|
||||
lease = engine->allocateAddress6(subnet_, DuidPtr(), iaid_,
|
||||
IOAddress("::"), false);
|
||||
ASSERT_FALSE(lease);
|
||||
}
|
||||
|
||||
|
||||
// This test verifies that the allocator picks addresses that belong to the
|
||||
// pool
|
||||
TEST_F(AllocEngine6Test, IterativeAllocator) {
|
||||
@@ -702,6 +728,42 @@ TEST_F(AllocEngine4Test, allocBogusHint4) {
|
||||
}
|
||||
|
||||
|
||||
// This test checks that NULL values are handled properly
|
||||
TEST_F(AllocEngine4Test, allocateAddress4Nulls) {
|
||||
boost::scoped_ptr<AllocEngine> engine;
|
||||
ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
|
||||
ASSERT_TRUE(engine);
|
||||
|
||||
// Allocations without subnet are not allowed
|
||||
Lease4Ptr lease = engine->allocateAddress4(SubnetPtr(), clientid_, hwaddr_,
|
||||
IOAddress("0.0.0.0"), false);
|
||||
EXPECT_FALSE(lease);
|
||||
|
||||
// Allocations without HW address are not allowed
|
||||
lease = engine->allocateAddress4(subnet_, clientid_, HWAddrPtr(),
|
||||
IOAddress("0.0.0.0"), false);
|
||||
EXPECT_FALSE(lease);
|
||||
|
||||
// Allocations without client-id are allowed
|
||||
clientid_ = ClientIdPtr();
|
||||
lease = engine->allocateAddress4(subnet_, ClientIdPtr(), hwaddr_,
|
||||
IOAddress("0.0.0.0"), false);
|
||||
// Check that we got a lease
|
||||
ASSERT_TRUE(lease);
|
||||
|
||||
// Do all checks on the lease
|
||||
checkLease4(lease);
|
||||
|
||||
// Check that the lease is indeed in LeaseMgr
|
||||
Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(lease->addr_);
|
||||
ASSERT_TRUE(from_mgr);
|
||||
|
||||
// Now check that the lease in LeaseMgr has the same parameters
|
||||
detailCompareLease(lease, from_mgr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This test verifies that the allocator picks addresses that belong to the
|
||||
// pool
|
||||
TEST_F(AllocEngine4Test, IterativeAllocator) {
|
||||
|
@@ -317,8 +317,7 @@ public:
|
||||
|
||||
} else if (address == straddress4_[7]) {
|
||||
lease->hwaddr_ = vector<uint8_t>(); // Empty
|
||||
lease->client_id_ = ClientIdPtr(
|
||||
new ClientId(vector<uint8_t>())); // Empty
|
||||
lease->client_id_ = ClientIdPtr(); // Empty
|
||||
lease->valid_lft_ = 7975;
|
||||
lease->cltt_ = 213876;
|
||||
lease->subnet_id_ = 19;
|
||||
@@ -1048,9 +1047,10 @@ TEST_F(MySqlLeaseMgrTest, getLease4ClientId) {
|
||||
ASSERT_EQ(1, returned.size());
|
||||
detailCompareLease(leases[3], *returned.begin());
|
||||
|
||||
// Check that an empty vector is valid
|
||||
EXPECT_TRUE(leases[7]->client_id_->getClientId().empty());
|
||||
returned = lmptr_->getLease4(leases[7]->hwaddr_);
|
||||
// Check that client-id is NULL
|
||||
EXPECT_FALSE(leases[7]->client_id_);
|
||||
HWAddr tmp(leases[7]->hwaddr_, HTYPE_ETHER);
|
||||
returned = lmptr_->getLease4(tmp);
|
||||
ASSERT_EQ(1, returned.size());
|
||||
detailCompareLease(leases[7], *returned.begin());
|
||||
|
||||
@@ -1075,7 +1075,11 @@ TEST_F(MySqlLeaseMgrTest, getLease4ClientIdSize) {
|
||||
// ClientId::MAX_CLIENT_ID_LEN is used in an EXPECT_EQ.
|
||||
int client_id_max = ClientId::MAX_CLIENT_ID_LEN;
|
||||
EXPECT_EQ(128, client_id_max);
|
||||
for (uint8_t i = 0; i <= client_id_max; i += 16) {
|
||||
|
||||
int client_id_min = ClientId::MIN_CLIENT_ID_LEN;
|
||||
EXPECT_EQ(2, client_id_min); // See RFC2132, section 9.14
|
||||
|
||||
for (uint8_t i = client_id_min; i <= client_id_max; i += 16) {
|
||||
vector<uint8_t> clientid_vec(i, i);
|
||||
leases[1]->client_id_.reset(new ClientId(clientid_vec));
|
||||
EXPECT_TRUE(lmptr_->addLease(leases[1]));
|
||||
@@ -1184,7 +1188,11 @@ TEST_F(MySqlLeaseMgrTest, getLease6DuidIaidSize) {
|
||||
// For speed, go from 0 to 128 is steps of 16.
|
||||
int duid_max = DUID::MAX_DUID_LEN;
|
||||
EXPECT_EQ(128, duid_max);
|
||||
for (uint8_t i = 0; i <= duid_max; i += 16) {
|
||||
|
||||
int duid_min = DUID::MIN_DUID_LEN;
|
||||
EXPECT_EQ(1, duid_min);
|
||||
|
||||
for (uint8_t i = duid_min; i <= duid_max; i += 16) {
|
||||
vector<uint8_t> duid_vec(i, i);
|
||||
leases[1]->duid_.reset(new DUID(duid_vec));
|
||||
EXPECT_TRUE(lmptr_->addLease(leases[1]));
|
||||
@@ -1249,7 +1257,11 @@ TEST_F(MySqlLeaseMgrTest, getLease6DuidIaidSubnetIdSize) {
|
||||
// For speed, go from 0 to 128 is steps of 16.
|
||||
int duid_max = DUID::MAX_DUID_LEN;
|
||||
EXPECT_EQ(128, duid_max);
|
||||
for (uint8_t i = 0; i <= duid_max; i += 16) {
|
||||
|
||||
int duid_min = DUID::MIN_DUID_LEN;
|
||||
EXPECT_EQ(1, duid_min);
|
||||
|
||||
for (uint8_t i = duid_min; i <= duid_max; i += 16) {
|
||||
vector<uint8_t> duid_vec(i, i);
|
||||
leases[1]->duid_.reset(new DUID(duid_vec));
|
||||
EXPECT_TRUE(lmptr_->addLease(leases[1]));
|
||||
|
Reference in New Issue
Block a user