]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
zonemd, zonemds are checked at start
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Thu, 22 Oct 2020 10:10:46 +0000 (12:10 +0200)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Thu, 22 Oct 2020 10:10:46 +0000 (12:10 +0200)
daemon/daemon.c
daemon/worker.c
libunbound/context.c
services/authzone.c
services/authzone.h
smallapp/unbound-checkconf.c
testcode/unitzonemd.c

index f480c94e61f6d5f3505a31c09dbb833b87dfc74d..83a30def707516228d8164ee39afebea630129a6 100644 (file)
@@ -632,19 +632,19 @@ daemon_fork(struct daemon* daemon)
                fatal_exit("Could not set up per-view response IP sets");
        daemon->use_response_ip = !respip_set_is_empty(daemon->respip_set) ||
                have_view_respip_cfg;
-       
+
+       /* setup modules */
+       daemon_setup_modules(daemon);
+
        /* read auth zonefiles */
        if(!auth_zones_apply_cfg(daemon->env->auth_zones, daemon->cfg, 1,
-               &daemon->use_rpz))
+               &daemon->use_rpz, daemon->env, &daemon->mods))
                fatal_exit("auth_zones could not be setup");
 
        /* Set-up EDNS tags */
        if(!edns_tags_apply_cfg(daemon->env->edns_tags, daemon->cfg))
                fatal_exit("Could not set up EDNS tags");
 
-       /* setup modules */
-       daemon_setup_modules(daemon);
-
        /* response-ip-xxx options don't work as expected without the respip
         * module.  To avoid run-time operational surprise we reject such
         * configuration. */
index 458afa04e2e350cbe5d251a44b8a1fdac4928a3f..9e4ad7c75c1530b18501e2bbd20444cde2bbc4fb 100644 (file)
@@ -1906,6 +1906,8 @@ worker_init(struct worker* worker, struct config_file *cfg,
 #endif
                ) {
                auth_xfer_pickup_initial(worker->env.auth_zones, &worker->env);
+               auth_zones_pickup_zonemd_verify(worker->env.auth_zones,
+                       &worker->env);
        }
 #ifdef USE_DNSTAP
        if(worker->daemon->cfg->dnstap
index 713259c718ce0ce52ea7825d0fe2fe8d00e9c258..914855470b437af281005e5275c4a187f93eed21 100644 (file)
@@ -78,7 +78,8 @@ context_finalize(struct ub_ctx* ctx)
                return UB_NOMEM;
        if(!local_zones_apply_cfg(ctx->local_zones, cfg))
                return UB_INITFAIL;
-       if(!auth_zones_apply_cfg(ctx->env->auth_zones, cfg, 1, &is_rpz))
+       if(!auth_zones_apply_cfg(ctx->env->auth_zones, cfg, 1, &is_rpz,
+               ctx->env, &ctx->mods))
                return UB_INITFAIL;
        if(!edns_tags_apply_cfg(ctx->env->edns_tags, cfg))
                return UB_INITFAIL;
index 727add00313d4552f56cfdba878e9cb8e454b9e3..4adf90c4f2e6d75477d2787d39925525eb254b34 100644 (file)
@@ -1745,9 +1745,47 @@ int auth_zone_write_file(struct auth_zone* z, const char* fname)
        return 1;
 }
 
+/** offline verify for zonemd, while reading a zone file to immediately
+ * spot bad hashes in zonefile as they are read.
+ * Creates temp buffers, but uses anchors and validation environment
+ * from the module_env. */
+static void
+zonemd_offline_verify(struct auth_zone* z, struct module_env* env_for_val,
+       struct module_stack* mods)
+{
+       struct module_env env;
+       struct mesh_area mesh;
+       time_t now = 0;
+       env = *env_for_val;
+       env.scratch_buffer = sldns_buffer_new(env.cfg->msg_buffer_size);
+       if(!env.scratch_buffer) {
+               log_err("out of memory");
+               goto clean_exit;
+       }
+       env.scratch = regional_create();
+       memset(&mesh, 0, sizeof(mesh));
+       mesh.mods = *mods;
+       env.mesh = &mesh;
+       if(!env.now) {
+               env.now = &now;
+               now = time(NULL);
+       }
+       if(!env.scratch) {
+               log_err("out of memory");
+               goto clean_exit;
+       }
+       auth_zone_verify_zonemd(z, &env, NULL, 1, 0);
+
+clean_exit:
+       /* clean up and exit */
+       sldns_buffer_free(env.scratch_buffer);
+       regional_destroy(env.scratch);
+}
+
 /** read all auth zones from file (if they have) */
 static int
