Commit 639a486f authored by Karel Slaný's avatar Karel Slaný Committed by Ondřej Surý
Browse files

Started working on 'server' cookie code.

parent 9e7ae9fd
......@@ -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);
}
......@@ -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);
/* 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);