]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
lib: implements authenitication option required for reconfigure message
authormayya <mayya@itwm.fraunhofer.de>
Mon, 18 Jun 2018 13:46:51 +0000 (15:46 +0200)
committermayya <mayya@itwm.fraunhofer.de>
Mon, 18 Jun 2018 13:46:51 +0000 (15:46 +0200)
 See #86

src/lib/dhcp/Makefile.am
src/lib/dhcp/option6_auth.cc [new file with mode: 0644]
src/lib/dhcp/option6_auth.h [new file with mode: 0644]
src/lib/dhcp/tests/Makefile.am
src/lib/dhcp/tests/option6_auth_unittest.cc [new file with mode: 0644]
src/lib/util/buffer.h
src/lib/util/io_utilities.h

index e27d06ab44197d7fac00282240a72ae9de9fbc09..2b6412cc8930453996bd939bdd1f4b81727a47d6 100644 (file)
@@ -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 (file)
index 0000000..4f9b364
--- /dev/null
@@ -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 <config.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/option6_auth.h>
+#include <dhcp/option_space.h>
+#include <exceptions/exceptions.h>
+#include <util/io_utilities.h>
+
+#include <sstream>
+#include <stdint.h>
+
+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<uint8_t>& 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<Option6Auth>());    
+}
+
+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 (file)
index 0000000..dfaa600
--- /dev/null
@@ -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 <dhcp/option.h>
+#include <boost/shared_ptr.hpp>
+
+#include <vector> 
+
+namespace isc {
+namespace dhcp {
+
+class Option6Auth;
+
+/// A pointer to the @c isc::dhcp::Option6Auth object.
+typedef boost::shared_ptr<Option6Auth> 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<uint8_t>& 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<uint8_t>& 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<uint8_t> 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<uint8_t> auth_info_;   
+};
+
+} // isc::dhcp namespace
+} // isc namespace
+
index 8993829733f41065944d6bca0619d91e532dc8a1..51790ceb2712a41dd0e409944dba424e6543c314 100644 (file)
@@ -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 (file)
index 0000000..7891af1
--- /dev/null
@@ -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 <dhcp/dhcp6.h>
+#include <dhcp/option.h>
+#include <dhcp/option6_auth.h>
+#include <util/buffer.h>
+
+#include <gtest/gtest.h>
+#include <boost/scoped_ptr.hpp>
+
+#include <iostream>
+#include <sstream>
+
+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<Option6Auth> 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<uint8_t> 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<Option6Auth> 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<uint8_t> 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<Option6Auth> auth;
+    std::vector<uint8_t> 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<Option6Auth> auth;
+    
+    std::vector<uint8_t> test_buf(16,0xa8);
+    std::vector<uint8_t> 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
index e6986b6248a3b7fb12bb9ca66b33c72d228fc62b..203f08015696a3409a589cdc2a4497bf791d338c 100644 (file)
@@ -522,6 +522,22 @@ public:
         buffer_[size_ ++] = static_cast<uint8_t>(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<uint8_t>((data & 0xff00000000000000) >> 56);
+        buffer_[size_ ++] = static_cast<uint8_t>((data & 0x00ff000000000000) >> 48);
+        buffer_[size_ ++] = static_cast<uint8_t>((data & 0x0000ff0000000000) >> 40);
+        buffer_[size_ ++] = static_cast<uint8_t>((data & 0x000000ff00000000) >> 32);
+        buffer_[size_ ++] = static_cast<uint8_t>((data & 0x00000000ff000000) >> 24);
+        buffer_[size_ ++] = static_cast<uint8_t>((data & 0x0000000000ff0000) >> 16);
+        buffer_[size_ ++] = static_cast<uint8_t>((data & 0x000000000000ff00) >> 8);
+        buffer_[size_ ++] = static_cast<uint8_t>(data &  0x00000000000000ff);
+    }
+
     /// \brief Copy an arbitrary length of data into the buffer.
     ///
     /// No conversion on the copied data is performed.
index 2ff8be40b0f9d76e423d9ad9600daca17f055f3a..b87e4e13da26cb0938c73206bfe746b62a921e1b 100644 (file)
@@ -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<const uint8_t*>(buffer);
+
+    uint64_t result = (static_cast<uint64_t>(byte_buffer[0])) << 56;
+    result |= (static_cast<uint64_t>(byte_buffer[1])) << 48;
+    result |= (static_cast<uint64_t>(byte_buffer[2])) << 40;
+    result |= (static_cast<uint64_t>(byte_buffer[3])) << 32;
+    result |= (static_cast<uint64_t>(byte_buffer[4])) << 24;
+    result |= (static_cast<uint64_t>(byte_buffer[5])) << 16;
+    result |= (static_cast<uint64_t>(byte_buffer[6])) << 8;
+    result |= (static_cast<uint64_t>(byte_buffer[7]));
+
+
+    return (result);
+}
+
 /// \brief Write Unsigned 32-Bit Integer to Buffer
 ///
 /// \param value 32-bit value to convert