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