diff options
Diffstat (limited to 'ipsecmod')
-rw-r--r-- | ipsecmod/ipsecmod-whitelist.c | 158 | ||||
-rw-r--r-- | ipsecmod/ipsecmod-whitelist.h | 82 | ||||
-rw-r--r-- | ipsecmod/ipsecmod.c | 515 | ||||
-rw-r--r-- | ipsecmod/ipsecmod.h | 97 |
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 */ |