--- /dev/null
+/*
+ * 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.
+ */
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/commandline.h>
+#include <isc/file.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/fixedname.h>
+#include <dns/message.h>
+#include <dns/name.h>
+#include <dns/rcode.h>
+#include <dns/tsig.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+
+#include "fuzz.h"
+
+bool debug = false;
+
+static isc_mem_t *mctx = NULL;
+
+/*
+ * Packet dumps of validily signed request ./IN/SOA
+ * requests.
+ *
+ * TSIG:
+ *
+ * 0x0000: 600b 0900 006a 1140 0000 0000 0000 0000
+ * 0x0010: 0000 0000 0000 0001 0000 0000 0000 0000
+ * 0x0020: 0000 0000 0000 0001 cc88 0035 006a 007d
+ * 0x0030: 1dfa 0000 0001 0000 0000 0001 0000 0600
+ * 0x0040: 0108 7473 6967 2d6b 6579 0000 fa00 ff00
+ * 0x0050: 0000 0000 3d0b 686d 6163 2d73 6861 3235
+ * 0x0060: 3600 0000 622a cce1 012c 0020 224d 5807
+ * 0x0070: 648d 1400 9d8e fc1c d049 55e9 cc90 2187
+ * 0x0080: 3b5f af5c 8899 dc27 c8df b34b 1dfa 0000
+ * 0x0090: 0000
+ *
+ * SIG(0):
+ *
+ * 0x0000: 6004 0e00 013f 1140 0000 0000 0000 0000
+ * 0x0010: 0000 0000 0000 0001 0000 0000 0000 0000
+ * 0x0020: 0000 0000 0000 0001 c0a7 0035 013f 0152
+ * 0x0030: 0000 0000 0001 0000 0000 0001 0000 0600
+ * 0x0040: 0100 0018 00ff 0000 0000 011b 0000 0800
+ * 0x0050: 0000 0000 622a ce0d 622a cbb5 da71 0773
+ * 0x0060: 6967 306b 6579 0068 988b 27bf 5c89 5270
+ * 0x0070: c5ba ea8b 2e10 0512 9b44 48d3 69de b7ec
+ * 0x0080: 7c67 15f3 6bc7 b0dc 277b e8f1 6979 4c89
+ * 0x0090: 149a 0203 30a1 c0b7 a711 ee8a 8d90 ebb9
+ * 0x00a0: 9e33 dd65 33d5 5d1d 90db cf9c bb6a b346
+ * 0x00b0: 568f a399 71d7 c877 616d 2fb7 0f86 963f
+ * 0x00c0: aa00 850d 180a 9f83 cd4b d115 c79f 64c9
+ * 0x00d0: ff05 e751 6810 28b3 2249 c4ba 2d8d 57ba
+ * 0x00e0: 9aad f1fc b34e c237 9465 04fd fe4d 19c9
+ * 0x00f0: 2368 ec8e 7097 eaea e067 2b9c 06eb c383
+ * 0x0100: e901 a11e 606b 4cce c12a 0e57 8c09 b7cb
+ * 0x0110: 23bb ec05 b68b 1852 9288 b665 fe89 cf62
+ * 0x0120: 0a41 5e5a acbe 6903 cbb7 e7b6 cab4 e4a2
+ * 0x0130: b98f 884f c09d 5b39 c695 c84c 9a92 f110
+ * 0x0140: ccc3 f2ee 313f a2a1 1cda 5aa2 faec d593
+ * 0x0150: 4514 724a 868f 94b9 0547 4dc9 7b73 c85e
+ * 0x0160: 544c 73d4 e892 f9
+ */
+
+#define HMACSHA256 "\x0bhmac-sha256"
+
+static isc_stdtime_t fuzztime = 0x622acce1;
+static dns_view_t *view = NULL;
+static dns_tsigkey_t *tsigkey = NULL;
+static dns_tsig_keyring_t *ring = NULL;
+static dns_tsig_keyring_t *emptyring = NULL;
+
+static void
+cleanup(void) {
+ if (view != NULL) {
+ dns_view_detach(&view);
+ }
+ if (tsigkey != NULL) {
+ dns_tsigkey_detach(&tsigkey);
+ }
+ if (ring != NULL) {
+ dns_tsigkeyring_detach(&ring);
+ }
+ if (emptyring != NULL) {
+ dns_tsigkeyring_detach(&emptyring);
+ }
+ if (mctx != NULL) {
+ isc_mem_detach(&mctx);
+ }
+}
+
+int
+LLVMFuzzerInitialize(int *argc __attribute__((unused)),
+ char ***argv __attribute__((unused))) {
+ isc_result_t result;
+ dns_fixedname_t fixed;
+ dns_name_t *name = dns_fixedname_initname(&fixed);
+ unsigned char secret[16] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff };
+ dns_zone_t *zone = NULL;
+
+ atexit(cleanup);
+
+ isc_mem_create(&mctx);
+
+ result = dst_lib_init(mctx, NULL);
+ if (result != ISC_R_SUCCESS) {
+ return (1);
+ }
+
+ result = dns_view_create(mctx, dns_rdataclass_in, "view", &view);
+ if (result != ISC_R_SUCCESS) {
+ return (1);
+ }
+
+ result = dns_tsigkeyring_create(mctx, &ring);
+ if (result != ISC_R_SUCCESS) {
+ return (1);
+ }
+
+ result = dns_tsigkeyring_create(mctx, &emptyring);
+ if (result != ISC_R_SUCCESS) {
+ return (1);
+ }
+
+ result = dns_name_fromstring(name, "tsig-key", 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ return (1);
+ }
+
+ result = dns_tsigkey_create(name, dns_tsig_hmacsha256_name, secret,
+ sizeof(secret), false, NULL, 0, 0, mctx,
+ ring, &tsigkey);
+ if (result != ISC_R_SUCCESS) {
+ return (1);
+ }
+
+ result = dns_name_fromstring(name, "sig0key", 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ return (1);
+ }
+
+ result = dns_zone_create(&zone, mctx);
+ if (result != ISC_R_SUCCESS) {
+ return (1);
+ }
+
+ result = dns_zone_setorigin(zone, name);
+ if (result != ISC_R_SUCCESS) {
+ return (1);
+ }
+
+ dns_zone_setclass(zone, view->rdclass);
+ dns_zone_settype(zone, dns_zone_primary);
+
+ result = dns_zone_setkeydirectory(zone, "dns_message_checksig.data");
+ if (result != ISC_R_SUCCESS) {
+ return (1);
+ }
+
+ result = dns_zone_setfile(zone, "dns_message_checksig.data/sig0key.db",
+ dns_masterformat_text,
+ &dns_master_style_default);
+ if (result != ISC_R_SUCCESS) {
+ return (1);
+ }
+
+ result = dns_zone_load(zone, false);
+ if (result != ISC_R_SUCCESS) {
+ return (1);
+ }
+
+ result = dns_view_addzone(view, zone);
+ if (result != ISC_R_SUCCESS) {
+ return (1);
+ }
+
+ dns_view_freeze(view);
+
+ dns_zone_detach(&zone);
+
+ return (0);
+}
+
+static isc_result_t
+create_message(dns_message_t **messagep, const uint8_t *data, size_t size,
+ bool addasig, bool addtsig) {
+ isc_result_t result;
+ dns_message_t *message = NULL;
+ isc_buffer_t b;
+ unsigned char buf[65535];
+
+ isc_buffer_init(&b, buf, sizeof(buf));
+
+ /* Message ID */
+ isc_buffer_putuint16(&b, 0);
+
+ /* QR, Opcode, other flags = 0, rcode = 0 */
+ isc_buffer_putuint16(&b, (*data & 0x1f) << 11);
+ /* Counts */
+ isc_buffer_putuint16(&b, 1);
+ isc_buffer_putuint16(&b, 0);
+ isc_buffer_putuint16(&b, 0);
+ isc_buffer_putuint16(&b, addasig ? 1 : 0);
+
+ /* Question ./IN/SOA */
+ isc_buffer_putuint8(&b, 0);
+ isc_buffer_putuint16(&b, 6);
+ isc_buffer_putuint16(&b, 1);
+
+ if (addasig) {
+ /* Signature */
+ if (addtsig) {
+ const unsigned char keyname[] = "\x08tsig-key";
+ isc_buffer_putmem(&b, keyname, sizeof(keyname));
+ isc_buffer_putuint16(&b, dns_rdatatype_tsig);
+ isc_buffer_putuint16(&b, dns_rdataclass_any);
+ } else {
+ isc_buffer_putuint8(&b, 0); /* '.' */
+ isc_buffer_putuint16(&b, dns_rdatatype_sig);
+ isc_buffer_putuint16(&b, dns_rdataclass_in);
+ }
+ isc_buffer_putuint32(&b, 0); /* ttl */
+ data++;
+ size--;
+ if (size > isc_buffer_availablelength(&b) - 2) {
+ size = isc_buffer_availablelength(&b) - 2;
+ }
+ isc_buffer_putuint16(&b, size);
+ isc_buffer_putmem(&b, data, size);
+ }
+
+ dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &message);
+
+ result = dns_message_parse(message, &b, 0);
+ if (debug) {
+ fprintf(stderr, "dns_message_parse => %s\n",
+ isc_result_totext(result));
+ }
+ if (result != ISC_R_SUCCESS) {
+ dns_message_detach(&message);
+ } else {
+ if (debug) {
+ char text[200000];
+ isc_buffer_init(&b, text, sizeof(text));
+
+ result = dns_message_totext(
+ message, &dns_master_style_debug, 0, &b);
+ if (result == ISC_R_SUCCESS) {
+ fprintf(stderr, "%.*s", (int)b.used, text);
+ } else {
+ fprintf(stderr, "dns_message_totext => %s\n",
+ isc_result_totext(result));
+ }
+ }
+ *messagep = message;
+ }
+ return (result);
+}
+
+int
+LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ isc_result_t result;
+ dns_message_t *message = NULL;
+ unsigned char query_tsig[23 + 32 + 6] = { 0 };
+ bool addasig = false;
+ bool addtime = false;
+ bool addtsig = false;
+ bool setquerytsig = false;
+ bool settsigkey = false;
+ bool subtime = false;
+ bool withring = false;
+ bool withview = false;
+
+ /*
+ * The first 2 octets affect setup.
+ * Octet 1 determines whether a signature is added and which type
+ * (addasig, addtsig), whether time should be adjusted (addtime,
+ * subtime), whether dns_message_setquerytsig and dns_message_settsigkey
+ * have been called, whether there is a keyring available with the
+ * TSIG key or a view is defined.
+ *
+ * The second octet defines if the message is a response and the
+ * opcode.
+ */
+ if (size > 65535 || size < 2) {
+ return (0);
+ }
+
+ addasig = (*data & 0x80) != 0;
+ addtime = (*data & 0x40) != 0;
+ addtsig = (*data & 0x20) != 0;
+ setquerytsig = (*data & 0x10) != 0;
+ settsigkey = (*data & 0x08) != 0;
+ subtime = (*data & 0x04) != 0;
+ withring = (*data & 0x02) != 0;
+ withview = (*data & 0x01) != 0;
+
+ data++;
+ size--;
+
+ if (debug) {
+ fprintf(stderr,
+ "addasig=%u addtime=%u addtsig=%u setquerytsig=%u "
+ "settsigkey=%u subtime=%u withring=%u\nwithview=%u\n",
+ addasig, addtime, addtsig, setquerytsig, settsigkey,
+ subtime, withring, withview);
+ }
+
+ result = create_message(&message, data, size, addasig, addtsig);
+ if (result != ISC_R_SUCCESS) {
+ return (0);
+ }
+
+ /*
+ * Make time calculations consistent.
+ */
+ message->fuzzing = 1;
+ message->fuzztime = fuzztime;
+ if (addtime) {
+ message->fuzztime += 1200;
+ }
+ if (subtime) {
+ message->fuzztime -= 1200;
+ }
+
+ if ((message->flags & DNS_MESSAGEFLAG_QR) != 0) {
+ if (setquerytsig) {
+ isc_buffer_t b;
+ unsigned char hmacname[] = HMACSHA256;
+ unsigned char hmac[32] = {
+ 0x22, 0x4d, 0x58, 0x07, 0x64, 0x8d, 0x14, 0x00,
+ 0x9d, 0x8e, 0xfc, 0x1c, 0xd0, 0x49, 0x55, 0xe9,
+ 0xcc, 0x90, 0x21, 0x87, 0x3b, 0x5f, 0xaf, 0x5c,
+ 0x88, 0x99, 0xdc, 0x27, 0xc8, 0xdf, 0xb3, 0x4b
+ };
+
+ /*
+ * Valid TSIG rdata for tsig-key over a plain
+ * DNS QUERY for ./SOA/IN with no flags set.
+ */
+ isc_buffer_init(&b, query_tsig, sizeof(query_tsig));
+ isc_buffer_putmem(&b, hmacname, sizeof(hmacname));
+ isc_buffer_putuint16(&b, 0); /* time high */
+ isc_buffer_putuint32(&b, 0x622abec0); /* time low */
+ isc_buffer_putuint16(&b, 300); /* Fudge */
+ isc_buffer_putuint16(&b, 32); /* Mac Length */
+ /* Mac */
+ isc_buffer_putmem(&b, hmac, 32);
+ isc_buffer_putuint16(&b, 7674); /* Original Id */
+ isc_buffer_putuint16(&b, 0); /* Error */
+ isc_buffer_putuint16(&b, 0); /* Other len */
+
+ dns_message_setquerytsig(message, &b);
+ }
+ }
+
+ if (settsigkey) {
+ result = dns_message_settsigkey(message, tsigkey);
+ if (debug) {
+ fprintf(stderr, "dns_message_settsigkey => %s\n",
+ isc_result_totext(result));
+ }
+ }
+
+ dns_view_setkeyring(view, withring ? ring : emptyring);
+
+ result = dns_message_checksig(message, withview ? view : NULL);
+ if (debug) {
+ char textbuf[64];
+ isc_buffer_t b;
+
+ fprintf(stderr, "dns_message_checksig => %s\n",
+ isc_result_totext(result));
+ isc_buffer_init(&b, textbuf, sizeof(textbuf));
+ dns_tsigrcode_totext(message->tsigstatus, &b);
+ fprintf(stderr, "tsigstatus=%.*s\n", (int)b.used, textbuf);
+ isc_buffer_init(&b, textbuf, sizeof(textbuf));
+ dns_tsigrcode_totext(message->sig0status, &b);
+ fprintf(stderr, "sig0status=%.*s\n", (int)b.used, textbuf);
+ }
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+cleanup:
+ if (message != NULL) {
+ dns_message_detach(&message);
+ }
+
+ return (0);
+}