diff --git a/Knot.files b/Knot.files index 064df80faac19c7e884767cb8520df027a7d32c5..ae6df4dad4b69539fbcc5037d2259503196c8cd7 100644 --- a/Knot.files +++ b/Knot.files @@ -330,10 +330,8 @@ src/libknot/codes.h src/libknot/consts.h src/libknot/control/control.c src/libknot/control/control.h -src/libknot/cookies/client.c -src/libknot/cookies/client.h -src/libknot/cookies/server.c -src/libknot/cookies/server.h +src/libknot/cookies.c +src/libknot/cookies.h src/libknot/db/db.h src/libknot/db/db_lmdb.c src/libknot/db/db_lmdb.h @@ -377,8 +375,6 @@ src/libknot/rrtype/nsec3.c src/libknot/rrtype/nsec3.h src/libknot/rrtype/nsec3param.c src/libknot/rrtype/nsec3param.h -src/libknot/rrtype/opt-cookie.c -src/libknot/rrtype/opt-cookie.h src/libknot/rrtype/opt.c src/libknot/rrtype/opt.h src/libknot/rrtype/rdname.h @@ -493,9 +489,7 @@ tests/contrib/test_time.c tests/contrib/test_wire.c tests/contrib/test_wire_ctx.c tests/libknot/test_control.c -tests/libknot/test_cookies-client.c -tests/libknot/test_cookies-opt.c -tests/libknot/test_cookies-server.c +tests/libknot/test_cookies.c tests/libknot/test_db.c tests/libknot/test_descriptor.c tests/libknot/test_dname.c diff --git a/src/Makefile.am b/src/Makefile.am index 49f4fc2ac09838e3377997e2fcdc33323a499942..7abb5861b5913bf604ed5c4b057136b1e15859fb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -101,8 +101,7 @@ nobase_libknot_la_HEADERS = \ libknot/codes.h \ libknot/consts.h \ libknot/control/control.h \ - libknot/cookies/client.h \ - libknot/cookies/server.h \ + libknot/cookies.h \ libknot/descriptor.h \ libknot/dname.h \ libknot/errcode.h \ @@ -129,7 +128,6 @@ nobase_libknot_la_HEADERS = \ libknot/rrtype/nsec3.h \ libknot/rrtype/nsec3param.h \ libknot/rrtype/opt.h \ - libknot/rrtype/opt-cookie.h \ libknot/rrtype/rdname.h \ libknot/rrtype/rrsig.h \ libknot/rrtype/soa.h \ @@ -147,8 +145,7 @@ nobase_libknot_la_HEADERS = \ libknot_la_SOURCES = \ libknot/codes.c \ libknot/control/control.c \ - libknot/cookies/client.c \ - libknot/cookies/server.c \ + libknot/cookies.c \ libknot/descriptor.c \ libknot/dname.c \ libknot/error.c \ @@ -168,7 +165,6 @@ libknot_la_SOURCES = \ libknot/rrtype/nsec3.c \ libknot/rrtype/nsec3param.c \ libknot/rrtype/opt.c \ - libknot/rrtype/opt-cookie.c \ libknot/rrtype/rrsig.c \ libknot/rrtype/soa.c \ libknot/rrtype/tsig.c \ diff --git a/src/libknot/cookies.c b/src/libknot/cookies.c new file mode 100644 index 0000000000000000000000000000000000000000..dd024d5c96843cb59c5284cbb62279bfc76a8326 --- /dev/null +++ b/src/libknot/cookies.c @@ -0,0 +1,130 @@ +/* Copyright (C) 2017 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 <stdbool.h> + +#include "libknot/attribute.h" +#include "libknot/cookies.h" +#include "libknot/errcode.h" +#include "contrib/string.h" +#include "contrib/sockaddr.h" +#include "contrib/openbsd/siphash.h" + +_public_ +int knot_edns_cookie_client_generate(knot_edns_cookie_t *out, + const knot_edns_cookie_params_t *params) +{ + if (out == NULL || params == NULL || params->client_addr == NULL || + params->server_addr == NULL) { + return KNOT_EINVAL; + } + + SIPHASH_CTX ctx; + assert(sizeof(params->secret) == sizeof(SIPHASH_KEY)); + SipHash24_Init(&ctx, (const SIPHASH_KEY *)params->secret); + + size_t addr_len = 0; + void *addr = sockaddr_raw(params->client_addr, &addr_len); + SipHash24_Update(&ctx, addr, addr_len); + + addr_len = 0; + addr = sockaddr_raw(params->server_addr, &addr_len); + SipHash24_Update(&ctx, addr, addr_len); + + uint64_t hash = SipHash24_End(&ctx); + memcpy(out->data, &hash, sizeof(hash)); + out->len = sizeof(hash); + + return KNOT_EOK; +} + +_public_ +int knot_edns_cookie_client_check(const knot_edns_cookie_t *cc, + const knot_edns_cookie_params_t *params) +{ + if (cc == NULL || cc->len != KNOT_EDNS_COOKIE_CLNT_SIZE) { + return KNOT_EINVAL; + } + + knot_edns_cookie_t ref; + int ret = knot_edns_cookie_client_generate(&ref, params); + if (ret != KNOT_EOK) { + return ret; + } + assert(ref.len == KNOT_EDNS_COOKIE_CLNT_SIZE); + + ret = const_time_memcmp(cc->data, ref.data, KNOT_EDNS_COOKIE_CLNT_SIZE); + if (ret != 0) { + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +_public_ +int knot_edns_cookie_server_generate(knot_edns_cookie_t *out, + const knot_edns_cookie_t *cc, + const knot_edns_cookie_params_t *params) +{ + if (out == NULL || cc == NULL || cc->len != KNOT_EDNS_COOKIE_CLNT_SIZE || + params == NULL || params->client_addr == NULL) { + return KNOT_EINVAL; + } + + SIPHASH_CTX ctx; + assert(sizeof(params->secret) == sizeof(SIPHASH_KEY)); + SipHash24_Init(&ctx, (const SIPHASH_KEY *)params->secret); + + size_t addr_len = 0; + void *addr = sockaddr_raw(params->client_addr, &addr_len); + SipHash24_Update(&ctx, addr, addr_len); + + SipHash24_Update(&ctx, cc->data, cc->len); + + uint64_t hash = SipHash24_End(&ctx); + memcpy(out->data, &hash, sizeof(hash)); + out->len = sizeof(hash); + + return KNOT_EOK; +} + +_public_ +int knot_edns_cookie_server_check(const knot_edns_cookie_t *sc, + const knot_edns_cookie_t *cc, + const knot_edns_cookie_params_t *params) +{ + if (sc == NULL || cc == NULL || params == NULL) { + return KNOT_EINVAL; + } + + knot_edns_cookie_t ref; + int ret = knot_edns_cookie_server_generate(&ref, cc, params); + if (ret != KNOT_EOK) { + return ret; + } + + if (sc->len != ref.len) { + return KNOT_EINVAL; + } + + ret = const_time_memcmp(sc->data, ref.data, sc->len); + if (ret != 0) { + return KNOT_EINVAL; + } + + return KNOT_EOK; +} diff --git a/src/libknot/cookies.h b/src/libknot/cookies.h new file mode 100644 index 0000000000000000000000000000000000000000..5bdfb68f727496501189979ac5ea30fcfd9e0aef --- /dev/null +++ b/src/libknot/cookies.h @@ -0,0 +1,97 @@ +/* Copyright (C) 2017 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/>. + */ +/*! +* \file +* +* \brief DNS cookies processing. +* +* \addtogroup libknot +* @{ +*/ + +#pragma once + +#include <stdint.h> +#include <sys/socket.h> + +#include "libknot/rrtype/opt.h" + +#define KNOT_EDNS_COOKIE_SECRET_SIZE 16 + +/*! + * \brief DNS Cookie parameters needed to compute the cookie value. + * + * \note Server address is not used for the server cookie check. + */ +typedef struct { + const struct sockaddr *client_addr; /*!< Client socket address. */ + const struct sockaddr *server_addr; /*!< Server socket address. */ + uint8_t secret[KNOT_EDNS_COOKIE_SECRET_SIZE]; /*!< Cookie secret data. */ +} knot_edns_cookie_params_t; + +/*! + * \brief Generate a client cookie using given parameters. + * + * \param out Generated client cookie. + * \param params Client cookie parameters. + * + * \retval KNOT_EOK + * \retval KNOT_EINVAL + */ +int knot_edns_cookie_client_generate(knot_edns_cookie_t *out, + const knot_edns_cookie_params_t *params); + +/*! + * \brief Check whether client cookie was generated using given parameters. + * + * \param cc Client cookie that should be checked. + * \param params Client cookie parameters. + * + * \retval KNOT_EOK + * \retval KNOT_EINVAL + */ +int knot_edns_cookie_client_check(const knot_edns_cookie_t *cc, + const knot_edns_cookie_params_t *params); + +/*! + * \brief Generate a server cookie using given parameters. + * + * \param out Generated server cookie. + * \param cc Client cookie parameter. + * \param params Server cookie parameters. + * + * \retval KNOT_EOK + * \retval KNOT_EINVAL + */ +int knot_edns_cookie_server_generate(knot_edns_cookie_t *out, + const knot_edns_cookie_t *cc, + const knot_edns_cookie_params_t *params); + +/*! + * \brief Check whether server cookie was generated using given parameters. + * + * \param sc Server cookie that should be checked. + * \param cc Client cookie parameter. + * \param params Server cookie parameters. + * + * \retval KNOT_EOK + * \retval KNOT_EINVAL + */ +int knot_edns_cookie_server_check(const knot_edns_cookie_t *sc, + const knot_edns_cookie_t *cc, + const knot_edns_cookie_params_t *params); + +/*! @} */ diff --git a/src/libknot/cookies/client.c b/src/libknot/cookies/client.c deleted file mode 100644 index e0b347d3ba0bdf774bcadbe5462a99790f01f674..0000000000000000000000000000000000000000 --- a/src/libknot/cookies/client.c +++ /dev/null @@ -1,75 +0,0 @@ -/* Copyright (C) 2017 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 <string.h> - -#include "libknot/attribute.h" -#include "libknot/cookies/client.h" -#include "libknot/errcode.h" -#include "libknot/rrtype/opt-cookie.h" -#include "contrib/string.h" -#include "contrib/sockaddr.h" - -_public_ -bool knot_cc_input_is_valid(const struct knot_cc_input *input) -{ - /* - * RFC7873 4.1 -- Client cookie should be generated from - * client IP address, server IP address and a secret quantity. - */ - - return input && (input->clnt_sockaddr || input->srvr_sockaddr); -} - -static uint64_t generate_client_cookie(const struct knot_cc_input *input) -{ - SIPHASH_CTX ctx; - SipHash24_Init(&ctx, &input->secret); - - if (input->clnt_sockaddr) { - size_t addr_len = 0; - void *addr = sockaddr_raw(input->clnt_sockaddr, &addr_len); - SipHash24_Update(&ctx, addr, addr_len); - } - - if (input->srvr_sockaddr) { - size_t addr_len = 0; - void *addr = sockaddr_raw(input->srvr_sockaddr, &addr_len); - SipHash24_Update(&ctx, addr, addr_len); - } - - return SipHash24_End(&ctx); -} - -_public_ -int knot_cc_check(const uint8_t *cc, uint16_t cc_len, - const struct knot_cc_input *input) -{ - if (!cc || cc_len != KNOT_OPT_COOKIE_CLNT || !knot_cc_input_is_valid(input)) { - return KNOT_EINVAL; - } - - uint64_t generated_cc = generate_client_cookie(input); - assert(sizeof(generated_cc) == KNOT_OPT_COOKIE_CLNT); - - int ret = const_time_memcmp(cc, &generated_cc, KNOT_OPT_COOKIE_CLNT); - if (ret != 0) { - return KNOT_EINVAL; - } - - return KNOT_EOK; -} diff --git a/src/libknot/cookies/client.h b/src/libknot/cookies/client.h deleted file mode 100644 index e345009f95ba74d1a7d56cfb06bfbaf157631de0..0000000000000000000000000000000000000000 --- a/src/libknot/cookies/client.h +++ /dev/null @@ -1,76 +0,0 @@ -/* Copyright (C) 2017 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 <stdbool.h> -#include <stdint.h> -#include <stdlib.h> - -#include "contrib/openbsd/siphash.h" - -/*! - * \brief Input data needed to compute the client cookie value. - */ -struct knot_cc_input { - const struct sockaddr *clnt_sockaddr; /*!< Client (local) socket address. */ - const struct sockaddr *srvr_sockaddr; /*!< Server (remote) socket address. */ - SIPHASH_KEY secret; /*!< Client secret data. */ -}; - -/*! - * \brief Check client cookie input data for basic sanity. - * - * \param input Data which to generate the cookie from. - * - * \retval true if input contains at least one socket and secret data - * \retval false if input is insufficient or NULL pointer passed - */ -bool knot_cc_input_is_valid(const struct knot_cc_input *input); - -/*! - * \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. - * \param cc_len Cookie buffer size. - * - * \retval non-zero size of written data on successful return - * \retval 0 on error - */ -typedef uint16_t (knot_cc_gen_t)(const struct knot_cc_input *input, - uint8_t *cc_out, uint16_t cc_len); - -/*! - * \brief Holds description of the client cookie algorithm. - */ -struct knot_cc_alg { - const uint16_t cc_size; /*!< Cookie size the algorithm operates with. */ - knot_cc_gen_t *gen_func; /*!< Cookie generator function. */ -}; - -/*! - * \brief Check whether client cookie \a cc was generated from given \a input. - * - * \param cc Client cookie that should be checked. - * \param cc_len Client cookie size. - * \param input Client cookie input algorithm parameters. - * - * \retval KNOT_EOK - * \retval KNOT_EINVAL - */ -int knot_cc_check(const uint8_t *cc, uint16_t cc_len, - const struct knot_cc_input *input); diff --git a/src/libknot/cookies/server.c b/src/libknot/cookies/server.c deleted file mode 100644 index 61dae82d353b9b7624e0dae286e47ddff527496d..0000000000000000000000000000000000000000 --- a/src/libknot/cookies/server.c +++ /dev/null @@ -1,145 +0,0 @@ -/* Copyright (C) 2017 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 <string.h> - -#include "libknot/attribute.h" -#include "libknot/cookies/server.h" -#include "libknot/errcode.h" -#include "libknot/rrtype/opt-cookie.h" -#include "contrib/string.h" -#include "contrib/sockaddr.h" - -#define SERVER_HASH_LEN 8 - -_public_ -bool knot_sc_input_is_valid(const struct knot_sc_input *input) -{ - /* - * RFC7873 4.2 -- Server cookie should be generated from request - * source IP address, a secret quantity and request client cookie. - */ - - return input && input->cc && input->cc_len > 0 && input->srvr_data; -} - -_public_ -int knot_sc_parse(uint16_t nonce_len, const uint8_t *sc, uint16_t sc_len, - struct knot_sc_content *content) -{ - if (!sc || !sc_len || !content) { - return KNOT_EINVAL; - } - - if (nonce_len >= sc_len) { - return KNOT_EINVAL; - } - - content->nonce = nonce_len ? sc : NULL; - content->nonce_len = nonce_len; - /* Rest of server cookie contains hash. */ - content->hash = sc + nonce_len; - content->hash_len = sc_len - nonce_len; - - return KNOT_EOK; -} - -static uint64_t generate_server_cookie(const struct knot_sc_input *input) -{ - SIPHASH_CTX ctx; - SipHash24_Init(&ctx, &input->srvr_data->secret); - - if (input->srvr_data->clnt_sockaddr) { - size_t addr_len = 0; - void *addr = sockaddr_raw(input->srvr_data->clnt_sockaddr, &addr_len); - if (addr) { - SipHash24_Update(&ctx, addr, addr_len); - } - } - - if (input->nonce && input->nonce_len > 0) { - SipHash24_Update(&ctx, input->nonce, input->nonce_len); - } - - if (input->cc && input->cc_len == KNOT_OPT_COOKIE_CLNT) { - SipHash24_Update(&ctx, input->cc, input->cc_len); - } - - return SipHash24_End(&ctx); -} - -_public_ -int knot_sc_check(uint16_t nonce_len, const struct knot_dns_cookies *cookies, - const struct knot_sc_private *srvr_data) -{ - if (!cookies || !srvr_data) { - return KNOT_EINVAL; - } - - if (!cookies->cc || !cookies->cc_len || - !cookies->sc || !cookies->sc_len) { - return KNOT_EINVAL; - } - - if (!srvr_data->clnt_sockaddr) { - return KNOT_EINVAL; - } - - if ((nonce_len + SERVER_HASH_LEN) > KNOT_OPT_COOKIE_SRVR_MAX) { - return KNOT_EINVAL; - } - - if (cookies->sc_len != (nonce_len + SERVER_HASH_LEN)) { - return KNOT_EINVAL; - } - - struct knot_sc_content content = { 0 }; - - /* Obtain data from received server cookie. */ - int ret = knot_sc_parse(nonce_len, cookies->sc, cookies->sc_len, &content); - if (ret != KNOT_EOK) { - return ret; - } - - if (content.hash_len != SERVER_HASH_LEN) { - return KNOT_EINVAL; - } - - struct knot_sc_input sc_input = { - .cc = cookies->cc, - .cc_len = cookies->cc_len, - .nonce = content.nonce, - .nonce_len = content.nonce_len, - .srvr_data = srvr_data - }; - - if (!knot_sc_input_is_valid(&sc_input)) { - return KNOT_EINVAL; - } - - /* Generate a new hash. */ - uint64_t generated_hash = generate_server_cookie(&sc_input); - assert(sizeof(generated_hash) == SERVER_HASH_LEN); - - /* Compare hashes. */ - ret = const_time_memcmp(content.hash, &generated_hash, SERVER_HASH_LEN); - if (ret != 0) { - return KNOT_EINVAL; - } - - return KNOT_EOK; -} diff --git a/src/libknot/cookies/server.h b/src/libknot/cookies/server.h deleted file mode 100644 index 825eaa76c0a281096d238b18b879f44818b865d6..0000000000000000000000000000000000000000 --- a/src/libknot/cookies/server.h +++ /dev/null @@ -1,134 +0,0 @@ -/* Copyright (C) 2017 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 <stdbool.h> -#include <stdint.h> -#include <stdlib.h> - -#include "contrib/openbsd/siphash.h" - -/*! - * \brief Convenience structure holding both, server and client, cookies. - */ -struct knot_dns_cookies { - const uint8_t *cc; /*!< Client cookie. */ - uint16_t cc_len; /*!< Client cookie size. */ - const uint8_t *sc; /*!< Server cookie. */ - uint16_t sc_len; /*!< Server cookie size. */ -}; - -/*! - * \brief Private data known to the server. - * - * \note Contains data needed to check the inbound server cookie and to - * generate a new one. - */ -struct knot_sc_private { - const struct sockaddr *clnt_sockaddr; /*!< Client (remote) socket address. */ - SIPHASH_KEY secret; /*!< Server secret data. */ -}; - -/*! - * \brief Inbound server cookie content structure. - * - * \note These data are obtained from an incoming server cookie. - */ -struct knot_sc_content { - const uint8_t *nonce; /*!< Some value prefixed to the hash. */ - uint16_t nonce_len; /*!< Nonce data length. */ - const uint8_t *hash; /*!< Hash data. */ - uint16_t hash_len; /*!< Hash data length. */ -}; - -/*! - * \brief Input data needed to compute the server cookie value. - * - * \note All these data are needed to generate a new server cookie hash. - */ -struct knot_sc_input { - const uint8_t *cc; /*!< Client cookie. */ - uint16_t cc_len; /*!< Client cookie size. */ - const uint8_t *nonce; /*!< Some value prefixed before the hash. */ - uint16_t nonce_len; /*!< Nonce data length. */ - const struct knot_sc_private *srvr_data; /*!< Private data known to the server. */ -}; - -/*! - * \brief Check server cookie input data for basic sanity. - * - * \param input Data which to generate the cookie from. - * - * \retval true if input contains client cookie and server secret data - * \retval false if input is insufficient or NULL pointer passed - */ -bool knot_sc_input_is_valid(const struct knot_sc_input *input); - -/*! - * \brief Reads a server cookie that contains \a nonce_len bytes of data - * prefixed before the actual hash. - * - * \see DNS Cookies, RFC 7873, Appendix B.1 and B.2 - * - * \param nonce_len Expected nonce data size. - * \param sc Server cookie. - * \param sc_len Server cookie length. - * \param content Server cookie content structure to be set. - * - * \retval KNOT_EOK - * \retval KNOT_EINVAL - */ -int knot_sc_parse(uint16_t nonce_len, const uint8_t *sc, uint16_t sc_len, - struct knot_sc_content *content); - -/*! - * \brief Hash generator function type. - * - * \note The function writes only the hash value. It does not write any nonce - * data prefixed before the actual hash value. Nonce data must be written - * by an external function into the server cookie. - * - * \param input Data which to generate the cookie from. - * \param hash_out Buffer to write the resulting hash data into. - * \param hash_len Hash buffer size. - * - * \retval non-zero size of written data on successful return - * \retval 0 on error - */ -typedef uint16_t (knot_sc_hash_t)(const struct knot_sc_input *input, - uint8_t *hash_out, uint16_t hash_len); - -/*! - * \brief Holds description of the server cookie algorithm. - */ -struct knot_sc_alg { - const uint16_t hash_size; /*!< Hash size the algorithm operates with. */ - knot_sc_hash_t *hash_func; /*!< Cookie generator function. */ -}; - -/*! - * \brief Check whether supplied client and server cookies match. - * - * \param nonce_len Expected nonce data size. - * \param cookies Cookie data. - * \param srvr_data Data known to the server needed for cookie validation. - * - * \retval KNOT_EOK - * \retval KNOT_EINVAL - */ -int knot_sc_check(uint16_t nonce_len, const struct knot_dns_cookies *cookies, - const struct knot_sc_private *srvr_data); diff --git a/src/libknot/rrtype/opt-cookie.c b/src/libknot/rrtype/opt-cookie.c deleted file mode 100644 index d678dbb322a5504ae2cdc49dd1913521c157ebab..0000000000000000000000000000000000000000 --- a/src/libknot/rrtype/opt-cookie.c +++ /dev/null @@ -1,98 +0,0 @@ -/* 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 <stdlib.h> - -#include "contrib/wire_ctx.h" -#include "libknot/errcode.h" -#include "libknot/rrtype/opt.h" -#include "libknot/rrtype/opt-cookie.h" - -#define cookie_len_ok(clen) \ - (((clen) == KNOT_OPT_COOKIE_CLNT) || \ - ((clen) >= (KNOT_OPT_COOKIE_CLNT + KNOT_OPT_COOKIE_SRVR_MIN) && \ - (clen) <= (KNOT_OPT_COOKIE_CLNT + KNOT_OPT_COOKIE_SRVR_MAX))) - -#define ccookie_len_ok(cclen) \ - ((cclen) == KNOT_OPT_COOKIE_CLNT) - -#define scookie_len_ok(sclen) \ - (((sclen) == 0) || \ - ((sclen) >= KNOT_OPT_COOKIE_SRVR_MIN && \ - (sclen) <= KNOT_OPT_COOKIE_SRVR_MAX)) - -_public_ -uint16_t knot_edns_opt_cookie_data_len(uint16_t clen, uint16_t slen) -{ - return (ccookie_len_ok(clen) && scookie_len_ok(slen)) ? (clen + slen) : 0; -} - -_public_ -uint16_t knot_edns_opt_cookie_write(const uint8_t *cc, uint16_t cc_len, - const uint8_t *sc, uint16_t sc_len, - uint8_t *data, uint16_t data_len) -{ - if ((cc == NULL && cc_len > 0) || (sc == NULL && sc_len > 0)) { - return 0; - } - - if (data == NULL || data_len == 0) { - return 0; - } - - uint16_t cookies_size = knot_edns_opt_cookie_data_len(cc_len, sc_len); - if (cookies_size == 0 || data_len < cookies_size) { - return 0; - } - - wire_ctx_t wire = wire_ctx_init(data, data_len); - wire_ctx_write(&wire, cc, cc_len); - if (sc_len > 0) { - wire_ctx_write(&wire, sc, sc_len); - } - - if (wire.error != KNOT_EOK) { - return 0; - } - - return wire_ctx_offset(&wire); -} - -_public_ -int knot_edns_opt_cookie_parse(const uint8_t *data, uint16_t data_len, - const uint8_t **cc, uint16_t *cc_len, - const uint8_t **sc, uint16_t *sc_len) -{ - if (data == NULL) { - return KNOT_EINVAL; - } - - if (!cookie_len_ok(data_len)) { - return KNOT_EMALF; - } - - if (cc != NULL && cc_len != NULL) { - *cc_len = KNOT_OPT_COOKIE_CLNT; - *cc = data; - } - - if (sc != NULL && sc_len != NULL) { - *sc_len = data_len - KNOT_OPT_COOKIE_CLNT; - *sc = (*sc_len == 0) ? NULL : (data + KNOT_OPT_COOKIE_CLNT); - } - - return KNOT_EOK; -} diff --git a/src/libknot/rrtype/opt-cookie.h b/src/libknot/rrtype/opt-cookie.h deleted file mode 100644 index 750630553cc616e63e8f97d7b24b14a00674454a..0000000000000000000000000000000000000000 --- a/src/libknot/rrtype/opt-cookie.h +++ /dev/null @@ -1,75 +0,0 @@ -/* 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 <stdint.h> - -#define KNOT_OPT_COOKIE_MIN 8 -#define KNOT_OPT_COOKIE_CLNT KNOT_OPT_COOKIE_MIN -#define KNOT_OPT_COOKIE_SRVR_MIN 8 -#define KNOT_OPT_COOKIE_SRVR_MAX 32 - -/*! - * \brief Returns the size of the buffer required to store the cookie. - * - * \note The value of \a clen and \a slen must be within defined limits. - * - * \param clen Client cookie portion length. - * \param slen Server cookie portion length. - * - * \retval <> 0 if the supplied arguments are within limits - * \retval 0 if the supplied parameters violate the requirements - */ -uint16_t knot_edns_opt_cookie_data_len(uint16_t clen, uint16_t slen); - -/*! - * \brief Write cookie wire data. - * - * \param cc Client cookie. - * \param cc_len Client cookie size. - * \param sc Server cookie. - * \param sc_len Server cookie size. - * \param data Output data buffer. - * \param data_len Size of output data buffer. - * - * \retval non-zero size of written data on successful return - * \retval 0 on error - */ -uint16_t knot_edns_opt_cookie_write(const uint8_t *cc, uint16_t cc_len, - const uint8_t *sc, uint16_t sc_len, - uint8_t *data, uint16_t data_len); - -/*! - * \brief Parse cookie wire data. - * - * \note The function only sets the pointers into the buffer. It does not - * copy any data. - * - * \param data Input data buffer containing whole cookie option. - * \param data_len Length of input data buffer. - * \param cc Client cookie. - * \param cc_len Client cookie size. - * \param sc Server cookie. - * \param sc_len Server cookie size. - * - * \retval KNOT_EOK - * \retval KNOT_EINVAL - * \retval KNOT_EMALF - */ -int knot_edns_opt_cookie_parse(const uint8_t *data, uint16_t data_len, - const uint8_t **cc, uint16_t *cc_len, - const uint8_t **sc, uint16_t *sc_len); diff --git a/src/libknot/rrtype/opt.c b/src/libknot/rrtype/opt.c index ec0ea3465bb485f6b823cfad351f83748d71bc72..9de8ce060db8a54e7148e6a996540f2c96153728 100644 --- a/src/libknot/rrtype/opt.c +++ b/src/libknot/rrtype/opt.c @@ -835,3 +835,71 @@ int knot_edns_chain_parse(knot_dname_t **point, const uint8_t *option, return KNOT_EOK; } + +_public_ +uint16_t knot_edns_cookie_size(const knot_edns_cookie_t *cc, + const knot_edns_cookie_t *sc) +{ + if (cc == NULL || cc->len != KNOT_EDNS_COOKIE_CLNT_SIZE) { + return 0; + } else if (sc == NULL || sc->len == 0) { + return KNOT_EDNS_COOKIE_CLNT_SIZE; + } else if (sc->len < KNOT_EDNS_COOKIE_SRVR_MIN_SIZE || + sc->len > KNOT_EDNS_COOKIE_SRVR_MAX_SIZE) { + return 0; + } else { + return cc->len + sc->len; + } +} + +_public_ +int knot_edns_cookie_write(uint8_t *option, size_t option_len, + const knot_edns_cookie_t *cc, + const knot_edns_cookie_t *sc) +{ + if (option == NULL || cc == NULL || cc->len != KNOT_EDNS_COOKIE_CLNT_SIZE) { + return KNOT_EINVAL; + } + + wire_ctx_t wire = wire_ctx_init(option, option_len); + wire_ctx_write(&wire, cc->data, cc->len); + + if (sc != NULL && sc->len > 0) { + if (sc->len < KNOT_EDNS_COOKIE_SRVR_MIN_SIZE || + sc->len > KNOT_EDNS_COOKIE_SRVR_MAX_SIZE) { + return KNOT_EINVAL; + } + wire_ctx_write(&wire, sc->data, sc->len); + } + + return wire.error; +} + +_public_ +int knot_edns_cookie_parse(knot_edns_cookie_t *cc, knot_edns_cookie_t *sc, + const uint8_t *option, uint16_t option_len) +{ + if (cc == NULL || sc == NULL || option == NULL) { + return KNOT_EINVAL; + } + + if (option_len != KNOT_EDNS_COOKIE_CLNT_SIZE && + (option_len < KNOT_EDNS_COOKIE_CLNT_SIZE + KNOT_EDNS_COOKIE_SRVR_MIN_SIZE || + option_len > KNOT_EDNS_COOKIE_CLNT_SIZE + KNOT_EDNS_COOKIE_SRVR_MAX_SIZE)) { + return KNOT_EMALF; + } + assert(option_len >= KNOT_EDNS_COOKIE_CLNT_SIZE); + + memcpy(cc->data, option, KNOT_EDNS_COOKIE_CLNT_SIZE); + cc->len = KNOT_EDNS_COOKIE_CLNT_SIZE; + + size_t sc_len = option_len - KNOT_EDNS_COOKIE_CLNT_SIZE; + if (sc_len == 0) { + sc->len = 0; + } else { + memcpy(sc->data, option + KNOT_EDNS_COOKIE_CLNT_SIZE, sc_len); + sc->len = sc_len; + } + + return KNOT_EOK; +} diff --git a/src/libknot/rrtype/opt.h b/src/libknot/rrtype/opt.h index 8ebd72e2eaa03ed254d681c760fac1869fccce6b..ba5889c9b2934d6d99af286e17cf53679cf93b28 100644 --- a/src/libknot/rrtype/opt.h +++ b/src/libknot/rrtype/opt.h @@ -55,9 +55,16 @@ enum knot_edns_const { /*! \brief Maximal size of EDNS client subnet address in bytes (IPv6). */ KNOT_EDNS_CLIENT_SUBNET_ADDRESS_MAXLEN = 16, + /*! \brief EDNS client cookie size. */ + KNOT_EDNS_COOKIE_CLNT_SIZE = 8, + /*! \brief EDNS minimum server cookie size. */ + KNOT_EDNS_COOKIE_SRVR_MIN_SIZE = 8, + /*! \brief EDNS maximum server cookie size. */ + KNOT_EDNS_COOKIE_SRVR_MAX_SIZE = 32, + /*! \brief NSID option code. */ KNOT_EDNS_OPTION_NSID = 3, - /*! \brief EDNS client subnet option code. */ + /*! \brief EDNS Client subnet option code. */ KNOT_EDNS_OPTION_CLIENT_SUBNET = 8, /*! \brief EDNS DNS Cookie option code. */ KNOT_EDNS_OPTION_COOKIE = 10, @@ -534,4 +541,50 @@ int knot_edns_chain_write(uint8_t *option, size_t option_len, int knot_edns_chain_parse(knot_dname_t **point, const uint8_t *option, uint16_t option_len); +/*! + * \brief DNS Cookie content. + */ +typedef struct { + uint8_t data[KNOT_EDNS_COOKIE_SRVR_MAX_SIZE]; /*!< Cookie data. */ + uint16_t len; /*!< Cookie length. */ +} knot_edns_cookie_t; + +/*! + * \brief Get size of the EDNS Cookie option wire size. + * + * \param[in] cc Client cookie. + * \param[in] sc Server cookie (can be NULL). + * + * \return Size of the EDNS option data or 0 if invalid input. + */ +uint16_t knot_edns_cookie_size(const knot_edns_cookie_t *cc, + const knot_edns_cookie_t *sc); + +/*! + * \brief Writes EDNS cookie wire data. + * + * \param[out] option EDNS option data buffer. + * \param[in] option_len EDNS option data buffer size. + * \param[in] cc EDNS client cookie. + * \param[in] sc EDNS server cookie (can be NULL). + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_edns_cookie_write(uint8_t *option, size_t option_len, + const knot_edns_cookie_t *cc, + const knot_edns_cookie_t *sc); + +/*! + * \brief Parses EDNS Cookie wire data. + * + * \param[out] cc EDNS client cookie. + * \param[out] sc EDNS server cookie. + * \param[in] option EDNS option data. + * \param[in] option_len EDNS option size. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_edns_cookie_parse(knot_edns_cookie_t *cc, knot_edns_cookie_t *sc, + const uint8_t *option, uint16_t option_len); + /*! @} */ diff --git a/tests/.gitignore b/tests/.gitignore index 2926e6405eeebcdfb84a20cbf734f3e952136976..a62ff73e30770c5e3770b69eb4404455ef0a321f 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -19,9 +19,7 @@ /contrib/test_wire_ctx /libknot/test_control -/libknot/test_cookies-client -/libknot/test_cookies-opt -/libknot/test_cookies-server +/libknot/test_cookies /libknot/test_db /libknot/test_descriptor /libknot/test_dname diff --git a/tests/Makefile.am b/tests/Makefile.am index 322aedbbd0a7fbfe3e2d250b4d2ea2d1ab273419..4b12dfa5c63e3e0b946647f3ca0bac794b48de66 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -30,9 +30,7 @@ check_PROGRAMS = \ check_PROGRAMS += \ libknot/test_control \ - libknot/test_cookies-client \ - libknot/test_cookies-opt \ - libknot/test_cookies-server \ + libknot/test_cookies \ libknot/test_db \ libknot/test_descriptor \ libknot/test_dname \ diff --git a/tests/libknot/test_cookies-client.c b/tests/libknot/test_cookies-client.c deleted file mode 100644 index 7c9e726466a9fb57f8f069ddd69c49f716d6aceb..0000000000000000000000000000000000000000 --- a/tests/libknot/test_cookies-client.c +++ /dev/null @@ -1,156 +0,0 @@ -/* Copyright (C) 2017 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 <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <tap/basic.h> - -#include "contrib/endian.h" -#include "contrib/openbsd/siphash.h" -#include "contrib/sockaddr.h" -#include "libknot/consts.h" -#include "libknot/cookies/client.h" -#include "libknot/cookies/client.c" -#include "libknot/errcode.h" - -int main(int argc, char *argv[]) -{ - plan_lazy(); - - int ret; - - SIPHASH_KEY secret = { - .k0 = htole64(0xDEADBEEFFACE), - .k1 = htole64(0xFACEDEADBEEF) - }; - - uint64_t hash; - - - struct knot_cc_input cc_in = { 0 }; - - struct sockaddr_storage unspec_sa = { 0 }; - - struct sockaddr_storage c4_sa = { 0 }; - struct sockaddr_storage s4_sa = { 0 }; - sockaddr_set(&c4_sa, AF_INET, "127.0.0.1", 0); - sockaddr_set(&s4_sa, AF_INET, "10.0.0.1", 0); - - struct sockaddr_storage c6_sa = { 0 }; - struct sockaddr_storage s6_sa = { 0 }; - sockaddr_set(&c6_sa, AF_INET6, "2001:db8:8714:3a90::12", 0); - sockaddr_set(&s6_sa, AF_INET6, "::1", 0); - - /* Client cookie hash algorithm. */ - - memset(&cc_in, 0, sizeof(cc_in)); - cc_in.clnt_sockaddr = (struct sockaddr *)&c4_sa; - cc_in.srvr_sockaddr = (struct sockaddr *)&s4_sa; - cc_in.secret = secret; - hash = generate_client_cookie(&cc_in); - { - uint64_t expected = 0xde3832f4f59bf5ab; - ok(hash == expected, "cookies: SipHash client cookie content"); - } - - memset(&cc_in, 0, sizeof(cc_in)); - cc_in.clnt_sockaddr = NULL; - cc_in.srvr_sockaddr = (struct sockaddr *)&s4_sa; - cc_in.secret = secret; - hash = generate_client_cookie(&cc_in); - { - uint64_t expected = 0x6b636ff225a1b340; - ok(hash == expected, "cookies: SipHash client cookie content"); - } - - memset(&cc_in, 0, sizeof(cc_in)); - cc_in.clnt_sockaddr = (struct sockaddr *)&unspec_sa; - cc_in.srvr_sockaddr = (struct sockaddr *)&s4_sa; - cc_in.secret = secret; - hash = generate_client_cookie(&cc_in); - { - uint64_t expected = 0x6b636ff225a1b340; - ok(hash == expected, "cookies: SipHash client cookie content"); - } - - memset(&cc_in, 0, sizeof(cc_in)); - cc_in.clnt_sockaddr = (struct sockaddr *)&c4_sa; - cc_in.srvr_sockaddr = NULL; - cc_in.secret = secret; - hash = generate_client_cookie(&cc_in); - { - uint64_t expected = 0xd713ab1a81179bb3; - ok(hash == expected, "cookies: SipHash client cookie content"); - } - - memset(&cc_in, 0, sizeof(cc_in)); - cc_in.clnt_sockaddr = (struct sockaddr *)&c4_sa; - cc_in.srvr_sockaddr = (struct sockaddr *)&unspec_sa; - cc_in.secret = secret; - hash = generate_client_cookie(&cc_in); - { - uint64_t expected = 0xd713ab1a81179bb3; - ok(hash == expected, "cookies: SipHash client cookie content"); - } - - /* Client cookie check. */ - - memset(&cc_in, 0, sizeof(cc_in)); - cc_in.clnt_sockaddr = (struct sockaddr *)&c6_sa; - cc_in.srvr_sockaddr = (struct sockaddr *)&s6_sa; - cc_in.secret = secret; - { - ret = knot_cc_check(NULL, 0, &cc_in); - is_int(KNOT_EINVAL, ret, "cookies: SipHash client cookie check no cookie"); - } - - { - uint8_t cookie[] = { 0xaf, 0xe5, 0x17, 0x94, 0x80, 0xa6, 0x0c, 0x33 }; - ret = knot_cc_check(cookie, sizeof(cookie), NULL); - is_int(KNOT_EINVAL, ret, "cookies: SipHash client cookie check no input"); - } - - memset(&cc_in, 0, sizeof(cc_in)); - cc_in.clnt_sockaddr = (struct sockaddr *)&c6_sa; - cc_in.srvr_sockaddr = (struct sockaddr *)&s6_sa; - cc_in.secret = secret; - { - uint64_t cookie = 0xf99dbd02b69ab3c2; - ret = knot_cc_check((const uint8_t *)&cookie, sizeof(cookie), &cc_in); - is_int(KNOT_EOK, ret, "cookies: SipHash client good cookie check"); - } - - memset(&cc_in, 0, sizeof(cc_in)); - cc_in.clnt_sockaddr = (struct sockaddr *)&c6_sa; - cc_in.srvr_sockaddr = (struct sockaddr *)&s6_sa; - cc_in.secret = secret; - { - uint64_t cookie = 0xf99dbd02b69ab3c2; - ret = knot_cc_check((const uint8_t *)&cookie, sizeof(cookie) - 1, &cc_in); - is_int(KNOT_EINVAL, ret, "cookies: SipHash client cookie check invalid length"); - } - - memset(&cc_in, 0, sizeof(cc_in)); - cc_in.clnt_sockaddr = (struct sockaddr *)&c6_sa; - cc_in.srvr_sockaddr = (struct sockaddr *)&s6_sa; - cc_in.secret = secret; - { - uint8_t cookie[] = { 0xaf, 0xe5, 0x17, 0x94, 0x80, 0xa6, 0x0c, 0x32 }; - ret = knot_cc_check((const uint8_t *)&cookie, sizeof(cookie), &cc_in); - is_int(KNOT_EINVAL, ret, "cookies: SipHash client cookie check invalid cookie"); - } -} diff --git a/tests/libknot/test_cookies-opt.c b/tests/libknot/test_cookies-opt.c deleted file mode 100644 index 8cc0dfe5bf7d59161bf2d97b439cde6f6e6ef2a8..0000000000000000000000000000000000000000 --- a/tests/libknot/test_cookies-opt.c +++ /dev/null @@ -1,155 +0,0 @@ -/* 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 <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <tap/basic.h> - -#include "libknot/consts.h" -#include "libknot/errcode.h" -#include "libknot/rrtype/opt.h" -#include "libknot/rrtype/opt-cookie.h" - -const char *cookie_opts[] = { - "\x00\x0a" "\x00\x00", /* Zero length cookie. */ - "\x00\x0a" "\x00\x01" "\x00", /* Short client cookie. */ - "\x00\x0a" "\x00\x07" "\x00\x01\x02\x03\x04\x05\x06", /* Short client cookie. */ - "\x00\x0a" "\x00\x09" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08", /* Short server cookie. */ - "\x00\x0a" "\x00\x0f" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e", /* Short server cookie. */ - "\x00\x0a" "\x00\x29" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28", /* Long server cookie. */ - "\x00\x0a" "\x00\x08" "\x00\x01\x02\x03\x04\x05\x06\x07", /* Only client cookie. */ - "\x00\x0a" "\x00\x10" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", /* 8 octets long server cookie. */ - "\x00\x0a" "\x00\x28" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27" /* 32 octets long server cookie. */ -}; - -#define ROPT(i) ((const uint8_t *)cookie_opts[(i)]) - -static void get_opt_data(const uint8_t *opt, - const uint8_t **data, uint16_t *data_len) -{ - if (opt == NULL) { - *data = NULL; - *data_len = 0; - } - - *data = knot_edns_opt_get_data((uint8_t *)opt); - *data_len = knot_edns_opt_get_length((uint8_t *)opt); -} - -int main(int argc, char *argv[]) -{ - plan_lazy(); - - uint16_t code; - uint16_t data_len; - const uint8_t *data; - int ret; - - const uint8_t *cc, *sc; - uint16_t cc_len, sc_len; - - code = knot_edns_opt_get_code(ROPT(0)); - ok(code == KNOT_EDNS_OPTION_COOKIE, "cookies: EDNS OPT code"); - - data_len = knot_edns_opt_get_length(ROPT(1)); - ok(data_len == 1, "cookies: EDNS OPT length"); - - /* Should return pointer to data, although option has zero length. */ - data = knot_edns_opt_get_data((uint8_t *)ROPT(0)); - ok(data != NULL, "cookies: EDNS OPT zero data"); - - data = knot_edns_opt_get_data((uint8_t *)ROPT(1)); - ok(data != NULL, "cookies: EDNS OPT data"); - - ret = knot_edns_opt_cookie_parse(NULL, 0, NULL, NULL, NULL, NULL); - is_int(KNOT_EINVAL, ret, "cookies: EDNS OPT parse NULL"); - - /* Malformed cookies. */ - - get_opt_data(ROPT(0), &data, &data_len); - ret = knot_edns_opt_cookie_parse(data, data_len, NULL, NULL, NULL, NULL); - is_int(KNOT_EMALF, ret, "cookies: EDNS OPT parse zero length"); - - get_opt_data(ROPT(1), &data, &data_len); - ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len, &sc, &sc_len); - is_int(KNOT_EMALF, ret, "cookies: EDNS OPT parse 1B (short) cookie"); - - get_opt_data(ROPT(2), &data, &data_len); - ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len, &sc, &sc_len); - is_int(KNOT_EMALF, ret, "cookies: EDNS OPT parse 7B (short) cookie"); - - get_opt_data(ROPT(3), &data, &data_len); - ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len, &sc, &sc_len); - is_int(KNOT_EMALF, ret, "cookies: EDNS OPT parse 9B (short) cookie"); - - get_opt_data(ROPT(4), &data, &data_len); - ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len, &sc, &sc_len); - is_int(KNOT_EMALF, ret, "cookies: EDNS OPT parse 15B (short) cookie"); - - get_opt_data(ROPT(5), &data, &data_len); - ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len, &sc, &sc_len); - is_int(KNOT_EMALF, ret, "cookies: EDNS OPT parse 41B (long) cookie"); - - get_opt_data(ROPT(5), &data, &data_len); - ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len, &sc, &sc_len); - is_int(KNOT_EMALF, ret, "cookies: EDNS OPT parse 41B (long) cookie"); - - /* Testing combination of output parameters. */ - - cc = sc = NULL; - cc_len = sc_len = 0; - get_opt_data(ROPT(7), &data, &data_len); - ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len, NULL, NULL); - ok(ret == KNOT_EOK && cc != NULL && cc_len == 8, "cookies: EDNS OPT parse client cookie"); - - cc = sc = NULL; - cc_len = sc_len = 0; - get_opt_data(ROPT(7), &data, &data_len); - ret = knot_edns_opt_cookie_parse(data, data_len, NULL, NULL, &sc, &sc_len); - ok(ret == KNOT_EOK && sc != NULL && sc_len == 8, "cookies: EDNS OPT parse server cookie"); - - /* Valid cookies. */ - - const void *DUMMYPTR = (void *)1; - const int DUMMYVAL = 1; - - cc = sc = DUMMYPTR; - cc_len = sc_len = DUMMYVAL; - get_opt_data(ROPT(6), &data, &data_len); - ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len, &sc, &sc_len); - ok(ret == KNOT_EOK && - cc != NULL && cc != DUMMYPTR && cc_len == 8 && - sc == NULL && sc_len == 0, "cookies: EDNS OPT parse 8B cookie"); - - cc = sc = DUMMYPTR; - cc_len = sc_len = DUMMYVAL; - get_opt_data(ROPT(7), &data, &data_len); - ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len, &sc, &sc_len); - ok(ret == KNOT_EOK && - cc != NULL && cc != DUMMYPTR && cc_len == 8 && - sc != NULL && sc != DUMMYPTR && sc_len == 8, "cookies: EDNS OPT parse 16B cookie"); - - cc = sc = DUMMYPTR; - cc_len = sc_len = DUMMYVAL; - get_opt_data(ROPT(8), &data, &data_len); - ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len, &sc, &sc_len); - ok(ret == KNOT_EOK && - cc != NULL && cc != DUMMYPTR && cc_len == 8 && - sc != NULL && sc != DUMMYPTR && sc_len == 32, "cookies: EDNS OPT parse 40B cookie"); - - return 0; -} diff --git a/tests/libknot/test_cookies-server.c b/tests/libknot/test_cookies-server.c deleted file mode 100644 index a235f7cb21f5f9076245a4ffd172ffbebfacf458..0000000000000000000000000000000000000000 --- a/tests/libknot/test_cookies-server.c +++ /dev/null @@ -1,291 +0,0 @@ -/* Copyright (C) 2017 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 <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <tap/basic.h> - -#include "contrib/endian.h" -#include "contrib/openbsd/siphash.h" -#include "contrib/sockaddr.h" -#include "libknot/consts.h" -#include "libknot/cookies/server.h" -#include "libknot/cookies/server.c" -#include "libknot/errcode.h" -#include "libknot/rrtype/opt.h" -#include "libknot/rrtype/opt-cookie.h" - -const char *cookie_opts[] = { - "\x00\x0a" "\x00\x10" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x19\x99\xc3\xbc\x4e\x95\xd9\xdf", /* 8 octets long wrong server cookie. */ - "\x00\x0a" "\x00\x10" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x69\xf4\xac\x05\x90\x6c\xac\x33", /* 8 octets long OK server cookie. */ - "\x00\x0a" "\x00\x18" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x39\x88\x78\x19\xec\xdb\xbd\xbf", /* 8B nonce 8B hash long wrong server cookie. */ - "\x00\x0a" "\x00\x18" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\xc5\xe0\x47\x89\xe9\x77\x32\xd2"/* 8B nonce 8B hash long OK server cookie. */ -}; - -#define ROPT(i) ((const uint8_t *)cookie_opts[(i)]) - -static void get_opt_cookies(const uint8_t *opt, struct knot_dns_cookies *cookies) -{ - memset(cookies, 0, sizeof(*cookies)); - - if (opt == NULL) { - return; - } - - const uint8_t *data = knot_edns_opt_get_data((uint8_t *)opt); - uint16_t data_len = knot_edns_opt_get_length((uint8_t *)opt); - - knot_edns_opt_cookie_parse(data, data_len, - &cookies->cc, &cookies->cc_len, - &cookies->sc, &cookies->sc_len); -} - -int main(int argc, char *argv[]) -{ - plan_lazy(); - - int ret; - - const uint8_t sc0[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; - const uint8_t sc1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17 }; - - const uint8_t cc[] = { 0, 1, 2, 3, 4, 5, 6, 7 }; - - SIPHASH_KEY secret = { - .k0 = htole64(0xDEADBEEFFACE), - .k1 = htole64(0xFACEDEADBEEF) - }; - - SIPHASH_KEY empty = { 0 }; - - const uint8_t nonce[] = { 10, 11, 12, 13, 14, 15, 16, 17 }; - - uint64_t hash; - - struct knot_sc_content sc_content; - - struct knot_sc_private srvr_data = { 0 }; - struct knot_sc_input sc_in = { 0 }; - - struct sockaddr_storage unspec_sa = { 0 }; - - struct sockaddr_storage c4_sa = { 0 }; - struct sockaddr_storage s4_sa = { 0 }; - sockaddr_set(&c4_sa, AF_INET, "127.0.0.1", 0); - sockaddr_set(&s4_sa, AF_INET, "10.0.0.1", 0); - - struct sockaddr_storage c6_sa = { 0 }; - struct sockaddr_storage s6_sa = { 0 }; - sockaddr_set(&c6_sa, AF_INET6, "2001:db8:8714:3a90::12", 0); - sockaddr_set(&s6_sa, AF_INET6, "::1", 0); - - struct knot_dns_cookies cookies; - - /* Server cookie hash algorithm. */ - - memset(&sc_in, 0, sizeof(sc_in)); - srvr_data.clnt_sockaddr = (struct sockaddr *)&c4_sa; - srvr_data.secret = secret; - sc_in.cc = cc; - sc_in.cc_len = sizeof(cc); - sc_in.nonce = nonce; - sc_in.nonce_len = sizeof(nonce); - sc_in.srvr_data = &srvr_data; - hash = generate_server_cookie(&sc_in); - { - uint64_t expected = 0xbb08ce797dade217; - ok(0 == memcmp(&expected, &hash, SERVER_HASH_LEN), "cookies: SipHash server cookie content"); - } - - memset(&sc_in, 0, sizeof(sc_in)); - srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa; - srvr_data.secret = secret; - sc_in.cc = cc; - sc_in.cc_len = sizeof(cc); - sc_in.nonce = nonce; - sc_in.nonce_len = sizeof(nonce); - sc_in.srvr_data = &srvr_data; - hash = generate_server_cookie(&sc_in); - { - uint64_t expected = 0xd23277e98947e0c5; - ok(0 == memcmp(&expected, &hash, SERVER_HASH_LEN), "cookies: SipHash server cookie content"); - } - - memset(&sc_in, 0, sizeof(sc_in)); - srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa; - srvr_data.secret = secret; - sc_in.cc = cc; - sc_in.cc_len = sizeof(cc); - sc_in.nonce = NULL; - sc_in.nonce_len = 0; - sc_in.srvr_data = &srvr_data; - hash = generate_server_cookie(&sc_in); - { - uint64_t expected = 0x33ac6c9005acf469; - ok(0 == memcmp(&expected, &hash, SERVER_HASH_LEN), "cookies: SipHash server cookie content"); - } - - memset(&sc_in, 0, sizeof(sc_in)); - srvr_data.clnt_sockaddr = (struct sockaddr *)&unspec_sa; - srvr_data.secret = secret; - sc_in.cc = cc; - sc_in.cc_len = sizeof(cc); - sc_in.nonce = nonce; - sc_in.nonce_len = sizeof(nonce); - sc_in.srvr_data = &srvr_data; - hash = generate_server_cookie(&sc_in); - { - uint64_t expected = 0x5732e9d1127281e9; - ok(0 == memcmp(&expected, &hash, SERVER_HASH_LEN), "cookies: SipHash server cookie content"); - } - - memset(&sc_in, 0, sizeof(sc_in)); - srvr_data.clnt_sockaddr = (struct sockaddr *)&unspec_sa; - srvr_data.secret = secret; - sc_in.cc = cc; - sc_in.cc_len = sizeof(cc) - 1; - sc_in.nonce = nonce; - sc_in.nonce_len = sizeof(nonce); - sc_in.srvr_data = &srvr_data; - hash = generate_server_cookie(&sc_in); - { - uint64_t expected = 0x98a4bf0c9fb53340; - ok(0 == memcmp(&expected, &hash, SERVER_HASH_LEN), "cookies: SipHash server cookie content"); - } - - memset(&sc_in, 0, sizeof(sc_in)); - srvr_data.clnt_sockaddr = (struct sockaddr *)&unspec_sa; - srvr_data.secret = secret; - sc_in.cc = cc; - sc_in.cc_len = sizeof(cc); - sc_in.nonce = nonce; - sc_in.nonce_len = sizeof(nonce) - 1; - sc_in.srvr_data = &srvr_data; - hash = generate_server_cookie(&sc_in); - { - uint64_t expected = 0xdb33fa2bb43d9ac5; - ok(0 == memcmp(&expected, &hash, SERVER_HASH_LEN), "cookies: SipHash server cookie content"); - } - - /* Server cookie parse. */ - - const void *DUMMYPTR = (void *)1; - const int DUMMYVAL = 1; - - ret = knot_sc_parse(0, NULL, 0, &sc_content); - is_int(KNOT_EINVAL, ret, "cookies: parse server cookie no cookie"); - - ret = knot_sc_parse(0, sc0, sizeof(sc0), NULL); - is_int(KNOT_EINVAL, ret, "cookies: parse server cookie no content"); - - ret = knot_sc_parse(sizeof(sc0), sc0, sizeof(sc0), &sc_content); - is_int(KNOT_EINVAL, ret, "cookies: parse server cookie too large nonce"); - - sc_content.nonce = sc_content.hash = DUMMYPTR; - sc_content.nonce_len = sc_content.hash_len = DUMMYVAL; - ret = knot_sc_parse(0, sc0, sizeof(sc0), &sc_content); - ok(ret == KNOT_EOK && - sc_content.nonce == NULL && sc_content.nonce_len == 0 && - sc_content.hash == sc0 && sc_content.hash_len == sizeof(sc0), "cookies: parse server cookie 0B nonce"); - - sc_content.nonce = sc_content.hash = DUMMYPTR; - sc_content.nonce_len = sc_content.hash_len = DUMMYVAL; - ret = knot_sc_parse(8, sc1, sizeof(sc1), &sc_content); - ok(ret == KNOT_EOK && - sc_content.nonce == sc1 && sc_content.nonce_len == 8 && - sc_content.hash == (sc1 + 8) && sc_content.hash_len == 8, "cookies: parse server cookie 8B nonce"); - - /* Server cookie check. */ - - get_opt_cookies(ROPT(1), &cookies); - srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa; - srvr_data.secret = secret; - ret = knot_sc_check(1, &cookies, &srvr_data); - is_int(KNOT_EINVAL, ret, "cookies: SipHash server cookie check - wrong nonce length"); - - get_opt_cookies(ROPT(1), &cookies); - srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa; - srvr_data.secret = secret; - ret = knot_sc_check(17, &cookies, &srvr_data); - is_int(KNOT_EINVAL, ret, "cookies: SipHash server cookie check - too long nonce"); - - get_opt_cookies(ROPT(1), &cookies); - srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa; - srvr_data.secret = secret; - ret = knot_sc_check(0, NULL, &srvr_data); - is_int(KNOT_EINVAL, ret, "cookies: SipHash server cookie check - no cookies"); - - get_opt_cookies(ROPT(1), &cookies); - cookies.cc = NULL; - cookies.cc_len = 0; - srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa; - srvr_data.secret = secret; - ret = knot_sc_check(0, NULL, &srvr_data); - is_int(KNOT_EINVAL, ret, "cookies: SipHash server cookie check - no client cookie"); - - get_opt_cookies(ROPT(1), &cookies); - cookies.sc = NULL; - cookies.sc_len = 0; - srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa; - srvr_data.secret = secret; - ret = knot_sc_check(0, NULL, &srvr_data); - is_int(KNOT_EINVAL, ret, "cookies: SipHash server cookie check - no server cookie"); - - get_opt_cookies(ROPT(1), &cookies); - srvr_data.clnt_sockaddr = NULL; - srvr_data.secret = secret; - ret = knot_sc_check(0, NULL, &srvr_data); - is_int(KNOT_EINVAL, ret, "cookies: SipHash server cookie check - no socket address"); - - get_opt_cookies(ROPT(1), &cookies); - srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa; - srvr_data.secret = empty; - ret = knot_sc_check(0, &cookies, &srvr_data); - is_int(KNOT_EINVAL, ret, "cookies: SipHash server cookie check - no secret"); - - get_opt_cookies(ROPT(0), &cookies); - srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa; - srvr_data.secret = secret; - ret = knot_sc_check(0, &cookies, &srvr_data); - is_int(KNOT_EINVAL, ret, "cookies: SipHash server cookie check - bad server cookie"); - - get_opt_cookies(ROPT(1), &cookies); - srvr_data.clnt_sockaddr = (struct sockaddr *)&unspec_sa; - srvr_data.secret = secret; - ret = knot_sc_check(0, &cookies, &srvr_data); - is_int(KNOT_EINVAL, ret, "cookies: SipHash server cookie check - bad socket"); - - get_opt_cookies(ROPT(1), &cookies); - srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa; - srvr_data.secret = secret; - ret = knot_sc_check(0, &cookies, &srvr_data); - is_int(KNOT_EOK, ret, "cookies: SipHash server cookie check"); - - get_opt_cookies(ROPT(2), &cookies); - srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa; - srvr_data.secret = secret; - ret = knot_sc_check(8, &cookies, &srvr_data); - is_int(KNOT_EINVAL, ret, "cookies: SipHash server cookie check - bad server cookie"); - - get_opt_cookies(ROPT(3), &cookies); - srvr_data.clnt_sockaddr = (struct sockaddr *)&c6_sa; - srvr_data.secret = secret; - ret = knot_sc_check(8, &cookies, &srvr_data); - is_int(KNOT_EOK, ret, "cookies: SipHash server cookie check"); - - return 0; -} diff --git a/tests/libknot/test_cookies.c b/tests/libknot/test_cookies.c new file mode 100644 index 0000000000000000000000000000000000000000..98e8cafa0506d038f509d5cf3378b367ffd97570 --- /dev/null +++ b/tests/libknot/test_cookies.c @@ -0,0 +1,176 @@ +/* Copyright (C) 2017 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 <stdlib.h> +#include <string.h> +#include <tap/basic.h> + +#include "libknot/cookies.h" +#include "libknot/errcode.h" +#include "contrib/sockaddr.h" + +static void client_generate(struct sockaddr_storage *c_addr, struct sockaddr_storage *s_addr, + const uint8_t *secret, const char *msg, int code, uint64_t le_cc) +{ + knot_edns_cookie_params_t params = { + .client_addr = (struct sockaddr *)c_addr, + .server_addr = (struct sockaddr *)s_addr, + }; + memcpy(params.secret, secret, sizeof(params.secret)); + + knot_edns_cookie_t cc; + int ret = knot_edns_cookie_client_generate(&cc, ¶ms); + is_int(ret, code, "client_generate ret: %s", msg); + if (ret == KNOT_EOK) { + uint64_t ref = le64toh(le_cc); + ok(cc.len == sizeof(ref) && memcmp(cc.data, &ref, cc.len) == 0, + "client_generate value: %s", msg); + } +} + +static void server_generate(struct sockaddr_storage *c_addr, const uint8_t *secret, + const knot_edns_cookie_t *cc, const char *msg, int code, + uint64_t le_sc) +{ + knot_edns_cookie_params_t params = { + .client_addr = (struct sockaddr *)c_addr, + }; + memcpy(params.secret, secret, sizeof(params.secret)); + + knot_edns_cookie_t sc; + int ret = knot_edns_cookie_server_generate(&sc, cc, ¶ms); + is_int(ret, code, "server_generate ret: %s", msg); + if (ret == KNOT_EOK) { + uint64_t ref = le64toh(le_sc); + ok(sc.len == sizeof(ref) && memcmp(sc.data, &ref, sc.len) == 0, + "server_generate value: %s", msg); + } +} + +static void client_check(struct sockaddr_storage *c_addr, struct sockaddr_storage *s_addr, + const uint8_t *secret, const char *msg, uint16_t le_cc_len, + uint64_t le_cc, int code) +{ + knot_edns_cookie_params_t params = { + .client_addr = (struct sockaddr *)c_addr, + .server_addr = (struct sockaddr *)s_addr, + }; + if (secret != NULL) { + memcpy(params.secret, secret, sizeof(params.secret)); + } + + uint64_t ref = le64toh(le_cc); + knot_edns_cookie_t cc = { + .len = le_cc_len + }; + memcpy(cc.data, &ref, le_cc_len); + + int ret = knot_edns_cookie_client_check(&cc, ¶ms); + is_int(ret, code, "client_check ret: %s", msg); +} + +static void server_check(struct sockaddr_storage *c_addr, const uint8_t *secret, + const char *msg, uint16_t le_cc_len, uint64_t le_cc, + uint16_t le_sc_len, uint64_t le_sc, int code) +{ + knot_edns_cookie_params_t params = { + .client_addr = (struct sockaddr *)c_addr, + }; + if (secret != NULL) { + memcpy(params.secret, secret, sizeof(params.secret)); + } + + uint64_t ref = le64toh(le_cc); + knot_edns_cookie_t cc = { + .len = le_cc_len + }; + memcpy(cc.data, &ref, le_cc_len); + + ref = le64toh(le_sc); + knot_edns_cookie_t sc = { + .len = le_sc_len + }; + memcpy(sc.data, &ref, le_sc_len); + + int ret = knot_edns_cookie_server_check(&sc, &cc, ¶ms); + is_int(ret, code, "server_check ret: %s", msg); +} + +int main(int argc, char *argv[]) +{ + plan_lazy(); + + const uint8_t secret[] = "\xCE\xFA\xEF\xBE\xAD\xDE\x00\x00\xEF\xBE\xAD\xDE\xCE\xFA\x00\x00"; + + struct sockaddr_storage unspec_sa = { 0 }; + + struct sockaddr_storage c4_sa = { 0 }; + struct sockaddr_storage s4_sa = { 0 }; + sockaddr_set(&c4_sa, AF_INET, "127.0.0.1", 0); + sockaddr_set(&s4_sa, AF_INET, "10.0.0.1", 0); + + struct sockaddr_storage c6_sa = { 0 }; + struct sockaddr_storage s6_sa = { 0 }; + sockaddr_set(&c6_sa, AF_INET6, "2001:db8:8714:3a90::12", 0); + sockaddr_set(&s6_sa, AF_INET6, "::1", 0); + + /* Client cookie generate. */ + client_generate(NULL, &s4_sa, secret, "NULL, IPv4", KNOT_EINVAL, 0); + client_generate(&c4_sa, NULL, secret, "IPv4, NULL", KNOT_EINVAL, 0); + client_generate(&c4_sa, &s4_sa, secret, "IPv4, IPv4", KNOT_EOK, 0xde3832f4f59bf5ab); + client_generate(&unspec_sa, &s4_sa, secret, "unspec, IPv4", KNOT_EOK, 0x6b636ff225a1b340); + client_generate(&c4_sa, &unspec_sa, secret, "IPv4, unspec", KNOT_EOK, 0xd713ab1a81179bb3); + + /* Client cookie check. */ + client_check(NULL, &s6_sa, secret, "no client addr", 8, 0xf99dbd02b69ab3c2, KNOT_EINVAL); + client_check(&c6_sa, NULL, secret, "no server addr", 8, 0xf99dbd02b69ab3c2, KNOT_EINVAL); + client_check(&c6_sa, &s6_sa, NULL, "no secret", 8, 0xf99dbd02b69ab3c2, KNOT_EINVAL); + client_check(&c6_sa, &s6_sa, secret, "no cookie", 0, 0, KNOT_EINVAL); + client_check(&c6_sa, &s6_sa, secret, "bad cookie length", 7, 0xf99dbd02b69ab3c2, KNOT_EINVAL); + client_check(&c6_sa, &s6_sa, secret, "invalid cookie", 8, 0, KNOT_EINVAL); + client_check(&c6_sa, &s6_sa, secret, "good cookie", 8, 0xf99dbd02b69ab3c2, KNOT_EOK); + + const knot_edns_cookie_t cc = { + .data = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }, + .len = 8 + }; + + /* Server cookie generate. */ + knot_edns_cookie_t cc_part = cc; cc_part.len--; + server_generate(NULL, secret, &cc, "NULL", KNOT_EINVAL, 0); + server_generate(&c4_sa, secret, &cc_part, "cookie part", KNOT_EINVAL, 0); + server_generate(&c4_sa, secret, &cc, "IPv4", KNOT_EOK, 0x52f86bfcc98ded6); + server_generate(&c6_sa, secret, &cc, "IPv6", KNOT_EOK, 0x33ac6c9005acf469); + server_generate(&unspec_sa, secret, &cc, "unspec", KNOT_EOK, 0x96df9dbf28f0f59e); + + /* Server cookie check. */ + server_check(NULL, secret, "no addr", 8, 0x0706050403020100, + 8, 0x33ac6c9005acf469, KNOT_EINVAL); + server_check(&c6_sa, NULL, "no secret", 8, 0x0706050403020100, + 8, 0x33ac6c9005acf469, KNOT_EINVAL); + server_check(&c6_sa, secret, "no client cookie", 0, 0, + 8, 0x33ac6c9005acf469, KNOT_EINVAL); + server_check(&c6_sa, secret, "no server cookie", 8, 0x0706050403020100, + 0, 0, KNOT_EINVAL); + server_check(&c6_sa, secret, "bad client cookie", 8, 0, + 8, 0x33ac6c9005acf469, KNOT_EINVAL); + server_check(&c6_sa, secret, "bad server cookie", 8, 0x0706050403020100, + 8, 0, KNOT_EINVAL); + server_check(&c6_sa, secret, "good cookie 1", 8, 0x0706050403020100, + 8, 0x33ac6c9005acf469, KNOT_EOK); + + return 0; +} diff --git a/tests/libknot/test_edns.c b/tests/libknot/test_edns.c index 5106a4c87b8e6134cb58a48f2d64f797b55338ae..28dfa12accddd5effb5ee3e00bea0bf6df880475 100644 --- a/tests/libknot/test_edns.c +++ b/tests/libknot/test_edns.c @@ -201,6 +201,9 @@ static void test_getters(knot_rrset_t *opt_rr) check = knot_edns_has_option(opt_rr, E_OPT4_CODE); ok(check, "OPT RR getters: empty option 2"); + + uint16_t code = knot_edns_opt_get_code((const uint8_t *)"\x00\x0a" "\x00\x00"); + ok(code == KNOT_EDNS_OPTION_COOKIE, "OPT RR getters: EDNS OPT code"); } static void test_setters(knot_rrset_t *opt_rr) @@ -845,6 +848,72 @@ static void test_chain(void) "%s: parse, malformed", __func__); } +static void check_cookie_parse(const char *opt, knot_edns_cookie_t *cc, + knot_edns_cookie_t *sc, int code, const char *msg) +{ + const uint8_t *data = NULL; + uint16_t data_len = 0; + if (opt != NULL) { + data = knot_edns_opt_get_data((uint8_t *)opt); + data_len = knot_edns_opt_get_length((uint8_t *)opt); + } + + int ret = knot_edns_cookie_parse(cc, sc, data, data_len); + is_int(code, ret, "cookie parse ret: %s", msg); +} + +static void ok_cookie_check(const char *opt, knot_edns_cookie_t *cc, + knot_edns_cookie_t *sc, uint16_t cc_len, uint16_t sc_len, + const char *msg) +{ + check_cookie_parse(opt, cc, sc, KNOT_EOK, msg); + + is_int(cc->len, cc_len, "cookie parse cc len: %s", msg); + is_int(sc->len, sc_len, "cookie parse cc len: %s", msg); + + uint16_t size = knot_edns_cookie_size(cc, sc); + is_int(size, cc_len + sc_len, "cookie len: %s", msg); + + uint8_t buf[64]; + int ret = knot_edns_cookie_write(buf, sizeof(buf), cc, sc); + is_int(KNOT_EOK, ret, "cookie write ret: %s", msg); +} + +static void test_cookie(void) +{ + const char *good[] = { + "\x00\x0a" "\x00\x08" "\x00\x01\x02\x03\x04\x05\x06\x07", /* Only client cookie. */ + "\x00\x0a" "\x00\x10" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", /* 8 octets long server cookie. */ + "\x00\x0a" "\x00\x28" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27" /* 32 octets long server cookie. */ + }; + + const char *bad[] = { + "\x00\x0a" "\x00\x00", /* Zero length cookie. */ + "\x00\x0a" "\x00\x01" "\x00", /* Short client cookie. */ + "\x00\x0a" "\x00\x07" "\x00\x01\x02\x03\x04\x05\x06", /* Short client cookie. */ + "\x00\x0a" "\x00\x09" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08", /* Short server cookie. */ + "\x00\x0a" "\x00\x0f" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e", /* Short server cookie. */ + "\x00\x0a" "\x00\x29" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28", /* Long server cookie. */ + }; + + knot_edns_cookie_t cc, sc; + + ok_cookie_check(good[0], &cc, &sc, 8, 0, "good cookie 0"); + ok_cookie_check(good[1], &cc, &sc, 8, 8, "good cookie 1"); + ok_cookie_check(good[2], &cc, &sc, 8, 32, "good cookie 2"); + + check_cookie_parse(NULL, &cc, &sc, KNOT_EINVAL, "no data"); + check_cookie_parse(good[0], NULL, &sc, KNOT_EINVAL, "no client cookie"); + check_cookie_parse(good[1], &cc, NULL, KNOT_EINVAL, "no server cookie"); + + check_cookie_parse(bad[0], &cc, &sc, KNOT_EMALF, "bad cookie 0"); + check_cookie_parse(bad[1], &cc, &sc, KNOT_EMALF, "bad cookie 1"); + check_cookie_parse(bad[2], &cc, &sc, KNOT_EMALF, "bad cookie 2"); + check_cookie_parse(bad[3], &cc, &sc, KNOT_EMALF, "bad cookie 3"); + check_cookie_parse(bad[4], &cc, &sc, KNOT_EMALF, "bad cookie 4"); + check_cookie_parse(bad[5], &cc, &sc, KNOT_EMALF, "bad cookie 5"); +} + int main(int argc, char *argv[]) { plan_lazy(); @@ -863,6 +932,7 @@ int main(int argc, char *argv[]) test_alignment(); test_keepalive(); test_chain(); + test_cookie(); knot_rrset_clear(&opt_rr, NULL);