xref: /netbsd-src/external/mpl/bind/dist/tests/dns/tsig_test.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: tsig_test.c,v 1.4 2025/01/26 16:25:48 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 #include <inttypes.h>
17 #include <sched.h> /* IWYU pragma: keep */
18 #include <setjmp.h>
19 #include <stdarg.h>
20 #include <stdbool.h>
21 #include <stddef.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 
25 #define UNIT_TESTING
26 #include <cmocka.h>
27 
28 #include <isc/mem.h>
29 #include <isc/random.h>
30 #include <isc/result.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 
39 #include <tests/dns.h>
40 
41 #define TEST_ORIGIN "test"
42 
43 #define CHECK(r)                               \
44 	{                                      \
45 		result = (r);                  \
46 		if (result != ISC_R_SUCCESS) { \
47 			goto cleanup;          \
48 		}                              \
49 	}
50 
51 static int
52 setup_test(void **state) {
53 	isc_result_t result;
54 
55 	UNUSED(state);
56 
57 	result = dst_lib_init(mctx, NULL);
58 
59 	if (result != ISC_R_SUCCESS) {
60 		return 1;
61 	}
62 
63 	return 0;
64 }
65 
66 static int
67 teardown_test(void **state) {
68 	UNUSED(state);
69 
70 	dst_lib_destroy();
71 
72 	return 0;
73 }
74 
75 static isc_result_t
76 add_mac(dst_context_t *tsigctx, isc_buffer_t *buf) {
77 	dns_rdata_any_tsig_t tsig;
78 	dns_rdata_t rdata = DNS_RDATA_INIT;
79 	isc_buffer_t databuf;
80 	isc_region_t r;
81 	isc_result_t result;
82 	unsigned char tsigbuf[1024];
83 
84 	isc_buffer_usedregion(buf, &r);
85 	dns_rdata_fromregion(&rdata, dns_rdataclass_any, dns_rdatatype_tsig,
86 			     &r);
87 	isc_buffer_init(&databuf, tsigbuf, sizeof(tsigbuf));
88 	CHECK(dns_rdata_tostruct(&rdata, &tsig, NULL));
89 	isc_buffer_putuint16(&databuf, tsig.siglen);
90 	isc_buffer_putmem(&databuf, tsig.signature, tsig.siglen);
91 	isc_buffer_usedregion(&databuf, &r);
92 	result = dst_context_adddata(tsigctx, &r);
93 	dns_rdata_freestruct(&tsig);
94 cleanup:
95 	return result;
96 }
97 
98 static isc_result_t
99 add_tsig(dst_context_t *tsigctx, dns_tsigkey_t *key, isc_buffer_t *target,
100 	 isc_stdtime_t now, bool mangle_sig) {
101 	dns_compress_t cctx;
102 	dns_rdata_any_tsig_t tsig;
103 	dns_rdata_t rdata = DNS_RDATA_INIT;
104 	dns_rdatalist_t rdatalist;
105 	dns_rdataset_t rdataset;
106 	isc_buffer_t *dynbuf = NULL;
107 	isc_buffer_t databuf;
108 	isc_buffer_t sigbuf;
109 	isc_region_t r;
110 	isc_result_t result = ISC_R_SUCCESS;
111 	unsigned char tsigbuf[1024];
112 	unsigned int count;
113 	unsigned int sigsize = 0;
114 
115 	memset(&tsig, 0, sizeof(tsig));
116 
117 	dns_compress_init(&cctx, mctx, 0);
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 	tsig.timesigned = now;
126 	tsig.fudge = DNS_TSIG_FUDGE;
127 	tsig.originalid = 50;
128 	tsig.error = dns_rcode_noerror;
129 	tsig.otherlen = 0;
130 	tsig.other = NULL;
131 
132 	isc_buffer_init(&databuf, tsigbuf, sizeof(tsigbuf));
133 	isc_buffer_putuint48(&databuf, tsig.timesigned);
134 	isc_buffer_putuint16(&databuf, tsig.fudge);
135 	isc_buffer_usedregion(&databuf, &r);
136 	CHECK(dst_context_adddata(tsigctx, &r));
137 
138 	CHECK(dst_key_sigsize(key->key, &sigsize));
139 	tsig.signature = isc_mem_get(mctx, sigsize);
140 	isc_buffer_init(&sigbuf, tsig.signature, sigsize);
141 	CHECK(dst_context_sign(tsigctx, &sigbuf));
142 	tsig.siglen = isc_buffer_usedlength(&sigbuf);
143 	assert_int_equal(sigsize, tsig.siglen);
144 	if (mangle_sig) {
145 		isc_random_buf(tsig.signature, tsig.siglen);
146 	}
147 
148 	isc_buffer_allocate(mctx, &dynbuf, 512);
149 	CHECK(dns_rdata_fromstruct(&rdata, dns_rdataclass_any,
150 				   dns_rdatatype_tsig, &tsig, dynbuf));
151 	dns_rdatalist_init(&rdatalist);
152 	rdatalist.rdclass = dns_rdataclass_any;
153 	rdatalist.type = dns_rdatatype_tsig;
154 	ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
155 	dns_rdataset_init(&rdataset);
156 	dns_rdatalist_tordataset(&rdatalist, &rdataset);
157 	CHECK(dns_rdataset_towire(&rdataset, key->name, &cctx, target, 0,
158 				  &count));
159 
160 	/*
161 	 * Fixup additional record count.
162 	 */
163 	((unsigned char *)target->base)[11]++;
164 	if (((unsigned char *)target->base)[11] == 0) {
165 		((unsigned char *)target->base)[10]++;
166 	}
167 cleanup:
168 	if (tsig.signature != NULL) {
169 		isc_mem_put(mctx, tsig.signature, sigsize);
170 	}
171 	if (dynbuf != NULL) {
172 		isc_buffer_free(&dynbuf);
173 	}
174 	dns_compress_invalidate(&cctx);
175 
176 	return result;
177 }
178 
179 static void
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(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(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(mctx, buf, len);
206 	}
207 }
208 
209 static void
210 render(isc_buffer_t *buf, unsigned int 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(mctx, NULL, NULL, 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 		dns_message_setquerytsig(msg, *tsigin);
240 	}
241 
242 	dns_compress_init(&cctx, mctx, 0);
243 
244 	result = dns_message_renderbegin(msg, &cctx, buf);
245 	assert_int_equal(result, ISC_R_SUCCESS);
246 
247 	result = dns_message_renderend(msg);
248 	assert_int_equal(result, ISC_R_SUCCESS);
249 
250 	if (tsigctx != NULL) {
251 		isc_region_t r;
252 
253 		isc_buffer_usedregion(buf, &r);
254 		result = dst_context_adddata(tsigctx, &r);
255 		assert_int_equal(result, ISC_R_SUCCESS);
256 	} else {
257 		if (tsigin == tsigout && *tsigin != NULL) {
258 			isc_buffer_free(tsigin);
259 		}
260 
261 		result = dns_message_getquerytsig(msg, mctx, tsigout);
262 		assert_int_equal(result, ISC_R_SUCCESS);
263 	}
264 
265 	dns_compress_invalidate(&cctx);
266 	dns_message_detach(&msg);
267 }
268 
269 static void
270 tsig_tcp(isc_stdtime_t now, isc_result_t expected_result, bool mangle_sig) {
271 	const dns_name_t *tsigowner = NULL;
272 	dns_fixedname_t fkeyname;
273 	dns_message_t *msg = NULL;
274 	dns_name_t *keyname;
275 	dns_tsigkeyring_t *ring = NULL;
276 	dns_tsigkey_t *key = NULL;
277 	isc_buffer_t *buf = NULL;
278 	isc_buffer_t *querytsig = NULL;
279 	isc_buffer_t *tsigin = NULL;
280 	isc_buffer_t *tsigout = NULL;
281 	isc_result_t result;
282 	unsigned char secret[16] = { 0 };
283 	dst_context_t *tsigctx = NULL;
284 	dst_context_t *outctx = NULL;
285 
286 	/* isc_log_setdebuglevel(lctx, 99); */
287 
288 	keyname = dns_fixedname_initname(&fkeyname);
289 	result = dns_name_fromstring(keyname, "test", dns_rootname, 0, NULL);
290 	assert_int_equal(result, ISC_R_SUCCESS);
291 
292 	dns_tsigkeyring_create(mctx, &ring);
293 	assert_non_null(ring);
294 
295 	result = dns_tsigkey_create(keyname, DST_ALG_HMACSHA256, secret,
296 				    sizeof(secret), mctx, &key);
297 	assert_int_equal(result, ISC_R_SUCCESS);
298 	result = dns_tsigkeyring_add(ring, key);
299 	assert_int_equal(result, ISC_R_SUCCESS);
300 	assert_non_null(key);
301 
302 	/*
303 	 * Create request.
304 	 */
305 	isc_buffer_allocate(mctx, &buf, 65535);
306 	render(buf, 0, key, &tsigout, &querytsig, NULL);
307 	isc_buffer_free(&buf);
308 
309 	/*
310 	 * Create response message 1.
311 	 */
312 	isc_buffer_allocate(mctx, &buf, 65535);
313 	render(buf, DNS_MESSAGEFLAG_QR, key, &querytsig, &tsigout, NULL);
314 	assert_non_null(tsigout);
315 
316 	/*
317 	 * Process response message 1.
318 	 */
319 	dns_message_create(mctx, NULL, NULL, DNS_MESSAGE_INTENTPARSE, &msg);
320 	assert_non_null(msg);
321 
322 	result = dns_message_settsigkey(msg, key);
323 	assert_int_equal(result, ISC_R_SUCCESS);
324 
325 	result = dns_message_parse(msg, buf, 0);
326 	assert_int_equal(result, ISC_R_SUCCESS);
327 
328 	printmessage(msg);
329 
330 	dns_message_setquerytsig(msg, querytsig);
331 
332 	result = dns_tsig_verify(buf, msg, NULL, NULL);
333 	assert_int_equal(result, ISC_R_SUCCESS);
334 	assert_int_equal(msg->verified_sig, 1);
335 	assert_int_equal(msg->tsigstatus, dns_rcode_noerror);
336 
337 	/*
338 	 * Check that we have a TSIG in the first message.
339 	 */
340 	assert_non_null(dns_message_gettsig(msg, &tsigowner));
341 
342 	result = dns_message_getquerytsig(msg, mctx, &tsigin);
343 	assert_int_equal(result, ISC_R_SUCCESS);
344 
345 	tsigctx = msg->tsigctx;
346 	msg->tsigctx = NULL;
347 	isc_buffer_free(&buf);
348 	dns_message_detach(&msg);
349 
350 	result = dst_context_create(key->key, mctx, DNS_LOGCATEGORY_DNSSEC,
351 				    false, 0, &outctx);
352 	assert_int_equal(result, ISC_R_SUCCESS);
353 	assert_non_null(outctx);
354 
355 	/*
356 	 * Start digesting.
357 	 */
358 	result = add_mac(outctx, tsigout);
359 	assert_int_equal(result, ISC_R_SUCCESS);
360 
361 	/*
362 	 * Create response message 2.
363 	 */
364 	isc_buffer_allocate(mctx, &buf, 65535);
365 
366 	assert_int_equal(result, ISC_R_SUCCESS);
367 	render(buf, DNS_MESSAGEFLAG_QR, key, &tsigout, &tsigout, outctx);
368 
369 	/*
370 	 * Process response message 2.
371 	 */
372 	dns_message_create(mctx, NULL, NULL, DNS_MESSAGE_INTENTPARSE, &msg);
373 	assert_non_null(msg);
374 
375 	msg->tcp_continuation = 1;
376 	msg->tsigctx = tsigctx;
377 	tsigctx = NULL;
378 
379 	result = dns_message_settsigkey(msg, key);
380 	assert_int_equal(result, ISC_R_SUCCESS);
381 
382 	result = dns_message_parse(msg, buf, 0);
383 	assert_int_equal(result, ISC_R_SUCCESS);
384 
385 	printmessage(msg);
386 
387 	dns_message_setquerytsig(msg, tsigin);
388 
389 	result = dns_tsig_verify(buf, msg, NULL, NULL);
390 	assert_int_equal(result, ISC_R_SUCCESS);
391 	assert_int_equal(msg->verified_sig, 0);
392 	assert_int_equal(msg->tsigstatus, dns_rcode_noerror);
393 
394 	/*
395 	 * Check that we don't have a TSIG in the second message.
396 	 */
397 	tsigowner = NULL;
398 	assert_true(dns_message_gettsig(msg, &tsigowner) == NULL);
399 
400 	tsigctx = msg->tsigctx;
401 	msg->tsigctx = NULL;
402 	isc_buffer_free(&buf);
403 	dns_message_detach(&msg);
404 
405 	/*
406 	 * Create response message 3.
407 	 */
408 	isc_buffer_allocate(mctx, &buf, 65535);
409 	render(buf, DNS_MESSAGEFLAG_QR, key, &tsigout, &tsigout, outctx);
410 
411 	result = add_tsig(outctx, key, buf, now, mangle_sig);
412 	assert_int_equal(result, ISC_R_SUCCESS);
413 
414 	/*
415 	 * Process response message 3.
416 	 */
417 	dns_message_create(mctx, NULL, NULL, DNS_MESSAGE_INTENTPARSE, &msg);
418 	assert_non_null(msg);
419 
420 	msg->tcp_continuation = 1;
421 	msg->tsigctx = tsigctx;
422 	tsigctx = NULL;
423 
424 	result = dns_message_settsigkey(msg, key);
425 	assert_int_equal(result, ISC_R_SUCCESS);
426 
427 	result = dns_message_parse(msg, buf, 0);
428 	assert_int_equal(result, ISC_R_SUCCESS);
429 
430 	printmessage(msg);
431 
432 	/*
433 	 * Check that we had a TSIG in the third message.
434 	 */
435 	assert_non_null(dns_message_gettsig(msg, &tsigowner));
436 
437 	dns_message_setquerytsig(msg, tsigin);
438 
439 	result = dns_tsig_verify(buf, msg, NULL, NULL);
440 	switch (expected_result) {
441 	case ISC_R_SUCCESS:
442 		assert_int_equal(result, ISC_R_SUCCESS);
443 		assert_int_equal(msg->verified_sig, 1);
444 		assert_int_equal(msg->tsigstatus, dns_rcode_noerror);
445 		break;
446 	case DNS_R_CLOCKSKEW:
447 		assert_int_equal(result, DNS_R_CLOCKSKEW);
448 		assert_int_equal(msg->verified_sig, 1);
449 		assert_int_equal(msg->tsigstatus, dns_tsigerror_badtime);
450 		break;
451 	default:
452 		UNREACHABLE();
453 	}
454 
455 	if (tsigin != NULL) {
456 		isc_buffer_free(&tsigin);
457 	}
458 
459 	result = dns_message_getquerytsig(msg, mctx, &tsigin);
460 	assert_int_equal(result, ISC_R_SUCCESS);
461 
462 	isc_buffer_free(&buf);
463 	dns_message_detach(&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 /*
484  * Test tsig tcp-continuation validation:
485  * Check that a simulated three message TCP sequence where the first
486  * and last messages contain TSIGs but the intermediate message doesn't
487  * correctly verifies.
488  */
489 ISC_RUN_TEST_IMPL(tsig_tcp) {
490 	/* Run with correct current time */
491 	tsig_tcp(isc_stdtime_now(), ISC_R_SUCCESS, false);
492 }
493 
494 ISC_RUN_TEST_IMPL(tsig_badtime) {
495 	/* Run with time outside of the fudge */
496 	tsig_tcp(isc_stdtime_now() - 2 * DNS_TSIG_FUDGE, DNS_R_CLOCKSKEW,
497 		 false);
498 	tsig_tcp(isc_stdtime_now() + 2 * DNS_TSIG_FUDGE, DNS_R_CLOCKSKEW,
499 		 false);
500 }
501 
502 ISC_RUN_TEST_IMPL(tsig_badsig) {
503 	tsig_tcp(isc_stdtime_now(), DNS_R_TSIGERRORSET, true);
504 }
505 
506 /* Tests the dns__tsig_algvalid function */
507 ISC_RUN_TEST_IMPL(algvalid) {
508 	UNUSED(state);
509 
510 	assert_true(dns__tsig_algvalid(DST_ALG_HMACMD5));
511 
512 	assert_true(dns__tsig_algvalid(DST_ALG_HMACSHA1));
513 	assert_true(dns__tsig_algvalid(DST_ALG_HMACSHA224));
514 	assert_true(dns__tsig_algvalid(DST_ALG_HMACSHA256));
515 	assert_true(dns__tsig_algvalid(DST_ALG_HMACSHA384));
516 	assert_true(dns__tsig_algvalid(DST_ALG_HMACSHA512));
517 
518 	assert_false(dns__tsig_algvalid(DST_ALG_GSSAPI));
519 }
520 
521 ISC_TEST_LIST_START
522 ISC_TEST_ENTRY_CUSTOM(tsig_tcp, setup_test, teardown_test)
523 ISC_TEST_ENTRY_CUSTOM(tsig_badtime, setup_test, teardown_test)
524 ISC_TEST_ENTRY(algvalid)
525 ISC_TEST_LIST_END
526 
527 ISC_TEST_MAIN
528