LCOV - code coverage report
Current view: top level - dns - tsig.h (source / functions) Hit Total Coverage
Test: report.info Lines: 2 2 100.0 %
Date: 2012-05-15 Functions: 1 2 50.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 2 4 50.0 %

           Branch data     Line data    Source code
       1                 :            : // Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
       2                 :            : //
       3                 :            : // Permission to use, copy, modify, and/or distribute this software for any
       4                 :            : // purpose with or without fee is hereby granted, provided that the above
       5                 :            : // copyright notice and this permission notice appear in all copies.
       6                 :            : //
       7                 :            : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
       8                 :            : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
       9                 :            : // AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
      10                 :            : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
      11                 :            : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
      12                 :            : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
      13                 :            : // PERFORMANCE OF THIS SOFTWARE.
      14                 :            : 
      15                 :            : #ifndef __TSIG_H
      16                 :            : #define __TSIG_H 1
      17                 :            : 
      18                 :            : #include <boost/noncopyable.hpp>
      19                 :            : 
      20                 :            : #include <exceptions/exceptions.h>
      21                 :            : 
      22                 :            : #include <dns/tsigerror.h>
      23                 :            : #include <dns/tsigkey.h>
      24                 :            : #include <dns/tsigrecord.h>
      25                 :            : 
      26                 :            : namespace isc {
      27                 :            : namespace dns {
      28                 :            : 
      29                 :            : /// An exception that is thrown for logic errors identified in TSIG
      30                 :            : /// sign/verify operations.
      31                 :            : ///
      32                 :            : /// Note that this exception is not thrown for TSIG protocol errors such as
      33                 :            : /// verification failures.  In general, this exception indicates an internal
      34                 :            : /// program bug.
      35                 :          4 : class TSIGContextError : public isc::Exception {
      36                 :            : public:
      37                 :            :     TSIGContextError(const char* file, size_t line, const char* what) :
      38 [ +  - ][ +  - ]:          4 :         isc::Exception(file, line, what) {}
      39                 :            : };
      40                 :            : 
      41                 :            : /// TSIG session context.
      42                 :            : ///
      43                 :            : /// The \c TSIGContext class maintains a context of a signed session of
      44                 :            : /// DNS transactions by TSIG.  In many cases a TSIG signed session consists
      45                 :            : /// of a single set of request (e.g. normal query) and reply (e.g. normal
      46                 :            : /// response), where the request is initially signed by the client, and the
      47                 :            : /// reply is signed by the server using the initial signature.  As mentioned
      48                 :            : /// in RFC2845, a session can consist of multiple exchanges in a TCP
      49                 :            : /// connection.  As also mentioned in the RFC, an AXFR response often contains
      50                 :            : /// multiple DNS messages, which can belong to the same TSIG session.
      51                 :            : /// This class supports all these cases.
      52                 :            : ///
      53                 :            : /// A \c TSIGContext object is generally constructed with a TSIG key to be
      54                 :            : /// used for the session, and keeps track of various kinds of session specific
      55                 :            : /// information, such as the original digest while waiting for a response or
      56                 :            : /// verification error information that is to be used for a subsequent
      57                 :            : /// response.
      58                 :            : ///
      59                 :            : /// This class has two main methods, \c sign() and \c verify().
      60                 :            : /// The \c sign() method signs given data (which is supposed to be a complete
      61                 :            : /// DNS message without the TSIG itself) using the TSIG key and other
      62                 :            : /// related information associated with the \c TSIGContext object.
      63                 :            : /// The \c verify() method verifies a given DNS message that contains a TSIG
      64                 :            : /// RR using the key and other internal information.
      65                 :            : ///
      66                 :            : /// In general, a DNS client that wants to send a signed query will construct
      67                 :            : /// a \c TSIGContext object with the TSIG key that the client is intending to
      68                 :            : /// use, and sign the query with the context.  The client will keeps the
      69                 :            : /// context, and verify the response with it.
      70                 :            : ///
      71                 :            : /// On the other hand, a DNS server will construct a \c TSIGContext object
      72                 :            : /// with the information of the TSIG RR included in a query with a set of
      73                 :            : /// possible keys (in the form of a \c TSIGKeyRing object).  The constructor
      74                 :            : /// in this mode will identify the appropriate TSIG key (or internally record
      75                 :            : /// an error if it doesn't find a key).  The server will then verify the
      76                 :            : /// query with the context, and generate a signed response using the same
      77                 :            : /// same context.
      78                 :            : ///
      79                 :            : /// When multiple messages belong to the same TSIG session, either side
      80                 :            : /// (signer or verifier) will keep using the same context.  It records
      81                 :            : /// the latest session state (such as the previous digest) so that repeated
      82                 :            : /// calls to \c sign() or \c verify() work correctly in terms of the TSIG
      83                 :            : /// protocol.
      84                 :            : ///
      85                 :            : /// \b Examples
      86                 :            : ///
      87                 :            : /// This is a typical client application that sends a TSIG signed query
      88                 :            : /// and verifies the response.
      89                 :            : ///
      90                 :            : /// \code
      91                 :            : ///    // "renderer" is of MessageRenderer to render the message.
      92                 :            : ///    // (TSIGKey would be configured from config or command line in real app)
      93                 :            : ///    TSIGContext ctx(TSIGKey("key.example:MSG6Ng=="));
      94                 :            : ///    Message message(Message::RENDER);
      95                 :            : ///    message.addQuestion(Question(Name("www.example.com"), RRClass::IN(),
      96                 :            : ///                                 RRType::A()));
      97                 :            : ///    message.toWire(renderer, ctx);
      98                 :            : ///
      99                 :            : ///    // sendto, then recvfrom.  received result in (data, data_len)
     100                 :            : ///
     101                 :            : ///    message.clear(Message::PARSE);
     102                 :            : ///    InputBuffer buffer(data, data_len);
     103                 :            : ///    message.fromWire(buffer);
     104                 :            : ///    TSIGError tsig_error = ctx.verify(message.getTSIGRecord(),
     105                 :            : ///                                      data, data_len);
     106                 :            : ///    if (tsig_error == TSIGError::NOERROR()) {
     107                 :            : ///        // okay.  ctx can be continuously used if it's receiving subsequent
     108                 :            : ///        // signed responses from a TCP stream.
     109                 :            : ///    } else if (message.getRcode() == Rcode::NOTAUTH()) {
     110                 :            : ///        // hard error.  give up this transaction per RFC2845 4.6.
     111                 :            : ///    } else {
     112                 :            : ///        // Other error: discard response keep waiting with the same ctx
     113                 :            : ///        // for another (again, RFC2845 4.6).
     114                 :            : ///    } \endcode
     115                 :            : ///
     116                 :            : /// And this is a typical server application that authenticates a signed
     117                 :            : /// query and returns a response according to the result.
     118                 :            : ///
     119                 :            : /// \code
     120                 :            : ///    // Assume "message" is of type Message for query handling and
     121                 :            : ///    // "renderer" is of MessageRenderer to render responses.
     122                 :            : ///    Message message(Message::RENDER);
     123                 :            : ///
     124                 :            : ///    TSIGKeyRing keyring; // this must be configured with keys somewhere
     125                 :            : ///
     126                 :            : ///    // Receive a query and store it in (data, data_len)
     127                 :            : ///    InputBuffer buffer(data, data_len);
     128                 :            : ///    message.clear(Message::PARSE);
     129                 :            : ///    message.fromWire(buffer);
     130                 :            : ///
     131                 :            : ///    const TSIGRecord* tsig = message.getTSIGRecord();
     132                 :            : ///    if (tsig != NULL) {
     133                 :            : ///        TSIGContext ctx(tsig->getName(), tsig->getRdata().getAlgorithm(),
     134                 :            : ///                        keyring);
     135                 :            : ///        ctx.verify(tsig, data, data_len);
     136                 :            : ///
     137                 :            : ///        // prepare response
     138                 :            : ///        message.makeResponse();
     139                 :            : ///        //...
     140                 :            : ///        message.toWire(renderer, ctx);
     141                 :            : ///
     142                 :            : ///        // send the response data back to the client.
     143                 :            : ///        // If this is a beginning of a signed session over a TCP and
     144                 :            : ///        // server has more data to send to the client, this ctx
     145                 :            : ///        // will be used to sign subsequent messages.
     146                 :            : ///    } \endcode
     147                 :            : ///
     148                 :            : /// <b>TCP Consideration</b>
     149                 :            : ///
     150                 :            : /// RFC2845 describes the case where a single TSIG session is used for
     151                 :            : /// multiple DNS messages (Section 4.4).  This class supports signing and
     152                 :            : /// verifying the messages in this scenario, but does not care if the messages
     153                 :            : /// were delivered over a TCP connection or not.  If, for example, the
     154                 :            : /// same \c TSIGContext object is used to sign two independent DNS queries
     155                 :            : /// sent over UDP, they will be considered to belong to the same TSIG
     156                 :            : /// session, and, as a result, verification will be likely to fail.
     157                 :            : ///
     158                 :            : /// \b Copyability
     159                 :            : ///
     160                 :            : /// This class is currently non copyable based on the observation of the
     161                 :            : /// typical usage as described above.  But there is no strong technical
     162                 :            : /// reason why this class cannot be copyable.  If we see the need for it
     163                 :            : /// in future we may change the implementation on this point.
     164                 :            : ///
     165                 :            : /// <b>Note to developers:</b>
     166                 :            : /// One basic design choice is to make the \c TSIGContext class is as
     167                 :            : /// independent from the \c Message class.  This is because the latter is
     168                 :            : /// much more complicated, depending on many other classes, while TSIG is
     169                 :            : /// a very specific part of the entire DNS protocol set.  If the \c TSIGContext
     170                 :            : /// class depends on \c \c Message, it will be more vulnerable to changes
     171                 :            : /// to other classes, and will be more difficult to test due to the
     172                 :            : /// direct or indirect dependencies.  The interface of \c sign() that takes
     173                 :            : /// opaque data (instead of, e.g., a \c Message or \c MessageRenderer object)
     174                 :            : /// is therefore a deliberate design decision.
     175                 :            : class TSIGContext : boost::noncopyable {
     176                 :            : public:
     177                 :            :     /// Internal state of context
     178                 :            :     ///
     179                 :            :     /// The constants of this enum type define a specific state of
     180                 :            :     /// \c TSIGContext to adjust the behavior.  The definition is public
     181                 :            :     /// and the state can be seen via the \c getState() method, but this is
     182                 :            :     /// mostly private information.  It's publicly visible mainly for testing
     183                 :            :     /// purposes; there is no API for the application to change the state
     184                 :            :     /// directly.
     185                 :            :     enum State {
     186                 :            :         INIT,                   ///< Initial state
     187                 :            :         SENT_REQUEST, ///< Client sent a signed request, waiting response
     188                 :            :         RECEIVED_REQUEST,       ///< Server received a signed request
     189                 :            :         SENT_RESPONSE,          ///< Server sent a signed response
     190                 :            :         VERIFIED_RESPONSE       ///< Client successfully verified a response
     191                 :            :     };
     192                 :            : 
     193                 :            :     /// \name Constructors and destructor
     194                 :            :     ///
     195                 :            :     //@{
     196                 :            :     /// Constructor from a TSIG key.
     197                 :            :     ///
     198                 :            :     /// \exception std::bad_alloc Resource allocation for internal data fails
     199                 :            :     ///
     200                 :            :     /// \param key The TSIG key to be used for TSIG sessions with this context.
     201                 :            :     explicit TSIGContext(const TSIGKey& key);
     202                 :            : 
     203                 :            :     /// Constructor from key parameters and key ring.
     204                 :            :     TSIGContext(const Name& key_name, const Name& algorithm_name,
     205                 :            :                 const TSIGKeyRing& keyring);
     206                 :            : 
     207                 :            :     /// The destructor.
     208                 :            :     ~TSIGContext();
     209                 :            :     //@}
     210                 :            : 
     211                 :            :     /// Sign a DNS message.
     212                 :            :     ///
     213                 :            :     /// This method computes the TSIG MAC for the given data, which is
     214                 :            :     /// generally expected to be a complete, wire-format DNS message
     215                 :            :     /// that doesn't contain a TSIG RR, based on the TSIG key and
     216                 :            :     /// other context information of \c TSIGContext, and returns a
     217                 :            :     /// result in the form of a (pointer object pointing to)
     218                 :            :     /// \c TSIGRecord object.
     219                 :            :     ///
     220                 :            :     /// The caller of this method will use the returned value to render a
     221                 :            :     /// complete TSIG RR into the message that has been signed so that it
     222                 :            :     /// will become a complete TSIG-signed message.
     223                 :            :     ///
     224                 :            :     /// In general, this method is called once by a client to send a
     225                 :            :     /// signed request or one more times by a server to sign
     226                 :            :     /// response(s) to a signed request.  To avoid allowing accidental
     227                 :            :     /// misuse, if this method is called after a "client" validates a
     228                 :            :     /// response, an exception of class \c TSIGContextError will be
     229                 :            :     /// thrown.
     230                 :            :     ///
     231                 :            :     /// \note Normal applications are not expected to call this method
     232                 :            :     /// directly; they will usually use the \c Message::toWire() method
     233                 :            :     /// with a \c TSIGContext object being a parameter and have the
     234                 :            :     /// \c Message class create a complete signed message.
     235                 :            :     ///
     236                 :            :     /// This method treats the given data as opaque, even though it's generally
     237                 :            :     /// expected to represent a wire-format DNS message (see also the class
     238                 :            :     /// description), and doesn't inspect it in any way.  For example, it
     239                 :            :     /// doesn't check whether the data length is sane for a valid DNS message.
     240                 :            :     /// This is also the reason why this method takes the \c qid parameter,
     241                 :            :     /// which will be used as the original ID of the resulting
     242                 :            :     /// \c TSIGRecordx object, even though this value should be stored in the
     243                 :            :     /// first two octets (in wire format) of the given data.
     244                 :            :     ///
     245                 :            :     /// \note This method still checks and rejects empty data (\c NULL pointer
     246                 :            :     /// data or the specified data length is 0) in order to avoid catastrophic
     247                 :            :     /// effect such as program crash.  Empty data is not necessarily invalid
     248                 :            :     /// for HMAC computation, but obviously it doesn't make sense for a DNS
     249                 :            :     /// message.
     250                 :            :     ///
     251                 :            :     /// This method provides the strong exception guarantee; unless the method
     252                 :            :     /// returns (without an exception being thrown), the internal state of
     253                 :            :     /// the \c TSIGContext won't be modified.
     254                 :            :     ///
     255                 :            :     /// \exception TSIGContextError Context already verified a response.
     256                 :            :     /// \exception InvalidParameter \c data is NULL or \c data_len is 0
     257                 :            :     /// \exception cryptolink::LibraryError Some unexpected error in the
     258                 :            :     /// underlying crypto operation
     259                 :            :     /// \exception std::bad_alloc Temporary resource allocation failure
     260                 :            :     ///
     261                 :            :     /// \param qid The QID to be as the value of the original ID field of
     262                 :            :     /// the resulting TSIG record
     263                 :            :     /// \param data Points to the wire-format data to be signed
     264                 :            :     /// \param data_len The length of \c data in bytes
     265                 :            :     ///
     266                 :            :     /// \return A TSIG record for the given data along with the context.
     267                 :            :     ConstTSIGRecordPtr sign(const uint16_t qid, const void* const data,
     268                 :            :                             const size_t data_len);
     269                 :            : 
     270                 :            :     /// Verify a DNS message.
     271                 :            :     ///
     272                 :            :     /// This method verifies given data along with the context and a given
     273                 :            :     /// TSIG in the form of a \c TSIGRecord object.  The data to be verified
     274                 :            :     /// is generally expected to be a complete, wire-format DNS message,
     275                 :            :     /// exactly as received by the host, and ending with a TSIG RR.
     276                 :            :     /// After verification process this method updates its internal state,
     277                 :            :     /// and returns the result in the form of a \c TSIGError object.
     278                 :            :     /// Possible return values are (see the \c TSIGError class description
     279                 :            :     /// for the mnemonics):
     280                 :            :     ///
     281                 :            :     /// - \c NOERROR: The data has been verified correctly.
     282                 :            :     /// - \c FORMERR: \c TSIGRecord is not given (see below).
     283                 :            :     /// - \c BAD_KEY: Appropriate key is not found or specified key doesn't
     284                 :            :     ///               match for the data.
     285                 :            :     /// - \c BAD_TIME: The current time doesn't fall in the range specified
     286                 :            :     ///                in the TSIG.
     287                 :            :     /// - \c BAD_SIG: The signature given in the TSIG doesn't match against
     288                 :            :     ///               the locally computed digest or is the signature is
     289                 :            :     ///               invalid in other way.
     290                 :            :     ///
     291                 :            :     /// If this method is called by a DNS client waiting for a signed
     292                 :            :     /// response and the result is not \c NOERROR, the context can be used
     293                 :            :     /// to try validating another signed message as described in RFC2845
     294                 :            :     /// Section 4.6.
     295                 :            :     ///
     296                 :            :     /// If this method is called by a DNS server that tries to authenticate
     297                 :            :     /// a signed request, and if the result is not \c NOERROR, the
     298                 :            :     /// corresponding error condition is recorded in the context so that
     299                 :            :     /// the server can return a response indicating what was wrong by calling
     300                 :            :     /// \c sign() with the updated context.
     301                 :            :     ///
     302                 :            :     /// In general, this method is called once by a server for
     303                 :            :     /// authenticating a signed request or one more times by a client to
     304                 :            :     /// validate signed response(s) to a signed request.  To avoid allowing
     305                 :            :     /// accidental misuse, if this method is called after a "server" signs
     306                 :            :     /// a response, an exception of class \c TSIGContextError will be thrown.
     307                 :            :     ///
     308                 :            :     /// The \c record parameter can be NULL; in that case this method simply
     309                 :            :     /// returns \c FORMERR as the case described in Section 4.6 of RFC2845,
     310                 :            :     /// i.e., receiving an unsigned response to a signed request.  This way
     311                 :            :     /// a client can transparently pass the result of
     312                 :            :     /// \c Message::getTSIGRecord() without checking whether it's non NULL
     313                 :            :     /// and take an appropriate action based on the result of this method.
     314                 :            :     ///
     315                 :            :     /// This method handles the given data mostly as opaque.  It digests
     316                 :            :     /// the data assuming it begins with a DNS header and ends with a TSIG
     317                 :            :     /// RR whose length is given by calling \c TSIGRecord::getLength() on
     318                 :            :     /// \c record, but otherwise it doesn't parse the data to confirm the
     319                 :            :     /// assumption.  It's caller's responsibility to ensure the data is
     320                 :            :     /// valid and consistent with \c record.  To avoid disruption, this
     321                 :            :     /// method performs minimal validation on the given \c data and \c record:
     322                 :            :     /// \c data must not be NULL; \c data_len must not be smaller than the
     323                 :            :     /// sum of the DNS header length (fixed, 12 octets) and the length of
     324                 :            :     /// the TSIG RR.  If this check fails it throws an \c InvalidParameter
     325                 :            :     /// exception.
     326                 :            :     ///
     327                 :            :     /// One unexpected case that is not covered by this method is that a
     328                 :            :     /// client receives a signed response to an unsigned request.  RFC2845 is
     329                 :            :     /// silent about such cases; BIND 9 explicitly identifies the case and
     330                 :            :     /// rejects it.  With this implementation, the client can know that the
     331                 :            :     /// response contains a TSIG via the result of
     332                 :            :     /// \c Message::getTSIGRecord() and that it is an unexpected TSIG due to
     333                 :            :     /// the fact that it doesn't have a corresponding \c TSIGContext.
     334                 :            :     /// It's up to the client implementation whether to react to such a case
     335                 :            :     /// explicitly (for example, it could either ignore the TSIG and accept
     336                 :            :     /// the response or drop it).
     337                 :            :     ///
     338                 :            :     /// This method provides the strong exception guarantee; unless the method
     339                 :            :     /// returns (without an exception being thrown), the internal state of
     340                 :            :     /// the \c TSIGContext won't be modified.
     341                 :            :     ///
     342                 :            :     /// \todo Support intermediate TCP DNS messages without TSIG (RFC2845 4.4)
     343                 :            :     /// \todo Signature truncation support based on RFC4635
     344                 :            :     ///
     345                 :            :     /// \exception TSIGContextError Context already signed a response.
     346                 :            :     /// \exception InvalidParameter \c data is NULL or \c data_len is too small.
     347                 :            :     ///
     348                 :            :     /// \param record The \c TSIGRecord to be verified with \c data
     349                 :            :     /// \param data Points to the wire-format data (exactly as received) to
     350                 :            :     /// be verified
     351                 :            :     /// \param data_len The length of \c data in bytes
     352                 :            :     /// \return The \c TSIGError that indicates verification result
     353                 :            :     TSIGError verify(const TSIGRecord* const record, const void* const data,
     354                 :            :                      const size_t data_len);
     355                 :            : 
     356                 :            :     /// Return the expected length of TSIG RR after \c sign()
     357                 :            :     ///
     358                 :            :     /// This method returns the length of the TSIG RR that would be
     359                 :            :     /// produced as a result of \c sign() with the state of the context
     360                 :            :     /// at the time of the call.  The expected length can be decided
     361                 :            :     /// from the key and the algorithm (which determines the MAC size if
     362                 :            :     /// included) and the recorded TSIG error.  Specifically, if a key
     363                 :            :     /// related error has been identified, the MAC will be excluded; if
     364                 :            :     /// a time error has occurred, the TSIG will include "other data".
     365                 :            :     ///
     366                 :            :     /// This method is provided mainly for the convenience of the Message
     367                 :            :     /// class, which needs to know the expected TSIG length in rendering a
     368                 :            :     /// signed DNS message so that it can handle truncated messages with TSIG
     369                 :            :     /// correctly.  Normal applications wouldn't need this method.  The Python
     370                 :            :     /// binding for this method won't be provided for the same reason.
     371                 :            :     ///
     372                 :            :     /// \exception None
     373                 :            :     ///
     374                 :            :     /// \return The expected TISG RR length in bytes
     375                 :            :     size_t getTSIGLength() const;
     376                 :            : 
     377                 :            :     /// Return the current state of the context
     378                 :            :     ///
     379                 :            :     /// \note
     380                 :            :     /// The states are visible in public mainly for testing purposes.
     381                 :            :     /// Normal applications won't have to deal with them.
     382                 :            :     ///
     383                 :            :     /// \exception None
     384                 :            :     State getState() const;
     385                 :            : 
     386                 :            :     /// Return the TSIG error as a result of the latest verification
     387                 :            :     ///
     388                 :            :     /// This method can be called even before verifying anything, but the
     389                 :            :     /// returned value is meaningless in that case.
     390                 :            :     ///
     391                 :            :     /// \exception None
     392                 :            :     TSIGError getError() const;
     393                 :            : 
     394                 :            :     /// \name Protocol constants and defaults
     395                 :            :     ///
     396                 :            :     //@{
     397                 :            :     /// The recommended fudge value (in seconds) by RFC2845.
     398                 :            :     ///
     399                 :            :     /// Right now fudge is not tunable, and all TSIGs generated by this API
     400                 :            :     /// will have this value of fudge.
     401                 :            :     static const uint16_t DEFAULT_FUDGE = 300;
     402                 :            :     //@}
     403                 :            : 
     404                 :            : private:
     405                 :            :     struct TSIGContextImpl;
     406                 :            :     TSIGContextImpl* impl_;
     407                 :            : };
     408                 :            : }
     409                 :            : }
     410                 :            : 
     411                 :            : #endif  // __TSIG_H
     412                 :            : 
     413                 :            : // Local Variables:
     414                 :            : // mode: c++
     415                 :            : // End:

Generated by: LCOV version 1.9