]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
Server cookie algorithm configuration via interactive interface.
authorKarel Slany <karel.slany@nic.cz>
Wed, 15 Jun 2016 07:26:57 +0000 (09:26 +0200)
committerOndřej Surý <ondrej@sury.org>
Thu, 11 Aug 2016 12:06:45 +0000 (14:06 +0200)
daemon/worker.c
lib/cookies/control.c
lib/cookies/control.h
lib/layer/cookiemonster.c
lib/resolve.c
modules/cookiectl/cookiectl.c

index 5f0265cee3c4a902e44b22056b4a0e3434baf943..75c851519457ed738f6e439251d9be094ce4d8de 100644 (file)
@@ -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;
index 7e0406657f217ccebbad91b9ab0353c71d0328ee..58259a330f9f8797cab8ddf974ae6a2a53d303db 100644 (file)
 #  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;
        }
index 0ebb7fbbf0759fd98f2f46ae238a33766da5cab8..a843ce50e70510859f45ddcbd20fe0be2435332f 100644 (file)
@@ -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);
index ce9c8fe90ae346fd2f8f7482c5e1aeba2dc6a9d9..bf05abfe549ebeaafe7dbb1f696b861d278d3542 100644 (file)
@@ -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");
index 842518d5178f1c1a56dbb6459e18d98412900a58..076b08a116acac5e297d79e7a9bb1e45cd5bc977 100644 (file)
@@ -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);
index 832c1bb15eb9d257123795754ef3de000e597926..aacdb7fa50de5eb6b63cb787c62035c471f184bc 100644 (file)
 
 #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);
+       }
+       return false;
+}
+
+static bool clnt_settings_equal(const struct kr_clnt_cookie_settings *s1,
+                                const struct kr_clnt_cookie_settings *s2)
+{
+       assert(s1 && s2 && s1->csec && s2->csec);
+
+       if (s1->calg != s2->calg || s1->csec->size != s2->csec->size) {
+               return false;
+       }
+
+       return 0 == memcmp(s1->csec->data, s2->csec->data, s1->csec->size);
+}
+
+static bool srvr_settings_equal(const struct kr_srvr_cookie_settings *s1,
+                                const struct kr_srvr_cookie_settings *s2)
+{
+       assert(s1 && s2 && s1->ssec && s2->ssec);
+
+       if (s1->salg != s2->salg || s1->ssec->size != s2->ssec->size) {
+               return false;
+       }
+
+       return 0 == memcmp(s1->ssec->data, s2->ssec->data, s1->ssec->size);
+}
+
+static void apply_from_copy(struct kr_cookie_ctx *running,
+                            const struct kr_cookie_ctx *shallow)
+{
+       assert(running && shallow);
+
+       if (!clnt_settings_equal(&running->clnt.current,
+                                &shallow->clnt.current)) {
+               free(running->clnt.recent.csec); /* Delete old secret. */
+               running->clnt.recent = running->clnt.current;
+               running->clnt.current = shallow->clnt.current;
+               /* Shallow will be deleted after this function call. */
+       }
+
+       if (!srvr_settings_equal(&running->srvr.current,
+                                &shallow->srvr.current)) {
+               free(running->srvr.recent.ssec); /* Delete old secret. */
+               running->srvr.recent = running->srvr.current;
+               running->srvr.current = shallow->srvr.current;
+               /* Shallow will be deleted after this function call. */
+       }
+
+       /* Direct application. */
+       running->clnt.cache_ttl = shallow->clnt.cache_ttl;
+       running->clnt.enabled = shallow->clnt.enabled;
+       running->srvr.enabled = shallow->srvr.enabled;
+}
+
+static bool apply_config(struct kr_cookie_ctx *ctx, const char *args)
+{
+       if (!ctx) {
+               return false;
+       }
+
+       if (!args || !strlen(args)) {
+               return true;
+       }
+
+       struct kr_cookie_ctx shallow_copy = *ctx;
+       bool success = true;
+
+       if (!args || !strlen(args)) {
+               return success;
+       }
+
+       JsonNode *node;
+       JsonNode *root_node = json_decode(args);
+       json_foreach (node, root_node) {
+               success = apply_configuration(&shallow_copy, node);
+               if (!success) {
+                       break;
+               }
+       }
+       json_delete(root_node);
+
+       if (success) {
+               apply_from_copy(ctx, &shallow_copy);
+       } else {
+               /* Clean newly allocated data. */
+               if (shallow_copy.clnt.current.csec != ctx->clnt.current.csec) {
+                       free(shallow_copy.clnt.current.csec);
+               }
+               if (shallow_copy.srvr.current.ssec != ctx->srvr.current.ssec) {
+                       free(shallow_copy.srvr.current.ssec);
+               }
+       }
+
+       return success;
+}
+
+char *read_config(struct kr_cookie_ctx *ctx)
+{
+       if (!ctx) {
+               return NULL;
        }
 
-       /* Return current configuration. */
        char *result = NULL;
        JsonNode *root_node = json_mkobject();
 
-       json_append_member(root_node, NAME_ENABLED,
-                          json_mkbool(kr_glob_cookie_ctx.enabled));
+       json_append_member(root_node, NAME_CLIENT_ENABLED,
+                          json_mkbool(ctx->clnt.enabled));
 
-       read_secret(root_node, &kr_glob_cookie_ctx);
+       read_secret(root_node, NAME_CLIENT_SECRET, ctx->clnt.current.csec);
 
-       assert(kr_glob_cookie_ctx.cc_alg->name);
+       assert(ctx->clnt.current.calg->name);
        json_append_member(root_node, NAME_CLIENT_COOKIE_ALG,
-                          json_mkstring(kr_glob_cookie_ctx.cc_alg->name));
+                          json_mkstring(ctx->clnt.current.calg->name));
 
