1 /* $NetBSD: tsig_test.c,v 1.9 2022/09/23 12:15:32 christos Exp $ */
2
3 /*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16 #if HAVE_CMOCKA
17
18 #include <sched.h> /* IWYU pragma: keep */
19 #include <setjmp.h>
20 #include <stdarg.h>
21 #include <stdbool.h>
22 #include <stddef.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25
26 #define UNIT_TESTING
27 #include <cmocka.h>
28
29 #include <isc/mem.h>
30 #include <isc/print.h>
31 #include <isc/util.h>
32
33 #include <dns/rdatalist.h>
34 #include <dns/rdataset.h>
35 #include <dns/tsig.h>
36
37 #include "../tsig_p.h"
38 #include "dnstest.h"
39
40 #define CHECK(r) \
41 do { \
42 result = (r); \
43 if (result != ISC_R_SUCCESS) { \
44 goto cleanup; \
45 } \
46 } while (0)
47
48 #define TEST_ORIGIN "test"
49
50 static int
_setup(void ** state)51 _setup(void **state) {
52 isc_result_t result;
53
54 UNUSED(state);
55
56 result = dns_test_begin(NULL, false);
57 assert_int_equal(result, ISC_R_SUCCESS);
58
59 return (0);
60 }
61
62 static int
_teardown(void ** state)63 _teardown(void **state) {
64 UNUSED(state);
65
66 dns_test_end();
67
68 return (0);
69 }
70
71 static int debug = 0;
72
73 static isc_result_t
add_mac(dst_context_t * tsigctx,isc_buffer_t * buf)74 add_mac(dst_context_t *tsigctx, isc_buffer_t *buf) {
75 dns_rdata_any_tsig_t tsig;
76 dns_rdata_t rdata = DNS_RDATA_INIT;
77 isc_buffer_t databuf;
78 isc_region_t r;
79 isc_result_t result;
80 unsigned char tsigbuf[1024];
81
82 isc_buffer_usedregion(buf, &r);
83 dns_rdata_fromregion(&rdata, dns_rdataclass_any, dns_rdatatype_tsig,
84 &r);
85 isc_buffer_init(&databuf, tsigbuf, sizeof(tsigbuf));
86 CHECK(dns_rdata_tostruct(&rdata, &tsig, NULL));
87 isc_buffer_putuint16(&databuf, tsig.siglen);
88 isc_buffer_putmem(&databuf, tsig.signature, tsig.siglen);
89 isc_buffer_usedregion(&databuf, &r);
90 result = dst_context_adddata(tsigctx, &r);
91 dns_rdata_freestruct(&tsig);
92 cleanup:
93 return (result);
94 }
95
96 static isc_result_t
add_tsig(dst_context_t * tsigctx,dns_tsigkey_t * key,isc_buffer_t * target)97 add_tsig(dst_context_t *tsigctx, dns_tsigkey_t *key, isc_buffer_t *target) {
98 dns_compress_t cctx;
99 dns_rdata_any_tsig_t tsig;
100 dns_rdata_t rdata = DNS_RDATA_INIT;
101 dns_rdatalist_t rdatalist;
102 dns_rdataset_t rdataset;
103 isc_buffer_t *dynbuf = NULL;
104 isc_buffer_t databuf;
105 isc_buffer_t sigbuf;
106 isc_region_t r;
107 isc_result_t result = ISC_R_SUCCESS;
108 isc_stdtime_t now;
109 unsigned char tsigbuf[1024];
110 unsigned int count;
111 unsigned int sigsize = 0;
112 bool invalidate_ctx = false;
113
114 memset(&tsig, 0, sizeof(tsig));
115
116 CHECK(dns_compress_init(&cctx, -1, dt_mctx));
117 invalidate_ctx = true;
118
119 tsig.common.rdclass = dns_rdataclass_any;
120 tsig.common.rdtype = dns_rdatatype_tsig;
121 ISC_LINK_INIT(&tsig.common, link);
122 dns_name_init(&tsig.algorithm, NULL);
123 dns_name_clone(key->algorithm, &tsig.algorithm);
124
125 isc_stdtime_get(&now);
126 tsig.timesigned = now;
127 tsig.fudge = DNS_TSIG_FUDGE;
128 tsig.originalid = 50;
129 tsig.error = dns_rcode_noerror;
130 tsig.otherlen = 0;
131 tsig.other = NULL;
132
133 isc_buffer_init(&databuf, tsigbuf, sizeof(tsigbuf));
134 isc_buffer_putuint48(&databuf, tsig.timesigned);
135 isc_buffer_putuint16(&databuf, tsig.fudge);
136 isc_buffer_usedregion(&databuf, &r);
137 CHECK(dst_context_adddata(tsigctx, &r));
138
139 CHECK(dst_key_sigsize(key->key, &sigsize));
140 tsig.signature = isc_mem_get(dt_mctx, sigsize);
141 isc_buffer_init(&sigbuf, tsig.signature, sigsize);
142 CHECK(dst_context_sign(tsigctx, &sigbuf));
143 tsig.siglen = isc_buffer_usedlength(&sigbuf);
144 assert_int_equal(sigsize, tsig.siglen);
145
146 isc_buffer_allocate(dt_mctx, &dynbuf, 512);
147 CHECK(dns_rdata_fromstruct(&rdata, dns_rdataclass_any,
148 dns_rdatatype_tsig, &tsig, dynbuf));
149 dns_rdatalist_init(&rdatalist);
150 rdatalist.rdclass = dns_rdataclass_any;
151 rdatalist.type = dns_rdatatype_tsig;
152 ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
153 dns_rdataset_init(&rdataset);
154 CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset));
155 CHECK(dns_rdataset_towire(&rdataset, &key->name, &cctx, target, 0,
156 &count));
157
158 /*
159 * Fixup additional record count.
160 */
161 ((unsigned char *)target->base)[11]++;
162 if (((unsigned char *)target->base)[11] == 0) {
163 ((unsigned char *)target->base)[10]++;
164 }
165 cleanup:
166 if (tsig.signature != NULL) {
167 isc_mem_put(dt_mctx, tsig.signature, sigsize);
168 }
169 if (dynbuf != NULL) {
170 isc_buffer_free(&dynbuf);
171 }
172 if (invalidate_ctx) {
173 dns_compress_invalidate(&cctx);
174 }
175
176 return (result);
177 }
178
179 static void
printmessage(dns_message_t * msg)180 printmessage(dns_message_t *msg) {
181 isc_buffer_t b;
182 char *buf = NULL;
183 int len = 1024;
184 isc_result_t result = ISC_R_SUCCESS;
185
186 if (!debug) {
187 return;
188 }
189
190 do {
191 buf = isc_mem_get(dt_mctx, len);
192
193 isc_buffer_init(&b, buf, len);
194 result = dns_message_totext(msg, &dns_master_style_debug, 0,
195 &b);
196 if (result == ISC_R_NOSPACE) {
197 isc_mem_put(dt_mctx, buf, len);
198 len *= 2;
199 } else if (result == ISC_R_SUCCESS) {
200 printf("%.*s\n", (int)isc_buffer_usedlength(&b), buf);
201 }
202 } while (result == ISC_R_NOSPACE);
203
204 if (buf != NULL) {
205 isc_mem_put(dt_mctx, buf, len);
206 }
207 }
208
209 static void
render(isc_buffer_t * buf,unsigned flags,dns_tsigkey_t * key,isc_buffer_t ** tsigin,isc_buffer_t ** tsigout,dst_context_t * tsigctx)210 render(isc_buffer_t *buf, unsigned flags, dns_tsigkey_t *key,
211 isc_buffer_t **tsigin, isc_buffer_t **tsigout, dst_context_t *tsigctx) {
212 dns_message_t *msg = NULL;
213 dns_compress_t cctx;
214 isc_result_t result;
215
216 dns_message_create(dt_mctx, DNS_MESSAGE_INTENTRENDER, &msg);
217 assert_non_null(msg);
218
219 msg->id = 50;
220 msg->rcode = dns_rcode_noerror;
221 msg->flags = flags;
222
223 /*
224 * XXXMPA: this hack needs to be replaced with use of
225 * dns_message_reply() at some point.
226 */
227 if ((flags & DNS_MESSAGEFLAG_QR) != 0) {
228 msg->verified_sig = 1;
229 }
230
231 if (tsigin == tsigout) {
232 msg->tcp_continuation = 1;
233 }
234
235 if (tsigctx == NULL) {
236 result = dns_message_settsigkey(msg, key);
237 assert_int_equal(result, ISC_R_SUCCESS);
238
239 result = dns_message_setquerytsig(msg, *tsigin);
240 assert_int_equal(result, ISC_R_SUCCESS);
241 }
242
243 result = dns_compress_init(&cctx, -1, dt_mctx);
244 assert_int_equal(result, ISC_R_SUCCESS);
245
246 result = dns_message_renderbegin(msg, &cctx, buf);
247 assert_int_equal(result, ISC_R_SUCCESS);
248
249 result = dns_message_renderend(msg);
250 assert_int_equal(result, ISC_R_SUCCESS);
251
252 if (tsigctx != NULL) {
253 isc_region_t r;
254
255 isc_buffer_usedregion(buf, &r);
256 result = dst_context_adddata(tsigctx, &r);
257 assert_int_equal(result, ISC_R_SUCCESS);
258 } else {
259 if (tsigin == tsigout && *tsigin != NULL) {
260 isc_buffer_free(tsigin);
261 }
262
263 result = dns_message_getquerytsig(msg, dt_mctx, tsigout);
264 assert_int_equal(result, ISC_R_SUCCESS);
265 }
266
267 dns_compress_invalidate(&cctx);
268 dns_message_detach(&msg);
269 }
270
271 /*
272 * Test tsig tcp-continuation validation:
273 * Check that a simulated three message TCP sequence where the first
274 * and last messages contain TSIGs but the intermediate message doesn't
275 * correctly verifies.
276 */
277 static void
tsig_tcp_test(void ** state)278 tsig_tcp_test(void **state) {
279 const dns_name_t *tsigowner = NULL;
280 dns_fixedname_t fkeyname;
281 dns_message_t *msg = NULL;
282 dns_name_t *keyname;
283 dns_tsig_keyring_t *ring = NULL;
284 dns_tsigkey_t *key = NULL;
285 isc_buffer_t *buf = NULL;
286 isc_buffer_t *querytsig = NULL;
287 isc_buffer_t *tsigin = NULL;
288 isc_buffer_t *tsigout = NULL;
289 isc_result_t result;
290 unsigned char secret[16] = { 0 };
291 dst_context_t *tsigctx = NULL;
292 dst_context_t *outctx = NULL;
293
294 UNUSED(state);
295
296 /* isc_log_setdebuglevel(lctx, 99); */
297
298 keyname = dns_fixedname_initname(&fkeyname);
299 result = dns_name_fromstring(keyname, "test", 0, NULL);
300 assert_int_equal(result, ISC_R_SUCCESS);
301
302 result = dns_tsigkeyring_create(dt_mctx, &ring);
303 assert_int_equal(result, ISC_R_SUCCESS);
304
305 result = dns_tsigkey_create(keyname, dns_tsig_hmacsha256_name, secret,
306 sizeof(secret), false, NULL, 0, 0, dt_mctx,
307 ring, &key);
308 assert_int_equal(result, ISC_R_SUCCESS);
309 assert_non_null(key);
310
311 /*
312 * Create request.
313 */
314 isc_buffer_allocate(dt_mctx, &buf, 65535);
315 render(buf, 0, key, &tsigout, &querytsig, NULL);
316 isc_buffer_free(&buf);
317
318 /*
319 * Create response message 1.
320 */
321 isc_buffer_allocate(dt_mctx, &buf, 65535);
322 render(buf, DNS_MESSAGEFLAG_QR, key, &querytsig, &tsigout, NULL);
323 assert_non_null(tsigout);
324
325 /*
326 * Process response message 1.
327 */
328 dns_message_create(dt_mctx, DNS_MESSAGE_INTENTPARSE, &msg);
329 assert_non_null(msg);
330
331 result = dns_message_settsigkey(msg, key);
332 assert_int_equal(result, ISC_R_SUCCESS);
333
334 result = dns_message_parse(msg, buf, 0);
335 assert_int_equal(result, ISC_R_SUCCESS);
336
337 printmessage(msg);
338
339 result = dns_message_setquerytsig(msg, querytsig);
340 assert_int_equal(result, ISC_R_SUCCESS);
341
342 result = dns_tsig_verify(buf, msg, NULL, NULL);
343 assert_int_equal(result, ISC_R_SUCCESS);
344 assert_int_equal(msg->verified_sig, 1);
345 assert_int_equal(msg->tsigstatus, dns_rcode_noerror);
346
347 /*
348 * Check that we have a TSIG in the first message.
349 */
350 assert_non_null(dns_message_gettsig(msg, &tsigowner));
351
352 result = dns_message_getquerytsig(msg, dt_mctx, &tsigin);
353 assert_int_equal(result, ISC_R_SUCCESS);
354
355 tsigctx = msg->tsigctx;
356 msg->tsigctx = NULL;
357 isc_buffer_free(&buf);
358 dns_message_detach(&msg);
359
360 result = dst_context_create(key->key, dt_mctx, DNS_LOGCATEGORY_DNSSEC,
361 false, 0, &outctx);
362 assert_int_equal(result, ISC_R_SUCCESS);
363 assert_non_null(outctx);
364
365 /*
366 * Start digesting.
367 */
368 result = add_mac(outctx, tsigout);
369 assert_int_equal(result, ISC_R_SUCCESS);
370
371 /*
372 * Create response message 2.
373 */
374 isc_buffer_allocate(dt_mctx, &buf, 65535);
375
376 assert_int_equal(result, ISC_R_SUCCESS);
377 render(buf, DNS_MESSAGEFLAG_QR, key, &tsigout, &tsigout, outctx);
378
379 /*
380 * Process response message 2.
381 */
382 dns_message_create(dt_mctx, DNS_MESSAGE_INTENTPARSE, &msg);
383 assert_non_null(msg);
384
385 msg->tcp_continuation = 1;
386 msg->tsigctx = tsigctx;
387 tsigctx = NULL;
388
389 result = dns_message_settsigkey(msg, key);
390 assert_int_equal(result, ISC_R_SUCCESS);
391
392 result = dns_message_parse(msg, buf, 0);
393 assert_int_equal(result, ISC_R_SUCCESS);
394
395 printmessage(msg);
396
397 result = dns_message_setquerytsig(msg, tsigin);
398 assert_int_equal(result, ISC_R_SUCCESS);
399
400 result = dns_tsig_verify(buf, msg, NULL, NULL);
401 assert_int_equal(result, ISC_R_SUCCESS);
402 assert_int_equal(msg->verified_sig, 0);
403 assert_int_equal(msg->tsigstatus, dns_rcode_noerror);
404
405 /*
406 * Check that we don't have a TSIG in the second message.
407 */
408 tsigowner = NULL;
409 assert_true(dns_message_gettsig(msg, &tsigowner) == NULL);
410
411 tsigctx = msg->tsigctx;
412 msg->tsigctx = NULL;
413 isc_buffer_free(&buf);
414 dns_message_detach(&msg);
415
416 /*
417 * Create response message 3.
418 */
419 isc_buffer_allocate(dt_mctx, &buf, 65535);
420 render(buf, DNS_MESSAGEFLAG_QR, key, &tsigout, &tsigout, outctx);
421
422 result = add_tsig(outctx, key, buf);
423 assert_int_equal(result, ISC_R_SUCCESS);
424
425 /*
426 * Process response message 3.
427 */
428 dns_message_create(dt_mctx, DNS_MESSAGE_INTENTPARSE, &msg);
429 assert_non_null(msg);
430
431 msg->tcp_continuation = 1;
432 msg->tsigctx = tsigctx;
433 tsigctx = NULL;
434
435 result = dns_message_settsigkey(msg, key);
436 assert_int_equal(result, ISC_R_SUCCESS);
437
438 result = dns_message_parse(msg, buf, 0);
439 assert_int_equal(result, ISC_R_SUCCESS);
440
441 printmessage(msg);
442
443 /*
444 * Check that we had a TSIG in the third message.
445 */
446 assert_non_null(dns_message_gettsig(msg, &tsigowner));
447
448 result = dns_message_setquerytsig(msg, tsigin);
449 assert_int_equal(result, ISC_R_SUCCESS);
450
451 result = dns_tsig_verify(buf, msg, NULL, NULL);
452 assert_int_equal(result, ISC_R_SUCCESS);
453 assert_int_equal(msg->verified_sig, 1);
454 assert_int_equal(msg->tsigstatus, dns_rcode_noerror);
455
456 if (tsigin != NULL) {
457 isc_buffer_free(&tsigin);
458 }
459
460 result = dns_message_getquerytsig(msg, dt_mctx, &tsigin);
461 assert_int_equal(result, ISC_R_SUCCESS);
462
463 isc_buffer_free(&buf);
464 dns_message_detach(&msg);
465
466 if (outctx != NULL) {
467 dst_context_destroy(&outctx);
468 }
469 if (querytsig != NULL) {
470 isc_buffer_free(&querytsig);
471 }
472 if (tsigin != NULL) {
473 isc_buffer_free(&tsigin);
474 }
475 if (tsigout != NULL) {
476 isc_buffer_free(&tsigout);
477 }
478 dns_tsigkey_detach(&key);
479 if (ring != NULL) {
480 dns_tsigkeyring_detach(&ring);
481 }
482 }
483
484 /* Tests the dns__tsig_algvalid function */
485 static void
algvalid_test(void ** state)486 algvalid_test(void **state) {
487 UNUSED(state);
488
489 assert_true(dns__tsig_algvalid(DST_ALG_HMACMD5));
490
491 assert_true(dns__tsig_algvalid(DST_ALG_HMACSHA1));
492 assert_true(dns__tsig_algvalid(DST_ALG_HMACSHA224));
493 assert_true(dns__tsig_algvalid(DST_ALG_HMACSHA256));
494 assert_true(dns__tsig_algvalid(DST_ALG_HMACSHA384));
495 assert_true(dns__tsig_algvalid(DST_ALG_HMACSHA512));
496
497 assert_false(dns__tsig_algvalid(DST_ALG_GSSAPI));
498 }
499
500 /* Tests the dns__tsig_algfromname function */
501 static void
algfromname_test(void ** state)502 algfromname_test(void **state) {
503 UNUSED(state);
504
505 assert_int_equal(dns__tsig_algfromname(DNS_TSIG_HMACMD5_NAME),
506 DST_ALG_HMACMD5);
507 assert_int_equal(dns__tsig_algfromname(DNS_TSIG_HMACSHA1_NAME),
508 DST_ALG_HMACSHA1);
509 assert_int_equal(dns__tsig_algfromname(DNS_TSIG_HMACSHA224_NAME),
510 DST_ALG_HMACSHA224);
511 assert_int_equal(dns__tsig_algfromname(DNS_TSIG_HMACSHA256_NAME),
512 DST_ALG_HMACSHA256);
513 assert_int_equal(dns__tsig_algfromname(DNS_TSIG_HMACSHA384_NAME),
514 DST_ALG_HMACSHA384);
515 assert_int_equal(dns__tsig_algfromname(DNS_TSIG_HMACSHA512_NAME),
516 DST_ALG_HMACSHA512);
517
518 assert_int_equal(dns__tsig_algfromname(DNS_TSIG_GSSAPI_NAME),
519 DST_ALG_GSSAPI);
520 assert_int_equal(dns__tsig_algfromname(DNS_TSIG_GSSAPIMS_NAME),
521 DST_ALG_GSSAPI);
522
523 assert_int_equal(dns__tsig_algfromname(dns_rootname), 0);
524 }
525
526 /* Tests the dns__tsig_algnamefromname function */
527
528 /*
529 * Helper function to create a dns_name_t from a string and see if
530 * the dns__tsig_algnamefromname function can correctly match it against the
531 * static table of known algorithms.
532 */
533 static void
test_name(const char * name_string,const dns_name_t * expected)534 test_name(const char *name_string, const dns_name_t *expected) {
535 dns_name_t name;
536 dns_name_init(&name, NULL);
537 assert_int_equal(dns_name_fromstring(&name, name_string, 0, dt_mctx),
538 ISC_R_SUCCESS);
539 assert_ptr_equal(dns__tsig_algnamefromname(&name), expected);
540 dns_name_free(&name, dt_mctx);
541 }
542
543 static void
algnamefromname_test(void ** state)544 algnamefromname_test(void **state) {
545 UNUSED(state);
546
547 /* test the standard algorithms */
548 test_name("hmac-md5.sig-alg.reg.int", DNS_TSIG_HMACMD5_NAME);
549 test_name("hmac-sha1", DNS_TSIG_HMACSHA1_NAME);
550 test_name("hmac-sha224", DNS_TSIG_HMACSHA224_NAME);
551 test_name("hmac-sha256", DNS_TSIG_HMACSHA256_NAME);
552 test_name("hmac-sha384", DNS_TSIG_HMACSHA384_NAME);
553 test_name("hmac-sha512", DNS_TSIG_HMACSHA512_NAME);
554
555 test_name("gss-tsig", DNS_TSIG_GSSAPI_NAME);
556 test_name("gss.microsoft.com", DNS_TSIG_GSSAPIMS_NAME);
557
558 /* try another name that isn't a standard algorithm name */
559 assert_null(dns__tsig_algnamefromname(dns_rootname));
560 }
561
562 /* Tests the dns__tsig_algallocated function */
563 static void
algallocated_test(void ** state)564 algallocated_test(void **state) {
565 UNUSED(state);
566
567 /* test the standard algorithms */
568 assert_false(dns__tsig_algallocated(DNS_TSIG_HMACMD5_NAME));
569 assert_false(dns__tsig_algallocated(DNS_TSIG_HMACSHA1_NAME));
570 assert_false(dns__tsig_algallocated(DNS_TSIG_HMACSHA224_NAME));
571 assert_false(dns__tsig_algallocated(DNS_TSIG_HMACSHA256_NAME));
572 assert_false(dns__tsig_algallocated(DNS_TSIG_HMACSHA384_NAME));
573 assert_false(dns__tsig_algallocated(DNS_TSIG_HMACSHA512_NAME));
574
575 assert_false(dns__tsig_algallocated(DNS_TSIG_HMACSHA512_NAME));
576 assert_false(dns__tsig_algallocated(DNS_TSIG_HMACSHA512_NAME));
577
578 /* try another name that isn't a standard algorithm name */
579 assert_true(dns__tsig_algallocated(dns_rootname));
580 }
581
582 int
main(void)583 main(void) {
584 const struct CMUnitTest tests[] = {
585 cmocka_unit_test_setup_teardown(tsig_tcp_test, _setup,
586 _teardown),
587 cmocka_unit_test(algvalid_test),
588 cmocka_unit_test(algfromname_test),
589 cmocka_unit_test_setup_teardown(algnamefromname_test, _setup,
590 _teardown),
591 cmocka_unit_test(algallocated_test),
592 };
593
594 return (cmocka_run_group_tests(tests, NULL, NULL));
595 }
596
597 #else /* HAVE_CMOCKA */
598
599 #include <stdio.h>
600
601 int
main(void)602 main(void) {
603 printf("1..0 # Skipped: cmocka not available\n");
604 return (SKIPPED_TEST_EXIT_CODE);
605 }
606
607 #endif /* if HAVE_CMOCKA */
608