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