1
0
Fork 0
knot-resolver/lib/dnssec/ta.c
Daniel Baumann fbc604e215
Adding upstream version 5.7.5.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-21 13:56:17 +02:00

157 lines
4.7 KiB
C

/* Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <contrib/cleanup.h>
#include <libknot/descriptor.h>
#include <libknot/rdataset.h>
#include <libknot/rrset.h>
#include <libknot/packet/wire.h>
#include <libdnssec/key.h>
#include <libdnssec/error.h>
#include "lib/defines.h"
#include "lib/dnssec.h"
#include "lib/dnssec/ta.h"
#include "lib/resolve.h"
#include "lib/utils.h"
knot_rrset_t *kr_ta_get(trie_t *trust_anchors, const knot_dname_t *name)
{
trie_val_t *val = trie_get_try(trust_anchors, (const char *)name, strlen((const char *)name));
return (val) ? *val : NULL;
}
const knot_dname_t * kr_ta_closest(const struct kr_context *ctx, const knot_dname_t *name,
const uint16_t type)
{
kr_require(ctx && name);
if (type == KNOT_RRTYPE_DS && name[0] != '\0') {
/* DS is parent-side record, so the parent name needs to be covered. */
name = knot_dname_next_label(name);
}
do {
struct kr_context *ctx_nc = (struct kr_context *)/*const-cast*/ctx;
if (kr_ta_get(ctx_nc->trust_anchors, name)) {
return name;
}
if (kr_ta_get(ctx_nc->negative_anchors, name)) {
return NULL;
}
if (name[0] == '\0') {
return NULL;
} else {
name = knot_dname_next_label(name);
}
} while (true);
}
/* @internal Create DS from DNSKEY, caller MUST free dst if successful. */
static int dnskey2ds(dnssec_binary_t *dst, const knot_dname_t *owner, const uint8_t *rdata, uint16_t rdlen)
{
dnssec_key_t *key = NULL;
int ret = dnssec_key_new(&key);
if (ret) goto cleanup;
/* Create DS from DNSKEY and reinsert */
const dnssec_binary_t key_data = { .size = rdlen, .data = (uint8_t *)rdata };
ret = dnssec_key_set_rdata(key, &key_data);
if (ret) goto cleanup;
/* Accept only keys with Zone and SEP flags that aren't revoked,
* as a precaution. RFC 5011 also utilizes these flags.
* TODO: kr_dnssec_key_* names are confusing. */
const bool flags_ok = kr_dnssec_key_zsk(rdata) && !kr_dnssec_key_revoked(rdata);
if (!flags_ok) {
auto_free char *owner_str = kr_dname_text(owner);
kr_log_error(TA, "refusing to trust %s DNSKEY because of flags %d\n",
owner_str, dnssec_key_get_flags(key));
ret = kr_error(EILSEQ);
goto cleanup;
} else if (!kr_dnssec_key_ksk(rdata)) {
auto_free char *owner_str = kr_dname_text(owner);
int flags = dnssec_key_get_flags(key);
kr_log_warning(TA, "warning: %s DNSKEY is missing the SEP bit; "
"flags %d instead of %d\n",
owner_str, flags, flags + 1/*a little ugly*/);
}
ret = dnssec_key_set_dname(key, owner);
if (ret) goto cleanup;
ret = dnssec_key_create_ds(key, DNSSEC_KEY_DIGEST_SHA256, dst);
cleanup:
dnssec_key_free(key);
return kr_error(ret);
}
/* @internal Insert new TA to trust anchor set, rdata MUST be of DS type. */
static int insert_ta(trie_t *trust_anchors, const knot_dname_t *name,
uint32_t ttl, const uint8_t *rdata, uint16_t rdlen)
{
bool is_new_key = false;
knot_rrset_t *ta_rr = kr_ta_get(trust_anchors, name);
if (!ta_rr) {
ta_rr = knot_rrset_new(name, KNOT_RRTYPE_DS, KNOT_CLASS_IN, ttl, NULL);
is_new_key = true;
}
/* Merge-in new key data */
if (!ta_rr || (rdlen > 0 && knot_rrset_add_rdata(ta_rr, rdata, rdlen, NULL) != 0)) {
knot_rrset_free(ta_rr, NULL);
return kr_error(ENOMEM);
}
if (is_new_key) {
trie_val_t *val = trie_get_ins(trust_anchors, (const char *)name, strlen((const char *)name));
if (kr_fails_assert(val))
return kr_error(EINVAL);
*val = ta_rr;
}
return kr_ok();
}
int kr_ta_add(trie_t *trust_anchors, const knot_dname_t *name, uint16_t type,
uint32_t ttl, const uint8_t *rdata, uint16_t rdlen)
{
if (!trust_anchors || !name) {
return kr_error(EINVAL);
}
/* DS/DNSKEY types are accepted, for DNSKEY we
* need to compute a DS digest. */
if (type == KNOT_RRTYPE_DS) {
return insert_ta(trust_anchors, name, ttl, rdata, rdlen);
} else if (type == KNOT_RRTYPE_DNSKEY) {
dnssec_binary_t ds_rdata = { 0, };
int ret = dnskey2ds(&ds_rdata, name, rdata, rdlen);
if (ret != 0) {
return ret;
}
ret = insert_ta(trust_anchors, name, ttl, ds_rdata.data, ds_rdata.size);
dnssec_binary_free(&ds_rdata);
return ret;
} else { /* Invalid type for TA */
return kr_error(EINVAL);
}
}
/* Delete record data */
static int del_record(trie_val_t *v, void *ext)
{
knot_rrset_t *ta_rr = *v;
if (ta_rr) {
knot_rrset_free(ta_rr, NULL);
}
return 0;
}
int kr_ta_del(trie_t *trust_anchors, const knot_dname_t *name)
{
knot_rrset_t *ta_rr;
int ret = trie_del(trust_anchors, (const char *)name, strlen((const char *)name),
(trie_val_t *) &ta_rr);
if (ret == KNOT_EOK && ta_rr)
knot_rrset_free(ta_rr, NULL);
return kr_ok();
}
void kr_ta_clear(trie_t *trust_anchors)
{
trie_apply(trust_anchors, del_record, NULL);
trie_clear(trust_anchors);
}