2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-31 05:55:28 +00:00

[master] Merge branch 'trac812' with fixing conflicts and regression.

Conflicts:
	doc/Doxyfile
	src/lib/Makefile.am
	src/lib/cryptolink/cryptolink.cc
	src/lib/dns/tsigkey.cc
	src/lib/util/unittests/Makefile.am
Other fixes:
 - cppcheck filter adjustment
 - (indirect) dependency on libbotan.  actually we should be able to make
   the link within libcryptolink, so I chose that approach.  hopefully
   it doesn't break autobuild.
This commit is contained in:
JINMEI Tatuya
2011-04-28 17:46:42 -07:00
26 changed files with 1735 additions and 42 deletions

View File

@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
# required by loadable python modules.
LIBRARY_PATH_PLACEHOLDER =
if SET_ENV_LIBRARY_PATH
LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH)
LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/xfr/.libs:$$$(ENV_LIBRARY_PATH)
endif
# test using command-line arguments, so use check-local target instead of TESTS

View File

@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
# required by loadable python modules.
LIBRARY_PATH_PLACEHOLDER =
if SET_ENV_LIBRARY_PATH
LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH)
LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$(abs_top_builddir)/src/lib/util/io/.libs:$$$(ENV_LIBRARY_PATH)
endif
# test using command-line arguments, so use check-local target instead of TESTS

View File

@@ -12,4 +12,4 @@ functionConst:src/lib/cache/rrset_cache.h
// Intentional self assignment tests. Suppress warning about them.
selfAssignment:src/lib/dns/tests/name_unittest.cc:293
selfAssignment:src/lib/dns/tests/rdata_unittest.cc:228
selfAssignment:src/lib/dns/tests/tsigkey_unittest.cc:104
selfAssignment:src/lib/dns/tests/tsigkey_unittest.cc:120

View File

@@ -2,7 +2,6 @@ SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
AM_CXXFLAGS = $(B10_CXXFLAGS)

View File

@@ -1,6 +1,5 @@
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CPPFLAGS += -I$(top_builddir)/src/lib/dns -I$(top_srcdir)/src/bin
AM_CPPFLAGS += -I$(top_builddir)/src/lib/util -I$(top_srcdir)/src/util
AM_CPPFLAGS += -I$(top_builddir)/src/lib/cc
AM_CPPFLAGS += -DTEST_DATA_DIR=\"$(srcdir)/testdata\"
@@ -17,8 +16,6 @@ TESTS =
if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.h
run_unittests_SOURCES += $(top_srcdir)/src/lib/dns/tests/unittest_util.cc
run_unittests_SOURCES += io_address_unittest.cc
run_unittests_SOURCES += io_endpoint_unittest.cc
run_unittests_SOURCES += io_socket_unittest.cc
@@ -32,7 +29,6 @@ run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(SQLITE_LIBS)
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la

View File

@@ -22,7 +22,6 @@ main(int argc, char* argv[])
{
::testing::InitGoogleTest(&argc, argv); // Initialize Google test
isc::log::setRootLoggerName("unittest"); // Set a root logger name
isc::UnitTestUtil::addDataPath(TEST_DATA_DIR); // Add location of test data
return (RUN_ALL_TESTS());
}

View File

@@ -26,7 +26,6 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/cc/libcc.la
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif

View File

@@ -10,3 +10,5 @@ lib_LTLIBRARIES = libcryptolink.la
libcryptolink_la_SOURCES = cryptolink.h cryptolink.cc
libcryptolink_la_SOURCES += crypto_hmac.h crypto_hmac.cc
libcryptolink_la_LIBADD = ${BOTAN_LDFLAGS} ${BOTAN_RPATH}

View File