-auth_zones_read_zones(struct auth_zones* az, struct config_file* cfg)
+auth_zones_read_zones(struct auth_zones* az, struct config_file* cfg,
+       struct module_env* env, struct module_stack* mods)
 {
        struct auth_zone* z;
        lock_rw_wrlock(&az->lock);
@@ -1758,6 +1796,8 @@ auth_zones_read_zones(struct auth_zones* az, struct config_file* cfg)
                        lock_rw_unlock(&az->lock);
                        return 0;
                }
+               if(z->zonefile && z->zonefile[0]!=0 && env)
+                       zonemd_offline_verify(z, env, mods);
                lock_rw_unlock(&z->lock);
        }
        lock_rw_unlock(&az->lock);
@@ -2103,7 +2143,8 @@ az_delete_deleted_zones(struct auth_zones* az)
 }
 
 int auth_zones_apply_cfg(struct auth_zones* az, struct config_file* cfg,
-       int setup, int* is_rpz)
+       int setup, int* is_rpz, struct module_env* env,
+       struct module_stack* mods)
 {
        struct config_auth* p;
        az_setall_deleted(az);
@@ -2119,7 +2160,7 @@ int auth_zones_apply_cfg(struct auth_zones* az, struct config_file* cfg,
                }
        }
        az_delete_deleted_zones(az);
