"Fossies" - the Fresh Open Source Software Archive

Member "knot-2.9.2/src/knot/dnssec/zone-sign.c" (12 Dec 2019, 30154 Bytes) of package /linux/misc/dns/knot-2.9.2.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "zone-sign.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.9.1_vs_2.9.2.

    1 /*  Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
    2 
    3     This program is free software: you can redistribute it and/or modify
    4     it under the terms of the GNU General Public License as published by
    5     the Free Software Foundation, either version 3 of the License, or
    6     (at your option) any later version.
    7 
    8     This program is distributed in the hope that it will be useful,
    9     but WITHOUT ANY WARRANTY; without even the implied warranty of
   10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11     GNU General Public License for more details.
   12 
   13     You should have received a copy of the GNU General Public License
   14     along with this program.  If not, see <https://www.gnu.org/licenses/>.
   15  */
   16 
   17 #include <assert.h>
   18 #include <pthread.h>
   19 #include <sys/types.h>
   20 
   21 #include "libdnssec/error.h"
   22 #include "libdnssec/key.h"
   23 #include "libdnssec/keytag.h"
   24 #include "libdnssec/sign.h"
   25 #include "knot/common/log.h"
   26 #include "knot/dnssec/key-events.h"
   27 #include "knot/dnssec/key_records.h"
   28 #include "knot/dnssec/rrset-sign.h"
   29 #include "knot/dnssec/zone-sign.h"
   30 #include "libknot/libknot.h"
   31 #include "contrib/dynarray.h"
   32 #include "contrib/macros.h"
   33 #include "contrib/wire_ctx.h"
   34 
   35 typedef struct {
   36     node_t n;
   37     uint16_t type;
   38 } type_node_t;
   39 
   40 typedef struct {
   41     knot_dname_t *dname;
   42     knot_dname_t *hashed_dname;
   43     list_t *type_list;
   44 } signed_info_t;
   45 
   46 /*- private API - common functions -------------------------------------------*/
   47 
   48 /*!
   49  * \brief Initializes RR set and set owner and rclass from template RR set.
   50  */
   51 static knot_rrset_t rrset_init_from(const knot_rrset_t *src, uint16_t type)
   52 {
   53     assert(src);
   54     knot_rrset_t rrset;
   55     knot_rrset_init(&rrset, src->owner, type, src->rclass, src->ttl);
   56     return rrset;
   57 }
   58 
   59 /*!
   60  * \brief Create empty RRSIG RR set for a given RR set to be covered.
   61  */
   62 static knot_rrset_t create_empty_rrsigs_for(const knot_rrset_t *covered)
   63 {
   64     assert(!knot_rrset_empty(covered));
   65     return rrset_init_from(covered, KNOT_RRTYPE_RRSIG);
   66 }
   67 
   68 static bool apex_rr_changed(const zone_node_t *old_apex,
   69                             const zone_node_t *new_apex,
   70                             uint16_t type)
   71 {
   72     assert(old_apex);
   73     assert(new_apex);
   74     knot_rrset_t old_rr = node_rrset(old_apex, type);
   75     knot_rrset_t new_rr = node_rrset(new_apex, type);
   76 
   77     return !knot_rrset_equal(&old_rr, &new_rr, false);
   78 }
   79 
   80 static bool apex_dnssec_changed(zone_update_t *update)
   81 {
   82     if (update->zone->contents == NULL || update->new_cont == NULL) {
   83         return false;
   84     }
   85     return apex_rr_changed(update->zone->contents->apex,
   86                    update->new_cont->apex, KNOT_RRTYPE_DNSKEY) ||
   87            apex_rr_changed(update->zone->contents->apex,
   88                    update->new_cont->apex, KNOT_RRTYPE_NSEC3PARAM);
   89 }
   90 
   91 /*- private API - signing of in-zone nodes -----------------------------------*/
   92 
   93 /*!
   94  * \brief Check if there is a valid signature for a given RR set and key.
   95  *
   96  * \param covered  RR set with covered records.
   97  * \param rrsigs   RR set with RRSIGs.
   98  * \param key      Signing key.
   99  * \param ctx      Signing context.
  100  * \param policy   DNSSEC policy.
  101  * \param skip_crypto All RRSIGs in this node have been verified, just check validity.
  102  * \param at       RRSIG position.
  103  *
  104  * \return The signature exists and is valid.
  105  */
  106 static bool valid_signature_exists(const knot_rrset_t *covered,
  107                    const knot_rrset_t *rrsigs,
  108                    const dnssec_key_t *key,
  109                    dnssec_sign_ctx_t *ctx,
  110                    const kdnssec_ctx_t *dnssec_ctx,
  111                                    bool skip_crypto,
  112                    uint16_t *at)
  113 {
  114     assert(key);
  115 
  116     if (knot_rrset_empty(rrsigs)) {
  117         return false;
  118     }
  119 
  120     uint16_t rrsigs_rdata_count = rrsigs->rrs.count;
  121     knot_rdata_t *rdata = rrsigs->rrs.rdata;
  122     for (uint16_t i = 0; i < rrsigs_rdata_count; i++) {
  123         uint16_t rr_keytag = knot_rrsig_key_tag(rdata);
  124         uint16_t rr_covered = knot_rrsig_type_covered(rdata);
  125         rdata = knot_rdataset_next(rdata);
  126 
  127         uint16_t keytag = dnssec_key_get_keytag(key);
  128         if (rr_keytag != keytag || rr_covered != covered->type) {
  129             continue;
  130         }
  131 
  132         if (knot_check_signature(covered, rrsigs, i, key, ctx,
  133                                  dnssec_ctx, skip_crypto) == KNOT_EOK) {
  134             if (at != NULL) {
  135                 *at = i;
  136             }
  137             return true;
  138         }
  139     }
  140 
  141     return false;
  142 }
  143 
  144 /*!
  145  * \brief Check if valid signature exists for all keys for a given RR set.
  146  *
  147  * \param covered    RR set with covered records.
  148  * \param rrsigs     RR set with RRSIGs.
  149  * \param sign_ctx   Local zone signing context.
  150  *
  151  * \return Valid signature exists for every key.
  152  */
  153 static bool all_signatures_exist(const knot_rrset_t *covered,
  154                                  const knot_rrset_t *rrsigs,
  155                                  zone_sign_ctx_t *sign_ctx)
  156 {
  157     assert(sign_ctx);
  158 
  159     for (int i = 0; i < sign_ctx->count; i++) {
  160         zone_key_t *key = &sign_ctx->keys[i];
  161         if (!knot_zone_sign_use_key(key, covered)) {
  162             continue;
  163         }
  164 
  165         if (!valid_signature_exists(covered, rrsigs, key->key,
  166                                     sign_ctx->sign_ctxs[i],
  167                                     sign_ctx->dnssec_ctx, false, NULL)) {
  168             return false;
  169         }
  170     }
  171 
  172     return true;
  173 }
  174 
  175 /*!
  176  * \brief Note earliest expiration of a signature.
  177  *
  178  * \param rrsig       RRSIG rdata.
  179  * \param expires_at  Current earliest expiration, will be updated.
  180  */
  181 static void note_earliest_expiration(const knot_rdata_t *rrsig, knot_time_t *expires_at)
  182 {
  183     assert(rrsig);
  184     if (expires_at == NULL) {
  185         return;
  186     }
  187 
  188     uint32_t curr_rdata = knot_rrsig_sig_expiration(rrsig);
  189     knot_time_t current = knot_time_from_u32(curr_rdata);
  190     *expires_at = knot_time_min(current, *expires_at);
  191 }
  192 
  193 // TODO: move somewhere?
  194 static bool rrsig_covers_type(const knot_rrset_t *rrsig, uint16_t type)
  195 {
  196     if (knot_rrset_empty(rrsig)) {
  197         return false;
  198     }
  199     assert(rrsig->type == KNOT_RRTYPE_RRSIG);
  200     knot_rdata_t *one_rr = rrsig->rrs.rdata;
  201     for (int i = 0; i < rrsig->rrs.count; i++) {
  202         if (type == knot_rrsig_type_covered(one_rr)) {
  203             return true;
  204         }
  205         one_rr = knot_rdataset_next(one_rr);
  206     }
  207     return false;
  208 }
  209 
  210 /*!
  211  * \brief Add missing RRSIGs into the changeset for adding.
  212  *
  213  * \note Also removes invalid RRSIGs.
  214  *
  215  * \param covered     RR set with covered records.
  216  * \param rrsigs      RR set with RRSIGs.
  217  * \param sign_ctx    Local zone signing context.
  218  * \param skip_crypto All RRSIGs in this node have been verified, just check validity.
  219  * \param changeset   Changeset to be updated.
  220  * \param update      Zone update to be updated. Exactly one of "changeset" and "update" must be NULL!
  221  * \param expires_at  Earliest RRSIG expiration.
  222  *
  223  * \return Error code, KNOT_EOK if successful.
  224  */
  225 static int add_missing_rrsigs(const knot_rrset_t *covered,
  226                               const knot_rrset_t *rrsigs,
  227                               zone_sign_ctx_t *sign_ctx,
  228                               bool skip_crypto,
  229                               changeset_t *changeset,
  230                               zone_update_t *update,
  231                               knot_time_t *expires_at)
  232 {
  233     assert(!knot_rrset_empty(covered));
  234     assert(sign_ctx);
  235     assert((bool)changeset != (bool)update);
  236 
  237     knot_rrset_t to_add = create_empty_rrsigs_for(covered);
  238     knot_rrset_t to_remove = create_empty_rrsigs_for(covered);
  239     int result = (!rrsig_covers_type(rrsigs, covered->type) ? KNOT_EOK :
  240                  knot_synth_rrsig(covered->type, &rrsigs->rrs, &to_remove.rrs, NULL));
  241 
  242     if (result == KNOT_EOK && sign_ctx->dnssec_ctx->offline_rrsig != NULL &&
  243         knot_dname_cmp(sign_ctx->dnssec_ctx->offline_rrsig->owner, covered->owner) == 0 &&
  244         rrsig_covers_type(sign_ctx->dnssec_ctx->offline_rrsig, covered->type)) {
  245         result = knot_synth_rrsig(covered->type,
  246             &sign_ctx->dnssec_ctx->offline_rrsig->rrs, &to_add.rrs, NULL);
  247         if (result == KNOT_EOK) {
  248             // don't remove what shall be added
  249             result = knot_rdataset_subtract(&to_remove.rrs, &to_add.rrs, NULL);
  250         }
  251         if (result == KNOT_EOK && !knot_rrset_empty(rrsigs)) {
  252             // don't add what's already present
  253             result = knot_rdataset_subtract(&to_add.rrs, &rrsigs->rrs, NULL);
  254         }
  255     }
  256 
  257     for (size_t i = 0; i < sign_ctx->count && result == KNOT_EOK; i++) {
  258         const zone_key_t *key = &sign_ctx->keys[i];
  259         if (!knot_zone_sign_use_key(key, covered)) {
  260             continue;
  261         }
  262 
  263         uint16_t valid_at;
  264         if (valid_signature_exists(covered, rrsigs, key->key, sign_ctx->sign_ctxs[i],
  265                                    sign_ctx->dnssec_ctx, skip_crypto, &valid_at)) {
  266             knot_rdata_t *valid_rr = knot_rdataset_at(&rrsigs->rrs, valid_at);
  267             result = knot_rdataset_remove(&to_remove.rrs, valid_rr, NULL);
  268             note_earliest_expiration(valid_rr, expires_at);
  269             continue;
  270         }
  271 
  272         result = knot_sign_rrset(&to_add, covered, key->key, sign_ctx->sign_ctxs[i],
  273                                  sign_ctx->dnssec_ctx, NULL, expires_at);
  274     }
  275 
  276     if (!knot_rrset_empty(&to_remove) && result == KNOT_EOK) {
  277         if (changeset != NULL) {
  278             result = changeset_add_removal(changeset, &to_remove, 0);
  279         } else {
  280             result = zone_update_remove(update, &to_remove);
  281         }
  282     }
  283 
  284     if (!knot_rrset_empty(&to_add) && result == KNOT_EOK) {
  285         if (changeset != NULL) {
  286             result = changeset_add_addition(changeset, &to_add, 0);
  287         } else {
  288             result = zone_update_add(update, &to_add);
  289         }
  290     }
  291 
  292     knot_rdataset_clear(&to_add.rrs, NULL);
  293     knot_rdataset_clear(&to_remove.rrs, NULL);
  294 
  295     return result;
  296 }
  297 
  298 /*!
  299  * \brief Add all RRSIGs into the changeset for removal.
  300  *
  301  * \param covered    RR set with covered records.
  302  * \param changeset  Changeset to be updated.
  303  *
  304  * \return Error code, KNOT_EOK if successful.
  305  */
  306 static int remove_rrset_rrsigs(const knot_dname_t *owner, uint16_t type,
  307                                const knot_rrset_t *rrsigs,
  308                                changeset_t *changeset)
  309 {
  310     assert(owner);
  311     assert(changeset);
  312     knot_rrset_t synth_rrsig;
  313     knot_rrset_init(&synth_rrsig, (knot_dname_t *)owner,
  314                     KNOT_RRTYPE_RRSIG, rrsigs->rclass, rrsigs->ttl);
  315     int ret = knot_synth_rrsig(type, &rrsigs->rrs, &synth_rrsig.rrs, NULL);
  316     if (ret != KNOT_EOK) {
  317         if (ret != KNOT_ENOENT) {
  318             return ret;
  319         }
  320         return KNOT_EOK;
  321     }
  322 
  323     ret = changeset_add_removal(changeset, &synth_rrsig, 0);
  324     knot_rdataset_clear(&synth_rrsig.rrs, NULL);
  325 
  326     return ret;
  327 }
  328 
  329 /*!
  330  * \brief Drop all existing and create new RRSIGs for covered records.
  331  *
  332  * \param covered    RR set with covered records.
  333  * \param rrsigs     Existing RRSIGs for covered RR set.
  334  * \param sign_ctx   Local zone signing context.
  335  * \param changeset  Changeset to be updated.
  336  *
  337  * \return Error code, KNOT_EOK if successful.
  338  */
  339 static int force_resign_rrset(const knot_rrset_t *covered,
  340                               const knot_rrset_t *rrsigs,
  341                               zone_sign_ctx_t *sign_ctx,
  342                               changeset_t *changeset)
  343 {
  344     assert(!knot_rrset_empty(covered));
  345 
  346     if (!knot_rrset_empty(rrsigs)) {
  347         int result = remove_rrset_rrsigs(covered->owner, covered->type,
  348                                          rrsigs, changeset);
  349         if (result != KNOT_EOK) {
  350             return result;
  351         }
  352     }
  353 
  354     return add_missing_rrsigs(covered, NULL, sign_ctx, false, changeset, NULL, NULL);
  355 }
  356 
  357 /*!
  358  * \brief Drop all expired and create new RRSIGs for covered records.
  359  *
  360  * \param covered     RR set with covered records.
  361  * \param rrsigs      Existing RRSIGs for covered RR set.
  362  * \param sign_ctx    Local zone signing context.
  363  * \param skip_crypto All RRSIGs in this node have been verified, just check validity.
  364  * \param changeset   Changeset to be updated.
  365  * \param expires_at  Current earliest expiration, will be updated.
  366  *
  367  * \return Error code, KNOT_EOK if successful.
  368  */
  369 static int resign_rrset(const knot_rrset_t *covered,
  370                         const knot_rrset_t *rrsigs,
  371                         zone_sign_ctx_t *sign_ctx,
  372                         bool skip_crypto,
  373                         changeset_t *changeset,
  374                         knot_time_t *expires_at)
  375 {
  376     assert(!knot_rrset_empty(covered));
  377 
  378     return add_missing_rrsigs(covered, rrsigs, sign_ctx, skip_crypto, changeset, NULL, expires_at);
  379 }
  380 
  381 static int remove_standalone_rrsigs(const zone_node_t *node,
  382                                     const knot_rrset_t *rrsigs,
  383                                     changeset_t *changeset)
  384 {
  385     if (rrsigs == NULL) {
  386         return KNOT_EOK;
  387     }
  388 
  389     uint16_t rrsigs_rdata_count = rrsigs->rrs.count;
  390     knot_rdata_t *rdata = rrsigs->rrs.rdata;
  391     for (uint16_t i = 0; i < rrsigs_rdata_count; ++i) {
  392         uint16_t type_covered = knot_rrsig_type_covered(rdata);
  393         if (!node_rrtype_exists(node, type_covered)) {
  394             knot_rrset_t to_remove;
  395             knot_rrset_init(&to_remove, rrsigs->owner, rrsigs->type,
  396                             rrsigs->rclass, rrsigs->ttl);
  397             int ret = knot_rdataset_add(&to_remove.rrs, rdata, NULL);
  398             if (ret != KNOT_EOK) {
  399                 return ret;
  400             }
  401             ret = changeset_add_removal(changeset, &to_remove, 0);
  402             knot_rdataset_clear(&to_remove.rrs, NULL);
  403             if (ret != KNOT_EOK) {
  404                 return ret;
  405             }
  406         }
  407         rdata = knot_rdataset_next(rdata);
  408     }
  409 
  410     return KNOT_EOK;
  411 }
  412 
  413 /*!
  414  * \brief Update RRSIGs in a given node by updating changeset.
  415  *
  416  * \param node        Node to be signed.
  417  * \param sign_ctx    Local zone signing context.
  418  * \param changeset   Changeset to be updated.
  419  * \param expires_at  Current earliest expiration, will be updated.
  420  *
  421  * \return Error code, KNOT_EOK if successful.
  422  */
  423 static int sign_node_rrsets(const zone_node_t *node,
  424                             zone_sign_ctx_t *sign_ctx,
  425                             changeset_t *changeset,
  426                             knot_time_t *expires_at)
  427 {
  428     assert(node);
  429     assert(sign_ctx);
  430 
  431     int result = KNOT_EOK;
  432     knot_rrset_t rrsigs = node_rrset(node, KNOT_RRTYPE_RRSIG);
  433     bool skip_crypto = (node->flags & NODE_FLAGS_RRSIGS_VALID) &&
  434                        !sign_ctx->dnssec_ctx->keytag_conflict;
  435 
  436     for (int i = 0; result == KNOT_EOK && i < node->rrset_count; i++) {
  437         knot_rrset_t rrset = node_rrset_at(node, i);
  438         if (!knot_zone_sign_rr_should_be_signed(node, &rrset)) {
  439             result = remove_rrset_rrsigs(rrset.owner, rrset.type, &rrsigs, changeset);
  440             continue;
  441         }
  442 
  443         if (sign_ctx->dnssec_ctx->rrsig_drop_existing) {
  444             result = force_resign_rrset(&rrset, &rrsigs,
  445                                         sign_ctx, changeset);
  446         } else {
  447             result = resign_rrset(&rrset, &rrsigs, sign_ctx, skip_crypto,
  448                                   changeset, expires_at);
  449         }
  450     }
  451 
  452     if (result == KNOT_EOK) {
  453         result = remove_standalone_rrsigs(node, &rrsigs, changeset);
  454     }
  455     return result;
  456 }
  457 
  458 /*!
  459  * \brief Struct to carry data for 'sign_data' callback function.
  460  */
  461 typedef struct {
  462     zone_tree_t *tree;
  463     zone_sign_ctx_t *sign_ctx;
  464     changeset_t changeset;
  465     knot_time_t expires_at;
  466     size_t num_threads;
  467     size_t thread_index;
  468     size_t rrset_index;
  469     int errcode;
  470     int thread_init_errcode;
  471     pthread_t thread;
  472 } node_sign_args_t;
  473 
  474 /*!
  475  * \brief Sign node (callback function).
  476  *
  477  * \param node  Node to be signed.
  478  * \param data  Callback data, node_sign_args_t.
  479  */
  480 static int sign_node(zone_node_t *node, void *data)
  481 {
  482     assert(node);
  483     assert(data);
  484 
  485     node_sign_args_t *args = (node_sign_args_t *)data;
  486 
  487     if (node->rrset_count == 0) {
  488         return KNOT_EOK;
  489     }
  490 
  491     if (args->rrset_index++ % args->num_threads != args->thread_index) {
  492         return KNOT_EOK;
  493     }
  494 
  495     int result = sign_node_rrsets(node, args->sign_ctx,
  496                                   &args->changeset, &args->expires_at);
  497 
  498     return result;
  499 }
  500 
  501 static void *tree_sign_thread(void *_arg)
  502 {
  503     node_sign_args_t *arg = _arg;
  504     arg->errcode = zone_tree_apply(arg->tree, sign_node, _arg);
  505     return NULL;
  506 }
  507 
  508 static int set_signed(zone_node_t *node, void *data)
  509 {
  510     UNUSED(data);
  511     node->flags |= NODE_FLAGS_RRSIGS_VALID;
  512     return KNOT_EOK;
  513 }
  514 
  515 /*!
  516  * \brief Update RRSIGs in a given zone tree by updating changeset.
  517  *
  518  * \param tree        Zone tree to be signed.
  519  * \param num_threads Number of threads to use for parallel signing.
  520  * \param zone_keys   Zone keys.
  521  * \param policy      DNSSEC policy.
  522  * \param update      Zone update structure to be updated.
  523  * \param expires_at  Expiration time of the oldest signature in zone.
  524  *
  525  * \return Error code, KNOT_EOK if successful.
  526  */
  527 static int zone_tree_sign(zone_tree_t *tree,
  528                           size_t num_threads,
  529                           zone_keyset_t *zone_keys,
  530                           const kdnssec_ctx_t *dnssec_ctx,
  531                           zone_update_t *update,
  532                           knot_time_t *expires_at)
  533 {
  534     assert(zone_keys);
  535     assert(dnssec_ctx);
  536     assert(update);
  537 
  538     int ret = KNOT_EOK;
  539     node_sign_args_t args[num_threads];
  540     memset(args, 0, sizeof(args));
  541     *expires_at = knot_time_plus(dnssec_ctx->now, dnssec_ctx->policy->rrsig_lifetime);
  542 
  543     // init context structures
  544     for (size_t i = 0; i < num_threads; i++) {
  545         args[i].tree = tree;
  546         args[i].sign_ctx = zone_sign_ctx(zone_keys, dnssec_ctx);
  547         if (args[i].sign_ctx == NULL) {
  548             ret = KNOT_ENOMEM;
  549             break;
  550         }
  551         ret = changeset_init(&args[i].changeset, update->zone->name);
  552         if (ret != KNOT_EOK) {
  553             break;
  554         }
  555         args[i].expires_at = 0;
  556         args[i].num_threads = num_threads;
  557         args[i].thread_index = i;
  558         args[i].rrset_index = 0;
  559         args[i].errcode = KNOT_EOK;
  560         args[i].thread_init_errcode = -1;
  561     }
  562     if (ret != KNOT_EOK) {
  563         for (size_t i = 0; i < num_threads; i++) {
  564             changeset_clear(&args[i].changeset);
  565             zone_sign_ctx_free(args[i].sign_ctx);
  566         }
  567         return ret;
  568     }
  569 
  570     if (num_threads == 1) {
  571         args[0].thread_init_errcode = 0;
  572         tree_sign_thread(&args[0]);
  573     } else {
  574         // start working threads
  575         for (size_t i = 0; i < num_threads; i++) {
  576             args[i].thread_init_errcode =
  577                 pthread_create(&args[i].thread, NULL, tree_sign_thread, &args[i]);
  578         }
  579 
  580         // join those threads that have been really started
  581         for (size_t i = 0; i < num_threads; i++) {
  582             if (args[i].thread_init_errcode == 0) {
  583                 args[i].thread_init_errcode = pthread_join(args[i].thread, NULL);
  584             }
  585         }
  586     }
  587 
  588     // collect return code and results
  589     for (size_t i = 0; i < num_threads && ret == KNOT_EOK; i++) {
  590         if (args[i].thread_init_errcode != 0) {
  591             ret = knot_map_errno_code(args[i].thread_init_errcode);
  592         } else {
  593             ret = args[i].errcode;
  594             if (ret == KNOT_EOK) {
  595                 ret = zone_update_apply_changeset(update, &args[i].changeset); // _fix not needed
  596                 *expires_at = knot_time_min(*expires_at, args[i].expires_at);
  597             }
  598         }
  599         changeset_clear(&args[i].changeset);
  600         zone_sign_ctx_free(args[i].sign_ctx);
  601     }
  602 
  603     return ret;
  604 }
  605 
  606 /*- private API - signing of NSEC(3) in changeset ----------------------------*/
  607 
  608 /*!
  609  * \brief Struct to carry data for changeset signing callback functions.
  610  */
  611 typedef struct {
  612     const zone_contents_t *zone;
  613     changeset_iter_t itt;
  614     zone_sign_ctx_t *sign_ctx;
  615     changeset_t changeset;
  616     knot_time_t expires_at;
  617     size_t num_threads;
  618     size_t thread_index;
  619     size_t rrset_index;
  620     int errcode;
  621     int thread_init_errcode;
  622     pthread_t thread;
  623 } changeset_signing_data_t;
  624 
  625 int rrset_add_zone_key(knot_rrset_t *rrset, zone_key_t *zone_key)
  626 {
  627     if (rrset == NULL || zone_key == NULL) {
  628         return KNOT_EINVAL;
  629     }
  630 
  631     dnssec_binary_t dnskey_rdata = { 0 };
  632     dnssec_key_get_rdata(zone_key->key, &dnskey_rdata);
  633 
  634     return knot_rrset_add_rdata(rrset, dnskey_rdata.data, dnskey_rdata.size, NULL);
  635 }
  636 
  637 static int rrset_add_zone_ds(knot_rrset_t *rrset, zone_key_t *zone_key)
  638 {
  639     assert(rrset);
  640     assert(zone_key);
  641 
  642     dnssec_binary_t cds_rdata = { 0 };
  643     zone_key_calculate_ds(zone_key, &cds_rdata);
  644 
  645     return knot_rrset_add_rdata(rrset, cds_rdata.data, cds_rdata.size, NULL);
  646 }
  647 
  648 int knot_zone_sign(zone_update_t *update,
  649                    zone_keyset_t *zone_keys,
  650                    const kdnssec_ctx_t *dnssec_ctx,
  651                    knot_time_t *expire_at)
  652 {
  653     if (!update || !zone_keys || !dnssec_ctx || !expire_at ||
  654         dnssec_ctx->policy->signing_threads < 1) {
  655         return KNOT_EINVAL;
  656     }
  657 
  658     int result;
  659 
  660     knot_time_t normal_expire = 0;
  661     result = zone_tree_sign(update->new_cont->nodes, dnssec_ctx->policy->signing_threads,
  662                             zone_keys, dnssec_ctx, update, &normal_expire);
  663     if (result != KNOT_EOK) {
  664         return result;
  665     }
  666 
  667     knot_time_t nsec3_expire = 0;
  668     result = zone_tree_sign(update->new_cont->nsec3_nodes, dnssec_ctx->policy->signing_threads,
  669                             zone_keys, dnssec_ctx, update, &nsec3_expire);
  670     if (result != KNOT_EOK) {
  671         return result;
  672     }
  673 
  674     result = zone_tree_apply(update->a_ctx->node_ptrs, set_signed, NULL);
  675     if (result == KNOT_EOK) {
  676         result = zone_tree_apply(update->a_ctx->nsec3_ptrs, set_signed, NULL);
  677     }
  678 
  679     *expire_at = knot_time_min(normal_expire, nsec3_expire);
  680 
  681     return result;
  682 }
  683 
  684 keyptr_dynarray_t knot_zone_sign_get_cdnskeys(const kdnssec_ctx_t *ctx,
  685                           zone_keyset_t *zone_keys)
  686 {
  687     keyptr_dynarray_t r = { 0 };
  688     unsigned crp = ctx->policy->cds_cdnskey_publish;
  689 
  690     if (crp == CDS_CDNSKEY_ROLLOVER || crp == CDS_CDNSKEY_ALWAYS ||
  691         crp == CDS_CDNSKEY_DOUBLE_DS) {
  692         // first, add strictly-ready keys
  693         for (int i = 0; i < zone_keys->count; i++) {
  694             zone_key_t *key = &zone_keys->keys[i];
  695             if (key->is_ready) {
  696                 assert(key->is_ksk);
  697                 keyptr_dynarray_add(&r, &key);
  698             }
  699         }
  700 
  701         // second, add active keys
  702         if ((crp == CDS_CDNSKEY_ALWAYS && r.size == 0) ||
  703             (crp == CDS_CDNSKEY_DOUBLE_DS)) {
  704             for (int i = 0; i < zone_keys->count; i++) {
  705                 zone_key_t *key = &zone_keys->keys[i];
  706                 if (key->is_ksk && key->is_active && !key->is_ready) {
  707                     keyptr_dynarray_add(&r, &key);
  708                 }
  709             }
  710         }
  711 
  712         if ((crp != CDS_CDNSKEY_DOUBLE_DS && r.size > 1) ||
  713             (r.size > 2)) {
  714             log_zone_warning(ctx->zone->dname, "DNSSEC, published CDS/CDNSKEY records for too many (%zu) keys", r.size);
  715         }
  716     }
  717 
  718     return r;
  719 }
  720 
  721 int knot_zone_sign_add_dnskeys(zone_keyset_t *zone_keys, const kdnssec_ctx_t *dnssec_ctx,
  722                    key_records_t *add_r)
  723 {
  724     if (add_r == NULL) {
  725         return KNOT_EINVAL;
  726     }
  727     int ret = KNOT_EOK;
  728     for (int i = 0; i < zone_keys->count; i++) {
  729         zone_key_t *key = &zone_keys->keys[i];
  730         if (key->is_public) {
  731             ret = rrset_add_zone_key(&add_r->dnskey, key);
  732             if (ret != KNOT_EOK) {
  733                 return ret;
  734             }
  735         }
  736     }
  737     keyptr_dynarray_t kcdnskeys = knot_zone_sign_get_cdnskeys(dnssec_ctx, zone_keys);
  738     dynarray_foreach(keyptr, zone_key_t *, ksk_for_cds, kcdnskeys) {
  739         ret = rrset_add_zone_key(&add_r->cdnskey, *ksk_for_cds);
  740         if (ret == KNOT_EOK) {
  741             ret = rrset_add_zone_ds(&add_r->cds, *ksk_for_cds);
  742         }
  743     }
  744 
  745     if (dnssec_ctx->policy->cds_cdnskey_publish == CDS_CDNSKEY_EMPTY && ret == KNOT_EOK) {
  746         const uint8_t cdnskey_empty[5] = { 0, 0, 3, 0, 0 };
  747         const uint8_t cds_empty[5] = { 0, 0, 0, 0, 0 };
  748         ret = knot_rrset_add_rdata(&add_r->cdnskey, cdnskey_empty, sizeof(cdnskey_empty), NULL);
  749         if (ret == KNOT_EOK) {
  750             ret = knot_rrset_add_rdata(&add_r->cds, cds_empty, sizeof(cds_empty), NULL);
  751         }
  752     }
  753 
  754     keyptr_dynarray_free(&kcdnskeys);
  755     return ret;
  756 }
  757 
  758 int knot_zone_sign_update_dnskeys(zone_update_t *update,
  759                                   zone_keyset_t *zone_keys,
  760                                   kdnssec_ctx_t *dnssec_ctx,
  761                                   knot_time_t *next_resign)
  762 {
  763     if (update == NULL || zone_keys == NULL || dnssec_ctx == NULL) {
  764         return KNOT_EINVAL;
  765     }
  766 
  767     const zone_node_t *apex = update->new_cont->apex;
  768     knot_rrset_t dnskeys = node_rrset(apex, KNOT_RRTYPE_DNSKEY);
  769     knot_rrset_t cdnskeys = node_rrset(apex, KNOT_RRTYPE_CDNSKEY);
  770     knot_rrset_t cdss = node_rrset(apex, KNOT_RRTYPE_CDS);
  771     key_records_t add_r;
  772     memset(&add_r, 0, sizeof(add_r));
  773     knot_rrset_t soa = node_rrset(apex, KNOT_RRTYPE_SOA);
  774     if (knot_rrset_empty(&soa)) {
  775         return KNOT_EINVAL;
  776     }
  777 
  778     changeset_t ch;
  779     int ret = changeset_init(&ch, apex->owner);
  780     if (ret != KNOT_EOK) {
  781         return ret;
  782     }
  783 
  784 #define CHECK_RET if (ret != KNOT_EOK) goto cleanup
  785 
  786     // remove all. This will cancel out with additions later
  787     ret = changeset_add_removal(&ch, &dnskeys, 0);
  788     CHECK_RET;
  789     ret = changeset_add_removal(&ch, &cdnskeys, 0);
  790     CHECK_RET;
  791     ret = changeset_add_removal(&ch, &cdss, 0);
  792     CHECK_RET;
  793 
  794     // add DNSKEYs, CDNSKEYs and CDSs
  795     key_records_init(dnssec_ctx, &add_r);
  796 
  797     if (dnssec_ctx->policy->offline_ksk) {
  798         ret = kasp_db_load_offline_records(dnssec_ctx->kasp_db, apex->owner, dnssec_ctx->now, next_resign, &add_r);
  799         if (ret == KNOT_EOK) {
  800             log_zone_info(dnssec_ctx->zone->dname,
  801                           "DNSSEC, using offline records, DNSKEYs %hu, CDNSKEYs %hu, CDs %hu, RRSIGs %hu",
  802                           add_r.dnskey.rrs.count, add_r.cdnskey.rrs.count, add_r.cds.rrs.count, add_r.rrsig.rrs.count);
  803         } else {
  804             log_zone_warning(dnssec_ctx->zone->dname, "DNSSEC, failed to load offline records (%s)",
  805                              knot_strerror(ret));
  806         }
  807     } else {
  808         ret = knot_zone_sign_add_dnskeys(zone_keys, dnssec_ctx, &add_r);
  809     }
  810     CHECK_RET;
  811 
  812     if (!knot_rrset_empty(&add_r.cdnskey)) {
  813         ret = changeset_add_addition(&ch, &add_r.cdnskey, CHANGESET_CHECK);
  814         CHECK_RET;
  815     }
  816 
  817     if (!knot_rrset_empty(&add_r.cds)) {
  818         ret = changeset_add_addition(&ch, &add_r.cds, CHANGESET_CHECK);
  819         if (node_rrtype_exists(ch.add->apex, KNOT_RRTYPE_CDS)) {
  820             // there is indeed a change to CDS
  821             update->zone->timers.next_ds_push = time(NULL);
  822             zone_events_schedule_now(update->zone, ZONE_EVENT_DS_PUSH);
  823         }
  824         CHECK_RET;
  825     }
  826 
  827     if (!knot_rrset_empty(&add_r.dnskey)) {
  828         ret = changeset_add_addition(&ch, &add_r.dnskey, CHANGESET_CHECK);
  829         CHECK_RET;
  830     }
  831 
  832     if (!knot_rrset_empty(&add_r.rrsig)) {
  833         dnssec_ctx->offline_rrsig = knot_rrset_copy(&add_r.rrsig, NULL);
  834     }
  835 
  836     ret = zone_update_apply_changeset(update, &ch);
  837 
  838 #undef CHECK_RET
  839 
  840 cleanup:
  841     key_records_clear(&add_r);
  842     changeset_clear(&ch);
  843     return ret;
  844 }
  845 
  846 bool knot_zone_sign_use_key(const zone_key_t *key, const knot_rrset_t *covered)
  847 {
  848     if (key == NULL || covered == NULL) {
  849         return false;
  850     }
  851 
  852     bool active_ksk = ((key->is_active || key->is_ksk_active_plus) && key->is_ksk);
  853     bool active_zsk = ((key->is_active || key->is_zsk_active_plus) && key->is_zsk);;
  854 
  855     // this may be a problem with offline KSK
  856     bool cds_sign_by_ksk = true;
  857 
  858     assert(key->is_zsk || key->is_ksk);
  859     bool is_apex = knot_dname_is_equal(covered->owner,
  860                                        dnssec_key_get_dname(key->key));
  861     if (!is_apex) {
  862         return active_zsk;
  863     }
  864 
  865     switch (covered->type) {
  866     case KNOT_RRTYPE_DNSKEY:
  867         return active_ksk;
  868     case KNOT_RRTYPE_CDS:
  869     case KNOT_RRTYPE_CDNSKEY:
  870         return (cds_sign_by_ksk ? active_ksk : active_zsk);
  871     default:
  872         return active_zsk;
  873     }
  874 }
  875 
  876 bool knot_zone_sign_soa_expired(const zone_contents_t *zone,
  877                                 const zone_keyset_t *zone_keys,
  878                                 const kdnssec_ctx_t *dnssec_ctx)
  879 {
  880     if (zone == NULL || zone_keys == NULL || dnssec_ctx == NULL) {
  881         return false;
  882     }
  883 
  884     knot_rrset_t soa = node_rrset(zone->apex, KNOT_RRTYPE_SOA);
  885     assert(!knot_rrset_empty(&soa));
  886     knot_rrset_t rrsigs = node_rrset(zone->apex, KNOT_RRTYPE_RRSIG);
  887     zone_sign_ctx_t *sign_ctx = zone_sign_ctx(zone_keys, dnssec_ctx);
  888     if (sign_ctx == NULL) {
  889         return false;
  890     }
  891     bool exist = all_signatures_exist(&soa, &rrsigs, sign_ctx);
  892     zone_sign_ctx_free(sign_ctx);
  893     return !exist;
  894 }
  895 
  896 int knot_zone_sign_nsecs_in_changeset(const zone_keyset_t *zone_keys,
  897                                       const kdnssec_ctx_t *dnssec_ctx,
  898                                       zone_update_t *update)
  899 {
  900     if (zone_keys == NULL || dnssec_ctx == NULL || update == NULL) {
  901         return KNOT_EINVAL;
  902     }
  903 
  904     zone_sign_ctx_t *sign_ctx = zone_sign_ctx(zone_keys, dnssec_ctx);
  905     if (sign_ctx == NULL) {
  906         return KNOT_ENOMEM;
  907     }
  908 
  909     zone_tree_it_t it = { 0 };
  910     int ret = zone_tree_it_double_begin(update->a_ctx->node_ptrs, update->a_ctx->nsec3_ptrs, &it);
  911 
  912     while (!zone_tree_it_finished(&it) && ret == KNOT_EOK) {
  913         zone_node_t *n = zone_tree_it_val(&it);
  914         bool skip_crypto = (n->flags & NODE_FLAGS_RRSIGS_VALID) && !dnssec_ctx->keytag_conflict;
  915 
  916         knot_rrset_t rrsigs = node_rrset(n, KNOT_RRTYPE_RRSIG);
  917         for (int i = 0; i < n->rrset_count; i++) {
  918             knot_rrset_t rr = node_rrset_at(n, i);
  919             if (rr.type == KNOT_RRTYPE_NSEC ||
  920                 rr.type == KNOT_RRTYPE_NSEC3 ||
  921                 rr.type == KNOT_RRTYPE_NSEC3PARAM) {
  922                 ret =  add_missing_rrsigs(&rr, &rrsigs, sign_ctx, skip_crypto, NULL, update, NULL);
  923             }
  924         }
  925 
  926         if (ret == KNOT_EOK) {
  927             n->flags |= NODE_FLAGS_RRSIGS_VALID; // non-NSEC RRSIGs had been validated in knot_dnssec_sign_update()
  928         }
  929 
  930         zone_tree_it_next(&it);
  931     }
  932     zone_tree_it_free(&it);
  933     zone_sign_ctx_free(sign_ctx);
  934 
  935     return ret;
  936 }
  937 
  938 bool knot_zone_sign_rr_should_be_signed(const zone_node_t *node,
  939                                         const knot_rrset_t *rrset)
  940 {
  941     if (node == NULL || knot_rrset_empty(rrset)) {
  942         return false;
  943     }
  944 
  945     if (rrset->type == KNOT_RRTYPE_RRSIG || (node->flags & NODE_FLAGS_NONAUTH)) {
  946         return false;
  947     }
  948 
  949     // At delegation points we only want to sign NSECs and DSs
  950     if (node->flags & NODE_FLAGS_DELEG) {
  951         if (!(rrset->type == KNOT_RRTYPE_NSEC ||
  952               rrset->type == KNOT_RRTYPE_DS)) {
  953             return false;
  954         }
  955     }
  956 
  957     return true;
  958 }
  959 
  960 int knot_zone_sign_update(zone_update_t *update,
  961                           zone_keyset_t *zone_keys,
  962                           const kdnssec_ctx_t *dnssec_ctx,
  963                           knot_time_t *expire_at)
  964 {
  965     if (update == NULL || zone_keys == NULL || dnssec_ctx == NULL || expire_at == NULL ||
  966         dnssec_ctx->policy->signing_threads < 1) {
  967         return KNOT_EINVAL;
  968     }
  969 
  970     int ret = KNOT_EOK;
  971 
  972     /* Check if the UPDATE changed DNSKEYs or NSEC3PARAM.
  973      * If so, we have to sign the whole zone. */
  974     const bool full_sign = apex_dnssec_changed(update);
  975     if (full_sign) {
  976         ret = knot_zone_sign(update, zone_keys, dnssec_ctx, expire_at);
  977     } else {
  978         ret = zone_tree_sign(update->a_ctx->node_ptrs, dnssec_ctx->policy->signing_threads,
  979                      zone_keys, dnssec_ctx, update, expire_at);
  980         if (ret == KNOT_EOK) {
  981             ret = zone_tree_apply(update->a_ctx->node_ptrs, set_signed, NULL);
  982         }
  983     }
  984 
  985     return ret;
  986 }
  987 
  988 int knot_zone_sign_soa(zone_update_t *update,
  989                const zone_keyset_t *zone_keys,
  990                const kdnssec_ctx_t *dnssec_ctx)
  991 {
  992     knot_rrset_t soa_to = node_rrset(update->new_cont->apex, KNOT_RRTYPE_SOA);
  993     knot_rrset_t soa_rrsig = node_rrset(update->new_cont->apex, KNOT_RRTYPE_RRSIG);
  994     changeset_t ch;
  995     int ret = changeset_init(&ch, update->zone->name);
  996     if (ret == KNOT_EOK) {
  997         zone_sign_ctx_t *sign_ctx = zone_sign_ctx(zone_keys, dnssec_ctx);
  998         if (sign_ctx == NULL) {
  999             changeset_clear(&ch);
 1000             return KNOT_ENOMEM;
 1001         }
 1002         ret = force_resign_rrset(&soa_to, &soa_rrsig, sign_ctx, &ch);
 1003         if (ret == KNOT_EOK) {
 1004             ret = zone_update_apply_changeset(update, &ch);
 1005         }
 1006         zone_sign_ctx_free(sign_ctx);
 1007     }
 1008     changeset_clear(&ch);
 1009     return ret;
 1010 }