From cd1c75c65eafaee083cb11e2231bf6136f6c7a4e Mon Sep 17 00:00:00 2001 From: Karel Slany <karel.slany@nic.cz> Date: Fri, 24 Jun 2016 11:40:45 +0200 Subject: [PATCH] Modification to meet latest libknot changes. --- daemon/worker.c | 9 +- lib/cookies/alg_containers.c | 4 +- lib/cookies/alg_sha.c | 197 +++++++++++++++++++++++ lib/cookies/alg_sha.h | 30 ++++ lib/cookies/cache.c | 48 +++--- lib/cookies/cache.h | 28 ++-- lib/cookies/control.c | 244 ----------------------------- lib/cookies/control.h | 58 +------ lib/cookies/helper.c | 285 ++++++++++++++++++++++++++++++++++ lib/cookies/helper.h | 84 ++++++++++ lib/cookies/nonce.c | 41 +++++ lib/cookies/nonce.h | 44 ++++++ lib/layer/cookiemonster.c | 51 +++--- lib/lib.mk | 18 ++- lib/resolve.c | 34 ++-- modules/cookiectl/cookiectl.c | 5 +- 16 files changed, 787 insertions(+), 393 deletions(-) create mode 100644 lib/cookies/alg_sha.c create mode 100644 lib/cookies/alg_sha.h create mode 100644 lib/cookies/helper.c create mode 100644 lib/cookies/helper.h create mode 100644 lib/cookies/nonce.c create mode 100644 lib/cookies/nonce.h diff --git a/daemon/worker.c b/daemon/worker.c index 75c851519..c98afd162 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -28,6 +28,7 @@ #if defined(ENABLE_COOKIES) #include <arpa/inet.h> /* inet_ntop() */ #include "lib/cookies/control.h" +#include "lib/cookies/helper.h" #endif /* defined(ENABLE_COOKIES) */ #include "lib/utils.h" #include "lib/layer.h" @@ -465,6 +466,8 @@ static bool subreq_update_cookies(uv_udp_t *handle, struct sockaddr *srvr_addr, return true; } + struct sockaddr_storage *sockaddr_ptr = NULL; /* Not supported yet. */ +#if 0 /* Libuv does not offer a convenient way how to obtain a source IP * address from a UDP handle that has been initialised using * uv_udp_init(). The uv_udp_getsockname() fails because of the lazy @@ -473,7 +476,6 @@ static bool subreq_update_cookies(uv_udp_t *handle, struct sockaddr *srvr_addr, * TODO -- A solution might be opening a separate socket and trying * to obtain the IP address from it. */ - struct sockaddr_storage sockaddr = {0, }; struct sockaddr_storage *sockaddr_ptr = &sockaddr; int sockaddr_len = sizeof(sockaddr); @@ -482,6 +484,7 @@ static bool subreq_update_cookies(uv_udp_t *handle, struct sockaddr *srvr_addr, if (ret != 0) { sockaddr_ptr = NULL; } +#endif /* 0 */ kr_request_put_cookie(&kr_glob_cookie_ctx.clnt.current, cookie_cache, (struct sockaddr*) sockaddr_ptr, srvr_addr, pkt); @@ -511,6 +514,10 @@ static int qr_task_send(struct qr_task *task, uv_handle_t *handle, struct sockad } if (handle->type == UV_UDP) { #if defined(ENABLE_COOKIES) + /* The actual server IP address is needed before generating the + * actual cookie. Also the resolver somehow mangles the query + * packets before building the query i.e. the space needed for + * the cookie cannot be allocated in the cookie layer. */ if (knot_wire_get_qr(pkt->wire) == 0) { /* Update DNS cookies data in query. */ subreq_update_cookies((uv_udp_t *) handle, addr, diff --git a/lib/cookies/alg_containers.c b/lib/cookies/alg_containers.c index 13d002d1f..1a5767b7a 100644 --- a/lib/cookies/alg_containers.c +++ b/lib/cookies/alg_containers.c @@ -49,9 +49,7 @@ const struct kr_cc_alg_descr *kr_cc_alg(const struct kr_cc_alg_descr cc_algs[], } const struct kr_sc_alg_descr kr_sc_algs[] = { - { "FNV-64-SIMPLE", &knot_sc_alg_fnv64_simple }, { "FNV-64", &knot_sc_alg_fnv64 }, - { "HMAC-SHA256-64-SIMPLE", &knot_sc_alg_hmac_sha256_64_simple }, { "HMAC-SHA256-64", &knot_sc_alg_hmac_sha256_64 }, { NULL, NULL } }; @@ -64,7 +62,7 @@ const struct kr_sc_alg_descr *kr_sc_alg(const struct kr_sc_alg_descr sc_algs[], } const struct kr_sc_alg_descr *aux_ptr = sc_algs; - while (aux_ptr && aux_ptr->alg && aux_ptr->alg->gen_func) { + while (aux_ptr && aux_ptr->alg && aux_ptr->alg->hash_func) { assert(aux_ptr->name); if (strcmp(aux_ptr->name, name) == 0) { return aux_ptr; diff --git a/lib/cookies/alg_sha.c b/lib/cookies/alg_sha.c new file mode 100644 index 000000000..c58073b76 --- /dev/null +++ b/lib/cookies/alg_sha.c @@ -0,0 +1,197 @@ +/* Copyright (C) 2016 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 <arpa/inet.h> /* htonl(), ... */ +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> +#include <openssl/hmac.h> +#include <openssl/sha.h> + +#include <libknot/errcode.h> +#include <libknot/rrtype/opt-cookie.h> + +#include "lib/cookies/alg_sha.h" +#include "lib/utils.h" + +/** + * Compute client cookie using HMAC_SHA256-64. + * @note At least one of the arguments must be non-null. + * @param input Input parameters. + * @param cc_out Buffer for computed client cookie. + * @param cc_len Size of buffer/written data. + * @return KNOT_EOK on success, error code else. + */ +static int cc_gen_hmac_sha256_64(const struct knot_cc_input *input, + uint8_t *cc_out, uint16_t *cc_len) +{ + if (!input || !cc_out || !cc_len) { + return KNOT_EINVAL; + } + + if ((!input->clnt_sockaddr && !input->srvr_sockaddr) || + !(input->secret_data && input->secret_len)) { + return KNOT_EINVAL; + } + + const uint8_t *addr = NULL; + int addr_len = 0; /* Address length. */ + + uint8_t digest[SHA256_DIGEST_LENGTH]; + unsigned int digest_len = SHA256_DIGEST_LENGTH; + + /* text: (client IP | server IP) + * key: client secret */ + + HMAC_CTX ctx; + HMAC_CTX_init(&ctx); + + int ret = HMAC_Init_ex(&ctx, input->secret_data, input->secret_len, + EVP_sha256(), NULL); + if (ret != 1) { + ret = KNOT_EINVAL; + goto fail; + } + + if (input->clnt_sockaddr) { + addr = (uint8_t *)kr_inaddr(input->clnt_sockaddr); + addr_len = kr_inaddr_len(input->clnt_sockaddr); + if (addr && addr_len > 0) { + ret = HMAC_Update(&ctx, addr, addr_len); + if (ret != 1) { + ret = KNOT_EINVAL; + goto fail; + } + } + } + + if (input->srvr_sockaddr) { + addr = (uint8_t *)kr_inaddr(input->srvr_sockaddr); + addr_len = kr_inaddr_len(input->srvr_sockaddr); + if (addr && addr_len > 0) { + ret = HMAC_Update(&ctx, addr, addr_len); + if (ret != 1) { + ret = KNOT_EINVAL; + goto fail; + } + } + } + + if (1 != HMAC_Final(&ctx, digest, &digest_len)) { + ret = KNOT_EINVAL; + goto fail; + } + + assert(KNOT_OPT_COOKIE_CLNT <= SHA256_DIGEST_LENGTH); + if (*cc_len < KNOT_OPT_COOKIE_CLNT) { + return KNOT_ESPACE; + } + + *cc_len = KNOT_OPT_COOKIE_CLNT; + memcpy(cc_out, digest, *cc_len); + ret = KNOT_EOK; + +fail: + HMAC_CTX_cleanup(&ctx); + return ret; +} + +#define SRVR_HMAC_SHA256_64_HASH_SIZE 8 + +/** + * @brief Compute server cookie using HMAC-SHA256-64). + * @note Server cookie = nonce | time | HMAC-SHA256-64( server secret, client cookie | nonce| time | client IP ) + * @param input data to compute cookie from + * @param hash_out hash cookie output buffer + * @param hash_len buffer size / written data size + * @return KNOT_EOK or error code. + */ +static int sc_gen_hmac_sha256_64(const struct knot_sc_input *input, + uint8_t *hash_out, uint16_t *hash_len) +{ + if (!input || !hash_out || + !hash_len || (*hash_len < SRVR_HMAC_SHA256_64_HASH_SIZE)) { + return KNOT_EINVAL; + } + + if (!input->cc || !input->cc_len || !input->srvr_data || + !input->srvr_data->secret_data || !input->srvr_data->secret_len) { + return KNOT_EINVAL; + } + + const uint8_t *addr = NULL; + size_t addr_len = 0; /* Address length. */ + + uint8_t digest[SHA256_DIGEST_LENGTH]; + unsigned int digest_len = SHA256_DIGEST_LENGTH; + + HMAC_CTX ctx; + HMAC_CTX_init(&ctx); + + int ret = HMAC_Init_ex(&ctx, input->srvr_data->secret_data, + input->srvr_data->secret_len, + EVP_sha256(), NULL); + if (ret != 1) { + ret = KNOT_EINVAL; + goto fail; + } + + ret = HMAC_Update(&ctx, input->cc, input->cc_len); + if (ret != 1) { + ret = KNOT_EINVAL; + goto fail; + } + + if (input->nonce && input->nonce_len) { + ret = HMAC_Update(&ctx, (void *)input->nonce, input->nonce_len); + if (ret != 1) { + ret = KNOT_EINVAL; + goto fail; + } + } + + if (input->srvr_data->clnt_sockaddr) { + addr = (uint8_t *)kr_inaddr(input->srvr_data->clnt_sockaddr); + addr_len = kr_inaddr_len(input->srvr_data->clnt_sockaddr); + if (addr && addr_len > 0) { + ret = HMAC_Update(&ctx, addr, addr_len); + if (ret != 1) { + ret = KNOT_EINVAL; + goto fail; + } + } + } + + if (1 != HMAC_Final(&ctx, digest, &digest_len)) { + ret = KNOT_EINVAL; + goto fail; + } + + assert(SRVR_HMAC_SHA256_64_HASH_SIZE <= SHA256_DIGEST_LENGTH); + + *hash_len = SRVR_HMAC_SHA256_64_HASH_SIZE; + memcpy(hash_out, digest, *hash_len); + + ret = KNOT_EOK; + +fail: + HMAC_CTX_cleanup(&ctx); + return ret; +} + +const struct knot_cc_alg knot_cc_alg_hmac_sha256_64 = { KNOT_OPT_COOKIE_CLNT, cc_gen_hmac_sha256_64 }; + +const struct knot_sc_alg knot_sc_alg_hmac_sha256_64 = { SRVR_HMAC_SHA256_64_HASH_SIZE, sc_gen_hmac_sha256_64 }; diff --git a/lib/cookies/alg_sha.h b/lib/cookies/alg_sha.h new file mode 100644 index 000000000..2d686aa4c --- /dev/null +++ b/lib/cookies/alg_sha.h @@ -0,0 +1,30 @@ +/* Copyright (C) 2016 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/>. + */ + +#pragma once + +#include <libknot/cookies/client.h> +#include <libknot/cookies/server.h> + +#include "lib/defines.h" + +/** FNV-64 client cookie algorithm. */ +KR_EXPORT +extern const struct knot_cc_alg knot_cc_alg_hmac_sha256_64; + +/** FNV-64 server cookie algorithm. */ +KR_EXPORT +extern const struct knot_sc_alg knot_sc_alg_hmac_sha256_64; diff --git a/lib/cookies/cache.c b/lib/cookies/cache.c index 1ec38c1ef..98fa31a79 100644 --- a/lib/cookies/cache.c +++ b/lib/cookies/cache.c @@ -25,6 +25,7 @@ #include "lib/cdb_lmdb.h" #include "lib/cookies/cache.h" #include "lib/cookies/control.h" +#include "lib/utils.h" /* Key size */ #define KEY_HSIZE (sizeof(uint8_t)) @@ -36,17 +37,16 @@ /** * @internal Composed key as { u8 tag, u8[4,16] IP address } */ -static size_t cache_key(uint8_t *buf, uint8_t tag, const void *sockaddr) +static size_t cache_key(uint8_t *buf, uint8_t tag, const struct sockaddr *sa) { - assert(buf && sockaddr); + assert(buf && sa); - const uint8_t *addr = NULL; - size_t addr_len = 0; + const char *addr = kr_inaddr(sa); + int addr_len = kr_inaddr_len(sa); - if (kr_ok() != knot_sockaddr_bytes(sockaddr, &addr, &addr_len)) { + if (!addr || (addr_len <= 0)) { return 0; } - assert(addr_len > 0); buf[0] = tag; memcpy(buf + sizeof(uint8_t), addr, addr_len); @@ -55,14 +55,14 @@ static size_t cache_key(uint8_t *buf, uint8_t tag, const void *sockaddr) } static struct kr_cache_entry *lookup(struct kr_cache *cache, uint8_t tag, - const void *sockaddr) + const struct sockaddr *sa) { - if (!cache || !sockaddr) { + if (!cache || !sa) { return NULL; } uint8_t keybuf[KEY_SIZE]; - size_t key_len = cache_key(keybuf, tag, sockaddr); + size_t key_len = cache_key(keybuf, tag, sa); /* Look up and return value */ knot_db_val_t key = { keybuf, key_len }; @@ -96,14 +96,14 @@ static int check_lifetime(struct kr_cache_entry *found, uint32_t *timestamp) } int kr_cookie_cache_peek(struct kr_cache *cache, uint8_t tag, - const void *sockaddr, struct kr_cache_entry **entry, + const struct sockaddr *sa, struct kr_cache_entry **entry, uint32_t *timestamp) { - if (!cache_isvalid(cache) || !sockaddr || !entry) { + if (!cache_isvalid(cache) || !sa || !entry) { return kr_error(EINVAL); } - struct kr_cache_entry *found = lookup(cache, tag, sockaddr); + struct kr_cache_entry *found = lookup(cache, tag, sa); if (!found) { cache->stats.miss += 1; return kr_error(ENOENT); @@ -130,16 +130,16 @@ static void entry_write(struct kr_cache_entry *dst, struct kr_cache_entry *heade } int kr_cookie_cache_insert(struct kr_cache *cache, - uint8_t tag, const void *sockaddr, + uint8_t tag, const struct sockaddr *sa, struct kr_cache_entry *header, knot_db_val_t data) { - if (!cache_isvalid(cache) || !sockaddr || !header) { + if (!cache_isvalid(cache) || !sa || !header) { return kr_error(EINVAL); } /* Insert key */ uint8_t keybuf[KEY_SIZE]; - size_t key_len = cache_key(keybuf, tag, sockaddr); + size_t key_len = cache_key(keybuf, tag, sa); if (key_len == 0) { return kr_error(EILSEQ); } @@ -169,14 +169,14 @@ int kr_cookie_cache_insert(struct kr_cache *cache, } int kr_cookie_cache_remove(struct kr_cache *cache, - uint8_t tag, const void *sockaddr) + uint8_t tag, const struct sockaddr *sa) { - if (!cache_isvalid(cache) || !sockaddr) { + if (!cache_isvalid(cache) || !sa) { return kr_error(EINVAL); } uint8_t keybuf[KEY_SIZE]; - size_t key_len = cache_key(keybuf, tag, sockaddr); + size_t key_len = cache_key(keybuf, tag, sa); if (key_len == 0) { return kr_error(EILSEQ); } @@ -185,16 +185,16 @@ int kr_cookie_cache_remove(struct kr_cache *cache, return cache_op(cache, remove, &key, 1); } -int kr_cookie_cache_peek_cookie(struct kr_cache *cache, const void *sockaddr, +int kr_cookie_cache_peek_cookie(struct kr_cache *cache, const struct sockaddr *sa, struct timed_cookie *cookie, uint32_t *timestamp) { - if (!cache_isvalid(cache) || !sockaddr || !cookie || !timestamp) { + if (!cache_isvalid(cache) || !sa || !cookie || !timestamp) { return kr_error(EINVAL); } /* Check if the RRSet is in the cache. */ struct kr_cache_entry *entry = NULL; - int ret = kr_cookie_cache_peek(cache, KR_CACHE_COOKIE, sockaddr, + int ret = kr_cookie_cache_peek(cache, KR_CACHE_COOKIE, sa, &entry, timestamp); if (ret != 0) { return ret; @@ -204,11 +204,11 @@ int kr_cookie_cache_peek_cookie(struct kr_cache *cache, const void *sockaddr, return kr_ok(); } -int kr_cookie_cache_insert_cookie(struct kr_cache *cache, const void *sockaddr, +int kr_cookie_cache_insert_cookie(struct kr_cache *cache, const struct sockaddr *sa, const struct timed_cookie *cookie, uint32_t timestamp) { - if (!cache_isvalid(cache) || !sockaddr) { + if (!cache_isvalid(cache) || !sa) { return kr_error(EINVAL); } @@ -230,6 +230,6 @@ int kr_cookie_cache_insert_cookie(struct kr_cache *cache, const void *sockaddr, knot_edns_opt_get_length(cookie->cookie_opt); knot_db_val_t data = { (uint8_t *) cookie->cookie_opt, cookie_opt_size }; - return kr_cookie_cache_insert(cache, KR_CACHE_COOKIE, sockaddr, + return kr_cookie_cache_insert(cache, KR_CACHE_COOKIE, sa, &header, data); } diff --git a/lib/cookies/cache.h b/lib/cookies/cache.h index afd83c406..bbe26ed47 100644 --- a/lib/cookies/cache.h +++ b/lib/cookies/cache.h @@ -16,6 +16,8 @@ #pragma once +#include <netinet/in.h> + #include "lib/cache.h" /** DNS cookie cache entry tag. */ @@ -26,40 +28,40 @@ * @note The 'drift' is the time passed between the inception time and now (in seconds). * @param cache cache structure * @param tag asset tag - * @param sockaddr asset socket address + * @param sa asset socket address * @param entry cache entry, will be set to valid pointer or NULL * @param timestamp current time (will be replaced with drift if successful) * @return 0 or an error code */ KR_EXPORT int kr_cookie_cache_peek(struct kr_cache *cache, - uint8_t tag, const void *sockaddr, + uint8_t tag, const struct sockaddr *sa, struct kr_cache_entry **entry, uint32_t *timestamp); /** * Insert asset into cache, replacing any existing data. * @param cache cache structure * @param tag asset tag - * @param sockaddr asset socket address + * @param sa asset socket address * @param header filled entry header (ttl and time stamp) * @param data inserted data * @return 0 or an error code */ KR_EXPORT int kr_cookie_cache_insert(struct kr_cache *cache, - uint8_t tag, const void *sockaddr, + uint8_t tag, const struct sockaddr *sa, struct kr_cache_entry *header, knot_db_val_t data); /** * Remove asset from cache. * @param cache cache structure * @param tag asset tag - * @param sockaddr asset socket address + * @param sa asset socket address * @return 0 or an error code */ KR_EXPORT int kr_cookie_cache_remove(struct kr_cache *cache, - uint8_t tag, const void *sockaddr); + uint8_t tag, const struct sockaddr *sa); /** * Structure used for cookie cache interface. @@ -74,33 +76,33 @@ struct timed_cookie { * Peek the cache for given cookie (socket address) * @note The 'drift' is the time passed between the cache time of the cookie and now (in seconds). * @param cache cache structure - * @param sockaddr socket address + * @param sa socket address * @param cookie asset * @param timestamp current time (will be replaced with drift if successful) * @return 0 or an error code */ KR_EXPORT -int kr_cookie_cache_peek_cookie(struct kr_cache *cache, const void *sockaddr, +int kr_cookie_cache_peek_cookie(struct kr_cache *cache, const struct sockaddr *sa, struct timed_cookie *cookie, uint32_t *timestamp); /** * Insert a DNS cookie (client and server) entry for the given server signature (IP address). * @param cache cache structure - * @param sockaddr server IP address + * @param sa server IP address * @param cookie ttl and whole EDNS cookie option (header, client and server cookies) * @param timestamp current time * @return 0 or an error code */ KR_EXPORT -int kr_cookie_cache_insert_cookie(struct kr_cache *cache, const void *sockaddr, +int kr_cookie_cache_insert_cookie(struct kr_cache *cache, const struct sockaddr *sa, const struct timed_cookie *cookie, uint32_t timestamp); /** * Remove asset from cache. * @param txn transaction instance - * @param sockaddr socket address + * @param sa socket address * @return 0 or an error code */ -#define kr_cookie_cache_remove_cookie(cache, sockaddr) \ - kr_cookie_cache_remove((cache), KR_CACHE_COOKIE, (sockaddr)) +#define kr_cookie_cache_remove_cookie(cache, sa) \ + kr_cookie_cache_remove((cache), KR_CACHE_COOKIE, (sa)) diff --git a/lib/cookies/control.c b/lib/cookies/control.c index c2f2c5524..f2ae2fca1 100644 --- a/lib/cookies/control.c +++ b/lib/cookies/control.c @@ -14,253 +14,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -//#define MODULE_DEBUG_MSGS 1 /* Comment out if debug messages are not desired. */ - -#include <assert.h> -#include <libknot/error.h> -#include <stdint.h> -#include <string.h> - -#include "lib/cookies/cache.h" #include "lib/cookies/control.h" -#include "lib/layer.h" -#include "lib/utils.h" - -#if defined(MODULE_DEBUG_MSGS) -# define DEBUG_MSG(qry, fmt...) QRDEBUG(qry, "cookies_control", fmt) -#else /* !defined(MODULE_DEBUG_MSGS) */ -# define DEBUG_MSG(qry, fmt...) do { } while (0) -#endif /* defined(MODULE_DEBUG_MSGS) */ struct kr_cookie_ctx kr_glob_cookie_ctx = { .clnt = { false, { NULL, NULL }, { NULL, NULL}, DFLT_COOKIE_TTL }, .srvr = { false, { NULL, NULL }, { NULL, NULL} } }; - -static int opt_rr_add_cookies(knot_rrset_t *opt_rr, - uint8_t *cc, uint16_t cc_len, - uint8_t *sc, uint16_t sc_len, - knot_mm_t *mm) -{ - uint16_t cookies_size = 0; - uint8_t *cookies_data = NULL; - - cookies_size = knot_edns_opt_cookie_data_len(cc_len, sc_len); - - int ret = knot_edns_reserve_option(opt_rr, KNOT_EDNS_OPTION_COOKIE, - cookies_size, &cookies_data, mm); - if (ret != KNOT_EOK) { - return ret; - } - assert(cookies_data != NULL); - - ret = knot_edns_opt_cookie_write(cc, cc_len, sc, sc_len, - cookies_data, &cookies_size); - if (ret != KNOT_EOK) { - return ret; - } - - assert(cookies_size == knot_edns_opt_cookie_data_len(cc_len, sc_len)); - - return KNOT_EOK; -} - -static int opt_rr_add_option(knot_rrset_t *opt_rr, uint8_t *option, - knot_mm_t *mm) -{ - assert(opt_rr && option); - - uint8_t *reserved_data = NULL; - uint16_t opt_code = knot_edns_opt_get_code(option); - uint16_t opt_len = knot_edns_opt_get_length(option); - uint8_t *opt_data = knot_edns_opt_get_data(option); - - int ret = knot_edns_reserve_option(opt_rr, opt_code, - opt_len, &reserved_data, mm); - if (ret != KNOT_EOK) { - return ret; - } - assert(reserved_data); - - memcpy(reserved_data, opt_data, opt_len); - return KNOT_EOK; -} - -/** - * Check whether there is a cached cookie that matches the current client - * cookie. - */ -static const uint8_t *peek_and_check_cc(struct kr_cache *cache, - const void *sockaddr, - const uint8_t *cc, uint16_t cc_len) -{ - assert(cache && sockaddr && cc && cc_len); - - uint32_t timestamp = 0; - struct timed_cookie timed_cookie = { 0, }; - - int ret = kr_cookie_cache_peek_cookie(cache, sockaddr, &timed_cookie, - ×tamp); - if (ret != kr_ok()) { - return NULL; - } - assert(timed_cookie.cookie_opt); - - /* Ignore the timestamp and time to leave. If the cookie is in cache - * then just use it. The cookie control should be prerformed in the - * cookie module/layer. */ - - const uint8_t *cached_cc = knot_edns_opt_get_data((uint8_t *) timed_cookie.cookie_opt); - - if (cc_len == KNOT_OPT_COOKIE_CLNT && - 0 == memcmp(cc, cached_cc, cc_len)) { - return timed_cookie.cookie_opt; - } - - return NULL; -} - -int kr_request_put_cookie(const struct kr_clnt_cookie_settings *clnt_cntrl, - struct kr_cache *cookie_cache, - const void *clnt_sockaddr, const void *srvr_sockaddr, - knot_pkt_t *pkt) -{ - if (!clnt_cntrl || !pkt) { - return kr_error(EINVAL); - } - - if (!pkt->opt_rr) { - return kr_ok(); - } - - if (!clnt_cntrl->csec || !clnt_cntrl->calg || - !cookie_cache) { - return kr_error(EINVAL); - } - - /* Generate client cookie. - * TODO -- generate client cookie from client address, server address - * and secret quantity. */ - struct knot_ccookie_input input = { - .clnt_sockaddr = clnt_sockaddr, - .srvr_sockaddr = srvr_sockaddr, - .secret_data = clnt_cntrl->csec->data, - .secret_len = clnt_cntrl->csec->size - }; - uint8_t cc[KNOT_OPT_COOKIE_CLNT]; - uint16_t cc_len = KNOT_OPT_COOKIE_CLNT; - assert(clnt_cntrl->calg && clnt_cntrl->calg->alg && - clnt_cntrl->calg->alg->gen_func); - int ret = clnt_cntrl->calg->alg->gen_func(&input, cc, &cc_len); - if (ret != kr_ok()) { - return ret; - } - assert(cc_len == KNOT_OPT_COOKIE_CLNT); - - const uint8_t *cached_cookie = peek_and_check_cc(cookie_cache, - srvr_sockaddr, - cc, cc_len); - - /* This is a very nasty hack that prevents the packet to be corrupted - * when using contemporary 'Cookie interface'. */ - assert(pkt->current == KNOT_ADDITIONAL); - pkt->sections[KNOT_ADDITIONAL].count -= 1; - pkt->rrset_count -= 1; - pkt->size -= knot_edns_wire_size(pkt->opt_rr); - knot_wire_set_arcount(pkt->wire, knot_wire_get_arcount(pkt->wire) - 1); -#if 0 - /* Reclaim reserved size -- does not work as intended.. */ - ret = knot_pkt_reclaim(pkt, knot_edns_wire_size(pkt->opt_rr)); - if (ret != KNOT_EOK) { - return ret; - } -#endif - - if (cached_cookie) { - ret = opt_rr_add_option(pkt->opt_rr, (uint8_t *) cached_cookie, - &pkt->mm); - } else { - ret = opt_rr_add_cookies(pkt->opt_rr, cc, cc_len, - NULL, 0, &pkt->mm); - } - - /* Write to packet. */ - assert(pkt->current == KNOT_ADDITIONAL); - return knot_pkt_put(pkt, KNOT_COMPR_HINT_NONE, pkt->opt_rr, KNOT_PF_FREE); -} - -int kr_answer_opt_rr_add_cookies(const struct knot_scookie_input *input, - const struct kr_sc_alg_descr *sc_alg, - knot_pkt_t *pkt) -{ - if (!input || !sc_alg || !sc_alg->alg || pkt) { - kr_error(EINVAL); - } - - uint16_t cookie_size = input->cc_len + sc_alg->alg->sc_size; - uint8_t *data = NULL; - - if (!pkt->opt_rr) { - kr_error(EINVAL); - } - int ret = knot_edns_reserve_option(pkt->opt_rr, - KNOT_EDNS_OPTION_COOKIE, - cookie_size, &data, &pkt->mm); - if (ret != KNOT_EOK) { - return kr_error(ret); - } - - memcpy(data, input->cc, input->cc_len); - cookie_size = sc_alg->alg->sc_size; - ret = sc_alg->alg->gen_func(input, data + input->cc_len, &cookie_size); - if (ret != kr_ok()) { - /* TODO -- Delete COOKIE option. */ - return ret; - } - - return ret; -} - -int kr_pkt_set_ext_rcode(knot_pkt_t *pkt, uint16_t whole_rcode) -{ - if (!pkt || !knot_pkt_has_edns(pkt)) { - return kr_error(EINVAL); - } - - uint8_t rcode = whole_rcode & 0x0f; - uint8_t ext_rcode = whole_rcode >> 4; - knot_wire_set_rcode(pkt->wire, rcode); - knot_edns_set_ext_rcode(pkt->opt_rr, ext_rcode); - - return kr_ok(); -} - -uint8_t *kr_is_cookie_query(const knot_pkt_t *pkt) -{ - if (!pkt || knot_wire_get_qdcount(pkt->wire) > 0) { - return false; - } - - if (knot_wire_get_qr(pkt->wire) != 0 || !pkt->opt_rr) { - return false; - } - - return knot_edns_get_option(pkt->opt_rr, KNOT_EDNS_OPTION_COOKIE); -} - -int kr_parse_cookie_opt(uint8_t *cookie_opt, struct knot_dns_cookies *cookies) -{ - if (!cookie_opt || !cookies) { - kr_error(EINVAL); - } - - const uint8_t *cookie_data = knot_edns_opt_get_data(cookie_opt); - uint16_t cookie_len = knot_edns_opt_get_length(cookie_opt); - assert(cookie_data && cookie_len); - - int ret = knot_edns_opt_cookie_parse(cookie_data, cookie_len, - &cookies->cc, &cookies->cc_len, - &cookies->sc, &cookies->sc_len); - - return (ret == KNOT_EOK) ? kr_ok() : kr_error(EINVAL); -} diff --git a/lib/cookies/control.h b/lib/cookies/control.h index e3bf44738..c2842d81c 100644 --- a/lib/cookies/control.h +++ b/lib/cookies/control.h @@ -16,9 +16,10 @@ #pragma once -#include <libknot/packet/pkt.h> #include <libknot/rrtype/opt-cookie.h> #include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> #include "lib/cookies/alg_containers.h" #include "lib/cache.h" @@ -72,58 +73,3 @@ struct kr_cookie_ctx { /** Global cookie control context. */ KR_EXPORT extern struct kr_cookie_ctx kr_glob_cookie_ctx; - -/** - * @brief Insert a DNS cookie into query packet. - * @note The packet must already contain ENDS section. - * @param clnt_cntrl Client cookie control structure. - * @param cookie_cache Cookie cache. - * @param clnt_sockaddr Client address. - * @param srvr_sockaddr Server address. - * @param pkt DNS request packet. - */ -KR_EXPORT -int kr_request_put_cookie(const struct kr_clnt_cookie_settings *clnt_cntrl, - struct kr_cache *cookie_cache, - const void *clnt_sockaddr, const void *srvr_sockaddr, - knot_pkt_t *pkt); - -/** - * @brief Add cookies into answer. - * @note Data are only added into the OPT RR. - * @param input input data to generate server cookie from - * @param sc_alg algorithm to use - * @param pkt packet which to put cookie into - * @return kr_ok() or error code - */ -KR_EXPORT -int kr_answer_opt_rr_add_cookies(const struct knot_scookie_input *input, - const struct kr_sc_alg_descr *sc_alg, - knot_pkt_t *pkt); - -/** - * @brief Set RCODE and extended RCODE. - * @param pkt DNS packet - * @param whole_rcode RCODE value - * @return kr_ok() or error code - */ -KR_EXPORT -int kr_pkt_set_ext_rcode(knot_pkt_t *pkt, uint16_t whole_rcode); - -/** - * @brief Check whether packet is a server cookie request. - * @param pkt Packet to be examined. - * @param cookies Received cookies. - * @return Pointer to entire cookie option if is a cookie query, NULL else. - */ -KR_EXPORT -uint8_t *kr_is_cookie_query(const knot_pkt_t *pkt); - -/** - * @brief Parse cookies from cookie option. - * @param cookie_opt Cookie option. - * @param cookies Cookie structure to be set. - * @return kr_ok() on success, error if cookies are malformed. - */ -KR_EXPORT -int kr_parse_cookie_opt(uint8_t *cookie_opt, struct knot_dns_cookies *cookies); diff --git a/lib/cookies/helper.c b/lib/cookies/helper.c new file mode 100644 index 000000000..5d4c3b869 --- /dev/null +++ b/lib/cookies/helper.c @@ -0,0 +1,285 @@ +/* Copyright (C) 2016 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 <libknot/rrtype/opt-cookie.h> + +#include "lib/cookies/cache.h" +#include "lib/cookies/helper.h" +#include "lib/defines.h" + +/** + * @brief Check whether there is a cached cookie that matches the current + * client cookie. + */ +static const uint8_t *peek_and_check_cc(struct kr_cache *cache, + const void *sockaddr, + const uint8_t *cc, uint16_t cc_len) +{ + assert(cache && sockaddr && cc && cc_len); + + uint32_t timestamp = 0; + struct timed_cookie timed_cookie = { 0, }; + + int ret = kr_cookie_cache_peek_cookie(cache, sockaddr, &timed_cookie, + ×tamp); + if (ret != kr_ok()) { + return NULL; + } + assert(timed_cookie.cookie_opt); + + /* Ignore the time stamp and time to leave. If the cookie is in cache + * then just use it. The cookie control should be performed in the + * cookie module/layer. */ + + const uint8_t *cached_cc = knot_edns_opt_get_data((uint8_t *) timed_cookie.cookie_opt); + + if (cc_len == KNOT_OPT_COOKIE_CLNT && + 0 == memcmp(cc, cached_cc, cc_len)) { + return timed_cookie.cookie_opt; + } + + return NULL; +} + +/** + * @brief Adds entire EDNS option into the RR Set. + */ +static int opt_rr_add_opt(knot_rrset_t *opt_rr, uint8_t *option, knot_mm_t *mm) +{ + assert(opt_rr && option); + + uint8_t *reserved_data = NULL; + uint16_t opt_code = knot_edns_opt_get_code(option); + uint16_t opt_len = knot_edns_opt_get_length(option); + uint8_t *opt_data = knot_edns_opt_get_data(option); + + int ret = knot_edns_reserve_option(opt_rr, opt_code, + opt_len, &reserved_data, mm); + if (ret != KNOT_EOK) { + return ret; + } + assert(reserved_data); + + memcpy(reserved_data, opt_data, opt_len); + return KNOT_EOK; +} + +/** + * @brief Add a client cookie option into the RR Set. + */ +static int opt_rr_add_cc(knot_rrset_t *opt_rr, uint8_t *cc, uint16_t cc_len, + knot_mm_t *mm) +{ +#define SC NULL +#define SC_LEN 0 + uint16_t cookies_size = 0; + uint8_t *cookies_data = NULL; + + cookies_size = knot_edns_opt_cookie_data_len(cc_len, SC_LEN); + + int ret = knot_edns_reserve_option(opt_rr, KNOT_EDNS_OPTION_COOKIE, + cookies_size, &cookies_data, mm); + if (ret != KNOT_EOK) { + return ret; + } + assert(cookies_data != NULL); + + ret = knot_edns_opt_cookie_write(cc, cc_len, SC, SC_LEN, + cookies_data, &cookies_size); + if (ret != KNOT_EOK) { + return ret; + } + + assert(cookies_size == knot_edns_opt_cookie_data_len(cc_len, SC_LEN)); + + return KNOT_EOK; +#undef SC +#undef SC_LEN +} + +int kr_request_put_cookie(const struct kr_clnt_cookie_settings *clnt_cntrl, + struct kr_cache *cookie_cache, + const struct sockaddr *clnt_sa, + const struct sockaddr *srvr_sa, + knot_pkt_t *pkt) +{ + if (!clnt_cntrl || !pkt) { + return kr_error(EINVAL); + } + + if (!pkt->opt_rr) { + return kr_ok(); + } + + if (!clnt_cntrl->csec || !clnt_cntrl->calg || + !cookie_cache) { + return kr_error(EINVAL); + } + + /* Generate client cookie. + * TODO -- generate client cookie from client address, server address + * and secret quantity. */ + struct knot_cc_input input = { + .clnt_sockaddr = clnt_sa, + .srvr_sockaddr = srvr_sa, + .secret_data = clnt_cntrl->csec->data, + .secret_len = clnt_cntrl->csec->size + }; + uint8_t cc[KNOT_OPT_COOKIE_CLNT]; + uint16_t cc_len = KNOT_OPT_COOKIE_CLNT; + assert(clnt_cntrl->calg && clnt_cntrl->calg->alg && + clnt_cntrl->calg->alg->gen_func); + int ret = clnt_cntrl->calg->alg->gen_func(&input, cc, &cc_len); + if (ret != kr_ok()) { + return ret; + } + assert(cc_len == KNOT_OPT_COOKIE_CLNT); + + const uint8_t *cached_cookie = peek_and_check_cc(cookie_cache, + srvr_sa, cc, cc_len); + + /* This is a very nasty hack that prevents the packet to be corrupted + * when using contemporary 'Cookie interface'. */ + assert(pkt->current == KNOT_ADDITIONAL); + pkt->sections[KNOT_ADDITIONAL].count -= 1; + pkt->rrset_count -= 1; + pkt->size -= knot_edns_wire_size(pkt->opt_rr); + knot_wire_set_arcount(pkt->wire, knot_wire_get_arcount(pkt->wire) - 1); +#if 0 + /* Reclaim reserved size -- does not work as intended.. */ + ret = knot_pkt_reclaim(pkt, knot_edns_wire_size(pkt->opt_rr)); + if (ret != KNOT_EOK) { + return ret; + } +#endif + + if (cached_cookie) { + ret = opt_rr_add_opt(pkt->opt_rr, (uint8_t *)cached_cookie, + &pkt->mm); + } else { + ret = opt_rr_add_cc(pkt->opt_rr, cc, cc_len, &pkt->mm); + } + + /* Write to packet. */ + assert(pkt->current == KNOT_ADDITIONAL); + return knot_pkt_put(pkt, KNOT_COMPR_HINT_NONE, pkt->opt_rr, KNOT_PF_FREE); +} + +int kr_answer_write_cookie(const struct knot_sc_private *srvr_data, + const uint8_t *cc, uint16_t cc_len, + struct kr_nonce_input *nonce, + const struct knot_sc_alg *alg, + knot_pkt_t *pkt) +{ + if (!srvr_data || !srvr_data->clnt_sockaddr || + !srvr_data->secret_data|| !srvr_data->secret_len) { + return kr_error(EINVAL); + } + + if (!cc || !cc_len || !nonce) { + return kr_error(EINVAL); + } + + if (!alg || !alg->hash_size || !alg->hash_func) { + return kr_error(EINVAL); + } + + if (!pkt && !pkt->opt_rr) { + return kr_error(EINVAL); + } + + uint16_t nonce_len = NONCE_LEN; + uint16_t hash_len = alg->hash_size; + + uint8_t *cookie = NULL; + uint16_t cookie_len = knot_edns_opt_cookie_data_len(cc_len, + nonce_len + hash_len); + if (cookie_len == 0) { + return kr_error(EINVAL); + } + + int ret = knot_edns_reserve_option(pkt->opt_rr, KNOT_EDNS_OPTION_COOKIE, + cookie_len, &cookie, &pkt->mm); + if (ret != KNOT_EOK) { + return kr_error(ENOMEM); + } + + struct knot_sc_input input = { + .cc = cookie, + .cc_len = cc_len, + .srvr_data = srvr_data + }; + memcpy(cookie, cc, cc_len); + + if (nonce_len) { + kr_nonce_write_wire(cookie + cc_len, &nonce_len, nonce); + + input.nonce = cookie + cc_len; + input.nonce_len = nonce_len; + } + + ret = alg->hash_func(&input, cookie + cc_len + nonce_len, &hash_len); + if (ret != KNOT_EOK) { + return kr_error(EINVAL); + } + + return kr_ok(); +} + +int kr_pkt_set_ext_rcode(knot_pkt_t *pkt, uint16_t whole_rcode) +{ + if (!pkt || !knot_pkt_has_edns(pkt)) { + return kr_error(EINVAL); + } + + uint8_t rcode = whole_rcode & 0x0f; + uint8_t ext_rcode = whole_rcode >> 4; + knot_wire_set_rcode(pkt->wire, rcode); + knot_edns_set_ext_rcode(pkt->opt_rr, ext_rcode); + + return kr_ok(); +} + +uint8_t *kr_is_cookie_query(const knot_pkt_t *pkt) +{ + if (!pkt || knot_wire_get_qdcount(pkt->wire) > 0) { + return false; + } + + if (knot_wire_get_qr(pkt->wire) != 0 || !pkt->opt_rr) { + return false; + } + + return knot_edns_get_option(pkt->opt_rr, KNOT_EDNS_OPTION_COOKIE); +} + +int kr_parse_cookie_opt(uint8_t *cookie_opt, struct knot_dns_cookies *cookies) +{ + if (!cookie_opt || !cookies) { + kr_error(EINVAL); + } + + const uint8_t *cookie_data = knot_edns_opt_get_data(cookie_opt); + uint16_t cookie_len = knot_edns_opt_get_length(cookie_opt); + assert(cookie_data && cookie_len); + + int ret = knot_edns_opt_cookie_parse(cookie_data, cookie_len, + &cookies->cc, &cookies->cc_len, + &cookies->sc, &cookies->sc_len); + + return (ret == KNOT_EOK) ? kr_ok() : kr_error(EINVAL); +} diff --git a/lib/cookies/helper.h b/lib/cookies/helper.h new file mode 100644 index 000000000..ec4d9fc31 --- /dev/null +++ b/lib/cookies/helper.h @@ -0,0 +1,84 @@ +/* Copyright (C) 2016 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/>. + */ + +#pragma once + +#include <libknot/packet/pkt.h> + +#include "lib/cookies/control.h" +#include "lib/cookies/nonce.h" +#include "lib/defines.h" + +/** + * @brief Insert a DNS cookie into query packet. + * @note The packet must already contain ENDS section. + * @param clnt_cntrl client cookie control structure + * @param cookie_cache cookie cache + * @param clnt_sa client socket address + * @param srvr_sa server socket address + * @param pkt DNS request packet + * @return kr_ok() or error code + */ +KR_EXPORT +int kr_request_put_cookie(const struct kr_clnt_cookie_settings *clnt_cntrl, + struct kr_cache *cookie_cache, + const struct sockaddr *clnt_sa, + const struct sockaddr *srvr_sa, + knot_pkt_t *pkt); + +/** + * @brief Inserts a cookie option into the OPT RR. It does not write any + * wire data. + * @param srvr_data server knowledge + * @param cc client cookie + * @param cc_len client cookie length + * @param nonce nonce value + * @param alg hash algorithm + * @param pkt DNS response packet + */ +KR_EXPORT +int kr_answer_write_cookie(const struct knot_sc_private *srvr_data, + const uint8_t *cc, uint16_t cc_len, + struct kr_nonce_input *nonce, + const struct knot_sc_alg *alg, + knot_pkt_t *pkt); + +/** + * @brief Set RCODE and extended RCODE. + * @param pkt DNS packet + * @param whole_rcode RCODE value + * @return kr_ok() or error code + */ +KR_EXPORT +int kr_pkt_set_ext_rcode(knot_pkt_t *pkt, uint16_t whole_rcode); + +/** + * @brief Check whether packet is a server cookie request. + * @param pkt Packet to be examined. + * @param cookies Received cookies. + * @return Pointer to entire cookie option if is a cookie query, NULL else. + */ +KR_EXPORT +uint8_t *kr_is_cookie_query(const knot_pkt_t *pkt); + +/** + * @brief Parse cookies from cookie option. + * @param cookie_opt Cookie option. + * @param cookies Cookie structure to be set. + * @return kr_ok() on success, error if cookies are malformed. + */ +KR_EXPORT +int kr_parse_cookie_opt(uint8_t *cookie_opt, struct knot_dns_cookies *cookies); diff --git a/lib/cookies/nonce.c b/lib/cookies/nonce.c new file mode 100644 index 000000000..486e35dd9 --- /dev/null +++ b/lib/cookies/nonce.c @@ -0,0 +1,41 @@ +/* Copyright (C) 2016 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 <arpa/inet.h> +#include <assert.h> + +#include "lib/cookies/nonce.h" + +int kr_nonce_write_wire(uint8_t *buf, uint16_t *buf_len, + struct kr_nonce_input *input) +{ + if (!buf || !buf_len || !input) {; + return kr_error(EINVAL); + } + + if (*buf_len < NONCE_LEN) { + kr_error(EINVAL); + } + + uint32_t aux = htonl(input->rand); + memcpy(buf, &aux, sizeof(aux)); + aux = htonl(input->time); + memcpy(buf + sizeof(aux), &aux, sizeof(aux)); + *buf_len = 2 * sizeof(aux); + assert(NONCE_LEN == *buf_len); + + return kr_ok(); +} diff --git a/lib/cookies/nonce.h b/lib/cookies/nonce.h new file mode 100644 index 000000000..a7311e2d7 --- /dev/null +++ b/lib/cookies/nonce.h @@ -0,0 +1,44 @@ +/* Copyright (C) 2016 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/>. + */ + +#pragma once + +#include "lib/defines.h" + +/* RFC7873 Appendix B.2 mentions an algorithm using two values before the + * actual server cookie hash. */ + +/** Nonce value length. */ +#define NONCE_LEN 8 + +/** Input data to generate nonce from. */ +struct kr_nonce_input { + uint32_t rand; /**< some random value */ + uint32_t time; /**< time stamp */ +}; + +/** + * @brief Writes server cookie nonce balue into given buffer. + * + * @param buf buffer to write nonce data in wire format into + * @param buf_len on input contains nonce buffer size, on output contains + * size of actual written data + * @param input data to generate wire data from + * @return kr_ok() on success, error code else + */ +KR_EXPORT +int kr_nonce_write_wire(uint8_t *buf, uint16_t *buf_len, + struct kr_nonce_input *input); diff --git a/lib/layer/cookiemonster.c b/lib/layer/cookiemonster.c index a46bcb0ba..5f1b98fb9 100644 --- a/lib/layer/cookiemonster.c +++ b/lib/layer/cookiemonster.c @@ -28,6 +28,8 @@ #include "lib/cookies/alg_containers.h" #include "lib/cookies/cache.h" #include "lib/cookies/control.h" +#include "lib/cookies/helper.h" +#include "lib/cookies/nonce.h" #include "lib/module.h" #include "lib/layer.h" @@ -72,8 +74,8 @@ static const struct sockaddr *guess_server_addr(const struct kr_nsrep *nsrep, const struct sockaddr *sockaddr = NULL; - struct knot_ccookie_input input = { - .clnt_sockaddr = NULL, + struct knot_cc_input input = { + .clnt_sockaddr = NULL, /* Not supported yet. */ .srvr_sockaddr = NULL, .secret_data = csecr->data, .secret_len = csecr->size @@ -85,10 +87,10 @@ static const struct sockaddr *guess_server_addr(const struct kr_nsrep *nsrep, break; } - input.srvr_sockaddr = &nsrep->addr[i]; - int ret = knot_ccookie_check(cc, cc_len, &input, cc_alg->alg); + input.srvr_sockaddr = (struct sockaddr *)&nsrep->addr[i]; + int ret = knot_cc_check(cc, cc_len, &input, cc_alg->alg); if (ret == KNOT_EOK) { - sockaddr = (struct sockaddr *) &nsrep->addr[i]; + sockaddr = (struct sockaddr *)&nsrep->addr[i]; break; } } @@ -118,21 +120,21 @@ static int srvr_sockaddr_cc_check(const struct sockaddr **sockaddr, if (tmp_sockaddr) { assert(clnt_cntrl->current.csec); - struct knot_ccookie_input input = { - .clnt_sockaddr = NULL, + struct knot_cc_input input = { + .clnt_sockaddr = NULL, /* Not supported yet. */ .srvr_sockaddr = tmp_sockaddr, .secret_data = clnt_cntrl->current.csec->data, .secret_len = clnt_cntrl->current.csec->size }; - int ret = knot_ccookie_check(cc, cc_len, &input, + int ret = knot_cc_check(cc, cc_len, &input, clnt_cntrl->current.calg->alg); bool have_current = (ret == KNOT_EOK); if ((ret != KNOT_EOK) && clnt_cntrl->recent.csec && clnt_cntrl->recent.calg) { input.secret_data = clnt_cntrl->recent.csec->data; input.secret_len = clnt_cntrl->recent.csec->size; - ret = knot_ccookie_check(cc, cc_len, &input, - clnt_cntrl->recent.calg->alg); + ret = knot_cc_check(cc, cc_len, &input, + clnt_cntrl->recent.calg->alg); } if (ret == KNOT_EOK) { *sockaddr = tmp_sockaddr; @@ -381,8 +383,6 @@ static inline uint8_t *req_cookie_option(struct kr_request *req) return knot_edns_get_option(req->qsource.opt, KNOT_EDNS_OPTION_COOKIE); } -/* TODO -- DNS cookie request. */ - static int check_request(knot_layer_t *ctx, void *module_param) { if (!kr_glob_cookie_ctx.srvr.enabled) { @@ -417,18 +417,15 @@ static int check_request(knot_layer_t *ctx, void *module_param) int return_state = ctx->state; - struct knot_scookie_check_ctx check_ctx = { + struct knot_sc_private srvr_data = { .clnt_sockaddr = req->qsource.addr, .secret_data = srvr_cntrl->current.ssec->data, .secret_len = srvr_cntrl->current.ssec->size }; - struct knot_scookie_input input = { - .cc = cookies.cc, - .cc_len = cookies.cc_len, - .nonce = kr_rand_uint(UINT32_MAX), - .time = req->current_query->timestamp.tv_sec, - .srvr_data = &check_ctx + struct kr_nonce_input nonce = { + .rand = kr_rand_uint(UINT32_MAX), + .time = req->current_query->timestamp.tv_sec }; if (!cookies.sc) { @@ -451,18 +448,18 @@ static int check_request(knot_layer_t *ctx, void *module_param) /* Check server cookie obtained in request. */ - ret = knot_scookie_check(&cookies, &check_ctx, - srvr_cntrl->current.salg->alg); + ret = knot_sc_check(NONCE_LEN, &cookies, &srvr_data, + srvr_cntrl->current.salg->alg); if (ret == KNOT_EINVAL && srvr_cntrl->recent.ssec && srvr_cntrl->recent.salg->alg) { /* Try recent algorithm. */ - struct knot_scookie_check_ctx recent_ctx = { + struct knot_sc_private recent_srvr_data = { .clnt_sockaddr = req->qsource.addr, .secret_data = srvr_cntrl->recent.ssec->data, .secret_len = srvr_cntrl->recent.ssec->size }; - ret = knot_scookie_check(&cookies, &recent_ctx, - srvr_cntrl->recent.salg->alg); + ret = knot_sc_check(NONCE_LEN, &cookies, &recent_srvr_data, + srvr_cntrl->recent.salg->alg); } if (ret != KNOT_EOK) { /* TODO -- Silently discard? */ @@ -486,8 +483,12 @@ static int check_request(knot_layer_t *ctx, void *module_param) answer_add_cookies: /* Add server cookie into response. */ - kr_answer_opt_rr_add_cookies(&input, srvr_cntrl->current.salg, + ret = kr_answer_write_cookie(&srvr_data, cookies.cc, cookies.cc_len, + &nonce, srvr_cntrl->current.salg->alg, req->answer); + if (ret != kr_ok()) { + return_state = KNOT_STATE_FAIL; + } return return_state; } diff --git a/lib/lib.mk b/lib/lib.mk index 17b845670..4eccea64c 100644 --- a/lib/lib.mk +++ b/lib/lib.mk @@ -48,15 +48,19 @@ ifeq ($(HAS_libcrypto),yes) libkres_SOURCES += \ lib/layer/cookiemonster.c \ lib/cookies/alg_containers.c \ - lib/cookies/alg_sha.c \ - lib/cookies/cache.c \ - lib/cookies/control.c + lib/cookies/alg_sha.c \ + lib/cookies/cache.c \ + lib/cookies/control.c \ + lib/cookies/helper.c \ + lib/cookies/nonce.c libkres_HEADERS += \ - lib/cookies/alg_containers..h \ - lib/cookies/alg_sha.h \ - lib/cookies/cache.h \ - lib/cookies/control.h + lib/cookies/alg_containers.h \ + lib/cookies/alg_sha.h \ + lib/cookies/cache.h \ + lib/cookies/control.h \ + lib/cookies/helper.h \ + lib/cookies/nonce.h libkres_LIBS += $(libcrypto_LIBS) endif diff --git a/lib/resolve.c b/lib/resolve.c index e9e2728d8..cc93f2b2a 100644 --- a/lib/resolve.c +++ b/lib/resolve.c @@ -29,6 +29,8 @@ #include "lib/dnssec/ta.h" #if defined(ENABLE_COOKIES) #include "lib/cookies/control.h" +#include "lib/cookies/helper.h" +#include "lib/cookies/nonce.h" #endif /* defined(ENABLE_COOKIES) */ #define DEBUG_MSG(qry, fmt...) QRDEBUG((qry), "resl", fmt) @@ -410,7 +412,7 @@ static int cookie_answer(const void *clnt_sockaddr, knot_wire_set_ra(answer->wire); knot_wire_set_rcode(answer->wire, KNOT_RCODE_NOERROR); - struct knot_scookie_check_ctx check_ctx = { + struct knot_sc_private srvr_data = { .clnt_sockaddr = clnt_sockaddr, .secret_data = srvr_cntrl->current.ssec->data, .secret_len = srvr_cntrl->current.ssec->size @@ -419,28 +421,22 @@ static int cookie_answer(const void *clnt_sockaddr, struct timeval tv; gettimeofday(&tv, NULL); - struct knot_scookie_input input = { - .cc = cookies->cc, - .cc_len = cookies->cc_len, - .nonce = kr_rand_uint(UINT32_MAX), - .time = tv.tv_sec, - .srvr_data = &check_ctx + struct kr_nonce_input nonce = { + .rand = kr_rand_uint(UINT32_MAX), + .time = tv.tv_sec }; - int ret = kr_answer_opt_rr_add_cookies(&input, - srvr_cntrl->current.salg, - answer); + /* Add fres cookie into the answer. */ + int ret = kr_answer_write_cookie(&srvr_data, + cookies->cc, cookies->cc_len, &nonce, + srvr_cntrl->current.salg->alg, answer); if (ret != kr_ok()) { return KNOT_STATE_FAIL; } - if (!cookies->sc) { - return KNOT_STATE_DONE; - } - /* Check server cookie only with current settings. */ - ret = knot_scookie_check(cookies, &check_ctx, - srvr_cntrl->current.salg->alg); + ret = knot_sc_check(NONCE_LEN, cookies, &srvr_data, + srvr_cntrl->current.salg->alg); if (ret != KNOT_EOK) { kr_pkt_set_ext_rcode(answer, KNOT_RCODE_BADCOOKIE); return KNOT_STATE_FAIL | KNOT_STATE_DONE; @@ -459,7 +455,11 @@ static int resolve_query(struct kr_request *request, const knot_pkt_t *packet) struct kr_query *qry = kr_rplan_push(rplan, NULL, qname, qclass, qtype); if (!qry) { #if defined(ENABLE_COOKIES) - /* May be a DNS cookies query. */ + /* RFC7873 5.4 specifies a query for server cookie. Such query + * has QDCOUNT == 0 and contains a cookie option. + * + * The layers don't expect to handle queries with QDCOUNT != 1 + * so such queries are handled directly here. */ struct knot_dns_cookies cookies = { 0, }; uint8_t *cookie_opt = kr_is_cookie_query(packet); if (cookie_opt && kr_glob_cookie_ctx.clnt.enabled) { diff --git a/modules/cookiectl/cookiectl.c b/modules/cookiectl/cookiectl.c index 23849b65c..55e7cba92 100644 --- a/modules/cookiectl/cookiectl.c +++ b/modules/cookiectl/cookiectl.c @@ -281,7 +281,7 @@ static bool read_available_sc_hashes(JsonNode *root) } const struct kr_sc_alg_descr *aux_ptr = kr_sc_algs; - while (aux_ptr && aux_ptr->alg && aux_ptr->alg->gen_func) { + while (aux_ptr && aux_ptr->alg && aux_ptr->alg->hash_func) { assert(aux_ptr->name); JsonNode *element = json_mkstring(aux_ptr->name); if (!element) { @@ -477,8 +477,7 @@ int cookiectl_init(struct kr_module *module) kr_glob_cookie_ctx.srvr.enabled = false; kr_glob_cookie_ctx.srvr.current.ssec = ss; - kr_glob_cookie_ctx.srvr.current.salg = kr_sc_alg(kr_sc_algs, - "HMAC-SHA256-64"); + kr_glob_cookie_ctx.srvr.current.salg = kr_sc_alg(kr_sc_algs, "FNV-64"); module->data = NULL; -- GitLab