-       read_available_cc_hashes(root_node, &kr_glob_cookie_ctx);
+       read_available_cc_hashes(root_node);
 
        json_append_member(root_node, NAME_CACHE_TTL,
-                          json_mknumber(kr_glob_cookie_ctx.cache_ttl));
+                          json_mknumber(ctx->clnt.cache_ttl));
+
+       json_append_member(root_node, NAME_SERVER_ENABLED,
+                          json_mkbool(ctx->srvr.enabled));
+
+       read_secret(root_node, NAME_SERVER_SECRET, ctx->srvr.current.ssec);
+
+       assert(ctx->srvr.current.salg->name);
+       json_append_member(root_node, NAME_SERVER_COOKIE_ALG,
+                          json_mkstring(ctx->srvr.current.salg->name));
+
+       read_available_sc_hashes(root_node);
 
        result = json_encode(root_node);
        json_delete(root_node);
        return result;
 }
 
+/**
+ * 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)
+{
+       /* Apply configuration, if any. */
+       apply_config(&kr_glob_cookie_ctx, args);
+
+       /* Return current configuration. */
+       return read_config(&kr_glob_cookie_ctx);
+}
+
 /*
  * Module implementation.
  */
@@ -289,12 +461,26 @@ int cookiectl_init(struct kr_module *module)
 
        memset(&kr_glob_cookie_ctx, 0, sizeof(kr_glob_cookie_ctx));
 
-       kr_glob_cookie_ctx.enabled = false;
-       kr_glob_cookie_ctx.current_cs = &dflt_cs;
-       kr_glob_cookie_ctx.cache_ttl = DFLT_COOKIE_TTL;
-       kr_glob_cookie_ctx.current_ss = &dflt_ss;
-       kr_glob_cookie_ctx.cc_alg = kr_clnt_cookie_alg(kr_clnt_cookie_algs, "FNV-64");
-       kr_glob_cookie_ctx.sc_alg = kr_srvr_cookie_alg(kr_srvr_cookie_algs, "HMAC-SHA256-64");
+       struct kr_cookie_secret *cs = new_cookie_secret(KNOT_OPT_COOKIE_CLNT,
+                                                       true);
+       struct kr_cookie_secret *ss = new_cookie_secret(KNOT_OPT_COOKIE_CLNT,
+                                                       true);
+       if (!cs || !ss) {
+               free(cs);
+               free(ss);
+               return kr_error(ENOMEM);
+       }
+
+       kr_glob_cookie_ctx.clnt.enabled = false;
+       kr_glob_cookie_ctx.clnt.current.csec = cs;
+       kr_glob_cookie_ctx.clnt.current.calg = kr_clnt_cookie_alg(kr_clnt_cookie_algs,
+                                                                 "FNV-64");
+       kr_glob_cookie_ctx.clnt.cache_ttl = DFLT_COOKIE_TTL;
+
+       kr_glob_cookie_ctx.srvr.enabled = false;
+       kr_glob_cookie_ctx.srvr.current.ssec = ss;
+       kr_glob_cookie_ctx.srvr.current.salg = kr_srvr_cookie_alg(kr_srvr_cookie_algs,
+                                                                 "HMAC-SHA256-64");
 
        module->data = NULL;
 
@@ -304,31 +490,21 @@ int cookiectl_init(struct kr_module *module)
 KR_EXPORT
 int cookiectl_deinit(struct kr_module *module)
 {
-       kr_glob_cookie_ctx.enabled = false;
+       kr_glob_cookie_ctx.clnt.enabled = false;
 
-       if (kr_glob_cookie_ctx.recent_cs &&
-           kr_glob_cookie_ctx.recent_cs != &dflt_cs) {
-               free(kr_glob_cookie_ctx.recent_cs);
-       }
-       kr_glob_cookie_ctx.recent_cs = NULL;
+       free(kr_glob_cookie_ctx.clnt.recent.csec);
+       kr_glob_cookie_ctx.clnt.recent.csec = NULL;
 
-       if (kr_glob_cookie_ctx.current_cs &&
-           kr_glob_cookie_ctx.current_cs != &dflt_cs) {
-               free(kr_glob_cookie_ctx.current_cs);
-       }
-       kr_glob_cookie_ctx.current_cs = &dflt_cs;
+       free(kr_glob_cookie_ctx.clnt.current.csec);
+       kr_glob_cookie_ctx.clnt.current.csec = NULL;
 
-       if (kr_glob_cookie_ctx.recent_ss &&
-           kr_glob_cookie_ctx.recent_ss != &dflt_ss) {
-               free(kr_glob_cookie_ctx.recent_ss);
-       }
-       kr_glob_cookie_ctx.recent_ss = NULL;
+       kr_glob_cookie_ctx.srvr.enabled = false;
 
-       if (kr_glob_cookie_ctx.current_ss &&
-           kr_glob_cookie_ctx.current_ss != &dflt_ss) {
-               free(kr_glob_cookie_ctx.current_ss);
-       }
-       kr_glob_cookie_ctx.current_ss = &dflt_ss;
+       free(kr_glob_cookie_ctx.srvr.recent.ssec);
+       kr_glob_cookie_ctx.srvr.recent.ssec = NULL;
+
+       free(kr_glob_cookie_ctx.srvr.current.ssec);
+       kr_glob_cookie_ctx.srvr.current.ssec = NULL;
 
        return kr_ok();
 }