1510. [security] tmark
+ kea-dhcp4 Memfile logic now ensures during reading and writing
+ that leases which are not in the declined state, have either
+ a hardware address, client id, or both. kea-dhcp6 Memfile logic
+ now ensures leases which are not declined have a non-empty DUID.
+ CVE:2019-6474
+ (Gitlab #805,!12-p, git #TBD)
+
+1509. [security] tmark
Added a new parameter, "max-row-errors", to Memfile lease database
configuration for kea-dhcp4 and kea-dhcp6. This parameter can be
used to limit the number of rows discarded due to error during
CVE:2019-6473
(Gitlab #730,!7-p git 2361b6f769a322dfa3d61e956b6bc0d6882d9760)
-1507. [bug] tmark
+1507. [security] tmark
Added logic to kea-dhcp6 to catch values for client or
server DUIDs that exceed 128 bytes to inbound packet
sanity checking.
-// 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
string b_3 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,"
"100,150,7,0,0,,1,\n";
- // This one should be invalid, no hardware address and state is not declined
- string c_1 = "192.0.2.3,,a:11:01:04,"
+ // This one should be invalid, no hardware address or client id
+ // and state is not declined
+ string c_1 = "192.0.2.3,,,"
"200,200,8,1,1,host.example.com,0,\n";
string d_1 = "192.0.2.5,16:17:18:19:1a:bc,,"
-// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-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
CSVRow row(getColumnCount());
row.writeAt(getColumnIndex("address"), lease.addr_.toText());
- if (!lease.hwaddr_) {
+
+ if (((!lease.hwaddr_) || lease.hwaddr_->hwaddr_.empty()) &&
+ ((!lease.client_id_) || (lease.client_id_->getClientId().empty())) &&
+ (lease.state_ != Lease::STATE_DECLINED)) {
// Bump the error counter
++write_errs_;
- isc_throw(BadValue, "Lease4 must have hardware address specified.");
+ isc_throw(BadValue, "Lease4: " << lease.addr_.toText() << ", state: "
+ << Lease::basicStatesToText(lease.state_)
+ << " has neither hardware address or client id");
+
+ }
+
+ // Hardware addr may be unset (NULL).
+ if (lease.hwaddr_) {
+ row.writeAt(getColumnIndex("hwaddr"), lease.hwaddr_->toText(false));
}
- row.writeAt(getColumnIndex("hwaddr"), lease.hwaddr_->toText(false));
+
// Client id may be unset (NULL).
if (lease.client_id_) {
row.writeAt(getColumnIndex("client_id"), lease.client_id_->toText());
}
+
row.writeAt(getColumnIndex("valid_lifetime"), lease.valid_lft_);
row.writeAt(getColumnIndex("expire"), static_cast<uint64_t>(lease.cltt_ + lease.valid_lft_));
row.writeAt(getColumnIndex("subnet_id"), lease.subnet_id_);
return (true);
}
+ // Get the lease address.
+ IOAddress addr(readAddress(row));
+
// Get client id. It is possible that the client id is empty and the
// returned pointer is NULL. This is ok, but if the client id is NULL,
// we need to be careful to not use the NULL pointer.
// that.
HWAddr hwaddr = readHWAddr(row);
uint32_t state = readState(row);
- if (hwaddr.hwaddr_.empty() && state != Lease::STATE_DECLINED) {
- isc_throw(isc::BadValue, "A blank hardware address is only"
- " valid for declined leases");
+
+ if ((hwaddr.hwaddr_.empty()) && (client_id_vec.empty()) &&
+ (state != Lease::STATE_DECLINED)) {
+ isc_throw(BadValue, "Lease4: " << addr.toText() << ", state: "
+ << Lease::basicStatesToText(state)
+ << " has neither hardware address or client id");
}
// Get the user context (can be NULL).
ConstElementPtr ctx = readContext(row);
- lease.reset(new Lease4(readAddress(row),
+ lease.reset(new Lease4(addr,
HWAddrPtr(new HWAddr(hwaddr)),
client_id_vec.empty() ? NULL : &client_id_vec[0],
client_id_len,
-// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-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
/// error.
///
/// @param lease Structure representing a DHCPv4 lease.
+ /// @throw BadValue if the lease has no hardware address, no client id and
+ /// is not in STATE_DECLINED.
void append(const Lease4& lease);
/// @brief Reads next lease from the CSV file.
/// message using @c CSVFile::setReadMsg and returns false. The error
/// string may be read using @c CSVFile::getReadMsg.
///
+ /// Treats rows without a hardware address or a client id when their
+ /// state is not STATE_DECLINED as an error.
+ ///
/// This function is exception safe.
///
/// @param [out] lease Pointer to the lease read from CSV file or
-// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-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
// Bump the number of write attempts
++writes_;
+ if (((!(lease.duid_)) || (*(lease.duid_) == DUID::EMPTY())) &&
+ (lease.state_ != Lease::STATE_DECLINED)) {
+ ++write_errs_;
+ isc_throw(BadValue, "Lease6: " << lease.addr_.toText() << ", state: "
+ << Lease::basicStatesToText(lease.state_) << ", has no DUID");
+ }
+
CSVRow row(getColumnCount());
row.writeAt(getColumnIndex("address"), lease.addr_.toText());
row.writeAt(getColumnIndex("duid"), lease.duid_->toText());
-// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-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
/// error.
///
/// @param lease Structure representing a DHCPv6 lease.
+ /// @throw BadValue if the lease to be written has an empty DUID and is
+ /// whose state is not STATE_DECLINED.
void append(const Lease6& lease);
/// @brief Reads next lease from the CSV file.
-// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-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
"fqdn_fwd,fqdn_rev,hostname,state,user_context\n"
"192.0.2.1,06:07:08:09:0a:bc,,200,200,8,1,1,"
"host.example.com,0,\n"
- "192.0.2.1,,a:11:01:04,200,200,8,1,1,host.example.com,0,\n"
- "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,100,100,7,"
- "0,0,,1,{ \"foobar\": true }\n");
+ "192.0.2.2,,,200,200,8,1,1,host.example.com,0,\n"
+ "192.0.2.3,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,100,100,7,"
+ "0,0,,1,{ \"foobar\": true }\n"
+ "192.0.2.4,,11:22:33:44:55:66,200,200,8,1,1,host.example.com,0,\n"
+ "192.0.2.5,,,200,200,8,1,1,,1,\n");
}
// This test checks the capability to read and parse leases from the file.
EXPECT_FALSE(lease->getContext());
}
- // Second lease is malformed - HW address is empty when state
+ // Second lease is malformed - has no HW address or client id and state
// is not declined.
{
SCOPED_TRACE("Second lease malformed");
EXPECT_FALSE(lf.next(lease));
+ EXPECT_FALSE(lease);
checkStats(lf, 2, 1, 1, 0, 0, 0);
}
checkStats(lf, 3, 2, 1, 0, 0, 0);
// Verify that the third lease is correct.
- EXPECT_EQ("192.0.3.15", lease->addr_.toText());
+ EXPECT_EQ("192.0.2.3", lease->addr_.toText());
HWAddr hwaddr3(*lease->hwaddr_);
EXPECT_EQ("dd:de:ba:0d:1b:2e:3e:4f", hwaddr3.toText(false));
ASSERT_TRUE(lease->client_id_);
EXPECT_EQ("{ \"foobar\": true }", lease->getContext()->str());
}
+ // Fourth lease has no hardware address but has client id
+ {
+ SCOPED_TRACE("Fourth lease valid");
+ EXPECT_TRUE(lf.next(lease));
+ ASSERT_TRUE(lease);
+ checkStats(lf, 4, 3, 1, 0, 0, 0);
+
+ EXPECT_EQ("192.0.2.4", lease->addr_.toText());
+ ASSERT_TRUE(lease->hwaddr_);
+ EXPECT_TRUE(lease->hwaddr_->hwaddr_.empty());
+ ASSERT_TRUE(lease->client_id_);
+ EXPECT_EQ("11:22:33:44:55:66", lease->client_id_->toText());
+ }
+
+ // Fifth lease has no hardware address or client id but is declined
+ {
+ SCOPED_TRACE("Fifth lease valid");
+ EXPECT_TRUE(lf.next(lease));
+ ASSERT_TRUE(lease);
+ checkStats(lf, 5, 4, 1, 0, 0, 0);
+
+ EXPECT_EQ("192.0.2.5", lease->addr_.toText());
+ ASSERT_TRUE(lease->hwaddr_);
+ EXPECT_TRUE(lease->hwaddr_->hwaddr_.empty());
+ ASSERT_FALSE(lease->client_id_);
+ EXPECT_EQ(lease->state_, Lease::STATE_DECLINED);
+ }
+
// There are no more leases. Reading should cause no error, but the returned
// lease pointer should be NULL.
{
- SCOPED_TRACE("Fifth read empty");
+ SCOPED_TRACE("Sixth read empty");
EXPECT_TRUE(lf.next(lease));
EXPECT_FALSE(lease);
- checkStats(lf, 4, 2, 1, 0, 0, 0);
+ checkStats(lf, 6, 4, 1, 0, 0, 0);
}
// We should be able to do it again.
{
- SCOPED_TRACE("Sixth read empty");
+ SCOPED_TRACE("Seventh read empty");
EXPECT_TRUE(lf.next(lease));
EXPECT_FALSE(lease);
- checkStats(lf, 5, 2, 1, 0, 0, 0);
+ checkStats(lf, 7, 4, 1, 0, 0, 0);
}
}
EXPECT_EQ(lease->cltt_, lease_read->cltt_);
}
+// Verifies that it is not possible to output a lease with empty hwaddr in other
+// than the declined state
+TEST_F(CSVLeaseFile4Test, emptyHWAddrDefaultStateOnly) {
+ CSVLeaseFile4 lf(filename_);
+ ASSERT_NO_THROW(lf.recreate());
+ ASSERT_TRUE(io_.exists());
+
+ HWAddrPtr hwaddr;
+
+ // Create lease with null hwaddr and default state
+ Lease4Ptr lease_null_hwaddr(new Lease4(IOAddress("192.0.3.2"),
+ hwaddr,
+ NULL, 0,
+ 0xFFFFFFFF, time(0),
+ 8, true, true,
+ "host.example.com"));
+ // Try to write this lease out to the lease file.
+ ASSERT_THROW(lf.append(*lease_null_hwaddr), BadValue);
+
+ hwaddr.reset(new HWAddr());
+
+ // Create lease with empty hwaddr and default state
+ Lease4Ptr lease_empty_hwaddr(new Lease4(IOAddress("192.0.3.2"),
+ hwaddr,
+ NULL, 0,
+ 0xFFFFFFFF, time(0),
+ 8, true, true,
+ "host.example.com"));
+ // Try to write this lease out to the lease file.
+ ASSERT_THROW(lf.append(*lease_empty_hwaddr), BadValue);
+
+ // Create lease with hwaddr and current time.
+ Lease4Ptr lease(new Lease4(IOAddress("192.0.3.2"),
+ hwaddr0_,
+ NULL, 0,
+ 0xFFFFFFFF, time(0),
+ 8, true, true,
+ "host.example.com"));
+
+ // Decline the lease
+ lease->decline(1000);
+ ASSERT_TRUE(lease->hwaddr_);
+ EXPECT_EQ(lease->hwaddr_->toText(false), "");
+
+ // Write this lease out to the lease file.
+ ASSERT_NO_THROW(lf.append(*lease));
+
+ // Close the lease file.
+ lf.close();
+
+ Lease4Ptr lease_read;
+
+ // Re-open the file for reading.
+ ASSERT_NO_THROW(lf.open());
+
+ // Read the lease and make sure it is successful.
+ EXPECT_TRUE(lf.next(lease_read));
+ ASSERT_TRUE(lease_read);
+
+ // The valid lifetime and the cltt should match with the original lease.
+ EXPECT_EQ(lease->valid_lft_, lease_read->valid_lft_);
+ EXPECT_EQ(lease->cltt_, lease_read->cltt_);
+}
+
/// @todo Currently we don't check invalid lease attributes, such as invalid
/// lease type, invalid preferred lifetime vs valid lifetime etc. The Lease6
/// should be extended with the function that validates lease attributes. Once
-// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-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
"2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05,300,300,6,150,"
"0,8,0,0,0,,,1,\n"
"3000:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,0,200,8,0,2,"
- "16,64,0,0,,,1,{ \"foobar\": true }\n");
+ "16,64,0,0,,,1,{ \"foobar\": true }\n"
+ "2001:db8:1::2,00,200,200,8,100,0,7,0,1,1,host.example.com,,0,\n"
+ "2001:db8:1::3,00,200,200,8,100,0,7,0,1,1,host.example.com,,1,\n");
}
// This test checks the capability to read and parse leases from the file.
EXPECT_FALSE(lease->getContext());
}
- // Second lease is malformed - DUID is empty.
+ // Second lease is malformed - DUID is blank (i.e. ",,")
{
SCOPED_TRACE("Second lease malformed");
EXPECT_FALSE(lf.next(lease));
EXPECT_EQ("{ \"foobar\": true }", lease->getContext()->str());
}
+
+ // Fifth lease is invalid - DUID is empty, state is not DECLINED
+ {
+ SCOPED_TRACE("Fifth lease invalid");
+ EXPECT_FALSE(lf.next(lease));
+ checkStats(lf, 5, 3, 2, 0, 0, 0);
+ }
+
+ // Reading the sixth lease should be successful.
+ {
+ SCOPED_TRACE("sixth lease valid");
+ EXPECT_TRUE(lf.next(lease));
+ ASSERT_TRUE(lease);
+ checkStats(lf, 6, 4, 2, 0, 0, 0);
+
+ // Verify that the lease is correct.
+ EXPECT_EQ("2001:db8:1::3", lease->addr_.toText());
+ ASSERT_TRUE(lease->duid_);
+ EXPECT_EQ("00", lease->duid_->toText());
+ EXPECT_EQ(Lease::STATE_DECLINED, lease->state_);
+ }
+
// There are no more leases. Reading should cause no error, but the returned
// lease pointer should be NULL.
{
- SCOPED_TRACE("Fifth read empty");
+ SCOPED_TRACE("Sixth read empty");
EXPECT_TRUE(lf.next(lease));
EXPECT_FALSE(lease);
- checkStats(lf, 5, 3, 1, 0, 0, 0);
+ checkStats(lf, 7, 4, 2, 0, 0, 0);
}
// We should be able to do it again.
{
- SCOPED_TRACE("Sixth read empty");
+ SCOPED_TRACE("Seventh read empty");
EXPECT_TRUE(lf.next(lease));
EXPECT_FALSE(lease);
- checkStats(lf, 6, 3, 1, 0, 0, 0);
+ checkStats(lf, 8, 4, 2, 0, 0, 0);
}
}
checkStats(lf, 0, 0, 0, 3, 3, 0);
}
+ DuidPtr empty(new DUID(DUID::EMPTY()));
+ lease.reset(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:2::10"),
+ empty, 8, 150, 300, 0, 0, 6, false, false,
+ "", HWAddrPtr(), 128));
+ lease->cltt_ = 0;
+ {
+ SCOPED_TRACE("Fourth write - invalid, no DUID, not declined");
+ ASSERT_THROW(lf.append(*lease), BadValue);
+ checkStats(lf, 0, 0, 0, 4, 3, 1);
+ }
+
+ {
+ SCOPED_TRACE("Fifth write - valid, no DUID, declined");
+ lease->state_ = Lease::STATE_DECLINED;
+ ASSERT_NO_THROW(lf.append(*lease));
+ checkStats(lf, 0, 0, 0, 5, 4, 1);
+ }
+
+
EXPECT_EQ("address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,"
"lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,"
"state,user_context\n"
"2001:db8:2::10,01:01:01:01:0a:01:02:03:04:05"
",300,300,6,150,0,8,128,0,0,,,0,\n"
"3000:1:1::,00:01:02:03:04:05:06:0a:0b:0c:0d:0e:0f,"
- "300,300,10,150,2,7,64,0,0,,,0,{ \"foobar\": true }\n",
+ "300,300,10,150,2,7,64,0,0,,,0,{ \"foobar\": true }\n"
+ "2001:db8:2::10,00,300,300,6,150,0,8,128,0,0,,,1,\n",
io_.readFile());
}
}
// Verifies that leases with no DUID are invalid, and that leases
-// with the "Empty" DUID (1 byte duid = 0x0) are valid only when
+// with the "Empty" DUID (1 byte duid = 0x0) are valid only when
// in the declined state.
TEST_F(CSVLeaseFile6Test, declinedLeaseTest) {
io_.writeFile("address,duid,valid_lifetime,expire,subnet_id,"
-// Copyright (C) 2014-2018 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-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
} else if (address == straddress4_[7]) {
lease->hwaddr_.reset(new HWAddr(vector<uint8_t>(), HTYPE_ETHER)); // Empty
- lease->client_id_ = ClientIdPtr(); // Empty
+ lease->client_id_ = ClientIdPtr(
+ new ClientId(vector<uint8_t>(8, 0x77)));
lease->valid_lft_ = 7975;
lease->cltt_ = 213876;
lease->subnet_id_ = 19;
// Check that an empty vector is valid
EXPECT_TRUE(leases[7]->hwaddr_->hwaddr_.empty());
+ EXPECT_FALSE(leases[7]->client_id_->getClientId().empty());
returned = lmptr_->getLease4(*leases[7]->hwaddr_);
ASSERT_EQ(1, returned.size());
detailCompareLease(leases[7], *returned.begin());
ASSERT_EQ(1, returned.size());
detailCompareLease(leases[3], *returned.begin());
- // Check that client-id is NULL
- EXPECT_FALSE(leases[7]->client_id_);
- HWAddr tmp(*leases[7]->hwaddr_);
- returned = lmptr_->getLease4(tmp);
- ASSERT_EQ(1, returned.size());
- detailCompareLease(leases[7], *returned.begin());
-
// Try to get something with invalid client ID
const uint8_t invalid_data[] = {0, 0, 0};
ClientId invalid(invalid_data, sizeof(invalid_data));
std::string b_2 = "192.0.3.15,dd:de:ba:0d:1b:2e:3e:4f,0a:00:01:04,"
"100,135,7,0,0,,1,\n";
- std::string c_1 = "192.0.2.3,,a:11:01:04,"
+ std::string c_1 = "192.0.2.3,,,"
"200,200,8,1,1,host.example.com,0,\n";
// Create lease file with leases for 192.0.2.1, 192.0.3.15. The lease
- // entry for the 192.0.2.3 is invalid (lacks HW address) and should
- // be discarded.
+ // entry for the 192.0.2.3 is invalid (lacks HW address and client id)
+ // and should be discarded.
test_str = v4_hdr_ + a_1 + b_1 + c_1 + b_2 + a_2;
io_.writeFile(test_str);