]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Purge keys implementation
authorMatthijs Mekking <matthijs@isc.org>
Mon, 8 Feb 2021 14:15:57 +0000 (15:15 +0100)
committerMatthijs Mekking <matthijs@isc.org>
Tue, 23 Feb 2021 08:16:48 +0000 (09:16 +0100)
On each keymgr run, we now also check if key files can be removed.
The 'purge-keys' interval determines how long keys should be retained
after they have become completely hidden.

Key files should not be removed if it has a state that is set to
something else then HIDDEN, if purge-keys is 0 (disabled), if
the key goal is set to OMNIPRESENT, or if the key is unused (a key is
unused if no timing metadata set, and no states are set or if set,
they are set to HIDDEN).

If the last changed timing metadata plus the purge-keys interval is
in the past, the key files may be removed.

Add a dst_key_t variable 'purge' to signal that the key file should
not be written to file again.

lib/dns/dnssec.c
lib/dns/include/dns/dnssec.h
lib/dns/keymgr.c

index 54bbd1fd6a7c363db254404ea721143806306e16..97316b7d33cafaf06d0c7e13eebf874576b6bf8a 100644 (file)
@@ -1290,6 +1290,7 @@ dns_dnsseckey_create(isc_mem_t *mctx, dst_key_t **dstkey,
        dk->hint_remove = false;
        dk->first_sign = false;
        dk->is_active = false;
+       dk->purge = false;
        dk->prepublish = 0;
        dk->source = dns_keysource_unknown;
        dk->index = 0;
index c51b2c9aad59592745666347456ebe3c0d75d528..e74ec4798e9362bef9b65317de8fb60c34033c82 100644 (file)
@@ -59,6 +59,7 @@ struct dns_dnsseckey {
        bool            hint_remove; /*% metadata says *don't* publish */
        bool            is_active;   /*% key is already active */
        bool            first_sign;  /*% key is newly becoming active */
+       bool            purge;       /*% remove key files */
        unsigned int    prepublish;  /*% how long until active? */
        dns_keysource_t source;      /*% how the key was found */
        bool            ksk;         /*% this is a key-signing key */
index ae952f732513def3043a9d5f2cdafa0d05b50a32..009c06b1952183ff0018548edca98d18c84aa147 100644 (file)
@@ -14,6 +14,7 @@
 #include <inttypes.h>
 #include <stdbool.h>
 #include <stdlib.h>
+#include <unistd.h>
 
 #include <isc/buffer.h>
 #include <isc/dir.h>
@@ -1823,6 +1824,94 @@ keymgr_key_rollover(dns_kasp_key_t *kaspkey, dns_dnsseckey_t *active_key,
        return (ISC_R_SUCCESS);
 }
 
+static bool
+keymgr_key_may_be_purged(dst_key_t *key, uint32_t after, isc_stdtime_t now) {
+       bool ksk = false;
+       bool zsk = false;
+       dst_key_state_t hidden[NUM_KEYSTATES] = { HIDDEN, NA, NA, NA };
+       isc_stdtime_t lastchange = 0;
+
+       char keystr[DST_KEY_FORMATSIZE];
+       dst_key_format(key, keystr, sizeof(keystr));
+
+       /* If 'purge-keys' is disabled, always retain keys. */
+       if (after == 0) {
+               return (false);
+       }
+
+       /* Don't purge keys with goal OMNIPRESENT */
+       if (dst_key_goal(key) == OMNIPRESENT) {
+               return (false);
+       }
+
+       /* Don't purge unused keys. */
+       if (dst_key_is_unused(key)) {
+               return (false);
+       }
+
+       /* If this key is completely HIDDEN it may be purged. */
+       (void)dst_key_getbool(key, DST_BOOL_KSK, &ksk);
+       (void)dst_key_getbool(key, DST_BOOL_ZSK, &zsk);
+       if (ksk) {
+               hidden[DST_KEY_KRRSIG] = HIDDEN;
+               hidden[DST_KEY_DS] = HIDDEN;
+       }
+       if (zsk) {
+               hidden[DST_KEY_ZRRSIG] = HIDDEN;
+       }
+       if (!keymgr_key_match_state(key, key, 0, NA, hidden)) {
+               return (false);
+       }
+
+       /*
+        * Check 'purge-keys' interval. If the interval has passed since
+        * the last key change, it may be purged.
+        */
+       for (int i = 0; i < NUM_KEYSTATES; i++) {
+               isc_stdtime_t change = 0;
+               (void)dst_key_gettime(key, keystatetimes[i], &change);
+               if (change > lastchange) {
+                       lastchange = change;
+               }
+       }
+
+       return ((lastchange + after) < now);
+}
+
+static void
+keymgr_purge_keyfile(dst_key_t *key, const char *dir, int type) {
+       isc_result_t ret;
+       isc_buffer_t fileb;
+       char filename[NAME_MAX];
+
+       /*
+        * Make the filename.
+        */
+       isc_buffer_init(&fileb, filename, sizeof(filename));
+       ret = dst_key_buildfilename(key, type, dir, &fileb);
+       if (ret != ISC_R_SUCCESS) {
+               char keystr[DST_KEY_FORMATSIZE];
+               dst_key_format(key, keystr, sizeof(keystr));
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
+                             DNS_LOGMODULE_DNSSEC, ISC_LOG_WARNING,
+                             "keymgr: failed to purge DNSKEY %s (%s): cannot "
+                             "build filename (%s)",
+                             keystr, keymgr_keyrole(key),
+                             isc_result_totext(ret));
+               return;
+       }
+
+       if (unlink(filename) < 0) {
+               char keystr[DST_KEY_FORMATSIZE];
+               dst_key_format(key, keystr, sizeof(keystr));
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
+                             DNS_LOGMODULE_DNSSEC, ISC_LOG_WARNING,
+                             "keymgr: failed to purge DNSKEY %s (%s): unlink "
+                             "'%s' failed",
+                             keystr, keymgr_keyrole(key), filename);
+       }
+}
+
 /*
  * Examine 'keys' and match 'kasp' policy.
  *
@@ -1901,6 +1990,27 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass,
                if (!found_match) {
                        keymgr_key_retire(dkey, kasp, now);
                }
+
+               /* Check purge-keys interval. */
+               if (keymgr_key_may_be_purged(dkey->key,
+                                            dns_kasp_purgekeys(kasp), now)) {
+                       dst_key_format(dkey->key, keystr, sizeof(keystr));
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC,
+                                     DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO,
+                                     "keymgr: purge DNSKEY %s (%s) according "
+                                     "to policy %s",
+                                     keystr, keymgr_keyrole(dkey->key),
+                                     dns_kasp_getname(kasp));
+
+                       keymgr_purge_keyfile(dkey->key, directory,
+                                            DST_TYPE_PUBLIC);
+                       keymgr_purge_keyfile(dkey->key, directory,
+                                            DST_TYPE_PRIVATE);
+                       keymgr_purge_keyfile(dkey->key, directory,
+                                            DST_TYPE_STATE);
+
+                       dkey->purge = true;
+               }
        }
 
        /* Create keys according to the policy, if come in short. */
@@ -1993,8 +2103,10 @@ dns_keymgr_run(const dns_name_t *origin, dns_rdataclass_t rdclass,
        for (dns_dnsseckey_t *dkey = ISC_LIST_HEAD(*keyring); dkey != NULL;
             dkey = ISC_LIST_NEXT(dkey, link))
        {
-               dns_dnssec_get_hints(dkey, now);
-               RETERR(dst_key_tofile(dkey->key, options, directory));
+               if (!dkey->purge) {
+                       dns_dnssec_get_hints(dkey, now);
+                       RETERR(dst_key_tofile(dkey->key, options, directory));
+               }
        }
 
        result = ISC_R_SUCCESS;