]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[sedhcpv6] preparing incoming signature (checkpoint)
authorFrancis Dupont <fdupont@isc.org>
Sun, 24 May 2015 11:27:19 +0000 (13:27 +0200)
committerFrancis Dupont <fdupont@isc.org>
Sun, 24 May 2015 11:27:19 +0000 (13:27 +0200)
13 files changed:
src/lib/dhcp/dhcp6.h
src/lib/dhcp/libdhcp++.cc
src/lib/dhcp/libdhcp++.h
src/lib/dhcp/pkt6.cc
src/lib/dhcp/pkt6.h
src/lib/dhcp/std_option_defs.h
src/lib/dhcp/tests/libdhcp++_unittest.cc
src/lib/dhcp/tests/pkt6_unittest.cc
src/lib/dhcpsrv/host.cc
src/lib/dhcpsrv/host.h
src/lib/dhcpsrv/parsers/host_reservation_parser.cc
src/lib/dhcpsrv/tests/host_reservation_parser_unittest.cc
src/lib/dhcpsrv/tests/host_unittest.cc

index 2cef8a7e7c71c4c3636cb97c6abed883be037994..817768ca53b3caff280aa8a98955ca7f7f380c48 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2006-2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2006-2011, 2015  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
 #define D6O_ERP_LOCAL_DOMAIN_NAME               65 /* RFC6440 */
 #define D6O_RSOO                                66 /* RFC6422 */
 #define D6O_CLIENT_LINKLAYER_ADDR               79 /* RFC6939 */
+/* secure DHCPv6 (draft-ietf-dhc-sedhcpv6-07) */
+#define D6O_PUBLIC_KEY                         701
+#define D6O_CERTIFICATE                        702
+#define D6O_SIGNATURE                          703
+#define D6O_TIMESTAMP                          704
 
 /*
  * Status Codes, from RFC 3315 section 24.4, and RFC 3633, 5007.
  */
-#define STATUS_Success           0
-#define STATUS_UnspecFail        1
-#define STATUS_NoAddrsAvail      2
-#define STATUS_NoBinding         3
-#define STATUS_NotOnLink         4
-#define STATUS_UseMulticast      5
-#define STATUS_NoPrefixAvail     6
-#define STATUS_UnknownQueryType  7
-#define STATUS_MalformedQuery    8
-#define STATUS_NotConfigured     9
-#define STATUS_NotAllowed       10
+#define STATUS_Success                   0
+#define STATUS_UnspecFail                1
+#define STATUS_NoAddrsAvail              2
+#define STATUS_NoBinding                 3
+#define STATUS_NotOnLink                 4
+#define STATUS_UseMulticast              5
+#define STATUS_NoPrefixAvail             6
+#define STATUS_UnknownQueryType          7
+#define STATUS_MalformedQuery            8
+#define STATUS_NotConfigured             9
+#define STATUS_NotAllowed               10
+/* secure DHCPv6 */
+#define STATUS_AlgorithmNotSupported   705
+#define STATUS_AuthenticationFail      706
+#define STATUS_TimestampFail           707
+#define STATUS_SignatureFail           708
 
 /*
  * DHCPv6 message types, defined in section 5.3 of RFC 3315
index be7095533d853a9037399d2ba556f8e973d606df..3adae94830aa2bb1c92467913b61f7be78fc1ccd 100644 (file)
@@ -246,7 +246,8 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
                                const std::string& option_space,
                                isc::dhcp::OptionCollection& options,
                                size_t* relay_msg_offset /* = 0 */,
-                               size_t* relay_msg_len /* = 0 */) {
+                               size_t* relay_msg_len /* = 0 */,
+                              size_t* signature_offset /* = 0 */) {
     size_t offset = 0;
     size_t length = buf.size();
 
@@ -292,6 +293,12 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
             continue;
         }
 
