summaryrefslogtreecommitdiffstats
path: root/lib/dns/tests/tsig_test.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 07:24:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 07:24:22 +0000
commit45d6379135504814ab723b57f0eb8be23393a51d (patch)
treed4f2ec4acca824a8446387a758b0ce4238a4dffa /lib/dns/tests/tsig_test.c
parentInitial commit. (diff)
downloadbind9-upstream/1%9.16.44.tar.xz
bind9-upstream/1%9.16.44.zip
Adding upstream version 1:9.16.44.upstream/1%9.16.44upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/dns/tests/tsig_test.c')
-rw-r--r--lib/dns/tests/tsig_test.c605
1 files changed, 605 insertions, 0 deletions
diff --git a/lib/dns/tests/tsig_test.c b/lib/dns/tests/tsig_test.c
new file mode 100644
index 0000000..1354ef7
--- /dev/null
+++ b/lib/dns/tests/tsig_test.c
@@ -0,0 +1,605 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#if HAVE_CMOCKA
+
+#include <sched.h> /* IWYU pragma: keep */
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define UNIT_TESTING
+#include <cmocka.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/util.h>
+
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/tsig.h>
+
+#include "../tsig_p.h"
+#include "dnstest.h"
+
+#define CHECK(r) \
+ do { \
+ result = (r); \
+ if (result != ISC_R_SUCCESS) { \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define TEST_ORIGIN "test"
+
+static int
+_setup(void **state) {
+ isc_result_t result;
+
+ UNUSED(state);
+
+ result = dns_test_begin(NULL, false);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ return (0);
+}
+
+static int
+_teardown(void **state) {
+ UNUSED(state);
+
+ dns_test_end();
+
+ return (0);
+}
+
+static int debug = 0;
+
+static isc_result_t
+add_mac(dst_context_t *tsigctx, isc_buffer_t *buf) {
+ dns_rdata_any_tsig_t tsig;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_buffer_t databuf;
+ isc_region_t r;
+ isc_result_t result;
+ unsigned char tsigbuf[1024];
+
+ isc_buffer_usedregion(buf, &r);
+ dns_rdata_fromregion(&rdata, dns_rdataclass_any, dns_rdatatype_tsig,
+ &r);
+ isc_buffer_init(&databuf, tsigbuf, sizeof(tsigbuf));
+ CHECK(dns_rdata_tostruct(&rdata, &tsig, NULL));
+ isc_buffer_putuint16(&databuf, tsig.siglen);
+ isc_buffer_putmem(&databuf, tsig.signature, tsig.siglen);
+ isc_buffer_usedregion(&databuf, &r);
+ result = dst_context_adddata(tsigctx, &r);
+ dns_rdata_freestruct(&tsig);
+cleanup:
+ return (result);
+}
+
+static isc_result_t
+add_tsig(dst_context_t *tsigctx, dns_tsigkey_t *key, isc_buffer_t *target) {
+ dns_compress_t cctx;
+ dns_rdata_any_tsig_t tsig;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdatalist_t rdatalist;
+ dns_rdataset_t rdataset;
+ isc_buffer_t *dynbuf = NULL;
+ isc_buffer_t databuf;
+ isc_buffer_t sigbuf;
+ isc_region_t r;
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_stdtime_t now;
+ unsigned char tsigbuf[1024];
+ unsigned int count;
+ unsigned int sigsize = 0;
+ bool invalidate_ctx = false;
+
+ memset(&tsig, 0, sizeof(tsig));
+
+ CHECK(dns_compress_init(&cctx, -1, dt_mctx));
+ invalidate_ctx = true;
+
+ tsig.common.rdclass = dns_rdataclass_any;
+ tsig.common.rdtype = dns_rdatatype_tsig;
+ ISC_LINK_INIT(&tsig.common, link);
+ dns_name_init(&tsig.algorithm, NULL);
+ dns_name_clone(key->algorithm, &tsig.algorithm);
+
+ isc_stdtime_get(&now);
+ tsig.timesigned = now;
+ tsig.fudge = DNS_TSIG_FUDGE;
+ tsig.originalid = 50;
+ tsig.error = dns_rcode_noerror;
+ tsig.otherlen = 0;
+ tsig.other = NULL;
+
+ isc_buffer_init(&databuf, tsigbuf, sizeof(tsigbuf));
+ isc_buffer_putuint48(&databuf, tsig.timesigned);
+ isc_buffer_putuint16(&databuf, tsig.fudge);
+ isc_buffer_usedregion(&databuf, &r);
+ CHECK(dst_context_adddata(tsigctx, &r));
+
+ CHECK(dst_key_sigsize(key->key, &sigsize));
+ tsig.signature = isc_mem_get(dt_mctx, sigsize);
+ isc_buffer_init(&sigbuf, tsig.signature, sigsize);
+ CHECK(dst_context_sign(tsigctx, &sigbuf));
+ tsig.siglen = isc_buffer_usedlength(&sigbuf);
+ assert_int_equal(sigsize, tsig.siglen);
+
+ isc_buffer_allocate(dt_mctx, &dynbuf, 512);
+ CHECK(dns_rdata_fromstruct(&rdata, dns_rdataclass_any,
+ dns_rdatatype_tsig, &tsig, dynbuf));
+ dns_rdatalist_init(&rdatalist);
+ rdatalist.rdclass = dns_rdataclass_any;
+ rdatalist.type = dns_rdatatype_tsig;
+ ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
+ dns_rdataset_init(&rdataset);
+ CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset));
+ CHECK(dns_rdataset_towire(&rdataset, &key->name, &cctx, target, 0,
+ &count));
+
+ /*
+ * Fixup additional record count.
+ */
+ ((unsigned char *)target->base)[11]++;
+ if (((unsigned char *)target->base)[11] == 0) {
+ ((unsigned char *)target->base)[10]++;
+ }
+cleanup:
+ if (tsig.signature != NULL) {
+ isc_mem_put(dt_mctx, tsig.signature, sigsize);
+ }
+ if (dynbuf != NULL) {
+ isc_buffer_free(&dynbuf);
+ }
+ if (invalidate_ctx) {
+ dns_compress_invalidate(&cctx);
+ }
+
+ return (result);
+}
+
+static void
+printmessage(dns_message_t *msg) {
+ isc_buffer_t b;
+ char *buf = NULL;
+ int len = 1024;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ if (!debug) {
+ return;
+ }
+
+ do {
+ buf = isc_mem_get(dt_mctx, len);
+
+ isc_buffer_init(&b, buf, len);
+ result = dns_message_totext(msg, &dns_master_style_debug, 0,
+ &b);
+ if (result == ISC_R_NOSPACE) {
+ isc_mem_put(dt_mctx, buf, len);
+ len *= 2;
+ } else if (result == ISC_R_SUCCESS) {
+ printf("%.*s\n", (int)isc_buffer_usedlength(&b), buf);
+ }
+ } while (result == ISC_R_NOSPACE);
+
+ if (buf != NULL) {
+ isc_mem_put(dt_mctx, buf, len);
+ }
+}
+
+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) {
+ dns_message_t *msg = NULL;
+ dns_compress_t cctx;
+ isc_result_t result;
+
+ dns_message_create(dt_mctx, DNS_MESSAGE_INTENTRENDER, &msg);
+ assert_non_null(msg);
+
+ msg->id = 50;
+ msg->rcode = dns_rcode_noerror;
+ msg->flags = flags;
+
+ /*
+ * XXXMPA: this hack needs to be replaced with use of
+ * dns_message_reply() at some point.
+ */
+ if ((flags & DNS_MESSAGEFLAG_QR) != 0) {
+ msg->verified_sig = 1;
+ }
+
+ if (tsigin == tsigout) {
+ msg->tcp_continuation = 1;
+ }
+
+ if (tsigctx == NULL) {
+ result = dns_message_settsigkey(msg, key);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = dns_message_setquerytsig(msg, *tsigin);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ }
+
+ result = dns_compress_init(&cctx, -1, dt_mctx);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = dns_message_renderbegin(msg, &cctx, buf);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = dns_message_renderend(msg);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ if (tsigctx != NULL) {
+ isc_region_t r;
+
+ isc_buffer_usedregion(buf, &r);
+ result = dst_context_adddata(tsigctx, &r);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ } else {
+ if (tsigin == tsigout && *tsigin != NULL) {
+ isc_buffer_free(tsigin);
+ }
+
+ result = dns_message_getquerytsig(msg, dt_mctx, tsigout);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ }
+
+ dns_compress_invalidate(&cctx);
+ dns_message_detach(&msg);
+}
+
+/*
+ * Test tsig tcp-continuation validation:
+ * Check that a simulated three message TCP sequence where the first
+ * and last messages contain TSIGs but the intermediate message doesn't
+ * correctly verifies.
+ */
+static void
+tsig_tcp_test(void **state) {
+ const dns_name_t *tsigowner = NULL;
+ dns_fixedname_t fkeyname;
+ dns_message_t *msg = NULL;
+ dns_name_t *keyname;
+ dns_tsig_keyring_t *ring = NULL;
+ dns_tsigkey_t *key = NULL;
+ isc_buffer_t *buf = NULL;
+ isc_buffer_t *querytsig = NULL;
+ isc_buffer_t *tsigin = NULL;
+ isc_buffer_t *tsigout = NULL;
+ isc_result_t result;
+ unsigned char secret[16] = { 0 };
+ dst_context_t *tsigctx = NULL;
+ dst_context_t *outctx = NULL;
+
+ UNUSED(state);
+
+ /* isc_log_setdebuglevel(lctx, 99); */
+
+ keyname = dns_fixedname_initname(&fkeyname);
+ result = dns_name_fromstring(keyname, "test", 0, NULL);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = dns_tsigkeyring_create(dt_mctx, &ring);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = dns_tsigkey_create(keyname, dns_tsig_hmacsha256_name, secret,
+ sizeof(secret), false, NULL, 0, 0, dt_mctx,
+ ring, &key);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ assert_non_null(key);
+
+ /*
+ * Create request.
+ */
+ isc_buffer_allocate(dt_mctx, &buf, 65535);
+ render(buf, 0, key, &tsigout, &querytsig, NULL);
+ isc_buffer_free(&buf);
+
+ /*
+ * Create response message 1.
+ */
+ isc_buffer_allocate(dt_mctx, &buf, 65535);
+ render(buf, DNS_MESSAGEFLAG_QR, key, &querytsig, &tsigout, NULL);
+ assert_non_null(tsigout);
+
+ /*
+ * Process response message 1.
+ */
+ dns_message_create(dt_mctx, DNS_MESSAGE_INTENTPARSE, &msg);
+ assert_non_null(msg);
+
+ result = dns_message_settsigkey(msg, key);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = dns_message_parse(msg, buf, 0);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ printmessage(msg);
+
+ result = dns_message_setquerytsig(msg, querytsig);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = dns_tsig_verify(buf, msg, NULL, NULL);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ assert_int_equal(msg->verified_sig, 1);
+ assert_int_equal(msg->tsigstatus, dns_rcode_noerror);
+
+ /*
+ * Check that we have a TSIG in the first message.
+ */
+ assert_non_null(dns_message_gettsig(msg, &tsigowner));
+
+ result = dns_message_getquerytsig(msg, dt_mctx, &tsigin);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ tsigctx = msg->tsigctx;
+ msg->tsigctx = NULL;
+ isc_buffer_free(&buf);
+ dns_message_detach(&msg);
+
+ result = dst_context_create(key->key, dt_mctx, DNS_LOGCATEGORY_DNSSEC,
+ false, 0, &outctx);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ assert_non_null(outctx);
+
+ /*
+ * Start digesting.
+ */
+ result = add_mac(outctx, tsigout);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ /*
+ * Create response message 2.
+ */
+ isc_buffer_allocate(dt_mctx, &buf, 65535);
+
+ assert_int_equal(result, ISC_R_SUCCESS);
+ render(buf, DNS_MESSAGEFLAG_QR, key, &tsigout, &tsigout, outctx);
+
+ /*
+ * Process response message 2.
+ */
+ dns_message_create(dt_mctx, DNS_MESSAGE_INTENTPARSE, &msg);
+ assert_non_null(msg);
+
+ msg->tcp_continuation = 1;
+ msg->tsigctx = tsigctx;
+ tsigctx = NULL;
+
+ result = dns_message_settsigkey(msg, key);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = dns_message_parse(msg, buf, 0);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ printmessage(msg);
+
+ result = dns_message_setquerytsig(msg, tsigin);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = dns_tsig_verify(buf, msg, NULL, NULL);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ assert_int_equal(msg->verified_sig, 0);
+ assert_int_equal(msg->tsigstatus, dns_rcode_noerror);
+
+ /*
+ * Check that we don't have a TSIG in the second message.
+ */
+ tsigowner = NULL;
+ assert_true(dns_message_gettsig(msg, &tsigowner) == NULL);
+
+ tsigctx = msg->tsigctx;
+ msg->tsigctx = NULL;
+ isc_buffer_free(&buf);
+ dns_message_detach(&msg);
+
+ /*
+ * Create response message 3.
+ */
+ isc_buffer_allocate(dt_mctx, &buf, 65535);
+ render(buf, DNS_MESSAGEFLAG_QR, key, &tsigout, &tsigout, outctx);
+
+ result = add_tsig(outctx, key, buf);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ /*
+ * Process response message 3.
+ */
+ dns_message_create(dt_mctx, DNS_MESSAGE_INTENTPARSE, &msg);
+ assert_non_null(msg);
+
+ msg->tcp_continuation = 1;
+ msg->tsigctx = tsigctx;
+ tsigctx = NULL;
+
+ result = dns_message_settsigkey(msg, key);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = dns_message_parse(msg, buf, 0);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ printmessage(msg);
+
+ /*
+ * Check that we had a TSIG in the third message.
+ */
+ assert_non_null(dns_message_gettsig(msg, &tsigowner));
+
+ result = dns_message_setquerytsig(msg, tsigin);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = dns_tsig_verify(buf, msg, NULL, NULL);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ assert_int_equal(msg->verified_sig, 1);
+ assert_int_equal(msg->tsigstatus, dns_rcode_noerror);
+
+ if (tsigin != NULL) {
+ isc_buffer_free(&tsigin);
+ }
+
+ result = dns_message_getquerytsig(msg, dt_mctx, &tsigin);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ isc_buffer_free(&buf);
+ dns_message_detach(&msg);
+
+ if (outctx != NULL) {
+ dst_context_destroy(&outctx);
+ }
+ if (querytsig != NULL) {
+ isc_buffer_free(&querytsig);
+ }
+ if (tsigin != NULL) {
+ isc_buffer_free(&tsigin);
+ }
+ if (tsigout != NULL) {
+ isc_buffer_free(&tsigout);
+ }
+ dns_tsigkey_detach(&key);
+ if (ring != NULL) {
+ dns_tsigkeyring_detach(&ring);
+ }
+}
+
+/* Tests the dns__tsig_algvalid function */
+static void
+algvalid_test(void **state) {
+ UNUSED(state);
+
+ assert_true(dns__tsig_algvalid(DST_ALG_HMACMD5));
+
+ assert_true(dns__tsig_algvalid(DST_ALG_HMACSHA1));
+ assert_true(dns__tsig_algvalid(DST_ALG_HMACSHA224));
+ assert_true(dns__tsig_algvalid(DST_ALG_HMACSHA256));
+ assert_true(dns__tsig_algvalid(DST_ALG_HMACSHA384));
+ assert_true(dns__tsig_algvalid(DST_ALG_HMACSHA512));
+
+ assert_false(dns__tsig_algvalid(DST_ALG_GSSAPI));
+}
+
+/* Tests the dns__tsig_algfromname function */
+static void
+algfromname_test(void **state) {
+ UNUSED(state);
+
+ assert_int_equal(dns__tsig_algfromname(DNS_TSIG_HMACMD5_NAME),
+ DST_ALG_HMACMD5);
+ assert_int_equal(dns__tsig_algfromname(DNS_TSIG_HMACSHA1_NAME),
+ DST_ALG_HMACSHA1);
+ assert_int_equal(dns__tsig_algfromname(DNS_TSIG_HMACSHA224_NAME),
+ DST_ALG_HMACSHA224);
+ assert_int_equal(dns__tsig_algfromname(DNS_TSIG_HMACSHA256_NAME),
+ DST_ALG_HMACSHA256);
+ assert_int_equal(dns__tsig_algfromname(DNS_TSIG_HMACSHA384_NAME),
+ DST_ALG_HMACSHA384);
+ assert_int_equal(dns__tsig_algfromname(DNS_TSIG_HMACSHA512_NAME),
+ DST_ALG_HMACSHA512);
+
+ assert_int_equal(dns__tsig_algfromname(DNS_TSIG_GSSAPI_NAME),
+ DST_ALG_GSSAPI);
+ assert_int_equal(dns__tsig_algfromname(DNS_TSIG_GSSAPIMS_NAME),
+ DST_ALG_GSSAPI);
+
+ assert_int_equal(dns__tsig_algfromname(dns_rootname), 0);
+}
+
+/* Tests the dns__tsig_algnamefromname function */
+
+/*
+ * Helper function to create a dns_name_t from a string and see if
+ * the dns__tsig_algnamefromname function can correctly match it against the
+ * static table of known algorithms.
+ */
+static void
+test_name(const char *name_string, const dns_name_t *expected) {
+ dns_name_t name;
+ dns_name_init(&name, NULL);
+ assert_int_equal(dns_name_fromstring(&name, name_string, 0, dt_mctx),
+ ISC_R_SUCCESS);
+ assert_ptr_equal(dns__tsig_algnamefromname(&name), expected);
+ dns_name_free(&name, dt_mctx);
+}
+
+static void
+algnamefromname_test(void **state) {
+ UNUSED(state);
+
+ /* test the standard algorithms */
+ test_name("hmac-md5.sig-alg.reg.int", DNS_TSIG_HMACMD5_NAME);
+ test_name("hmac-sha1", DNS_TSIG_HMACSHA1_NAME);
+ test_name("hmac-sha224", DNS_TSIG_HMACSHA224_NAME);
+ test_name("hmac-sha256", DNS_TSIG_HMACSHA256_NAME);
+ test_name("hmac-sha384", DNS_TSIG_HMACSHA384_NAME);
+ test_name("hmac-sha512", DNS_TSIG_HMACSHA512_NAME);
+
+ test_name("gss-tsig", DNS_TSIG_GSSAPI_NAME);
+ test_name("gss.microsoft.com", DNS_TSIG_GSSAPIMS_NAME);
+
+ /* try another name that isn't a standard algorithm name */
+ assert_null(dns__tsig_algnamefromname(dns_rootname));
+}
+
+/* Tests the dns__tsig_algallocated function */
+static void
+algallocated_test(void **state) {
+ UNUSED(state);
+
+ /* test the standard algorithms */
+ assert_false(dns__tsig_algallocated(DNS_TSIG_HMACMD5_NAME));
+ assert_false(dns__tsig_algallocated(DNS_TSIG_HMACSHA1_NAME));
+ assert_false(dns__tsig_algallocated(DNS_TSIG_HMACSHA224_NAME));
+ assert_false(dns__tsig_algallocated(DNS_TSIG_HMACSHA256_NAME));
+ assert_false(dns__tsig_algallocated(DNS_TSIG_HMACSHA384_NAME));
+ assert_false(dns__tsig_algallocated(DNS_TSIG_HMACSHA512_NAME));
+
+ assert_false(dns__tsig_algallocated(DNS_TSIG_HMACSHA512_NAME));
+ assert_false(dns__tsig_algallocated(DNS_TSIG_HMACSHA512_NAME));
+
+ /* try another name that isn't a standard algorithm name */
+ assert_true(dns__tsig_algallocated(dns_rootname));
+}
+
+int
+main(void) {
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(tsig_tcp_test, _setup,
+ _teardown),
+ cmocka_unit_test(algvalid_test),
+ cmocka_unit_test(algfromname_test),
+ cmocka_unit_test_setup_teardown(algnamefromname_test, _setup,
+ _teardown),
+ cmocka_unit_test(algallocated_test),
+ };
+
+ return (cmocka_run_group_tests(tests, NULL, NULL));
+}
+
+#else /* HAVE_CMOCKA */
+
+#include <stdio.h>
+
+int
+main(void) {
+ printf("1..0 # Skipped: cmocka not available\n");
+ return (SKIPPED_TEST_EXIT_CODE);
+}
+
+#endif /* if HAVE_CMOCKA */