diff --git a/lib/cookies/alg_srvr.c b/lib/cookies/alg_srvr.c index c3cba6d923bef8d4afb69f85e1fcffc6d8e0c0b1..a3ec3e6499af26faef86e8fb5d1a833583763406 100644 --- a/lib/cookies/alg_srvr.c +++ b/lib/cookies/alg_srvr.c @@ -363,12 +363,16 @@ const struct kr_srvr_cookie_alg_descr *kr_srvr_cookie_alg(const struct kr_srvr_c return NULL; } -int kr_srvr_cookie_check(const uint8_t *cc, uint16_t cc_len, - const uint8_t *sc, uint16_t sc_len, +int kr_srvr_cookie_check(const struct kr_dns_cookies *cookies, const struct kr_srvr_cookie_check_ctx *check_ctx, const struct kr_srvr_cookie_alg_descr *sc_alg) { - if (!cc || !sc || !sc_len || !check_ctx || !sc_alg) { + if (!cookies || !check_ctx || !sc_alg) { + return kr_error(EINVAL); + } + + if (!cookies->cc || !cookies->cc_len || + !cookies->sc || !cookies->sc_len) { return kr_error(EINVAL); } @@ -382,7 +386,7 @@ int kr_srvr_cookie_check(const uint8_t *cc, uint16_t cc_len, return kr_error(EINVAL); } - if (sc_len != sc_alg->srvr_cookie_size) { + if (cookies->sc_len != sc_alg->srvr_cookie_size) { /* Cookie size does to match. */ return kr_error(EBADMSG); } @@ -390,7 +394,8 @@ int kr_srvr_cookie_check(const uint8_t *cc, uint16_t cc_len, struct kr_srvr_cookie_inbound inbound_sc = { 0, }; /* Obtain data from received server cookie. */ - int ret = sc_alg->opt_parse_func(sc, sc_len, &inbound_sc); + int ret = sc_alg->opt_parse_func(cookies->sc, cookies->sc_len, + &inbound_sc); if (ret != kr_ok()) { return ret; } @@ -398,8 +403,8 @@ int kr_srvr_cookie_check(const uint8_t *cc, uint16_t cc_len, uint8_t generated_sc[KNOT_OPT_COOKIE_SRVR_MAX] = { 0, }; uint16_t generated_sc_len = KNOT_OPT_COOKIE_SRVR_MAX; struct kr_srvr_cookie_input sc_input = { - .clnt_cookie = cc, - .clnt_cookie_len = cc_len, + .clnt_cookie = cookies->cc, + .clnt_cookie_len = cookies->cc_len, .nonce = inbound_sc.nonce, .time = inbound_sc.time, .srvr_data = check_ctx @@ -412,7 +417,7 @@ int kr_srvr_cookie_check(const uint8_t *cc, uint16_t cc_len, } assert(generated_sc_len == sc_alg->srvr_cookie_size); - ret = (memcmp(sc, generated_sc, generated_sc_len) == 0) ? + ret = (memcmp(cookies->sc, generated_sc, generated_sc_len) == 0) ? kr_ok() : kr_error(EBADMSG); return ret; diff --git a/lib/cookies/alg_srvr.h b/lib/cookies/alg_srvr.h index e633ace824197f5bc321f9015b55798b3fe5a845..711152acee5a4c634db76b0af5ae2d4cb076cbfa 100644 --- a/lib/cookies/alg_srvr.h +++ b/lib/cookies/alg_srvr.h @@ -20,6 +20,14 @@ #include "lib/defines.h" +/** Convenience Structure holding both, server and client, cookies. */ +struct kr_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. */ +}; + /** Inbound server cookie checking context. */ struct kr_srvr_cookie_check_ctx { const void *clnt_sockaddr; /**< Client (remote) socket address. */ @@ -91,16 +99,12 @@ const struct kr_srvr_cookie_alg_descr *kr_srvr_cookie_alg(const struct kr_srvr_c /** * @brief Check whether supplied client and server cookie match. - * @param cc Client cookie. - * @param cc_len Client cookie length. - * @param sc Server cookie that should be checked. - * @param sc_len Server cookie length. - * @param check_ctx Data known to the server needed for cookie validation. - * @param sc_alg Server cookie algorithm. + * @param cookies Cookie data. + * @param check_ctx Data known to the server needed for cookie validation. + * @param sc_alg Server cookie algorithm. * @return kr_ok() if check OK, error code else. */ KR_EXPORT -int kr_srvr_cookie_check(const uint8_t *cc, uint16_t cc_len, - const uint8_t *sc, uint16_t sc_len, +int kr_srvr_cookie_check(const struct kr_dns_cookies *cookies, const struct kr_srvr_cookie_check_ctx *check_ctx, const struct kr_srvr_cookie_alg_descr *sc_alg); diff --git a/lib/cookies/control.c b/lib/cookies/control.c index c9f47f3356004a0aa2107ab86f13e5e3d123d29c..d6e935d60fc44b02ba8305ced6b89064a4b8b158 100644 --- a/lib/cookies/control.c +++ b/lib/cookies/control.c @@ -187,3 +187,79 @@ int kr_request_put_cookie(const struct kr_clnt_cookie_settings *clnt_cntrl, assert(pkt->current == KNOT_ADDITIONAL); return knot_pkt_put(pkt, KNOT_COMPR_HINT_NONE, pkt->opt_rr, KNOT_PF_FREE); } + +int kr_answer_opt_rr_add_cookies(const struct kr_srvr_cookie_input *input, + const struct kr_srvr_cookie_alg_descr *alg, + knot_pkt_t *pkt) +{ + if (!input || !alg || pkt) { + kr_error(EINVAL); + } + + uint16_t cookie_size = input->clnt_cookie_len + alg->srvr_cookie_size; + uint8_t *data = NULL; + + if (!pkt->opt_rr) { + kr_error(EINVAL); + } + int ret = knot_edns_reserve_option(pkt->opt_rr, + KNOT_EDNS_OPTION_COOKIE, + cookie_size, &data, &pkt->mm); + if (ret != KNOT_EOK) { + return kr_error(ret); + } + + memcpy(data, input->clnt_cookie, input->clnt_cookie_len); + cookie_size = alg->srvr_cookie_size; + ret = alg->gen_func(input, data + input->clnt_cookie_len, &cookie_size); + if (ret != kr_ok()) { + /* TODO -- Delete COOKIE option. */ + return ret; + } + + return ret; +} + +int kr_pkt_set_ext_rcode(knot_pkt_t *pkt, uint16_t whole_rcode) +{ + if (!pkt || !knot_pkt_has_edns(pkt)) { + return kr_error(EINVAL); + } + + uint8_t rcode = whole_rcode & 0x0f; + uint8_t ext_rcode = whole_rcode >> 4; + knot_wire_set_rcode(pkt->wire, rcode); + knot_edns_set_ext_rcode(pkt->opt_rr, ext_rcode); + + return kr_ok(); +} + +uint8_t *kr_is_cookie_query(const knot_pkt_t *pkt) +{ + if (!pkt || knot_wire_get_qdcount(pkt->wire) > 0) { + return false; + } + + if (knot_wire_get_qr(pkt->wire) != 0 || !pkt->opt_rr) { + return false; + } + + return knot_edns_get_option(pkt->opt_rr, KNOT_EDNS_OPTION_COOKIE); +} + +int kr_parse_cookie_opt(uint8_t *cookie_opt, struct kr_dns_cookies *cookies) +{ + if (!cookie_opt || !cookies) { + kr_error(EINVAL); + } + + const uint8_t *cookie_data = knot_edns_opt_get_data(cookie_opt); + uint16_t cookie_len = knot_edns_opt_get_length(cookie_opt); + assert(cookie_data && cookie_len); + + int ret = knot_edns_opt_cookie_parse(cookie_data, cookie_len, + &cookies->cc, &cookies->cc_len, + &cookies->sc, &cookies->sc_len); + + return (ret == KNOT_EOK) ? kr_ok() : kr_error(EINVAL); +} diff --git a/lib/cookies/control.h b/lib/cookies/control.h index a843ce50e70510859f45ddcbd20fe0be2435332f..8d481c042e556a5cc212305e38fc82d4437b1fb7 100644 --- a/lib/cookies/control.h +++ b/lib/cookies/control.h @@ -21,6 +21,7 @@ #include <stdbool.h> #include "lib/cookies/alg_clnt.h" +#include "lib/cookies/alg_srvr.h" #include "lib/cache.h" #include "lib/defines.h" @@ -74,7 +75,7 @@ KR_EXPORT extern struct kr_cookie_ctx kr_glob_cookie_ctx; /** - * Insert a DNS cookie into query packet. + * @brief Insert a DNS cookie into query packet. * @note The packet must already contain ENDS section. * @param clnt_cntrl Client cookie control structure. * @param cookie_cache Cookie cache. @@ -87,3 +88,43 @@ 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); + +/** + * @brief Add cookies into answer. + * @note Data are only added into the OPT RR. + * @param input input data to generate server cookie from + * @param alg algorithm to use + * @param pkt packet which to put cookie into + * @return kr_ok() or error code + */ +KR_EXPORT +int kr_answer_opt_rr_add_cookies(const struct kr_srvr_cookie_input *input, + const struct kr_srvr_cookie_alg_descr *alg, + knot_pkt_t *pkt); + +/** + * @brief Set RCODE and extended RCODE. + * @param pkt DNS packet + * @param whole_rcode RCODE value + * @return kr_ok() or error code + */ +KR_EXPORT +int kr_pkt_set_ext_rcode(knot_pkt_t *pkt, uint16_t whole_rcode); + +/** + * @brief Check whether packet is a server cookie request. + * @param pkt Packet to be examined. + * @param cookies Received cookies. + * @return Pointer to entire cookie option if is a cookie query, NULL else. + */ +KR_EXPORT +uint8_t *kr_is_cookie_query(const knot_pkt_t *pkt); + +/** + * @brief Parse cookies from cookie option. + * @param cookie_opt Cookie option. + * @param cookies Cookie structure to be set. + * @return kr_ok() on success, error if cookies are malformed. + */ +KR_EXPORT +int kr_parse_cookie_opt(uint8_t *cookie_opt, struct kr_dns_cookies *cookies); diff --git a/lib/layer/cookiemonster.c b/lib/layer/cookiemonster.c index 3878837554b61531f0216f7a730f8ca9a62699d0..b0851bdb97f6c254f09c0a54a7dd3613465c5231 100644 --- a/lib/layer/cookiemonster.c +++ b/lib/layer/cookiemonster.c @@ -373,47 +373,13 @@ static int check_response(knot_layer_t *ctx, knot_pkt_t *pkt) return ctx->state; } -static int knot_pkt_set_ext_rcode(knot_pkt_t *pkt, uint16_t whole_rcode) +static inline uint8_t *req_cookie_option(struct kr_request *req) { - assert(pkt && knot_pkt_has_edns(pkt)); - - uint8_t rcode = whole_rcode & 0x0f; - uint8_t ext_rcode = whole_rcode >> 4; - knot_wire_set_rcode(pkt->wire, rcode); - knot_edns_set_ext_rcode(pkt->opt_rr, ext_rcode); - - return kr_ok(); -} - -/** Only adds cookies into the OPT RR. */ -static int answer_opt_rr_add_cookies(knot_pkt_t *answer, - const struct kr_srvr_cookie_input *input, - const struct kr_srvr_cookie_alg_descr *alg) -{ - assert(answer && input && alg); - - size_t cookie_size = input->clnt_cookie_len + alg->srvr_cookie_size; - uint8_t *data = NULL; - - if (!answer->opt_rr) { - kr_error(EINVAL); - } - int ret = knot_edns_reserve_option(answer->opt_rr, - KNOT_EDNS_OPTION_COOKIE, - cookie_size, &data, &answer->mm); - if (ret != KNOT_EOK) { - return kr_error(ret); + if (!req || !req->qsource.opt) { + return NULL; } - memcpy(data, input->clnt_cookie, input->clnt_cookie_len); - cookie_size = alg->srvr_cookie_size; - ret = alg->gen_func(input, data + input->clnt_cookie_len, &cookie_size); - if (ret != kr_ok()) { - /* TODO -- Delete COOKIE option. */ - return ret; - } - - return ret; + return knot_edns_get_option(req->qsource.opt, KNOT_EDNS_OPTION_COOKIE); } /* TODO -- DNS cookie request. */ @@ -427,75 +393,67 @@ static int check_request(knot_layer_t *ctx, void *module_param) } struct kr_request *req = ctx->data; - const knot_rrset_t *req_opt_rr = req->qsource.opt; - - if (!req_opt_rr) { - return ctx->state; - } - - uint8_t *req_cookie_opt = knot_edns_get_option(req_opt_rr, - KNOT_EDNS_OPTION_COOKIE); + uint8_t *req_cookie_opt = req_cookie_option(req); if (!req_cookie_opt) { - return ctx->state; + return ctx->state; /* Don't do anything without cookies. */ } - const uint8_t *req_cookie_data = knot_edns_opt_get_data(req_cookie_opt); - uint16_t req_cookie_len = knot_edns_opt_get_length(req_cookie_opt); - assert(req_cookie_data && req_cookie_len); - - const uint8_t *req_cc = NULL, *req_sc = NULL; - uint16_t req_cc_len = 0, req_sc_len = 0; - int ret = knot_edns_opt_cookie_parse(req_cookie_data, req_cookie_len, - &req_cc, &req_cc_len, - &req_sc, &req_sc_len); - if (ret != KNOT_EOK) { - /* Generate FORMERR response because malformed DNS cookie. */ - DEBUG_MSG(NULL, "%s\n", "got malformed DNS cookie in request"); + struct kr_dns_cookies cookies = { 0, }; + int ret = kr_parse_cookie_opt(req_cookie_opt, &cookies); + if (ret != kr_ok()) { + /* FORMERR -- malformed cookies. */ + DEBUG_MSG(NULL, "%s\n", "request with malformed cookie"); knot_wire_set_rcode(req->answer->wire, KNOT_RCODE_FORMERR); return KNOT_STATE_FAIL | KNOT_STATE_DONE; } - assert(req_cc_len == KNOT_OPT_COOKIE_CLNT); bool ignore_badcookie = true; /* TODO -- Occasionally ignore? */ - if (!req_sc) { - /* TODO -- Silently discard? */ + struct kr_srvr_cookie_ctx *srvr_cntrl = &kr_glob_cookie_ctx.srvr; + if (!req->qsource.addr || + !srvr_cntrl->current.ssec || !srvr_cntrl->current.salg) { + DEBUG_MSG(NULL, "%s\n", "missing server cookie context"); + return KNOT_STATE_FAIL; + } + + int return_state = ctx->state; + + struct kr_srvr_cookie_check_ctx check_ctx = { + .clnt_sockaddr = req->qsource.addr, + .secret_data = srvr_cntrl->current.ssec->data, + .secret_len = srvr_cntrl->current.ssec->size + }; + + struct kr_srvr_cookie_input input = { + .clnt_cookie = cookies.cc, + .clnt_cookie_len = cookies.cc_len, + .nonce = kr_rand_uint(UINT32_MAX), + .time = req->current_query->timestamp.tv_sec, + .srvr_data = &check_ctx + }; + + if (!cookies.sc) { + /* Request has no server cookie. TODO -- Silently discard? */ if (!ignore_badcookie) { /* Generate BADCOOKIE response. */ DEBUG_MSG(NULL, "%s\n", - "missing server DNS cookie in request"); + "request is missing server cookie"); if (!knot_pkt_has_edns(req->answer)) { DEBUG_MSG(NULL, "%s\n", "missing EDNS section in prepared answer"); return KNOT_STATE_FAIL; } - knot_pkt_set_ext_rcode(req->answer, - KNOT_RCODE_BADCOOKIE); - return KNOT_STATE_FAIL | KNOT_STATE_DONE; + kr_pkt_set_ext_rcode(req->answer, + KNOT_RCODE_BADCOOKIE); + return_state = KNOT_STATE_FAIL | KNOT_STATE_DONE; } - DEBUG_MSG(NULL, "%s\n", - "ignoring missing server DNS cookie in request"); - return ctx->state; + goto answer_add_cookies; } /* Check server cookie obtained in request. */ - struct kr_srvr_cookie_ctx *srvr_cntrl = &kr_glob_cookie_ctx.srvr; - - if (!req->qsource.addr || - !srvr_cntrl->current.ssec || !srvr_cntrl->current.salg) { - DEBUG_MSG(NULL, "%s\n", "no server DNS cookie context data"); - return KNOT_STATE_FAIL; - } - - struct kr_srvr_cookie_check_ctx check_ctx = { - .clnt_sockaddr = req->qsource.addr, - .secret_data = srvr_cntrl->current.ssec->data, - .secret_len = srvr_cntrl->current.ssec->size - }; - - ret = kr_srvr_cookie_check(req_cc, req_cc_len, req_sc, req_sc_len, - &check_ctx, srvr_cntrl->current.salg); + ret = kr_srvr_cookie_check(&cookies, &check_ctx, + srvr_cntrl->current.salg); if (ret == kr_error(EBADMSG) && srvr_cntrl->recent.ssec && srvr_cntrl->recent.salg) { /* Try recent algorithm. */ @@ -504,8 +462,7 @@ static int check_request(knot_layer_t *ctx, void *module_param) .secret_data = srvr_cntrl->recent.ssec->data, .secret_len = srvr_cntrl->recent.ssec->size }; - ret = kr_srvr_cookie_check(req_cc, req_cc_len, req_sc, - req_sc_len, &recent_ctx, + ret = kr_srvr_cookie_check(&cookies, &recent_ctx, srvr_cntrl->recent.salg); } if (ret != kr_ok()) { @@ -513,34 +470,26 @@ static int check_request(knot_layer_t *ctx, void *module_param) if (!ignore_badcookie) { /* Generate BADCOOKIE response. */ DEBUG_MSG(NULL, "%s\n", - "invalid server DNS cookie in request"); + "request has invalid server cookie"); if (!knot_pkt_has_edns(req->answer)) { DEBUG_MSG(NULL, "%s\n", "missing EDNS section in prepared answer"); return KNOT_STATE_FAIL; } - knot_pkt_set_ext_rcode(req->answer, - KNOT_RCODE_BADCOOKIE); - return KNOT_STATE_FAIL | KNOT_STATE_DONE; + kr_pkt_set_ext_rcode(req->answer, + KNOT_RCODE_BADCOOKIE); + return_state = KNOT_STATE_FAIL | KNOT_STATE_DONE; } - DEBUG_MSG(NULL, "%s\n", - "ignoring invalid server DNS cookie in request"); - return ctx->state; + goto answer_add_cookies; } - /* Server cookie OK. */ + /* Server cookie is OK. */ +answer_add_cookies: /* Add server cookie into response. */ - struct kr_srvr_cookie_input input = { - .clnt_cookie = req_cc, - .clnt_cookie_len = req_cc_len, - .nonce = 0, /*TODO -- Some pseudo-random value? */ - .time = req->current_query->timestamp.tv_sec, - .srvr_data = &check_ctx - }; - answer_opt_rr_add_cookies(req->answer, &input, srvr_cntrl->current.salg); - - return ctx->state; + kr_answer_opt_rr_add_cookies(&input, srvr_cntrl->current.salg, + req->answer); + return return_state; } /** Module implementation. */ diff --git a/lib/resolve.c b/lib/resolve.c index 076b08a116acac5e297d79e7a9bb1e45cd5bc977..0266de469fe7622fb5b21b78928df5bf0be375bf 100644 --- a/lib/resolve.c +++ b/lib/resolve.c @@ -389,6 +389,67 @@ int kr_resolve_begin(struct kr_request *request, struct kr_context *ctx, knot_pk return KNOT_STATE_CONSUME; } +#if defined(ENABLE_COOKIES) +/** + * @brief Put cookie into answer packet. + * @param clnt_sockaddr client socket address + * @param srvr_cntrl control structure of the server cookie algorithm + * @param cookies obtained cookies + * @param answer answer packet + * @return state + */ +static int cookie_answer(const void *clnt_sockaddr, + const struct kr_srvr_cookie_ctx *srvr_cntrl, + struct kr_dns_cookies *cookies, knot_pkt_t *answer) +{ + assert(srvr_cntrl && cookies && answer); + + /* Initialise answer. */ + knot_wire_set_qr(answer->wire); + knot_wire_clear_aa(answer->wire); + knot_wire_set_ra(answer->wire); + knot_wire_set_rcode(answer->wire, KNOT_RCODE_NOERROR); + + struct kr_srvr_cookie_check_ctx check_ctx = { + .clnt_sockaddr = clnt_sockaddr, + .secret_data = srvr_cntrl->current.ssec->data, + .secret_len = srvr_cntrl->current.ssec->size + }; + + struct timeval tv; + gettimeofday(&tv, NULL); + + struct kr_srvr_cookie_input input = { + .clnt_cookie = cookies->cc, + .clnt_cookie_len = cookies->cc_len, + .nonce = kr_rand_uint(UINT32_MAX), + .time = tv.tv_sec, + .srvr_data = &check_ctx + }; + + int ret = kr_answer_opt_rr_add_cookies(&input, + srvr_cntrl->current.salg, + answer); + if (ret != kr_ok()) { + return KNOT_STATE_FAIL; + } + + if (!cookies->sc) { + return KNOT_STATE_DONE; + } + + /* Check server cookie only with current settings. */ + ret = kr_srvr_cookie_check(cookies, &check_ctx, + srvr_cntrl->current.salg); + if (ret != kr_ok()) { + kr_pkt_set_ext_rcode(answer, KNOT_RCODE_BADCOOKIE); + return KNOT_STATE_FAIL | KNOT_STATE_DONE; + } + + return KNOT_STATE_DONE; +} +#endif /* defined(ENABLE_COOKIES) */ + static int resolve_query(struct kr_request *request, const knot_pkt_t *packet) { struct kr_rplan *rplan = &request->rplan; @@ -397,7 +458,24 @@ static int resolve_query(struct kr_request *request, const knot_pkt_t *packet) uint16_t qtype = knot_pkt_qtype(packet); struct kr_query *qry = kr_rplan_push(rplan, NULL, qname, qclass, qtype); if (!qry) { +#if defined(ENABLE_COOKIES) + /* May be a DNS cookies query. */ + struct kr_dns_cookies cookies = { 0, }; + uint8_t *cookie_opt = kr_is_cookie_query(packet); + if (cookie_opt && kr_glob_cookie_ctx.clnt.enabled) { + if (kr_ok() != kr_parse_cookie_opt(cookie_opt, + &cookies)) { + /* TODO -- KNOT_RCODE_FORMERR? */ + return KNOT_STATE_FAIL; + } + } + + return cookie_answer(request->qsource.addr, + &kr_glob_cookie_ctx.srvr, + &cookies, request->answer); +#else /* !defined(ENABLE_COOKIES) */ return KNOT_STATE_FAIL; +#endif /* defined(ENABLE_COOKIES) */ } /* Deferred zone cut lookup for this query. */