+       if (opt_type == D6O_SIGNATURE && signature_offset) {
+           // remember offset of the beginning of the signature option
+           // should check if there is only most one
+           *signature_offset = offset;
+       }
+
         if (opt_type == D6O_VENDOR_OPTS) {
             if (offset + 4 > length) {
                 // Truncated vendor-option. There is expected at least 4 bytes
index 45b0ae388d123e5e60e0456f92638c4a68e4fd38..e2a2a277faee4d1d8b085b9c07868e2645f1556d 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2014 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2015 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
@@ -152,32 +152,41 @@ public:
                                  const std::string& option_space,
                                  isc::dhcp::OptionCollection& options);
 
-    /// @brief Parses provided buffer as DHCPv6 options and creates Option objects.
+    /// @brief Parses provided buffer as DHCPv6 options and creates
+    /// Option objects.
     ///
-    /// Parses provided buffer and stores created Option objects in options
-    /// container. The last two parameters are optional and are used in
-    /// relay parsing. If they are specified, relay-msg option is not created,
-    /// but rather those two parameters are specified to point out where
-    /// the relay-msg option resides and what is its length. This is perfromance
-    /// optimization that avoids unnecessary copying of potentially large
-    /// relay-msg option. It is not used for anything, except in the next
-    /// iteration its content will be treated as buffer to be parsed.
+
+    /// Parses provided buffer and stores created Option objects in
+    /// options container. The last three parameters are optional and
+    /// are used in relay parsing and secure DHCPv6. If the first two
+    /// are specified, relay-msg option is not created, but rather
+    /// those two parameters are specified to point out where the
+    /// relay-msg option resides and what is its length. This is
+    /// performance optimization that avoids unnecessary copying of
+    /// potentially large relay-msg option. It is not used for
+    /// anything, except in the next iteration its content will be
+    /// treated as buffer to be parsed.
     ///
     /// @param buf Buffer to be parsed.
     /// @param option_space A name of the option space which holds definitions
     /// of to be used to parse options in the packets.
     /// @param options Reference to option container. Options will be
     ///        put here.
-    /// @param relay_msg_offset reference to a size_t structure. If specified,
+    /// @param relay_msg_offset reference to a size_t. If specified,
     ///        offset to beginning of relay_msg option will be stored in it.
-    /// @param relay_msg_len reference to a size_t structure. If specified,
+    /// @param relay_msg_len reference to a size_t. If specified,
     ///        length of the relay_msg option will be stored in it.
-    /// @return offset to the first byte after the last successfully parsed option
+    /// @param signature_offset reference to a size_t. If specified
+    ///        offset to beginning of signautre option will be stored in it.
+    /// @return offset to the first byte after the last successfully
+    /// parsed option
+
     static size_t unpackOptions6(const OptionBuffer& buf,
                                  const std::string& option_space,
                                  isc::dhcp::OptionCollection& options,
                                  size_t* relay_msg_offset = 0,
-                                 size_t* relay_msg_len = 0);
+                                 size_t* relay_msg_len = 0,
+                                 size_t* signature_offset = 0);
 
     /// Registers factory method that produces options of specific option types.
     ///
index 1d04200b5e7206f1816971aaa0d9c070bd06290a..9b18ce0bba4ab0c2508eacdb5941c50a7ff0dd10 100644 (file)
@@ -40,17 +40,17 @@ namespace dhcp {
 
 Pkt6::RelayInfo::RelayInfo()
     :msg_type_(0), hop_count_(0), linkaddr_(DEFAULT_ADDRESS6),
-    peeraddr_(DEFAULT_ADDRESS6), relay_msg_len_(0) {
+     peeraddr_(DEFAULT_ADDRESS6), relay_msg_len_(0) {
 }
 
 Pkt6::Pkt6(const uint8_t* buf, uint32_t buf_len, DHCPv6Proto proto /* = UDP */)
-   :Pkt(buf, buf_len, DEFAULT_ADDRESS6, DEFAULT_ADDRESS6, 0, 0),
-    proto_(proto), msg_type_(0) {
+    :Pkt(buf, buf_len, DEFAULT_ADDRESS6, DEFAULT_ADDRESS6, 0, 0),
+     proto_(proto), msg_type_(0), signature_offset_(0) {
 }
 
 Pkt6::Pkt6(uint8_t msg_type, uint32_t transid, DHCPv6Proto proto /*= UDP*/)
-:Pkt(transid, DEFAULT_ADDRESS6, DEFAULT_ADDRESS6, 0, 0), proto_(proto),
-    msg_type_(msg_type) {
+    :Pkt(transid, DEFAULT_ADDRESS6, DEFAULT_ADDRESS6, 0, 0), proto_(proto),
+     msg_type_(msg_type), signature_offset_() {
 }
 
 size_t Pkt6::len() {
@@ -300,8 +300,7 @@ Pkt6::unpackUDP() {
 }
 
 void
-Pkt6::unpackMsg(OptionBuffer::const_iterator begin,
-                OptionBuffer::const_iterator end) {
+Pkt6::unpackMsg(OptionBufferConstIter begin, OptionBufferConstIter end) {
     size_t size = std::distance(begin, end);
     if (size < 4) {
         // truncated message (less than 4 bytes)
@@ -309,6 +308,10 @@ Pkt6::unpackMsg(OptionBuffer::const_iterator begin,
                   << data_.size() << ", DHCPv6 header alone has 4 bytes.");
     }
 
+    // Keep begin and end for secure DHCPv6
+    raw_begin_ = begin;
+    raw_end_ = end;
+
     msg_type_ = *begin++;
 
     transid_ = ( (*begin++) << 16 ) +
@@ -326,7 +329,8 @@ Pkt6::unpackMsg(OptionBuffer::const_iterator begin,
     // to parse options. Otherwise, use standard function from libdhcp.
     size_t offset;
     if (callback_.empty()) {
-        offset = LibDHCP::unpackOptions6(opt_buffer, "dhcp6", options_);
+       offset = LibDHCP::unpackOptions6(opt_buffer, "dhcp6", options_,
+                                        0, 0, &signature_offset_);
     } else {
         // The last two arguments hold the DHCPv6 Relay message offset and
         // length. Setting them to NULL because we are dealing with the
index fe3943c56297f40a281311a8728438745e20f9e4..4cb0774aadc19bb7e98b00b472694a6f882a3ae7 100644 (file)
@@ -164,6 +164,21 @@ public:
     /// @return string with text representation
     virtual std::string toText() const;
 
+    /// @brief Returns the begin of the (possibly relayed) packet
+    OptionBufferConstIter rawBegin() const {
+        return (raw_begin_);
+    }
+
+    /// @brief Returns the end of the (possibly relayed) packet
+    OptionBufferConstIter rawEnd() const {
+        return (raw_end_);
+    }
+
+    /// @brief Returns the offset of the signature option (or 0)
+    size_t getSignatureOffset() const {
+        return (signature_offset_);
+    }
+
     /// @brief Returns length of the packet.
     ///
     /// This function returns size required to hold this packet.
@@ -395,8 +410,7 @@ protected:
     /// @param begin start of the buffer
     /// @param end end of the buffer
     /// @throw tbd
-    void unpackMsg(OptionBuffer::const_iterator begin,
-                   OptionBuffer::const_iterator end);
+    void unpackMsg(OptionBufferConstIter begin, OptionBufferConstIter end);
 
     /// @brief Unpacks relayed message (RELAY-FORW or RELAY-REPL).
     ///
@@ -430,6 +444,15 @@ protected:
     /// DHCPv6 message type
     uint8_t msg_type_;
 
+    /// Begin of the raw packet
+    OptionBufferConstIter raw_begin_;
+
+    /// End of the raw packet
+    OptionBufferConstIter raw_end_;
+
+    /// Offset of the signature option
+    size_t signature_offset_;
+
 }; // Pkt6 class
 
 } // isc::dhcp namespace
index 69fabbb06538de9521dd09132539b643fa91178a..284038eca2ea7839a95b4ea6cbdd0f1e6e782947 100644 (file)
@@ -237,6 +237,11 @@ RECORD_DECL(REMOTE_ID_RECORDS, OPT_UINT32_TYPE, OPT_BINARY_TYPE);
 RECORD_DECL(STATUS_CODE_RECORDS, OPT_UINT16_TYPE, OPT_STRING_TYPE);
 // vendor-class
 RECORD_DECL(VENDOR_CLASS_RECORDS, OPT_UINT32_TYPE, OPT_BINARY_TYPE);
+// sedhcpv6 signature
+RECORD_DECL(SIGNATURE_RECORDS, OPT_UINT8_TYPE, OPT_UINT8_TYPE,
+            OPT_BINARY_TYPE);
+// sedhcpv6 timestamp (should be uint64)
+RECORD_DECL(TIMESTAMP_RECORDS, OPT_UINT32_TYPE, OPT_UINT32_TYPE);
 
 /// Standard DHCPv6 option definitions.
 ///
@@ -331,7 +336,15 @@ const OptionDefParams OPTION_DEF_PARAMS6[] = {
       NO_RECORD_DEF, "" },
     { "rsoo", D6O_RSOO, OPT_EMPTY_TYPE, false, NO_RECORD_DEF, "rsoo-opts" },
     { "client-linklayer-addr", D6O_CLIENT_LINKLAYER_ADDR, OPT_BINARY_TYPE, false,
-        NO_RECORD_DEF, "" }
+      NO_RECORD_DEF, "" },
+    { "public-key", D6O_PUBLIC_KEY, OPT_BINARY_TYPE, false,
+      NO_RECORD_DEF, "" },
+    { "certificate", D6O_CERTIFICATE, OPT_BINARY_TYPE, false,
+      NO_RECORD_DEF, "" },
+    { "signature", D6O_SIGNATURE, OPT_RECORD_TYPE, false,
+      RECORD_DEF(SIGNATURE_RECORDS), "" },
+    { "timestamp", D6O_TIMESTAMP, OPT_RECORD_TYPE, false,
+      RECORD_DEF(TIMESTAMP_RECORDS), "" }
 
     // @todo There is still a bunch of options for which we have to provide
     // definitions but we don't do it because they are not really
index 92ab21a9358612b734e129bae44d753037ca89e3..4b482fb76f854f13a952f29f8c99ba09f13b9cbf 100644 (file)
@@ -1148,6 +1148,18 @@ TEST_F(LibDhcpTest, stdOptionDefs6) {
     LibDhcpTest::testStdOptionDefs6(D6O_ERP_LOCAL_DOMAIN_NAME,
                                     fqdn_buf.begin(), fqdn_buf.end(),
                                     typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_PUBLIC_KEY, begin, end,
+                                    typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_CERTIFICATE, begin, end,
+                                    typeid(Option));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_SIGNATURE, begin, end,
+                                    typeid(OptionCustom));
+
+    LibDhcpTest::testStdOptionDefs6(D6O_TIMESTAMP, begin, begin + 8,
+                                    typeid(OptionCustom));
 }
 
 // This test checks if the DHCPv6 option definition can be searched by
index fe99bcb45604945e71a488e46a37468c356aa1df..8efada95551008a2db7752cb7bd11ce89cf49835 100644 (file)
@@ -286,7 +286,10 @@ TEST_F(Pkt6Test, unpack_solicit1) {
     ASSERT_NO_THROW(sol->unpack());
 
     // Check for length
-    EXPECT_EQ(98, sol->len() );
+    EXPECT_EQ(98, sol->len());
+    size_t raw_len = std::distance(sol->rawBegin(), sol->rawEnd());
+    EXPECT_EQ(98, raw_len);
+    EXPECT_EQ(0, sol->getSignatureOffset());
 
     // Check for type
     EXPECT_EQ(DHCPV6_SOLICIT, sol->getType() );
@@ -302,6 +305,7 @@ TEST_F(Pkt6Test, unpack_solicit1) {
     EXPECT_FALSE(sol->getOption(D6O_SERVERID)); // server-id is missing
     EXPECT_FALSE(sol->getOption(D6O_IA_TA));
     EXPECT_FALSE(sol->getOption(D6O_IAADDR));
+
 }
 
 TEST_F(Pkt6Test, packUnpack) {
@@ -568,6 +572,13 @@ TEST_F(Pkt6Test, relayUnpack) {
 
     ASSERT_EQ(2, msg->relay_info_.size());
 
+    // Check the raw stuff
+    OptionBufferConstIter begin = msg->rawBegin();
+    OptionBufferConstIter end = msg->rawEnd();
+    EXPECT_EQ(DHCPV6_SOLICIT, *begin);
+    EXPECT_EQ(54, std::distance(begin, end));
+    EXPECT_EQ(0, msg->getSignatureOffset());
+
     OptionPtr opt;
 
     // Part 1: Check options inserted by the first relay
index 43efb8629936f8364db131894d2662467fcd7461..e367ada1a2ad6763c8b3a1a619f8bfc248cf01eb 100644 (file)
@@ -85,8 +85,10 @@ Host::Host(const uint8_t* identifier, const size_t identifier_len,
     : hw_address_(), duid_(), ipv4_subnet_id_(ipv4_subnet_id),
       ipv6_subnet_id_(ipv6_subnet_id),
       ipv4_reservation_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()),
-      hostname_(hostname), dhcp4_client_classes_(dhcp4_client_classes),
-      dhcp6_client_classes_(dhcp6_client_classes) {
+      hostname_(hostname),
+      dhcp4_client_classes_(dhcp4_client_classes),
+      dhcp6_client_classes_(dhcp6_client_classes),
+      credential_("") {
 
     // Initialize HWAddr or DUID
     setIdentifier(identifier, identifier_len, identifier_type);
@@ -106,8 +108,10 @@ Host::Host(const std::string& identifier, const std::string& identifier_name,
     : hw_address_(), duid_(), ipv4_subnet_id_(ipv4_subnet_id),
       ipv6_subnet_id_(ipv6_subnet_id),
       ipv4_reservation_(asiolink::IOAddress::IPV4_ZERO_ADDRESS()),
-      hostname_(hostname), dhcp4_client_classes_(dhcp4_client_classes),
-      dhcp6_client_classes_(dhcp6_client_classes) {
+      hostname_(hostname),
+      dhcp4_client_classes_(dhcp4_client_classes),
+      dhcp6_client_classes_(dhcp6_client_classes),
+      credential_("") {
 
     // Initialize HWAddr or DUID
     setIdentifier(identifier, identifier_name);
@@ -323,6 +327,11 @@ Host::toText() const {
           << "=" << *cclass;
     }
 
+    // Add credential
+    if (!credential_.empty()) {
+        s << " credential=" << credential_;
+    }
+
     return (s.str());
 }
 
index 0f30d59471004987c26beb434a7b17998108aa1d..74d2a740e04048fc6d7169533e8bc5be1c4a864d 100644 (file)
@@ -137,6 +137,8 @@ typedef std::pair<IPv6ResrvIterator, IPv6ResrvIterator> IPv6ResrvRange;
 /// DHCPv6 exchanges.
 /// - client classes which the client is associated with
 /// - DHCP options specifically configured for the device
+/// - filename of public key or certificate of the client for
+/// secure DHCPv6.
 ///
 /// Note, that "host" in this context has a different meaning from
 /// host construed as device attached to a network with (possibly) multiple
@@ -168,6 +170,7 @@ typedef std::pair<IPv6ResrvIterator, IPv6ResrvIterator> IPv6ResrvRange;
 /// - remove and replace IPv6 reservations
 /// - remove and replace client classes
 /// - disable IPv4 reservation without a need to set it to the 0.0.0.0 address
+/// - implement Trust-on-first-use for secure DHCPv6
 /// Note that the last three operations are mainly required for managing
 /// host reservations which will be implemented later.
 class Host {
@@ -417,6 +420,18 @@ public:
         return (dhcp6_client_classes_);
     }
 
+    /// @brief Sets new credential (public key or certificate)
+    ///
+    /// @param filename New filename to the public key or certificate.
+    void setCredential(const std::string& filename) {
+        credential_ = filename;
+    }
+
+    /// @brief Returns credential
+    const std::string& getCredential() const {
+        return (credential_);
+    }
+
     /// @brief Returns information about the host in the textual format.
     std::string toText() const;
 
@@ -455,6 +470,8 @@ private:
     ClientClasses dhcp4_client_classes_;
     /// @brief Collection of classes associated with a DHCPv6 client.
     ClientClasses dhcp6_client_classes_;
+    /// @brief Credential (filename of public key or certificate)
+    std::string credential_;
 };
 
 /// @brief Pointer to the @c Host object.
index b5ddc0fcff863d26c044727689ad3151f2b3a48b..aa161e23b3e705200b9aebe6ab75dab0b48e5571 100644 (file)
@@ -190,6 +190,10 @@ HostReservationParser6::build(isc::data::ConstElementPtr reservation_data) {
                               << prefix_element->getPosition() << ")");
                 }
             }
+        } else if (element.first == "public-key" ||
+                   element.first == "certificate") {
+            // Set the credential (filename of public key or certificate)
+            host_->setCredential(element.second->stringValue());
         }
     }
 
index 5c94779f7198a3b6ca77f00e53e0c647c7827436..fc39943c1ef7baa14b2922d4b2095aac3c3cbf09 100644 (file)
@@ -115,6 +115,7 @@ TEST_F(HostReservationParserTest, dhcp4HWaddr) {
     EXPECT_EQ(0, hosts[0]->getIPv6SubnetID());
     EXPECT_EQ("192.0.2.134", hosts[0]->getIPv4Reservation().toText());
     EXPECT_EQ("foo.example.com", hosts[0]->getHostname());
+    EXPECT_TRUE(hosts[0]->getCredential().empty());
 }
 
 // This test verfies that the parser can parse the reservation entry for
@@ -139,6 +140,7 @@ TEST_F(HostReservationParserTest, dhcp4DUID) {
     EXPECT_EQ(0, hosts[0]->getIPv6SubnetID());
     EXPECT_EQ("192.0.2.112", hosts[0]->getIPv4Reservation().toText());
     EXPECT_TRUE(hosts[0]->getHostname().empty());
+    EXPECT_TRUE(hosts[0]->getCredential().empty());
 }
 
 // This test verifies that the parser can parse the reservation entry
@@ -162,6 +164,7 @@ TEST_F(HostReservationParserTest, dhcp4NoHostname) {
     EXPECT_EQ(0, hosts[0]->getIPv6SubnetID());
     EXPECT_EQ("192.0.2.10", hosts[0]->getIPv4Reservation().toText());
     EXPECT_TRUE(hosts[0]->getHostname().empty());
+    EXPECT_TRUE(hosts[0]->getCredential().empty());
 }
 
 
@@ -222,6 +225,7 @@ TEST_F(HostReservationParserTest, noIPAddress) {
     EXPECT_EQ(0, hosts[0]->getIPv6SubnetID());
     EXPECT_EQ("0.0.0.0", hosts[0]->getIPv4Reservation().toText());
     EXPECT_EQ("foo.example.com", hosts[0]->getHostname());
+    EXPECT_TRUE(hosts[0]->getCredential().empty());
 }
 
 // This test verifies  that the configuration parser for host reservations
@@ -296,6 +300,7 @@ TEST_F(HostReservationParserTest, dhcp6HWaddr) {
     EXPECT_EQ(0, hosts[0]->getIPv4SubnetID());
     EXPECT_EQ(10, hosts[0]->getIPv6SubnetID());
     EXPECT_EQ("foo.example.com", hosts[0]->getHostname());
+    EXPECT_TRUE(hosts[0]->getCredential().empty());
 
     IPv6ResrvRange addresses = hosts[0]->
         getIPv6Reservations(IPv6Resrv::TYPE_NA);
@@ -343,6 +348,7 @@ TEST_F(HostReservationParserTest, dhcp6DUID) {
     EXPECT_EQ(0, hosts[0]->getIPv4SubnetID());
     EXPECT_EQ(12, hosts[0]->getIPv6SubnetID());
     EXPECT_EQ("foo.example.com", hosts[0]->getHostname());
+    EXPECT_TRUE(hosts[0]->getCredential().empty());
 
     IPv6ResrvRange addresses = hosts[0]->
         getIPv6Reservations(IPv6Resrv::TYPE_NA);
@@ -380,6 +386,7 @@ TEST_F(HostReservationParserTest, dhcp6NoHostname) {
     EXPECT_EQ(0, hosts[0]->getIPv4SubnetID());
     EXPECT_EQ(12, hosts[0]->getIPv6SubnetID());
     EXPECT_TRUE(hosts[0]->getHostname().empty());
+    EXPECT_TRUE(hosts[0]->getCredential().empty());
 
     IPv6ResrvRange addresses = hosts[0]->
         getIPv6Reservations(IPv6Resrv::TYPE_NA);
@@ -483,5 +490,82 @@ TEST_F(HostReservationParserTest, dhcp6DuplicatedPrefix) {
     EXPECT_THROW(parser.build(config_element), DhcpConfigError);
 }
 
+// This test verifies that the configuration parser ignores a public key
+// in DHCPv4
+TEST_F(HostReservationParserTest, dhcp4PublicKey) {
+    std::string config = "{ \"hw-address\": \"01:02:03:04:05:06\","
+        "\"ip-address\": \"192.0.2.134\","
+        "\"public-key\": \"foobar\" }";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    HostReservationParser4 parser(SubnetID(1));
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
+    HostCollection hosts;
+    ASSERT_NO_THROW(hosts = cfg_hosts->getAll(hwaddr_, DuidPtr()));
+
+    ASSERT_EQ(1, hosts.size());
+    EXPECT_TRUE(hosts[0]->getCredential().empty());
+}
+
+// This test verifies that the configuration parser handles a public key
+TEST_F(HostReservationParserTest, dhcp6PublicKey) {
+    std::string config = "{ \"duid\": \"01:02:03:04:05:06:07:08:09:0A\","
+        "\"ip-addresses\": [ \"2001:db8:1::100\" ],"
+        "\"public-key\": \"foobar\" }";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    HostReservationParser6 parser(SubnetID(1));
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
+    HostCollection hosts;
+    ASSERT_NO_THROW(hosts = cfg_hosts->getAll(HWAddrPtr(), duid_));
+
+    ASSERT_EQ(1, hosts.size());
+    EXPECT_EQ("foobar", hosts[0]->getCredential());
+}
+
+// This test verifies that the configuration parser ignores a certificate
+// in DHCPv4
+TEST_F(HostReservationParserTest, dhcp4Certificate) {
+    std::string config = "{ \"hw-address\": \"01:02:03:04:05:06\","
+        "\"ip-address\": \"192.0.2.134\","
+        "\"certificate\": \"foobar\" }";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    HostReservationParser4 parser(SubnetID(1));
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
+    HostCollection hosts;
+    ASSERT_NO_THROW(hosts = cfg_hosts->getAll(hwaddr_, DuidPtr()));
+
+    ASSERT_EQ(1, hosts.size());
+    EXPECT_TRUE(hosts[0]->getCredential().empty());
+}
+
+// This test verifies that the configuration parser handles a certificate
+TEST_F(HostReservationParserTest, dhcp6Certificate) {
+    std::string config = "{ \"duid\": \"01:02:03:04:05:06:07:08:09:0A\","
+        "\"ip-addresses\": [ \"2001:db8:1::100\" ],"
+        "\"certificate\": \"foobar\" }";
+
+    ElementPtr config_element = Element::fromJSON(config);
+
+    HostReservationParser6 parser(SubnetID(1));
+    ASSERT_NO_THROW(parser.build(config_element));
+
+    CfgHostsPtr cfg_hosts = CfgMgr::instance().getStagingCfg()->getCfgHosts();
+    HostCollection hosts;
+    ASSERT_NO_THROW(hosts = cfg_hosts->getAll(HWAddrPtr(), duid_));
+
+    ASSERT_EQ(1, hosts.size());
+    EXPECT_EQ("foobar", hosts[0]->getCredential());
+}
 
 } // end of anonymous namespace
index c67c6483289a229a631956d46ec7b87f81053496..aa50ed265d30b655deb48b073dc721ce3e8cadac 100644 (file)
@@ -511,6 +511,18 @@ TEST(HostTest, addClientClasses) {
     EXPECT_TRUE(host->getClientClasses6().contains("bar"));
 }
 
+// Test credential management
+TEST(HostTest, setCredential) {
+    boost::scoped_ptr<Host> host;
+    ASSERT_NO_THROW(host.reset(new Host("01:02:03:04:05:06", "hw-address",
+                                        SubnetID(1), SubnetID(2),
+                                        IOAddress("192.0.2.3"))));
+
+    EXPECT_EQ("", host->getCredential());
+    host->setCredential("foobar");
+    EXPECT_EQ("foobar", host->getCredential());
+}
+
 TEST(HostTest, getIdentifierAsText) {
     Host host1("01:02:03:04:05:06", "hw-address",
                SubnetID(1), SubnetID(2),