@@ -17,12 +17,6 @@
#include <botan/botan.h>
#include <boost/scoped_ptr.hpp>
using namespace std;
using namespace isc::util;
namespace isc {
namespace cryptolink {

View File

@@ -16,7 +16,7 @@ TESTS += run_unittests
run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += crypto_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = ${BOTAN_LDFLAGS} ${BOTAN_RPATH} $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libcryptolink.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la

View File

@@ -80,12 +80,18 @@ libdns___la_SOURCES += rrsetlist.h rrsetlist.cc
libdns___la_SOURCES += rrttl.h rrttl.cc
libdns___la_SOURCES += rrtype.cc
libdns___la_SOURCES += question.h question.cc
libdns___la_SOURCES += tsig.h tsig.cc
libdns___la_SOURCES += tsigerror.h tsigerror.cc
libdns___la_SOURCES += tsigkey.h tsigkey.cc
libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.h
libdns___la_SOURCES += rdata/generic/detail/nsec_bitmap.cc
libdns___la_CPPFLAGS = $(AM_CPPFLAGS)
libdns___la_LIBADD = $(top_builddir)/src/lib/util/libutil.la
# Most applications of libdns++ will only implicitly rely on libcryptolink,
# so we add the dependency here so that the applications don't have to link
# libcryptolink explicitly.
libdns___la_LIBADD = $(top_builddir)/src/lib/cryptolink/libcryptolink.la
libdns___la_LIBADD += $(top_builddir)/src/lib/util/libutil.la
nodist_libdns___la_SOURCES = rdataclass.cc rrclass.h rrtype.h
nodist_libdns___la_SOURCES += rrparamregistry.cc

View File

@@ -20,7 +20,7 @@ EXTRA_DIST += testutil.py
# required by loadable python modules.
LIBRARY_PATH_PLACEHOLDER =
if SET_ENV_LIBRARY_PATH
LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
endif
# test using command-line arguments, so use check-local target instead of TESTS

View File

@@ -47,6 +47,8 @@ run_unittests_SOURCES += question_unittest.cc
run_unittests_SOURCES += rrparamregistry_unittest.cc
run_unittests_SOURCES += masterload_unittest.cc
run_unittests_SOURCES += message_unittest.cc
run_unittests_SOURCES += tsig_unittest.cc
run_unittests_SOURCES += tsigerror_unittest.cc
run_unittests_SOURCES += tsigkey_unittest.cc
run_unittests_SOURCES += run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
@@ -54,6 +56,8 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
run_unittests_LDADD = $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/dns/libdns++.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
run_unittests_LDADD += $(top_builddir)/src/lib/util/io/libutil_io.la
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
endif

View File

@@ -0,0 +1,505 @@
// Copyright (C) 2011 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
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <time.h>
#include <string>
#include <stdexcept>
#include <vector>
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
#include <exceptions/exceptions.h>
#include <util/buffer.h>
#include <util/encode/base64.h>
#include <util/unittests/newhook.h>
#include <dns/message.h>
#include <dns/messagerenderer.h>
#include <dns/question.h>
#include <dns/opcode.h>
#include <dns/rcode.h>
#include <dns/rrclass.h>
#include <dns/rrtype.h>
#include <dns/tsig.h>
#include <dns/tsigkey.h>
#include <dns/tests/unittest_util.h>
using namespace std;
using namespace isc;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::util::encode;
using namespace isc::dns::rdata;
using isc::UnitTestUtil;
// See dnssectime.cc
namespace isc {
namespace dns {
namespace tsig {
namespace detail {
extern int64_t (*gettimeFunction)();
}
}
}
}
namespace {
// See dnssectime_unittest.cc
template <int64_t NOW>
int64_t
testGetTime() {
return (NOW);
}
class TSIGTest : public ::testing::Test {
protected:
TSIGTest() :
tsig_ctx(NULL), qid(0x2d65), test_name("www.example.com"),
test_class(RRClass::IN()), test_ttl(86400), message(Message::RENDER),
buffer(0), renderer(buffer)
{
// Make sure we use the system time by default so that we won't be
// confused due to other tests that tweak the time.
tsig::detail::gettimeFunction = NULL;
decodeBase64("SFuWd/q99SzF8Yzd1QbB9g==", secret);
tsig_ctx.reset(new TSIGContext(TSIGKey(test_name,
TSIGKey::HMACMD5_NAME(),
&secret[0], secret.size())));
tsig_verify_ctx.reset(new TSIGContext(TSIGKey(test_name,
TSIGKey::HMACMD5_NAME(),
&secret[0],
secret.size())));
}
~TSIGTest() {
tsig::detail::gettimeFunction = NULL;
}
// Many of the tests below create some DNS message and sign it under
// some specific TSIG context. This helper method unifies the common
// logic with slightly different parameters.
ConstTSIGRecordPtr createMessageAndSign(uint16_t qid, const Name& qname,
TSIGContext* ctx,
unsigned int message_flags =
RD_FLAG,
RRType qtype = RRType::A(),
const char* answer_data = NULL,
const RRType* answer_type = NULL,
bool add_question = true,
Rcode rcode = Rcode::NOERROR());
// bit-wise constant flags to configure DNS header flags for test
// messages.
static const unsigned int QR_FLAG = 0x1;
static const unsigned int AA_FLAG = 0x2;
static const unsigned int RD_FLAG = 0x4;
boost::scoped_ptr<TSIGContext> tsig_ctx;
boost::scoped_ptr<TSIGContext> tsig_verify_ctx;
const uint16_t qid;
const Name test_name;
const RRClass test_class;
const RRTTL test_ttl;
Message message;
OutputBuffer buffer;
MessageRenderer renderer;
vector<uint8_t> secret;
};
ConstTSIGRecordPtr
TSIGTest::createMessageAndSign(uint16_t id, const Name& qname,
TSIGContext* ctx, unsigned int message_flags,
RRType qtype, const char* answer_data,
const RRType* answer_type, bool add_question,
Rcode rcode)
{
message.clear(Message::RENDER);
message.setQid(id);
message.setOpcode(Opcode::QUERY());
message.setRcode(rcode);
if ((message_flags & QR_FLAG) != 0) {
message.setHeaderFlag(Message::HEADERFLAG_QR);
}
if ((message_flags & AA_FLAG) != 0) {
message.setHeaderFlag(Message::HEADERFLAG_AA);
}
if ((message_flags & RD_FLAG) != 0) {
message.setHeaderFlag(Message::HEADERFLAG_RD);
}
if (add_question) {
message.addQuestion(Question(qname, test_class, qtype));
}
if (answer_data != NULL) {
if (answer_type == NULL) {
answer_type = &qtype;
}
RRsetPtr answer_rrset(new RRset(qname, test_class, *answer_type,
test_ttl));
answer_rrset->addRdata(createRdata(*answer_type, test_class,
answer_data));
message.addRRset(Message::SECTION_ANSWER, answer_rrset);
}
renderer.clear();
message.toWire(renderer);
ConstTSIGRecordPtr tsig = ctx->sign(id, renderer.getData(),
renderer.getLength());
EXPECT_EQ(TSIGContext::SIGNED, ctx->getState());
return (tsig);
}
void
commonTSIGChecks(ConstTSIGRecordPtr tsig, uint16_t expected_qid,
uint64_t expected_timesigned,
const uint8_t* expected_mac, size_t expected_maclen,
uint16_t expected_error = 0,
uint16_t expected_otherlen = 0,
const uint8_t* expected_otherdata = NULL,
const Name& expected_algorithm = TSIGKey::HMACMD5_NAME())
{
ASSERT_TRUE(tsig != NULL);
const any::TSIG& tsig_rdata = tsig->getRdata();
EXPECT_EQ(expected_algorithm, tsig_rdata.getAlgorithm());
EXPECT_EQ(expected_timesigned, tsig_rdata.getTimeSigned());
EXPECT_EQ(300, tsig_rdata.getFudge());
EXPECT_EQ(expected_maclen, tsig_rdata.getMACSize());
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
tsig_rdata.getMAC(), tsig_rdata.getMACSize(),
expected_mac, expected_maclen);
EXPECT_EQ(expected_qid, tsig_rdata.getOriginalID());
EXPECT_EQ(expected_error, tsig_rdata.getError());
EXPECT_EQ(expected_otherlen, tsig_rdata.getOtherLen());
EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
tsig_rdata.getOtherData(), tsig_rdata.getOtherLen(),
expected_otherdata, expected_otherlen);
}
TEST_F(TSIGTest, initialState) {
// Until signing or verifying, the state should be INIT
EXPECT_EQ(TSIGContext::INIT, tsig_ctx->getState());
// And there should be no error code.
EXPECT_EQ(TSIGError(Rcode::NOERROR()), tsig_ctx->getError());
}
// Example output generated by
// "dig -y www.example.com:SFuWd/q99SzF8Yzd1QbB9g== www.example.com
// QID: 0x2d65
// Time Signed: 0x00004da8877a
// MAC: 227026ad297beee721ce6c6fff1e9ef3
const uint8_t common_expected_mac[] = {
0x22, 0x70, 0x26, 0xad, 0x29, 0x7b, 0xee, 0xe7,
0x21, 0xce, 0x6c, 0x6f, 0xff, 0x1e, 0x9e, 0xf3
};
TEST_F(TSIGTest, sign) {
tsig::detail::gettimeFunction = testGetTime<0x4da8877a>;
{
SCOPED_TRACE("Sign test for query");
commonTSIGChecks(createMessageAndSign(qid, test_name, tsig_ctx.get()),
qid, 0x4da8877a, common_expected_mac,
sizeof(common_expected_mac));
}
}
// Same test as sign, but specifying the key name with upper-case (i.e.
// non canonical) characters. The digest must be the same. It should actually
// be ensured at the level of TSIGKey, but we confirm that at this level, too.
TEST_F(TSIGTest, signUsingUpperCasedKeyName) {
tsig::detail::gettimeFunction = testGetTime<0x4da8877a>;
TSIGContext cap_ctx(TSIGKey(Name("WWW.EXAMPLE.COM"),
TSIGKey::HMACMD5_NAME(),
&secret[0], secret.size()));
{
SCOPED_TRACE("Sign test for query using non canonical key name");
commonTSIGChecks(createMessageAndSign(qid, test_name, &cap_ctx), qid,
0x4da8877a, common_expected_mac,
sizeof(common_expected_mac));
}
}
// Same as the previous test, but for the algorithm name.
TEST_F(TSIGTest, signUsingUpperCasedAlgorithmName) {
tsig::detail::gettimeFunction = testGetTime<0x4da8877a>;
TSIGContext cap_ctx(TSIGKey(test_name,
Name("HMAC-md5.SIG-alg.REG.int"),
&secret[0], secret.size()));
{
SCOPED_TRACE("Sign test for query using non canonical algorithm name");
commonTSIGChecks(createMessageAndSign(qid, test_name, &cap_ctx), qid,
0x4da8877a, common_expected_mac,
sizeof(common_expected_mac));
}
}
TEST_F(TSIGTest, signAtActualTime) {
// Sign the message using the actual time, and check the accuracy of it.
// We cannot reasonably predict the expected MAC, so don't bother to
// check it.
const uint64_t now = static_cast<uint64_t>(time(NULL));
{
SCOPED_TRACE("Sign test for query at actual time");
ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name,
tsig_ctx.get());
const any::TSIG& tsig_rdata = tsig->getRdata();
// Check the resulted time signed is in the range of [now, now + 5]
// (5 is an arbitrary choice). Note that due to the order of the call
// to time() and sign(), time signed must not be smaller than the
// current time.
EXPECT_LE(now, tsig_rdata.getTimeSigned());
EXPECT_GE(now + 5, tsig_rdata.getTimeSigned());
}
}
TEST_F(TSIGTest, signBadData) {
// some specific bad data should be rejected proactively.
const unsigned char dummy_data = 0;
EXPECT_THROW(tsig_ctx->sign(0, NULL, 10), InvalidParameter);
EXPECT_THROW(tsig_ctx->sign(0, &dummy_data, 0), InvalidParameter);
}
#ifdef ENABLE_CUSTOM_OPERATOR_NEW
// We enable this test only when we enable custom new/delete at build time
// We could enable/disable the test runtime using the gtest filter, but
// we'd basically like to minimize the number of disabled tests (they
// should generally be considered tests that temporarily fail and should
// be fixed).
TEST_F(TSIGTest, signExceptionSafety) {
// Check sign() provides the strong exception guarantee for the simpler
// case (with a key error and empty MAC). The general case is more
// complicated and involves more memory allocation, so the test result
// won't be reliable.
tsig_verify_ctx->verifyTentative(createMessageAndSign(qid, test_name,
tsig_ctx.get()),
TSIGError::BAD_KEY());
// At this point the state should be changed to "CHECKED"
ASSERT_EQ(TSIGContext::CHECKED, tsig_verify_ctx->getState());
try {
int dummydata;
isc::util::unittests::force_throw_on_new = true;
isc::util::unittests::throw_size_on_new = sizeof(TSIGRecord);
tsig_verify_ctx->sign(0, &dummydata, sizeof(dummydata));
isc::util::unittests::force_throw_on_new = false;
ASSERT_FALSE(true) << "Expected throw on new, but it didn't happen";
} catch (const std::bad_alloc&) {
isc::util::unittests::force_throw_on_new = false;
// sign() threw, so the state should still be "CHECKED".
EXPECT_EQ(TSIGContext::CHECKED, tsig_verify_ctx->getState());
}
isc::util::unittests::force_throw_on_new = false;
}
#endif // ENABLE_CUSTOM_OPERATOR_NEW
// Same test as "sign" but use a different algorithm just to confirm we don't
// naively hardcode constants specific to a particular algorithm.
// Test data generated by
// "dig -y hmac-sha1:www.example.com:MA+QDhXbyqUak+qnMFyTyEirzng= www.example.com"
// QID: 0x0967, RDflag
// Current Time: 00004da8be86
// Time Signed: 00004dae7d5f
// HMAC Size: 20
// HMAC: 415340c7daf824ed684ee586f7b5a67a2febc0d3
TEST_F(TSIGTest, signUsingHMACSHA1) {
tsig::detail::gettimeFunction = testGetTime<0x4dae7d5f>;
secret.clear();
decodeBase64("MA+QDhXbyqUak+qnMFyTyEirzng=", secret);
TSIGContext sha1_ctx(TSIGKey(test_name, TSIGKey::HMACSHA1_NAME(),
&secret[0], secret.size()));
const uint16_t sha1_qid = 0x0967;
const uint8_t expected_mac[] = {
0x41, 0x53, 0x40, 0xc7, 0xda, 0xf8, 0x24, 0xed, 0x68, 0x4e,
0xe5, 0x86, 0xf7, 0xb5, 0xa6, 0x7a, 0x2f, 0xeb, 0xc0, 0xd3
};
{
SCOPED_TRACE("Sign test using HMAC-SHA1");
commonTSIGChecks(createMessageAndSign(sha1_qid, test_name, &sha1_ctx),
sha1_qid, 0x4dae7d5f, expected_mac,
sizeof(expected_mac), 0, 0, NULL,
TSIGKey::HMACSHA1_NAME());
}
}
// An example response to the signed query used for the "sign" test.
// Answer: www.example.com. 86400 IN A 192.0.2.1
// MAC: 8fcda66a7cd1a3b9948eb1869d384a9f
TEST_F(TSIGTest, signResponse) {
tsig::detail::gettimeFunction = testGetTime<0x4da8877a>;
ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name,
tsig_ctx.get());
tsig_verify_ctx->verifyTentative(tsig);
EXPECT_EQ(TSIGContext::CHECKED, tsig_verify_ctx->getState());
// Transform the original message to a response, then sign the response
// with the context of "verified state".
tsig = createMessageAndSign(qid, test_name, tsig_verify_ctx.get(),
QR_FLAG|AA_FLAG|RD_FLAG,
RRType::A(), "192.0.2.1");
const uint8_t expected_mac[] = {
0x8f, 0xcd, 0xa6, 0x6a, 0x7c, 0xd1, 0xa3, 0xb9,
0x94, 0x8e, 0xb1, 0x86, 0x9d, 0x38, 0x4a, 0x9f
};
{
SCOPED_TRACE("Sign test for response");
commonTSIGChecks(tsig, qid, 0x4da8877a,
expected_mac, sizeof(expected_mac));
}
}
// Example of signing multiple messages in a single TCP stream,
// taken from data using BIND 9's "one-answer" transfer-format.
// First message:
// QID: 0x3410, flags QR, AA
// Question: example.com/IN/AXFR
// Answer: example.com. 86400 IN SOA ns.example.com. root.example.com. (
// 2011041503 7200 3600 2592000 1200)
// Time Signed: 0x4da8e951
// Second message:
// Answer: example.com. 86400 IN NS ns.example.com.
// MAC: 102458f7f62ddd7d638d746034130968
TEST_F(TSIGTest, signContinuation) {
tsig::detail::gettimeFunction = testGetTime<0x4da8e951>;
const uint16_t axfr_qid = 0x3410;
const Name zone_name("example.com");
// Create and sign the AXFR request, then verify it.
tsig_verify_ctx->verifyTentative(createMessageAndSign(axfr_qid, zone_name,
tsig_ctx.get(), 0,
RRType::AXFR()));
EXPECT_EQ(TSIGContext::CHECKED, tsig_verify_ctx->getState());
// Create and sign the first response message (we don't need the result
// for the purpose of this test)
createMessageAndSign(axfr_qid, zone_name, tsig_verify_ctx.get(),
AA_FLAG|QR_FLAG, RRType::AXFR(),
"ns.example.com. root.example.com. "
"2011041503 7200 3600 2592000 1200",
&RRType::SOA());
// Create and sign the second response message
const uint8_t expected_mac[] = {
0x10, 0x24, 0x58, 0xf7, 0xf6, 0x2d, 0xdd, 0x7d,
0x63, 0x8d, 0x74, 0x60, 0x34, 0x13, 0x09, 0x68
};
{
SCOPED_TRACE("Sign test for continued response in TCP stream");
commonTSIGChecks(createMessageAndSign(axfr_qid, zone_name,
tsig_verify_ctx.get(),
AA_FLAG|QR_FLAG, RRType::AXFR(),
"ns.example.com.", &RRType::NS(),
false),
axfr_qid, 0x4da8e951,
expected_mac, sizeof(expected_mac));
}
}
// BADTIME example, taken from data using specially hacked BIND 9's nsupdate
// Query:
// QID: 0x1830, RD flag
// Current Time: 00004da8be86
// Time Signed: 00004da8b9d6
// Question: www.example.com/IN/SOA
//(mac) 8406 7d50 b8e7 d054 3d50 5bd9 de2a bb68
// Response:
// QRbit, RCODE=9(NOTAUTH)
// Time Signed: 00004da8b9d6 (the one in the query)
// MAC: d4b043f6f44495ec8a01260e39159d76
// Error: 0x12 (BADTIME), Other Len: 6
// Other data: 00004da8be86
TEST_F(TSIGTest, badtimeResponse) {
tsig::detail::gettimeFunction = testGetTime<0x4da8b9d6>;
const uint16_t test_qid = 0x7fc4;
ConstTSIGRecordPtr tsig = createMessageAndSign(test_qid, test_name,
tsig_ctx.get(), 0,
RRType::SOA());
// "advance the clock" and try validating, which should fail due to BADTIME
// (verifyTentative actually doesn't check the time, though)
tsig::detail::gettimeFunction = testGetTime<0x4da8be86>;
tsig_verify_ctx->verifyTentative(tsig, TSIGError::BAD_TIME());
EXPECT_EQ(TSIGError::BAD_TIME(), tsig_verify_ctx->getError());
// make and sign a response in the context of TSIG error.
tsig = createMessageAndSign(test_qid, test_name, tsig_verify_ctx.get(),
QR_FLAG, RRType::SOA(), NULL, NULL,
true, Rcode::NOTAUTH());
const uint8_t expected_otherdata[] = { 0, 0, 0x4d, 0xa8, 0xbe, 0x86 };
const uint8_t expected_mac[] = {
0xd4, 0xb0, 0x43, 0xf6, 0xf4, 0x44, 0x95, 0xec,
0x8a, 0x01, 0x26, 0x0e, 0x39, 0x15, 0x9d, 0x76
};
{
SCOPED_TRACE("Sign test for response with BADTIME");
commonTSIGChecks(tsig, message.getQid(), 0x4da8b9d6,
expected_mac, sizeof(expected_mac),
18, // error: BADTIME
sizeof(expected_otherdata),
expected_otherdata);
}
}
TEST_F(TSIGTest, badsigResponse) {
tsig::detail::gettimeFunction = testGetTime<0x4da8877a>;
// Sign a simple message, and force the verification to fail with
// BADSIG.
tsig_verify_ctx->verifyTentative(createMessageAndSign(qid, test_name,
tsig_ctx.get()),
TSIGError::BAD_SIG());
// Sign the same message (which doesn't matter for this test) with the
// context of "checked state".
{
SCOPED_TRACE("Sign test for response with BADSIG error");
commonTSIGChecks(createMessageAndSign(qid, test_name,
tsig_verify_ctx.get()),
message.getQid(), 0x4da8877a, NULL, 0,
16); // 16: BADSIG
}
}
TEST_F(TSIGTest, badkeyResponse) {
// A similar test as badsigResponse but for BADKEY
tsig::detail::gettimeFunction = testGetTime<0x4da8877a>;
tsig_verify_ctx->verifyTentative(createMessageAndSign(qid, test_name,
tsig_ctx.get()),
TSIGError::BAD_KEY());
{
SCOPED_TRACE("Sign test for response with BADKEY error");
commonTSIGChecks(createMessageAndSign(qid, test_name,
tsig_verify_ctx.get()),
message.getQid(), 0x4da8877a, NULL, 0,
17); // 17: BADKEYSIG
}
}
} // end namespace

