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

Server cookie algorithm configuration via interactive interface.

parent b4a88628
......@@ -461,7 +461,7 @@ static bool subreq_update_cookies(uv_udp_t *handle, struct sockaddr *srvr_addr,
assert(pkt);
/* Cookies disabled or packet has no ENDS section. */
if (!kr_glob_cookie_ctx.enabled || !pkt->opt_rr) {
if (!kr_glob_cookie_ctx.clnt.enabled || !pkt->opt_rr) {
return true;
}
......@@ -483,7 +483,7 @@ static bool subreq_update_cookies(uv_udp_t *handle, struct sockaddr *srvr_addr,
sockaddr_ptr = NULL;
}
kr_request_put_cookie(&kr_glob_cookie_ctx, cookie_cache,
kr_request_put_cookie(&kr_glob_cookie_ctx.clnt.current, cookie_cache,
(struct sockaddr*) sockaddr_ptr, srvr_addr, pkt);
return true;
......
......@@ -32,22 +32,9 @@
# define DEBUG_MSG(qry, fmt...) do { } while (0)
#endif /* defined(MODULE_DEBUG_MSGS) */
/* 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 }
};
/* Default srver secret. */
struct kr_cookie_secret dflt_ss = {
.size = KNOT_OPT_COOKIE_CLNT,
.data = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
struct kr_cookie_ctx kr_glob_cookie_ctx = {
.enabled = false,
.current_cs = &dflt_cs,
.current_ss = &dflt_ss
.clnt = { false, { NULL, NULL }, { NULL, NULL}, DFLT_COOKIE_TTL },
.srvr = { false, { NULL, NULL }, { NULL, NULL} }
};
static int opt_rr_add_cookies(knot_rrset_t *opt_rr,
......@@ -132,12 +119,12 @@ static const uint8_t *peek_and_check_cc(struct kr_cache *cache,
return NULL;
}
int kr_request_put_cookie(const struct kr_cookie_ctx *cntrl,
int kr_request_put_cookie(const struct kr_clnt_cookie_settings *clnt_cntrl,
struct kr_cache *cookie_cache,
const void *clnt_sockaddr, const void *srvr_sockaddr,
knot_pkt_t *pkt)
{
if (!cntrl || !pkt) {
if (!clnt_cntrl || !pkt) {
return kr_error(EINVAL);
}
......@@ -145,7 +132,8 @@ int kr_request_put_cookie(const struct kr_cookie_ctx *cntrl,
return kr_ok();
}
if (!cntrl->current_cs || !cookie_cache) {
if (!clnt_cntrl->csec || !clnt_cntrl->calg ||
!cookie_cache) {
return kr_error(EINVAL);
}
......@@ -155,12 +143,12 @@ int kr_request_put_cookie(const struct kr_cookie_ctx *cntrl,
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
.secret_data = clnt_cntrl->csec->data,
.secret_len = clnt_cntrl->csec->size
};
uint8_t cc[KNOT_OPT_COOKIE_CLNT];
assert(cntrl->cc_alg && cntrl->cc_alg->func);
int ret = cntrl->cc_alg->func(&input, cc);
assert(clnt_cntrl->calg && clnt_cntrl->calg->func);
int ret = clnt_cntrl->calg->func(&input, cc);
if (ret != kr_ok()) {
return ret;
}
......
......@@ -30,31 +30,43 @@ struct kr_cookie_secret {
uint8_t data[]; /*!< Secret quantity data. */
};
/** Default client secret. */
KR_EXPORT
extern struct kr_cookie_secret dflt_cs;
/** Default server secret. */
KR_EXPORT
extern struct kr_cookie_secret dflt_ss;
/** Default cookie TTL. */
#define DFLT_COOKIE_TTL 72000
/** DNS cookies controlling structure. */
struct kr_cookie_ctx {
bool enabled; /**< Enabled/disables DNS cookies functionality. */
/** Holds settings that have direct influence on client cookie values. */
struct kr_clnt_cookie_settings {
struct kr_cookie_secret *csec; /*!< Client secret data. */
const struct kr_clnt_cookie_alg_descr *calg; /**< Client cookie algorithm. */
};
/** Holds settings that control client behaviour. */
struct kr_clnt_cookie_ctx {
bool enabled; /**< Enable/disables client DNS cookies functionality. */
struct kr_cookie_secret *current_cs; /**< current client secret */
struct kr_cookie_secret *recent_cs; /**< recent client secret */
struct kr_clnt_cookie_settings current; /**< Current cookie client settings. */
struct kr_clnt_cookie_settings recent; /**< Current cookie client settings. */
uint32_t cache_ttl; /**< TTL used when caching cookies */
};
/** Holds settings that have direct influence on server cookie values. */
struct kr_srvr_cookie_settings {
struct kr_cookie_secret *ssec; /*!< Server secret data. */
const struct kr_srvr_cookie_alg_descr *salg; /**< Server cookie algorithm. */
};
struct kr_cookie_secret *current_ss; /**< current server secret */
struct kr_cookie_secret *recent_ss; /**< recent server secret */
/** Holds settings that control server behaviour. */
struct kr_srvr_cookie_ctx {
bool enabled; /**< Enable/disables server DNS cookies functionality. */
const struct kr_clnt_cookie_alg_descr *cc_alg; /**< Client cookie algorithm. */
const struct kr_srvr_cookie_alg_descr *sc_alg; /**< Server cookie algorithm. */
struct kr_srvr_cookie_settings current; /**< Current cookie server settings. */
struct kr_srvr_cookie_settings recent; /**< Current cookie server settings. */
};
/** DNS cookies controlling structure. */
struct kr_cookie_ctx {
struct kr_clnt_cookie_ctx clnt; /**< Client settings. */
struct kr_srvr_cookie_ctx srvr; /**< Server settings. */
};
/** Global cookie control context. */
......@@ -64,14 +76,14 @@ extern struct kr_cookie_ctx kr_glob_cookie_ctx;
/**
* Insert a DNS cookie into query packet.
* @note The packet must already contain ENDS section.
* @param cntrl Cookie control structure.
* @param clnt_cntrl Client cookie control structure.
* @param cookie_cache Cookie cache.
* @param clnt_sockaddr Client address.
* @param srvr_sockaddr Server address.
* @param pkt DNS request packet.
*/
KR_EXPORT
int kr_request_put_cookie(const struct kr_cookie_ctx *cntrl,
int kr_request_put_cookie(const struct kr_clnt_cookie_settings *clnt_cntrl,
struct kr_cache *cookie_cache,
const void *clnt_sockaddr, const void *srvr_sockaddr,
knot_pkt_t *pkt);
......@@ -32,8 +32,6 @@
#include "lib/module.h"
#include "lib/layer.h"
#include "lib/layer/__/print_pkt.h"
#define DEBUG_MSG(qry, fmt...) QRDEBUG(qry, "cookiemonster", fmt)
/* TODO -- The context must store sent cookies and server addresses in order
......@@ -103,34 +101,37 @@ static const struct sockaddr *guess_server_addr(const struct kr_nsrep *nsrep,
* @param sockaddr pointer to socket address to be set
* @param is_current set to true if the cookie was generate from current secret
* @param cc client cookie from the response
* @param cntr cookie control structure
* @param clnt_cntr client cookie control structure
* @return kr_ok() if matching address found, error code else
*/
static int srvr_sockaddr_cc_check(const struct sockaddr **sockaddr,
bool *is_current, const struct kr_query *qry,
const uint8_t cc[KNOT_OPT_COOKIE_CLNT],
const struct kr_cookie_ctx *cntrl)
const struct kr_clnt_cookie_ctx *clnt_cntrl)
{
assert(sockaddr && is_current && qry && cc && cntrl);
assert(sockaddr && is_current && qry && cc && clnt_cntrl);
const struct sockaddr *tmp_sockaddr = passed_server_sockaddr(qry);
/* The address must correspond with the client cookie. */
if (tmp_sockaddr) {
assert(cntrl->current_cs);
assert(clnt_cntrl->current.csec);
struct kr_clnt_cookie_input input = {
.clnt_sockaddr = NULL,
.srvr_sockaddr = tmp_sockaddr,
.secret_data = cntrl->current_cs->data,
.secret_len = cntrl->current_cs->size
.secret_data = clnt_cntrl->current.csec->data,
.secret_len = clnt_cntrl->current.csec->size
};
int ret = kr_clnt_cookie_check(cc, &input, cntrl->cc_alg);
int ret = kr_clnt_cookie_check(cc, &input,
clnt_cntrl->current.calg);
bool have_current = (ret == kr_ok());
if ((ret != kr_ok()) && cntrl->recent_cs) {
input.secret_data = cntrl->recent_cs->data;
input.secret_len = cntrl->recent_cs->size;
ret = kr_clnt_cookie_check(cc, &input, cntrl->cc_alg);
if ((ret != kr_ok()) &&
clnt_cntrl->recent.csec && clnt_cntrl->recent.calg) {
input.secret_data = clnt_cntrl->recent.csec->data;
input.secret_len = clnt_cntrl->recent.csec->size;
ret = kr_clnt_cookie_check(cc, &input,
clnt_cntrl->recent.calg);
}
if (ret == kr_ok()) {
*sockaddr = tmp_sockaddr;
......@@ -139,22 +140,24 @@ static int srvr_sockaddr_cc_check(const struct sockaddr **sockaddr,
return ret;
}
if (!cc || !cntrl) {
return kr_error(EINVAL);
}
// if (!cc || !clnt_cntrl) {
// return kr_error(EINVAL);
// }
DEBUG_MSG(NULL, "%s\n",
"guessing response address from ns reputation");
/* Abusing name server reputation mechanism to guess IP addresses. */
const struct kr_nsrep *ns = &qry->ns;
tmp_sockaddr = guess_server_addr(ns, cc, cntrl->current_cs,
cntrl->cc_alg);
tmp_sockaddr = guess_server_addr(ns, cc, clnt_cntrl->current.csec,
clnt_cntrl->current.calg);
bool have_current = (tmp_sockaddr != NULL);
if (!tmp_sockaddr && cntrl->recent_cs) {
if (!tmp_sockaddr &&
clnt_cntrl->recent.csec && clnt_cntrl->recent.calg) {
/* Try recent client secret to check obtained cookie. */
tmp_sockaddr = guess_server_addr(ns, cc, cntrl->recent_cs,
cntrl->cc_alg);
tmp_sockaddr = guess_server_addr(ns, cc,
clnt_cntrl->recent.csec,
clnt_cntrl->recent.calg);
}
if (tmp_sockaddr) {
*sockaddr = tmp_sockaddr;
......@@ -245,12 +248,12 @@ static bool is_cookie_cached(struct kr_cache *cache,
/**
* Check cookie content and store it to cache.
*/
static bool check_cookie_content_and_cache(struct kr_cookie_ctx *cntrl,
static bool check_cookie_content_and_cache(struct kr_clnt_cookie_ctx *clnt_cntrl,
struct kr_query *qry,
uint8_t *pkt_cookie_opt,
struct kr_cache *cache)
{
assert(cntrl && qry && pkt_cookie_opt && cache);
assert(clnt_cntrl && qry && pkt_cookie_opt && cache);
uint8_t *pkt_cookie_data = knot_edns_opt_get_data(pkt_cookie_opt);
uint16_t pkt_cookie_len = knot_edns_opt_get_length(pkt_cookie_opt);
......@@ -273,7 +276,7 @@ static bool check_cookie_content_and_cache(struct kr_cookie_ctx *cntrl,
const struct sockaddr *srvr_sockaddr = NULL;
bool returned_current = false;
ret = srvr_sockaddr_cc_check(&srvr_sockaddr, &returned_current, qry,
pkt_cc, cntrl);
pkt_cc, clnt_cntrl);
if (ret != kr_ok()) {
DEBUG_MSG(NULL, "%s\n", "could not match received cookie");
return false;
......@@ -284,7 +287,7 @@ static bool check_cookie_content_and_cache(struct kr_cookie_ctx *cntrl,
if (returned_current &&
!is_cookie_cached(cache, srvr_sockaddr, qry->timestamp.tv_sec,
pkt_cookie_opt)) {
struct timed_cookie timed_cookie = { cntrl->cache_ttl, pkt_cookie_opt };
struct timed_cookie timed_cookie = { clnt_cntrl->cache_ttl, pkt_cookie_opt };
ret = kr_cookie_cache_insert_cookie(cache, srvr_sockaddr,
&timed_cookie,
......@@ -305,7 +308,7 @@ static int check_response(knot_layer_t *ctx, knot_pkt_t *pkt)
struct kr_request *req = ctx->data;
struct kr_query *qry = req->current_query;
if (!kr_glob_cookie_ctx.enabled || (qry->flags & QUERY_TCP)) {
if (!kr_glob_cookie_ctx.clnt.enabled || (qry->flags & QUERY_TCP)) {
return ctx->state;
}
......@@ -334,7 +337,7 @@ static int check_response(knot_layer_t *ctx, knot_pkt_t *pkt)
return ctx->state;
}
if (!check_cookie_content_and_cache(&kr_glob_cookie_ctx, qry,
if (!check_cookie_content_and_cache(&kr_glob_cookie_ctx.clnt, qry,
pkt_cookie_opt, cookie_cache)) {
return KNOT_STATE_FAIL;
}
......@@ -369,7 +372,7 @@ static int check_response(knot_layer_t *ctx, knot_pkt_t *pkt)
static int check_request(knot_layer_t *ctx, void *module_param)
{
if (!kr_glob_cookie_ctx.enabled) {
if (!kr_glob_cookie_ctx.srvr.enabled) {
/* TODO -- IS there a way how to determine whether the original
* request came via TCP? */
return ctx->state;
......@@ -406,15 +409,16 @@ static int check_request(knot_layer_t *ctx, void *module_param)
if (!req_sc) {
/* TODO -- BADCOOKIE? Occasionally ignore? */
DEBUG_MSG(NULL, "%s\n", "no server cookie in request");
DEBUG_MSG(NULL, "%s\n", "no server DNS cookie in request");
return ctx->state;
}
/* Check server cookie obtained in request. */
const struct kr_cookie_ctx *cntrl = &kr_glob_cookie_ctx;
struct kr_srvr_cookie_ctx *srvr_cntrl = &kr_glob_cookie_ctx.srvr;
if (!req->qsource.addr || !cntrl->current_ss) {
if (!req->qsource.addr ||
!srvr_cntrl->current.ssec || !srvr_cntrl->current.salg) {
/* TODO -- SERVFAIL? */
DEBUG_MSG(NULL, "%s\n", "no server DNS cookie context data");
return ctx->state;
......@@ -422,12 +426,21 @@ static int check_request(knot_layer_t *ctx, void *module_param)
struct kr_srvr_cookie_check_ctx check_ctx = {
.clnt_sockaddr = req->qsource.addr,
.secret_data = cntrl->current_ss->data,
.secret_len = cntrl->current_ss->size
.secret_data = srvr_cntrl->current.ssec->data,
.secret_len = srvr_cntrl->current.ssec->size
};
ret = kr_srvr_cookie_check(req_cc, req_sc, req_sc_len, &check_ctx,
cntrl->sc_alg);
srvr_cntrl->current.salg);
if (ret == kr_error(EBADMSG) &&
srvr_cntrl->recent.ssec && srvr_cntrl->recent.salg) {
/* Try recent algorithm. */
check_ctx.secret_data = srvr_cntrl->recent.ssec->data;
check_ctx.secret_len = srvr_cntrl->recent.ssec->size;
ret = kr_srvr_cookie_check(req_cc, req_sc, req_sc_len,
&check_ctx,
srvr_cntrl->recent.salg);
}
if (ret != kr_ok()) {
/* TODO -- BADCOOKIE? Occasionally ignore? */
DEBUG_MSG(NULL, "%s\n", "invalid server DNS cookie data");
......
......@@ -271,7 +271,7 @@ static int edns_create(knot_pkt_t *pkt, knot_pkt_t *template, struct kr_request
pkt->opt_rr = knot_rrset_copy(req->ctx->opt_rr, &pkt->mm);
#if defined(ENABLE_COOKIES)
size_t wire_size = knot_edns_wire_size(pkt->opt_rr);
if (kr_glob_cookie_ctx.enabled) {
if (kr_glob_cookie_ctx.clnt.enabled) {
wire_size += KR_COOKIE_OPT_MAX_LEN;
}
return knot_pkt_reserve(pkt, wire_size);
......
......@@ -28,33 +28,57 @@
#define DEBUG_MSG(qry, fmt...) QRDEBUG(qry, "cookiectl", fmt)
#define NAME_ENABLED "enabled"
#define NAME_CLIENT_ENABLED "client_enabled"
#define NAME_CLIENT_SECRET "client_secret"
#define NAME_CLIENT_COOKIE_ALG "client_cookie_alg"
#define NAME_AVAILABLE_CLIENT_COOKIE_ALGS "available_client_cookie_algs"
#define NAME_CACHE_TTL "cache_ttl"
static bool aply_enabled(struct kr_cookie_ctx *cntrl, const JsonNode *node)
#define NAME_SERVER_ENABLED "server_enabled"
#define NAME_SERVER_SECRET "server_secret"
#define NAME_SERVER_COOKIE_ALG "server_cookie_alg"
#define NAME_AVAILABLE_SERVER_COOKIE_ALGS "available_server_cookie_algs"
static bool aply_enabled(bool *enabled, const JsonNode *node)
{
assert(enabled && node);
if (node->tag == JSON_BOOL) {
cntrl->enabled = node->bool_;
*enabled = node->bool_;
return true;
}
return false;
}
static struct kr_cookie_secret *new_cookie_secret(size_t size, bool zero)
{
if (!size) {
return NULL;
}
struct kr_cookie_secret *sq = malloc(sizeof(*sq) + size);
if (!sq) {
return NULL;
}
sq->size = size;
if (zero) {
memset(sq->data, 0, size);
}
return sq;
}
static struct kr_cookie_secret *new_sq_str(const JsonNode *node)
{
assert(node && node->tag == JSON_STRING);
size_t len = strlen(node->string_);
struct kr_cookie_secret *sq = malloc(sizeof(*sq) + len);
struct kr_cookie_secret *sq = new_cookie_secret(len, false);
if (!sq) {
return NULL;
}
sq->size = len;
memcpy(sq->data, node->string_, len);
return sq;
......@@ -78,12 +102,11 @@ static struct kr_cookie_secret *new_sq_array(const JsonNode *node)
return NULL;
}
struct kr_cookie_secret *sq = malloc(sizeof(*sq) + cnt);
struct kr_cookie_secret *sq = new_cookie_secret(cnt, false);
if (!sq) {
return NULL;
}
sq->size = cnt;
cnt = 0;
json_foreach(element, node) {
sq->data[cnt++] = (uint8_t) element->number_;
......@@ -92,9 +115,10 @@ static struct kr_cookie_secret *new_sq_array(const JsonNode *node)
return sq;
}
static bool apply_client_secret(struct kr_cookie_ctx *cntrl,
const JsonNode *node)
static bool apply_secret(struct kr_cookie_secret **sec, const JsonNode *node)
{
assert(sec && node);
struct kr_cookie_secret *sq = NULL;
switch (node->tag) {
......@@ -112,20 +136,8 @@ static bool apply_client_secret(struct kr_cookie_ctx *cntrl,
return false;
}
if (sq->size == cntrl->current_cs->size &&
memcmp(sq->data, cntrl->current_cs->data, sq->size) == 0) {
/* Ignore same values. */
free(sq);
return true;
}
struct kr_cookie_secret *tmp = cntrl->recent_cs;
cntrl->recent_cs = cntrl->current_cs;
cntrl->current_cs = sq;
if (tmp && tmp != &dflt_cs) {
free(tmp);
}
/* Overwrite data. */
*sec = sq;
return true;
}
......@@ -139,7 +151,23 @@ static bool apply_client_hash_func(struct kr_cookie_ctx *cntrl,
if (!cc_alg) {
return false;
}
cntrl->cc_alg = cc_alg;
cntrl->clnt.current.calg = cc_alg;
return true;
}
return false;
}
static bool apply_server_hash_func(struct kr_cookie_ctx *cntrl,
const JsonNode *node)
{
if (node->tag == JSON_STRING) {
const struct kr_srvr_cookie_alg_descr *sc_alg = kr_srvr_cookie_alg(kr_srvr_cookie_algs,
node->string_);
if (!sc_alg) {
return false;
}
cntrl->srvr.current.salg = sc_alg;
return true;
}
......@@ -149,7 +177,7 @@ static bool apply_client_hash_func(struct kr_cookie_ctx *cntrl,
static bool apply_cache_ttl(struct kr_cookie_ctx *cntrl, const JsonNode *node)
{
if (node->tag == JSON_NUMBER) {
cntrl->cache_ttl = node->number_;
cntrl->clnt.cache_ttl = node->number_;
return true;
}
......@@ -165,37 +193,44 @@ static bool apply_configuration(struct kr_cookie_ctx *cntrl, const JsonNode *nod
return false;
}
if (strcmp(node->key, NAME_ENABLED) == 0) {
return aply_enabled(cntrl, node);
if (strcmp(node->key, NAME_CLIENT_ENABLED) == 0) {
return aply_enabled(&cntrl->clnt.enabled, node);
} else if (strcmp(node->key, NAME_CLIENT_SECRET) == 0) {
return apply_client_secret(cntrl, node);
return apply_secret(&cntrl->clnt.current.csec, node);
} else if (strcmp(node->key, NAME_CLIENT_COOKIE_ALG) == 0) {
return apply_client_hash_func(cntrl, node);
} else if (strcmp(node->key, NAME_CACHE_TTL) == 0) {
return apply_cache_ttl(cntrl, node);
} else if (strcmp(node->key, NAME_SERVER_ENABLED) == 0) {
return aply_enabled(&cntrl->srvr.enabled, node);
} else if (strcmp(node->key, NAME_SERVER_SECRET) == 0) {
return apply_secret(&cntrl->srvr.current.ssec, node);
} else if (strcmp(node->key, NAME_SERVER_COOKIE_ALG) == 0) {
return apply_server_hash_func(cntrl, node);
}
return false;
}
static bool read_secret(JsonNode *root, struct kr_cookie_ctx *cntrl)
static bool read_secret(JsonNode *root, const char *node_name,
const struct kr_cookie_secret *secret)
{
assert(root && cntrl);
assert(root && node_name && secret);
JsonNode *array = json_mkarray();
if (!array) {
return false;
}
for (size_t i = 0; i < cntrl->current_cs->size; ++i) {
JsonNode *element = json_mknumber(cntrl->current_cs->data[i]);
for (size_t i = 0; i < secret->size; ++i) {
JsonNode *element = json_mknumber(secret->data[i]);
if (!element) {
goto fail;
}
json_append_element(array, element);
}
json_append_member(root, NAME_CLIENT_SECRET, array);
json_append_member(root, node_name, array);
return true;
......@@ -206,10 +241,9 @@ fail:
return false;
}
static bool read_available_cc_hashes(JsonNode *root,
struct kr_cookie_ctx *cntrl)
static bool read_available_cc_hashes(JsonNode *root)
{
assert(root && cntrl);
assert(root);
JsonNode *array = json_mkarray();
if (!array) {
......@@ -238,46 +272,184 @@ fail:
return false;
}
/**
* Get/set DNS cookie related stuff.
*
* Input: { name: value, ... }
* Output: current configuration
*/
static char *cookiectl_config(void *env, struct kr_module *module, const char *args)
static bool read_available_sc_hashes(JsonNode *root)
{
if (args && strlen(args) > 0) {
JsonNode *node;
JsonNode *root_node = json_decode(args);
json_foreach (node, root_node) {
apply_configuration(&kr_glob_cookie_ctx, node);
assert(root);
JsonNode *array = json_mkarray();
if (!array) {
return false;
}
const struct kr_srvr_cookie_alg_descr *aux_ptr = kr_srvr_cookie_algs;
while (aux_ptr && aux_ptr->gen_func) {
assert(aux_ptr->name);
JsonNode *element = json_mkstring(aux_ptr->name);
if (!element) {
goto fail;
}
json_delete(root_node);
json_append_element(array, element);
++aux_ptr;
}
json_append_member(root, NAME_AVAILABLE_SERVER_COOKIE_ALGS, array);
return true;
fail:
if (array) {
json_delete(array