mirror of
https://github.com/isc-projects/bind9.git
synced 2026-05-23 10:37:43 -04:00
If an invalid SKR file is imported, reading the time from the token buffer might overflow the buffer on the local stack. This has been fixed by removing the intermediate buffer and parsing the lexer token directly.
415 lines
10 KiB
C
415 lines
10 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
|
|
/*! \file */
|
|
|
|
#include <isc/lex.h>
|
|
#include <isc/log.h>
|
|
|
|
#include <dns/callbacks.h>
|
|
#include <dns/fixedname.h>
|
|
#include <dns/rdata.h>
|
|
#include <dns/rdataclass.h>
|
|
#include <dns/rdatatype.h>
|
|
#include <dns/skr.h>
|
|
#include <dns/time.h>
|
|
#include <dns/ttl.h>
|
|
|
|
#define READLINE(lex, opt, token)
|
|
|
|
#define NEXTTOKEN(lex, opt, token) CHECK(isc_lex_gettoken(lex, opt, token))
|
|
|
|
#define BADTOKEN() CLEANUP(ISC_R_UNEXPECTEDTOKEN)
|
|
|
|
#define TOKENSIZ (8 * 1024)
|
|
#define STR(t) ((t).value.as_textregion.base)
|
|
|
|
static isc_result_t
|
|
parse_rr(isc_lex_t *lex, isc_mem_t *mctx, char *owner, dns_name_t *origin,
|
|
dns_rdataclass_t rdclass, isc_buffer_t *buf, dns_ttl_t *ttl,
|
|
dns_rdatatype_t *rdtype, dns_rdata_t **rdata) {
|
|
dns_rdatacallbacks_t callbacks;
|
|
dns_fixedname_t dfname;
|
|
dns_name_t *dname = NULL;
|
|
dns_rdataclass_t clas;
|
|
isc_buffer_t b;
|
|
isc_token_t token;
|
|
unsigned int opt = ISC_LEXOPT_EOL;
|
|
isc_result_t result = ISC_R_SUCCESS;
|
|
|
|
isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE);
|
|
|
|
/* Read the domain name */
|
|
if (!strcmp(owner, "@")) {
|
|
BADTOKEN();
|
|
}
|
|
dname = dns_fixedname_initname(&dfname);
|
|
isc_buffer_init(&b, owner, strlen(owner));
|
|
isc_buffer_add(&b, strlen(owner));
|
|
CHECK(dns_name_fromtext(dname, &b, dns_rootname, 0));
|
|
if (dns_name_compare(dname, origin) != 0) {
|
|
CLEANUP(DNS_R_BADOWNERNAME);
|
|
}
|
|
isc_buffer_clear(&b);
|
|
|
|
/* Read the next word: either TTL, class, or type */
|
|
NEXTTOKEN(lex, opt, &token);
|
|
if (token.type != isc_tokentype_string) {
|
|
BADTOKEN();
|
|
}
|
|
|
|
/* If it's a TTL, read the next one */
|
|
result = dns_ttl_fromtext(&token.value.as_textregion, ttl);
|
|
if (result == ISC_R_SUCCESS) {
|
|
NEXTTOKEN(lex, opt, &token);
|
|
}
|
|
if (token.type != isc_tokentype_string) {
|
|
BADTOKEN();
|
|
}
|
|
|
|
/* If it's a class, read the next one */
|
|
result = dns_rdataclass_fromtext(&clas, &token.value.as_textregion);
|
|
if (result == ISC_R_SUCCESS) {
|
|
if (clas != rdclass) {
|
|
BADTOKEN();
|
|
}
|
|
NEXTTOKEN(lex, opt, &token);
|
|
}
|
|
if (token.type != isc_tokentype_string) {
|
|
BADTOKEN();
|
|
}
|
|
|
|
/* Must be the record type */
|
|
result = dns_rdatatype_fromtext(rdtype, &token.value.as_textregion);
|
|
if (result != ISC_R_SUCCESS) {
|
|
BADTOKEN();
|
|
}
|
|
switch (*rdtype) {
|
|
case dns_rdatatype_dnskey:
|
|
case dns_rdatatype_cdnskey:
|
|
case dns_rdatatype_cds:
|
|
case dns_rdatatype_rrsig:
|
|
/* Allowed record types */
|
|
break;
|
|
default:
|
|
BADTOKEN();
|
|
}
|
|
|
|
dns_rdatacallbacks_init(&callbacks);
|
|
result = dns_rdata_fromtext(*rdata, rdclass, *rdtype, lex, dname, 0,
|
|
mctx, buf, &callbacks);
|
|
cleanup:
|
|
isc_lex_setcomments(lex, 0);
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
skrbundle_create(isc_mem_t *mctx, isc_stdtime_t inception,
|
|
dns_skrbundle_t **bp) {
|
|
dns_skrbundle_t *b;
|
|
|
|
REQUIRE(bp != NULL && *bp == NULL);
|
|
|
|
b = isc_mem_get(mctx, sizeof(*b));
|
|
b->magic = DNS_SKRBUNDLE_MAGIC;
|
|
b->inception = inception;
|
|
dns_diff_init(mctx, &b->diff);
|
|
|
|
ISC_LINK_INIT(b, link);
|
|
|
|
*bp = b;
|
|
}
|
|
|
|
static void
|
|
skrbundle_addtuple(dns_skrbundle_t *bundle, dns_difftuple_t **tuple) {
|
|
REQUIRE(DNS_DIFFTUPLE_VALID(*tuple));
|
|
REQUIRE(DNS_SKRBUNDLE_VALID(bundle));
|
|
REQUIRE(DNS_DIFF_VALID(&bundle->diff));
|
|
|
|
dns_diff_append(&bundle->diff, tuple);
|
|
}
|
|
|
|
isc_result_t
|
|
dns_skrbundle_getsig(dns_skrbundle_t *bundle, dst_key_t *key,
|
|
dns_rdatatype_t covering_type, dns_rdata_t *sigrdata) {
|
|
REQUIRE(DNS_SKRBUNDLE_VALID(bundle));
|
|
REQUIRE(DNS_DIFF_VALID(&bundle->diff));
|
|
|
|
ISC_LIST_FOREACH(bundle->diff.tuples, tuple, link) {
|
|
dns_rdata_rrsig_t rrsig;
|
|
|
|
if (tuple->op != DNS_DIFFOP_ADDRESIGN) {
|
|
continue;
|
|
}
|
|
INSIST(tuple->rdata.type == dns_rdatatype_rrsig);
|
|
|
|
RETERR(dns_rdata_tostruct(&tuple->rdata, &rrsig, NULL));
|
|
|
|
/*
|
|
* Check if covering type matches, and if the signature is
|
|
* generated by 'key'.
|
|
*/
|
|
if (rrsig.covered == covering_type &&
|
|
rrsig.keyid == dst_key_id(key))
|
|
{
|
|
dns_rdata_clone(&tuple->rdata, sigrdata);
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return ISC_R_NOTFOUND;
|
|
}
|
|
|
|
void
|
|
dns_skr_create(isc_mem_t *mctx, const char *filename, dns_name_t *origin,
|
|
dns_rdataclass_t rdclass, dns_skr_t **skrp) {
|
|
isc_time_t now;
|
|
dns_skr_t *skr = NULL;
|
|
|
|
REQUIRE(skrp != NULL && *skrp == NULL);
|
|
REQUIRE(mctx != NULL);
|
|
|
|
UNUSED(origin);
|
|
UNUSED(rdclass);
|
|
|
|
now = isc_time_now();
|
|
skr = isc_mem_get(mctx, sizeof(*skr));
|
|
*skr = (dns_skr_t){
|
|
.magic = DNS_SKR_MAGIC,
|
|
.filename = isc_mem_strdup(mctx, filename),
|
|
.loadtime = now,
|
|
};
|
|
/*
|
|
* A list is not the best structure to store bundles that
|
|
* we need to look up, but we don't expect many bundles
|
|
* per SKR so it is acceptable for now.
|
|
*/
|
|
ISC_LIST_INIT(skr->bundles);
|
|
|
|
isc_mem_attach(mctx, &skr->mctx);
|
|
isc_refcount_init(&skr->references, 1);
|
|
*skrp = skr;
|
|
}
|
|
|
|
static void
|
|
addbundle(dns_skr_t *skr, dns_skrbundle_t **bundlep) {
|
|
REQUIRE(DNS_SKR_VALID(skr));
|
|
REQUIRE(DNS_SKRBUNDLE_VALID(*bundlep));
|
|
|
|
ISC_LIST_APPEND(skr->bundles, *bundlep, link);
|
|
*bundlep = NULL;
|
|
}
|
|
|
|
isc_result_t
|
|
dns_skr_read(isc_mem_t *mctx, const char *filename, dns_name_t *origin,
|
|
dns_rdataclass_t rdclass, dns_ttl_t dnskeyttl, dns_skr_t **skrp) {
|
|
isc_result_t result;
|
|
dns_skrbundle_t *bundle = NULL;
|
|
uint32_t bundle_id;
|
|
isc_lex_t *lex = NULL;
|
|
isc_lexspecials_t specials;
|
|
isc_token_t token;
|
|
unsigned int opt = ISC_LEXOPT_EOL;
|
|
|
|
REQUIRE(DNS_SKR_VALID(*skrp));
|
|
|
|
isc_lex_create(mctx, TOKENSIZ, &lex);
|
|
memset(specials, 0, sizeof(specials));
|
|
specials['('] = 1;
|
|
specials[')'] = 1;
|
|
specials['"'] = 1;
|
|
isc_lex_setspecials(lex, specials);
|
|
result = isc_lex_openfile(lex, filename);
|
|
if (result != ISC_R_SUCCESS) {
|
|
isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
|
|
ISC_LOG_ERROR, "unable to open ksr file %s: %s",
|
|
filename, isc_result_totext(result));
|
|
isc_lex_destroy(&lex);
|
|
return result;
|
|
}
|
|
|
|
for (result = isc_lex_gettoken(lex, opt, &token);
|
|
result == ISC_R_SUCCESS;
|
|
result = isc_lex_gettoken(lex, opt, &token))
|
|
{
|
|
if (token.type == isc_tokentype_eol) {
|
|
continue;
|
|
}
|
|
|
|
if (token.type != isc_tokentype_string) {
|
|
CLEANUP(DNS_R_SYNTAX);
|
|
}
|
|
|
|
if (strcmp(STR(token), ";;") == 0) {
|
|
/* New bundle */
|
|
CHECK(isc_lex_gettoken(lex, opt, &token));
|
|
if (token.type != isc_tokentype_string ||
|
|
strcmp(STR(token), "SignedKeyResponse") != 0)
|
|
{
|
|
CLEANUP(DNS_R_SYNTAX);
|
|
}
|
|
|
|
/* Version */
|
|
CHECK(isc_lex_gettoken(lex, opt, &token));
|
|
if (token.type != isc_tokentype_string ||
|
|
strcmp(STR(token), "1.0") != 0)
|
|
{
|
|
CLEANUP(DNS_R_SYNTAX);
|
|
}
|
|
|
|
/* Date and time of bundle */
|
|
CHECK(isc_lex_gettoken(lex, opt, &token));
|
|
if (token.type != isc_tokentype_string) {
|
|
CLEANUP(DNS_R_SYNTAX);
|
|
}
|
|
if (strcmp(STR(token), "generated") == 0) {
|
|
/* Final bundle */
|
|
goto readline;
|
|
}
|
|
if (token.type != isc_tokentype_string) {
|
|
CLEANUP(DNS_R_SYNTAX);
|
|
}
|
|
|
|
/* Add previous bundle */
|
|
if (bundle != NULL) {
|
|
addbundle(*skrp, &bundle);
|
|
}
|
|
|
|
/* Create new bundle */
|
|
CHECK(dns_time32_fromtext(STR(token), &bundle_id));
|
|
bundle = NULL;
|
|
skrbundle_create(mctx, (isc_stdtime_t)bundle_id,
|
|
&bundle);
|
|
|
|
readline:
|
|
/* Read remainder of header line */
|
|
do {
|
|
CHECK(isc_lex_gettoken(lex, opt, &token));
|
|
} while (token.type != isc_tokentype_eol);
|
|
} else {
|
|
isc_buffer_t buf;
|
|
dns_rdata_t *rdata = NULL;
|
|
u_char rdatabuf[DST_KEY_MAXSIZE];
|
|
dns_rdatatype_t rdtype;
|
|
|
|
/* Parse record */
|
|
rdata = isc_mem_get(mctx, sizeof(*rdata));
|
|
dns_rdata_init(rdata);
|
|
isc_buffer_init(&buf, rdatabuf, sizeof(rdatabuf));
|
|
result = parse_rr(lex, mctx, STR(token), origin,
|
|
rdclass, &buf, &dnskeyttl, &rdtype,
|
|
&rdata);
|
|
if (result != ISC_R_SUCCESS) {
|
|
isc_log_write(
|
|
DNS_LOGCATEGORY_GENERAL,
|
|
DNS_LOGMODULE_ZONE, ISC_LOG_DEBUG(1),
|
|
"read skr file %s(%lu) parse rr "
|
|
"failed: %s",
|
|
filename, isc_lex_getsourceline(lex),
|
|
isc_result_totext(result));
|
|
isc_mem_put(mctx, rdata, sizeof(*rdata));
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Create new diff tuple */
|
|
dns_diffop_t op = (rdtype == dns_rdatatype_rrsig)
|
|
? DNS_DIFFOP_ADDRESIGN
|
|
: DNS_DIFFOP_ADD;
|
|
dns_difftuple_t *tuple = NULL;
|
|
|
|
dns_difftuple_create((*skrp)->mctx, op, origin,
|
|
dnskeyttl, rdata, &tuple);
|
|
|
|
skrbundle_addtuple(bundle, &tuple);
|
|
INSIST(tuple == NULL);
|
|
|
|
isc_mem_put(mctx, rdata, sizeof(*rdata));
|
|
}
|
|
}
|
|
|
|
if (result != ISC_R_EOF) {
|
|
CLEANUP(DNS_R_SYNTAX);
|
|
}
|
|
result = ISC_R_SUCCESS;
|
|
|
|
/* Add final bundle */
|
|
if (bundle != NULL) {
|
|
addbundle(*skrp, &bundle);
|
|
}
|
|
|
|
cleanup:
|
|
if (result != ISC_R_SUCCESS) {
|
|
isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_ZONE,
|
|
ISC_LOG_DEBUG(1),
|
|
"read skr file %s(%lu) failed: %s", filename,
|
|
isc_lex_getsourceline(lex),
|
|
isc_result_totext(result));
|
|
}
|
|
|
|
/* Clean up */
|
|
isc_lex_destroy(&lex);
|
|
return result;
|
|
}
|
|
|
|
dns_skrbundle_t *
|
|
dns_skr_lookup(dns_skr_t *skr, isc_stdtime_t time, uint32_t sigval) {
|
|
REQUIRE(DNS_SKR_VALID(skr));
|
|
|
|
ISC_LIST_FOREACH(skr->bundles, b, link) {
|
|
dns_skrbundle_t *next = ISC_LIST_NEXT(b, link);
|
|
isc_stdtime_t expired = (next != NULL)
|
|
? next->inception
|
|
: (b->inception + sigval);
|
|
if (b->inception <= time && time < expired) {
|
|
return b;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
dns_skr_attach(dns_skr_t *source, dns_skr_t **targetp) {
|
|
REQUIRE(DNS_SKR_VALID(source));
|
|
REQUIRE(targetp != NULL && *targetp == NULL);
|
|
|
|
isc_refcount_increment(&source->references);
|
|
*targetp = source;
|
|
}
|
|
|
|
void
|
|
dns_skr_detach(dns_skr_t **skrp) {
|
|
REQUIRE(skrp != NULL && DNS_SKR_VALID(*skrp));
|
|
|
|
dns_skr_t *skr = *skrp;
|
|
*skrp = NULL;
|
|
|
|
if (isc_refcount_decrement(&skr->references) == 1) {
|
|
dns_skr_destroy(skr);
|
|
}
|
|
}
|
|
|
|
void
|
|
dns_skr_destroy(dns_skr_t *skr) {
|
|
REQUIRE(DNS_SKR_VALID(skr));
|
|
|
|
ISC_LIST_FOREACH(skr->bundles, b, link) {
|
|
ISC_LIST_UNLINK(skr->bundles, b, link);
|
|
dns_diff_clear(&b->diff);
|
|
isc_mem_put(skr->mctx, b, sizeof(*b));
|
|
}
|
|
INSIST(ISC_LIST_EMPTY(skr->bundles));
|
|
|
|
isc_mem_free(skr->mctx, skr->filename);
|
|
isc_mem_putanddetach(&skr->mctx, skr, sizeof(*skr));
|
|
}
|