Kea 2.5.8
isc::dns::TSIGContext Class Reference

TSIG session context. More...

#include <tsig.h>

+ Inheritance diagram for isc::dns::TSIGContext:

Classes

struct  TSIGContextImpl
 

Public Types

enum  State {
  INIT , SENT_REQUEST , RECEIVED_REQUEST , SENT_RESPONSE ,
  VERIFIED_RESPONSE
}
 Internal state of context. More...
 

Public Member Functions

Constructors and destructor
 TSIGContext (const TSIGKey &key)
 Constructor from a TSIG key.
 
 TSIGContext (const Name &key_name, const Name &algorithm_name, const TSIGKeyRing &keyring)
 Constructor from key parameters and key ring.
 
virtual ~TSIGContext ()
 The destructor.
 
virtual ConstTSIGRecordPtr sign (const uint16_t qid, const void *const data, const size_t data_len)
 Sign a DNS message.
 
virtual TSIGError verify (const TSIGRecord *const record, const void *const data, const size_t data_len)
 Verify a DNS message.
 
virtual bool lastHadSignature () const
 Check whether the last verified message was signed.
 
virtual size_t getTSIGLength () const
 Return the expected length of TSIG RR after sign()
 
virtual State getState () const
 Return the current state of the context.
 
virtual TSIGError getError () const
 Return the TSIG error as a result of the latest verification.
 

Protocol constants and defaults

static const uint16_t DEFAULT_FUDGE = 300
 The recommended fudge value (in seconds) by RFC2845.
 
void update (const void *const data, size_t len)
 Update internal HMAC state by more data.
 

Detailed Description

TSIG session context.