View File

@@ -0,0 +1,102 @@
// Copyright (C) 2011 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
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <string>
#include <ostream>
#include <gtest/gtest.h>
#include <exceptions/exceptions.h>
#include <dns/rcode.h>
#include <dns/tsigerror.h>
using namespace std;
using namespace isc;
using namespace isc::dns;
namespace {
TEST(TSIGErrorTest, constructFromErrorCode) {
// These are pretty trivial, and also test getCode();
EXPECT_EQ(0, TSIGError(0).getCode());
EXPECT_EQ(18, TSIGError(18).getCode());
EXPECT_EQ(65535, TSIGError(65535).getCode());
}
TEST(TSIGErrorTest, constructFromRcode) {
// We use RCODE for code values from 0-15.
EXPECT_EQ(0, TSIGError(Rcode::NOERROR()).getCode());
EXPECT_EQ(15, TSIGError(Rcode(15)).getCode());
// From error code 16 TSIG errors define a separate space, so passing
// corresponding RCODE for such code values should be prohibited.
EXPECT_THROW(TSIGError(Rcode(16)).getCode(), OutOfRange);
}
TEST(TSIGErrorTest, constants) {
// We'll only test arbitrarily chosen subsets of the codes.
// This class is quite simple, so it should be suffice.
EXPECT_EQ(TSIGError::BAD_SIG_CODE, TSIGError(16).getCode());
EXPECT_EQ(TSIGError::BAD_KEY_CODE, TSIGError(17).getCode());
EXPECT_EQ(TSIGError::BAD_TIME_CODE, TSIGError(18).getCode());
EXPECT_EQ(0, TSIGError::NOERROR().getCode());
EXPECT_EQ(9, TSIGError::NOTAUTH().getCode());
EXPECT_EQ(14, TSIGError::RESERVED14().getCode());
EXPECT_EQ(TSIGError::BAD_SIG_CODE, TSIGError::BAD_SIG().getCode());
EXPECT_EQ(TSIGError::BAD_KEY_CODE, TSIGError::BAD_KEY().getCode());
EXPECT_EQ(TSIGError::BAD_TIME_CODE, TSIGError::BAD_TIME().getCode());
}
TEST(TSIGErrorTest, equal) {
EXPECT_TRUE(TSIGError::NOERROR() == TSIGError(Rcode::NOERROR()));
EXPECT_TRUE(TSIGError(Rcode::NOERROR()) == TSIGError::NOERROR());
EXPECT_TRUE(TSIGError::NOERROR().equals(TSIGError(Rcode::NOERROR())));
EXPECT_TRUE(TSIGError::NOERROR().equals(TSIGError(Rcode::NOERROR())));
EXPECT_TRUE(TSIGError::BAD_SIG() == TSIGError(16));
EXPECT_TRUE(TSIGError(16) == TSIGError::BAD_SIG());
EXPECT_TRUE(TSIGError::BAD_SIG().equals(TSIGError(16)));
EXPECT_TRUE(TSIGError(16).equals(TSIGError::BAD_SIG()));
}
TEST(TSIGErrorTest, nequal) {
EXPECT_TRUE(TSIGError::BAD_KEY() != TSIGError(Rcode::NOERROR()));
EXPECT_TRUE(TSIGError(Rcode::NOERROR()) != TSIGError::BAD_KEY());
EXPECT_TRUE(TSIGError::BAD_KEY().nequals(TSIGError(Rcode::NOERROR())));
EXPECT_TRUE(TSIGError(Rcode::NOERROR()).nequals(TSIGError::BAD_KEY()));
}
TEST(TSIGErrorTest, toText) {
// TSIGError derived from the standard Rcode
EXPECT_EQ("NOERROR", TSIGError(Rcode::NOERROR()).toText());
// Well known TSIG errors
EXPECT_EQ("BADSIG", TSIGError::BAD_SIG().toText());
EXPECT_EQ("BADKEY", TSIGError::BAD_KEY().toText());
EXPECT_EQ("BADTIME", TSIGError::BAD_TIME().toText());
// Unknown (or not yet supported) codes. Simply converted as numeric.
EXPECT_EQ("19", TSIGError(19).toText());
EXPECT_EQ("65535", TSIGError(65535).toText());
}
// test operator<<. We simply confirm it appends the result of toText().
TEST(TSIGErrorTest, LeftShiftOperator) {
ostringstream oss;
oss << TSIGError::BAD_KEY();
EXPECT_EQ(TSIGError::BAD_KEY().toText(), oss.str());
}
} // end namespace

View File

@@ -18,6 +18,8 @@
#include <exceptions/exceptions.h>
#include <cryptolink/cryptolink.h>
#include <dns/tsigkey.h>
#include <dns/tests/unittest_util.h>
@@ -38,6 +40,15 @@ TEST_F(TSIGKeyTest, algorithmNames) {
EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), TSIGKey::HMACMD5_NAME());
EXPECT_EQ(Name("hmac-sha1"), TSIGKey::HMACSHA1_NAME());
EXPECT_EQ(Name("hmac-sha256"), TSIGKey::HMACSHA256_NAME());
// Also check conversion to cryptolink definitions
EXPECT_EQ(isc::cryptolink::MD5, TSIGKey(key_name, TSIGKey::HMACMD5_NAME(),
NULL, 0).getCryptoAlgorithm());
EXPECT_EQ(isc::cryptolink::SHA1, TSIGKey(key_name, TSIGKey::HMACSHA1_NAME(),
NULL, 0).getCryptoAlgorithm());
EXPECT_EQ(isc::cryptolink::SHA256, TSIGKey(key_name,
TSIGKey::HMACSHA256_NAME(),
NULL, 0).getCryptoAlgorithm());
}
TEST_F(TSIGKeyTest, construct) {
@@ -58,6 +69,11 @@ TEST_F(TSIGKeyTest, construct) {
secret.c_str(),
secret.size()).getAlgorithmName().toText());
EXPECT_EQ("example.com.",
TSIGKey(Name("EXAMPLE.CoM."), TSIGKey::HMACSHA256_NAME(),
secret.c_str(),
secret.size()).getKeyName().toText());
// Invalid combinations of secret and secret_len:
EXPECT_THROW(TSIGKey(key_name, TSIGKey::HMACSHA1_NAME(), secret.c_str(), 0),
isc::InvalidParameter);

232
src/lib/dns/tsig.cc Normal file
View File

@@ -0,0 +1,232 @@
// Copyright (C) 2011 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
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <sys/time.h>
#include <stdint.h>
#include <cassert> // for the tentative verifyTentative()
#include <vector>
#include <boost/shared_ptr.hpp>
#include <exceptions/exceptions.h>
#include <util/buffer.h>
#include <dns/rdataclass.h>
#include <dns/rrclass.h>
#include <dns/tsig.h>
#include <dns/tsigerror.h>
#include <dns/tsigkey.h>
#include <cryptolink/cryptolink.h>
#include <cryptolink/crypto_hmac.h>
using namespace std;
using namespace isc::util;
using namespace isc::cryptolink;
using namespace isc::dns::rdata;
namespace isc {
namespace dns {
// Borrowed from dnssectime.cc. This trick should be unified somewhere.
namespace tsig {
namespace detail {
int64_t (*gettimeFunction)() = NULL;
}
}
namespace {
int64_t
gettimeofdayWrapper() {
using namespace tsig::detail;
if (gettimeFunction != NULL) {
return (gettimeFunction());
}
struct timeval now;
gettimeofday(&now, NULL);
return (static_cast<int64_t>(now.tv_sec));
}
}
namespace {
typedef boost::shared_ptr<HMAC> HMACPtr;
}
const RRClass&
TSIGRecord::getClass() {
return (RRClass::ANY());
}
struct TSIGContext::TSIGContextImpl {
TSIGContextImpl(const TSIGKey& key) :
state_(INIT), key_(key), error_(Rcode::NOERROR()),
previous_timesigned_(0)
{}
State state_;
TSIGKey key_;
vector<uint8_t> previous_digest_;
TSIGError error_;
uint64_t previous_timesigned_; // only meaningful for response with BADTIME
};
TSIGContext::TSIGContext(const TSIGKey& key) : impl_(new TSIGContextImpl(key))
{
}
TSIGContext::~TSIGContext() {
delete impl_;
}
TSIGContext::State
TSIGContext::getState() const {
return (impl_->state_);
}
TSIGError
TSIGContext::getError() const {
return (impl_->error_);
}
ConstTSIGRecordPtr
TSIGContext::sign(const uint16_t qid, const void* const data,
const size_t data_len)
{
if (data == NULL || data_len == 0) {
isc_throw(InvalidParameter, "TSIG sign error: empty data is given");
}
TSIGError error(TSIGError::NOERROR());
// TSIG uses 48-bit unsigned integer to represent time signed.
// Since gettimeofdayWrapper() returns a 64-bit *signed* integer, we
// make sure it's stored in an unsigned 64-bit integer variable and
// represents a value in the expected range. (In reality, however,
// gettimeofdayWrapper() will return a positive integer that will fit
// in 48 bits)
const uint64_t now = (gettimeofdayWrapper() & 0x0000ffffffffffffULL);
// For responses adjust the error code.
if (impl_->state_ == CHECKED) {
error = impl_->error_;
}
// For errors related to key or MAC, return an unsigned response as
// specified in Section 4.3 of RFC2845.
if (error == TSIGError::BAD_SIG() || error == TSIGError::BAD_KEY()) {
ConstTSIGRecordPtr tsig(new TSIGRecord(
any::TSIG(impl_->key_.getAlgorithmName(),
now, DEFAULT_FUDGE, NULL, 0,
qid, error.getCode(), 0, NULL)));
impl_->previous_digest_.clear();
impl_->state_ = SIGNED;
return (tsig);
}
OutputBuffer variables(0);
HMACPtr hmac(CryptoLink::getCryptoLink().createHMAC(
impl_->key_.getSecret(),
impl_->key_.getSecretLength(),
impl_->key_.getCryptoAlgorithm()),
deleteHMAC);
// If the context has previous MAC (either the Request MAC or its own
// previous MAC), digest it.
if (impl_->state_ != INIT) {
const uint16_t previous_digest_len(impl_->previous_digest_.size());
variables.writeUint16(previous_digest_len);
if (previous_digest_len != 0) {
variables.writeData(&impl_->previous_digest_[0],
previous_digest_len);
}
hmac->update(variables.getData(), variables.getLength());
}
// Digest the message (without TSIG)
hmac->update(data, data_len);
//
// Digest TSIG variables. If state_ is SIGNED we skip digesting them
// except for time related variables (RFC2845 4.4).
//
variables.clear();
if (impl_->state_ != SIGNED) {
impl_->key_.getKeyName().toWire(variables);
TSIGRecord::getClass().toWire(variables);
variables.writeUint32(TSIGRecord::TSIG_TTL);
impl_->key_.getAlgorithmName().toWire(variables);
}
const uint64_t time_signed = (error == TSIGError::BAD_TIME()) ?
impl_->previous_timesigned_ : now;
variables.writeUint16(time_signed >> 32);
variables.writeUint32(time_signed & 0xffffffff);
variables.writeUint16(DEFAULT_FUDGE);
hmac->update(variables.getData(), variables.getLength());
variables.clear();
if (impl_->state_ != SIGNED) {
variables.writeUint16(error.getCode());
// For BADTIME error, digest 6 bytes of other data.
// (6 bytes = size of time signed value)
variables.writeUint16((error == TSIGError::BAD_TIME()) ? 6 : 0);
hmac->update(variables.getData(), variables.getLength());
variables.clear();
if (error == TSIGError::BAD_TIME()) {
variables.writeUint16(now >> 32);
variables.writeUint32(now & 0xffffffff);
hmac->update(variables.getData(), variables.getLength());
}
}
const uint16_t otherlen = variables.getLength();
// Get the final digest, update internal state, then finish.
vector<uint8_t> digest = hmac->sign();
ConstTSIGRecordPtr tsig(new TSIGRecord(
any::TSIG(impl_->key_.getAlgorithmName(),
time_signed, DEFAULT_FUDGE,
digest.size(), &digest[0],
qid, error.getCode(), otherlen,
otherlen == 0 ?
NULL : variables.getData())));
// Exception free from now on.
impl_->previous_digest_.swap(digest);
impl_->state_ = SIGNED;
return (tsig);
}
void
TSIGContext::verifyTentative(ConstTSIGRecordPtr tsig, TSIGError error) {
const any::TSIG tsig_rdata = tsig->getRdata();
impl_->error_ = error;
if (error == TSIGError::BAD_TIME()) {
impl_->previous_timesigned_ = tsig_rdata.getTimeSigned();
}
// For simplicity we assume non empty digests.
assert(tsig_rdata.getMACSize() != 0);
impl_->previous_digest_.assign(
static_cast<const uint8_t*>(tsig_rdata.getMAC()),
static_cast<const uint8_t*>(tsig_rdata.getMAC()) +
tsig_rdata.getMACSize());
impl_->state_ = CHECKED;
}
} // namespace dns
} // namespace isc

302
src/lib/dns/tsig.h Normal file
View File

