summaryrefslogtreecommitdiff
path: root/ipsecmod
diff options
context:
space:
mode:
Diffstat (limited to 'ipsecmod')
-rw-r--r--ipsecmod/ipsecmod-whitelist.c158
-rw-r--r--ipsecmod/ipsecmod-whitelist.h82
-rw-r--r--ipsecmod/ipsecmod.c515
-rw-r--r--ipsecmod/ipsecmod.h97
4 files changed, 852 insertions, 0 deletions
diff --git a/ipsecmod/ipsecmod-whitelist.c b/ipsecmod/ipsecmod-whitelist.c
new file mode 100644
index 000000000000..c2b1f5d4a596
--- /dev/null
+++ b/ipsecmod/ipsecmod-whitelist.c
@@ -0,0 +1,158 @@
+/*
+ * ipsecmod/ipsecmod-whitelist.h - White listed domains for the ipsecmod to
+ * operate on.
+ *
+ * Copyright (c) 2017, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file
+ *
+ * Keep track of the white listed domains for ipsecmod.
+ */
+
+#include "config.h"
+
+#ifdef USE_IPSECMOD
+#include "ipsecmod/ipsecmod.h"
+#include "ipsecmod/ipsecmod-whitelist.h"
+#include "util/regional.h"
+#include "util/log.h"
+#include "util/config_file.h"
+#include "util/rbtree.h"
+#include "util/data/dname.h"
+#include "util/storage/dnstree.h"
+#include "sldns/str2wire.h"
+
+/** Apply ipsecmod-whitelist string. */
+static int
+whitelist_str_cfg(rbtree_type* whitelist, const char* name)
+{
+ struct name_tree_node* n;
+ size_t len;
+ uint8_t* nm = sldns_str2wire_dname(name, &len);
+ if(!nm) {
+ log_err("ipsecmod: could not parse %s for whitelist.", name);
+ return 0;
+ }
+ n = (struct name_tree_node*)calloc(1, sizeof(*n));
+ if(!n) {
+ log_err("ipsecmod: out of memory while creating whitelist.");
+ free(nm);
+ return 0;
+ }
+ n->node.key = n;
+ n->name = nm;
+ n->len = len;
+ n->labs = dname_count_labels(nm);
+ n->dclass = LDNS_RR_CLASS_IN;
+ if(!name_tree_insert(whitelist, n, nm, len, n->labs, n->dclass)) {
+ /* duplicate element ignored, idempotent */
+ free(n->name);
+ free(n);
+ }
+ return 1;
+}
+
+/** Read ipsecmod-whitelist config. */
+static int
+read_whitelist(rbtree_type* whitelist, struct config_file* cfg)
+{
+ struct config_strlist* p;
+ for(p = cfg->ipsecmod_whitelist; p; p = p->next) {
+ log_assert(p->str);
+ if(!whitelist_str_cfg(whitelist, p->str))
+ return 0;
+ }
+ return 1;
+}
+
+int
+ipsecmod_whitelist_apply_cfg(struct ipsecmod_env* ie,
+ struct config_file* cfg)
+{
+ ie->whitelist = rbtree_create(name_tree_compare);
+ if(!read_whitelist(ie->whitelist, cfg))
+ return 0;
+ name_tree_init_parents(ie->whitelist);
+ return 1;
+}
+
+/** Delete ipsecmod_env->whitelist element. */
+static void
+whitelist_free(struct rbnode_type* n, void* ATTR_UNUSED(d))
+{
+ if(n) {
+ free(((struct name_tree_node*)n)->name);
+ free(n);
+ }
+}
+
+/** Get memory usage of ipsecmod_env->whitelist element. */
+static void
+whitelist_get_mem(struct rbnode_type* n, void* arg)
+{
+ struct name_tree_node* node = (struct name_tree_node*)n;
+ size_t* size = (size_t*) arg;
+ if(node) {
+ *size += sizeof(node) + node->len;
+ }
+}
+
+void
+ipsecmod_whitelist_delete(rbtree_type* whitelist)
+{
+ if(whitelist) {
+ traverse_postorder(whitelist, whitelist_free, NULL);
+ free(whitelist);
+ }
+}
+
+int
+ipsecmod_domain_is_whitelisted(struct ipsecmod_env* ie, uint8_t* dname,
+ size_t dname_len, uint16_t qclass)
+{
+ if(!ie->whitelist) return 1; /* No whitelist, treat as whitelisted. */
+ return name_tree_lookup(ie->whitelist, dname, dname_len,
+ dname_count_labels(dname), qclass) != NULL;
+}
+
+size_t
+ipsecmod_whitelist_get_mem(rbtree_type* whitelist)
+{
+ size_t size = 0;
+ if(whitelist) {
+ traverse_postorder(whitelist, whitelist_get_mem, &size);
+ }
+ return size;
+}
+
+#endif /* USE_IPSECMOD */
diff --git a/ipsecmod/ipsecmod-whitelist.h b/ipsecmod/ipsecmod-whitelist.h
new file mode 100644
index 000000000000..d98868814284
--- /dev/null
+++ b/ipsecmod/ipsecmod-whitelist.h
@@ -0,0 +1,82 @@
+/*
+ * ipsecmod/ipsecmod-whitelist.h - White listed domains for the ipsecmod to
+ * operate on.
+ *
+ * Copyright (c) 2017, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file
+ *
+ * Keep track of the white listed domains for ipsecmod.
+ */
+
+#ifndef IPSECMOD_WHITELIST_H
+#define IPSECMOD_WHITELIST_H
+#include "util/storage/dnstree.h"
+
+struct config_file;
+struct regional;
+
+/**
+ * Process ipsecmod_whitelist config.
+ * @param ie: ipsecmod environment.
+ * @param cfg: config options.
+ * @return 0 on error.
+ */
+int ipsecmod_whitelist_apply_cfg(struct ipsecmod_env* ie,
+ struct config_file* cfg);
+
+/**
+ * Delete the ipsecmod whitelist.
+ * @param whitelist: ipsecmod whitelist.
+ */
+void ipsecmod_whitelist_delete(rbtree_type* whitelist);
+
+/**
+ * See if a domain is whitelisted.
+ * @param ie: ipsecmod environment.
+ * @param dname: domain name to check.
+ * @param dname_len: length of domain name.
+ * @param qclass: query CLASS.
+ * @return: true if the domain is whitelisted for the ipsecmod.
+ */
+int ipsecmod_domain_is_whitelisted(struct ipsecmod_env* ie, uint8_t* dname,
+ size_t dname_len, uint16_t qclass);
+
+/**
+ * Get memory used by ipsecmod whitelist.
+ * @param whitelist: structure for domain storage.
+ * @return bytes in use.
+ */
+size_t ipsecmod_whitelist_get_mem(rbtree_type* whitelist);
+
+#endif /* IPSECMOD_WHITELIST_H */
diff --git a/ipsecmod/ipsecmod.c b/ipsecmod/ipsecmod.c
new file mode 100644
index 000000000000..3e4ee6a53508
--- /dev/null
+++ b/ipsecmod/ipsecmod.c
@@ -0,0 +1,515 @@
+/*
+ * ipsecmod/ipsecmod.c - facilitate opportunistic IPsec module
+ *
+ * Copyright (c) 2017, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * This file contains a module that facilitates opportunistic IPsec. It does so
+ * by also quering for the IPSECKEY for A/AAAA queries and calling a
+ * configurable hook (eg. signaling an IKE daemon) before replying.
+ */
+
+#include "config.h"
+#ifdef USE_IPSECMOD
+#include "ipsecmod/ipsecmod.h"
+#include "ipsecmod/ipsecmod-whitelist.h"
+#include "util/fptr_wlist.h"
+#include "util/regional.h"
+#include "util/net_help.h"
+#include "util/config_file.h"
+#include "services/cache/dns.h"
+#include "sldns/wire2str.h"
+
+/** Apply configuration to ipsecmod module 'global' state. */
+static int
+ipsecmod_apply_cfg(struct ipsecmod_env* ipsecmod_env, struct config_file* cfg)
+{
+ if(!cfg->ipsecmod_hook || (cfg->ipsecmod_hook && !cfg->ipsecmod_hook[0])) {
+ log_err("ipsecmod: missing ipsecmod-hook.");
+ return 0;
+ }
+ if(cfg->ipsecmod_whitelist &&
+ !ipsecmod_whitelist_apply_cfg(ipsecmod_env, cfg))
+ return 0;
+ return 1;
+}
+
+int
+ipsecmod_init(struct module_env* env, int id)
+{
+ struct ipsecmod_env* ipsecmod_env = (struct ipsecmod_env*)calloc(1,
+ sizeof(struct ipsecmod_env));
+ if(!ipsecmod_env) {
+ log_err("malloc failure");
+ return 0;
+ }
+ env->modinfo[id] = (void*)ipsecmod_env;
+ ipsecmod_env->whitelist = NULL;
+ if(!ipsecmod_apply_cfg(ipsecmod_env, env->cfg)) {
+ log_err("ipsecmod: could not apply configuration settings.");
+ return 0;
+ }
+ return 1;
+}
+
+void
+ipsecmod_deinit(struct module_env* env, int id)
+{
+ struct ipsecmod_env* ipsecmod_env;
+ if(!env || !env->modinfo[id])
+ return;
+ ipsecmod_env = (struct ipsecmod_env*)env->modinfo[id];
+ /* Free contents. */
+ ipsecmod_whitelist_delete(ipsecmod_env->whitelist);
+ free(ipsecmod_env);
+ env->modinfo[id] = NULL;
+}
+
+/** New query for ipsecmod. */
+static int
+ipsecmod_new(struct module_qstate* qstate, int id)
+{
+ struct ipsecmod_qstate* iq = (struct ipsecmod_qstate*)regional_alloc(
+ qstate->region, sizeof(struct ipsecmod_qstate));
+ memset(iq, 0, sizeof(*iq));
+ qstate->minfo[id] = iq;
+ if(!iq)
+ return 0;
+ /* Initialise it. */
+ iq->enabled = qstate->env->cfg->ipsecmod_enabled;
+ iq->is_whitelisted = ipsecmod_domain_is_whitelisted(
+ (struct ipsecmod_env*)qstate->env->modinfo[id], qstate->qinfo.qname,
+ qstate->qinfo.qname_len, qstate->qinfo.qclass);
+ return 1;
+}
+
+/**
+ * Exit module with an error status.
+ * @param qstate: query state
+ * @param id: module id.
+ */
+static void
+ipsecmod_error(struct module_qstate* qstate, int id)
+{
+ qstate->ext_state[id] = module_error;
+ qstate->return_rcode = LDNS_RCODE_SERVFAIL;
+}
+
+/**
+ * Generate a request for the IPSECKEY.
+ *
+ * @param qstate: query state that is the parent.
+ * @param id: module id.
+ * @param name: what name to query for.
+ * @param namelen: length of name.
+ * @param qtype: query type.
+ * @param qclass: query class.
+ * @param flags: additional flags, such as the CD bit (BIT_CD), or 0.
+ * @return false on alloc failure.
+ */
+static int
+generate_request(struct module_qstate* qstate, int id, uint8_t* name,
+ size_t namelen, uint16_t qtype, uint16_t qclass, uint16_t flags)
+{
+ struct module_qstate* newq;
+ struct query_info ask;
+ ask.qname = name;
+ ask.qname_len = namelen;
+ ask.qtype = qtype;
+ ask.qclass = qclass;
+ ask.local_alias = NULL;
+ log_query_info(VERB_ALGO, "ipsecmod: generate request", &ask);
+ fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
+ if(!(*qstate->env->attach_sub)(qstate, &ask,
+ (uint16_t)(BIT_RD|flags), 0, 0, &newq)){
+ log_err("Could not generate request: out of memory");
+ return 0;
+ }
+ qstate->ext_state[id] = module_wait_subquery;
+ return 1;
+}
+
+/**
+ * Prepare the data and call the hook.
+ *
+ * @param qstate: query state.
+ * @param iq: ipsecmod qstate.
+ * @param ie: ipsecmod environment.
+ * @return true on success, false otherwise.
+ */
+static int
+call_hook(struct module_qstate* qstate, struct ipsecmod_qstate* iq,
+ struct ipsecmod_env* ATTR_UNUSED(ie))
+{
+ size_t slen, tempdata_len, tempstring_len, i;
+ char str[65535], *s, *tempstring;
+ int w;
+ struct ub_packed_rrset_key* rrset_key;
+ struct packed_rrset_data* rrset_data;
+ uint8_t *tempdata;
+
+ /* Check if a shell is available */
+ if(system(NULL) == 0) {
+ log_err("ipsecmod: no shell available for ipsecmod-hook");
+ return 0;
+ }
+
+ /* Zero the buffer. */
+ s = str;
+ slen = sizeof(str);
+ memset(s, 0, slen);
+
+ /* Copy the hook into the buffer. */
+ sldns_str_print(&s, &slen, "%s", qstate->env->cfg->ipsecmod_hook);
+ /* Put space into the buffer. */
+ sldns_str_print(&s, &slen, " ");
+ /* Copy the qname into the buffer. */
+ tempstring = sldns_wire2str_dname(qstate->qinfo.qname,
+ qstate->qinfo.qname_len);
+ if(!tempstring) {
+ log_err("ipsecmod: out of memory when calling the hook");
+ return 0;
+ }
+ sldns_str_print(&s, &slen, "\"%s\"", tempstring);
+ free(tempstring);
+ /* Put space into the buffer. */
+ sldns_str_print(&s, &slen, " ");
+ /* Copy the IPSECKEY TTL into the buffer. */
+ rrset_data = (struct packed_rrset_data*)iq->ipseckey_rrset->entry.data;
+ sldns_str_print(&s, &slen, "\"%ld\"", (long)rrset_data->ttl);
+ /* Put space into the buffer. */
+ sldns_str_print(&s, &slen, " ");
+ /* Copy the A/AAAA record(s) into the buffer. Start and end this section
+ * with a double quote. */
+ rrset_key = reply_find_answer_rrset(&qstate->return_msg->qinfo,
+ qstate->return_msg->rep);
+ rrset_data = (struct packed_rrset_data*)rrset_key->entry.data;
+ sldns_str_print(&s, &slen, "\"");
+ for(i=0; i<rrset_data->count; i++) {
+ if(i > 0) {
+ /* Put space into the buffer. */
+ sldns_str_print(&s, &slen, " ");
+ }
+ /* Ignore the first two bytes, they are the rr_data len. */
+ w = sldns_wire2str_rdata_buf(rrset_data->rr_data[i] + 2,
+ rrset_data->rr_len[i] - 2, s, slen, qstate->qinfo.qtype);
+ if(w < 0) {
+ /* Error in printout. */
+ return -1;
+ } else if((size_t)w >= slen) {
+ s = NULL; /* We do not want str to point outside of buffer. */
+ slen = 0;
+ return -1;
+ } else {
+ s += w;
+ slen -= w;
+ }
+ }
+ sldns_str_print(&s, &slen, "\"");
+ /* Put space into the buffer. */
+ sldns_str_print(&s, &slen, " ");
+ /* Copy the IPSECKEY record(s) into the buffer. Start and end this section
+ * with a double quote. */
+ sldns_str_print(&s, &slen, "\"");
+ rrset_data = (struct packed_rrset_data*)iq->ipseckey_rrset->entry.data;
+ for(i=0; i<rrset_data->count; i++) {
+ if(i > 0) {
+ /* Put space into the buffer. */
+ sldns_str_print(&s, &slen, " ");
+ }
+ /* Ignore the first two bytes, they are the rr_data len. */
+ tempdata = rrset_data->rr_data[i] + 2;
+ tempdata_len = rrset_data->rr_len[i] - 2;
+ /* Save the buffer pointers. */
+ tempstring = s; tempstring_len = slen;
+ w = sldns_wire2str_ipseckey_scan(&tempdata, &tempdata_len, &s, &slen,
+ NULL, 0);
+ /* There was an error when parsing the IPSECKEY; reset the buffer
+ * pointers to their previous values. */
+ if(w == -1){
+ s = tempstring; slen = tempstring_len;
+ }
+ }
+ sldns_str_print(&s, &slen, "\"");
+ verbose(VERB_ALGO, "ipsecmod: hook command: '%s'", str);
+ /* ipsecmod-hook should return 0 on success. */
+ if(system(str) != 0)
+ return 0;
+ return 1;
+}
+
+/**
+ * Handle an ipsecmod module event with a query
+ * @param qstate: query state (from the mesh), passed between modules.
+ * contains qstate->env module environment with global caches and so on.
+ * @param iq: query state specific for this module. per-query.
+ * @param ie: environment specific for this module. global.
+ * @param id: module id.
+ */
+static void
+ipsecmod_handle_query(struct module_qstate* qstate,
+ struct ipsecmod_qstate* iq, struct ipsecmod_env* ie, int id)
+{
+ struct ub_packed_rrset_key* rrset_key;
+ struct packed_rrset_data* rrset_data;
+ size_t i;
+ /* Pass to next module if we are not enabled and whitelisted. */
+ if(!(iq->enabled && iq->is_whitelisted)) {
+ qstate->ext_state[id] = module_wait_module;
+ return;
+ }
+ /* New query, check if the query is for an A/AAAA record and disable
+ * caching for other modules. */
+ if(!iq->ipseckey_done) {
+ if(qstate->qinfo.qtype == LDNS_RR_TYPE_A ||
+ qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) {
+ char type[16];
+ sldns_wire2str_type_buf(qstate->qinfo.qtype, type,
+ sizeof(type));
+ verbose(VERB_ALGO, "ipsecmod: query for %s; engaging",
+ type);
+ qstate->no_cache_store = 1;
+ }
+ /* Pass request to next module. */
+ qstate->ext_state[id] = module_wait_module;
+ return;
+ }
+ /* IPSECKEY subquery is finished. */
+ /* We have an IPSECKEY answer. */
+ if(iq->ipseckey_rrset) {
+ rrset_data = (struct packed_rrset_data*)iq->ipseckey_rrset->entry.data;
+ if(rrset_data) {
+ /* If bogus return SERVFAIL. */
+ if(!qstate->env->cfg->ipsecmod_ignore_bogus &&
+ rrset_data->security == sec_status_bogus) {
+ log_err("ipsecmod: bogus IPSECKEY");
+ ipsecmod_error(qstate, id);
+ return;
+ }
+ /* We have a valid IPSECKEY reply, call hook. */
+ if(!call_hook(qstate, iq, ie) &&
+ qstate->env->cfg->ipsecmod_strict) {
+ log_err("ipsecmod: ipsecmod-hook failed");
+ ipsecmod_error(qstate, id);
+ return;
+ }
+ /* Make sure the A/AAAA's TTL is equal/less than the
+ * ipsecmod_max_ttl. */
+ rrset_key = reply_find_answer_rrset(&qstate->return_msg->qinfo,
+ qstate->return_msg->rep);
+ rrset_data = (struct packed_rrset_data*)rrset_key->entry.data;
+ if(rrset_data->ttl > (time_t)qstate->env->cfg->ipsecmod_max_ttl) {
+ /* Update TTL for rrset to fixed value. */
+ rrset_data->ttl = qstate->env->cfg->ipsecmod_max_ttl;
+ for(i=0; i<rrset_data->count+rrset_data->rrsig_count; i++)
+ rrset_data->rr_ttl[i] = qstate->env->cfg->ipsecmod_max_ttl;
+ /* Also update reply_info's TTL */
+ if(qstate->return_msg->rep->ttl > (time_t)qstate->env->cfg->ipsecmod_max_ttl) {
+ qstate->return_msg->rep->ttl =
+ qstate->env->cfg->ipsecmod_max_ttl;
+ qstate->return_msg->rep->prefetch_ttl = PREFETCH_TTL_CALC(
+ qstate->return_msg->rep->ttl);
+ }
+ }
+ }
+ }
+ /* Store A/AAAA in cache. */
+ if(!dns_cache_store(qstate->env, &qstate->qinfo,
+ qstate->return_msg->rep, 0, qstate->prefetch_leeway,
+ 0, qstate->region, qstate->query_flags)) {
+ log_err("ipsecmod: out of memory caching record");
+ }
+ qstate->ext_state[id] = module_finished;
+}
+
+/**
+ * Handle an ipsecmod module event with a response from the iterator.
+ * @param qstate: query state (from the mesh), passed between modules.
+ * contains qstate->env module environment with global caches and so on.
+ * @param iq: query state specific for this module. per-query.
+ * @param ie: environment specific for this module. global.
+ * @param id: module id.
+ */
+static void
+ipsecmod_handle_response(struct module_qstate* qstate,
+ struct ipsecmod_qstate* ATTR_UNUSED(iq),
+ struct ipsecmod_env* ATTR_UNUSED(ie), int id)
+{
+ /* Pass to previous module if we are not enabled and whitelisted. */
+ if(!(iq->enabled && iq->is_whitelisted)) {
+ qstate->ext_state[id] = module_finished;
+ return;
+ }
+ /* check if the response is for an A/AAAA query. */
+ if((qstate->qinfo.qtype == LDNS_RR_TYPE_A ||
+ qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) &&
+ /* check that we had an answer for the A/AAAA query. */
+ qstate->return_msg &&
+ reply_find_answer_rrset(&qstate->return_msg->qinfo,
+ qstate->return_msg->rep) &&
+ /* check that another module didn't SERVFAIL. */
+ qstate->return_rcode == LDNS_RCODE_NOERROR) {
+ char type[16];
+ sldns_wire2str_type_buf(qstate->qinfo.qtype, type,
+ sizeof(type));
+ verbose(VERB_ALGO, "ipsecmod: response for %s; generating IPSECKEY "
+ "subquery", type);
+ /* generate an IPSECKEY query. */
+ if(!generate_request(qstate, id, qstate->qinfo.qname,
+ qstate->qinfo.qname_len, LDNS_RR_TYPE_IPSECKEY,
+ qstate->qinfo.qclass, 0)) {
+ log_err("ipsecmod: could not generate subquery.");
+ ipsecmod_error(qstate, id);
+ }
+ return;
+ }
+ /* we are done with the query. */
+ qstate->ext_state[id] = module_finished;
+}
+
+void
+ipsecmod_operate(struct module_qstate* qstate, enum module_ev event, int id,
+ struct outbound_entry* outbound)
+{
+ struct ipsecmod_env* ie = (struct ipsecmod_env*)qstate->env->modinfo[id];
+ struct ipsecmod_qstate* iq = (struct ipsecmod_qstate*)qstate->minfo[id];
+ verbose(VERB_QUERY, "ipsecmod[module %d] operate: extstate:%s event:%s",
+ id, strextstate(qstate->ext_state[id]), strmodulevent(event));
+ if(iq) log_query_info(VERB_QUERY, "ipsecmod operate: query",
+ &qstate->qinfo);
+
+ /* create ipsecmod_qstate. */
+ if((event == module_event_new || event == module_event_pass) &&
+ iq == NULL) {
+ if(!ipsecmod_new(qstate, id)) {
+ ipsecmod_error(qstate, id);
+ return;
+ }
+ iq = (struct ipsecmod_qstate*)qstate->minfo[id];
+ }
+ if(iq && (event == module_event_pass || event == module_event_new)) {
+ ipsecmod_handle_query(qstate, iq, ie, id);
+ return;
+ }
+ if(iq && (event == module_event_moddone)) {
+ ipsecmod_handle_response(qstate, iq, ie, id);
+ return;
+ }
+ if(iq && outbound) {
+ /* cachedb does not need to process responses at this time
+ * ignore it.
+ cachedb_process_response(qstate, iq, ie, id, outbound, event);
+ */
+ return;
+ }
+ if(event == module_event_error) {
+ verbose(VERB_ALGO, "got called with event error, giving up");
+ ipsecmod_error(qstate, id);
+ return;
+ }
+ if(!iq && (event == module_event_moddone)) {
+ /* during priming, module done but we never started. */
+ qstate->ext_state[id] = module_finished;
+ return;
+ }
+
+ log_err("ipsecmod: bad event %s", strmodulevent(event));
+ ipsecmod_error(qstate, id);
+ return;
+}
+
+void
+ipsecmod_inform_super(struct module_qstate* qstate, int id,
+ struct module_qstate* super)
+{
+ struct ipsecmod_qstate* siq;
+ log_query_info(VERB_ALGO, "ipsecmod: inform_super, sub is",
+ &qstate->qinfo);
+ log_query_info(VERB_ALGO, "super is", &super->qinfo);
+ siq = (struct ipsecmod_qstate*)super->minfo[id];
+ if(!siq) {
+ verbose(VERB_ALGO, "super has no ipsecmod state");
+ return;
+ }
+
+ if(qstate->return_msg) {
+ struct ub_packed_rrset_key* rrset_key = reply_find_answer_rrset(
+ &qstate->return_msg->qinfo, qstate->return_msg->rep);
+ if(rrset_key) {
+ /* We have an answer. */
+ /* Copy to super's region. */
+ rrset_key = packed_rrset_copy_region(rrset_key, super->region, 0);
+ siq->ipseckey_rrset = rrset_key;
+ if(!rrset_key) {
+ log_err("ipsecmod: out of memory.");
+ }
+ }
+ }
+ /* Notify super to proceed. */
+ siq->ipseckey_done = 1;
+}
+
+void
+ipsecmod_clear(struct module_qstate* qstate, int id)
+{
+ if(!qstate)
+ return;
+ qstate->minfo[id] = NULL;
+}
+
+size_t
+ipsecmod_get_mem(struct module_env* env, int id)
+{
+ struct ipsecmod_env* ie = (struct ipsecmod_env*)env->modinfo[id];
+ if(!ie)
+ return 0;
+ return sizeof(*ie) + ipsecmod_whitelist_get_mem(ie->whitelist);
+}
+
+/**
+ * The ipsecmod function block
+ */
+static struct module_func_block ipsecmod_block = {
+ "ipsecmod",
+ &ipsecmod_init, &ipsecmod_deinit, &ipsecmod_operate,
+ &ipsecmod_inform_super, &ipsecmod_clear, &ipsecmod_get_mem
+};
+
+struct module_func_block*
+ipsecmod_get_funcblock(void)
+{
+ return &ipsecmod_block;
+}
+#endif /* USE_IPSECMOD */
diff --git a/ipsecmod/ipsecmod.h b/ipsecmod/ipsecmod.h
new file mode 100644
index 000000000000..e00816d4bf99
--- /dev/null
+++ b/ipsecmod/ipsecmod.h
@@ -0,0 +1,97 @@
+/*
+ * ipsecmod/ipsecmod.h - facilitate opportunistic IPsec module
+ *
+ * Copyright (c) 2017, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file
+ *
+ * This file contains a module that facilitates opportunistic IPsec. It does so
+ * by also quering for the IPSECKEY for A/AAAA queries and calling a
+ * configurable hook (eg. signaling an IKE daemon) before replying.
+ */
+
+#ifndef IPSECMOD_H
+#define IPSECMOD_H
+#include "util/module.h"
+#include "util/rbtree.h"
+
+/**
+ * The global variable environment contents for the ipsecmod
+ * Shared between threads, this represents long term information.
+ */
+struct ipsecmod_env {
+ /** White listed domains for ipsecmod. */
+ rbtree_type* whitelist;
+};
+
+/**
+ * Per query state for the ipsecmod module.
+ */
+struct ipsecmod_qstate {
+ /** State of the IPsec module. */
+ /** NOTE: This value is copied here from the configuration so that a change
+ * with unbound-control would not complicate an already running mesh. */
+ int enabled;
+ /** If the qname is whitelisted or not. */
+ /** NOTE: No whitelist means all qnames are whitelisted. */
+ int is_whitelisted;
+ /** Pointer to IPSECKEY rrset allocated in the qstate region. NULL if there
+ * was no IPSECKEY reply from the subquery. */
+ struct ub_packed_rrset_key* ipseckey_rrset;
+ /** If the IPSECKEY subquery has finished. */
+ int ipseckey_done;
+};
+
+/** Init the ipsecmod module */
+int ipsecmod_init(struct module_env* env, int id);
+/** Deinit the ipsecmod module */
+void ipsecmod_deinit(struct module_env* env, int id);
+/** Operate on an event on a query (in qstate). */
+void ipsecmod_operate(struct module_qstate* qstate, enum module_ev event,
+ int id, struct outbound_entry* outbound);
+/** Subordinate query done, inform this super request of its conclusion */
+void ipsecmod_inform_super(struct module_qstate* qstate, int id,
+ struct module_qstate* super);
+/** clear the ipsecmod query-specific contents out of qstate */
+void ipsecmod_clear(struct module_qstate* qstate, int id);
+/** return memory estimate for the ipsecmod module */
+size_t ipsecmod_get_mem(struct module_env* env, int id);
+
+/**
+ * Get the function block with pointers to the ipsecmod functions
+ * @return the function block for "ipsecmod".
+ */
+struct module_func_block* ipsecmod_get_funcblock(void);
+
+#endif /* IPSECMOD_H */