Commit 14648742 authored by Jan Včelák's avatar Jan Včelák 🚀

DNSSEC: move RR set signing into separate module

Slight cleanup and API changes included.

refs #4
parent df7a3091
......@@ -148,6 +148,8 @@ src/libknot/dnssec/nsec-bitmap.h
src/libknot/dnssec/nsec3.c
src/libknot/dnssec/nsec3.h
src/libknot/dnssec/policy.h
src/libknot/dnssec/rrset-sign.c
src/libknot/dnssec/rrset-sign.h
src/libknot/dnssec/sig0.c
src/libknot/dnssec/sig0.h
src/libknot/dnssec/sign.c
......
......@@ -156,6 +156,8 @@ libknot_la_SOURCES = \
libknot/dnssec/nsec3.c \
libknot/dnssec/nsec3.h \
libknot/dnssec/policy.h \
libknot/dnssec/rrset-sign.c \
libknot/dnssec/rrset-sign.h \
libknot/dnssec/sig0.c \
libknot/dnssec/sig0.h \
libknot/dnssec/sign.c \
......
This diff is collapsed.
/* Copyright (C) 2013 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*!
* \file rrsig-sign.h
*
* \author Jan Vcelak <jan.vcelak@nic.cz>
*
* \brief Interface for DNSSEC signing of RR sets.
*
* \addtogroup dnssec
* @{
*/
#ifndef _KNOT_DNSSEC_RRSET_SIGN_H_
#define _KNOT_DNSSEC_RRSET_SIGN_H_
#include <stdbool.h>
#include <stdlib.h>
#include "libknot/dnssec/policy.h"
#include "libknot/dnssec/sign.h"
#include "libknot/rrset.h"
/*!
* \brief Create RRSIG RR for given RR set.
*
* \param rrsigs RR set with RRSIGs into which the result will be added.
* \param covered RR set to create a new signature for.
* \param key Signing key.
* \param sign_ctx Signing context.
* \param policy DNSSEC policy.
*
* \return Error code, KNOT_EOK if successful.
*/
int knot_sign_rrset(knot_rrset_t *rrsigs,
const knot_rrset_t *covered,
const knot_dnssec_key_t *key,
knot_dnssec_sign_context_t *sign_ctx,
const knot_dnssec_policy_t *policy);
/*!
* \brief Check if RRSIG signature is valid.
*
* \param covered RRs covered by the signature.
* \param rrsigs RR set with RRSIGs.
* \param pos Number of RRSIG RR in 'rrsigs' to be validated.
* \param key Signing key.
* \param ctx Signing context.
* \param policy DNSSEC policy.
*
* \return Error code, KNOT_EOK if successful and the signature is valid.
* \retval KNOT_DNSSEC_EINVALID_SIGNATURE The signature is invalid.
*/
int knot_is_valid_signature(const knot_rrset_t *covered,
const knot_rrset_t *rrsigs, size_t pos,
const knot_dnssec_key_t *key,
knot_dnssec_sign_context_t *ctx,
const knot_dnssec_policy_t *policy);
#endif // _KNOT_DNSSEC_RRSET_SIGN_H_
/*! @} */
......@@ -26,190 +26,14 @@
#include "libknot/dname.h"
#include "libknot/dnssec/key.h"
#include "libknot/dnssec/policy.h"
#include "libknot/dnssec/rrset-sign.h"
#include "libknot/dnssec/sign.h"
#include "libknot/dnssec/zone-keys.h"
#include "libknot/rdata.h"
#include "libknot/updates/changesets.h"
#include "libknot/util/debug.h"
#include "libknot/zone/node.h"
#include "libknot/zone/zone-contents.h"
#include "libknot/util/debug.h"
//! \todo Check if defined elsewhere.
#define MAX_RR_WIREFORMAT_SIZE (64 * 1024 * sizeof(uint8_t))
#define RRSIG_RDATA_OFFSET 18
// COPIED FROM SIG(0) AND MODIFIED
static size_t rrsig_rdata_size(const knot_dnssec_key_t *key)
{
assert(key);
size_t size;
// static part
size = sizeof(uint16_t) // type covered
+ sizeof(uint8_t) // algorithm
+ sizeof(uint8_t) // labels
+ sizeof(uint32_t) // original TTL
+ sizeof(uint32_t) // signature expiration
+ sizeof(uint32_t) // signature inception
+ sizeof(uint16_t); // key tag (footprint)
// variable part
size += knot_dname_size(key->name);
size += knot_dnssec_sign_size(key);
return size;
}
// COPIED FROM SIG(0) AND MODIFIED
static void rrsig_write_rdata(uint8_t *rdata,
const knot_dnssec_key_t *key,
const knot_dname_t *owner,
const knot_rrset_t *covered,
uint32_t sig_incepted,
uint32_t sig_expires)
{
assert(key);
assert(rdata);
uint8_t *w = rdata;
uint8_t owner_labels = knot_dname_labels(owner, NULL);
if (knot_dname_is_wildcard(owner)) {
owner_labels -= 1;
}
knot_wire_write_u16(w, covered->type); // type covered
w += sizeof(uint16_t);
*w = key->algorithm; // algorithm
w += sizeof(uint8_t);
*w = owner_labels; // labels
w += sizeof(uint8_t);
knot_wire_write_u32(w, covered->ttl); // original TTL
w += sizeof(uint32_t);
knot_wire_write_u32(w, sig_expires); // signature expiration
w += sizeof(uint32_t);
knot_wire_write_u32(w, sig_incepted); // signature inception
w += sizeof(uint32_t);
knot_wire_write_u16(w, key->keytag); // key footprint
w += sizeof(uint16_t);
assert(w == rdata + 18);
memcpy(w, key->name, knot_dname_size(key->name)); // signer
}
static uint8_t *create_rrsigs_rdata(knot_rrset_t *rrsigs,
const knot_rrset_t *covered,
const knot_dnssec_key_t *key,
uint32_t sig_incept, uint32_t sig_expire)
{
uint8_t *rdata = knot_rrset_create_rdata(rrsigs, rrsig_rdata_size(key));
if (!rdata) {
return NULL;
}
rrsig_write_rdata(rdata, key, covered->owner, covered, sig_incept,
sig_expire);
return rdata;
}
static int sign_rrset_ctx_add_self(knot_dnssec_sign_context_t *ctx,
const uint8_t *rdata,
const knot_dnssec_key_t *key)
{
assert(ctx);
assert(key);
int result = knot_dnssec_sign_add(ctx, rdata, RRSIG_RDATA_OFFSET);
if (result != KNOT_EOK) {
return result;
}
return knot_dnssec_sign_add(ctx, key->name, knot_dname_size(key->name));
}
static int sign_rrset_ctx_add_records(knot_dnssec_sign_context_t *ctx,
const knot_rrset_t *covered)
{
// huge block of rrsets can be optionally created
uint8_t *rrwf = malloc(MAX_RR_WIREFORMAT_SIZE);
if (!rrwf) {
return KNOT_ENOMEM;
}
int result = KNOT_EOK;
uint16_t rr_count = covered->rdata_count;
for (uint16_t i = 0; i < rr_count; i++) {
size_t rr_size;
result = knot_rrset_to_wire_one(covered, i, rrwf,
MAX_RR_WIREFORMAT_SIZE,
&rr_size, NULL);
if (result != KNOT_EOK) {
break;
}
result = knot_dnssec_sign_add(ctx, rrwf, rr_size);
if (result != KNOT_EOK) {
break;
}
}
free(rrwf);
return result;
}
static int sign_rrset_ctx_add_data(knot_dnssec_sign_context_t *ctx,
const knot_dnssec_key_t *key,
const uint8_t *rrsig_rdata,
const knot_rrset_t *covered)
{
// RFC 4034: The signature covers RRSIG RDATA field (excluding the
// signature) and all matching RR records, which are ordered
// canonically.
int result = sign_rrset_ctx_add_self(ctx, rrsig_rdata, key);
if (result != KNOT_EOK) {
return result;
}
return sign_rrset_ctx_add_records(ctx, covered);
}
static int sign_rrset_one(knot_rrset_t *rrsigs,
const knot_rrset_t *covered,
const knot_dnssec_key_t *key,
knot_dnssec_sign_context_t *sign_ctx,
const knot_dnssec_policy_t *policy)
{
uint32_t sig_incept = policy->now;
uint32_t sig_expire = sig_incept + policy->sign_lifetime;
uint8_t *rdata = create_rrsigs_rdata(rrsigs, covered, key,
sig_incept, sig_expire);
if (!rdata) {
return KNOT_ENOMEM;
}
int result = knot_dnssec_sign_new(sign_ctx);
if (result != KNOT_EOK) {
return result;
}
result = sign_rrset_ctx_add_data(sign_ctx, key, rdata, covered);
if (result != KNOT_EOK) {
return result;
}
uint8_t *rdata_signature = rdata + RRSIG_RDATA_OFFSET
+ knot_dname_size(key->name);
return knot_dnssec_sign_write(sign_ctx, rdata_signature);
}
static knot_rrset_t *create_empty_rrsigs_for(const knot_rrset_t *covered)
{
......@@ -221,82 +45,6 @@ static knot_rrset_t *create_empty_rrsigs_for(const knot_rrset_t *covered)
covered->ttl);
}
static bool is_expired_signature(const knot_rrset_t *rrsigs, size_t pos,
const knot_dnssec_policy_t *policy)
{
assert(rrsigs);
assert(rrsigs->type == KNOT_RRTYPE_RRSIG);
assert(policy);
uint32_t now = policy->now;
uint32_t refresh = policy->sign_refresh;
uint32_t expiration = knot_rdata_rrsig_sig_expiration(rrsigs, pos);
return expiration - refresh <= now;
}
static bool is_valid_signature(const knot_rrset_t *covered,
const knot_rrset_t *rrsigs, size_t pos,
const knot_dnssec_key_t *key,
knot_dnssec_sign_context_t *ctx,
const knot_dnssec_policy_t *policy)
{
assert(covered);
assert(rrsigs);
assert(policy);
if (key == NULL || ctx == NULL) {
return false;
}
if (is_expired_signature(rrsigs, pos, policy)) {
return false;
}
// identify fields in the signature being validated
uint8_t *rdata = knot_rrset_get_rdata(rrsigs, pos);
const uint8_t *signer = knot_rdata_rrsig_signer_name(rrsigs, pos);
if (!rdata || !signer) {
return false;
}
size_t header_size = RRSIG_RDATA_OFFSET + knot_dname_size(signer);
uint8_t *signature = rdata + header_size;
size_t signature_size = rrset_rdata_item_size(rrsigs, pos) - header_size;
if (!signature || signature_size == 0) {
return false;
}
// perform the validation
int result = knot_dnssec_sign_new(ctx);
if (result != KNOT_EOK) {
return false;
}
result = knot_dnssec_sign_add(ctx, rdata, RRSIG_RDATA_OFFSET);
if (result != KNOT_EOK) {
return false;
}
result = knot_dnssec_sign_add(ctx, signer, knot_dname_size(signer));
if (result != KNOT_EOK) {
return false;
}
result = sign_rrset_ctx_add_records(ctx, covered);
if (result != KNOT_EOK) {
return false;
}
result = knot_dnssec_sign_verify(ctx, signature, signature_size);
return result == KNOT_EOK;
}
static bool valid_signature_exists(const knot_rrset_t *covered,
const knot_rrset_t *rrsigs,
const knot_dnssec_key_t *key,
......@@ -316,7 +64,8 @@ static bool valid_signature_exists(const knot_rrset_t *covered,
continue;
}
return is_valid_signature(covered, rrsigs, i, key, ctx, policy);
return knot_is_valid_signature(covered, rrsigs, i, key, ctx,
policy) == KNOT_EOK;
}
return false;
......@@ -390,18 +139,28 @@ static int remove_expired_rrsigs(const knot_rrset_t *covered,
knot_dnssec_sign_context_t *ctx = NULL;
get_matching_signing_data(rrsigs, i, zone_keys, &key, &ctx);
if (key && ctx && is_valid_signature(covered, rrsigs, i, key, ctx, policy))
continue;
result = knot_is_valid_signature(covered, rrsigs, i, key, ctx,
policy);
if (result == KNOT_EOK) {
continue; // valid signature
}
if (result != KNOT_DNSSEC_EINVALID_SIGNATURE) {
return result;
}
if (to_remove == NULL) {
to_remove = create_empty_rrsigs_for(rrsigs);
if (to_remove == NULL)
if (to_remove == NULL) {
return KNOT_ENOMEM;
}
}
result = knot_rrset_add_rr_from_rrset(to_remove, rrsigs, i);
if (result != KNOT_EOK)
if (result != KNOT_EOK) {
break;
}
}
if (to_remove != NULL && result == KNOT_EOK) {
......@@ -453,7 +212,7 @@ static int add_missing_rrsigs(const knot_rrset_t *covered,
}
}
result = sign_rrset_one(to_add, covered, key, ctx, policy);
result = knot_sign_rrset(to_add, covered, key, ctx, policy);
if (result != KNOT_EOK) {
break;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment