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