From: mayya Date: Mon, 18 Jun 2018 13:46:51 +0000 (+0200) Subject: lib: implements authenitication option required for reconfigure message X-Git-Tag: trac5694_base~2^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5b6bf06c899e051b36d134a53862b35323dfafe2;p=thirdparty%2Fkea.git lib: implements authenitication option required for reconfigure message See #86 --- diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am index e27d06ab44..2b6412cc89 100644 --- a/src/lib/dhcp/Makefile.am +++ b/src/lib/dhcp/Makefile.am @@ -24,6 +24,7 @@ libkea_dhcp___la_SOURCES += opaque_data_tuple.cc opaque_data_tuple.h libkea_dhcp___la_SOURCES += option4_addrlst.cc option4_addrlst.h libkea_dhcp___la_SOURCES += option4_client_fqdn.cc option4_client_fqdn.h libkea_dhcp___la_SOURCES += option6_addrlst.cc option6_addrlst.h +libkea_dhcp___la_SOURCES += option6_auth.cc option6_auth.h libkea_dhcp___la_SOURCES += option6_client_fqdn.cc option6_client_fqdn.h libkea_dhcp___la_SOURCES += option6_ia.cc option6_ia.h libkea_dhcp___la_SOURCES += option6_iaaddr.cc option6_iaaddr.h @@ -97,6 +98,7 @@ libkea_dhcp___include_HEADERS = \ option4_addrlst.h \ option4_client_fqdn.h \ option6_addrlst.h \ + option6_auth.h \ option6_client_fqdn.h \ option6_ia.h \ option6_iaaddr.h \ diff --git a/src/lib/dhcp/option6_auth.cc b/src/lib/dhcp/option6_auth.cc new file mode 100644 index 0000000000..4f9b364cf9 --- /dev/null +++ b/src/lib/dhcp/option6_auth.cc @@ -0,0 +1,127 @@ +// Copyright (C) 2011-2016 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 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace isc::util; + +namespace isc { +namespace dhcp { + + Option6Auth::Option6Auth(const uint8_t proto, const uint8_t algo, + const uint8_t method, const uint64_t rdm, + const std::vector& info) + : Option(Option::V6, D6O_AUTH), + protocol_(proto), algorithm_(algo), + rdm_method_(method), rdm_value_(rdm), + auth_info_(info) { +} + +OptionPtr +Option6Auth::clone() const { + return (cloneInternal()); +} + +void +Option6Auth::pack(isc::util::OutputBuffer& buf) const { + if (buf.getCapacity() < (OPTION6_AUTH_MIN_LEN + OPTION6_HASH_MSG_LEN)) { + isc_throw(OutOfRange, "Option " << type_ << "No space for" + "computing packing data"); + } + + //header = option code + length + buf.writeUint16(type_); + // length = 11 bytes fixed field length+ length of auth information + buf.writeUint16(11 + uint16_t(auth_info_.size())); + // protocol 1 byte + buf.writeUint8(protocol_); + // algoritm 1 byte + buf.writeUint8(algorithm_); + // replay detection method + buf.writeUint8(rdm_method_); + // replay detection value + buf.writeUint64( rdm_value_); + // authentication information for reconfig msg + // should have zero + + for (auto i : auth_info_) { + buf.writeUint8(i); + } +} + +void +Option6Auth::packHashInput(isc::util::OutputBuffer& buf) const { + if (buf.getCapacity() < (OPTION6_AUTH_MIN_LEN + OPTION6_HASH_MSG_LEN)) { + isc_throw(OutOfRange, "Option " << type_ << "No space for" + "computing hash input"); + } + + //header = option code + length + buf.writeUint16(type_); + // length = 11 bytes fixed field length+ length of auth information + buf.writeUint16(OPTION6_AUTH_MIN_LEN + OPTION6_HASH_MSG_LEN); + // protocol 1 byte + buf.writeUint8(protocol_); + // algoritm 1 byte + buf.writeUint8(algorithm_); + // replay detection method + buf.writeUint8(rdm_method_); + // replay detection value + buf.writeUint64(rdm_value_); + // authentication information for reconfig msg + // should have zero + for (uint8_t i = 0; i < OPTION6_HASH_MSG_LEN; i++) { + buf.writeUint8(0); + } +} + +void +Option6Auth::unpack(OptionBufferConstIter begin, + OptionBufferConstIter end) { + // throw if it contains length less than minimum size of the auth option + if (distance(begin, end) < Option6Auth::OPTION6_AUTH_MIN_LEN) { + isc_throw(OutOfRange, "Option " << type_ << " truncated"); + } + + protocol_ = *begin; + begin += sizeof(uint8_t); + + algorithm_ = *begin; + begin += sizeof(uint8_t); + + rdm_method_ = *begin; + begin += sizeof(uint8_t); + + rdm_value_ = isc::util::readUint64(&(*begin), sizeof(uint64_t)); + begin += sizeof(uint64_t); + + auth_info_.erase(auth_info_.begin(), auth_info_.end()); + std::for_each(begin, end, [this](uint8_t msgdata) + { auth_info_.push_back(msgdata); }); +} + +std::string +Option6Auth::toText(int indent) const { + stringstream output; + std::string in(indent, ' '); //base indent + + output << in << "protocol= " << protocol_ << "algorithm= " << algorithm_ + << "rdm method= " << rdm_method_ << "rdm value= " << rdm_value_ ; + return std::string(output); +} + +} // end namespace dhcp +} // end namespace isc diff --git a/src/lib/dhcp/option6_auth.h b/src/lib/dhcp/option6_auth.h new file mode 100644 index 0000000000..dfaa600452 --- /dev/null +++ b/src/lib/dhcp/option6_auth.h @@ -0,0 +1,142 @@ +// Copyright (C) 2011-2016 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 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef OPTION6_AUTH_H +#define OPTION6_AUTH_H +#endif + +#include +#include + +#include + +namespace isc { +namespace dhcp { + +class Option6Auth; + +/// A pointer to the @c isc::dhcp::Option6Auth object. +typedef boost::shared_ptr Option6AuthPtr; + +class Option6Auth: public Option { + +public: + + static const uint8_t OPTION6_AUTH_MIN_LEN = 11; + static const uint8_t OPTION6_HASH_MSG_LEN = 16; + /// @brief Constructor, used for auth options while transmitting + /// + /// @param proto protocol type + /// @param algo algorithm type + /// @param method remote detection method + /// @param rdm replay detection value + /// @param info authentication info. + Option6Auth(const uint8_t proto, const uint8_t algo, const uint8_t method, + const uint64_t rdm, const std::vector& info); + + /// @brief Copies this option and returns a pointer to the copy. + virtual OptionPtr clone() const; + + /// Writes option in wire-format to buf, returns pointer to first unused + /// byte after stored option. + /// + /// @param buf buffer (option will be stored here) + void pack(isc::util::OutputBuffer& buf) const; + + /// Writes option in wire-format to buf, for computing hash + /// auth info filled with 0 for a length of 128 bits + /// returns with pointer to first unused + /// byte after stored option. + /// + /// @param buf buffer (option will be stored here) + void packHashInput(isc::util::OutputBuffer& buf) const; + + /// @brief Parses received buffer + /// @brief Parses received buffer + /// + /// Parses received buffer and returns offset to the first unused byte after + /// parsed option. + /// + /// @param begin iterator to first byte of option data + /// @param end iterator to end of option data (first byte after option end) + virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end); + + /// Provides human readable text representation + /// + /// @param indent number of leading space characters + /// + /// @return string with text representation + virtual std::string toText(int indent = 0) const; + + /// Set protocol type + /// + /// @param proto protocol type to be set + void setProtocol(uint8_t proto) { protocol_ = proto; } + + /// Set hash alogrithm type + /// + /// @param algo hash alogrithm type to be set + void setHashAlgo(uint8_t algo) { algorithm_ = algo; } + + /// Set replay detection method type + /// + /// @param method replay detection method to be set + void setRplyDtctnMthd(uint8_t method) { rdm_method_ = method; } + + /// Set replay detection method value + /// + /// @param rdm replay detection method value to be set + void setRplyDtctnValue(uint64_t value) { rdm_value_ = value; } + /// Set authentication information + /// + /// @param auth_info authentication information to be set + void setAuthInfo(const std::vector& auth_info) { auth_info_ = auth_info; } + + /// Returns protocol type + /// + /// @return protocol value + uint8_t getProtocol() const { return protocol_; } + + /// Returns hash algorithm type + /// + /// @return hash algorithm value + uint8_t getHashAlgo() const { return algorithm_; } + + /// Returns replay detection method type + /// + /// @return replay detection method type value + uint8_t getRplyDtctnMthd() const { return rdm_method_; } + + /// Return replay detection mechanism + /// + /// @return replay detection method value + uint64_t getRplyDtctnValue() const { return rdm_value_; } + + /// Return authentication information + /// + /// @return authentication information value + std::vector getAuthInfo() const { return auth_info_; } + +protected: + /// keeps protocol type + uint8_t protocol_; + + /// keeps hash algorithm value + uint8_t algorithm_; + + /// keeps replay detection method type + uint8_t rdm_method_; + + /// keeps replay detection method value + uint64_t rdm_value_; + + /// keeps authentication information + std::vector auth_info_; +}; + +} // isc::dhcp namespace +} // isc namespace + diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am index 8993829733..51790ceb27 100644 --- a/src/lib/dhcp/tests/Makefile.am +++ b/src/lib/dhcp/tests/Makefile.am @@ -51,6 +51,7 @@ libdhcp___unittests_SOURCES += option4_addrlst_unittest.cc libdhcp___unittests_SOURCES += option4_client_fqdn_unittest.cc libdhcp___unittests_SOURCES += option6_addrlst_unittest.cc libdhcp___unittests_SOURCES += option6_client_fqdn_unittest.cc +libdhcp___unittests_SOURCES += option6_auth_unittest.cc libdhcp___unittests_SOURCES += option6_ia_unittest.cc libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc libdhcp___unittests_SOURCES += option6_iaprefix_unittest.cc diff --git a/src/lib/dhcp/tests/option6_auth_unittest.cc b/src/lib/dhcp/tests/option6_auth_unittest.cc new file mode 100644 index 0000000000..7891af141d --- /dev/null +++ b/src/lib/dhcp/tests/option6_auth_unittest.cc @@ -0,0 +1,141 @@ +// Copyright (C) 2011-2015 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 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include +#include + +#include +#include + +#include +#include + +using namespace std; +using namespace isc; +using namespace isc::dhcp; +using namespace isc::util; +using boost::scoped_ptr; + +namespace { +class Option6AuthTest : public ::testing::Test { +public: + Option6AuthTest(): buff_(28) { + } + OptionBuffer buff_; +}; + +// check constructor, setters and getters +TEST_F(Option6AuthTest, basic) { + + scoped_ptr auth; + ASSERT_NO_THROW(auth.reset(new Option6Auth(1,2,0,0x9000,{'a','b','c','d'}))); + + ASSERT_EQ(1, auth->getProtocol()); + ASSERT_EQ(2, auth->getHashAlgo()); + ASSERT_EQ(0, auth->getRplyDtctnMthd()); + ASSERT_EQ(0x9000, auth->getRplyDtctnValue()); + + std::vector test_buf = {'a','b','c','d'}; + ASSERT_EQ(test_buf, auth->getAuthInfo()); + + auth->setProtocol(2); + auth->setHashAlgo(3); + auth->setRplyDtctnMthd(1); + auth->setRplyDtctnValue(109034830); + auth->setAuthInfo({1,2,3,4}); + + ASSERT_EQ(2, auth->getProtocol()); + ASSERT_EQ(3, auth->getHashAlgo()); + ASSERT_EQ(1, auth->getRplyDtctnMthd()); + ASSERT_EQ(109034830, auth->getRplyDtctnValue()); + + test_buf = {1,2,3,4}; + ASSERT_EQ(test_buf, auth->getAuthInfo()); +} + +//Check if all the fields are properly parsed and stored +// todo define userdefined literal and add packing function to it +TEST_F(Option6AuthTest, parseFields) { + buff_[0] = 0xa1; //protocol + buff_[1] = 0xa2; //algo + buff_[2] = 0xa3; //rdm method + buff_[3] = 0xa4; //rdm value + buff_[4] = 0xa5; //rdm value + buff_[5] = 0xa6; //rdm value + buff_[6] = 0xa7; //rdm value + buff_[7] = 0xa8; //rdm value + buff_[8] = 0xa9; //rdm value + buff_[9] = 0xaa; //rdm value + buff_[10] = 0xab; //rdm value + for ( uint8_t i = 11; i < 27; i++ ) { + buff_[i] = 0xa8; //auth info 16 bytes + } + + scoped_ptr auth; + auth.reset(new Option6Auth(1,2,0,9000,{'a','b','c','d'})); + + auth->unpack(buff_.begin(), buff_.begin()+27); //26 element is 16 byte offset from 10 + + std::vector test_buf(16,0xa8); + ASSERT_EQ(0xa1, auth->getProtocol()); + ASSERT_EQ(0xa2, auth->getHashAlgo()); + ASSERT_EQ(0xa3, auth->getRplyDtctnMthd()); + ASSERT_EQ(0xa4a5a6a7a8a9aaab, auth->getRplyDtctnValue()); + ASSERT_EQ(test_buf, auth->getAuthInfo()); +} + +//Check of the options are correctly packed and set +TEST_F(Option6AuthTest, setFields) { + scoped_ptr auth; + std::vector test_buf(16,0xa8); + auth.reset(new Option6Auth(1,2,0,0x0090000000000000,test_buf)); + + isc::util::OutputBuffer buf(29);//2 header + fixed 11 and key 16 + ASSERT_NO_THROW(auth->pack(buf)); + + const uint8_t ref_data[] = { + 0, 11, 0, 27, 1, 2, 0, //header , proto algo method + 0, 0x90, 0, 0, 0, 0, 0, 0, //64 bit rdm field + 0xa8, 0xa8, 0xa8, 0xa8, //128 bits/16 byte key + 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8 + }; + //first check if they are of equal size + ASSERT_EQ(buf.getLength(), sizeof(ref_data)); + + //evaluate the contents of the option byte by byte + ASSERT_EQ(0, memcmp(ref_data, buf.getData(), buf.getLength())); +} + +TEST_F(Option6AuthTest, checkHashInput) { + scoped_ptr auth; + + std::vector test_buf(16,0xa8); + std::vector hash_op(16,0x00); + auth.reset(new Option6Auth(1,2,0,0x0090000000000000,test_buf)); + + isc::util::OutputBuffer buf(29); + auth->packHashInput(buf); + //auth info must be 0 for calculating the checksum + const uint8_t ref_data[] = { + 0, 11, 0, 27, 1, 2, 0, //header , proto algo method + 0, 0x90, 0, 0, 0, 0, 0, 0, //64 bit rdm field + 0x00, 0x00, 0x00, 0x00, //128 bits/16 byte key + 0x00, 0x00, 0x00, 0x00, //128 bits/16 byte key + 0x00, 0x00, 0x00, 0x00, //128 bits/16 byte key + 0x00, 0x00, 0x00, 0x00, //128 bits/16 byte key + }; + //first check if they are of equal size + ASSERT_EQ(buf.getLength(), sizeof(ref_data)); + + //evaluate the contents of the option byte by byte + ASSERT_EQ(0, memcmp(ref_data, buf.getData(), buf.getLength())); +} + +} //end namespace diff --git a/src/lib/util/buffer.h b/src/lib/util/buffer.h index e6986b6248..203f080156 100644 --- a/src/lib/util/buffer.h +++ b/src/lib/util/buffer.h @@ -522,6 +522,22 @@ public: buffer_[size_ ++] = static_cast(data & 0x000000ff); } + /// \brief Write an unsigned 64-bit integer in host byte order + /// into the buffer in network byte order. + /// + /// \param data The 64-bit integer to be written into the buffer. + void writeUint64(uint64_t data) { + ensureAllocated(size_ + sizeof(data)); + buffer_[size_ ++] = static_cast((data & 0xff00000000000000) >> 56); + buffer_[size_ ++] = static_cast((data & 0x00ff000000000000) >> 48); + buffer_[size_ ++] = static_cast((data & 0x0000ff0000000000) >> 40); + buffer_[size_ ++] = static_cast((data & 0x000000ff00000000) >> 32); + buffer_[size_ ++] = static_cast((data & 0x00000000ff000000) >> 24); + buffer_[size_ ++] = static_cast((data & 0x0000000000ff0000) >> 16); + buffer_[size_ ++] = static_cast((data & 0x000000000000ff00) >> 8); + buffer_[size_ ++] = static_cast(data & 0x00000000000000ff); + } + /// \brief Copy an arbitrary length of data into the buffer. /// /// No conversion on the copied data is performed. diff --git a/src/lib/util/io_utilities.h b/src/lib/util/io_utilities.h index 2ff8be40b0..b87e4e13da 100644 --- a/src/lib/util/io_utilities.h +++ b/src/lib/util/io_utilities.h @@ -93,6 +93,37 @@ readUint32(const uint8_t* buffer, size_t length) { return (result); } +/// \brief Read Unsigned 64-Bit Integer from Buffer +/// +/// \param buffer Data buffer at least four bytes long of which the first four +/// bytes are assumed to represent a 64-bit integer in network-byte +/// order. +/// \param length Length of the data buffer. +/// +/// \return Value of 64-bit unsigned integer +inline uint64_t +readUint64(const uint8_t* buffer, size_t length) { + if (length < sizeof(uint64_t)) { + isc_throw(isc::OutOfRange, + "Length (" << length << ") of buffer is insufficient " << + "to read a uint64_t"); + } + + const uint8_t* byte_buffer = static_cast(buffer); + + uint64_t result = (static_cast(byte_buffer[0])) << 56; + result |= (static_cast(byte_buffer[1])) << 48; + result |= (static_cast(byte_buffer[2])) << 40; + result |= (static_cast(byte_buffer[3])) << 32; + result |= (static_cast(byte_buffer[4])) << 24; + result |= (static_cast(byte_buffer[5])) << 16; + result |= (static_cast(byte_buffer[6])) << 8; + result |= (static_cast(byte_buffer[7])); + + + return (result); +} + /// \brief Write Unsigned 32-Bit Integer to Buffer /// /// \param value 32-bit value to convert