From: Libor Peltan Date: Fri, 22 Jun 2018 15:03:56 +0000 (+0200) Subject: load: ignore dnssec-related records if load difference and signing enabled X-Git-Tag: v2.7.0~10^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=77f8a9eeddf35f3d2dc1e4efb19c460a11bf0af3;p=thirdparty%2Fknot-dns.git load: ignore dnssec-related records if load difference and signing enabled The difference ignores RRSIGs, NSECs, and NSEC3s. This is motivated by users willing to have zonefile w/o DNSSEC, but signed zone. If the final update consists only from changed SOA serial and RRSIG-SOA, clear it. This happens after automatic serial icrement, we avoid increment on simple reload. --- diff --git a/src/knot/events/handlers/load.c b/src/knot/events/handlers/load.c index b02b1cd533..f47d5c7403 100644 --- a/src/knot/events/handlers/load.c +++ b/src/knot/events/handlers/load.c @@ -119,6 +119,7 @@ int event_load(conf_t *conf, zone_t *zone) ret = zone_load_journal(conf, zone, zf_conts); if (ret != KNOT_EOK) { zone_contents_deep_free(zf_conts); + zf_conts = NULL; log_zone_warning(zone->name, "failed to load journal (%s)", knot_strerror(ret)); } @@ -138,6 +139,8 @@ int event_load(conf_t *conf, zone_t *zone) val = conf_zone_get(conf, C_DNSSEC_SIGNING, zone->name); bool dnssec_enable = conf_bool(&val), zu_from_zf_conts = false; zone_update_t up = { 0 }; + bool ignore_dnssec = ((zf_from == ZONEFILE_LOAD_DIFF || zf_from == ZONEFILE_LOAD_DIFSE) + && dnssec_enable); // Create zone_update structure according to current state. if (old_contents_exist) { @@ -153,7 +156,9 @@ int event_load(conf_t *conf, zone_t *zone) zu_from_zf_conts = true; } else { // compute ZF diff and if success, apply it - ret = zone_update_from_differences(&up, zone, zone->contents, zf_conts, UPDATE_INCREMENTAL); + ret = zone_update_from_differences(&up, zone, zone->contents, zf_conts, UPDATE_INCREMENTAL, ignore_dnssec); + zone_contents_deep_free(zf_conts); + zf_conts = NULL; } } else { if (journal_conts != NULL && zf_from != ZONEFILE_LOAD_WHOLE) { @@ -162,16 +167,18 @@ int event_load(conf_t *conf, zone_t *zone) ret = zone_update_from_contents(&up, zone, journal_conts, UPDATE_INCREMENTAL); } else { // load zone-in-journal, compute ZF diff and if success, apply it - ret = zone_update_from_differences(&up, zone, journal_conts, zf_conts, UPDATE_INCREMENTAL); + ret = zone_update_from_differences(&up, zone, journal_conts, zf_conts, UPDATE_INCREMENTAL, ignore_dnssec); + zone_contents_deep_free(zf_conts); + zf_conts = NULL; if (ret == KNOT_ESEMCHECK || ret == KNOT_ERANGE) { log_zone_warning(zone->name, "zone file changed with SOA serial %s, " "ignoring zone file and loading from journal", (ret == KNOT_ESEMCHECK ? "unupdated" : "decreased")); - zone_contents_deep_free(zf_conts); ret = zone_update_from_contents(&up, zone, journal_conts, UPDATE_INCREMENTAL); } else { zone_contents_deep_free(journal_conts); + journal_conts = NULL; } } } else { @@ -212,17 +219,6 @@ int event_load(conf_t *conf, zone_t *zone) zf_conts = NULL; journal_conts = NULL; - // If the change is only automatically incremented SOA serial, make it no change. - if (zf_from == ZONEFILE_LOAD_DIFSE && (up.flags & UPDATE_INCREMENTAL) && - changeset_differs_just_serial(&up.change)) { - uint32_t orig_ser = knot_soa_serial(&up.change.soa_from->rrs); - ret = changeset_remove_addition(&up.change, up.change.soa_to); - zone_contents_set_soa_serial(up.new_cont, orig_ser); - if (ret != KNOT_EOK) { - goto cleanup; - } - } - // Sign zone using DNSSEC if configured. zone_sign_reschedule_t dnssec_refresh = { .allow_rollover = true, .allow_nsec3resalt = true, }; if (dnssec_enable) { @@ -239,6 +235,24 @@ int event_load(conf_t *conf, zone_t *zone) } } + // If the change is only automatically incremented SOA serial, make it no change. + if (zf_from == ZONEFILE_LOAD_DIFSE && (up.flags & UPDATE_INCREMENTAL) && + changeset_differs_just_serial(&up.change)) { + changeset_t *cpy = changeset_clone(&up.change); + if (cpy == NULL) { + ret = KNOT_ENOMEM; + goto cleanup; + } + ret = zone_update_apply_changeset_reverse(&up, cpy); + changeset_free(cpy); + if (ret == KNOT_EOK) { + ret = changeset_remove_addition(&up.change, up.change.soa_to); + } + if (ret != KNOT_EOK) { + goto cleanup; + } + } + // Commit zone_update back to zone (including journal update, rcu,...). ret = zone_update_commit(conf, &up); zone_update_clear(&up); diff --git a/src/knot/updates/changesets.c b/src/knot/updates/changesets.c index b19e69fdca..393521485a 100644 --- a/src/knot/updates/changesets.c +++ b/src/knot/updates/changesets.c @@ -537,17 +537,26 @@ bool changeset_differs_just_serial(const changeset_t *ch) return false; } - if (!zone_contents_is_empty(ch->remove) || !zone_contents_is_empty(ch->add)) { - return false; - } - knot_rrset_t *soa_to_cpy = knot_rrset_copy(ch->soa_to, NULL); - knot_soa_serial_set(&soa_to_cpy->rrs, knot_soa_serial(&ch->soa_from->rrs)); + knot_soa_serial_set(soa_to_cpy->rrs.rdata, knot_soa_serial(ch->soa_from->rrs.rdata)); - bool res = knot_rrset_equal(ch->soa_from, soa_to_cpy, KNOT_RRSET_COMPARE_WHOLE); + bool ret = knot_rrset_equal(ch->soa_from, soa_to_cpy, KNOT_RRSET_COMPARE_WHOLE); knot_rrset_free(soa_to_cpy, NULL); - return res; + changeset_iter_t itt; + changeset_iter_all(&itt, ch); + + knot_rrset_t rrset = changeset_iter_next(&itt); + while (!knot_rrset_empty(&rrset) && ret) { + if (rrset.type != KNOT_RRTYPE_RRSIG || rrset.rrs.count != 1 || + knot_rrsig_type_covered(rrset.rrs.rdata) != KNOT_RRTYPE_SOA) { + ret = false; + } + rrset = changeset_iter_next(&itt); + } + changeset_iter_clear(&itt); + + return ret; } int changeset_to_contents(changeset_t *ch, zone_contents_t **out) @@ -648,6 +657,49 @@ void changeset_clear(changeset_t *ch) free(ch->data); } +changeset_t *changeset_clone(const changeset_t *ch) +{ + if (ch == NULL) { + return NULL; + } + + changeset_t *res = changeset_new(ch->soa_from->owner); + if (res == NULL) { + return NULL; + } + + res->soa_from = knot_rrset_copy(ch->soa_from, NULL); + res->soa_to = knot_rrset_copy(ch->soa_to, NULL); + + int ret = KNOT_EOK; + changeset_iter_t itt; + + changeset_iter_rem(&itt, ch); + knot_rrset_t rr = changeset_iter_next(&itt); + while (!knot_rrset_empty(&rr) && ret == KNOT_EOK) { + ret = changeset_add_removal(res, &rr, 0); + rr = changeset_iter_next(&itt); + } + changeset_iter_clear(&itt); + + changeset_iter_add(&itt, ch); + rr = changeset_iter_next(&itt); + while (!knot_rrset_empty(&rr) && ret == KNOT_EOK) { + ret = changeset_add_addition(res, &rr, 0); + rr = changeset_iter_next(&itt); + } + changeset_iter_clear(&itt); + + if ((ch->soa_from != NULL && res->soa_from == NULL) || + (ch->soa_to != NULL && res->soa_to == NULL) || + ret != KNOT_EOK) { + changeset_free(res); + return NULL; + } + + return res; +} + void changeset_free(changeset_t *ch) { changeset_clear(ch); diff --git a/src/knot/updates/changesets.h b/src/knot/updates/changesets.h index 4aa345b332..8002712666 100644 --- a/src/knot/updates/changesets.h +++ b/src/knot/updates/changesets.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 CZ.NIC, z.s.p.o. +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. 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 @@ -161,10 +161,13 @@ int changeset_cancelout(changeset_t *ch); /*! * \brief Check the changes and SOA, ignoring possibly updated SOA serial. * + * \note Also tolerates changed RRSIG of SOA. + * * \param ch Changeset in question. * - * \return false if the changeset changes other records than SOA, or some SOA field other than serial changed - * true otherwise + * \retval false If the changeset changes other records than SOA, or some SOA field + * other than serial changed. + * \retval true Otherwise. */ bool changeset_differs_just_serial(const changeset_t *ch); @@ -215,6 +218,15 @@ void changesets_free(list_t *chgs); */ void changeset_clear(changeset_t *ch); +/*! + * \brief Copy changeset to newly allocated space, all rrsigs are copied. + * + * \param ch Changeset to be copied. + * + * \return a copy, or NULL if error. + */ +changeset_t *changeset_clone(const changeset_t *ch); + /*! * \brief Frees single changeset. * diff --git a/src/knot/updates/zone-update.c b/src/knot/updates/zone-update.c index 478436b414..3b6d2a0ad8 100644 --- a/src/knot/updates/zone-update.c +++ b/src/knot/updates/zone-update.c @@ -26,9 +26,9 @@ #include -static int init_incremental(zone_update_t *update, zone_t *zone) +static int init_incremental(zone_update_t *update, zone_t *zone, zone_contents_t *old_contents) { - if (zone->contents == NULL) { + if (old_contents == NULL) { return KNOT_EINVAL; } @@ -39,7 +39,7 @@ static int init_incremental(zone_update_t *update, zone_t *zone) update->new_cont_deep_copy = false; - ret = apply_prepare_zone_copy(zone->contents, &update->new_cont); + ret = apply_prepare_zone_copy(old_contents, &update->new_cont); if (ret != KNOT_EOK) { changeset_clear(&update->change); return ret; @@ -50,7 +50,7 @@ static int init_incremental(zone_update_t *update, zone_t *zone) /* Copy base SOA RR. */ update->change.soa_from = - node_create_rrset(zone->contents->apex, KNOT_RRTYPE_SOA); + node_create_rrset(old_contents->apex, KNOT_RRTYPE_SOA); if (update->change.soa_from == NULL) { zone_contents_free(update->new_cont); changeset_clear(&update->change); @@ -96,11 +96,10 @@ static int replace_soa(zone_contents_t *contents, const knot_rrset_t *rr) return ret; } -/* ------------------------------- API -------------------------------------- */ - -int zone_update_init(zone_update_t *update, zone_t *zone, zone_update_flags_t flags) +int init_base(zone_update_t *update, zone_t *zone, zone_contents_t *old_contents, + zone_update_flags_t flags) { - if (update == NULL || zone == NULL) { + if (update == NULL || zone == NULL || (old_contents == NULL && (flags & UPDATE_INCREMENTAL))) { return KNOT_EINVAL; } @@ -117,7 +116,7 @@ int zone_update_init(zone_update_t *update, zone_t *zone, zone_update_flags_t fl int ret = KNOT_EINVAL; if (flags & UPDATE_INCREMENTAL) { - ret = init_incremental(update, zone); + ret = init_incremental(update, zone, old_contents); } else if (flags & UPDATE_FULL) { ret = init_full(update, zone); } @@ -128,45 +127,49 @@ int zone_update_init(zone_update_t *update, zone_t *zone, zone_update_flags_t fl return ret; } +/* ------------------------------- API -------------------------------------- */ + +int zone_update_init(zone_update_t *update, zone_t *zone, zone_update_flags_t flags) +{ + return init_base(update, zone, zone->contents, flags); +} + int zone_update_from_differences(zone_update_t *update, zone_t *zone, zone_contents_t *old_cont, - zone_contents_t *new_cont, zone_update_flags_t flags) + zone_contents_t *new_cont, zone_update_flags_t flags, bool ignore_dnssec) { if (update == NULL || zone == NULL || new_cont == NULL || !(flags & UPDATE_INCREMENTAL) || (flags & UPDATE_FULL)) { return KNOT_EINVAL; } - memset(update, 0, sizeof(*update)); - update->zone = zone; - - mm_ctx_mempool(&update->mm, MM_DEFAULT_BLKSIZE); - update->flags = flags; - - update->new_cont = new_cont; - update->new_cont_deep_copy = true; + changeset_t diff; + int ret = changeset_init(&diff, zone->name); + if (ret != KNOT_EOK) { + return ret; + } - update->a_ctx = calloc(1, sizeof(*update->a_ctx)); - if (update->a_ctx == NULL) { - return KNOT_ENOMEM; + ret = zone_contents_diff(old_cont, new_cont, &diff, ignore_dnssec); + if (ret != KNOT_EOK && ret != KNOT_ENODIFF && ret != KNOT_ESEMCHECK) { + changeset_clear(&diff); + return ret; } - int ret = changeset_init(&update->change, zone->name); + ret = init_base(update, zone, old_cont, flags); if (ret != KNOT_EOK) { - free(update->a_ctx); + changeset_clear(&diff); return ret; } - ret = zone_contents_diff(old_cont, new_cont, &update->change); - if (ret != KNOT_EOK && ret != KNOT_ENODIFF && ret != KNOT_ESEMCHECK) { - free(update->a_ctx); - changeset_clear(&update->change); + bool diff_semcheck = (ret == KNOT_ESEMCHECK); + + ret = zone_update_apply_changeset(update, &diff); + changeset_clear(&diff); + if (ret != KNOT_EOK) { + zone_update_clear(update); return ret; } - uint32_t apply_flags = update->flags & UPDATE_STRICT ? APPLY_STRICT : 0; - apply_init_ctx(update->a_ctx, update->new_cont, apply_flags); - - if (ret == KNOT_ESEMCHECK) { + if (diff_semcheck) { ret = zone_update_increment_soa(update, conf()); if (ret != KNOT_EOK) { zone_update_clear(update); @@ -522,6 +525,16 @@ int zone_update_apply_changeset_fix(zone_update_t *update, changeset_t *changes) return ret; } +int zone_update_apply_changeset_reverse(zone_update_t *update, const changeset_t *changes) +{ + changeset_t reverse; + reverse.remove = changes->add; + reverse.add = changes->remove; + reverse.soa_from = changes->soa_to; + reverse.soa_to = changes->soa_from; + return zone_update_apply_changeset(update, &reverse); +} + static int set_new_soa(zone_update_t *update, unsigned serial_policy) { assert(update); diff --git a/src/knot/updates/zone-update.h b/src/knot/updates/zone-update.h index bc9a279a16..2a0094dba3 100644 --- a/src/knot/updates/zone-update.h +++ b/src/knot/updates/zone-update.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 CZ.NIC, z.s.p.o. +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. 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 @@ -76,7 +76,7 @@ int zone_update_init(zone_update_t *update, zone_t *zone, zone_update_flags_t fl * \return KNOT_E* */ int zone_update_from_differences(zone_update_t *update, zone_t *zone, zone_contents_t *old_cont, - zone_contents_t *new_cont, zone_update_flags_t flags); + zone_contents_t *new_cont, zone_update_flags_t flags, bool ignore_dnssec); /*! * \brief Inits a zone update based on new zone contents. @@ -228,6 +228,16 @@ int zone_update_apply_changeset(zone_update_t *update, const changeset_t *change */ int zone_update_apply_changeset_fix(zone_update_t *update, changeset_t *changes); +/*! + * \brief Applies the changeset in reverse, rsets from REM section are added and from ADD section removed. + * + * \param update Zone update. + * \param changes Changes to be un-done. + * + * \return KNOT_E* + */ +int zone_update_apply_changeset_reverse(zone_update_t *update, const changeset_t *changes); + /*! * \brief Increment SOA serial (according to cofigured policy) in the update. * diff --git a/src/knot/zone/zone-diff.c b/src/knot/zone/zone-diff.c index 7cf642fec1..245a19459e 100644 --- a/src/knot/zone/zone-diff.c +++ b/src/knot/zone/zone-diff.c @@ -25,8 +25,21 @@ struct zone_diff_param { zone_tree_t *nodes; changeset_t *changeset; + bool ignore_dnssec; }; +static bool rrset_is_dnssec(const knot_rrset_t *rrset) +{ + switch (rrset->type) { + case KNOT_RRTYPE_RRSIG: + case KNOT_RRTYPE_NSEC: + case KNOT_RRTYPE_NSEC3: + return true; + default: + return false; + } +} + static int load_soas(const zone_contents_t *zone1, const zone_contents_t *zone2, changeset_t *changeset) { @@ -75,11 +88,16 @@ static int load_soas(const zone_contents_t *zone1, const zone_contents_t *zone2, return KNOT_EOK; } -static int add_node(const zone_node_t *node, changeset_t *changeset) +static int add_node(const zone_node_t *node, changeset_t *changeset, bool ignore_dnssec) { /* Add all rrsets from node. */ for (unsigned i = 0; i < node->rrset_count; i++) { knot_rrset_t rrset = node_rrset_at(node, i); + + if (ignore_dnssec && rrset_is_dnssec(&rrset)) { + continue; + } + int ret = changeset_add_addition(changeset, &rrset, 0); if (ret != KNOT_EOK) { return ret; @@ -89,11 +107,16 @@ static int add_node(const zone_node_t *node, changeset_t *changeset) return KNOT_EOK; } -static int remove_node(const zone_node_t *node, changeset_t *changeset) +static int remove_node(const zone_node_t *node, changeset_t *changeset, bool ignore_dnssec) { /* Remove all the RRSets of the node. */ for (unsigned i = 0; i < node->rrset_count; i++) { knot_rrset_t rrset = node_rrset_at(node, i); + + if (ignore_dnssec && rrset_is_dnssec(&rrset)) { + continue; + } + int ret = changeset_add_removal(changeset, &rrset, 0); if (ret != KNOT_EOK) { return ret; @@ -204,7 +227,7 @@ static int knot_zone_diff_node(zone_node_t **node_ptr, void *data) */ zone_node_t *node_in_second_tree = zone_tree_get(param->nodes, node->owner); if (node_in_second_tree == NULL) { - return remove_node(node, param->changeset); + return remove_node(node, param->changeset, param->ignore_dnssec); } assert(node_in_second_tree != node); @@ -215,7 +238,7 @@ static int knot_zone_diff_node(zone_node_t **node_ptr, void *data) * If there are no RRs in the first tree, all of the RRs * in the second tree will have to be inserted to ADD section. */ - return add_node(node_in_second_tree, param->changeset); + return add_node(node_in_second_tree, param->changeset, param->ignore_dnssec); } for (unsigned i = 0; i < node->rrset_count; i++) { @@ -227,6 +250,10 @@ static int knot_zone_diff_node(zone_node_t **node_ptr, void *data) continue; } + if (param->ignore_dnssec && rrset_is_dnssec(&rrset)) { + continue; + } + knot_rrset_t rrset_from_second_node = node_rrset(node_in_second_tree, rrset.type); if (knot_rrset_empty(&rrset_from_second_node)) { @@ -255,6 +282,10 @@ static int knot_zone_diff_node(zone_node_t **node_ptr, void *data) continue; } + if (param->ignore_dnssec && rrset_is_dnssec(&rrset)) { + continue; + } + knot_rrset_t rrset_from_first_node = node_rrset(node, rrset.type); if (knot_rrset_empty(&rrset_from_first_node)) { /* RRSet has been added. Make a copy and add. */ @@ -291,19 +322,20 @@ static int add_new_nodes(zone_node_t **node_ptr, void *data) zone_node_t *new_node = zone_tree_get(param->nodes, node->owner); if (new_node == NULL) { assert(node); - return add_node(node, param->changeset); + return add_node(node, param->changeset, param->ignore_dnssec); } return KNOT_EOK; } static int load_trees(zone_tree_t *nodes1, zone_tree_t *nodes2, - changeset_t *changeset) + changeset_t *changeset, bool ignore_dnssec) { assert(changeset); struct zone_diff_param param = { - .changeset = changeset + .changeset = changeset, + .ignore_dnssec = ignore_dnssec, }; // Traverse one tree, compare every node, each RRSet with its rdata. @@ -319,7 +351,7 @@ static int load_trees(zone_tree_t *nodes1, zone_tree_t *nodes2, } int zone_contents_diff(const zone_contents_t *zone1, const zone_contents_t *zone2, - changeset_t *changeset) + changeset_t *changeset, bool ignore_dnssec) { if (zone1 == NULL || zone2 == NULL || changeset == NULL) { return KNOT_EINVAL; @@ -330,12 +362,12 @@ int zone_contents_diff(const zone_contents_t *zone1, const zone_contents_t *zone return ret_soa; } - int ret = load_trees(zone1->nodes, zone2->nodes, changeset); + int ret = load_trees(zone1->nodes, zone2->nodes, changeset, ignore_dnssec); if (ret != KNOT_EOK) { return ret; } - ret = load_trees(zone1->nsec3_nodes, zone2->nsec3_nodes, changeset); + ret = load_trees(zone1->nsec3_nodes, zone2->nsec3_nodes, changeset, ignore_dnssec); if (ret != KNOT_EOK) { return ret; } @@ -353,5 +385,5 @@ int zone_tree_add_diff(zone_tree_t *t1, zone_tree_t *t2, changeset_t *changeset) return KNOT_EINVAL; } - return load_trees(t1, t2, changeset); + return load_trees(t1, t2, changeset, false); } diff --git a/src/knot/zone/zone-diff.h b/src/knot/zone/zone-diff.h index b5fef50f01..5f467aabd3 100644 --- a/src/knot/zone/zone-diff.h +++ b/src/knot/zone/zone-diff.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2016 CZ.NIC, z.s.p.o. +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. 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 @@ -23,7 +23,7 @@ * \brief Create diff between two zone trees. * */ int zone_contents_diff(const zone_contents_t *zone1, const zone_contents_t *zone2, - changeset_t *changeset); + changeset_t *changeset, bool ignore_dnssec); /*! * \brief Add diff between two zone trees into the changeset.