diff --git a/src/Makefile.am b/src/Makefile.am index b78e3c0c1c38a90f48b0329e43c76b962252dd6a..b4f1bf83b056c3475ecfa1aa3a672b9c42ecd3ad 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -155,8 +155,9 @@ libknot_la_SOURCES = \ libknot/tsig-op.c \ libknot/binary.h \ libknot/binary.c \ - libknot/dnssec/cleanup.h \ libknot/dnssec/config.h \ + libknot/dnssec/crypto.c \ + libknot/dnssec/crypto.h \ libknot/dnssec/key.c \ libknot/dnssec/key.h \ libknot/dnssec/nsec-bitmap.h \ diff --git a/src/knot/main.c b/src/knot/main.c index bc1b6458b4fffff34f383fa33c79d869c77dd4b7..704b5a89d649b3421139ecc6305032e6ad9596cb 100644 --- a/src/knot/main.c +++ b/src/knot/main.c @@ -27,7 +27,7 @@ #endif /* HAVE_CAP_NG_H */ #include "libknot/common.h" -#include "libknot/dnssec/cleanup.h" +#include "libknot/dnssec/crypto.h" #include "common/evqueue.h" #include "knot/knot.h" #include "knot/server/server.h" @@ -54,6 +54,13 @@ char *zone = NULL; // Cleanup handler static int do_cleanup(server_t *server, char *configf, char *pidf); +// atexit() handler for server code +static void deinit(void) +{ + knot_crypto_cleanup(); + knot_crypto_cleanup_threads(); +} + // SIGINT signal handler void interrupt_handle(int s) { @@ -100,7 +107,7 @@ void help(void) int main(int argc, char **argv) { - atexit(knot_dnssec_cleanup); + atexit(deinit); // Parse command line arguments int c = 0, li = 0; @@ -167,6 +174,10 @@ int main(int argc, char **argv) return 1; } + // Initialize cryptographic backend + knot_crypto_init(); + knot_crypto_init_threads(); + // Now check if we want to daemonize if (daemonize) { if (daemon(1, 0) != 0) { diff --git a/src/knot/server/server.c b/src/knot/server/server.c index e433f25206a9a54ecf578e01774ca80d7ed28a0a..3d89002de55cfae5fefa839a579d5a9f67959a3b 100644 --- a/src/knot/server/server.c +++ b/src/knot/server/server.c @@ -20,7 +20,6 @@ #include <stdlib.h> #include <sys/stat.h> #include <errno.h> -#include <openssl/evp.h> #include <assert.h> #include "common/prng.h" @@ -35,7 +34,7 @@ #include "libknot/nameserver/name-server.h" #include "libknot/zone/zonedb.h" #include "libknot/dname.h" -#include "libknot/dnssec/cleanup.h" +#include "libknot/dnssec/crypto.h" /*! \brief Event scheduler loop. */ static int evsched_run(dthread_t *thread) @@ -77,7 +76,7 @@ static int evsched_run(dthread_t *thread) /*! \brief Event scheduler thread destructor. */ static int evsched_destruct(dthread_t *thread) { - knot_dnssec_thread_cleanup(); + knot_crypto_cleanup_thread(); return KNOT_EOK; } @@ -353,8 +352,6 @@ server_t *server_create() return NULL; } knot_ns_set_data(server->nameserver, server); - dbg_server("server: initializing OpenSSL\n"); - OpenSSL_add_all_digests(); // Create XFR handler server->xfr = xfr_create(XFR_THREADS_COUNT, server->nameserver); diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c index 8ec350a1bb4f32dcfe377d6a1b20273dd556b2bf..2390208fd8c68e2c4f2d0e7428e353ec18366de6 100644 --- a/src/knot/server/tcp-handler.c +++ b/src/knot/server/tcp-handler.c @@ -41,8 +41,8 @@ #include "knot/server/zones.h" #include "libknot/nameserver/name-server.h" #include "libknot/packet/wire.h" -#include "libknot/dnssec/cleanup.h" #include "libknot/nameserver/ns_proc_query.h" +#include "libknot/dnssec/crypto.h" /*! \brief TCP worker data. */ typedef struct tcp_worker_t { @@ -568,7 +568,7 @@ finish: int tcp_handler_destruct(dthread_t *thread) { - knot_dnssec_thread_cleanup(); + knot_crypto_cleanup_thread(); return KNOT_EOK; } diff --git a/src/knot/server/udp-handler.c b/src/knot/server/udp-handler.c index 8ab9b9ddccf5fbf365a85393ec5e6aab4b829dd2..d5a8a69a83cea3737b38df182962cef22a721a15 100644 --- a/src/knot/server/udp-handler.c +++ b/src/knot/server/udp-handler.c @@ -50,7 +50,7 @@ #include "libknot/packet/pkt.h" #include "knot/server/zones.h" #include "knot/server/notify.h" -#include "libknot/dnssec/cleanup.h" +#include "libknot/dnssec/crypto.h" /* Buffer identifiers. */ enum { @@ -604,6 +604,6 @@ int udp_master(dthread_t *thread) int udp_master_destruct(dthread_t *thread) { - knot_dnssec_thread_cleanup(); + knot_crypto_cleanup_thread(); return KNOT_EOK; } diff --git a/src/knot/server/zone-load.c b/src/knot/server/zone-load.c index 86be8e6bda294f8098da6ec2db05860b21eec859..e2bed82b7027518be448a3599ff8a8ff37562193 100644 --- a/src/knot/server/zone-load.c +++ b/src/knot/server/zone-load.c @@ -26,7 +26,7 @@ #include "knot/server/zones.h" #include "knot/zone/zone-load.h" #include "libknot/dname.h" -#include "libknot/dnssec/cleanup.h" +#include "libknot/dnssec/crypto.h" #include "libknot/nameserver/name-server.h" #include "libknot/rdata.h" #include "libknot/zone/zone.h" @@ -695,7 +695,7 @@ static int zone_loader_thread(dthread_t *thread) static int zone_loader_destruct(dthread_t *thread) { - knot_dnssec_thread_cleanup(); + knot_crypto_cleanup_thread(); return KNOT_EOK; } diff --git a/src/libknot/dnssec/crypto.c b/src/libknot/dnssec/crypto.c new file mode 100644 index 0000000000000000000000000000000000000000..b85171cdc4e3ef2bf9ed5d73c40b215c9fbcadc0 --- /dev/null +++ b/src/libknot/dnssec/crypto.c @@ -0,0 +1,147 @@ +/* Copyright (C) 2013 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + 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 3 of the License, or + (at your option) any later version. + + 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <openssl/crypto.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <pthread.h> + +#include "libknot/common.h" +#include "libknot/dnssec/crypto.h" + +/*! + * \brief Mutexes to be used by OpenSSL. + */ +static pthread_mutex_t *openssl_mutex = NULL; +static int openssl_mutex_count = 0; + +/*! + * \brief Callback for OpenSSL mutex locking and unlocking. + * + * \see CRYPTO_set_locking_callback() in OpenSSL documentation. + * + * \param mode Locking mode. + * \param n Mutex number. + * \param file Source file where locking occurs (for debugging). + * \param line Line number where locking occurs (for debugging). + */ +static void openssl_mutex_cb(int mode, int n, const char *file, int line) +{ + UNUSED(file); + UNUSED(line); + + assert(openssl_mutex); + assert(n < openssl_mutex_count); + + pthread_mutex_t *mutex = &openssl_mutex[n]; + + if (mode & CRYPTO_LOCK) { + pthread_mutex_lock(mutex); + } else { + pthread_mutex_unlock(mutex); + } +} + +/*! + * \brief Initialize mutexes for OpenSSL usage. + */ +static void openssl_mutexes_init(void) +{ + assert(openssl_mutex_count == 0); + assert(openssl_mutex == NULL); + + openssl_mutex_count = CRYPTO_num_locks(); + if (openssl_mutex_count == 0) { + return; + } + + openssl_mutex = calloc(openssl_mutex_count, sizeof(pthread_mutex_t)); + for (int i = 0; i < openssl_mutex_count; i++) { + pthread_mutex_init(&openssl_mutex[i], NULL); + } + + CRYPTO_set_locking_callback(openssl_mutex_cb); +} + +/*! + * \brief Destroy mutexes for OpenSSL usage. + */ +static void openssl_mutexes_destroy(void) +{ + assert(openssl_mutex); + + for (int i = 0; i < openssl_mutex_count; i++) { + pthread_mutex_destroy(&openssl_mutex[i]); + } + + free(openssl_mutex); + + openssl_mutex_count = 0; + openssl_mutex = NULL; +} + +/*! + * \brief Callback for thread identification for purpose of OpenSSL. + * + * \see CRYPTO_THREADID_set_callback() in OpenSSL documentation. + * + * \param openssl_id Thread identifier in OpenSSL. + */ +static void openssl_threadid_cb(CRYPTO_THREADID *openssl_id) +{ + pthread_t id = pthread_self(); + CRYPTO_THREADID_set_numeric(openssl_id, id); +} + +/*- public API --------------------------------------------------------------*/ + +void knot_crypto_init(void) +{ + OpenSSL_add_all_digests(); +} + +void knot_crypto_cleanup(void) +{ + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); + ERR_free_strings(); + + knot_crypto_cleanup_thread(); +} + +void knot_crypto_cleanup_thread(void) +{ + ERR_remove_state(0); +} + +void knot_crypto_init_threads(void) +{ + // locking + if (!openssl_mutex) { + openssl_mutexes_init(); + } + + // thread identification + CRYPTO_THREADID_set_callback(openssl_threadid_cb); +} + +void knot_crypto_cleanup_threads(void) +{ + if (openssl_mutex) { + openssl_mutexes_destroy(); + } +} diff --git a/src/libknot/dnssec/cleanup.h b/src/libknot/dnssec/crypto.h similarity index 51% rename from src/libknot/dnssec/cleanup.h rename to src/libknot/dnssec/crypto.h index 40d1948edf718aecb352cd1dd3f52567f46f5680..4273c5aeb59bbc70226d1fa5f275f6a2e0322ff4 100644 --- a/src/libknot/dnssec/cleanup.h +++ b/src/libknot/dnssec/crypto.h @@ -14,42 +14,46 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ /*! - * \file cleanup.h + * \file crypto.h * * \author Jan Vcelak <jan.vcelak@nic.cz> * - * \brief DNSSEC deinitialization + * \brief Cryptographic backend initialization and clean up. * * \addtogroup dnssec * @{ */ -#ifndef _KNOT_DNSSEC_CLEANUP_H_ -#define _KNOT_DNSSEC_CLEANUP_H_ +#ifndef _KNOT_DNSSEC_CRYPTO_H_ +#define _KNOT_DNSSEC_CRYPTO_H_ -#include <openssl/err.h> -#include <openssl/evp.h> +/*! + * \brief Initialize cryptographic backend. + */ +void knot_crypto_init(void); + +/*! + * \brief Clean up data allocated by cryptographic backend. + */ +void knot_crypto_cleanup(void); /*! - * \brief Deinitialize OpenSSL library thread specific data. + * \brief Clean up thread specific data allocated by cryptographic backend. */ -static inline void knot_dnssec_thread_cleanup(void) -{ - ERR_remove_state(0); -} +void knot_crypto_cleanup_thread(void); /*! - * \brief Deinitialize OpenSSL library. + * \brief Initialize data required for thread-safety of cryptographic backend. + * + * \note Does not include actions performed by knot_crypto_init(). */ -static inline void knot_dnssec_cleanup(void) -{ - EVP_cleanup(); - CRYPTO_cleanup_all_ex_data(); - ERR_free_strings(); +void knot_crypto_init_threads(void); - knot_dnssec_thread_cleanup(); -} +/*! + * \brief Clean up allocated data required for thread-safety of crypto backend. + */ +void knot_crypto_cleanup_threads(void); -#endif // _KNOT_DNSSEC_CLEANUP_H_ +#endif // _KNOT_DNSSEC_CRYPTO_H_ /*! @} */ diff --git a/src/utils/nsec3hash/nsec3hash_main.c b/src/utils/nsec3hash/nsec3hash_main.c index c164571124f0b18fac28f9d28a0cf68411fb8042..59249faabc8328665f36bac83f29543b66577226 100644 --- a/src/utils/nsec3hash/nsec3hash_main.c +++ b/src/utils/nsec3hash/nsec3hash_main.c @@ -24,7 +24,7 @@ #include "common/errcode.h" #include "common/hex.h" #include "common/strtonum.h" -#include "libknot/dnssec/cleanup.h" +#include "libknot/dnssec/crypto.h" #include "libknot/dnssec/nsec3.h" #define PROGRAM_NAME "knsec3hash" @@ -115,7 +115,7 @@ int main(int argc, char *argv[]) return 1; } - atexit(knot_dnssec_cleanup); + atexit(knot_crypto_cleanup); int exit_code = 1; knot_nsec3_params_t nsec3_params = { 0 }; diff --git a/src/utils/nsupdate/nsupdate_main.c b/src/utils/nsupdate/nsupdate_main.c index 174019fd957e8b5beb177c596bf2dd022f452bc3..7d8c74ee662eab04e9bf3a99d6898ca9565d1c96 100644 --- a/src/utils/nsupdate/nsupdate_main.c +++ b/src/utils/nsupdate/nsupdate_main.c @@ -20,11 +20,11 @@ #include "common/errcode.h" // KNOT_EOK #include "utils/nsupdate/nsupdate_params.h" // params_t #include "utils/nsupdate/nsupdate_exec.h" // host_exec -#include "libknot/dnssec/cleanup.h" // knot_dnssec_cleanup +#include "libknot/dnssec/crypto.h" // knot_crypto_cleanup int main(int argc, char *argv[]) { - atexit(knot_dnssec_cleanup); + atexit(knot_crypto_cleanup); int ret = EXIT_SUCCESS; diff --git a/tests-extra/functional/functests.py b/tests-extra/functional/functests.py index 898cc03b41a802b9eddb113868eba1ed6d1546dc..660575118977d2afa5dfc72b471d23dbfc39984e 100755 --- a/tests-extra/functional/functests.py +++ b/tests-extra/functional/functests.py @@ -57,7 +57,7 @@ def parse_args(cmd_args): test = parts[0] if test in storage: - storage[test].append(case) + storage[test].extend(case) else: storage[test] = case diff --git a/tests-extra/functional/tests/basic/nsec/test.py b/tests-extra/functional/tests/basic/nsec/test.py index c367da207fc6ef36cf383e79d0377a903d7e2a97..cf58dbf44e50fc8d91fe3e79deccc54f983d0937 100644 --- a/tests-extra/functional/tests/basic/nsec/test.py +++ b/tests-extra/functional/tests/basic/nsec/test.py @@ -18,34 +18,42 @@ t.start() # B1. Answer. resp = knot.dig("x.w.example", "MX", dnssec=True) +resp.check(rcode="NOERROR", flags="QR AA", eflags="DO") resp.cmp(bind) # B2. Name Error. -resp = knot.dig("mv.example", "A", dnssec=True) +resp = knot.dig("ml.example", "A", dnssec=True) +resp.check(rcode="NXDOMAIN", flags="QR AA", eflags="DO") resp.cmp(bind) # B3. No Data Error. resp = knot.dig("ns1.example", "MX", dnssec=True) +resp.check(rcode="NOERROR", flags="QR AA", eflags="DO") resp.cmp(bind) # B4. Referral to Signed Zone. resp = knot.dig("mc.a.example", "MX", dnssec=True) +resp.check(rcode="NOERROR", flags="QR", noflags="AA", eflags="DO") resp.cmp(bind) # B5. Referral to Unsigned Zone. resp = knot.dig("mc.b.example", "MX", dnssec=True) +resp.check(rcode="NOERROR", flags="QR", noflags="AA", eflags="DO") resp.cmp(bind) # B6. Wildcard Expansion. resp = knot.dig("a.z.w.example", "MX", dnssec=True) +resp.check(rcode="NOERROR", flags="QR AA", eflags="DO") resp.cmp(bind) # B7. Wildcard No Data Error. resp = knot.dig("a.z.w.example", "AAAA", dnssec=True) +resp.check(rcode="NOERROR", flags="QR AA", eflags="DO") resp.cmp(bind) # B8. DS Child Zone No Data Error. resp = knot.dig("example", "DS", dnssec=True) +resp.check(rcode="NOERROR", flags="QR AA", eflags="DO") resp.cmp(bind) t.end() diff --git a/tests-extra/functional/tests/basic/nsec3/test.py b/tests-extra/functional/tests/basic/nsec3/test.py index 1002f013916c2df16b26e64d91669bfceb62c8cd..5287a8ab28ee468a0203dd624370883e3a673596 100644 --- a/tests-extra/functional/tests/basic/nsec3/test.py +++ b/tests-extra/functional/tests/basic/nsec3/test.py @@ -18,34 +18,42 @@ t.start() # B1. Answer. resp = knot.dig("x.w.example", "MX", dnssec=True) +resp.check(rcode="NOERROR", flags="QR AA", eflags="DO") resp.cmp(bind) # B2. Name Error. -resp = knot.dig("mv.example", "A", dnssec=True) +resp = knot.dig("ml.example", "A", dnssec=True) +resp.check(rcode="NXDOMAIN", flags="QR AA", eflags="DO") resp.cmp(bind) # B3. No Data Error. resp = knot.dig("ns1.example", "MX", dnssec=True) +resp.check(rcode="NOERROR", flags="QR AA", eflags="DO") resp.cmp(bind) # B4. Referral to Signed Zone. resp = knot.dig("mc.a.example", "MX", dnssec=True) +resp.check(rcode="NOERROR", flags="QR", noflags="AA", eflags="DO") resp.cmp(bind) # B5. Referral to Unsigned Zone. resp = knot.dig("mc.b.example", "MX", dnssec=True) +resp.check(rcode="NOERROR", flags="QR", noflags="AA", eflags="DO") resp.cmp(bind) # B6. Wildcard Expansion. resp = knot.dig("a.z.w.example", "MX", dnssec=True) +resp.check(rcode="NOERROR", flags="QR AA", eflags="DO") resp.cmp(bind) # B7. Wildcard No Data Error. resp = knot.dig("a.z.w.example", "AAAA", dnssec=True) +resp.check(rcode="NOERROR", flags="QR AA", eflags="DO") resp.cmp(bind) # B8. DS Child Zone No Data Error. resp = knot.dig("example", "DS", dnssec=True) +resp.check(rcode="NOERROR", flags="QR AA", eflags="DO") resp.cmp(bind) t.end() diff --git a/tests-extra/functional/tools/dnstest/response.py b/tests-extra/functional/tools/dnstest/response.py index e4535916cb9858e53ce07e8156151a0c11a58319..8a059386c586afa73f7aad73b3827b12963fcda7 100644 --- a/tests-extra/functional/tools/dnstest/response.py +++ b/tests-extra/functional/tools/dnstest/response.py @@ -42,11 +42,23 @@ class Response(object): flag_val = dns.flags.from_text(flag) isset(not(self.resp.flags & flag_val), "no %s flag" % flag) + def _check_eflags(self, eflags, noeflags): + eflag_names = eflags.split() + for flag in eflag_names: + flag_val = dns.flags.edns_from_text(flag) + isset(self.resp.ednsflags & flag_val, "%s flag" % flag) + + eflag_names = noeflags.split() + for flag in eflag_names: + flag_val = dns.flags.edns_from_text(flag) + isset(not(self.resp.ednsflags & flag_val), "no %s flag" % flag) + def check(self, rdata=None, ttl=None, rcode="NOERROR", flags="", \ - noflags=""): + noflags="", eflags="", noeflags=""): '''Flags are text strings separated by whitespace character''' self._check_flags(flags, noflags) + self._check_eflags(eflags, noeflags) self._check_question() # Check rcode. diff --git a/tests/dnssec_sign.c b/tests/dnssec_sign.c index c043c86a9cff13b5ed693b49bca0143e950e1645..aade0d47224451e3ae70ca0bb1f5921c9a3a8fd8 100644 --- a/tests/dnssec_sign.c +++ b/tests/dnssec_sign.c @@ -20,8 +20,8 @@ #include <tap/basic.h> #include "common/errcode.h" -#include "libknot/dnssec/cleanup.h" #include "libknot/dnssec/config.h" +#include "libknot/dnssec/crypto.h" #include "libknot/dnssec/sign.h" #ifdef KNOT_ENABLE_ECDSA @@ -141,7 +141,7 @@ int main(int argc, char *argv[]) knot_free_key_params(&kp); } - knot_dnssec_cleanup(); + knot_crypto_cleanup(); return 0; }