2 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (c) 1999-2003 by Internet Software Consortium
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 * Internet Systems Consortium, Inc.
19 * Redwood City, CA 94063
25 static const char rcsid
[] = "$Id: ns_verify.c,v 1.3 2005/08/11 17:13:26 drochner Exp $";
28 #define time(x) trace_mr_time (x)
32 #include <sys/types.h>
33 #include <sys/param.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37 #include <sys/socket.h>
47 #include "minires/minires.h"
48 #include "arpa/nameser.h"
49 #include <isc-dhcp/dst.h>
51 time_t trace_mr_time (time_t *);
55 #define BOUNDS_CHECK(ptr, count) \
57 if ((ptr) + (count) > eom) { \
58 return (NS_TSIG_ERROR_FORMERR); \
65 ns_find_tsig(u_char
*msg
, u_char
*eom
) {
66 HEADER
*hp
= (HEADER
*)msg
;
68 u_char
*cp
= msg
, *start
;
71 if (msg
== NULL
|| eom
== NULL
|| msg
> eom
)
74 if (cp
+ HFIXEDSZ
>= eom
)
82 status
= ns_skiprr(cp
, eom
, ns_s_qd
, ntohs(hp
->qdcount
), &n
);
83 if (status
!= ISC_R_SUCCESS
)
87 status
= ns_skiprr(cp
, eom
, ns_s_an
, ntohs(hp
->ancount
), &n
);
88 if (status
!= ISC_R_SUCCESS
)
92 status
= ns_skiprr(cp
, eom
, ns_s_ns
, ntohs(hp
->nscount
), &n
);
93 if (status
!= ISC_R_SUCCESS
)
97 status
= ns_skiprr(cp
, eom
, ns_s_ar
, ntohs(hp
->arcount
) - 1, &n
);
98 if (status
!= ISC_R_SUCCESS
)
103 n
= dn_skipname(cp
, eom
);
107 if (cp
+ INT16SZ
>= eom
)
111 if (type
!= ns_t_tsig
)
119 * msg received message
120 * msglen length of message
121 * key tsig key used for verifying.
122 * querysig (response), the signature in the query
123 * querysiglen (response), the length of the signature in the query
124 * sig (query), a buffer to hold the signature
125 * siglen (query), input - length of signature buffer
126 * output - length of signature
130 * - invalid dns message (NS_TSIG_ERROR_FORMERR)
131 * - TSIG is not present (NS_TSIG_ERROR_NO_TSIG)
132 * - key doesn't match (-ns_r_badkey)
133 * - TSIG verification fails with BADKEY (-ns_r_badkey)
134 * - TSIG verification fails with BADSIG (-ns_r_badsig)
135 * - TSIG verification fails with BADTIME (-ns_r_badtime)
136 * - TSIG verification succeeds, error set to BAKEY (ns_r_badkey)
137 * - TSIG verification succeeds, error set to BADSIG (ns_r_badsig)
138 * - TSIG verification succeeds, error set to BADTIME (ns_r_badtime)
141 ns_verify(u_char
*msg
, unsigned *msglen
, void *k
,
142 const u_char
*querysig
, unsigned querysiglen
,
143 u_char
*sig
, unsigned *siglen
, time_t *timesigned
, int nostrip
)
145 HEADER
*hp
= (HEADER
*)msg
;
146 DST_KEY
*key
= (DST_KEY
*)k
;
147 u_char
*cp
= msg
, *eom
;
148 char name
[MAXDNAME
], alg
[MAXDNAME
];
149 u_char
*recstart
, *rdatastart
;
150 u_char
*sigstart
, *otherstart
;
153 u_int16_t type
, length
;
154 u_int16_t fudge
, sigfieldlen
, id
, otherfieldlen
;
157 if (msg
== NULL
|| msglen
== NULL
|| *msglen
< 0)
158 return ISC_R_INVALIDARG
;
162 recstart
= ns_find_tsig(msg
, eom
);
163 if (recstart
== NULL
)
164 return ISC_R_NO_TSIG
;
168 /* Read the key name. */
169 n
= dn_expand(msg
, eom
, cp
, name
, MAXDNAME
);
171 return ISC_R_FORMERR
;
175 BOUNDS_CHECK(cp
, 2*INT16SZ
+ INT32SZ
+ INT16SZ
);
177 if (type
!= ns_t_tsig
)
178 return ISC_R_NO_TSIG
;
180 /* Skip the class and TTL, save the length. */
181 cp
+= INT16SZ
+ INT32SZ
;
182 GETSHORT(length
, cp
);
183 if (eom
- cp
!= length
)
184 return ISC_R_FORMERR
;
186 /* Read the algorithm name. */
188 n
= dn_expand(msg
, eom
, cp
, alg
, MAXDNAME
);
190 return ISC_R_FORMERR
;
191 if (ns_samename(alg
, NS_TSIG_ALG_HMAC_MD5
) != 1)
192 return ISC_R_INVALIDKEY
;
195 /* Read the time signed and fudge. */
196 BOUNDS_CHECK(cp
, INT16SZ
+ INT32SZ
+ INT16SZ
);
198 GETLONG((*timesigned
), cp
);
201 /* Read the signature. */
202 BOUNDS_CHECK(cp
, INT16SZ
);
203 GETSHORT(sigfieldlen
, cp
);
204 BOUNDS_CHECK(cp
, sigfieldlen
);
208 /* Read the original id and error. */
209 BOUNDS_CHECK(cp
, 2*INT16SZ
);
213 /* Parse the other data. */
214 BOUNDS_CHECK(cp
, INT16SZ
);
215 GETSHORT(otherfieldlen
, cp
);
216 BOUNDS_CHECK(cp
, otherfieldlen
);
221 return ISC_R_FORMERR
;
223 /* Verify that the key used is OK. */
225 if (key
->dk_alg
!= KEY_HMAC_MD5
)
226 return ISC_R_INVALIDKEY
;
227 if (error
!= ns_r_badsig
&& error
!= ns_r_badkey
) {
228 if (ns_samename(key
->dk_key_name
, name
) != 1)
229 return ISC_R_INVALIDKEY
;
233 hp
->arcount
= htons(ntohs(hp
->arcount
) - 1);
236 * Do the verification.
239 if (key
!= NULL
&& error
!= ns_r_badsig
&& error
!= ns_r_badkey
) {
241 u_char buf
[MAXDNAME
];
243 /* Digest the query signature, if this is a response. */
244 dst_verify_data(SIG_MODE_INIT
, key
, &ctx
, NULL
, 0, NULL
, 0);
245 if (querysiglen
> 0 && querysig
!= NULL
) {
246 u_int16_t len_n
= htons(querysiglen
);
247 dst_verify_data(SIG_MODE_UPDATE
, key
, &ctx
,
248 (u_char
*)&len_n
, INT16SZ
, NULL
, 0);
249 dst_verify_data(SIG_MODE_UPDATE
, key
, &ctx
,
250 querysig
, querysiglen
, NULL
, 0);
253 /* Digest the message. */
254 dst_verify_data(SIG_MODE_UPDATE
, key
, &ctx
, msg
,
255 (unsigned)(recstart
- msg
), NULL
, 0);
257 /* Digest the key name. */
258 n
= ns_name_ntol(recstart
, buf
, sizeof(buf
));
259 dst_verify_data(SIG_MODE_UPDATE
, key
, &ctx
, buf
, n
, NULL
, 0);
261 /* Digest the class and TTL. */
262 dst_verify_data(SIG_MODE_UPDATE
, key
, &ctx
,
263 recstart
+ dn_skipname(recstart
, eom
) + INT16SZ
,
264 INT16SZ
+ INT32SZ
, NULL
, 0);
266 /* Digest the algorithm. */
267 n
= ns_name_ntol(rdatastart
, buf
, sizeof(buf
));
268 dst_verify_data(SIG_MODE_UPDATE
, key
, &ctx
, buf
, n
, NULL
, 0);
270 /* Digest the time signed and fudge. */
271 dst_verify_data(SIG_MODE_UPDATE
, key
, &ctx
,
272 rdatastart
+ dn_skipname(rdatastart
, eom
),
273 INT16SZ
+ INT32SZ
+ INT16SZ
, NULL
, 0);
275 /* Digest the error and other data. */
276 dst_verify_data(SIG_MODE_UPDATE
, key
, &ctx
,
277 otherstart
- INT16SZ
- INT16SZ
,
278 (unsigned)otherfieldlen
+ INT16SZ
+ INT16SZ
,
281 n
= dst_verify_data(SIG_MODE_FINAL
, key
, &ctx
, NULL
, 0,
282 sigstart
, sigfieldlen
);
287 if (sig
!= NULL
&& siglen
!= NULL
) {
288 if (*siglen
< sigfieldlen
)
289 return ISC_R_NOSPACE
;
290 memcpy(sig
, sigstart
, sigfieldlen
);
291 *siglen
= sigfieldlen
;
295 return ISC_R_FORMERR
;
296 if (sig
!= NULL
&& siglen
!= NULL
)
300 /* Reset the counter, since we still need to check for badtime. */
301 hp
->arcount
= htons(ntohs(hp
->arcount
) + 1);
303 /* Verify the time. */
304 if (abs((*timesigned
) - time(NULL
)) > fudge
)
305 return ISC_R_BADTIME
;
308 *msglen
= recstart
- msg
;
309 hp
->arcount
= htons(ntohs(hp
->arcount
) - 1);
312 if (error
!= NOERROR
)
313 return ns_rcode_to_isc (error
);
315 return ISC_R_SUCCESS
;
320 ns_verify_tcp_init(void *k
, const u_char
*querysig
, unsigned querysiglen
,
321 ns_tcp_tsig_state
*state
)
324 if (state
== NULL
|| k
== NULL
|| querysig
== NULL
|| querysiglen
< 0)
325 return ISC_R_INVALIDARG
;
328 if (state
->key
->dk_alg
!= KEY_HMAC_MD5
)
330 if (querysiglen
> sizeof(state
->sig
))
331 return ISC_R_NOSPACE
;
332 memcpy(state
->sig
, querysig
, querysiglen
);
333 state
->siglen
= querysiglen
;
334 return ISC_R_SUCCESS
;
338 ns_verify_tcp(u_char
*msg
, unsigned *msglen
, ns_tcp_tsig_state
*state
,
341 HEADER
*hp
= (HEADER
*)msg
;
342 u_char
*recstart
, *rdatastart
, *sigstart
;
343 unsigned sigfieldlen
, otherfieldlen
;
344 u_char
*cp
, *eom
= msg
+ *msglen
, *cp2
;
345 char name
[MAXDNAME
], alg
[MAXDNAME
];
346 u_char buf
[MAXDNAME
];
347 int n
, type
, length
, fudge
, id
, error
;
350 if (msg
== NULL
|| msglen
== NULL
|| state
== NULL
)
351 return ISC_R_INVALIDARG
;
354 if (state
->counter
== 0)
355 return (ns_verify(msg
, msglen
, state
->key
,
356 state
->sig
, state
->siglen
,
357 state
->sig
, &state
->siglen
, ×igned
, 0));
359 if (state
->siglen
> 0) {
360 u_int16_t siglen_n
= htons(state
->siglen
);
362 dst_verify_data(SIG_MODE_INIT
, state
->key
, &state
->ctx
,
364 dst_verify_data(SIG_MODE_UPDATE
, state
->key
, &state
->ctx
,
365 (u_char
*)&siglen_n
, INT16SZ
, NULL
, 0);
366 dst_verify_data(SIG_MODE_UPDATE
, state
->key
, &state
->ctx
,
367 state
->sig
, state
->siglen
, NULL
, 0);
371 cp
= recstart
= ns_find_tsig(msg
, eom
);
373 if (recstart
== NULL
) {
375 return ISC_R_NO_TSIG
;
376 dst_verify_data(SIG_MODE_UPDATE
, state
->key
, &state
->ctx
,
377 msg
, *msglen
, NULL
, 0);
378 return ISC_R_SUCCESS
;
381 hp
->arcount
= htons(ntohs(hp
->arcount
) - 1);
382 dst_verify_data(SIG_MODE_UPDATE
, state
->key
, &state
->ctx
,
383 msg
, (unsigned)(recstart
- msg
), NULL
, 0);
385 /* Read the key name. */
386 n
= dn_expand(msg
, eom
, cp
, name
, MAXDNAME
);
388 return ISC_R_FORMERR
;
392 BOUNDS_CHECK(cp
, 2*INT16SZ
+ INT32SZ
+ INT16SZ
);
394 if (type
!= ns_t_tsig
)
395 return ISC_R_NO_TSIG
;
397 /* Skip the class and TTL, save the length. */
398 cp
+= INT16SZ
+ INT32SZ
;
399 GETSHORT(length
, cp
);
400 if (eom
- cp
!= length
)
401 return ISC_R_FORMERR
;
403 /* Read the algorithm name. */
405 n
= dn_expand(msg
, eom
, cp
, alg
, MAXDNAME
);
407 return ISC_R_FORMERR
;
408 if (ns_samename(alg
, NS_TSIG_ALG_HMAC_MD5
) != 1)
412 /* Verify that the key used is OK. */
413 if ((ns_samename(state
->key
->dk_key_name
, name
) != 1 ||
414 state
->key
->dk_alg
!= KEY_HMAC_MD5
))
417 /* Read the time signed and fudge. */
418 BOUNDS_CHECK(cp
, INT16SZ
+ INT32SZ
+ INT16SZ
);
420 GETLONG(timesigned
, cp
);
423 /* Read the signature. */
424 BOUNDS_CHECK(cp
, INT16SZ
);
425 GETSHORT(sigfieldlen
, cp
);
426 BOUNDS_CHECK(cp
, sigfieldlen
);
430 /* Read the original id and error. */
431 BOUNDS_CHECK(cp
, 2*INT16SZ
);
435 /* Parse the other data. */
436 BOUNDS_CHECK(cp
, INT16SZ
);
437 GETSHORT(otherfieldlen
, cp
);
438 BOUNDS_CHECK(cp
, otherfieldlen
);
442 return ISC_R_FORMERR
;
445 * Do the verification.
448 /* Digest the time signed and fudge. */
450 PUTSHORT(0, cp2
); /* Top 16 bits of time. */
451 PUTLONG(timesigned
, cp2
);
452 PUTSHORT(NS_TSIG_FUDGE
, cp2
);
454 dst_verify_data(SIG_MODE_UPDATE
, state
->key
, &state
->ctx
,
455 buf
, (unsigned)(cp2
- buf
), NULL
, 0);
457 n
= dst_verify_data(SIG_MODE_FINAL
, state
->key
, &state
->ctx
, NULL
, 0,
458 sigstart
, sigfieldlen
);
462 if (sigfieldlen
> sizeof(state
->sig
))
465 if (sigfieldlen
> sizeof(state
->sig
))
466 return ISC_R_NOSPACE
;
468 memcpy(state
->sig
, sigstart
, sigfieldlen
);
469 state
->siglen
= sigfieldlen
;
471 /* Verify the time. */
472 if (abs(timesigned
- time(NULL
)) > fudge
)
473 return ISC_R_BADTIME
;
475 *msglen
= recstart
- msg
;
477 if (error
!= NOERROR
)
478 return ns_rcode_to_isc (error
);
480 return ISC_R_SUCCESS
;