@@ -0,0 +1,302 @@
// Copyright (C) 2011 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
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef __TSIG_H
#define __TSIG_H 1
#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>
#include <dns/rdataclass.h>
#include <dns/tsigerror.h>
#include <dns/tsigkey.h>
namespace isc {
namespace dns {
/// TSIG resource record.
///
/// A \c TSIGRecord class object represents a TSIG resource record and is
/// responsible for conversion to and from wire format TSIG record based on
/// the protocol specification (RFC2845).
/// This class is provided so that other classes and applications can handle
/// TSIG without knowing protocol details of TSIG, such as that it uses a
/// fixed constant of TTL.
///
/// \note So the plan is to eventually provide a \c toWire() method and
/// the "from wire" constructor. They are not yet provided in this initial
/// step.
///
/// \note
/// This class could be a derived class of \c AbstractRRset. That way
/// it would be able to be used in a polymorphic way; for example,
/// an application can construct a TSIG RR by itself and insert it to a
/// \c Message object as a generic RRset. On the other hand, it would mean
/// this class would have to implement an \c RdataIterator (even though it
/// can be done via straightforward forwarding) while the iterator is mostly
/// redundant since there should be one and only one RDATA for a valid TSIG
/// RR. Likewise, some methods such as \c setTTL() method wouldn't be well
/// defined due to such special rules for TSIG as using a fixed TTL.
/// Overall, TSIG is a very special RR type that simply uses the compatible
/// resource record format, and it will be unlikely that a user wants to
/// handle it through a generic interface in a polymorphic way.
/// We therefore chose to define it as a separate class. This is also
/// similar to why \c EDNS is a separate class.
class TSIGRecord {
public:
/// Constructor from TSIG RDATA
///
/// \exception std::bad_alloc Resource allocation for copying the RDATA
/// fails
explicit TSIGRecord(const rdata::any::TSIG& tsig_rdata) :
rdata_(tsig_rdata)
{}
/// Return the RDATA of the TSIG RR
///
/// \exception None
const rdata::any::TSIG& getRdata() const { return (rdata_); }
/// \name Protocol constants and defaults
///
//@{
/// Return the RR class of TSIG
///
/// TSIG always uses the ANY RR class. This static method returns it,
/// when, though unlikely, an application wants to know which class TSIG
/// is supposed to use.
///
/// \exception None
static const RRClass& getClass();
/// The TTL value to be used in TSIG RRs.
static const uint32_t TSIG_TTL = 0;
//@}
private:
const rdata::any::TSIG rdata_;
};
/// A pointer-like type pointing to a \c TSIGRecord object.
typedef boost::shared_ptr<TSIGRecord> TSIGRecordPtr;
/// A pointer-like type pointing to an immutable \c TSIGRecord object.
typedef boost::shared_ptr<const TSIGRecord> ConstTSIGRecordPtr;
/// TSIG session context.
///
/// The \c TSIGContext class maintains a context of a signed session of
/// DNS transactions by TSIG. In many cases a TSIG signed session consists
/// of a single set of request (e.g. normal query) and reply (e.g. normal
/// response), where the request is initially signed by the client, and the
/// reply is signed by the server using the initial signature. As mentioned
/// in RFC2845, a session can consist of multiple exchanges in a TCP
/// connection. As also mentioned in the RFC, an AXFR response often contains
/// multiple DNS messages, which can belong to the same TSIG session.
/// This class supports all these cases.
///
/// A \c TSIGContext object is generally constructed with a TSIG key to be
/// used for the session, and keeps track of various kinds of session specific
/// information, such as the original digest while waiting for a response or
/// verification error information that is to be used for a subsequent
/// response.
///
/// This class has two main methods, \c sign() and \c verify().
/// The \c sign() method signs given data (which is supposed to be a complete
/// DNS message without the TSIG itself) using the TSIG key and other
/// related information associated with the \c TSIGContext object.
/// The \c verify() method verifies a given DNS message that contains a TSIG
/// RR using the key and other internal information.
///
/// In general, a DNS client that wants to send a signed query will construct
/// a \c TSIGContext object with the TSIG key that the client is intending to
/// use, and sign the query with the context. The client will keeps the
/// context, and verify the response with it.
///
/// On the other hand, a DNS server will construct a \c TSIGContext object
/// with the information of the TSIG RR included in a query with a set of
/// possible keys (in the form of a \c TSIGKeyRing object). The constructor
/// in this mode will identify the appropriate TSIG key (or internally record
/// an error if it doesn't find a key). The server will then verify the
/// query with the context, and generate a signed response using the same
/// same context. (Note: this mode is not yet implemented and may change,
/// see below).
///
/// When multiple messages belong to the same TSIG session, either side
/// (signer or verifier) will keep using the same context. It records
/// the latest session state (such as the previous digest) so that repeated
/// calls to \c sign() or \c verify() work correctly in terms of the TSIG
/// protocol.
///
/// \note The \c verify() method is not yet implemented. The implementation
/// and documentation should be updated in the corresponding task.
///
/// <b>TCP Consideration</b>
///
/// RFC2845 describes the case where a single TSIG session is used for
/// multiple DNS messages (Section 4.4). This class supports signing and
/// verifying the messages in this scenario, but does not care if the messages
/// were delivered over a TCP connection or not. If, for example, the
/// same \c TSIGContext object is used to sign two independent DNS queries
/// sent over UDP, they will be considered to belong to the same TSIG
/// session, and, as a result, verification will be likely to fail.
///
/// \b Copyability
///
/// This class is currently non copyable based on the observation of the
/// typical usage as described above. But there is no strong technical
/// reason why this class cannot be copyable. If we see the need for it
/// in future we may change the implementation on this point.
///
/// <b>Note to developers:</b>
/// One basic design choice is to make the \c TSIGContext class is as
/// independent from the \c Message class. This is because the latter is
/// much more complicated, depending on many other classes, while TSIG is
/// a very specific part of the entire DNS protocol set. If the \c TSIGContext
/// class depends on \c \c Message, it will be more vulnerable to changes
/// to other classes, and will be more difficult to test due to the
/// direct or indirect dependencies. The interface of \c sign() that takes
/// opaque data (instead of, e.g., a \c Message or \c MessageRenderer object)
/// is therefore a deliberate design decision.
class TSIGContext : boost::noncopyable {
public:
/// Internal state of context
///
/// The constants of this enum type define a specific state of
/// \c TSIGContext to adjust the behavior. The definition is public
/// and the state can be seen via the \c getState() method, but this is
/// mostly private information. It's publicly visible mainly for testing
/// purposes; there is no API for the application to change the state
/// directly.
enum State {
INIT, ///< Initial state
SIGNED, ///< Sign completed
CHECKED ///< Verification completed (may or may not successfully)
};
/// \name Constructors and destructor
///
//@{
/// Constructor from a TSIG key.
///
/// \exception std::bad_alloc Resource allocation for internal data fails
///
/// \param key The TSIG key to be used for TSIG sessions with this context.
explicit TSIGContext(const TSIGKey& key);
/// The destructor.
~TSIGContext();
//@}
/// Sign a DNS message.
///
/// This method computes the TSIG MAC for the given data, which is
/// generally expected to be a complete, wire-format DNS message
/// that doesn't contain a TSIG RR, based on the TSIG key and
/// other context information of \c TSIGContext, and returns a
/// result in the form of a (pointer object pointing to)
/// \c TSIGRecord object.
///
/// The caller of this method will use the returned value to render a
/// complete TSIG RR into the message that has been signed so that it
/// will become a complete TSIG-signed message.
///
/// \note Normal applications are not expected to call this method
/// directly; they will usually use the \c Message::toWire() method
/// with a \c TSIGContext object being a parameter and have the
/// \c Message class create a complete signed message.
///
/// This method treats the given data as opaque, even though it's generally
/// expected to represent a wire-format DNS message (see also the class
/// description), and doesn't inspect it in any way. For example, it
/// doesn't check whether the data length is sane for a valid DNS message.
/// This is also the reason why this method takes the \c qid parameter,
/// which will be used as the original ID of the resulting
/// \c TSIGRecordx object, even though this value should be stored in the
/// first two octets (in wire format) of the given data.
///
/// \note This method still checks and rejects empty data (\c NULL pointer
/// data or the specified data length is 0) in order to avoid catastrophic
/// effect such as program crash. Empty data is not necessarily invalid
/// for HMAC computation, but obviously it doesn't make sense for a DNS
/// message.
///
/// This method provides the strong exception guarantee; unless the method
/// returns (without an exception being thrown), the internal state of
/// the \c TSIGContext won't be modified.
///
/// \exception InvalidParameter \c data is NULL or \c data_len is 0
/// \exception cryptolink::LibraryError Some unexpected error in the
/// underlying crypto operation
/// \exception std::bad_alloc Temporary resource allocation failure
///
/// \param qid The QID to be as the value of the original ID field of
/// the resulting TSIG record
/// \param data Points to the wire-format data to be signed
/// \param data_len The length of \c data in bytes
///
/// \return A TSIG record for the given data along with the context.
ConstTSIGRecordPtr sign(const uint16_t qid, const void* const data,
const size_t data_len);
/// Return the current state of the context
///
/// \note
/// The states are visible in public mainly for testing purposes.
/// Normal applications won't have to deal with them.
///
/// \exception None
State getState() const;
/// Return the TSIG error as a result of the latest verification
///
/// This method can be called even before verifying anything, but the
/// returned value is meaningless in that case.
///
/// \exception None
TSIGError getError() const;
// This method is tentatively added for testing until a complete
// verify() method is implemented. Once it's done this should be
// removed, and corresponding tests should be updated.
//
// This tentative "verify" method changes the internal state of
// the TSIGContext to the CHECKED as if it were verified (though possibly
// unsuccessfully) with given tsig_rdata. If the error parameter is
// given and not NOERROR, it's recorded inside the context so that the
// subsequent sign() will behave accordingly.
void verifyTentative(ConstTSIGRecordPtr tsig,
TSIGError error = TSIGError::NOERROR());
/// \name Protocol constants and defaults
///
//@{
/// The recommended fudge value (in seconds) by RFC2845.
///
/// Right now fudge is not tunable, and all TSIGs generated by this API
/// will have this value of fudge.
static const uint16_t DEFAULT_FUDGE = 300;
//@}
private:
struct TSIGContextImpl;
TSIGContextImpl* impl_;
};
}
}
#endif // __TSIG_H
// Local Variables:
// mode: c++
// End:

57
src/lib/dns/tsigerror.cc Normal file
View File

@@ -0,0 +1,57 @@
// Copyright (C) 2011 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
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <ostream>
#include <string>
#include <boost/lexical_cast.hpp>
#include <exceptions/exceptions.h>
#include <dns/rcode.h>
#include <dns/tsigerror.h>
namespace isc {
namespace dns {
namespace {
const char* const tsigerror_text[] = {
"BADSIG",
"BADKEY",
"BADTIME"
};
}
TSIGError::TSIGError(Rcode rcode) : code_(rcode.getCode()) {
if (code_ > MAX_RCODE_FOR_TSIGERROR) {
isc_throw(OutOfRange, "Invalid RCODE for TSIG Error: " << rcode);
}
}
std::string
TSIGError::toText() const {
if (code_ <= MAX_RCODE_FOR_TSIGERROR) {
return (Rcode(code_).toText());
} else if (code_ <= BAD_TIME_CODE) {
return (tsigerror_text[code_ - (MAX_RCODE_FOR_TSIGERROR + 1)]);
} else {
return (boost::lexical_cast<std::string>(code_));
}
}
std::ostream&
operator<<(std::ostream& os, const TSIGError& error) {
return (os << error.toText());
}
} // namespace dns
} // namespace isc

328
src/lib/dns/tsigerror.h Normal file
View File

