Commit d8276f10 authored by Francis Dupont's avatar Francis Dupont
Browse files

[550-authentication-key-to-text-method-miss-spelled] AuthKey is binary

parent 1aaae966
......@@ -1732,6 +1732,7 @@ AC_CONFIG_FILES([Makefile
src/share/database/scripts/pgsql/upgrade_3.2_to_3.3.sh
src/share/database/scripts/pgsql/upgrade_3.3_to_4.0.sh
src/share/database/scripts/pgsql/upgrade_4.0_to_5.0.sh
src/share/database/scripts/pgsql/upgrade_5.0_to_5.1.sh
src/share/database/scripts/pgsql/wipe_data.sh
src/share/yang/Makefile
src/share/yang/modules/Makefile
......
#!/bin/sh
# 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
......@@ -120,7 +120,7 @@ pgsql_lease_version_test() {
# Verify that kea-admin lease-version returns the correct version
version=$(${keaadmin} lease-version pgsql -u $db_user -p $db_password -n $db_name)
assert_str_eq "5.0" ${version} "Expected kea-admin to return %s, returned value was %s"
assert_str_eq "5.1" ${version} "Expected kea-admin to return %s, returned value was %s"
# Let's wipe the whole database
pgsql_wipe
......@@ -230,10 +230,10 @@ pgsql_upgrade_2_0_to_3_0() {
assert_eq 1 "$output" "lease_hwaddr_source does not contain entry for HWADDR_SOURCE_UNKNOWN. (record count %d, expected %d)"
}
pgsql_upgrade_3_0_to_5_0() {
# Verify upgraded schema reports version 5.0.
pgsql_upgrade_3_0_to_5_1() {
# Verify upgraded schema reports version 5.1.
version=$(${keaadmin} lease-version pgsql -u $db_user -p $db_password -n $db_name -d $db_scripts_dir)
assert_str_eq "5.0" ${version} "Expected kea-admin to return %s, returned value was %s"
assert_str_eq "5.1" ${version} "Expected kea-admin to return %s, returned value was %s"
# Added user_context to lease4
output=`pgsql_execute "select user_context from lease4;"`
......@@ -270,8 +270,8 @@ pgsql_upgrade_test() {
# Check 2.0 to 3.0 upgrade
pgsql_upgrade_2_0_to_3_0
# Check 3.0 to 5.0 upgrade
pgsql_upgrade_3_0_to_5_0
# Check 3.0 to 5.1 upgrade
pgsql_upgrade_3_0_to_5_1
# Let's wipe the whole database
pgsql_wipe
......
......@@ -1373,7 +1373,7 @@ CqlHostExchange::prepareExchange(const HostPtr& host,
host_ipv4_boot_file_name_ = host->getBootFileName();
// auth_key: varchar
auth_key_ = host->getKey().ToText();
auth_key_ = host->getKey().toText();
// hostname: text
hostname_ = host->getHostname();
......
// 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
......@@ -22,37 +22,53 @@ using namespace isc::asiolink;
namespace isc {
namespace dhcp {
AuthKey::AuthKey(const std::string key) {
AuthKey::AuthKey(const std::vector<uint8_t>& key) {
setAuthKey(key);
}
AuthKey::AuthKey(void) {
AuthKey::AuthKey(const std::string& key) {
setAuthKey(key);
}
AuthKey::AuthKey() {
authKey_ = AuthKey::getRandomKeyString();
}
std::string
std::vector<uint8_t>
AuthKey::getRandomKeyString() {
std::vector<uint8_t> rs = isc::cryptolink::random(AuthKey::KEY_LEN);
std::string result;
result.resize(rs.size());
memmove(&result[0], &rs[0], result.size());
return (result);
return (isc::cryptolink::random(AuthKey::KEY_LEN));
}
std::string
AuthKey::ToText() const {
// this will need enhancement if the stored container is not a string
return (authKey_);
std::string
AuthKey::toText() const {
if (authKey_.empty()) {
return ("");
}
return (util::encode::encodeHex(authKey_));
}
void
AuthKey::setAuthKey(const std::string& key) {
AuthKey::setAuthKey(const std::vector<uint8_t>& key) {
authKey_ = key;
if (authKey_.size() > AuthKey::KEY_LEN) {
authKey_.resize(AuthKey::KEY_LEN);
}
}
void
AuthKey::setAuthKey(const std::string& key) {
if (key.empty()) {
authKey_.clear();
}
try {
std::vector<uint8_t> bin;
util::encode::decodeHex(key, bin);
setAuthKey(bin);
} catch (const std::exception& ex) {
isc_throw(BadValue, "bad auth key: " << ex.what());
}
}
bool
AuthKey::operator==(const AuthKey& other) const {
return (authKey_ == other.authKey_);
......@@ -138,7 +154,7 @@ Host::Host(const uint8_t* identifier, const size_t identifier_len,
next_server_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()),
server_host_name_(server_host_name), boot_file_name_(boot_file_name),
host_id_(0), cfg_option4_(new CfgOption()),
cfg_option6_(new CfgOption()), negative_(false),
cfg_option6_(new CfgOption()), negative_(false),
key_(auth_key) {
// Initialize host identifier.
......@@ -575,8 +591,8 @@ Host::toElement6() const {
// Set auth key
//@todo: uncomment once storing in configuration file is enabled
//map->set("auth-key", Element::create(getKey().ToText()));
//map->set("auth-key", Element::create(getKey().toText()));
return (map);
}
......@@ -614,7 +630,7 @@ Host::toText() const {
// Add boot file name.
s << " file=" << (boot_file_name_.empty() ? "(empty)" : boot_file_name_);
s << " key=" << (key_.ToText().empty() ? "(empty)" : key_.ToText());
s << " key=" << (key_.toText().empty() ? "(empty)" : key_.toText());
if (ipv6_reservations_.empty()) {
s << " ipv6_reservations=(none)";
......
// 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
......@@ -27,72 +27,85 @@ namespace dhcp {
/// @brief HostID (used only when storing in MySQL, PostgreSQL or Cassandra)
typedef uint64_t HostID;
/// @brief Authentication keys
///
/// This class represents authentication keys to be used for
/// calculating HMAC in the authentication field of the recofigure message
/// @brief Authentication keys.
///
/// This class represents authentication keys to be used for
/// calculating HMAC in the authentication field of the recofigure message.
class AuthKey {
public:
/// @brief Length of the key - 128 bits
/// @brief Length of the key - 128 bits.
const static uint8_t KEY_LEN = 16;
/// @brief Constructor
/// @brief Constructor.
///
/// Constructor for assigning auth keys in host reservation.
/// Ensures the key length is not greater than 16 bytes.
/// @param key auth key to be stored
AuthKey(const std::string key);
/// @brief Constructor
/// @param key auth key in binary to be stored.
AuthKey(const std::vector<uint8_t>& key);
/// @brief Constructor.
///
/// Constructor for assigning auth keys in host reservation.
/// Ensures the key length is not greater than 16 bytes.
/// @param key auth key in hexadecimal to be stored.
AuthKey(const std::string& key);
/// @brief Constructor.
///
/// Constructor for generating auth keys, with no argument.
/// shall use the internal function for generationg random keys.
AuthKey();
// @brief get random string
// @brief Get random string.
///
/// Random string is generated by default will be used for
/// the keys to be used for signing Reconfigure Message.
/// @return auth keys
/// @todo Move randomization function to cryptolink
static std::string getRandomKeyString();
/// @return Random binary string of 16 bytes.
static std::vector<uint8_t> getRandomKeyString();
/// @brief set auth key value
/// @brief Set auth key value from binary.
///
/// Set the key value.
// If the size is greater than 16 bytes, we resize to 16 Bytes
/// Doesnt throw an exception
/// @param key auth key to be stored
// If the size is greater than 16 bytes, we resize to 16 bytes.
/// Doesnt throw an exception.
/// @param key auth key in binary to be stored
void setAuthKey(const std::vector<uint8_t>& key);
/// @brief Set auth key value from hexadecimal.
///
/// Set the key value.
/// If the size is greater than 16 bytes, we resize to 16 bytes.
/// @param key auth key in hexadecimal to be stored.
/// @throw BadValue if the string is not a valid hexadecimal encoding.
void setAuthKey(const std::string& key);
/// @brief return auth key
/// @brief Return auth key.
///
/// @return auth key
std::string getAuthKey() {
/// @return auth key in binary.
const std::vector<uint8_t>& getAuthKey() const {
return authKey_;
}
/// @brief return text format for keys
/// @brief Return text format for keys.
///
/// Although returning member would have sufficed
/// this is added incase in future authkey is no longer std::string
std::string ToText() const;
/// @return auth key in hexadecimal.
std::string toText() const;
///
/// @brief equality operator
///
/// equality operator to compare two AuthKey classes
/// @param other Authkey to be compared against
/// @brief Equality operator.
///
/// equality operator to compare two AuthKey objects.
/// @param other Authkey to be compared against.
bool operator==(const AuthKey& other) const;
/// @brief inequality operator
/// @brief Inequality operator.
///
/// inequality operator to compare two AuthKey classes
/// @param other Authkey to be compared against
/// inequality operator to compare two AuthKey objects.
/// @param other Authkey to be compared against.
bool operator!=(const AuthKey& other) const;
private:
std::string authKey_;
std::vector<uint8_t> authKey_;
};
/// @brief IPv6 reservation for a host.
......@@ -641,14 +654,14 @@ public:
///
/// @return Element representation of the host
isc::data::ElementPtr toElement6() const;
/// @brief sets key.
///
/// Keys are used for signing the Reconfigure Message.
void setKey(const AuthKey& key) {
key_ = key;
}
/// @brief Returns the key.
///
/// Keys are used for signing the Reconfigure Message.
......
......@@ -73,8 +73,8 @@ const size_t SERVER_HOSTNAME_MAX_LEN = 64;
/// @brief Maximum length of the boot file name.
const size_t BOOT_FILE_NAME_MAX_LEN = 128;
/// @brief Maximum length of keys.
const size_t KEY_LEN = 16;
/// @brief Maximum length of keys (coded in hexadecimal).
const size_t KEY_LEN = 16 * 2;
/// @brief Numeric value representing last supported identifier.
///
......@@ -406,7 +406,7 @@ public:
// auth key
bind_[13].buffer_type = MYSQL_TYPE_STRING;
std::string auth_key = host->getKey().ToText();
std::string auth_key = host->getKey().toText();
std::strncpy(auth_key_, auth_key.c_str(), KEY_LEN);
auth_key_null_ = auth_key.empty() ? MLM_TRUE : MLM_FALSE;
bind_[13].buffer = auth_key_;
......
......@@ -256,7 +256,7 @@ public:
bind_array->add(host->getBootFileName());
// add auth keys
std::string key = host->getKey().ToText();
std::string key = host->getKey().toText();
if (key.empty()) {
bind_array->addNull();
} else {
......
// 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
......@@ -13,8 +13,9 @@
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
#include <cstdlib>
#include <unordered_map>
#include <unordered_set>
#include <sstream>
#include <boost/functional/hash.hpp>
using namespace isc;
using namespace isc::dhcp;
......@@ -199,7 +200,7 @@ TEST_F(HostTest, createFromHWAddrString) {
std::string(), std::string(),
IOAddress("192.0.0.2"),
"server-hostname.example.org",
"bootfile.efi", AuthKey("key123"))));
"bootfile.efi", AuthKey("12345678"))));
// The HW address should be set to non-null.
HWAddrPtr hwaddr = host->getHWAddress();
ASSERT_TRUE(hwaddr);
......@@ -215,7 +216,7 @@ TEST_F(HostTest, createFromHWAddrString) {
EXPECT_EQ("192.0.0.2", host->getNextServer().toText());
EXPECT_EQ("server-hostname.example.org", host->getServerHostname());
EXPECT_EQ("bootfile.efi", host->getBootFileName());
EXPECT_EQ("key123", host->getKey().ToText());
EXPECT_EQ("12345678", host->getKey().toText());
EXPECT_FALSE(host->getContext());
// Use invalid identifier name
......@@ -281,7 +282,7 @@ TEST_F(HostTest, createFromHWAddrBinary) {
std::string(), std::string(),
IOAddress("192.0.0.2"),
"server-hostname.example.org",
"bootfile.efi", AuthKey("keyabc"))));
"bootfile.efi", AuthKey("0abc1234"))));
// Hardware address should be non-null.
HWAddrPtr hwaddr = host->getHWAddress();
......@@ -298,7 +299,7 @@ TEST_F(HostTest, createFromHWAddrBinary) {
EXPECT_EQ("192.0.0.2", host->getNextServer().toText());
EXPECT_EQ("server-hostname.example.org", host->getServerHostname());
EXPECT_EQ("bootfile.efi", host->getBootFileName());
EXPECT_EQ("keyabc", host->getKey().ToText());
EXPECT_EQ("0ABC1234", host->getKey().toText());
EXPECT_FALSE(host->getContext());
}
......@@ -680,7 +681,8 @@ TEST_F(HostTest, setValues) {
host->setNextServer(IOAddress("192.0.2.2"));
host->setServerHostname("server-hostname.example.org");
host->setBootFileName("bootfile.efi");
host->setKey(AuthKey("random-value"));
const std::vector<uint8_t>& random_value(AuthKey::getRandomKeyString());
host->setKey(AuthKey(random_value));
std::string user_context = "{ \"foo\": \"bar\" }";
host->setContext(Element::fromJSON(user_context));
host->setNegative(true);
......@@ -692,7 +694,7 @@ TEST_F(HostTest, setValues) {
EXPECT_EQ("192.0.2.2", host->getNextServer().toText());
EXPECT_EQ("server-hostname.example.org", host->getServerHostname());
EXPECT_EQ("bootfile.efi", host->getBootFileName());
EXPECT_EQ("random-value", host->getKey().ToText());
EXPECT_EQ(random_value, host->getKey().getAuthKey());
ASSERT_TRUE(host->getContext());
EXPECT_EQ(user_context, host->getContext()->str());
EXPECT_TRUE(host->getNegative());
......@@ -1047,7 +1049,7 @@ TEST_F(HostTest, toText) {
// Create global host identified by DUID, with a very basic configuration.
ASSERT_NO_THROW(host.reset(new Host("11:12:13:14:15", "duid",
SUBNET_ID_GLOBAL, SUBNET_ID_GLOBAL,
SUBNET_ID_GLOBAL, SUBNET_ID_GLOBAL,
IOAddress::IPV4_ZERO_ADDRESS(),
"myhost")));
......@@ -1141,7 +1143,7 @@ TEST_F(HostTest, unparse) {
// Create host identified by DUID, instead of HWADDR, with a very
// basic configuration.
ASSERT_NO_THROW(host.reset(new Host("11:12:13:14:15", "duid",
SUBNET_ID_UNUSED, SUBNET_ID_UNUSED,
SUBNET_ID_UNUSED, SUBNET_ID_UNUSED,
IOAddress::IPV4_ZERO_ADDRESS(),
"myhost")));
......@@ -1240,65 +1242,67 @@ TEST_F(HostTest, keys) {
SubnetID(1), SubnetID(2),
IOAddress("192.0.2.3"),
"myhost.example.com")));
//Key must be empty
EXPECT_EQ(0,host->getKey().ToText().length());
//now set to random value
host->setKey(AuthKey("random_key"));
EXPECT_EQ("random_key", host->getKey().ToText());
// Key must be empty
EXPECT_EQ(0, host->getKey().getAuthKey().size());
EXPECT_EQ("", host->getKey().toText());
// now set to random value
const std::vector<uint8_t>& random_key(AuthKey::getRandomKeyString());
host->setKey(AuthKey(random_key));
EXPECT_EQ(random_key, host->getKey().getAuthKey());
}
// Test verifies if getRandomKeyString can generate 1000 keys which are random
// Test verifies if getRandomKeyString can generate 1000 keys which are random
TEST_F(HostTest, randomKeys) {
//use hashtable and set size to 1000
std::unordered_map<std::string, int> key_map;
// use hashtable and set size to 1000
std::unordered_set<std::vector<uint8_t>,
boost::hash<std::vector<uint8_t>>> keys;
int dup_element = 0;
const uint16_t max_iter = 1000;
uint16_t iter_num = 0;
size_t max_hash_size = 1000;
key_map.reserve(max_hash_size);
keys.reserve(max_hash_size);
for (iter_num = 0; iter_num < max_iter; iter_num++) {
std::string key = AuthKey::getRandomKeyString();
if (key_map[key]) {
std::vector<uint8_t> key = AuthKey::getRandomKeyString();
if (keys.count(key)) {
dup_element++;
break;
}
key_map[key] = 1;
keys.insert(key);
}
EXPECT_EQ(0, dup_element);
}
//Test performs basic functionality test of the AuthKey class
// Test performs basic functionality test of the AuthKey class
TEST(AuthKeyTest, basicTest) {
//call the constructor with default argument
// Call the constructor with default argument
// Default constructor should generate random string of 16 bytes
AuthKey defaultKey;
ASSERT_EQ(16, defaultKey.getAuthKey().size());
AuthKey longKey("someRandomStringGreaterThan16Bytes");
AuthKey longKey("0123456789abcdef1122334455667788");
ASSERT_EQ(16, longKey.getAuthKey().size());
//check the setters for valid and invalid string
std::string key16ByteStr = "0123456789abcdef";
// Check the setters for valid and invalid string
std::string key16ByteStr = "000102030405060708090A0B0C0D0E0F";
std::vector<uint8_t> key16ByteBin = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf
};
std::string key18ByteStr = "0123456789abcdefgh";
AuthKey defaultTestKey;
defaultTestKey.setAuthKey(key16ByteStr);
ASSERT_EQ(16, defaultTestKey.getAuthKey().size());
ASSERT_EQ(key16ByteStr, defaultTestKey.getAuthKey());
ASSERT_EQ(key16ByteStr, defaultTestKey.ToText());
defaultTestKey.setAuthKey(key18ByteStr);
ASSERT_EQ(16, defaultTestKey.getAuthKey().size());
ASSERT_EQ(key16ByteStr, defaultTestKey.getAuthKey());
ASSERT_EQ(key16ByteStr, defaultTestKey.ToText());
ASSERT_EQ(key16ByteStr, defaultTestKey.toText());
ASSERT_EQ(key16ByteBin, defaultTestKey.getAuthKey());
ASSERT_THROW(defaultTestKey.setAuthKey(key18ByteStr), BadValue);
}
} // end of anonymous namespace
......@@ -17,9 +17,9 @@
namespace isc {
namespace db {
/// @brief Define PostgreSQL backend version: 5.0
/// @brief Define PostgreSQL backend version: 5.1
const uint32_t PG_SCHEMA_VERSION_MAJOR = 5;
const uint32_t PG_SCHEMA_VERSION_MINOR = 0;
const uint32_t PG_SCHEMA_VERSION_MINOR = 1;
// Maximum number of parameters that can be used a statement
// @todo This allows us to use an initializer list (since we can't
......
......@@ -2367,6 +2367,10 @@ CREATE TRIGGER dhcp6_server_ADEL AFTER DELETE ON dhcp6_server
END $$
DELIMITER ;
# Put the auth key in hexadecimal (double size but far more user friendly).
ALTER TABLE hosts
MODIFY COLUMN auth_key VARCHAR(32) NULL;
# Update the schema version number
UPDATE schema_version
SET version = '8', minor = '1';
......
......@@ -93,6 +93,10 @@ CREATE TRIGGER dhcp6_server_ADEL AFTER DELETE ON dhcp6_server
END $$
DELIMITER ;
# Put the auth key in hexadecimal (double size but far more user friendly).
ALTER TABLE hosts
MODIFY COLUMN auth_key VARCHAR(32) NULL;
# Update the schema version number
UPDATE schema_version
SET version = '8', minor = '1';
......
......@@ -5,4 +5,5 @@
/upgrade_3.2_to_3.3.sh
/upgrade_3.3_to_4.0.sh
/upgrade_4.0_to_5.0.sh
/upgrade_5.0_to_5.1.sh
/wipe_data.sh
......@@ -10,6 +10,7 @@ sqlscripts_DATA += upgrade_3.1_to_3.2.sh
sqlscripts_DATA += upgrade_3.2_to_3.3.sh
sqlscripts_DATA += upgrade_3.3_to_4.0.sh
sqlscripts_DATA += upgrade_4.0_to_5.0.sh
sqlscripts_DATA += upgrade_5.0_to_5.1.sh
sqlscripts_DATA += wipe_data.sh
DISTCLEANFILES = upgrade_1.0_to_2.0.sh
......@@ -19,6 +20,7 @@ DISTCLEANFILES += upgrade_3.1_to_3.2.sh
DISTCLEANFILES += upgrade_3.2_to_3.3.sh
DISTCLEANFILES += upgrade_3.3_to_4.0.sh
DISTCLEANFILES += upgrade_4.0_to_5.0.sh
DISTCLEANFILES += upgrade_5.0_to_5.1.sh
DISTCLEANFILES += wipe_data.sh
EXTRA_DIST = ${sqlscripts_DATA}
-- Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
-- Copyright (C) 2012-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
......@@ -876,6 +876,17 @@ UPDATE schema_version
-- Schema 5.0 specification ends here.
-- Upgrade to schema 5.1 begins here:
-- Put the auth key in hexadecimal (double size but far more user friendly).
ALTER TABLE hosts ALTER COLUMN auth_key TYPE VARCHAR(32);
-- Set schema 5.1 version
UPDATE schema_version
SET version = '5', minor = '1';
-- Schema 5.1 specification ends here.
-- Commit the script transaction.
COMMIT;
......
#!/bin/sh
prefix=@prefix@
# Include utilities. Use installed version if available and
# use build version if it isn't.
if [ -e @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh ]; then
. @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh
else
. @abs_top_builddir@/src/bin/admin/admin-utils.sh
fi
VERSION=`pgsql_version "$@"`
if [ "$VERSION" != "5.0" ]; then
printf "This script upgrades 5.0 to 5.1. Reported version is $VERSION. Skipping upgrade.\n"
exit 0
fi
psql "$@" >/dev/null <<EOF