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

Moved cookie hash functions into separate unit.

parent 47a30119
/* 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> /* inet_ntop() */
#include <sys/socket.h>
#include <netinet/in.h>
#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/algorithm.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])
{
if (!input || !cc_out) {
return kr_error(EINVAL);
}
if ((!input->clnt_sockaddr && !input->srvr_sockaddr) ||
!(input->secret_data && input->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 defined(CC_HASH_USE_CLIENT_ADDRESS)
if (input->clnt_sockaddr) {
if (kr_ok() == kr_address_bytes(input->clnt_sockaddr, &addr,
&alen)) {
assert(addr && alen);
hash_val = fnv_64a_buf(addr, alen, hash_val);
}
}
#endif /* defined(CC_HASH_USE_CLIENT_ADDRESS) */
if (input->srvr_sockaddr) {
if (kr_ok() == kr_address_bytes(input->srvr_sockaddr, &addr,
&alen)) {
assert(addr && alen);
hash_val = fnv_64a_buf((void *) addr, alen, hash_val);
}
}
hash_val = fnv_64a_buf((void *) input->secret_data, input->secret_len,
hash_val);
assert(KNOT_OPT_COOKIE_CLNT == sizeof(hash_val));
memcpy(cc_out, &hash_val, KNOT_OPT_COOKIE_CLNT);
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])
{
if (!input || !cc_out) {
return kr_error(EINVAL);
}
if ((!input->clnt_sockaddr && !input->srvr_sockaddr) ||
!(input->secret_data && input->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;
/* text: (client IP | server IP)
* key: client secret */
HMAC_CTX ctx;
HMAC_CTX_init(&ctx);
int ret = HMAC_Init_ex(&ctx, input->secret_data, input->secret_len,
EVP_sha256(), NULL);
if (ret != 1) {
ret = kr_error(EINVAL);
goto fail;
}
#if defined(CC_HASH_USE_CLIENT_ADDRESS)
if (input->clnt_sockaddr) {
if (kr_ok() == kr_address_bytes(input->clnt_sockaddr, &addr,
&alen)) {
assert(addr && alen);
ret = HMAC_Update(&ctx, addr, alen);
if (ret != 1) {
ret = kr_error(EINVAL);
goto fail;
}
}
}
#endif /* defined(CC_HASH_USE_CLIENT_ADDRESS) */
if (input->srvr_sockaddr) {
if (kr_ok() == kr_address_bytes(input->srvr_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(KNOT_OPT_COOKIE_CLNT <= SHA256_DIGEST_LENGTH);
memcpy(cc_out, digest, KNOT_OPT_COOKIE_CLNT);
ret = kr_ok();
fail:
HMAC_CTX_cleanup(&ctx);
return ret;
}
/* 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"
/** Maximal size of a cookie option. */
#define KR_COOKIE_OPT_MAX_LEN (KNOT_EDNS_OPTION_HDRLEN + KNOT_OPT_COOKIE_CLNT + KNOT_OPT_COOKIE_SRVR_MAX)
/** Client cookie computation context. */
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;
};
/** Client cookie algorithm type. */
typedef int (clnt_cookie_alg_t)(const struct kr_clnt_cookie_input *input,
uint8_t *);
/** 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. */
};
/**
* List of available client cookie algorithms.
*
* Last element contains all null entries.
*/
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.
* @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.
*/
KR_EXPORT
const char *kr_clnt_cookie_alg_name(const struct kr_clnt_cookie_alg_descr cc_algs[],
clnt_cookie_alg_t *func);
/**
* Get pointers to IP address bytes.
* @param sockaddr socket address
* @param addr pointer to address
* @param len address length
* @return kr_ok() on success, error code else.
*/
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.
*/
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]);
......@@ -16,17 +16,11 @@
//#define MODULE_DEBUG_MSGS 1 /* Comment out if debug messages are not desired. */
#include <arpa/inet.h> /* inet_ntop() */
#include <sys/socket.h>
#include <netinet/in.h>
#include <assert.h>
#include <libknot/error.h>
#include <openssl/hmac.h>
#include <openssl/sha.h>
#include <stdint.h>
#include <string.h>
#include "contrib/fnv/fnv.h"
#include "lib/cookies/cache.h"
#include "lib/cookies/control.h"
#include "lib/layer.h"
......@@ -38,20 +32,12 @@
# define DEBUG_MSG(qry, fmt...) do { } while (0)
#endif /* defined(MODULE_DEBUG_MSGS) */
//#define CC_HASH_USE_CLIENT_ADDRESS /* When defined, client address will be used when generating client cookie. */
/* Default client secret. */
struct kr_cookie_secret dflt_cs = {
.size = KNOT_OPT_COOKIE_CLNT,
.data = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
const struct kr_cc_hash_descr kr_cc_hashes[] = {
{ kr_cc_compute_fnv64, "FNV-64" },
{ kr_cc_compute_hmac_sha256_64, "HMAC-SHA256-64" },
{ NULL, NULL }
};
struct kr_cookie_ctx kr_glob_cookie_ctx = {
.enabled = false,
.current_cs = &dflt_cs
......@@ -106,194 +92,6 @@ static int opt_rr_add_option(knot_rrset_t *opt_rr, uint8_t *option,
return KNOT_EOK;
}
cc_compute_func_t *kr_cc_hash_func(const struct kr_cc_hash_descr cc_hashes[],
const char *name)
{
if (!cc_hashes || !name) {
return NULL;
}
const struct kr_cc_hash_descr *aux_ptr = cc_hashes;
while (aux_ptr && aux_ptr->hash_func) {
assert(aux_ptr->name);
if (strcmp(aux_ptr->name, name) == 0) {
return aux_ptr->hash_func;
}
++aux_ptr;
}
return NULL;
}
const char *kr_cc_hash_name(const struct kr_cc_hash_descr cc_hashes[],
cc_compute_func_t *func)
{
if (!cc_hashes || !func) {
return NULL;
}
const struct kr_cc_hash_descr *aux_ptr = cc_hashes;
while (aux_ptr && aux_ptr->hash_func) {
assert(aux_ptr->name);
if (aux_ptr->hash_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;
DEBUG_MSG(NULL, "%s\n", "could obtain IP address");
return kr_error(EINVAL);
break;
}
WITH_DEBUG {
char ns_str[INET6_ADDRSTRLEN];
inet_ntop(addr_family, *addr, ns_str, sizeof(ns_str));
DEBUG_MSG(NULL, "obtained IP address '%s'\n", ns_str);
}
return kr_ok();
}
int kr_cc_compute_fnv64(uint8_t cc_buf[KNOT_OPT_COOKIE_CLNT],
const void *clnt_sockaddr, const void *srvr_sockaddr,
const struct kr_cookie_secret *secret)
{
if (!cc_buf) {
return kr_error(EINVAL);
}
if ((!clnt_sockaddr && !srvr_sockaddr) ||
!(secret && secret->size && secret->data)) {
return kr_error(EINVAL);
}
const uint8_t *addr = NULL;
size_t alen = 0; /* Address length. */
Fnv64_t hash_val = FNV1A_64_INIT;
#if defined(CC_HASH_USE_CLIENT_ADDRESS)
if (clnt_sockaddr) {
if (kr_ok() == kr_address_bytes(clnt_sockaddr, &addr, &alen)) {
assert(addr && alen);
hash_val = fnv_64a_buf(addr, alen, hash_val);
}
}
#endif /* defined(CC_HASH_USE_CLIENT_ADDRESS) */
if (srvr_sockaddr) {
if (kr_ok() == kr_address_bytes(srvr_sockaddr, &addr, &alen)) {
assert(addr && alen);
hash_val = fnv_64a_buf((void *) addr, alen, hash_val);
}
}
hash_val = fnv_64a_buf((void *) secret->data, secret->size, hash_val);
assert(KNOT_OPT_COOKIE_CLNT == sizeof(hash_val));
memcpy(cc_buf, &hash_val, KNOT_OPT_COOKIE_CLNT);
return kr_ok();
}
int kr_cc_compute_hmac_sha256_64(uint8_t cc_buf[KNOT_OPT_COOKIE_CLNT],
const void *clnt_sockaddr, const void *srvr_sockaddr,
const struct kr_cookie_secret *secret)
{
if (!cc_buf) {
return kr_error(EINVAL);
}
if ((!clnt_sockaddr && !srvr_sockaddr) ||
!(secret && secret->size && secret->data)) {
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;
/* text: (client IP | server IP)
* key: client secret */
HMAC_CTX ctx;
HMAC_CTX_init(&ctx);
int ret = HMAC_Init_ex(&ctx, secret->data, secret->size, EVP_sha256(),
NULL);
if (ret != 1) {
ret = kr_error(EINVAL);
goto fail;
}
#if defined(CC_HASH_USE_CLIENT_ADDRESS)
if (clnt_sockaddr) {
if (kr_ok() == kr_address_bytes(clnt_sockaddr, &addr, &alen)) {
assert(addr && alen);
ret = HMAC_Update(&ctx, addr, alen);
if (ret != 1) {
ret = kr_error(EINVAL);
goto fail;
}
}
}
#endif /* defined(CC_HASH_USE_CLIENT_ADDRESS) */
if (srvr_sockaddr) {
if (kr_ok() == kr_address_bytes(srvr_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(KNOT_OPT_COOKIE_CLNT <= SHA256_DIGEST_LENGTH);
memcpy(cc_buf, digest, KNOT_OPT_COOKIE_CLNT);
ret = kr_ok();
fail:
HMAC_CTX_cleanup(&ctx);
return ret;
}
/**
* Check whether there is a cached cookie that matches the current client
* cookie.
......@@ -347,10 +145,15 @@ int kr_request_put_cookie(const struct kr_cookie_ctx *cntrl,
/* Generate client cookie.
* TODO -- generate client cookie from client address, server address
* and secret quantity. */
struct kr_clnt_cookie_input input = {
.clnt_sockaddr = clnt_sockaddr,
.srvr_sockaddr = srvr_sockaddr,
.secret_data = cntrl->current_cs->data,
.secret_len = cntrl->current_cs->size
};
uint8_t cc[KNOT_OPT_COOKIE_CLNT];
assert(cntrl->cc_compute_func);
int ret = cntrl->cc_compute_func(cc, clnt_sockaddr, srvr_sockaddr,
cntrl->current_cs);
assert(cntrl->cc_alg_func);
int ret = cntrl->cc_alg_func(&input, cc);
if (ret != kr_ok()) {
return ret;
}
......
......@@ -20,12 +20,10 @@
#include <libknot/rrtype/opt_cookie.h>
#include <stdbool.h>
#include "lib/cookies/algorithm.h"
#include "lib/cache.h"
#include "lib/defines.h"
/** Maximal size of a cookie option. */
#define KR_COOKIE_OPT_MAX_LEN (KNOT_EDNS_OPTION_HDRLEN + KNOT_OPT_COOKIE_CLNT + KNOT_OPT_COOKIE_SRVR_MAX)
/** Holds secret quantity. */
struct kr_cookie_secret {
size_t size; /*!< Secret quantity size. */
......@@ -39,24 +37,6 @@ extern struct kr_cookie_secret dflt_cs;
/** Default cookie TTL. */
#define DFLT_COOKIE_TTL 72000
/** Client cookie creation function type. */
typedef int (cc_compute_func_t)(uint8_t *, const void *, const void *,
const struct kr_cookie_secret *);
/** Holds description of client cookie hashing algorithms. */
struct kr_cc_hash_descr {
cc_compute_func_t *hash_func; /**< Pointer to has function. */
const char *name; /**< Hash function name. */
};
/**
* List of available client cookie hash functions.
*
* Last element contains all null entries.
*/
KR_EXPORT
extern const struct kr_cc_hash_descr kr_cc_hashes[];
/** DNS cookies controlling structure. */
struct kr_cookie_ctx {
bool enabled; /**< Enabled/disables DNS cookies functionality. */
......@@ -66,69 +46,13 @@ struct kr_cookie_ctx {
uint32_t cache_ttl; /**< TTL used when caching cookies */
cc_compute_func_t *cc_compute_func; /**< Client cookie hash computation callback. */
clnt_cookie_alg_t *cc_alg_func; /**< Client cookie hash computation callback. */
};
/** Global cookie control context. */
KR_EXPORT
extern struct kr_cookie_ctx kr_glob_cookie_ctx;
/**
* @brief Return pointer to client cookie hash function with given name.
* @param cc_hashes list of avilable has functions
* @param name has function name
* @return pointer to function or NULL if not found
*/
KR_EXPORT
cc_compute_func_t