From 639a486f7a6bc8d1d141628f7a3b7231400b2b84 Mon Sep 17 00:00:00 2001 From: Karel Slany <karel.slany@nic.cz> Date: Mon, 13 Jun 2016 19:05:15 +0200 Subject: [PATCH] Started working on 'server' cookie code. --- lib/cookies/{algorithm.c => alg_clnt.c} | 172 +++++++----- lib/cookies/{algorithm.h => alg_clnt.h} | 60 ++-- lib/cookies/alg_srvr.c | 355 ++++++++++++++++++++++++ lib/cookies/alg_srvr.h | 92 ++++++ lib/cookies/control.c | 4 +- lib/cookies/control.h | 4 +- lib/layer/cookiemonster.c | 50 +--- lib/lib.mk | 6 +- modules/cookiectl/cookiectl.c | 18 +- 9 files changed, 590 insertions(+), 171 deletions(-) rename lib/cookies/{algorithm.c => alg_clnt.c} (73%) rename lib/cookies/{algorithm.h => alg_clnt.h} (54%) create mode 100644 lib/cookies/alg_srvr.c create mode 100644 lib/cookies/alg_srvr.h diff --git a/lib/cookies/algorithm.c b/lib/cookies/alg_clnt.c similarity index 73% rename from lib/cookies/algorithm.c rename to lib/cookies/alg_clnt.c index 5724c9b17..d441ed2be 100644 --- a/lib/cookies/algorithm.c +++ b/lib/cookies/alg_clnt.c @@ -24,84 +24,19 @@ #include <openssl/sha.h> #include "contrib/fnv/fnv.h" -#include "lib/cookies/algorithm.h" +#include "lib/cookies/alg_clnt.h" //#define CC_HASH_USE_CLIENT_ADDRESS /* When defined, client address will be used when generating client cookie. */ -const struct kr_clnt_cookie_alg_descr kr_clnt_cookie_algs[] = { - { kr_clnt_cookie_alg_fnv64, "FNV-64" }, - { kr_clnt_cookie_alg_hmac_sha256_64, "HMAC-SHA256-64" }, - { NULL, NULL } -}; - -clnt_cookie_alg_t *kr_clnt_cookie_alg_func(const struct kr_clnt_cookie_alg_descr cc_algs[], - const char *name) -{ - if (!cc_algs || !name) { - return NULL; - } - - const struct kr_clnt_cookie_alg_descr *aux_ptr = cc_algs; - while (aux_ptr && aux_ptr->func) { - assert(aux_ptr->name); - if (strcmp(aux_ptr->name, name) == 0) { - return aux_ptr->func; - } - ++aux_ptr; - } - - return NULL; -} - -const char *kr_clnt_cookie_alg_name(const struct kr_clnt_cookie_alg_descr cc_algs[], - clnt_cookie_alg_t *func) -{ - if (!cc_algs || !func) { - return NULL; - } - - const struct kr_clnt_cookie_alg_descr *aux_ptr = cc_algs; - while (aux_ptr && aux_ptr->func) { - assert(aux_ptr->name); - if (aux_ptr->func == func) { - return aux_ptr->name; - } - ++aux_ptr; - } - - return NULL; -} - -int kr_address_bytes(const void *sockaddr, const uint8_t **addr, size_t *len) -{ - if (!sockaddr || !addr || !len) { - return kr_error(EINVAL); - } - - int addr_family = ((struct sockaddr *) sockaddr)->sa_family; - - switch (addr_family) { - case AF_INET: - *addr = (uint8_t *) &((struct sockaddr_in *) sockaddr)->sin_addr; - *len = 4; - break; - case AF_INET6: - *addr = (uint8_t *) &((struct sockaddr_in6 *) sockaddr)->sin6_addr; - *len = 16; - break; - default: - *addr = NULL; - *len = 0; - addr_family = AF_UNSPEC; - return kr_error(EINVAL); - break; - } - - return kr_ok(); -} - -int kr_clnt_cookie_alg_fnv64(const struct kr_clnt_cookie_input *input, - uint8_t cc_out[KNOT_OPT_COOKIE_CLNT]) +/** + * Compute client cookie using FNV-64. + * @note At least one of the arguments must be non-null. + * @param input Input parameters. + * @param cc_out Buffer for computed client cookie. + * @return kr_ok() on success, error code else. + */ +static int kr_clnt_cookie_alg_fnv64(const struct kr_clnt_cookie_input *input, + uint8_t cc_out[KNOT_OPT_COOKIE_CLNT]) { if (!input || !cc_out) { return kr_error(EINVAL); @@ -145,8 +80,15 @@ int kr_clnt_cookie_alg_fnv64(const struct kr_clnt_cookie_input *input, return kr_ok(); } -int kr_clnt_cookie_alg_hmac_sha256_64(const struct kr_clnt_cookie_input *input, - uint8_t cc_out[KNOT_OPT_COOKIE_CLNT]) +/** + * 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. + * @return kr_ok() on success, error code else. + */ +static int kr_clnt_cookie_alg_hmac_sha256_64(const struct kr_clnt_cookie_input *input, + uint8_t cc_out[KNOT_OPT_COOKIE_CLNT]) { if (!input || !cc_out) { return kr_error(EINVAL); @@ -216,3 +158,79 @@ fail: HMAC_CTX_cleanup(&ctx); return ret; } + +const struct kr_clnt_cookie_alg_descr kr_clnt_cookie_algs[] = { + { "FNV-64", kr_clnt_cookie_alg_fnv64 }, + { "HMAC-SHA256-64", kr_clnt_cookie_alg_hmac_sha256_64 }, + { NULL, NULL } +}; + +const struct kr_clnt_cookie_alg_descr *kr_clnt_cookie_alg(const struct kr_clnt_cookie_alg_descr cc_algs[], + const char *name) +{ + if (!cc_algs || !name) { + return NULL; + } + + const struct kr_clnt_cookie_alg_descr *aux_ptr = cc_algs; + while (aux_ptr && aux_ptr->func) { + assert(aux_ptr->name); + if (strcmp(aux_ptr->name, name) == 0) { + return aux_ptr; + } + ++aux_ptr; + } + + return NULL; +} + +int kr_address_bytes(const void *sockaddr, const uint8_t **addr, size_t *len) +{ + if (!sockaddr || !addr || !len) { + return kr_error(EINVAL); + } + + int addr_family = ((struct sockaddr *) sockaddr)->sa_family; + + switch (addr_family) { + case AF_INET: + *addr = (uint8_t *) &((struct sockaddr_in *) sockaddr)->sin_addr; + *len = 4; + break; + case AF_INET6: + *addr = (uint8_t *) &((struct sockaddr_in6 *) sockaddr)->sin6_addr; + *len = 16; + break; + default: + *addr = NULL; + *len = 0; + addr_family = AF_UNSPEC; + return kr_error(EINVAL); + break; + } + + return kr_ok(); +} + +int kr_clnt_cookie_check(const uint8_t cc[KNOT_OPT_COOKIE_CLNT], + const struct kr_clnt_cookie_input *input, + const struct kr_clnt_cookie_alg_descr *cc_alg) +{ + if (!cc || !input || !cc_alg || !cc_alg->func) { + return kr_error(EINVAL); + } + + uint8_t generated_cc[KNOT_OPT_COOKIE_CLNT] = {0, }; + + int ret = cc_alg->func(input, generated_cc); + if (ret != kr_ok()) { + return ret; + } + + ret = memcmp(cc, generated_cc, KNOT_OPT_COOKIE_CLNT); + if (ret == 0) { + return kr_ok(); + } + + return kr_error(EINVAL); +} diff --git a/lib/cookies/algorithm.h b/lib/cookies/alg_clnt.h similarity index 54% rename from lib/cookies/algorithm.h rename to lib/cookies/alg_clnt.h index e3fc53138..97cf5d174 100644 --- a/lib/cookies/algorithm.h +++ b/lib/cookies/alg_clnt.h @@ -28,17 +28,22 @@ struct kr_clnt_cookie_input { const void *clnt_sockaddr; /**< Client (local) socket address. */ const void *srvr_sockaddr; /**< Server (remote) socket address. */ const uint8_t *secret_data; /**< Client secret data. */ - size_t secret_len; + size_t secret_len; /**< Secret data length. */ }; -/** Client cookie algorithm type. */ +/** + * @brief Client cookie generator function type. + * @param input Data which to generate the cookie from. + * @param cc_out Buffer to write the resulting client cookie data into. + * @return kr_ok() or error code + */ typedef int (clnt_cookie_alg_t)(const struct kr_clnt_cookie_input *input, - uint8_t *); + uint8_t *cc_out); /** Holds description of client cookie hashing algorithms. */ struct kr_clnt_cookie_alg_descr { - clnt_cookie_alg_t *func; /**< Pointer to has function. */ const char *name; /**< Hash function name. */ + clnt_cookie_alg_t *func; /**< Pointer to hash function. */ }; /** @@ -50,27 +55,17 @@ KR_EXPORT extern const struct kr_clnt_cookie_alg_descr kr_clnt_cookie_algs[]; /** - * @brief Return pointer to client cookie hash function with given name. + * @brief Return pointer to client cookie algorithm with given name. * @param cc_algs List of available algorithms. * @param name Algorithm name. - * @return pointer to function or NULL if not found. - */ -KR_EXPORT -clnt_cookie_alg_t *kr_clnt_cookie_alg_func(const struct kr_clnt_cookie_alg_descr cc_algs[], - const char *name); - -/** - * @brief Return name of given client cookie hash function. - * @param cc_algs List of available algorithms. - * @param func Sought algorithm function. - * @return pointer to string or NULL if not found. + * @return pointer to algorithm or NULL if not found. */ KR_EXPORT -const char *kr_clnt_cookie_alg_name(const struct kr_clnt_cookie_alg_descr cc_algs[], - clnt_cookie_alg_t *func); +const struct kr_clnt_cookie_alg_descr *kr_clnt_cookie_alg(const struct kr_clnt_cookie_alg_descr cc_algs[], + const char *name); /** - * Get pointers to IP address bytes. + * @brief Get pointers to IP address bytes. * @param sockaddr socket address * @param addr pointer to address * @param len address length @@ -79,23 +74,14 @@ const char *kr_clnt_cookie_alg_name(const struct kr_clnt_cookie_alg_descr cc_alg int kr_address_bytes(const void *sockaddr, const uint8_t **addr, size_t *len); /** - * Compute client cookie using FNV-64. - * @note At least one of the arguments must be non-null. - * @param input Input parameters. - * @param cc_out Buffer for computed client cookie. - * @return kr_ok() on success, error code else. - */ -KR_EXPORT -int kr_clnt_cookie_alg_fnv64(const struct kr_clnt_cookie_input *input, - uint8_t cc_out[KNOT_OPT_COOKIE_CLNT]); - -/** - * 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. - * @return kr_ok() on success, error code else. + * @brief Check whether supplied client cookie was generated from given client + * secret and address. + * @param cc Client cookie that should be checked. + * @param input Input cookie algorithm parameters. + * @param cc_alg Client cookie algorithm. + * @return kr_ok() or error code */ KR_EXPORT -int kr_clnt_cookie_alg_hmac_sha256_64(const struct kr_clnt_cookie_input *input, - uint8_t cc_buf[KNOT_OPT_COOKIE_CLNT]); +int kr_clnt_cookie_check(const uint8_t cc[KNOT_OPT_COOKIE_CLNT], + const struct kr_clnt_cookie_input *input, + const struct kr_clnt_cookie_alg_descr *cc_alg); diff --git a/lib/cookies/alg_srvr.c b/lib/cookies/alg_srvr.c new file mode 100644 index 000000000..884dbb174 --- /dev/null +++ b/lib/cookies/alg_srvr.c @@ -0,0 +1,355 @@ +/* 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> /* ntohl(), ... */ +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> +#include <openssl/hmac.h> +#include <openssl/sha.h> + +#include "contrib/fnv/fnv.h" +#include "lib/cookies/alg_clnt.h" /* kr_address_bytes() */ +#include "lib/cookies/alg_srvr.h" + +/** + * @brief Server cookie contains only hash value. + * @note DNS Cookies -- Appendix B.1 + */ +static int srvr_cookie_parse_simple(const uint8_t *cookie_data, uint16_t data_len, + struct kr_srvr_cookie_inbound *inbound) +{ + if (!cookie_data || !inbound) { + return kr_error(EINVAL); + } + + const uint8_t *cc = NULL, *sc = NULL; + uint16_t cc_len = 0, sc_len = 0; + int ret = knot_edns_opt_cookie_parse(cookie_data, data_len, + &cc, &cc_len, &sc, &sc_len); + if (ret != KNOT_EOK || !sc) { + kr_error(EINVAL); /* Server cookie missing. */ + } + assert(cc_len == KNOT_OPT_COOKIE_CLNT); + + //memset(inbound, 0, sizeof(*inbound)); + inbound->clnt_cookie = cc; + inbound->hash_data = sc; /* Entire server cookie contains data. */ + inbound->hash_len = sc_len; + + return kr_ok(); +} + +/** + * @brief Server cookie contains also additional values. + * @note DNS Cookies -- Appendix B.2 + */ +static int srvr_cookie_parse(const uint8_t *cookie_data, uint16_t data_len, + struct kr_srvr_cookie_inbound *inbound) +{ + if (!cookie_data || !inbound) { + return kr_error(EINVAL); + } + + const uint8_t *cc = NULL, *sc = NULL; + uint16_t cc_len = 0, sc_len = 0; + int ret = knot_edns_opt_cookie_parse(cookie_data, data_len, + &cc, &cc_len, &sc, &sc_len); + if (ret != KNOT_EOK || !sc) { + kr_error(EINVAL); /* Server cookie missing. */ + } + assert(cc_len == KNOT_OPT_COOKIE_CLNT); + + if (sc_len <= (2 * sizeof(uint32_t))) { /* nonce + time */ + return kr_error(EINVAL); + } + + uint32_t aux; + + inbound->clnt_cookie = cc; + memcpy(&aux, sc, sizeof(aux)); + inbound->nonce = ntohl(aux); + memcpy(&aux, sc + sizeof(aux), sizeof(aux)); + inbound->time = ntohl(aux); + inbound->hash_data = sc + (2 * sizeof(aux)); + inbound->hash_len = sc_len - (2 * sizeof(aux)); + + return kr_ok(); +} + +#define SRVR_FNV64_SIMPLE_HASH_SIZE 8 + +/** + * @brief Compute server cookie using FNV-64 (hash only). + * @note Server cookie = FNV-64( client IP | client cookie | server secret ) + */ +static int kr_srvr_cookie_alg_fnv64_simple(const struct kr_srvr_cookie_input *input, + uint8_t sc_out[KNOT_OPT_COOKIE_SRVR_MAX], + size_t *sc_size) +{ + if (!input || !sc_out || + !sc_size || (*sc_size < SRVR_FNV64_SIMPLE_HASH_SIZE)) { + return kr_error(EINVAL); + } + + if (!input->clnt_cookie || + !input->srvr_data.secret_data || !input->srvr_data.secret_len) { + return kr_error(EINVAL); + } + + const uint8_t *addr = NULL; + size_t alen = 0; /* Address length. */ + + Fnv64_t hash_val = FNV1A_64_INIT; + + if (kr_ok() == kr_address_bytes(input->srvr_data.clnt_sockaddr, &addr, + &alen)) { + assert(addr && alen); + hash_val = fnv_64a_buf((void *) addr, alen, hash_val); + } + + hash_val = fnv_64a_buf((void *) input->clnt_cookie, + KNOT_OPT_COOKIE_CLNT, hash_val); + + hash_val = fnv_64a_buf((void *) input->srvr_data.secret_data, + input->srvr_data.secret_len, hash_val); + + memcpy(sc_out, &hash_val, sizeof(hash_val)); + *sc_size = sizeof(hash_val); + assert(SRVR_FNV64_SIMPLE_HASH_SIZE == *sc_size); + + return kr_ok(); +} + +#define SRVR_FNV64_SIZE 16 + +/** + * @brief Compute server cookie using FNV-64. + * @note Server cookie = nonce | time | FNV-64( client IP | nonce| time | client cookie | server secret ) + */ +static int kr_srvr_cookie_alg_fnv64(const struct kr_srvr_cookie_input *input, + uint8_t sc_out[KNOT_OPT_COOKIE_SRVR_MAX], + size_t *sc_size) +{ + if (!input || !sc_out || + !sc_size || (*sc_size < SRVR_FNV64_SIMPLE_HASH_SIZE)) { + return kr_error(EINVAL); + } + + if (!input->clnt_cookie || + !input->srvr_data.secret_data || !input->srvr_data.secret_len) { + return kr_error(EINVAL); + } + + const uint8_t *addr = NULL; + size_t alen = 0; /* Address length. */ + + Fnv64_t hash_val = FNV1A_64_INIT; + + if (input->srvr_data.clnt_sockaddr) { + if (kr_ok() == kr_address_bytes(input->srvr_data.clnt_sockaddr, + &addr, &alen)) { + assert(addr && alen); + hash_val = fnv_64a_buf((void *) addr, alen, hash_val); + } + } + + hash_val = fnv_64a_buf((void *) &input->nonce, sizeof(input->nonce), + hash_val); + + hash_val = fnv_64a_buf((void *) &input->time, sizeof(input->time), + hash_val); + + hash_val = fnv_64a_buf((void *) input->clnt_cookie, + KNOT_OPT_COOKIE_CLNT, hash_val); + + hash_val = fnv_64a_buf((void *) input->srvr_data.secret_data, + input->srvr_data.secret_len, hash_val); + + uint32_t aux = htonl(input->nonce); + memcpy(sc_out, &aux, sizeof(aux)); + aux = htonl(input->time); + memcpy(sc_out + sizeof(aux), &aux, sizeof(aux)); + + memcpy(sc_out + (2 * sizeof(aux)), &hash_val, sizeof(hash_val)); + *sc_size = (2 * sizeof(aux)) + sizeof(hash_val); + assert(SRVR_FNV64_SIZE == *sc_size); + + return kr_ok(); +} + +#define SRVR_HMAC_SHA256_64_SIMPLE_HASH_SIZE 8 + +/** + * @brief Compute server cookie using HMAC-SHA256-64 (hash only). + * @note Server cookie = HMAC-SHA256-64( server secret, client cookie | client IP ) + */ +static int kr_srvr_cookie_alg_hmac_sha256_64_simple(const struct kr_srvr_cookie_input *input, + uint8_t sc_out[KNOT_OPT_COOKIE_SRVR_MAX], + size_t *sc_size) +{ + if (!input || !sc_out || + !sc_size || (*sc_size < SRVR_FNV64_SIMPLE_HASH_SIZE)) { + return kr_error(EINVAL); + } + + if (!input->clnt_cookie || + !input->srvr_data.secret_data || !input->srvr_data.secret_len) { + return kr_error(EINVAL); + } + + const uint8_t *addr = NULL; + size_t alen = 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 = kr_error(EINVAL); + goto fail; + } + + ret = HMAC_Update(&ctx, input->clnt_cookie, KNOT_OPT_COOKIE_CLNT); + if (ret != 1) { + ret = kr_error(EINVAL); + goto fail; + } + + if (input->srvr_data.clnt_sockaddr) { + if (kr_ok() == kr_address_bytes(input->srvr_data.clnt_sockaddr, + &addr, &alen)) { + assert(addr && alen); + ret = HMAC_Update(&ctx, addr, alen); + if (ret != 1) { + ret = kr_error(EINVAL); + goto fail; + } + } + } + + if (1 != HMAC_Final(&ctx, digest, &digest_len)) { + ret = kr_error(EINVAL); + goto fail; + } + + assert(SRVR_HMAC_SHA256_64_SIMPLE_HASH_SIZE <= SHA256_DIGEST_LENGTH); + + memcpy(sc_out, digest, SRVR_HMAC_SHA256_64_SIMPLE_HASH_SIZE); + *sc_size = SRVR_HMAC_SHA256_64_SIMPLE_HASH_SIZE; + + ret = kr_ok(); + +fail: + HMAC_CTX_cleanup(&ctx); + return ret; +} + +#define SRVR_HMAC_SHA256_64_SIZE 16 + +/** + * @brief Compute server cookie using HMAC-SHA256-64). + * @note Server cookie = nonce | time | HMAC-SHA256-64( server secret, client cookie | nonce| time | client IP ) + */ +static int kr_srvr_cookie_alg_hmac_sha256_64(const struct kr_srvr_cookie_input *input, + uint8_t sc_out[KNOT_OPT_COOKIE_SRVR_MAX], + size_t *sc_size) +{ + if (!input || !sc_out || + !sc_size || (*sc_size < SRVR_FNV64_SIMPLE_HASH_SIZE)) { + return kr_error(EINVAL); + } + + if (!input->clnt_cookie || + !input->srvr_data.secret_data || !input->srvr_data.secret_len) { + return kr_error(EINVAL); + } + + const uint8_t *addr = NULL; + size_t alen = 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 = kr_error(EINVAL); + goto fail; + } + + ret = HMAC_Update(&ctx, input->clnt_cookie, KNOT_OPT_COOKIE_CLNT); + if (ret != 1) { + ret = kr_error(EINVAL); + goto fail; + } + + ret = HMAC_Update(&ctx, (void *) &input->nonce, sizeof(input->nonce)); + if (ret != 1) { + ret = kr_error(EINVAL); + goto fail; + } + + ret = HMAC_Update(&ctx, (void *) &input->time, sizeof(input->time)); + if (ret != 1) { + ret = kr_error(EINVAL); + goto fail; + } + + if (input->srvr_data.clnt_sockaddr) { + if (kr_ok() == kr_address_bytes(input->srvr_data.clnt_sockaddr, + &addr, &alen)) { + assert(addr && alen); + ret = HMAC_Update(&ctx, addr, alen); + if (ret != 1) { + ret = kr_error(EINVAL); + goto fail; + } + } + } + + if (1 != HMAC_Final(&ctx, digest, &digest_len)) { + ret = kr_error(EINVAL); + goto fail; + } + + uint32_t aux = htonl(input->nonce); + memcpy(sc_out, &aux, sizeof(aux)); + aux = htonl(input->time); + memcpy(sc_out + sizeof(aux), &aux, sizeof(aux)); + + assert(SRVR_HMAC_SHA256_64_SIMPLE_HASH_SIZE <= SHA256_DIGEST_LENGTH); + + memcpy(sc_out + (2 * sizeof(aux)), digest, + SRVR_HMAC_SHA256_64_SIMPLE_HASH_SIZE); + *sc_size = (2 * sizeof(aux)) + SRVR_HMAC_SHA256_64_SIMPLE_HASH_SIZE; + assert(SRVR_HMAC_SHA256_64_SIZE == *sc_size); + + ret = kr_ok(); + +fail: + HMAC_CTX_cleanup(&ctx); + return ret; +} diff --git a/lib/cookies/alg_srvr.h b/lib/cookies/alg_srvr.h new file mode 100644 index 000000000..64e425c93 --- /dev/null +++ b/lib/cookies/alg_srvr.h @@ -0,0 +1,92 @@ +/* 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/rrtype/opt_cookie.h> + +#include "lib/defines.h" + +/** Inbound server cookie checking context. */ +struct kr_srvr_cookie_check_ctx { + const void *clnt_sockaddr; /**< Client (remote) socket address. */ + const uint8_t *secret_data; /**< Server secret data. */ + size_t secret_len; /**< Server secret data length. */ +}; + +/** Inbound server cookie content structure. */ +struct kr_srvr_cookie_inbound { + const uint8_t *clnt_cookie; /**< Client cookie, `KNOT_OPT_COOKIE_CLNT` bytes long. */ + uint32_t nonce; /**< Some value. */ + uint32_t time; /**< Time stamp. */ + const uint8_t *hash_data; /**< Hash data. */ + uint16_t hash_len; /**< Hash data length. */ +}; + +/** Server cookie creation context. */ +struct kr_srvr_cookie_input { + const uint8_t *clnt_cookie; /**< Client cookie, must be `KNOT_OPT_COOKIE_CLNT` bytes long. */ + uint32_t nonce; /**< Some generated value. */ + uint32_t time; /**< Cookie time stamp. */ + struct kr_srvr_cookie_check_ctx srvr_data; /**< Data known to the server. */ +}; + +/** + * @brief Server cookie parser function type. + * @param cookie_data Entire cookie option data (without option header). + * @param data_len Cookie data length. + * @param inbound Inbound cookie structure to be set. + * @return kr_ok() or error code. + */ +typedef int (srvr_cookie_parse_t)(const uint8_t *cookie_data, uint16_t data_len, + struct kr_srvr_cookie_inbound *inbound); +/** + * @brief Server cookie generator function type. + * @param input Data which to generate the cookie from. + * @param sc_out Buffer to write the resulting client cookie data into. + * @param sc_size On input must contain size of the buffer, on successful return contains size of actual written data. + * @return kr_ok() or error code + */ +typedef int (srvr_cookie_gen_t)(const struct kr_srvr_cookie_input *input, + uint8_t *sc_out, size_t *sc_size); + +/** Holds description of server cookie hashing algorithms. */ +struct kr_srvr_cookie_alg_descr { + const char *name; /** Server cookie algorithm name. */ + const uint16_t srvr_cookie_size; /**< Size of the generated server cookie. */ + const srvr_cookie_parse_t *opt_parse_func; /**< Cookie option parser function. */ + const srvr_cookie_gen_t *gen_func; /*< Cookie generator function. */ +}; + +/** + * List of available server cookie algorithms. + * + * Last element contains all null entries. + */ +KR_EXPORT +extern const struct kr_srvr_cookie_alg_descr kr_srvr_cookie_algs[]; + +/** + * @brief Check whether supplied client and server cookie match. + * @param cookie_opt Entire cookie option, must contain server cookie. + * @param check_ctx Data known to the server needed for cookie validation. + * @param sc_alg Server cookie algorithm. + * @return kr_ok() if check OK, error code else. + */ +KR_EXPORT +int kr_srvr_cookie_check(const uint8_t *cookie_opt, + const struct kr_srvr_cookie_check_ctx *check_ctx, + const struct kr_srvr_cookie_alg_descr *sc_alg); diff --git a/lib/cookies/control.c b/lib/cookies/control.c index 778c51f27..9afd81368 100644 --- a/lib/cookies/control.c +++ b/lib/cookies/control.c @@ -152,8 +152,8 @@ int kr_request_put_cookie(const struct kr_cookie_ctx *cntrl, .secret_len = cntrl->current_cs->size }; uint8_t cc[KNOT_OPT_COOKIE_CLNT]; - assert(cntrl->cc_alg_func); - int ret = cntrl->cc_alg_func(&input, cc); + assert(cntrl->cc_alg && cntrl->cc_alg->func); + int ret = cntrl->cc_alg->func(&input, cc); if (ret != kr_ok()) { return ret; } diff --git a/lib/cookies/control.h b/lib/cookies/control.h index 98727d99e..99c43db34 100644 --- a/lib/cookies/control.h +++ b/lib/cookies/control.h @@ -20,7 +20,7 @@ #include <libknot/rrtype/opt_cookie.h> #include <stdbool.h> -#include "lib/cookies/algorithm.h" +#include "lib/cookies/alg_clnt.h" #include "lib/cache.h" #include "lib/defines.h" @@ -46,7 +46,7 @@ struct kr_cookie_ctx { uint32_t cache_ttl; /**< TTL used when caching cookies */ - clnt_cookie_alg_t *cc_alg_func; /**< Client cookie hash computation callback. */ + const struct kr_clnt_cookie_alg_descr *cc_alg; /**< Client cookie algorithm. */ }; /** Global cookie control context. */ diff --git a/lib/layer/cookiemonster.c b/lib/layer/cookiemonster.c index 4bed370b1..89c34732a 100644 --- a/lib/layer/cookiemonster.c +++ b/lib/layer/cookiemonster.c @@ -25,7 +25,7 @@ #include <string.h> #include "daemon/engine.h" -#include "lib/cookies/algorithm.h" +#include "lib/cookies/alg_clnt.h" #include "lib/cookies/cache.h" #include "lib/cookies/control.h" #include "lib/module.h" @@ -38,37 +38,6 @@ /* TODO -- The context must store sent cookies and server addresses in order * to make the process more reliable. */ -/** - * Check whether supplied client cookie was generated from given client secret - * and address. - * @param cc client cookie - * @param input input cookie algorithm parameters - * @param cc_alg_func function generating client cookie - * @return kr_ok() or error code - */ -static int check_client_cookie(const uint8_t cc[KNOT_OPT_COOKIE_CLNT], - const struct kr_clnt_cookie_input *input, - clnt_cookie_alg_t *cc_alg_func) -{ - if (!cc || !input || !cc_alg_func) { - return kr_error(EINVAL); - } - - uint8_t generated_cc[KNOT_OPT_COOKIE_CLNT] = {0, }; - - int ret = cc_alg_func(input, generated_cc); - if (ret != kr_ok()) { - return ret; - } - - ret = memcmp(cc, generated_cc, KNOT_OPT_COOKIE_CLNT); - if (ret == 0) { - return kr_ok(); - } - - return kr_error(EINVAL); -} - /** * Obtain address from query/response context if if can be obtained. * @param qry query context @@ -92,15 +61,15 @@ static const struct sockaddr *passed_server_sockaddr(const struct kr_query *qry) * @param nsrep name server reputation context * @param cc client cookie data * @param csecr client secret - * @param cc_alg_func function generating client cookie + * @param cc_alg client cookie algorithm * @return pointer to address if a matching found, NULL if none matches */ static const struct sockaddr *guess_server_addr(const struct kr_nsrep *nsrep, const uint8_t cc[KNOT_OPT_COOKIE_CLNT], const struct kr_cookie_secret *csecr, - clnt_cookie_alg_t *cc_alg_func) + const struct kr_clnt_cookie_alg_descr *cc_alg) { - assert(nsrep && cc && csecr && cc_alg_func); + assert(nsrep && cc && csecr && cc_alg); const struct sockaddr *sockaddr = NULL; @@ -118,7 +87,7 @@ static const struct sockaddr *guess_server_addr(const struct kr_nsrep *nsrep, } input.srvr_sockaddr = &nsrep->addr[i]; - int ret = check_client_cookie(cc, &input, cc_alg_func); + int ret = kr_clnt_cookie_check(cc, &input, cc_alg); if (ret == kr_ok()) { sockaddr = (struct sockaddr *) &nsrep->addr[i]; break; @@ -155,13 +124,12 @@ static int srvr_sockaddr_cc_check(const struct sockaddr **sockaddr, .secret_data = cntrl->current_cs->data, .secret_len = cntrl->current_cs->size }; - int ret = check_client_cookie(cc, &input, cntrl->cc_alg_func); + int ret = kr_clnt_cookie_check(cc, &input, cntrl->cc_alg); bool have_current = (ret == kr_ok()); if ((ret != kr_ok()) && cntrl->recent_cs) { input.secret_data = cntrl->recent_cs->data; input.secret_len = cntrl->recent_cs->size; - ret = check_client_cookie(cc, &input, - cntrl->cc_alg_func); + ret = kr_clnt_cookie_check(cc, &input, cntrl->cc_alg); } if (ret == kr_ok()) { *sockaddr = tmp_sockaddr; @@ -180,12 +148,12 @@ static int srvr_sockaddr_cc_check(const struct sockaddr **sockaddr, /* Abusing name server reputation mechanism to guess IP addresses. */ const struct kr_nsrep *ns = &qry->ns; tmp_sockaddr = guess_server_addr(ns, cc, cntrl->current_cs, - cntrl->cc_alg_func); + cntrl->cc_alg); bool have_current = (tmp_sockaddr != NULL); if (!tmp_sockaddr && cntrl->recent_cs) { /* Try recent client secret to check obtained cookie. */ tmp_sockaddr = guess_server_addr(ns, cc, cntrl->recent_cs, - cntrl->cc_alg_func); + cntrl->cc_alg); } if (tmp_sockaddr) { *sockaddr = tmp_sockaddr; diff --git a/lib/lib.mk b/lib/lib.mk index ac5e53827..33bdc7a3b 100644 --- a/lib/lib.mk +++ b/lib/lib.mk @@ -48,12 +48,14 @@ ifeq ($(HAS_libcrypto),yes) libkres_SOURCES += \ contrib/fnv/hash_64a.c \ lib/layer/cookiemonster.c \ - lib/cookies/algorithm.c \ + lib/cookies/alg_clnt.c \ + lib/cookies/alg_srvr.c \ lib/cookies/cache.c \ lib/cookies/control.c libkres_HEADERS += \ - lib/cookies/algorithm.h \ + lib/cookies/alg_clnt.h \ + lib/cookies/alg_srvr.h \ lib/cookies/cache.h \ lib/cookies/control.h diff --git a/modules/cookiectl/cookiectl.c b/modules/cookiectl/cookiectl.c index ce7fda084..156b9ea7e 100644 --- a/modules/cookiectl/cookiectl.c +++ b/modules/cookiectl/cookiectl.c @@ -21,7 +21,7 @@ #include <string.h> #include "daemon/engine.h" -#include "lib/cookies/algorithm.h" +#include "lib/cookies/alg_clnt.h" #include "lib/cookies/control.h" #include "lib/layer.h" @@ -133,12 +133,12 @@ static bool apply_client_hash_func(struct kr_cookie_ctx *cntrl, const JsonNode *node) { if (node->tag == JSON_STRING) { - clnt_cookie_alg_t *cc_alg_func = kr_clnt_cookie_alg_func(kr_clnt_cookie_algs, - node->string_); - if (!cc_alg_func) { + const struct kr_clnt_cookie_alg_descr *cc_alg = kr_clnt_cookie_alg(kr_clnt_cookie_algs, + node->string_); + if (!cc_alg) { return false; } - cntrl->cc_alg_func = cc_alg_func; + cntrl->cc_alg = cc_alg; return true; } @@ -263,11 +263,9 @@ static char *cookiectl_config(void *env, struct kr_module *module, const char *a read_secret(root_node, &kr_glob_cookie_ctx); - const char *name = kr_clnt_cookie_alg_name(kr_clnt_cookie_algs, - kr_glob_cookie_ctx.cc_alg_func); - assert(name); + assert(kr_glob_cookie_ctx.cc_alg->name); json_append_member(root_node, NAME_CLIENT_COOKIE_ALG, - json_mkstring(name)); + json_mkstring(kr_glob_cookie_ctx.cc_alg->name)); read_available_cc_hashes(root_node, &kr_glob_cookie_ctx); @@ -293,7 +291,7 @@ int cookiectl_init(struct kr_module *module) kr_glob_cookie_ctx.enabled = false; kr_glob_cookie_ctx.current_cs = &dflt_cs; kr_glob_cookie_ctx.cache_ttl = DFLT_COOKIE_TTL; - kr_glob_cookie_ctx.cc_alg_func = kr_clnt_cookie_alg_fnv64; + kr_glob_cookie_ctx.cc_alg = kr_clnt_cookie_alg(kr_clnt_cookie_algs, "FNV-64"); module->data = NULL; -- GitLab