4 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (c) 1999 by Internet Software Consortium, Inc.
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 static const char rcsid
[] = "Id: ns_verify.c,v 1.5 2006/03/09 23:57:56 marka Exp";
26 #include "port_before.h"
27 #include "fd_setsize.h"
29 #include <sys/types.h>
30 #include <sys/param.h>
32 #include <netinet/in.h>
33 #include <arpa/nameser.h>
34 #include <arpa/inet.h>
47 #include "port_after.h"
51 #define BOUNDS_CHECK(ptr, count) \
53 if ((ptr) + (count) > eom) { \
54 return (NS_TSIG_ERROR_FORMERR); \
61 ns_find_tsig(u_char
*msg
, u_char
*eom
) {
62 HEADER
*hp
= (HEADER
*)msg
;
64 u_char
*cp
= msg
, *start
;
66 if (msg
== NULL
|| eom
== NULL
|| msg
> eom
)
69 if (cp
+ HFIXEDSZ
>= eom
)
77 n
= ns_skiprr(cp
, eom
, ns_s_qd
, ntohs(hp
->qdcount
));
82 n
= ns_skiprr(cp
, eom
, ns_s_an
, ntohs(hp
->ancount
));
87 n
= ns_skiprr(cp
, eom
, ns_s_ns
, ntohs(hp
->nscount
));
92 n
= ns_skiprr(cp
, eom
, ns_s_ar
, ntohs(hp
->arcount
) - 1);
98 n
= dn_skipname(cp
, eom
);
102 if (cp
+ INT16SZ
>= eom
)
106 if (type
!= ns_t_tsig
)
115 *\li msg received message
116 *\li msglen length of message
117 *\li key tsig key used for verifying.
118 *\li querysig (response), the signature in the query
119 *\li querysiglen (response), the length of the signature in the query
120 *\li sig (query), a buffer to hold the signature
121 *\li siglen (query), input - length of signature buffer
122 * output - length of signature
125 *\li - bad input (-1)
126 *\li - invalid dns message (NS_TSIG_ERROR_FORMERR)
127 *\li - TSIG is not present (NS_TSIG_ERROR_NO_TSIG)
128 *\li - key doesn't match (-ns_r_badkey)
129 *\li - TSIG verification fails with BADKEY (-ns_r_badkey)
130 *\li - TSIG verification fails with BADSIG (-ns_r_badsig)
131 *\li - TSIG verification fails with BADTIME (-ns_r_badtime)
132 *\li - TSIG verification succeeds, error set to BAKEY (ns_r_badkey)
133 *\li - TSIG verification succeeds, error set to BADSIG (ns_r_badsig)
134 *\li - TSIG verification succeeds, error set to BADTIME (ns_r_badtime)
137 ns_verify(u_char
*msg
, int *msglen
, void *k
,
138 const u_char
*querysig
, int querysiglen
, u_char
*sig
, int *siglen
,
139 time_t *timesigned
, int nostrip
)
141 HEADER
*hp
= (HEADER
*)msg
;
142 DST_KEY
*key
= (DST_KEY
*)k
;
143 u_char
*cp
= msg
, *eom
;
144 char name
[MAXDNAME
], alg
[MAXDNAME
];
145 u_char
*recstart
, *rdatastart
;
146 u_char
*sigstart
, *otherstart
;
149 u_int16_t type
, length
;
150 u_int16_t fudge
, sigfieldlen
, otherfieldlen
;
153 if (msg
== NULL
|| msglen
== NULL
|| *msglen
< 0)
158 recstart
= ns_find_tsig(msg
, eom
);
159 if (recstart
== NULL
)
160 return (NS_TSIG_ERROR_NO_TSIG
);
164 /* Read the key name. */
165 n
= dn_expand(msg
, eom
, cp
, name
, MAXDNAME
);
167 return (NS_TSIG_ERROR_FORMERR
);
171 BOUNDS_CHECK(cp
, 2*INT16SZ
+ INT32SZ
+ INT16SZ
);
173 if (type
!= ns_t_tsig
)
174 return (NS_TSIG_ERROR_NO_TSIG
);
176 /* Skip the class and TTL, save the length. */
177 cp
+= INT16SZ
+ INT32SZ
;
178 GETSHORT(length
, cp
);
179 if (eom
- cp
!= length
)
180 return (NS_TSIG_ERROR_FORMERR
);
182 /* Read the algorithm name. */
184 n
= dn_expand(msg
, eom
, cp
, alg
, MAXDNAME
);
186 return (NS_TSIG_ERROR_FORMERR
);
187 if (ns_samename(alg
, NS_TSIG_ALG_HMAC_MD5
) != 1)
188 return (-ns_r_badkey
);
191 /* Read the time signed and fudge. */
192 BOUNDS_CHECK(cp
, INT16SZ
+ INT32SZ
+ INT16SZ
);
194 GETLONG((*timesigned
), cp
);
197 /* Read the signature. */
198 BOUNDS_CHECK(cp
, INT16SZ
);
199 GETSHORT(sigfieldlen
, cp
);
200 BOUNDS_CHECK(cp
, sigfieldlen
);
204 /* Skip id and read error. */
205 BOUNDS_CHECK(cp
, 2*INT16SZ
);
209 /* Parse the other data. */
210 BOUNDS_CHECK(cp
, INT16SZ
);
211 GETSHORT(otherfieldlen
, cp
);
212 BOUNDS_CHECK(cp
, otherfieldlen
);
217 return (NS_TSIG_ERROR_FORMERR
);
219 /* Verify that the key used is OK. */
221 if (key
->dk_alg
!= KEY_HMAC_MD5
)
222 return (-ns_r_badkey
);
223 if (error
!= ns_r_badsig
&& error
!= ns_r_badkey
) {
224 if (ns_samename(key
->dk_key_name
, name
) != 1)
225 return (-ns_r_badkey
);
229 hp
->arcount
= htons(ntohs(hp
->arcount
) - 1);
232 * Do the verification.
235 if (key
!= NULL
&& error
!= ns_r_badsig
&& error
!= ns_r_badkey
) {
237 u_char buf
[MAXDNAME
];
238 u_char buf2
[MAXDNAME
];
240 /* Digest the query signature, if this is a response. */
241 dst_verify_data(SIG_MODE_INIT
, key
, &ctx
, NULL
, 0, NULL
, 0);
242 if (querysiglen
> 0 && querysig
!= NULL
) {
243 u_int16_t len_n
= htons(querysiglen
);
244 dst_verify_data(SIG_MODE_UPDATE
, key
, &ctx
,
245 (u_char
*)&len_n
, INT16SZ
, NULL
, 0);
246 dst_verify_data(SIG_MODE_UPDATE
, key
, &ctx
,
247 querysig
, querysiglen
, NULL
, 0);
250 /* Digest the message. */
251 dst_verify_data(SIG_MODE_UPDATE
, key
, &ctx
, msg
, recstart
- msg
,
254 /* Digest the key name. */
255 n
= ns_name_pton(name
, buf2
, sizeof(buf2
));
258 n
= ns_name_ntol(buf2
, buf
, sizeof(buf
));
261 dst_verify_data(SIG_MODE_UPDATE
, key
, &ctx
, buf
, n
, NULL
, 0);
263 /* Digest the class and TTL. */
264 dst_verify_data(SIG_MODE_UPDATE
, key
, &ctx
,
265 recstart
+ dn_skipname(recstart
, eom
) + INT16SZ
,
266 INT16SZ
+ INT32SZ
, NULL
, 0);
268 /* Digest the algorithm. */
269 n
= ns_name_pton(alg
, buf2
, sizeof(buf2
));
272 n
= ns_name_ntol(buf2
, buf
, sizeof(buf
));
275 dst_verify_data(SIG_MODE_UPDATE
, key
, &ctx
, buf
, n
, NULL
, 0);
277 /* Digest the time signed and fudge. */
278 dst_verify_data(SIG_MODE_UPDATE
, key
, &ctx
,
279 rdatastart
+ dn_skipname(rdatastart
, eom
),
280 INT16SZ
+ INT32SZ
+ INT16SZ
, NULL
, 0);
282 /* Digest the error and other data. */
283 dst_verify_data(SIG_MODE_UPDATE
, key
, &ctx
,
284 otherstart
- INT16SZ
- INT16SZ
,
285 otherfieldlen
+ INT16SZ
+ INT16SZ
, NULL
, 0);
287 n
= dst_verify_data(SIG_MODE_FINAL
, key
, &ctx
, NULL
, 0,
288 sigstart
, sigfieldlen
);
291 return (-ns_r_badsig
);
293 if (sig
!= NULL
&& siglen
!= NULL
) {
294 if (*siglen
< sigfieldlen
)
295 return (NS_TSIG_ERROR_NO_SPACE
);
296 memcpy(sig
, sigstart
, sigfieldlen
);
297 *siglen
= sigfieldlen
;
301 return (NS_TSIG_ERROR_FORMERR
);
302 if (sig
!= NULL
&& siglen
!= NULL
)
306 /* Reset the counter, since we still need to check for badtime. */
307 hp
->arcount
= htons(ntohs(hp
->arcount
) + 1);
309 /* Verify the time. */
310 if (abs((*timesigned
) - time(NULL
)) > fudge
)
311 return (-ns_r_badtime
);
314 *msglen
= recstart
- msg
;
315 hp
->arcount
= htons(ntohs(hp
->arcount
) - 1);
318 if (error
!= NOERROR
)
325 ns_verify_tcp_init(void *k
, const u_char
*querysig
, int querysiglen
,
326 ns_tcp_tsig_state
*state
)
329 if (state
== NULL
|| k
== NULL
|| querysig
== NULL
|| querysiglen
< 0)
333 if (state
->key
->dk_alg
!= KEY_HMAC_MD5
)
334 return (-ns_r_badkey
);
335 if (querysiglen
> (int)sizeof(state
->sig
))
337 memcpy(state
->sig
, querysig
, querysiglen
);
338 state
->siglen
= querysiglen
;
343 ns_verify_tcp(u_char
*msg
, int *msglen
, ns_tcp_tsig_state
*state
,
346 HEADER
*hp
= (HEADER
*)msg
;
347 u_char
*recstart
, *sigstart
;
348 unsigned int sigfieldlen
, otherfieldlen
;
349 u_char
*cp
, *eom
, *cp2
;
350 char name
[MAXDNAME
], alg
[MAXDNAME
];
351 u_char buf
[MAXDNAME
];
352 int n
, type
, length
, fudge
, error
;
355 if (msg
== NULL
|| msglen
== NULL
|| state
== NULL
)
361 if (state
->counter
== 0)
362 return (ns_verify(msg
, msglen
, state
->key
,
363 state
->sig
, state
->siglen
,
364 state
->sig
, &state
->siglen
, ×igned
, 0));
366 if (state
->siglen
> 0) {
367 u_int16_t siglen_n
= htons(state
->siglen
);
369 dst_verify_data(SIG_MODE_INIT
, state
->key
, &state
->ctx
,
371 dst_verify_data(SIG_MODE_UPDATE
, state
->key
, &state
->ctx
,
372 (u_char
*)&siglen_n
, INT16SZ
, NULL
, 0);
373 dst_verify_data(SIG_MODE_UPDATE
, state
->key
, &state
->ctx
,
374 state
->sig
, state
->siglen
, NULL
, 0);
378 cp
= recstart
= ns_find_tsig(msg
, eom
);
380 if (recstart
== NULL
) {
382 return (NS_TSIG_ERROR_NO_TSIG
);
383 dst_verify_data(SIG_MODE_UPDATE
, state
->key
, &state
->ctx
,
384 msg
, *msglen
, NULL
, 0);
388 hp
->arcount
= htons(ntohs(hp
->arcount
) - 1);
389 dst_verify_data(SIG_MODE_UPDATE
, state
->key
, &state
->ctx
,
390 msg
, recstart
- msg
, NULL
, 0);
392 /* Read the key name. */
393 n
= dn_expand(msg
, eom
, cp
, name
, MAXDNAME
);
395 return (NS_TSIG_ERROR_FORMERR
);
399 BOUNDS_CHECK(cp
, 2*INT16SZ
+ INT32SZ
+ INT16SZ
);
401 if (type
!= ns_t_tsig
)
402 return (NS_TSIG_ERROR_NO_TSIG
);
404 /* Skip the class and TTL, save the length. */
405 cp
+= INT16SZ
+ INT32SZ
;
406 GETSHORT(length
, cp
);
407 if (eom
- cp
!= length
)
408 return (NS_TSIG_ERROR_FORMERR
);
410 /* Read the algorithm name. */
411 n
= dn_expand(msg
, eom
, cp
, alg
, MAXDNAME
);
413 return (NS_TSIG_ERROR_FORMERR
);
414 if (ns_samename(alg
, NS_TSIG_ALG_HMAC_MD5
) != 1)
415 return (-ns_r_badkey
);
418 /* Verify that the key used is OK. */
419 if ((ns_samename(state
->key
->dk_key_name
, name
) != 1 ||
420 state
->key
->dk_alg
!= KEY_HMAC_MD5
))
421 return (-ns_r_badkey
);
423 /* Read the time signed and fudge. */
424 BOUNDS_CHECK(cp
, INT16SZ
+ INT32SZ
+ INT16SZ
);
426 GETLONG(timesigned
, cp
);
429 /* Read the signature. */
430 BOUNDS_CHECK(cp
, INT16SZ
);
431 GETSHORT(sigfieldlen
, cp
);
432 BOUNDS_CHECK(cp
, sigfieldlen
);
436 /* Skip id and read error. */
437 BOUNDS_CHECK(cp
, 2*INT16SZ
);
441 /* Parse the other data. */
442 BOUNDS_CHECK(cp
, INT16SZ
);
443 GETSHORT(otherfieldlen
, cp
);
444 BOUNDS_CHECK(cp
, otherfieldlen
);
448 return (NS_TSIG_ERROR_FORMERR
);
451 * Do the verification.
454 /* Digest the time signed and fudge. */
456 PUTSHORT(0, cp2
); /*%< Top 16 bits of time. */
457 PUTLONG(timesigned
, cp2
);
458 PUTSHORT(NS_TSIG_FUDGE
, cp2
);
460 dst_verify_data(SIG_MODE_UPDATE
, state
->key
, &state
->ctx
,
461 buf
, cp2
- buf
, NULL
, 0);
463 n
= dst_verify_data(SIG_MODE_FINAL
, state
->key
, &state
->ctx
, NULL
, 0,
464 sigstart
, sigfieldlen
);
466 return (-ns_r_badsig
);
468 if (sigfieldlen
> sizeof(state
->sig
))
469 return (NS_TSIG_ERROR_NO_SPACE
);
471 memcpy(state
->sig
, sigstart
, sigfieldlen
);
472 state
->siglen
= sigfieldlen
;
474 /* Verify the time. */
475 if (abs(timesigned
- time(NULL
)) > fudge
)
476 return (-ns_r_badtime
);
478 *msglen
= recstart
- msg
;
480 if (error
!= NOERROR
)