diff --git a/configure.ac b/configure.ac index 66cbfa987..82c530424 100644 --- a/configure.ac +++ b/configure.ac @@ -136,6 +136,7 @@ ARG_DISBL_SET([pkcs12], [disable PKCS12 container support plugin.]) ARG_DISBL_SET([pgp], [disable PGP key decoding plugin.]) ARG_DISBL_SET([dnskey], [disable DNS RR key decoding plugin.]) ARG_DISBL_SET([sshkey], [disable SSH key decoding plugin.]) +ARG_ENABL_SET([dnscert], [enable DNSCERT authentication plugin.]) ARG_ENABL_SET([ipseckey], [enable IPSECKEY authentication plugin.]) ARG_DISBL_SET([pem], [disable PEM decoding plugin.]) ARG_DISBL_SET([hmac], [disable HMAC crypto implementation plugin.]) @@ -1015,6 +1016,7 @@ ADD_PLUGIN([pkcs12], [s charon scepclient pki scripts cmd]) ADD_PLUGIN([pgp], [s charon]) ADD_PLUGIN([dnskey], [s charon pki]) ADD_PLUGIN([sshkey], [s charon pki nm cmd]) +ADD_PLUGIN([dnscert], [c charon]) ADD_PLUGIN([ipseckey], [c charon]) ADD_PLUGIN([pem], [s charon openac scepclient pki scripts manager medsrv attest nm cmd]) ADD_PLUGIN([padlock], [s charon]) @@ -1181,6 +1183,7 @@ AM_CONDITIONAL(USE_ANDROID_LOG, test x$android_log = xtrue) AM_CONDITIONAL(USE_MAEMO, test x$maemo = xtrue) AM_CONDITIONAL(USE_SMP, test x$smp = xtrue) AM_CONDITIONAL(USE_SQL, test x$sql = xtrue) +AM_CONDITIONAL(USE_DNSCERT, test x$dnscert = xtrue) AM_CONDITIONAL(USE_IPSECKEY, test x$ipseckey = xtrue) AM_CONDITIONAL(USE_UPDOWN, test x$updown = xtrue) AM_CONDITIONAL(USE_DHCP, test x$dhcp = xtrue) @@ -1442,6 +1445,7 @@ AC_CONFIG_FILES([ src/libcharon/plugins/farp/Makefile src/libcharon/plugins/smp/Makefile src/libcharon/plugins/sql/Makefile + src/libcharon/plugins/dnscert/Makefile src/libcharon/plugins/ipseckey/Makefile src/libcharon/plugins/medsrv/Makefile src/libcharon/plugins/medcli/Makefile diff --git a/man/strongswan.conf.5.in b/man/strongswan.conf.5.in index e8dbe63f8..0fb239adc 100644 --- a/man/strongswan.conf.5.in +++ b/man/strongswan.conf.5.in @@ -886,7 +886,15 @@ File to read pseudo random bytes from, instead of @urandom_device@ File to read DNS resolver configuration from .TP .BR libstrongswan.plugins.unbound.trust_anchors " [/etc/ipsec.d/dnssec.keys]" -File to read DNSSEC trust anchors from (usually root zone KSK) +File to read DNSSEC trust anchors from (usually root zone KSK). The format of +the file is the standard DNS Zone file format, anchors can be stored as DS or +DNSKEY entries in the file. +.TP +.BR libstrongswan.plugins.unbound.dlv_anchors +File to read trusted keys for DLV (DNSSEC Lookaside Validation) from. It uses +the same format as \fItrust_anchors\fR. Only one DLV can be configured, which +is then used as a root trusted DLV, this means that it is a lookaside for +the root. .SS libtls section .TP .BR libtls.cipher diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index 5ee047b9c..df58eaa10 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -216,6 +216,13 @@ if MONOLITHIC endif endif +if USE_DNSCERT + SUBDIRS += plugins/dnscert +if MONOLITHIC + libcharon_la_LIBADD += plugins/dnscert/libstrongswan-dnscert.la +endif +endif + if USE_IPSECKEY SUBDIRS += plugins/ipseckey if MONOLITHIC diff --git a/src/libcharon/plugins/dnscert/Makefile.am b/src/libcharon/plugins/dnscert/Makefile.am new file mode 100644 index 000000000..51d542b30 --- /dev/null +++ b/src/libcharon/plugins/dnscert/Makefile.am @@ -0,0 +1,20 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libhydra \ + -I$(top_srcdir)/src/libcharon + +AM_CFLAGS = \ + -rdynamic + +if MONOLITHIC +noinst_LTLIBRARIES = libstrongswan-dnscert.la +else +plugin_LTLIBRARIES = libstrongswan-dnscert.la +endif + +libstrongswan_dnscert_la_SOURCES = \ + dnscert_plugin.h dnscert_plugin.c \ + dnscert_cred.h dnscert_cred.c \ + dnscert.h dnscert.c + +libstrongswan_dnscert_la_LDFLAGS = -module -avoid-version diff --git a/src/libcharon/plugins/dnscert/dnscert.c b/src/libcharon/plugins/dnscert/dnscert.c new file mode 100644 index 000000000..882ad9f0c --- /dev/null +++ b/src/libcharon/plugins/dnscert/dnscert.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2013 Ruslan Marchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "dnscert.h" + +#include +#include +#include + +typedef struct private_dnscert_t private_dnscert_t; + +/** +* private data of the dnscert +*/ +struct private_dnscert_t { + + /** + * public functions + */ + dnscert_t public; + + /** + * Certificate type + */ + u_int16_t cert_type; + + /** + * Key tag + */ + u_int16_t key_tag; + + /** + * Algorithm + */ + u_int8_t algorithm; + + /** + * Certificate + */ + chunk_t certificate; +}; + +METHOD(dnscert_t, get_cert_type, dnscert_type_t, + private_dnscert_t *this) +{ + return this->cert_type; +} + +METHOD(dnscert_t, get_key_tag, u_int16_t, + private_dnscert_t *this) +{ + return this->key_tag; +} + +METHOD(dnscert_t, get_algorithm, dnscert_algorithm_t, + private_dnscert_t *this) +{ + return this->algorithm; +} + +METHOD(dnscert_t, get_certificate, chunk_t, + private_dnscert_t *this) +{ + return this->certificate; +} + +METHOD(dnscert_t, destroy, void, + private_dnscert_t *this) +{ + chunk_free(&this->certificate); + free(this); +} + +dnscert_t *dnscert_create_frm_rr(rr_t *rr) +{ + private_dnscert_t *this; + bio_reader_t *reader = NULL; + + INIT(this, + .public = { + .get_cert_type = _get_cert_type, + .get_key_tag = _get_key_tag, + .get_algorithm = _get_algorithm, + .get_certificate = _get_certificate, + .destroy = _destroy, + }, + ); + + if (rr->get_type(rr) != RR_TYPE_CERT) + { + DBG1(DBG_CFG, "unable to create a dnscert out of an RR " + "whose type is not CERT"); + free(this); + return NULL; + } + + /** + * Parse the content (RDATA field) of the RR + * First - type/tag/algo fields and then cert body + */ + reader = bio_reader_create(rr->get_rdata(rr)); + if (!reader->read_uint16(reader, &this->cert_type) || + !reader->read_uint16(reader, &this->key_tag) || + !reader->read_uint8(reader, &this->algorithm) ) + { + DBG1(DBG_CFG, "CERT RR has a wrong format"); + reader->destroy(reader); + free(this); + return NULL; + } + + if (!reader->read_data(reader, reader->remaining(reader), + &this->certificate)) + { + DBG1(DBG_CFG, "failed to read DNS certificate field"); + reader->destroy(reader); + free(this); + return NULL; + } + this->certificate = chunk_clone(this->certificate); + reader->destroy(reader); + return &this->public; +} diff --git a/src/libcharon/plugins/dnscert/dnscert.h b/src/libcharon/plugins/dnscert/dnscert.h new file mode 100644 index 000000000..f93263fd6 --- /dev/null +++ b/src/libcharon/plugins/dnscert/dnscert.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2013 Ruslan Marchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup dnscert_i dnscert + * @{ @ingroup dnscert + */ + +#ifndef DNSCERT_H_ +#define DNSCERT_H_ + +typedef struct dnscert_t dnscert_t; +typedef enum dnscert_algorithm_t dnscert_algorithm_t; +typedef enum dnscert_type_t dnscert_type_t; + +#include + +/** + * DNS CERT types as defined in RFC 4398. + */ +enum dnscert_type_t { + /** Reserved value */ + DNSCERT_TYPE_RESERVED = 0, + /** An x509 PKIX certificate */ + DNSCERT_TYPE_PKIX = 1, + /** A SKPI certificate */ + DNSCERT_TYPE_SKPI = 2, + /** A PGP certificate */ + DNSCERT_TYPE_PGP = 3, + /** An x509 PKIX cert URL */ + DNSCERT_TYPE_IPKIX = 4, + /** A SKPI cert URL */ + DNSCERT_TYPE_ISKPI = 5, + /** A PGP cert fingerprint and URL */ + DNSCERT_TYPE_IPGP = 6, + /** An attribute Certificate */ + DNSCERT_TYPE_ACPKIX = 7, + /** An attribute cert URL */ + DNSCERT_TYPE_IACKPIX = 8 +}; + +/** + * DNSCERT algorithms as defined in http://www.iana.org/assignments/ + * dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml#dns-sec-alg-numbers-1 + */ +enum dnscert_algorithm_t { + /** No defined */ + DNSCERT_ALGORITHM_UNDEFINED = 0, + /** RSA/MD5 */ + DNSCERT_ALGORITHM_RSAMD5 = 1, + /** Diffie-Hellman */ + DNSCERT_ALGORITHM_DH = 2, + /** DSA/SHA1 */ + DNSCERT_ALGORITHM_DSASHA = 3, + /** Reserved */ + DNSCERT_ALGORITHM_RSRVD4 = 4, + /** RSA/SHA1 */ + DNSCERT_ALGORITHM_RSASHA = 5, + /** DSA/NSEC3/SHA */ + DNSCERT_ALGORITHM_DSANSEC3 = 6, + /** RSA/NSEC3/SHA */ + DNSCERT_ALGORITHM_RSANSEC3 = 7, + /** RSA/SHA256 */ + DNSCERT_ALGORITHM_RSASHA256 = 8, + /** Reserved */ + DNSCERT_ALGORITHM_RSRVD9 = 9, + /** RSA/SHA512 */ + DNSCERT_ALGORITHM_RSASHA512 = 10, +}; + +/** + * DNS CERT RR as defined in RFC 4398. + * + * The CERT resource record (RR) has the structure given below. Its RR + * type code is 37. + * + * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | type | key tag | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | algorithm | / + * +---------------+ certificate or CRL / + * / / + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-| + */ +struct dnscert_t { + + /** + * Get the type of the certificate body. + * + * The certificate "type" determines the format of the body + * of the CERT data. + * + * @return certificate type + */ + dnscert_type_t (*get_cert_type)(dnscert_t *this); + + /** + * Get the tag of the key part of the CERT. + * + * @return keytag + */ + u_int16_t (*get_key_tag)(dnscert_t *this); + + /** + * Get the algorithm. + * + * The "algorithm" determines the format of the public key field + * of the DNS CERT. + * + * @return algorithm + */ + dnscert_algorithm_t (*get_algorithm)(dnscert_t *this); + + /** + * Get the content of the certificate field as chunk. + * + * The format of the certificate depends on the type. + * + * The data pointed by the chunk is still owned by the DNSCERT. + * Clone it if necessary. + * + * @return certificate field as chunk + */ + chunk_t (*get_certificate)(dnscert_t *this); + + /** + * Destroy the DNSCERT. + */ + void (*destroy) (dnscert_t *this); +}; + +/** + * Create a dnscert instance out of a resource record. + * + * @param rr resource record which contains a DNSCERT + * @return dnscert, NULL on failure + */ +dnscert_t *dnscert_create_frm_rr(rr_t *rr); + +#endif /** DNSCERT_H_ @}*/ diff --git a/src/libcharon/plugins/dnscert/dnscert_cred.c b/src/libcharon/plugins/dnscert/dnscert_cred.c new file mode 100644 index 000000000..790252682 --- /dev/null +++ b/src/libcharon/plugins/dnscert/dnscert_cred.c @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Copyright (C) 2012 Reto Guadagnini + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +/* + * Copyright (C) 2013 Ruslan Marchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#define _GNU_SOURCE +#include +#include + +#include "dnscert_cred.h" +#include "dnscert.h" + +typedef struct private_dnscert_cred_t private_dnscert_cred_t; + +/** + * Private data of an dnscert_cred_t object + */ +struct private_dnscert_cred_t { + + /** + * Public part + */ + dnscert_cred_t public; + + /** + * DNS resolver + */ + resolver_t *res; +}; + +/** + * enumerator over certificates + */ +typedef struct { + /** implements enumerator interface */ + enumerator_t public; + /** inner enumerator (enumerates CERT resource records) */ + enumerator_t *inner; + /** response of the DNS resolver which contains the CERTs */ + resolver_response_t *response; +} cert_enumerator_t; + +METHOD(enumerator_t, cert_enumerator_enumerate, bool, + cert_enumerator_t *this, certificate_t **cert) +{ + dnscert_t *cur_crt; + rr_t *cur_rr; + chunk_t certificate; + + /* Get the next supported CERT using the inner enumerator. */ + while (this->inner->enumerate(this->inner, &cur_rr)) + { + cur_crt = dnscert_create_frm_rr(cur_rr); + + if (!cur_crt) + { + DBG1(DBG_CFG, " failed to parse CERT RR, skipping"); + continue; + } + + if (cur_crt->get_cert_type(cur_crt) != DNSCERT_TYPE_PKIX && + cur_crt->get_cert_type(cur_crt) != DNSCERT_TYPE_PGP) + { + DBG1(DBG_CFG, " unsupported CERT type [%d], skipping", + cur_crt->get_cert_type(cur_crt)); + cur_crt->destroy(cur_crt); + continue; + } + /* Try to parse PEM certificate container. Both x509 and PGP should + * presumably come as PEM encoded certs. */ + certificate = cur_crt->get_certificate(cur_crt); + *cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_ANY, + BUILD_BLOB_PEM, certificate, + BUILD_END); + if (*cert == NULL) + { + DBG1(DBG_CFG, " unable to parse certificate, skipping", + cur_crt->get_cert_type(cur_crt)); + cur_crt->destroy(cur_crt); + continue; + } + cur_crt->destroy(cur_crt); + return TRUE; + } + return FALSE; +} + +METHOD(enumerator_t, cert_enumerator_destroy, void, + cert_enumerator_t *this) +{ + this->inner->destroy(this->inner); + this->response->destroy(this->response); + free(this); +} + +METHOD(credential_set_t, create_cert_enumerator, enumerator_t*, + private_dnscert_cred_t *this, certificate_type_t cert, key_type_t key, + identification_t *id, bool trusted) +{ + resolver_response_t *response; + cert_enumerator_t *e; + char *fqdn; + + if (!id || id->get_type(id) != ID_FQDN) + { + return enumerator_create_empty(); + } + + /* query the DNS for the required CERT RRs */ + if (asprintf(&fqdn, "%Y", id) <= 0) + { + DBG1(DBG_CFG, "failed to determine FQDN to retrieve CERT RRs"); + return enumerator_create_empty(); + } + + DBG1(DBG_CFG, "performing a DNS query for CERT RRs of '%s'", fqdn); + response = this->res->query(this->res, fqdn, RR_CLASS_IN, RR_TYPE_CERT); + if (!response) + { + DBG1(DBG_CFG, " query for CERT RRs failed"); + free(fqdn); + return enumerator_create_empty(); + } + free(fqdn); + + if (!response->has_data(response) || + !response->query_name_exist(response)) + { + DBG1(DBG_CFG, " unable to retrieve CERT RRs from the DNS"); + response->destroy(response); + return enumerator_create_empty(); + } + + if (response->get_security_state(response) != SECURE) + { + DBG1(DBG_CFG, " DNSSEC state of CERT RRs is not secure"); + response->destroy(response); + return enumerator_create_empty(); + } + + INIT(e, + .public = { + .enumerate = (void*)_cert_enumerator_enumerate, + .destroy = _cert_enumerator_destroy, + }, + .inner = response->get_rr_set(response)->create_rr_enumerator( + response->get_rr_set(response)), + .response = response + ); + return &e->public; +} + +METHOD(dnscert_cred_t, destroy, void, + private_dnscert_cred_t *this) +{ + this->res->destroy(this->res); + free(this); +} + +/** + * Described in header. + */ +dnscert_cred_t *dnscert_cred_create(resolver_t *res) +{ + private_dnscert_cred_t *this; + + INIT(this, + .public = { + .set = { + .create_private_enumerator = (void*)return_null, + .create_cert_enumerator = _create_cert_enumerator, + .create_shared_enumerator = (void*)return_null, + .create_cdp_enumerator = (void*)return_null, + .cache_cert = (void*)nop, + }, + .destroy = _destroy, + }, + .res = res, + ); + + return &this->public; +} diff --git a/src/libcharon/plugins/dnscert/dnscert_cred.h b/src/libcharon/plugins/dnscert/dnscert_cred.h new file mode 100644 index 000000000..ceaa68f09 --- /dev/null +++ b/src/libcharon/plugins/dnscert/dnscert_cred.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2013 Ruslan Marchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup dnscert_cred_i dnscert_cred + * @{ @ingroup dnscert + */ + +#ifndef DNSCERT_CRED_H_ +#define DNSCERT_CRED_H_ + +#include +#include + +typedef struct dnscert_cred_t dnscert_cred_t; + +/** + * DNSCERT credential set. + * + * The dnscert credential set contains CERT RRs as certificates. + */ +struct dnscert_cred_t { + + /** + * Implements credential_set_t interface + */ + credential_set_t set; + + /** + * Destroy the dnscert_cred. + */ + void (*destroy)(dnscert_cred_t *this); +}; + +/** + * Create a dnscert_cred instance which uses the given resolver + * to query the DNS for CERT resource records. + * + * @param res resolver to use (gets adopted) + * @return credential set + */ +dnscert_cred_t *dnscert_cred_create(resolver_t *res); + +#endif /** DNSCERT_CRED_H_ @}*/ diff --git a/src/libcharon/plugins/dnscert/dnscert_plugin.c b/src/libcharon/plugins/dnscert/dnscert_plugin.c new file mode 100644 index 000000000..713bc9e3b --- /dev/null +++ b/src/libcharon/plugins/dnscert/dnscert_plugin.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Copyright (C) 2012 Reto Guadagnini + * Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +/* + * Copyright (C) 2013 Ruslan Marchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. +*/ + +#include "dnscert_plugin.h" + +#include +#include "dnscert_cred.h" + +typedef struct private_dnscert_plugin_t private_dnscert_plugin_t; + + +/** + * private data of the dnscert plugin + */ +struct private_dnscert_plugin_t { + + /** + * implements plugin interface + */ + dnscert_plugin_t public; + + /** + * credential set + */ + dnscert_cred_t *cred; + + /** + * DNSCERT based authentication enabled + */ + bool enabled; +}; + +METHOD(plugin_t, get_name, char*, + private_dnscert_plugin_t *this) +{ + return "dnscert"; +} + +METHOD(plugin_t, reload, bool, + private_dnscert_plugin_t *this) +{ + bool enabled = lib->settings->get_bool(lib->settings, + "%s.plugins.dnscert.enable", FALSE, charon->name); + + if (enabled != this->enabled) + { + if (enabled) + { + lib->credmgr->add_set(lib->credmgr, &this->cred->set); + } + else + { + lib->credmgr->remove_set(lib->credmgr, &this->cred->set); + } + this->enabled = enabled; + } + DBG1(DBG_CFG, "dnscert plugin is %sabled", this->enabled ? "en" : "dis"); + return TRUE; +} + +/** + * Create resolver and register credential set + */ +static bool plugin_cb(private_dnscert_plugin_t *this, + plugin_feature_t *feature, bool reg, void *cb_data) +{ + if (reg) + { + resolver_t *res; + + res = lib->resolver->create(lib->resolver); + if (!res) + { + DBG1(DBG_CFG, "failed to create a DNS resolver instance"); + return FALSE; + } + + this->cred = dnscert_cred_create(res); + reload(this); + } + else + { + if (this->enabled) + { + lib->credmgr->remove_set(lib->credmgr, &this->cred->set); + } + this->cred->destroy(this->cred); + } + return TRUE; +} + +METHOD(plugin_t, get_features, int, + private_dnscert_plugin_t *this, plugin_feature_t *features[]) +{ + static plugin_feature_t f[] = { + PLUGIN_CALLBACK((plugin_feature_callback_t)plugin_cb, NULL), + PLUGIN_PROVIDE(CUSTOM, "dnscert"), + PLUGIN_DEPENDS(RESOLVER), + PLUGIN_DEPENDS(CERT_DECODE, CERT_ANY), + PLUGIN_SDEPEND(CERT_DECODE, CERT_X509), + PLUGIN_SDEPEND(CERT_DECODE, CERT_GPG), + }; + *features = f; + return countof(f); +} + +METHOD(plugin_t, destroy, void, + private_dnscert_plugin_t *this) +{ + free(this); +} + +/* + * see header file + */ +plugin_t *dnscert_plugin_create() +{ + private_dnscert_plugin_t *this; + + INIT(this, + .public = { + .plugin = { + .get_name = _get_name, + .get_features = _get_features, + .reload = _reload, + .destroy = _destroy, + }, + }, + ); + + return &this->public.plugin; +} diff --git a/src/libcharon/plugins/dnscert/dnscert_plugin.h b/src/libcharon/plugins/dnscert/dnscert_plugin.h new file mode 100644 index 000000000..79ea64e40 --- /dev/null +++ b/src/libcharon/plugins/dnscert/dnscert_plugin.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2013 Ruslan Marchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup dnscert dnscert + * @ingroup cplugins + * + * @defgroup dnscert_plugin dnscert_plugin + * @{ @ingroup dnscert + */ + +#ifndef DNSCERT_PLUGIN_H_ +#define DNSCERT_PLUGIN_H_ + +#include + +typedef struct dnscert_plugin_t dnscert_plugin_t; + +/** + * DNSCERT plugin + * + * The DNSCERT plugin registers a credential set for CERT RRs. + * + * With this credential set it is possible to authenticate tunnel endpoints + * using CERT resource records which are retrieved from the DNS in a secure + * way (DNSSEC). + */ +struct dnscert_plugin_t { + + /** + * implements plugin interface + */ + plugin_t plugin; +}; + +#endif /** DNSCERT_PLUGIN_H_ @}*/ diff --git a/src/libcharon/plugins/ipseckey/ipseckey_cred.c b/src/libcharon/plugins/ipseckey/ipseckey_cred.c index e8722f12c..3ff6dd87d 100644 --- a/src/libcharon/plugins/ipseckey/ipseckey_cred.c +++ b/src/libcharon/plugins/ipseckey/ipseckey_cred.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2013 Tobias Brunner * Copyright (C) 2012 Reto Guadagnini * Hochschule fuer Technik Rapperswil * @@ -12,6 +13,7 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ + #define _GNU_SOURCE #include #include @@ -20,7 +22,6 @@ #include "ipseckey.h" #include -#include typedef struct private_ipseckey_cred_t private_ipseckey_cred_t; @@ -61,64 +62,59 @@ typedef struct { METHOD(enumerator_t, cert_enumerator_enumerate, bool, cert_enumerator_t *this, certificate_t **cert) { - rr_t *cur_rr = NULL; - ipseckey_t *cur_ipseckey = NULL; - chunk_t pub_key; - public_key_t * key = NULL; - bool supported_ipseckey_found = FALSE; + ipseckey_t *cur_ipseckey; + public_key_t *public; + rr_t *cur_rr; + chunk_t key; /* Get the next supported IPSECKEY using the inner enumerator. */ - while (this->inner->enumerate(this->inner, &cur_rr) && - !supported_ipseckey_found) + while (this->inner->enumerate(this->inner, &cur_rr)) { - supported_ipseckey_found = TRUE; - cur_ipseckey = ipseckey_create_frm_rr(cur_rr); if (!cur_ipseckey) { - DBG1(DBG_CFG, "failed to parse ipseckey - skipping this key"); - supported_ipseckey_found = FALSE; + DBG1(DBG_CFG, " failed to parse IPSECKEY, skipping"); + continue; } - if (cur_ipseckey && - cur_ipseckey->get_algorithm(cur_ipseckey) != IPSECKEY_ALGORITHM_RSA) + if (cur_ipseckey->get_algorithm(cur_ipseckey) != IPSECKEY_ALGORITHM_RSA) { - DBG1(DBG_CFG, "unsupported ipseckey algorithm -skipping this key"); + DBG1(DBG_CFG, " unsupported IPSECKEY algorithm, skipping"); cur_ipseckey->destroy(cur_ipseckey); - supported_ipseckey_found = FALSE; + continue; } - } - if (supported_ipseckey_found) - { - /* - * Wrap the key of the IPSECKEY in a certificate and return this - * certificate. - */ - pub_key = cur_ipseckey->get_public_key(cur_ipseckey); - - key = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, - BUILD_BLOB_DNSKEY, pub_key, - BUILD_END); - - if (!key) + /* wrap the key of the IPSECKEY in a certificate and return this + * certificate */ + key = cur_ipseckey->get_public_key(cur_ipseckey); + public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, + BUILD_BLOB_DNSKEY, key, + BUILD_END); + if (!public) { - DBG1(DBG_CFG, "failed to create public key from ipseckey"); + DBG1(DBG_CFG, " failed to create public key from IPSECKEY"); cur_ipseckey->destroy(cur_ipseckey); - return FALSE; + continue; } *cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_TRUSTED_PUBKEY, - BUILD_PUBLIC_KEY, key, + BUILD_PUBLIC_KEY, public, BUILD_SUBJECT, this->identity, BUILD_NOT_BEFORE_TIME, this->notBefore, BUILD_NOT_AFTER_TIME, this->notAfter, BUILD_END); + if (*cert == NULL) + { + DBG1(DBG_CFG, " failed to create certificate from IPSECKEY"); + cur_ipseckey->destroy(cur_ipseckey); + public->destroy(public); + continue; + } + cur_ipseckey->destroy(cur_ipseckey); return TRUE; } - return FALSE; } @@ -134,101 +130,95 @@ METHOD(credential_set_t, create_cert_enumerator, enumerator_t*, private_ipseckey_cred_t *this, certificate_type_t cert, key_type_t key, identification_t *id, bool trusted) { - char *fqdn = NULL; - resolver_response_t *response = NULL; - rr_set_t *rrset = NULL; - enumerator_t *rrsig_enum = NULL; - rr_t *rrsig = NULL; - bio_reader_t *reader = NULL; - chunk_t ignore; - u_int32_t nBefore, nAfter; + resolver_response_t *response; + enumerator_t *rrsig_enum; cert_enumerator_t *e; + rr_set_t *rrset; + rr_t *rrsig; + bio_reader_t *reader; + u_int32_t nBefore, nAfter; + chunk_t ignore; + char *fqdn; - if (id && id->get_type(id) == ID_FQDN) + if (!id || id->get_type(id) != ID_FQDN) { - /** Query the DNS for the required IPSECKEY RRs */ - - if (0 >= asprintf(&fqdn, "%Y", id)) - { - DBG1(DBG_CFG, "empty FQDN string"); - return enumerator_create_empty(); - } - - DBG1(DBG_CFG, "performing a DNS query for IPSECKEY RRs of '%s'", - fqdn); - response = this->res->query(this->res, fqdn, RR_CLASS_IN, - RR_TYPE_IPSECKEY); - if (!response) - { - DBG1(DBG_CFG, " query for IPSECKEY RRs failed"); - free(fqdn); - return enumerator_create_empty(); - } - - if (!response->has_data(response) || - !response->query_name_exist(response)) - { - DBG1(DBG_CFG, " unable to retrieve IPSECKEY RRs from the DNS"); - response->destroy(response); - free(fqdn); - return enumerator_create_empty(); - } - - if (!(response->get_security_state(response) == SECURE)) - { - DBG1(DBG_CFG, " DNSSEC state of IPSECKEY RRs is not secure"); - response->destroy(response); - free(fqdn); - return enumerator_create_empty(); - } - - free(fqdn); - - /** Determine the validity period of the retrieved IPSECKEYs - * - * We use the "Signature Inception" and "Signature Expiration" field - * of the first RRSIG RR to determine the validity period of the - * IPSECKEY RRs. TODO: Take multiple RRSIGs into account. - */ - rrset = response->get_rr_set(response); - rrsig_enum = rrset->create_rrsig_enumerator(rrset); - if (!rrsig_enum || !rrsig_enum->enumerate(rrsig_enum, &rrsig)) - { - DBG1(DBG_CFG, " unable to determine the validity period of " - "IPSECKEY RRs because no RRSIGs are present"); - DESTROY_IF(rrsig_enum); - response->destroy(response); - return enumerator_create_empty(); - } - - /** - * Parse the RRSIG for its validity period (RFC 4034) - */ - reader = bio_reader_create(rrsig->get_rdata(rrsig)); - reader->read_data(reader, 8, &ignore); - reader->read_uint32(reader, &nAfter); - reader->read_uint32(reader, &nBefore); - reader->destroy(reader); - - /*Create and return an iterator over the retrieved IPSECKEYs */ - INIT(e, - .public = { - .enumerate = (void*)_cert_enumerator_enumerate, - .destroy = _cert_enumerator_destroy, - }, - .inner = response->get_rr_set(response)->create_rr_enumerator( - response->get_rr_set(response)), - .response = response, - .notBefore = nBefore, - .notAfter = nAfter, - .identity = id, - ); - - return &e->public; + return enumerator_create_empty(); } + /* query the DNS for the required IPSECKEY RRs */ + if (asprintf(&fqdn, "%Y", id) <= 0) + { + DBG1(DBG_CFG, "failed to determine FQDN to retrieve IPSECKEY RRs"); + return enumerator_create_empty(); + } + DBG1(DBG_CFG, "performing a DNS query for IPSECKEY RRs of '%s'", fqdn); + response = this->res->query(this->res, fqdn, RR_CLASS_IN, RR_TYPE_IPSECKEY); + if (!response) + { + DBG1(DBG_CFG, " query for IPSECKEY RRs failed"); + free(fqdn); + return enumerator_create_empty(); + } + free(fqdn); - return enumerator_create_empty(); + if (!response->has_data(response) || + !response->query_name_exist(response)) + { + DBG1(DBG_CFG, " unable to retrieve IPSECKEY RRs from the DNS"); + response->destroy(response); + return enumerator_create_empty(); + } + + if (response->get_security_state(response) != SECURE) + { + DBG1(DBG_CFG, " DNSSEC state of IPSECKEY RRs is not secure"); + response->destroy(response); + return enumerator_create_empty(); + } + + /* determine the validity period of the retrieved IPSECKEYs + * + * we use the "Signature Inception" and "Signature Expiration" field + * of the first RRSIG RR to determine the validity period of the + * IPSECKEY RRs. + * TODO: Take multiple RRSIGs into account. */ + rrset = response->get_rr_set(response); + rrsig_enum = rrset->create_rrsig_enumerator(rrset); + if (!rrsig_enum || !rrsig_enum->enumerate(rrsig_enum, &rrsig)) + { + DBG1(DBG_CFG, " unable to determine the validity period of " + "IPSECKEY RRs because no RRSIGs are present"); + DESTROY_IF(rrsig_enum); + response->destroy(response); + return enumerator_create_empty(); + } + rrsig_enum->destroy(rrsig_enum); + + /* parse the RRSIG for its validity period (RFC 4034) */ + reader = bio_reader_create(rrsig->get_rdata(rrsig)); + if (!reader->read_data(reader, 8, &ignore) || + !reader->read_uint32(reader, &nAfter) || + !reader->read_uint32(reader, &nBefore)) + { + DBG1(DBG_CFG, " unable to determine the validity period of RRSIG RRs"); + reader->destroy(reader); + response->destroy(response); + return enumerator_create_empty(); + } + reader->destroy(reader); + + INIT(e, + .public = { + .enumerate = (void*)_cert_enumerator_enumerate, + .destroy = _cert_enumerator_destroy, + }, + .inner = rrset->create_rr_enumerator(rrset), + .response = response, + .notBefore = nBefore, + .notAfter = nAfter, + .identity = id, + ); + return &e->public; } METHOD(ipseckey_cred_t, destroy, void, diff --git a/src/libcharon/plugins/ipseckey/ipseckey_plugin.c b/src/libcharon/plugins/ipseckey/ipseckey_plugin.c index 2fd820f94..9bc49ba28 100644 --- a/src/libcharon/plugins/ipseckey/ipseckey_plugin.c +++ b/src/libcharon/plugins/ipseckey/ipseckey_plugin.c @@ -109,6 +109,8 @@ METHOD(plugin_t, get_features, int, PLUGIN_CALLBACK((plugin_feature_callback_t)plugin_cb, NULL), PLUGIN_PROVIDE(CUSTOM, "ipseckey"), PLUGIN_DEPENDS(RESOLVER), + PLUGIN_DEPENDS(PUBKEY, KEY_RSA), + PLUGIN_DEPENDS(CERT_ENCODE, CERT_TRUSTED_PUBKEY), }; *features = f; return countof(f); diff --git a/src/libstrongswan/plugins/unbound/unbound_resolver.c b/src/libstrongswan/plugins/unbound/unbound_resolver.c index 44a2c764b..42cdbc6cc 100644 --- a/src/libstrongswan/plugins/unbound/unbound_resolver.c +++ b/src/libstrongswan/plugins/unbound/unbound_resolver.c @@ -94,16 +94,17 @@ resolver_t *unbound_resolver_create(void) { private_resolver_t *this; int ub_retval = 0; - char *resolv_conf_file; - char *trust_anchor_file; + char *resolv_conf, *trust_anchors, *dlv_anchors; - resolv_conf_file = lib->settings->get_str(lib->settings, + resolv_conf = lib->settings->get_str(lib->settings, "libstrongswan.plugins.unbound.resolv_conf", RESOLV_CONF_FILE); - - trust_anchor_file = lib->settings->get_str(lib->settings, + trust_anchors = lib->settings->get_str(lib->settings, "libstrongswan.plugins.unbound.trust_anchors", TRUST_ANCHOR_FILE); + dlv_anchors = lib->settings->get_str(lib->settings, + "libstrongswan.plugins.unbound.dlv_anchors", + NULL); INIT(this, .public = { @@ -120,24 +121,34 @@ resolver_t *unbound_resolver_create(void) return NULL; } - DBG1(DBG_CFG, "loading unbound resolver config from '%s'", resolv_conf_file); - ub_retval = ub_ctx_resolvconf(this->ctx, resolv_conf_file); + DBG2(DBG_CFG, "loading unbound resolver config from '%s'", resolv_conf); + ub_retval = ub_ctx_resolvconf(this->ctx, resolv_conf); if (ub_retval) { DBG1(DBG_CFG, "failed to read the resolver config: %s (%s)", - ub_strerror(ub_retval), strerror(errno)); + ub_strerror(ub_retval), strerror(errno)); destroy(this); return NULL; } - DBG1(DBG_CFG, "loading unbound trust anchors from '%s'", trust_anchor_file); - ub_retval = ub_ctx_add_ta_file(this->ctx, trust_anchor_file); + DBG2(DBG_CFG, "loading unbound trust anchors from '%s'", trust_anchors); + ub_retval = ub_ctx_add_ta_file(this->ctx, trust_anchors); if (ub_retval) { DBG1(DBG_CFG, "failed to load trust anchors: %s (%s)", - ub_strerror(ub_retval), strerror(errno)); + ub_strerror(ub_retval), strerror(errno)); } + if (dlv_anchors) + { + DBG2(DBG_CFG, "loading trusted keys for DLV from '%s'", dlv_anchors); + ub_retval = ub_ctx_set_option(this->ctx, "dlv-anchor-file:", + dlv_anchors); + if (ub_retval) + { + DBG1(DBG_CFG, "failed to load trusted keys for DLV: %s (%s)", + ub_strerror(ub_retval), strerror(errno)); + } + } return &this->public; } - diff --git a/testing/hosts/winnetou/etc/bind/db.strongswan.org b/testing/hosts/winnetou/etc/bind/db.strongswan.org index dfd2705cb..694e2cee1 100644 --- a/testing/hosts/winnetou/etc/bind/db.strongswan.org +++ b/testing/hosts/winnetou/etc/bind/db.strongswan.org @@ -31,6 +31,57 @@ crl IN CNAME winnetou.strongswan.org. ldap IN CNAME winnetou.strongswan.org. ocsp IN CNAME winnetou.strongswan.org. ; +moon IN CERT ( 1 0 0 + MIIEIjCCAwqgAwIBAgIBFzANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJDSDEZ + MBcGA1UEChMQTGludXggc3Ryb25nU3dhbjEbMBkGA1UEAxMSc3Ryb25nU3dhbiBS + b290IENBMB4XDTA5MDgyNzEwMDMzMloXDTE0MDgyNjEwMDMzMlowRjELMAkGA1UE + BhMCQ0gxGTAXBgNVBAoTEExpbnV4IHN0cm9uZ1N3YW4xHDAaBgNVBAMTE21vb24u + c3Ryb25nc3dhbi5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDK + L2M91Lu6BYYhWxWgMS9z9TMSTwszm5rhO7ZIsCtMRo4PAeYw+++SGXt3CPXb/+p+ + SWKGlm11rPE71eQ3ehgh2C3hAurfmWO0iQQaCw+fdreeIVCqOQIOP6UqZ327h5yY + YpHk8VQv4vBJTpxclU1PqnWheqe1ZlLxsW773LRml/fQt/UgvJkCBTZZONLNMfK+ + 7TDnYaVsAtncgvDN78nUNEe2qY92KK7SrBJ6SpUEg49m51F+XgsGcsgWVHS85on3 + Om/G48crLEVJjdu8CxewSRVgb+lPJWzHd8QsU0Vg/7vlqs3ZRMyNtNKrr4opSvVb + A6agGlTXhDCreDiXU8KHAgMBAAGjggEaMIIBFjAJBgNVHRMEAjAAMAsGA1UdDwQE + AwIDqDAdBgNVHQ4EFgQUapx00fiJeYn2WpTpifH6w2SdKS4wbQYDVR0jBGYwZIAU + XafdcAZRMn7ntm2zteXgYOouTe+hSaRHMEUxCzAJBgNVBAYTAkNIMRkwFwYDVQQK + ExBMaW51eCBzdHJvbmdTd2FuMRswGQYDVQQDExJzdHJvbmdTd2FuIFJvb3QgQ0GC + AQAwHgYDVR0RBBcwFYITbW9vbi5zdHJvbmdzd2FuLm9yZzATBgNVHSUEDDAKBggr + BgEFBQcDATA5BgNVHR8EMjAwMC6gLKAqhihodHRwOi8vY3JsLnN0cm9uZ3N3YW4u + b3JnL3N0cm9uZ3N3YW4uY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQCctXg2xeMozaTV + jiBL1P8MY9uEH5JtU0EceQ1RbI5/2vGRdnECND9oADY5vamaaE2Mdq2Qh/vlXnML + o3ii5ELjsQlYdTYZOcMOdcUUXYvbbFX1cwpkBhyBl1H25KptHcgQ/HnceKp3kOuq + wYOYjgwePXulcpWXx0E2QtQCFQQZFPyEWeNJxH0oglg53QPXfHY9I2/Gukj5V0bz + p7ME0Gs8KdnYdmbbDqzQgPsta96/m+HoJlsrVF+4Gqihj6BWMBQ2ybjPWZdG3oH9 + 25cE8v60Ry98D0Z/tygbAUFnh5oOvaf642paVgc3aoA77I8U+UZjECxISoiHultY + 7QTufOwP + ) +sun IN CERT ( 1 0 0 + MIIEIDCCAwigAwIBAgIBFjANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJDSDEZ + MBcGA1UEChMQTGludXggc3Ryb25nU3dhbjEbMBkGA1UEAxMSc3Ryb25nU3dhbiBS + b290IENBMB4XDTA5MDgyNzA5NTkwNFoXDTE0MDgyNjA5NTkwNFowRTELMAkGA1UE + BhMCQ0gxGTAXBgNVBAoTEExpbnV4IHN0cm9uZ1N3YW4xGzAZBgNVBAMTEnN1bi5z + dHJvbmdzd2FuLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN+V + VIpn6Q5jaU//EN6p6A5cSfUfhBK0mFa2laFFZh/Y0h66AXqqrQ3X917h7YNsSk68 + oowY9h9I3gOx7hNVBsJr2VjdYC+b0q5NTha09/A5mimv/prYj6o0yawxoPjoDs9Y + h7D7Kf+F8fkgk0stlHJZX66J7dNrFXbg1xBld+Ep5Or2FbEZ9QWUpRQTuhdpNt/4 + 9YuxQ59DemY9IRbwsrKCHH0mGrJsDdqeb0ap+8QvSXHjCt1fr9MNKWaAFAQLKQI4 + e0da1ntPCEQLeE833+NNRBgGufk0KqGT3eAXqrxa9AEIUJnVcPexQdqUMjcUpXFb + 8WNzRWB8Egh3BDK6FsECAwEAAaOCARkwggEVMAkGA1UdEwQCMAAwCwYDVR0PBAQD + AgOoMB0GA1UdDgQWBBRW1p4v2qihzRlcI1PnxbZwluML+zBtBgNVHSMEZjBkgBRd + p91wBlEyfue2bbO15eBg6i5N76FJpEcwRTELMAkGA1UEBhMCQ0gxGTAXBgNVBAoT + EExpbnV4IHN0cm9uZ1N3YW4xGzAZBgNVBAMTEnN0cm9uZ1N3YW4gUm9vdCBDQYIB + ADAdBgNVHREEFjAUghJzdW4uc3Ryb25nc3dhbi5vcmcwEwYDVR0lBAwwCgYIKwYB + BQUHAwEwOQYDVR0fBDIwMDAuoCygKoYoaHR0cDovL2NybC5zdHJvbmdzd2FuLm9y + Zy9zdHJvbmdzd2FuLmNybDANBgkqhkiG9w0BAQsFAAOCAQEAo37LYT9Awx0MK/nA + FZpPJqUr0Ey+O5Ukcsdx7nd00SlmpiQRY8KmuRXCBQnDEgdLstd3slQjT0pJEgWF + 0pzxybnI6eOzYAhLfhart+X1hURiNGbXjggm2s4I5+K32bVIkNEqlsYnd/6F9oo5 + ZNO0/eTTruLZfkNe/zchBGKe/Z7MacVwlYWWCbMtBV4K1d5dGcRRgpQ9WivDlmat + Nh9wlscDSgSGk3HJkbxnq695VN7zUbDWAUvWWhV5bIDjlAR/xyT9ApqIxiyVVRul + fYrE7U05Hbt6GgAroAKLp6qJup9+TxQAKSjKIwJ0hf7OuYyQ8TZtVHS7AOhm+T/5 + G/jGGA== + ) +; moon IN IPSECKEY ( 10 1 2 192.168.0.1 AwEAAcovYz3Uu7oFhiFbFaAxL3P1MxJPCzObmuE7tkiwK0xGjg8B5jD7 75IZe3cI9dv/6n5JYoaWbXWs8TvV5Dd6GCHYLeEC6t+ZY7SJBBoLD592 diff --git a/testing/scripts/recipes/013_strongswan.mk b/testing/scripts/recipes/013_strongswan.mk index efa4a77e3..85f80fe5b 100644 --- a/testing/scripts/recipes/013_strongswan.mk +++ b/testing/scripts/recipes/013_strongswan.mk @@ -75,6 +75,7 @@ CONFIG_OPTS = \ --enable-unity \ --enable-unbound \ --enable-ipseckey \ + --enable-dnscert \ --enable-cmd \ --enable-libipsec \ --enable-kernel-libipsec \ diff --git a/testing/testing.conf b/testing/testing.conf index 4b30ee04b..1e691f8cd 100644 --- a/testing/testing.conf +++ b/testing/testing.conf @@ -14,9 +14,10 @@ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. -if [ -f testing.conf.local ] +TESTINGDIR=$(dirname `readlink -f ${BASH_SOURCE[0]}`) +if [ -f $TESTINGDIR/testing.conf.local ] then - . testing.conf.local + . $TESTINGDIR/testing.conf.local fi # Root directory of testing diff --git a/testing/tests/ikev2/net2net-dnscert/description.txt b/testing/tests/ikev2/net2net-dnscert/description.txt new file mode 100644 index 000000000..40c112bc4 --- /dev/null +++ b/testing/tests/ikev2/net2net-dnscert/description.txt @@ -0,0 +1,8 @@ +A connection between the subnets behind the gateways moon and sun is set up. +The authentication is based on trustworthy public keys stored as CERT +resource records in the Domain Name System (DNS) and protected by DNSSEC. +

+Upon the successful establishment of the IPsec tunnel, leftfirewall=yes +automatically inserts iptables-based firewall rules that let pass the tunneled traffic. +In order to test both tunnel and firewall, client alice behind gateway moon +pings client bob located behind gateway sun. diff --git a/testing/tests/ikev2/net2net-dnscert/evaltest.dat b/testing/tests/ikev2/net2net-dnscert/evaltest.dat new file mode 100644 index 000000000..effc9bc1f --- /dev/null +++ b/testing/tests/ikev2/net2net-dnscert/evaltest.dat @@ -0,0 +1,9 @@ +moon:: cat /var/log/daemon.log::performing a DNS query for CERT RRs of.*sun.strongswan.org::YES +sun:: cat /var/log/daemon.log::performing a DNS query for CERT RRs of.*moon.strongswan.org::YES +moon:: ipsec status 2> /dev/null::net-net.*ESTABLISHED.*moon.strongswan.org.*sun.strongswan.org::YES +sun:: ipsec status 2> /dev/null::net-net.*ESTABLISHED.*sun.strongswan.org.*moon.strongswan.org::YES +moon:: ipsec status 2> /dev/null::INSTALLED, TUNNEL::YES +sun:: ipsec status 2> /dev/null::INSTALLED, TUNNEL::YES +alice::ping -c 1 PH_IP_BOB::64 bytes from PH_IP_BOB: icmp_req=1::YES +sun::tcpdump::IP moon.strongswan.org > sun.strongswan.org: ESP::YES +sun::tcpdump::IP sun.strongswan.org > moon.strongswan.org: ESP::YES diff --git a/testing/tests/ikev2/net2net-dnscert/hosts/moon/etc/ipsec.conf b/testing/tests/ikev2/net2net-dnscert/hosts/moon/etc/ipsec.conf new file mode 100644 index 000000000..3eaf60a1d --- /dev/null +++ b/testing/tests/ikev2/net2net-dnscert/hosts/moon/etc/ipsec.conf @@ -0,0 +1,26 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +config setup + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + keyexchange=ikev2 + mobike=no + +conn net-net + left=PH_IP_MOON + leftid=moon.strongswan.org + leftsubnet=10.1.0.0/16 + leftcert=moonCert.pem + leftsendcert=never + leftauth=pubkey + leftfirewall=yes + right=sun.strongswan.org + rightid=sun.strongswan.org + rightsubnet=10.2.0.0/16 + rightsendcert=never + rightauth=pubkey + auto=add diff --git a/testing/tests/ikev2/net2net-dnscert/hosts/moon/etc/ipsec.d/dnssec.keys b/testing/tests/ikev2/net2net-dnscert/hosts/moon/etc/ipsec.d/dnssec.keys new file mode 100644 index 000000000..d059d8476 --- /dev/null +++ b/testing/tests/ikev2/net2net-dnscert/hosts/moon/etc/ipsec.d/dnssec.keys @@ -0,0 +1,10 @@ +; This is a key-signing key, keyid 32329, for . +. IN DNSKEY 257 3 8 ( + AwEAAbcskaratFgvgvXl0bNq4I43ZBzd9jYnoPqsIcA0ahqXlUTUa+c2 + XzN2mS7DGcI4Z5Gn+8v/Ih4lQJQrlf9I/c2HjooCAsK1bA5cRS2DiU+b + L6Ge0nLtvNOf4C0MHGLrWcDONg5QoL0OcFvMXuUtOvDkoIMdtfDYDScx + E9vSokc98Sx553/MTxpssXeM9i+OauGqohIZU+MVRdWwvJPieCL7Ma4b + AttgG+KSbQy7x/qXPISoqzwGQvCxsL93fvD/cpp+KziqA0oH+Dfryvc5 + nWdCdra4gYz7WCFFwcY1PW6PbL5ie4jnjl3WWxopuzT46HKROxDhE+FO + O9fOgGnjzAk= + ) diff --git a/testing/tests/ikev2/net2net-dnscert/hosts/moon/etc/iptables.rules b/testing/tests/ikev2/net2net-dnscert/hosts/moon/etc/iptables.rules new file mode 100644 index 000000000..b2c425289 --- /dev/null +++ b/testing/tests/ikev2/net2net-dnscert/hosts/moon/etc/iptables.rules @@ -0,0 +1,28 @@ +*filter + +# default policy is DROP +-P INPUT DROP +-P OUTPUT DROP +-P FORWARD DROP + +# allow esp +-A INPUT -i eth0 -p 50 -j ACCEPT +-A OUTPUT -o eth0 -p 50 -j ACCEPT + +# allow IKE +-A INPUT -i eth0 -p udp --sport 500 --dport 500 -j ACCEPT +-A OUTPUT -o eth0 -p udp --dport 500 --sport 500 -j ACCEPT + +# allow MobIKE +-A INPUT -i eth0 -p udp --sport 4500 --dport 4500 -j ACCEPT +-A OUTPUT -o eth0 -p udp --dport 4500 --sport 4500 -j ACCEPT + +# allow ssh +-A INPUT -p tcp --dport 22 -j ACCEPT +-A OUTPUT -p tcp --sport 22 -j ACCEPT + +# allow DNSSEC fetch from winnetou +-A INPUT -i eth0 -p udp --sport 53 -s PH_IP_WINNETOU -j ACCEPT +-A OUTPUT -o eth0 -p udp --dport 53 -d PH_IP_WINNETOU -j ACCEPT + +COMMIT diff --git a/testing/tests/ikev2/net2net-dnscert/hosts/moon/etc/resolv.conf b/testing/tests/ikev2/net2net-dnscert/hosts/moon/etc/resolv.conf new file mode 100644 index 000000000..73d926def --- /dev/null +++ b/testing/tests/ikev2/net2net-dnscert/hosts/moon/etc/resolv.conf @@ -0,0 +1 @@ +nameserver PH_IP_WINNETOU diff --git a/testing/tests/ikev2/net2net-dnscert/hosts/moon/etc/strongswan.conf b/testing/tests/ikev2/net2net-dnscert/hosts/moon/etc/strongswan.conf new file mode 100644 index 000000000..e9c79b333 --- /dev/null +++ b/testing/tests/ikev2/net2net-dnscert/hosts/moon/etc/strongswan.conf @@ -0,0 +1,20 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon { + load = sha1 sha2 md5 aes des hmac gmp dnskey pem pkcs1 pubkey unbound dnscert random nonce x509 curl kernel-netlink socket-default stroke updown + + plugins { + dnscert { + enable = yes + } + } +} + +libstrongswan { + plugins { + unbound { + # trust_anchors = /etc/ipsec.d/dnssec.keys + # resolv_conf = /etc/resolv.conf + } + } +} diff --git a/testing/tests/ikev2/net2net-dnscert/hosts/sun/etc/ipsec.conf b/testing/tests/ikev2/net2net-dnscert/hosts/sun/etc/ipsec.conf new file mode 100644 index 000000000..75c4addda --- /dev/null +++ b/testing/tests/ikev2/net2net-dnscert/hosts/sun/etc/ipsec.conf @@ -0,0 +1,26 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +config setup + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + keyexchange=ikev2 + mobike=no + +conn net-net + left=PH_IP_SUN + leftid=sun.strongswan.org + leftsubnet=10.2.0.0/16 + leftcert=sunCert.pem + leftsendcert=never + leftauth=pubkey + leftfirewall=yes + right=moon.strongswan.org + rightid=moon.strongswan.org + rightsubnet=10.1.0.0/16 + rightsendcert=never + rightauth=pubkey + auto=add diff --git a/testing/tests/ikev2/net2net-dnscert/hosts/sun/etc/ipsec.d/dnssec.keys b/testing/tests/ikev2/net2net-dnscert/hosts/sun/etc/ipsec.d/dnssec.keys new file mode 100644 index 000000000..d059d8476 --- /dev/null +++ b/testing/tests/ikev2/net2net-dnscert/hosts/sun/etc/ipsec.d/dnssec.keys @@ -0,0 +1,10 @@ +; This is a key-signing key, keyid 32329, for . +. IN DNSKEY 257 3 8 ( + AwEAAbcskaratFgvgvXl0bNq4I43ZBzd9jYnoPqsIcA0ahqXlUTUa+c2 + XzN2mS7DGcI4Z5Gn+8v/Ih4lQJQrlf9I/c2HjooCAsK1bA5cRS2DiU+b + L6Ge0nLtvNOf4C0MHGLrWcDONg5QoL0OcFvMXuUtOvDkoIMdtfDYDScx + E9vSokc98Sx553/MTxpssXeM9i+OauGqohIZU+MVRdWwvJPieCL7Ma4b + AttgG+KSbQy7x/qXPISoqzwGQvCxsL93fvD/cpp+KziqA0oH+Dfryvc5 + nWdCdra4gYz7WCFFwcY1PW6PbL5ie4jnjl3WWxopuzT46HKROxDhE+FO + O9fOgGnjzAk= + ) diff --git a/testing/tests/ikev2/net2net-dnscert/hosts/sun/etc/iptables.rules b/testing/tests/ikev2/net2net-dnscert/hosts/sun/etc/iptables.rules new file mode 100644 index 000000000..b2c425289 --- /dev/null +++ b/testing/tests/ikev2/net2net-dnscert/hosts/sun/etc/iptables.rules @@ -0,0 +1,28 @@ +*filter + +# default policy is DROP +-P INPUT DROP +-P OUTPUT DROP +-P FORWARD DROP + +# allow esp +-A INPUT -i eth0 -p 50 -j ACCEPT +-A OUTPUT -o eth0 -p 50 -j ACCEPT + +# allow IKE +-A INPUT -i eth0 -p udp --sport 500 --dport 500 -j ACCEPT +-A OUTPUT -o eth0 -p udp --dport 500 --sport 500 -j ACCEPT + +# allow MobIKE +-A INPUT -i eth0 -p udp --sport 4500 --dport 4500 -j ACCEPT +-A OUTPUT -o eth0 -p udp --dport 4500 --sport 4500 -j ACCEPT + +# allow ssh +-A INPUT -p tcp --dport 22 -j ACCEPT +-A OUTPUT -p tcp --sport 22 -j ACCEPT + +# allow DNSSEC fetch from winnetou +-A INPUT -i eth0 -p udp --sport 53 -s PH_IP_WINNETOU -j ACCEPT +-A OUTPUT -o eth0 -p udp --dport 53 -d PH_IP_WINNETOU -j ACCEPT + +COMMIT diff --git a/testing/tests/ikev2/net2net-dnscert/hosts/sun/etc/resolv.conf b/testing/tests/ikev2/net2net-dnscert/hosts/sun/etc/resolv.conf new file mode 100644 index 000000000..73d926def --- /dev/null +++ b/testing/tests/ikev2/net2net-dnscert/hosts/sun/etc/resolv.conf @@ -0,0 +1 @@ +nameserver PH_IP_WINNETOU diff --git a/testing/tests/ikev2/net2net-dnscert/hosts/sun/etc/strongswan.conf b/testing/tests/ikev2/net2net-dnscert/hosts/sun/etc/strongswan.conf new file mode 100644 index 000000000..e9c79b333 --- /dev/null +++ b/testing/tests/ikev2/net2net-dnscert/hosts/sun/etc/strongswan.conf @@ -0,0 +1,20 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon { + load = sha1 sha2 md5 aes des hmac gmp dnskey pem pkcs1 pubkey unbound dnscert random nonce x509 curl kernel-netlink socket-default stroke updown + + plugins { + dnscert { + enable = yes + } + } +} + +libstrongswan { + plugins { + unbound { + # trust_anchors = /etc/ipsec.d/dnssec.keys + # resolv_conf = /etc/resolv.conf + } + } +} diff --git a/testing/tests/ikev2/net2net-dnscert/posttest.dat b/testing/tests/ikev2/net2net-dnscert/posttest.dat new file mode 100644 index 000000000..c594c4dc8 --- /dev/null +++ b/testing/tests/ikev2/net2net-dnscert/posttest.dat @@ -0,0 +1,8 @@ +moon::ipsec stop +sun::ipsec stop +moon::iptables-restore < /etc/iptables.flush +sun::iptables-restore < /etc/iptables.flush +moon::rm /etc/resolv.conf +sun::rm /etc/resolv.conf +moon::rm /etc/ipsec.d/dnssec.keys +sun::rm /etc/ipsec.d/dnssec.keys diff --git a/testing/tests/ikev2/net2net-dnscert/pretest.dat b/testing/tests/ikev2/net2net-dnscert/pretest.dat new file mode 100644 index 000000000..0f4ae0f4f --- /dev/null +++ b/testing/tests/ikev2/net2net-dnscert/pretest.dat @@ -0,0 +1,8 @@ +moon::iptables-restore < /etc/iptables.rules +sun::iptables-restore < /etc/iptables.rules +moon::rm /etc/ipsec.d/cacerts/* +sun::rm /etc/ipsec.d/cacerts/* +moon::ipsec start +sun::ipsec start +moon::sleep 2 +moon::ipsec up net-net diff --git a/testing/tests/ikev2/net2net-dnscert/test.conf b/testing/tests/ikev2/net2net-dnscert/test.conf new file mode 100644 index 000000000..afa2accbe --- /dev/null +++ b/testing/tests/ikev2/net2net-dnscert/test.conf @@ -0,0 +1,21 @@ +#!/bin/bash +# +# This configuration file provides information on the +# guest instances used for this test + +# All guest instances that are required for this test +# +VIRTHOSTS="alice moon winnetou sun bob" + +# Corresponding block diagram +# +DIAGRAM="a-m-w-s-b.png" + +# Guest instances on which tcpdump is to be started +# +TCPDUMPHOSTS="sun" + +# Guest instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="moon sun"