1 /* Copyright (C) 2016-2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2 * SPDX-License-Identifier: GPL-3.0-or-later
3 */
4
5 #include <libknot/rrtype/opt.h>
6 #include <libknot/rrtype/opt-cookie.h>
7
8 #include "lib/cookies/helper.h"
9 #include "lib/defines.h"
10
11 /**
12 * @brief Check whether there is a cached cookie that matches the current
13 * client cookie.
14 */
peek_and_check_cc(kr_cookie_lru_t * cache,const void * sa,const uint8_t * cc,uint16_t cc_len)15 static const uint8_t *peek_and_check_cc(kr_cookie_lru_t *cache, const void *sa,
16 const uint8_t *cc, uint16_t cc_len)
17 {
18 if (kr_fails_assert(cache && sa && cc && cc_len))
19 return NULL;
20
21 const uint8_t *cached_opt = kr_cookie_lru_get(cache, sa);
22 if (!cached_opt)
23 return NULL;
24
25 const uint8_t *cached_cc = knot_edns_opt_get_data((uint8_t *) cached_opt);
26
27 if (cc_len == KNOT_OPT_COOKIE_CLNT &&
28 0 == memcmp(cc, cached_cc, cc_len)) {
29 return cached_opt;
30 }
31
32 return NULL;
33 }
34
35 /**
36 * @brief Put a client cookie into the RR Set.
37 */
opt_rr_put_cookie(knot_rrset_t * opt_rr,uint8_t * data,uint16_t data_len,knot_mm_t * mm)38 static int opt_rr_put_cookie(knot_rrset_t *opt_rr, uint8_t *data,
39 uint16_t data_len, knot_mm_t *mm)
40 {
41 if (kr_fails_assert(opt_rr && data && data_len > 0))
42 return kr_error(EINVAL);
43
44 const uint8_t *cc = NULL, *sc = NULL;
45 uint16_t cc_len = 0, sc_len = 0;
46
47 int ret = knot_edns_opt_cookie_parse(data, data_len, &cc, &cc_len,
48 &sc, &sc_len);
49 if (ret != KNOT_EOK)
50 return kr_error(EINVAL);
51 if (kr_fails_assert(data_len == cc_len + sc_len))
52 return kr_error(EINVAL);
53
54 uint16_t cookies_size = data_len;
55 uint8_t *cookies_data = NULL;
56
57 ret = knot_edns_reserve_unique_option(opt_rr, KNOT_EDNS_OPTION_COOKIE,
58 cookies_size, &cookies_data, mm);
59 if (ret != KNOT_EOK)
60 return kr_error(EINVAL);
61 if (kr_fails_assert(cookies_data))
62 return kr_error(EINVAL);
63
64 cookies_size = knot_edns_opt_cookie_write(cc, cc_len, sc, sc_len,
65 cookies_data, cookies_size);
66 if (cookies_size == 0)
67 return kr_error(EINVAL);
68 if (kr_fails_assert(cookies_size == data_len))
69 return kr_error(EINVAL);
70
71 return kr_ok();
72 }
73
74 /**
75 * @brief Puts entire EDNS option into the RR Set.
76 */
opt_rr_put_cookie_opt(knot_rrset_t * opt_rr,uint8_t * option,knot_mm_t * mm)77 static int opt_rr_put_cookie_opt(knot_rrset_t *opt_rr, uint8_t *option, knot_mm_t *mm)
78 {
79 if (kr_fails_assert(opt_rr && option))
80 return kr_error(EINVAL);
81
82 uint16_t opt_code = knot_edns_opt_get_code(option);
83 if (opt_code != KNOT_EDNS_OPTION_COOKIE)
84 return kr_error(EINVAL);
85
86 uint16_t opt_len = knot_edns_opt_get_length(option);
87 uint8_t *opt_data = knot_edns_opt_get_data(option);
88 if (!opt_data || opt_len == 0)
89 return kr_error(EINVAL);
90
91 return opt_rr_put_cookie(opt_rr, opt_data, opt_len, mm);
92 }
93
kr_request_put_cookie(const struct kr_cookie_comp * clnt_comp,kr_cookie_lru_t * cookie_cache,const struct sockaddr * clnt_sa,const struct sockaddr * srvr_sa,struct kr_request * req)94 int kr_request_put_cookie(const struct kr_cookie_comp *clnt_comp,
95 kr_cookie_lru_t *cookie_cache,
96 const struct sockaddr *clnt_sa,
97 const struct sockaddr *srvr_sa,
98 struct kr_request *req)
99 {
100 if (!clnt_comp || !req)
101 return kr_error(EINVAL);
102
103 if (!req->ctx->opt_rr)
104 return kr_ok();
105
106 if (!clnt_comp->secr || (clnt_comp->alg_id < 0) || !cookie_cache)
107 return kr_error(EINVAL);
108
109 /*
110 * Generate client cookie from client address, server address and
111 * secret quantity.
112 */
113 struct knot_cc_input input = {
114 .clnt_sockaddr = clnt_sa,
115 .srvr_sockaddr = srvr_sa,
116 .secret_data = clnt_comp->secr->data,
117 .secret_len = clnt_comp->secr->size
118 };
119 uint8_t cc[KNOT_OPT_COOKIE_CLNT];
120 uint16_t cc_len = KNOT_OPT_COOKIE_CLNT;
121 const struct knot_cc_alg *cc_alg = kr_cc_alg_get(clnt_comp->alg_id);
122 if (!cc_alg)
123 return kr_error(EINVAL);
124 if (kr_fails_assert(cc_alg->gen_func))
125 return kr_error(EINVAL);
126 cc_len = cc_alg->gen_func(&input, cc, cc_len);
127 if (cc_len != KNOT_OPT_COOKIE_CLNT)
128 return kr_error(EINVAL);
129
130 const uint8_t *cached_cookie = peek_and_check_cc(cookie_cache,
131 srvr_sa, cc, cc_len);
132
133 /* Add cookie option. */
134 int ret;
135 if (cached_cookie) {
136 ret = opt_rr_put_cookie_opt(req->ctx->opt_rr,
137 (uint8_t *)cached_cookie,
138 req->ctx->pool);
139 } else {
140 ret = opt_rr_put_cookie(req->ctx->opt_rr, cc, cc_len,
141 req->ctx->pool);
142 }
143
144 return ret;
145 }
146
kr_answer_write_cookie(struct knot_sc_input * sc_input,const struct kr_nonce_input * nonce,const struct knot_sc_alg * alg,knot_pkt_t * pkt)147 int kr_answer_write_cookie(struct knot_sc_input *sc_input,
148 const struct kr_nonce_input *nonce,
149 const struct knot_sc_alg *alg, knot_pkt_t *pkt)
150 {
151 if (!sc_input || !sc_input->cc || sc_input->cc_len == 0)
152 return kr_error(EINVAL);
153
154 if (!sc_input->srvr_data || !sc_input->srvr_data->clnt_sockaddr ||
155 !sc_input->srvr_data->secret_data ||
156 !sc_input->srvr_data->secret_len) {
157 return kr_error(EINVAL);
158 }
159
160 if (!nonce)
161 return kr_error(EINVAL);
162
163 if (!alg || !alg->hash_size || !alg->hash_func)
164 return kr_error(EINVAL);
165
166 if (!pkt || !pkt->opt_rr)
167 return kr_error(EINVAL);
168
169 uint16_t nonce_len = KR_NONCE_LEN;
170 uint16_t hash_len = alg->hash_size;
171
172 /*
173 * Space for cookie is reserved inside the EDNS OPT RR of
174 * the answer packet.
175 */
176 uint8_t *cookie = NULL;
177 uint16_t cookie_len = knot_edns_opt_cookie_data_len(sc_input->cc_len,
178 nonce_len + hash_len);
179 if (cookie_len == 0)
180 return kr_error(EINVAL);
181
182 int ret = knot_edns_reserve_unique_option(pkt->opt_rr,
183 KNOT_EDNS_OPTION_COOKIE,
184 cookie_len, &cookie,
185 &pkt->mm);
186 if (ret != KNOT_EOK)
187 return kr_error(ENOMEM);
188 if (kr_fails_assert(cookie))
189 return kr_error(EFAULT);
190
191 /*
192 * Function knot_edns_opt_cookie_data_len() returns the sum of its
193 * parameters or zero. Anyway, let's check again.
194 */
195 if (cookie_len < (sc_input->cc_len + nonce_len + hash_len))
196 return kr_error(EINVAL);
197
198 /* Copy client cookie data portion. */
199 memcpy(cookie, sc_input->cc, sc_input->cc_len);
200
201 if (nonce_len) {
202 /* Write nonce data portion. */
203 kr_nonce_write_wire(cookie + sc_input->cc_len, nonce_len,
204 nonce);
205 /* Adjust input for written nonce value. */
206 sc_input->nonce = cookie + sc_input->cc_len;
207 sc_input->nonce_len = nonce_len;
208 }
209
210 hash_len = alg->hash_func(sc_input,
211 cookie + sc_input->cc_len + nonce_len,
212 hash_len);
213 /* Zero nonce values. */
214 sc_input->nonce = NULL;
215 sc_input->nonce_len = 0;
216
217 return (hash_len != 0) ? kr_ok() : kr_error(EINVAL);
218 }
219
kr_pkt_set_ext_rcode(knot_pkt_t * pkt,uint16_t whole_rcode)220 int kr_pkt_set_ext_rcode(knot_pkt_t *pkt, uint16_t whole_rcode)
221 {
222 /*
223 * RFC6891 6.1.3 -- extended RCODE forms the upper 8 bits of whole
224 * 12-bit RCODE (together with the 4 bits of 'normal' RCODE).
225 *
226 * | 11 10 09 08 07 06 05 04 | 03 02 01 00 |
227 * | 12-bit whole RCODE |
228 * | 8-bit extended RCODE | 4-bit RCODE |
229 */
230
231 if (!pkt || !knot_pkt_has_edns(pkt))
232 return kr_error(EINVAL);
233
234 uint8_t rcode = whole_rcode & 0x0f;
235 uint8_t ext_rcode = whole_rcode >> 4;
236 knot_wire_set_rcode(pkt->wire, rcode);
237 knot_edns_set_ext_rcode(pkt->opt_rr, ext_rcode);
238
239 return kr_ok();
240 }
241
kr_no_question_cookie_query(const knot_pkt_t * pkt)242 uint8_t *kr_no_question_cookie_query(const knot_pkt_t *pkt)
243 {
244 if (!pkt || knot_wire_get_qdcount(pkt->wire) > 0)
245 return false;
246
247 if (knot_wire_get_qr(pkt->wire) != 0 || !pkt->opt_rr)
248 return false;
249
250 return knot_edns_get_option(pkt->opt_rr, KNOT_EDNS_OPTION_COOKIE);
251 }
252
kr_parse_cookie_opt(uint8_t * cookie_opt,struct knot_dns_cookies * cookies)253 int kr_parse_cookie_opt(uint8_t *cookie_opt, struct knot_dns_cookies *cookies)
254 {
255 if (!cookie_opt || !cookies)
256 return kr_error(EINVAL);
257
258 const uint8_t *cookie_data = knot_edns_opt_get_data(cookie_opt);
259 uint16_t cookie_len = knot_edns_opt_get_length(cookie_opt);
260 if (!cookie_data || cookie_len == 0)
261 return kr_error(EINVAL);
262
263 int ret = knot_edns_opt_cookie_parse(cookie_data, cookie_len,
264 &cookies->cc, &cookies->cc_len,
265 &cookies->sc, &cookies->sc_len);
266
267 return (ret == KNOT_EOK) ? kr_ok() : kr_error(EINVAL);
268 }
269