@@ -0,0 +1,328 @@
// Copyright (C) 2011 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
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef __TSIGERROR_H
#define __TSIGERROR_H 1
#include <ostream>
#include <string>
#include <dns/rcode.h>
namespace isc {
namespace dns {
class RRClass;
/// TSIG errors
///
/// The \c TSIGError class objects represent standard errors related to
/// TSIG protocol operations as defined in related specifications, mainly
/// in RFC2845.
///
/// (RCODEs) of the header section of DNS messages, and extended response
/// codes as defined in the EDNS specification.
class TSIGError {
public:
/// Constants for pre-defined TSIG error values.
///
/// Code values from 0 through 15 (inclusive) are derived from those of
/// RCODE and are not defined here. See the \c Rcode class.
///
/// \note Unfortunately some systems define "BADSIG" as a macro in a public
/// header file. To avoid conflict with it we add an underscore to our
/// definitions.
enum CodeValue {
BAD_SIG_CODE = 16, ///< 16: TSIG verification failure
BAD_KEY_CODE = 17, ///< 17: TSIG key is not recognized
BAD_TIME_CODE = 18 ///< 18: Current time and time signed are too different
};
/// \name Constructors
///
/// We use the default versions of destructor, copy constructor,
/// and assignment operator.
//@{
/// Constructor from the code value.
///
/// \exception None
///
/// \param code The underlying 16-bit error code value of the \c TSIGError.
explicit TSIGError(uint16_t error_code) : code_(error_code) {}
/// Constructor from \c Rcode.
///
/// As defined in RFC2845, error code values from 0 to 15 (inclusive) are
/// derived from the DNS RCODEs, which are represented via the \c Rcode
/// class in this library. This constructor works as a converter from
/// these RCODEs to corresponding TSIGError objects.
///
/// \exception isc::OutOfRange Given rcode is not convertible to
/// TSIGErrors.
///
/// \param rcode the \c Rcode from which the TSIGError should be derived.
explicit TSIGError(Rcode rcode);
//@}
/// \brief Returns the \c TSIGCode error code value.
///
/// \exception None
///
/// \return The underlying code value corresponding to the \c TSIGError.
uint16_t getCode() const { return (code_); }
/// \brief Return true iff two \c TSIGError objects are equal.
///
/// Two TSIGError objects are equal iff their error codes are equal.
///
/// \exception None
///
/// \param other the \c TSIGError object to compare against.
/// \return true if the two TSIGError are equal; otherwise false.
bool equals(const TSIGError& other) const
{ return (code_ == other.code_); }
/// \brief Same as \c equals().
bool operator==(const TSIGError& other) const { return (equals(other)); }
/// \brief Return true iff two \c TSIGError objects are not equal.
///
/// \exception None
///
/// \param other the \c TSIGError object to compare against.
/// \return true if the two TSIGError objects are not equal;
/// otherwise false.
bool nequals(const TSIGError& other) const
{ return (code_ != other.code_); }
/// \brief Same as \c nequals().
bool operator!=(const TSIGError& other) const { return (nequals(other)); }
/// \brief Convert the \c TSIGError to a string.
///
/// For codes derived from RCODEs up to 15, this method returns the
/// same string as \c Rcode::toText() for the corresponding code.
/// For other pre-defined code values (see TSIGError::CodeValue),
/// this method returns a string representation of the "mnemonic' used
/// for the enum and constant objects as defined in RFC2845.
/// For example, the string for code value 16 is "BADSIG", etc.
/// For other code values it returns a string representation of the decimal
/// number of the value, e.g. "32", "100", etc.
///
/// \exception std::bad_alloc Resource allocation for the string fails
///
/// \return A string representation of the \c TSIGError.
std::string toText() const;
/// A constant TSIG error object derived from \c Rcode::NOERROR()
static const TSIGError& NOERROR();
/// A constant TSIG error object derived from \c Rcode::FORMERR()
static const TSIGError& FORMERR();
/// A constant TSIG error object derived from \c Rcode::SERVFAIL()
static const TSIGError& SERVFAIL();
/// A constant TSIG error object derived from \c Rcode::NXDOMAIN()
static const TSIGError& NXDOMAIN();
/// A constant TSIG error object derived from \c Rcode::NOTIMP()
static const TSIGError& NOTIMP();
/// A constant TSIG error object derived from \c Rcode::REFUSED()
static const TSIGError& REFUSED();
/// A constant TSIG error object derived from \c Rcode::YXDOMAIN()
static const TSIGError& YXDOMAIN();
/// A constant TSIG error object derived from \c Rcode::YXRRSET()
static const TSIGError& YXRRSET();
/// A constant TSIG error object derived from \c Rcode::NXRRSET()
static const TSIGError& NXRRSET();
/// A constant TSIG error object derived from \c Rcode::NOTAUTH()
static const TSIGError& NOTAUTH();
/// A constant TSIG error object derived from \c Rcode::NOTZONE()
static const TSIGError& NOTZONE();
/// A constant TSIG error object derived from \c Rcode::RESERVED11()
static const TSIGError& RESERVED11();
/// A constant TSIG error object derived from \c Rcode::RESERVED12()
static const TSIGError& RESERVED12();
/// A constant TSIG error object derived from \c Rcode::RESERVED13()
static const TSIGError& RESERVED13();
/// A constant TSIG error object derived from \c Rcode::RESERVED14()
static const TSIGError& RESERVED14();
/// A constant TSIG error object derived from \c Rcode::RESERVED15()
static const TSIGError& RESERVED15();
/// A constant TSIG error object for the BADSIG code
/// (see \c TSIGError::BAD_SIG_CODE).
static const TSIGError& BAD_SIG();
/// A constant TSIG error object for the BADKEY code
/// (see \c TSIGError::BAD_KEY_CODE).
static const TSIGError& BAD_KEY();
/// A constant TSIG error object for the BADTIME code
/// (see \c TSIGError::BAD_TIME_CODE).
static const TSIGError& BAD_TIME();
private:
// This is internally used to specify the maximum possible RCODE value
// that can be convertible to TSIGErrors.
static const int MAX_RCODE_FOR_TSIGERROR = 15;
uint16_t code_;
};
inline const TSIGError&
TSIGError::NOERROR() {
static TSIGError e(Rcode::NOERROR());
return (e);
}
inline const TSIGError&
TSIGError::FORMERR() {
static TSIGError e(Rcode::FORMERR());
return (e);
}
inline const TSIGError&
TSIGError::SERVFAIL() {
static TSIGError e(Rcode::SERVFAIL());
return (e);
}
inline const TSIGError&
TSIGError::NXDOMAIN() {
static TSIGError e(Rcode::NXDOMAIN());
return (e);
}
inline const TSIGError&
TSIGError::NOTIMP() {
static TSIGError e(Rcode::NOTIMP());
return (e);
}
inline const TSIGError&
TSIGError::REFUSED() {
static TSIGError e(Rcode::REFUSED());
return (e);
}
inline const TSIGError&
TSIGError::YXDOMAIN() {
static TSIGError e(Rcode::YXDOMAIN());
return (e);
}
inline const TSIGError&
TSIGError::YXRRSET() {
static TSIGError e(Rcode::YXRRSET());
return (e);
}
inline const TSIGError&
TSIGError::NXRRSET() {
static TSIGError e(Rcode::NXRRSET());
return (e);
}
inline const TSIGError&
TSIGError::NOTAUTH() {
static TSIGError e(Rcode::NOTAUTH());
return (e);
}
inline const TSIGError&
TSIGError::NOTZONE() {
static TSIGError e(Rcode::NOTZONE());
return (e);
}
inline const TSIGError&
TSIGError::RESERVED11() {
static TSIGError e(Rcode::RESERVED11());
return (e);
}
inline const TSIGError&
TSIGError::RESERVED12() {
static TSIGError e(Rcode::RESERVED12());
return (e);
}
inline const TSIGError&
TSIGError::RESERVED13() {
static TSIGError e(Rcode::RESERVED13());
return (e);
}
inline const TSIGError&
TSIGError::RESERVED14() {
static TSIGError e(Rcode::RESERVED14());
return (e);
}
inline const TSIGError&
TSIGError::RESERVED15() {
static TSIGError e(Rcode::RESERVED15());
return (e);
}
inline const TSIGError&
TSIGError::BAD_SIG() {
static TSIGError e(BAD_SIG_CODE);
return (e);
}
inline const TSIGError&
TSIGError::BAD_KEY() {
static TSIGError e(BAD_KEY_CODE);
return (e);
}
inline const TSIGError&
TSIGError::BAD_TIME() {
static TSIGError e(BAD_TIME_CODE);
return (e);
}
/// Insert the \c TSIGError as a string into stream.
///
/// This method convert \c tsig_error into a string and inserts it into the
/// output stream \c os.
///
/// \param os A \c std::ostream object on which the insertion operation is
/// performed.
/// \param tsig_error An \c TSIGError object output by the operation.
/// \return A reference to the same \c std::ostream object referenced by
/// parameter \c os after the insertion operation.
std::ostream& operator<<(std::ostream& os, const TSIGError& tsig_error);
}
}
#endif // __TSIGERROR_H
// Local Variables:
// mode: c++
// End:

View File

@@ -26,47 +26,58 @@
#include <dns/tsigkey.h>
using namespace std;
namespace {
bool isValidAlgorithmName(const isc::dns::Name& name) {
return (name == isc::dns::TSIGKey::HMACMD5_NAME() ||
name == isc::dns::TSIGKey::HMACSHA1_NAME() ||
name == isc::dns::TSIGKey::HMACSHA256_NAME());
}
}
using namespace isc::cryptolink;
namespace isc {
namespace dns {
namespace {
HashAlgorithm
convertAlgorithmName(const isc::dns::Name& name) {
if (name == TSIGKey::HMACMD5_NAME()) {
return (isc::cryptolink::MD5);
}
if (name == TSIGKey::HMACSHA1_NAME()) {
return (isc::cryptolink::SHA1);
}
if (name == TSIGKey::HMACSHA256_NAME()) {
return (isc::cryptolink::SHA256);
}
isc_throw(InvalidParameter,
"Unknown TSIG algorithm is specified: " << name);
}
}
struct
TSIGKey::TSIGKeyImpl {
TSIGKeyImpl(const Name& key_name, const Name& algorithm_name,
isc::cryptolink::HashAlgorithm algorithm,
const void* secret, size_t secret_len) :
key_name_(key_name), algorithm_name_(algorithm_name),
algorithm_(algorithm),
secret_(static_cast<const uint8_t*>(secret),
static_cast<const uint8_t*>(secret) + secret_len)
{
// Convert the name to the canonical form.
// Convert the key and algorithm names to the canonical form.
key_name_.downcase();
algorithm_name_.downcase();
}
const Name key_name_;
Name key_name_;
Name algorithm_name_;
const isc::cryptolink::HashAlgorithm algorithm_;
const vector<uint8_t> secret_;
};
TSIGKey::TSIGKey(const Name& key_name, const Name& algorithm_name,
const void* secret, size_t secret_len) : impl_(NULL)
{
if (!isValidAlgorithmName(algorithm_name)) {
isc_throw(InvalidParameter, "Unknown TSIG algorithm is specified: " <<
algorithm_name);
}
const HashAlgorithm algorithm = convertAlgorithmName(algorithm_name);
if ((secret != NULL && secret_len == 0) ||
(secret == NULL && secret_len != 0)) {
isc_throw(InvalidParameter,
"TSIGKey secret and its length are inconsistent");
}
impl_ = new TSIGKeyImpl(key_name, algorithm_name, secret, secret_len);
impl_ = new TSIGKeyImpl(key_name, algorithm_name, algorithm, secret,
secret_len);
}
TSIGKey::TSIGKey(const std::string& str) : impl_(NULL) {
@@ -95,16 +106,13 @@ TSIGKey::TSIGKey(const std::string& str) : impl_(NULL) {
const Name algo_name(algo_str.empty() ? "hmac-md5.sig-alg.reg.int" :
algo_str);
if (!isValidAlgorithmName(algo_name)) {
isc_throw(InvalidParameter, "Unknown TSIG algorithm is specified: " <<
algo_name);
}
const HashAlgorithm algorithm = convertAlgorithmName(algo_name);
vector<uint8_t> secret;
isc::util::encode::decodeBase64(secret_str, secret);
impl_ = new TSIGKeyImpl(Name(keyname_str), algo_name, &secret[0],
secret.size());
impl_ = new TSIGKeyImpl(Name(keyname_str), algo_name, algorithm,
&secret[0], secret.size());
} catch (const Exception& e) {
// 'reduce' the several types of exceptions name parsing and
// Base64 decoding can throw to just the InvalidParameter
@@ -143,6 +151,11 @@ TSIGKey::getAlgorithmName() const {
return (impl_->algorithm_name_);
}
isc::cryptolink::HashAlgorithm
TSIGKey::getCryptoAlgorithm() const {
return (impl_->algorithm_);
}
const void*
TSIGKey::getSecret() const {
return ((impl_->secret_.size() > 0) ? &impl_->secret_[0] : NULL);

View File

@@ -15,6 +15,8 @@
#ifndef __TSIGKEY_H
#define __TSIGKEY_H 1
#include <cryptolink/cryptolink.h>
namespace isc {
namespace dns {
@@ -142,6 +144,9 @@ public:
/// Return the algorithm name.
const Name& getAlgorithmName() const;
/// Return the hash algorithm name in the form of cryptolink::HashAlgorithm
isc::cryptolink::HashAlgorithm getCryptoAlgorithm() const;
/// Return the length of the TSIG secret in bytes.
size_t getSecretLength() const;

View File

@@ -6,7 +6,7 @@ EXTRA_DIST = $(PYTESTS)
# required by loadable python modules.
LIBRARY_PATH_PLACEHOLDER =
if SET_ENV_LIBRARY_PATH
LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/dns/.libs:$(abs_top_builddir)/src/lib/cryptolink/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
endif
# test using command-line arguments, so use check-local target instead of TESTS

View File

@@ -3,5 +3,6 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
lib_LTLIBRARIES = libutil_unittests.la
libutil_unittests_la_SOURCES = fork.h fork.cc resolver.h
libutil_unittests_la_SOURCES += newhook.h newhook.cc
CLEANFILES = *.gcno *.gcda

View File

@@ -0,0 +1,51 @@
// Copyright (C) 2011 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
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <stdlib.h>
#include <new>
#include <stdexcept>
#include "newhook.h"
#ifdef ENABLE_CUSTOM_OPERATOR_NEW
void*
operator new(size_t size) throw(std::bad_alloc) {
if (isc::util::unittests::force_throw_on_new &&
size == isc::util::unittests::throw_size_on_new) {
throw std::bad_alloc();
}
void* p = malloc(size);
if (p == NULL) {
throw std::bad_alloc();
}
return (p);
}
void
operator delete(void* p) throw() {
if (p != NULL) {
free(p);
}
}
#endif
namespace isc {
namespace util {
namespace unittests {
bool force_throw_on_new = false;
size_t throw_size_on_new = 0;
}
}
}

View File

@@ -0,0 +1,82 @@
// Copyright (C) 2011 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
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef __UTIL_UNITTESTS_NEWHOOK_H
#define __UTIL_UNITTESTS_NEWHOOK_H 1
/**
* @file newhook.h
* @short Enable the use of special operator new that throws for testing.
*
* This small utility allows a test case to force the global operator new
* to throw for a given size to test a case where memory allocation fails
* (which normally doesn't happen). To enable the feature, everything must
* be built with defining ENABLE_CUSTOM_OPERATOR_NEW beforehand, and set
* \c force_throw_on_new to \c true and \c throw_size_on_new to the size
* of data that should trigger the exception, immediately before starting
* the specific test that needs the exception.
*
* Example:
* \code #include <util/unittests/newhook.h>
* ...
* TEST(SomeTest, newException) {
* isc::util::unittests::force_throw_on_new = true;
* isc::util::unittests::throw_size_on_new = sizeof(Foo);
* try {
* // this will do 'new Foo()' internally and should throw
* createFoo();
* isc::util::unittests::force_throw_on_new = false;
* ASSERT_FALSE(true) << "Expected throw on new";
* } catch (const std::bad_alloc&) {
* isc::util::unittests::force_throw_on_new = false;
* // do some integrity check, etc, if necessary
* }
* } \endcode
*
* Replacing the global operator new (and delete) is a dangerous technique,
* and triggering an exception solely based on the allocation size is not
* reliable, so this feature is disabled by default two-fold: The
* ENABLE_CUSTOM_OPERATOR_NEW build time variable, and run-time
* \c force_throw_on_new.
*/
namespace isc {
namespace util {
namespace unittests {
/// Switch to enable the use of special operator new
///
/// This is set to \c false by default.
extern bool force_throw_on_new;
/// The allocation size that triggers an exception in the special operator new
///
/// This is the exact size that causes an exception to be thrown;
/// for example, if it is set to 100, an attempt of allocating 100 bytes
/// will result in an exception, but allocation attempt for 101 bytes won't
/// (unless, of course, memory is really exhausted and allocation really
/// fails).
///
/// The default value is 0. The value of this variable has no meaning
/// unless the use of the special operator is enabled at build time and
/// via \c force_throw_on_new.
extern size_t throw_size_on_new;
}
}
}
#endif // __UTIL_UNITTESTS_NEWHOOK_H
// Local Variables:
// mode: c++
// End: