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