Commit d88bd046 authored by Mukund Sivaraman's avatar Mukund Sivaraman
Browse files

[2185] Add support for the TLSA RR type

parent 17f56de8
......@@ -70,6 +70,8 @@ EXTRA_DIST += rdata/generic/spf_99.cc
EXTRA_DIST += rdata/generic/spf_99.h
EXTRA_DIST += rdata/generic/sshfp_44.cc
EXTRA_DIST += rdata/generic/sshfp_44.h
EXTRA_DIST += rdata/generic/tlsa_52.cc
EXTRA_DIST += rdata/generic/tlsa_52.h
EXTRA_DIST += rdata/generic/txt_16.cc
EXTRA_DIST += rdata/generic/txt_16.h
EXTRA_DIST += rdata/generic/minfo_14.cc
......
......@@ -41,7 +41,7 @@ meta_types = {
'10': 'null', '11': 'wks', '19': 'x25', '21': 'rt', '22': 'nsap',
'23': 'nsap-ptr', '24': 'sig', '20': 'isdn', '25': 'key', '26': 'px',
'27': 'gpos', '29': 'loc', '36': 'kx', '37': 'cert', '42': 'apl',
'45': 'ipseckey', '52': 'tlsa', '55': 'hip', '103': 'unspec',
'45': 'ipseckey', '55': 'hip', '103': 'unspec',
'104': 'nid', '105': 'l32', '106': 'l64', '107': 'lp', '249': 'tkey',
'253': 'mailb', '256': 'uri', '257': 'caa'
}
......
// Copyright (C) 2014 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 <config.h>
#include <boost/lexical_cast.hpp>
#include <exceptions/exceptions.h>
#include <util/buffer.h>
#include <util/encode/hex.h>
#include <dns/name.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
using namespace std;
using boost::lexical_cast;
using namespace isc::util;
using namespace isc::util::encode;
// BEGIN_ISC_NAMESPACE
// BEGIN_RDATA_NAMESPACE
struct TLSAImpl {
// straightforward representation of TLSA RDATA fields
TLSAImpl(uint8_t certificate_usage, uint8_t selector,
uint8_t matching_type, const vector<uint8_t>& data) :
certificate_usage_(certificate_usage),
selector_(selector),
matching_type_(matching_type),
data_(data)
{}
uint8_t certificate_usage_;
uint8_t selector_;
uint8_t matching_type_;
const vector<uint8_t> data_;
};
// helper function for string and lexer constructors
TLSAImpl*
TLSA::constructFromLexer(MasterLexer& lexer) {
const uint32_t certificate_usage =
lexer.getNextToken(MasterToken::NUMBER).getNumber();
if (certificate_usage > 255) {
isc_throw(InvalidRdataText,
"TLSA certificate usage field out of range");
}
const uint32_t selector =
lexer.getNextToken(MasterToken::NUMBER).getNumber();
if (selector > 255) {
isc_throw(InvalidRdataText,
"TLSA selector field out of range");
}
const uint32_t matching_type =
lexer.getNextToken(MasterToken::NUMBER).getNumber();
if (matching_type > 255) {
isc_throw(InvalidRdataText,
"TLSA matching type field out of range");
}
std::string certificate_assoc_data;
std::string data_substr;
while (true) {
const MasterToken& token =
lexer.getNextToken(MasterToken::STRING, true);
if ((token.getType() == MasterToken::END_OF_FILE) ||
(token.getType() == MasterToken::END_OF_LINE)) {
break;
}
token.getString(data_substr);
certificate_assoc_data.append(data_substr);
}
lexer.ungetToken();
vector<uint8_t> data;
// If certificate association data is missing, it's OK. See the API
// documentation of the constructor.
if (certificate_assoc_data.size() > 0) {
try {
decodeHex(certificate_assoc_data, data);
} catch (const isc::BadValue& e) {
isc_throw(InvalidRdataText,
"Bad TLSA certificate association data: " << e.what());
}
}
return (new TLSAImpl(certificate_usage, selector, matching_type, data));
}
/// \brief Constructor from string.
///
/// The given string must represent a valid TLSA RDATA. There can be
/// extra space characters at the beginning or end of the text (which
/// are simply ignored), but other extra text, including a new line,
/// will make the construction fail with an exception.
///
/// The Certificate Usage, Selector and Matching Type fields must be
/// within their valid ranges, but are not constrained to the values
/// defined in RFC6698.
///
/// The Certificate Association Data Field field may be absent, but if
/// present it must contain a valid hex encoding of the data. Whitespace
/// is allowed in the hex text.
///
/// \throw InvalidRdataText if any fields are missing, out of their
/// valid ranges, or are incorrect, or Certificate Association Data is
/// not a valid hex string.
///
/// \param tlsa_str A string containing the RDATA to be created
TLSA::TLSA(const string& tlsa_str) :
impl_(NULL)
{
// We use auto_ptr here because if there is an exception in this
// constructor, the destructor is not called and there could be a
// leak of the TLSAImpl that constructFromLexer() returns.
std::auto_ptr<TLSAImpl> impl_ptr(NULL);
try {
std::istringstream ss(tlsa_str);
MasterLexer lexer;
lexer.pushSource(ss);
impl_ptr.reset(constructFromLexer(lexer));
if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
isc_throw(InvalidRdataText, "extra input text for TLSA: "
<< tlsa_str);
}
} catch (const MasterLexer::LexerError& ex) {
isc_throw(InvalidRdataText, "Failed to construct TLSA from '" <<
tlsa_str << "': " << ex.what());
}
impl_ = impl_ptr.release();
}
/// \brief Constructor with a context of MasterLexer.
///
/// The \c lexer should point to the beginning of valid textual representation
/// of an TLSA RDATA.
///
/// \throw MasterLexer::LexerError General parsing error such as missing field.
/// \throw InvalidRdataText Fields are out of their valid range, or are
/// incorrect, or Certificate Association Data is not a valid hex string.
///
/// \param lexer A \c MasterLexer object parsing a master file for the
/// RDATA to be created
TLSA::TLSA(MasterLexer& lexer, const Name*,
MasterLoader::Options, MasterLoaderCallbacks&) :
impl_(constructFromLexer(lexer))
{
}
/// \brief Constructor from InputBuffer.
///
/// The passed buffer must contain a valid TLSA RDATA.
///
/// The Certificate Usage, Selector and Matching Type fields must be
/// within their valid ranges, but are not constrained to the values
/// defined in RFC6698. It is okay for the certificate association data
/// to be missing (see the description of the constructor from string).
TLSA::TLSA(InputBuffer& buffer, size_t rdata_len) {
if (rdata_len < 3) {
isc_throw(InvalidRdataLength, "TLSA record too short");
}
const uint8_t certificate_usage = buffer.readUint8();
const uint8_t selector = buffer.readUint8();
const uint8_t matching_type = buffer.readUint8();
vector<uint8_t> data;
rdata_len -= 3;
if (rdata_len > 0) {
data.resize(rdata_len);
buffer.readData(&data[0], rdata_len);
}
impl_ = new TLSAImpl(certificate_usage, selector, matching_type, data);
}
TLSA::TLSA(uint8_t certificate_usage, uint8_t selector,
uint8_t matching_type, const std::string& certificate_assoc_data) :
impl_(NULL)
{
vector<uint8_t> data;
try {
decodeHex(certificate_assoc_data, data);
} catch (const isc::BadValue& e) {
isc_throw(InvalidRdataText,
"Bad TLSA certificate association data: " << e.what());
}
impl_ = new TLSAImpl(certificate_usage, selector, matching_type, data);
}
TLSA::TLSA(const TLSA& other) :
Rdata(), impl_(new TLSAImpl(*other.impl_))
{}
TLSA&
TLSA::operator=(const TLSA& source) {
if (this == &source) {
return (*this);
}
TLSAImpl* newimpl = new TLSAImpl(*source.impl_);
delete impl_;
impl_ = newimpl;
return (*this);
}
TLSA::~TLSA() {
delete impl_;
}
void
TLSA::toWire(OutputBuffer& buffer) const {
buffer.writeUint8(impl_->certificate_usage_);
buffer.writeUint8(impl_->selector_);
buffer.writeUint8(impl_->matching_type_);
if (!impl_->data_.empty()) {
buffer.writeData(&impl_->data_[0],
impl_->data_.size());
}
}
void
TLSA::toWire(AbstractMessageRenderer& renderer) const {
renderer.writeUint8(impl_->certificate_usage_);
renderer.writeUint8(impl_->selector_);
renderer.writeUint8(impl_->matching_type_);
if (!impl_->data_.empty()) {
renderer.writeData(&impl_->data_[0],
impl_->data_.size());
}
}
string
TLSA::toText() const {
return (lexical_cast<string>(static_cast<int>(impl_->certificate_usage_)) + " " +
lexical_cast<string>(static_cast<int>(impl_->selector_)) + " " +
lexical_cast<string>(static_cast<int>(impl_->matching_type_)) +
(impl_->data_.empty() ? "" :
" " + encodeHex(impl_->data_)));
}
int
TLSA::compare(const Rdata& other) const {
const TLSA& other_tlsa = dynamic_cast<const TLSA&>(other);
if (impl_->certificate_usage_ < other_tlsa.impl_->certificate_usage_) {
return (-1);
} else if (impl_->certificate_usage_ >
other_tlsa.impl_->certificate_usage_) {
return (1);
}
if (impl_->selector_ < other_tlsa.impl_->selector_) {
return (-1);
} else if (impl_->selector_ > other_tlsa.impl_->selector_) {
return (1);
}
if (impl_->matching_type_ < other_tlsa.impl_->matching_type_) {
return (-1);
} else if (impl_->matching_type_ >
other_tlsa.impl_->matching_type_) {
return (1);
}
const size_t this_len = impl_->data_.size();
const size_t other_len = other_tlsa.impl_->data_.size();
const size_t cmplen = min(this_len, other_len);
if (cmplen > 0) {
const int cmp = memcmp(&impl_->data_[0],
&other_tlsa.impl_->data_[0],
cmplen);
if (cmp != 0) {
return (cmp);
}
}
if (this_len == other_len) {
return (0);
} else if (this_len < other_len) {
return (-1);
} else {
return (1);
}
}
uint8_t
TLSA::getCertificateUsage() const {
return (impl_->certificate_usage_);
}
uint8_t
TLSA::getSelector() const {
return (impl_->selector_);
}
uint8_t
TLSA::getMatchingType() const {
return (impl_->matching_type_);
}
const std::vector<uint8_t>&
TLSA::getData() const {
return (impl_->data_);
}
size_t
TLSA::getDataLength() const {
return (impl_->data_.size());
}
// END_RDATA_NAMESPACE
// END_ISC_NAMESPACE
// Copyright (C) 2014 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.
// BEGIN_HEADER_GUARD
#include <stdint.h>
#include <dns/name.h>
#include <dns/rdata.h>
#include <string>
#include <vector>
// BEGIN_ISC_NAMESPACE
// BEGIN_COMMON_DECLARATIONS
// END_COMMON_DECLARATIONS
// BEGIN_RDATA_NAMESPACE
struct TLSAImpl;
class TLSA : public Rdata {
public:
// BEGIN_COMMON_MEMBERS
// END_COMMON_MEMBERS
TLSA(uint8_t certificate_usage, uint8_t selector,
uint8_t matching_type, const std::string& certificate_assoc_data);
TLSA& operator=(const TLSA& source);
~TLSA();
///
/// Specialized methods
///
uint8_t getCertificateUsage() const;
uint8_t getSelector() const;
uint8_t getMatchingType() const;
const std::vector<uint8_t>& getData() const;
size_t getDataLength() const;
private:
TLSAImpl* constructFromLexer(MasterLexer& lexer);
TLSAImpl* impl_;
};
// END_RDATA_NAMESPACE
// END_ISC_NAMESPACE
// END_HEADER_GUARD
// Local Variables:
// mode: c++
// End:
......@@ -59,6 +59,7 @@ run_unittests_SOURCES += rdata_nsec3param_like_unittest.cc
run_unittests_SOURCES += rdata_rrsig_unittest.cc
run_unittests_SOURCES += rdata_rp_unittest.cc
run_unittests_SOURCES += rdata_srv_unittest.cc
run_unittests_SOURCES += rdata_tlsa_unittest.cc
run_unittests_SOURCES += rdata_minfo_unittest.cc
run_unittests_SOURCES += rdata_tsig_unittest.cc
run_unittests_SOURCES += rdata_naptr_unittest.cc
......
// Copyright (C) 2014 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 <algorithm>
#include <string>
#include <util/buffer.h>
#include <dns/messagerenderer.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/rrclass.h>
#include <dns/rrtype.h>
#include <gtest/gtest.h>
#include <dns/tests/unittest_util.h>
#include <dns/tests/rdata_unittest.h>
#include <boost/algorithm/string.hpp>
using isc::UnitTestUtil;
using namespace std;
using namespace isc;
using namespace isc::dns;
using namespace isc::util;
using namespace isc::dns::rdata;
namespace {
class Rdata_TLSA_Test : public RdataTest {
protected:
Rdata_TLSA_Test() :
tlsa_txt("0 0 1 d2abde240d7cd3ee6b4b28c54df034b9"
"7983a1d16e8a410e4561cb106618e971"),
rdata_tlsa(tlsa_txt)
{}
void checkFromText_None(const string& rdata_str) {
checkFromText<generic::TLSA, isc::Exception, isc::Exception>(
rdata_str, rdata_tlsa, false, false);
}
void checkFromText_InvalidText(const string& rdata_str) {
checkFromText<generic::TLSA, InvalidRdataText, InvalidRdataText>(
rdata_str, rdata_tlsa, true, true);
}
void checkFromText_LexerError(const string& rdata_str) {
checkFromText
<generic::TLSA, InvalidRdataText, MasterLexer::LexerError>(
rdata_str, rdata_tlsa, true, true);
}
void checkFromText_BadString(const string& rdata_str) {
checkFromText
<generic::TLSA, InvalidRdataText, isc::Exception>(
rdata_str, rdata_tlsa, true, false);
}
const string tlsa_txt;
const generic::TLSA rdata_tlsa;
};
const uint8_t rdata_tlsa_wiredata[] = {
// certificate usage
0x00,
// selector
0x00,
// matching type
0x01,
// certificate association data
0xd2, 0xab, 0xde, 0x24, 0x0d, 0x7c, 0xd3, 0xee,
0x6b, 0x4b, 0x28, 0xc5, 0x4d, 0xf0, 0x34, 0xb9,
0x79, 0x83, 0xa1, 0xd1, 0x6e, 0x8a, 0x41, 0x0e,
0x45, 0x61, 0xcb, 0x10, 0x66, 0x18, 0xe9, 0x71
};
TEST_F(Rdata_TLSA_Test, createFromText) {
// Basic test
checkFromText_None(tlsa_txt);
// With different spacing
checkFromText_None("0 0 1 d2abde240d7cd3ee6b4b28c54df034b9"
"7983a1d16e8a410e4561cb106618e971");
// Combination of lowercase and uppercase
checkFromText_None("0 0 1 D2ABDE240D7CD3EE6B4B28C54DF034B9"
"7983a1d16e8a410e4561cb106618e971");
// spacing in the certificate association data field
checkFromText_None("0 0 1 d2abde240d7cd3ee6b4b28c54df034b9"
" 7983a1d16e8a410e4561cb106618e971");
// multi-line certificate association data field
checkFromText_None("0 0 1 ( d2abde240d7cd3ee6b4b28c54df034b9\n"
" 7983a1d16e8a410e4561cb106618e971 )");
// string constructor throws if there's extra text,
// but lexer constructor doesn't
checkFromText_BadString(tlsa_txt + "\n" + tlsa_txt);
}
TEST_F(Rdata_TLSA_Test, fields) {
// Some of these may not be RFC conformant, but we relax the check
// in our code to work with other field values that may show up in
// the future.
EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("1 0 1 12ab"));
EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("2 0 1 12ab"));
EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("3 0 1 12ab"));
EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("128 0 1 12ab"));
EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("255 0 1 12ab"));
EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 1 1 12ab"));
EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 2 1 12ab"));
EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 3 1 12ab"));
EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 128 1 12ab"));
EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 255 1 12ab"));
EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 0 1 12ab"));
EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 0 2 12ab"));
EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 0 3 12ab"));
EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 0 128 12ab"));
EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 0 255 12ab"));
// > 255 would be broken
EXPECT_THROW(const generic::TLSA rdata_tlsa("256 0 1 12ab"),
InvalidRdataText);
EXPECT_THROW(const generic::TLSA rdata_tlsa("0 256 1 12ab"),
InvalidRdataText);
EXPECT_THROW(const generic::TLSA rdata_tlsa("0 0 256 12ab"),
InvalidRdataText);
}
TEST_F(Rdata_TLSA_Test, badText) {
checkFromText_LexerError("1");
checkFromText_LexerError("ONE 2 3 123456789abcdef67890123456789abcdef67890");
checkFromText_LexerError("1 TWO 3 123456789abcdef67890123456789abcdef67890");
checkFromText_LexerError("1 2 THREE 123456789abcdef67890123456789abcdef67890");
checkFromText_InvalidText("1 2 3 BAABAABLACKSHEEP");
checkFromText_InvalidText(tlsa_txt + " extra text");
// yes, these are redundant to the last test cases in the .fields
// test
checkFromText_InvalidText(
"2345 1 2 123456789abcdef67890123456789abcdef67890");
checkFromText_InvalidText(
"3 1234 4 123456789abcdef67890123456789abcdef67890");
checkFromText_InvalidText(
"5 6 1234 123456789abcdef67890123456789abcdef67890");
// negative values are trapped in the lexer rather than the
// constructor
checkFromText_LexerError("-2 0 1 123456789abcdef67890123456789abcdef67890");
checkFromText_LexerError("0 -2 1 123456789abcdef67890123456789abcdef67890");
checkFromText_LexerError("0 0 -2 123456789abcdef67890123456789abcdef67890");
}
TEST_F(Rdata_TLSA_Test, copyAndAssign) {
// Copy construct
generic::TLSA rdata_tlsa2(rdata_tlsa);
EXPECT_EQ(0, rdata_tlsa.compare(rdata_tlsa2));
// Assignment, mainly to confirm it doesn't cause disruption.
rdata_tlsa2 = rdata_tlsa;
EXPECT_EQ(0, rdata_tlsa.compare(rdata_tlsa2));
}