The 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 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, sign() and verify(). The 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 TSIGContext object. The 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 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 TSIGContext object with the information of the TSIG RR included in a query with a set of possible keys (in the form of a 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.

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 sign() or verify() work correctly in terms of the TSIG protocol.

Examples

This is a typical client application that sends a TSIG signed query and verifies the response.

// "renderer" is of MessageRenderer to render the message.
// (TSIGKey would be configured from config or command line in real app)
TSIGContext ctx(TSIGKey("key.example:MSG6Ng=="));
message.addQuestion(Question(Name("www.example.com"), RRClass::IN(),
RRType::A()));
message.toWire(renderer, ctx);
// sendto, then recvfrom. received result in (data, data_len)
message.clear(Message::PARSE);
InputBuffer buffer(data, data_len);
message.fromWire(buffer);
TSIGError tsig_error = ctx.verify(message.getTSIGRecord(),
data, data_len);
if (tsig_error == TSIGError::NOERROR()) {
// okay. ctx can be continuously used if it's receiving subsequent
// signed responses from a TCP stream.
} else if (message.getRcode() == Rcode::NOTAUTH()) {
// hard error. give up this transaction per RFC2845 4.6.
} else {
// Other error: discard response keep waiting with the same ctx
// for another (again, RFC2845 4.6).
}
The Message class encapsulates a standard DNS message.
Definition: message.h:152
The Name class encapsulates DNS names.
Definition: name.h:219
The Question class encapsulates the common search key of DNS lookup, consisting of owner name,...
Definition: question.h:88
static const RRClass & IN()
Definition: rrclass.h:304
static const RRType & A()
Definition: rrtype.h:279
static const Rcode & NOTAUTH()
A constant object for the NOTAUTH Rcode (see Rcode::NOTAUTH_CODE).
Definition: rcode.h:282
TSIG session context.
Definition: tsig.h:171
TSIG errors.
Definition: tsigerror.h:22
static const TSIGError & NOERROR()
A constant TSIG error object derived from Rcode::NOERROR()
Definition: tsigerror.h:227

And this is a typical server application that authenticates a signed query and returns a response according to the result.

// Assume "message" is of type Message for query handling and
// "renderer" is of MessageRenderer to render responses.
TSIGKeyRing keyring; // this must be configured with keys somewhere
// Receive a query and store it in (data, data_len)
InputBuffer buffer(data, data_len);
message.clear(Message::PARSE);
message.fromWire(buffer);
const TSIGRecord* tsig = message.getTSIGRecord();
if (tsig) {
TSIGContext ctx(tsig->getName(), tsig->getRdata().getAlgorithm(),
keyring);
ctx.verify(tsig, data, data_len);
// prepare response
message.makeResponse();
//...
message.toWire(renderer, ctx);
// send the response data back to the client.
// If this is a beginning of a signed session over a TCP and
// server has more data to send to the client, this ctx
// will be used to sign subsequent messages.
}
A simple repository of a set of TSIGKey objects.
Definition: tsigkey.h:245
TSIG resource record.
Definition: tsigrecord.h:51
const Name & getName() const
Return the owner name of the TSIG RR, which is the TSIG key name.
Definition: tsigrecord.h:168
const rdata::any::TSIG & getRdata() const
Return the RDATA of the TSIG RR.
Definition: tsigrecord.h:175
const Name & getAlgorithm() const
Return the algorithm name.

TCP Consideration

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 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.

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.

Note to developers: One basic design choice is to make the TSIGContext class is as independent from the 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 TSIGContext class depends on 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 sign() that takes opaque data (instead of, e.g., a Message or MessageRenderer object) is therefore a deliberate design decision.

Definition at line 171 of file tsig.h.

Member Enumeration Documentation

◆ State

Internal state of context.

The constants of this enum type define a specific state of TSIGContext to adjust the behavior. The definition is public and the state can be seen via the 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.

Enumerator
INIT 

Initial state.

SENT_REQUEST 

Client sent a signed request, waiting response.

RECEIVED_REQUEST 

Server received a signed request.

SENT_RESPONSE 

Server sent a signed response.

VERIFIED_RESPONSE 

Client successfully verified a response.

Definition at line 181 of file tsig.h.

Constructor & Destructor Documentation

◆ TSIGContext() [1/2]

isc::dns::TSIGContext::TSIGContext ( const TSIGKey key)
explicit

Constructor from a TSIG key.

Exceptions
std::bad_allocResource allocation for internal data fails
Parameters
keyThe TSIG key to be used for TSIG sessions with this context.

Definition at line 264 of file tsig.cc.

◆ TSIGContext() [2/2]

isc::dns::TSIGContext::TSIGContext ( const Name key_name,
const Name algorithm_name,
const TSIGKeyRing keyring 
)

Constructor from key parameters and key ring.

Definition at line 267 of file tsig.cc.

References isc::dns::TSIGError::BAD_KEY(), isc::dns::TSIGKeyRing::FindResult::code, isc::dns::TSIGKeyRing::find(), isc::dns::TSIGKeyRing::FindResult::key, and isc::dns::TSIGKeyRing::NOTFOUND.

+ Here is the call graph for this function:

◆ ~TSIGContext()

isc::dns::TSIGContext::~TSIGContext ( )
virtual

The destructor.

Definition at line 283 of file tsig.cc.

Member Function Documentation

◆ getError()

TSIGError isc::dns::TSIGContext::getError ( ) const
virtual

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.

Exceptions
None

Definition at line 331 of file tsig.cc.

◆ getState()

TSIGContext::State isc::dns::TSIGContext::getState ( ) const
virtual

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.
Exceptions
None

Definition at line 326 of file tsig.cc.

◆ getTSIGLength()

size_t isc::dns::TSIGContext::getTSIGLength ( ) const
virtual

Return the expected length of TSIG RR after sign()

This method returns the length of the TSIG RR that would be produced as a result of sign() with the state of the context at the time of the call. The expected length can be decided from the key and the algorithm (which determines the MAC size if included) and the recorded TSIG error. Specifically, if a key related error has been identified, the MAC will be excluded; if a time error has occurred, the TSIG will include "other data".

This method is provided mainly for the convenience of the Message class, which needs to know the expected TSIG length in rendering a signed DNS message so that it can handle truncated messages with TSIG correctly. Normal applications wouldn't need this method. The Python binding for this method won't be provided for the same reason.

Exceptions
None
Returns
The expected TSIG RR length in bytes

Definition at line 287 of file tsig.cc.

References isc::dns::TSIGError::BAD_KEY(), isc::dns::TSIGError::BAD_SIG(), and isc::dns::TSIGError::BAD_TIME().

Referenced by isc::dns::MessageImpl::toWire().

+ Here is the call graph for this function:

◆ lastHadSignature()

bool isc::dns::TSIGContext::lastHadSignature ( ) const
virtual

Check whether the last verified message was signed.

RFC2845 allows for some of the messages not to be signed. However, the last message must be signed and the class has no knowledge if a given message is the last one, therefore it can't check directly.

It is up to the caller to check if the last verified message was signed after all are verified by calling this function.

Returns
If the last message was signed or not.
Exceptions
TSIGContextErrorif no message was verified yet.

Definition at line 558 of file tsig.cc.

References isc_throw.

◆ sign()

ConstTSIGRecordPtr isc::dns::TSIGContext::sign ( const uint16_t  qid,
const void *const  data,
const size_t  data_len 
)
virtual

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 TSIGContext, and returns a result in the form of a (pointer object pointing to) 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.

In general, this method is called once by a client to send a signed request or one more times by a server to sign response(s) to a signed request. To avoid allowing accidental misuse, if this method is called after a "client" validates a response, an exception of class TSIGContextError will be thrown.

Note
Normal applications are not expected to call this method directly; they will usually use the Message::toWire() method with a TSIGContext object being a parameter and have the 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 qid parameter, which will be used as the original ID of the resulting 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 (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 TSIGContext won't be modified.

Exceptions
TSIGContextErrorContext already verified a response.
InvalidParameterdata is 0 or data_len is 0
cryptolink::LibraryErrorSome unexpected error in the underlying crypto operation
std::bad_allocTemporary resource allocation failure
Parameters
qidThe QID to be as the value of the original ID field of the resulting TSIG record
dataPoints to the wire-format data to be signed
data_lenThe length of data in bytes
Returns
A TSIG record for the given data along with the context.

Definition at line 336 of file tsig.cc.

References isc::dns::TSIGError::BAD_KEY(), isc::dns::TSIGError::BAD_SIG(), isc::dns::TSIGError::BAD_TIME(), DEFAULT_FUDGE, isc::cryptolink::digest(), isc::dns::TSIGRecord::getClass(), isc::dns::RRClass::getCode(), isc::util::OutputBuffer::getData(), INIT, isc_throw, isc_throw_assert, isc::dns::TSIGError::NOERROR(), RECEIVED_REQUEST, SENT_REQUEST, SENT_RESPONSE, isc::dns::TSIGRecord::TSIG_TTL, VERIFIED_RESPONSE, isc::util::OutputBuffer::writeUint16(), and isc::util::OutputBuffer::writeUint32().

Referenced by isc::dns::MessageImpl::toWire().

+ Here is the call graph for this function:

◆ update()

void isc::dns::TSIGContext::update ( const void *const  data,
size_t  len 
)
protected

Update internal HMAC state by more data.

This is used mostly internally, when we need to verify a message without TSIG signature in the middle of signed TCP stream. However, it is also used in tests, so it's protected instead of private, to allow tests in.

It doesn't contain sanity checks, and it is not tested directly. But we may want to add these one day to allow generating the skipped TSIG messages too. Until then, do not use this method.

Definition at line 566 of file tsig.cc.

Referenced by verify().

◆ verify()

TSIGError isc::dns::TSIGContext::verify ( const TSIGRecord *const  record,
const void *const  data,
const size_t  data_len 
)
virtual

Verify a DNS message.

This method verifies given data along with the context and a given TSIG in the form of a TSIGRecord object. The data to be verified is generally expected to be a complete, wire-format DNS message, exactly as received by the host, and ending with a TSIG RR. After verification process this method updates its internal state, and returns the result in the form of a TSIGError object. Possible return values are (see the TSIGError class description for the mnemonics):

  • NOERROR: The data has been verified correctly.
  • FORMERR: TSIGRecord is not given (see below).
  • BAD_KEY: Appropriate key is not found or specified key doesn't match for the data.
  • BAD_TIME: The current time doesn't fall in the range specified in the TSIG.
  • BAD_SIG: The signature given in the TSIG doesn't match against the locally computed digest or is the signature is invalid in other way.
  • BAD_MODE: Not yet implemented TKEY error
  • BAD_NAME: Not yet implemented TKEY error
  • BAD_ALG: Not yet implemented TKEY error
  • BAD_TRUNC: The signature or truncated signature length is too small.

If this method is called by a DNS client waiting for a signed response and the result is not NOERROR, the context can be used to try validating another signed message as described in RFC2845 Section 4.6.

If this method is called by a DNS server that tries to authenticate a signed request, and if the result is not NOERROR, the corresponding error condition is recorded in the context so that the server can return a response indicating what was wrong by calling sign() with the updated context.

In general, this method is called once by a server for authenticating a signed request or one more times by a client to validate signed response(s) to a signed request. To avoid allowing accidental misuse, if this method is called after a "server" signs a response, an exception of class TSIGContextError will be thrown.

The record parameter can be 0; in that case this method simply returns FORMERR as the case described in Section 4.6 of RFC2845, i.e., receiving an unsigned response to a signed request. This way a client can transparently pass the result of Message::getTSIGRecord() without checking whether it isn't 0 and take an appropriate action based on the result of this method.

This method handles the given data mostly as opaque. It digests the data assuming it begins with a DNS header and ends with a TSIG RR whose length is given by calling TSIGRecord::getLength() on record, but otherwise it doesn't parse the data to confirm the assumption. It's caller's responsibility to ensure the data is valid and consistent with record. To avoid disruption, this method performs minimal validation on the given data and record: data must not be 0; data_len must not be smaller than the sum of the DNS header length (fixed, 12 octets) and the length of the TSIG RR. If this check fails it throws an InvalidParameter exception.

One unexpected case that is not covered by this method is that a client receives a signed response to an unsigned request. RFC2845 is silent about such cases; BIND 9 explicitly identifies the case and rejects it. With this implementation, the client can know that the response contains a TSIG via the result of Message::getTSIGRecord() and that it is an unexpected TSIG due to the fact that it doesn't have a corresponding TSIGContext. It's up to the client implementation whether to react to such a case explicitly (for example, it could either ignore the TSIG and accept the response or drop it).

This method provides the strong exception guarantee; unless the method returns (without an exception being thrown), the internal state of the TSIGContext won't be modified.

Todo:
Signature truncation support based on RFC4635
Exceptions
TSIGContextErrorContext already signed a response.
InvalidParameterdata is 0 or data_len is too small.
Parameters
recordThe TSIGRecord to be verified with data
dataPoints to the wire-format data (exactly as received) to be verified
data_lenThe length of data in bytes
Returns
The TSIGError that indicates verification result

Definition at line 419 of file tsig.cc.

References isc::dns::TSIGError::BAD_KEY(), isc::dns::TSIGError::BAD_SIG(), isc::dns::TSIGError::BAD_TIME(), isc::dns::TSIGError::BAD_TRUNC(), DEFAULT_FUDGE, isc::cryptolink::digest(), isc::dns::TSIGError::FORMERR(), isc::dns::rdata::any::TSIG::getAlgorithm(), isc::dns::TSIGRecord::getClass(), isc::dns::rdata::any::TSIG::getError(), isc::dns::rdata::any::TSIG::getFudge(), isc::dns::TSIGRecord::getLength(), isc::dns::rdata::any::TSIG::getMAC(), isc::dns::rdata::any::TSIG::getMACSize(), isc::dns::TSIGRecord::getName(), isc::dns::rdata::any::TSIG::getOriginalID(), isc::dns::rdata::any::TSIG::getOtherData(), isc::dns::rdata::any::TSIG::getOtherLen(), isc::dns::TSIGRecord::getRdata(), isc::dns::rdata::any::TSIG::getTimeSigned(), INIT, isc_throw, isc::dns::TSIGError::NOERROR(), SENT_RESPONSE, isc::dns::TSIGRecord::TSIG_TTL, update(), and VERIFIED_RESPONSE.

Referenced by isc::d2::D2UpdateMessage::fromWire().

+ Here is the call graph for this function:

Member Data Documentation

◆ DEFAULT_FUDGE

const uint16_t isc::dns::TSIGContext::DEFAULT_FUDGE = 300
static

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.

Definition at line 414 of file tsig.h.

Referenced by sign(), and verify().


The documentation for this class was generated from the following files: