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