-       if(!auth_zones_read_zones(az, cfg))
+       if(!auth_zones_read_zones(az, cfg, env, mods))
                return 0;
        if(setup) {
                if(!auth_zones_setup_zones(az))
@@ -8044,7 +8085,7 @@ zonemd_lookup_dnskey(struct auth_zone* z, struct module_env* env)
 }
 
 void auth_zone_verify_zonemd(struct auth_zone* z, struct module_env* env,
-       char** result)
+       char** result, int offline, int only_online)
 {
        char* reason = NULL, *why_bogus = NULL;
        struct trust_anchor* anchor = NULL;
@@ -8061,6 +8102,10 @@ void auth_zone_verify_zonemd(struct auth_zone* z, struct module_env* env,
         * otherwise we have the zone DNSKEY for the DNSSEC verification. */
        anchor = anchors_lookup(env->anchors, z->name, z->namelen, z->dclass);
        if(anchor && query_dname_compare(z->name, anchor->name) == 0) {
+               if(only_online) {
+                       lock_basic_unlock(&anchor->lock);
+                       return;
+               }
                /* equal to trustanchor, no need for online lookups */
                dnskey = zonemd_get_dnskey_from_anchor(z, env, anchor,
                        &is_insecure, &why_bogus, &keystorage);
@@ -8071,6 +8116,8 @@ void auth_zone_verify_zonemd(struct auth_zone* z, struct module_env* env,
        } else if(anchor) {
                lock_basic_unlock(&anchor->lock);
                /* perform online lookups */
+               if(offline)
+                       return;
                /* setup online lookups, and wait for them */
                if(zonemd_lookup_dnskey(z, env)) {
                        /* wait for the lookup */
@@ -8079,6 +8126,8 @@ void auth_zone_verify_zonemd(struct auth_zone* z, struct module_env* env,
                reason = "could not lookup DNSKEY for chain of trust";
        } else {
                /* the zone is not under a trust anchor */
+               if(only_online)
+                       return;
                dnskey = NULL;
                is_insecure = 1;
        }
@@ -8091,3 +8140,37 @@ void auth_zone_verify_zonemd(struct auth_zone* z, struct module_env* env,
        auth_zone_verify_zonemd_with_key(z, env, dnskey, is_insecure, result);
        regional_free_all(env->scratch);
 }
+
+void auth_zones_pickup_zonemd_verify(struct auth_zones* az,
+       struct module_env* env)
+{
+       struct auth_zone key;
+       uint8_t savezname[255+1];
+       size_t savezname_len;
+       struct auth_zone* z;
+       key.node.key = &key;
+       lock_rw_rdlock(&az->lock);
+       RBTREE_FOR(z, struct auth_zone*, &az->ztree) {
+               lock_rw_wrlock(&z->lock);
+               key.dclass = z->dclass;
+               key.namelabs = z->namelabs;
+               if(z->namelen > sizeof(savezname)) {
+                       lock_rw_unlock(&z->lock);
+                       log_err("auth_zones_pickup_zonemd_verify: zone name too long");
+                       continue;
+               }
+               savezname_len = z->namelen;
+               memmove(savezname, z->name, z->namelen);
+               lock_rw_unlock(&az->lock);
+               auth_zone_verify_zonemd(z, env, NULL, 0, 1);
+               lock_rw_unlock(&z->lock);
+               lock_rw_rdlock(&az->lock);
+               /* find the zone we had before, it is not deleted,
+                * because we have a flag for that that is processed at
+                * apply_cfg time */
+               key.namelen = savezname_len;
+               key.name = savezname;
+               z = (struct auth_zone*)rbtree_search(&az->ztree, &key);
+       }
+       lock_rw_unlock(&az->lock);
+}
index c00598ad119e644aca1923b025b248351886046a..9ddc756f97e09caef26fcc3cab237aef0423dfe0 100644 (file)
@@ -479,10 +479,13 @@ struct auth_zones* auth_zones_create(void);
  * @param cfg: config to apply.
  * @param setup: if true, also sets up values in the auth zones structure
  * @param is_rpz: set to 1 if at least one RPZ zone is configured.
+ * @param env: environment for offline verification.
+ * @param mods: modules in environment.
  * @return false on failure.
  */
 int auth_zones_apply_cfg(struct auth_zones* az, struct config_file* cfg,
-       int setup, int* is_rpz);
+       int setup, int* is_rpz, struct module_env* env,
+       struct module_stack* mods);
 
 /** initial pick up of worker timeouts, ties events to worker event loop
  * @param az: auth zones structure
@@ -744,13 +747,27 @@ int auth_zone_generate_zonemd_check(struct auth_zone* z, int scheme,
  * @param z: auth zone to check.  Caller holds lock. wrlock.
  * @param env: with temp region, buffer and config.
  * @param result: if not NULL, result string strdupped in here.
+ * @param offline: if true, there is no spawned lookup when online is needed.
+ *     Those zones are skipped for ZONEMD checking.
+ * @param only_online: if true, only for ZONEMD that need online lookup
+ *     of DNSKEY chain of trust are processed.
  */
 void auth_zone_verify_zonemd(struct auth_zone* z, struct module_env* env,
-       char** result);
+       char** result, int offline, int only_online);
 
 /** mesh callback for zonemd on lookup of dnskey */
 void auth_zonemd_dnskey_lookup_callback(void* arg, int rcode,
        struct sldns_buffer* buf, enum sec_status sec, char* why_bogus,
        int was_ratelimited);
 
+/**
+ * Check the ZONEMD records that need online DNSSEC chain lookups,
+ * for them spawn the lookup process to get it checked out.
+ * Attaches the lookup process to the worker event base and mesh state.
+ * @param az: auth zones, every zones is checked.
+ * @param env: env of the worker where the task is attached.
+ */
+void auth_zones_pickup_zonemd_verify(struct auth_zones* az,
+       struct module_env* env);
+
 #endif /* SERVICES_AUTHZONE_H */
index b1b7ae7ef9d461f7c585149329e8143984192890..34a1f5bb44dc6ddbc6bc8db50b8350c6e621f2e5 100644 (file)
@@ -851,7 +851,7 @@ check_auth(struct config_file* cfg)
 {
        int is_rpz = 0;
        struct auth_zones* az = auth_zones_create();
-       if(!az || !auth_zones_apply_cfg(az, cfg, 0, &is_rpz)) {
+       if(!az || !auth_zones_apply_cfg(az, cfg, 0, &is_rpz, NULL, NULL)) {
                fatal_exit("Could not setup authority zones");
        }
        auth_zones_delete(az);
index a20a6b0b297e2118567c7c7ccb7aee3ed498dbbb..10c40c630ad6b353ee18ce3272a4d1eef6d19434 100644 (file)
@@ -291,7 +291,7 @@ static void zonemd_verify_test(char* zname, char* zfile, char* tastr,
 
        /* test */
        lock_rw_wrlock(&z->lock);
-       auth_zone_verify_zonemd(z, &env, &result);
+       auth_zone_verify_zonemd(z, &env, &result, 1, 0);
        lock_rw_unlock(&z->lock);
        if(verbosity >= VERB_ALGO) {
                printf("auth zone %s: ZONEMD verification %s: %s\n", zname,