Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * util/edns.c - handle base EDNS options. |
3 | | * |
4 | | * Copyright (c) 2018, NLnet Labs. All rights reserved. |
5 | | * |
6 | | * This software is open source. |
7 | | * |
8 | | * Redistribution and use in source and binary forms, with or without |
9 | | * modification, are permitted provided that the following conditions |
10 | | * are met: |
11 | | * |
12 | | * Redistributions of source code must retain the above copyright notice, |
13 | | * this list of conditions and the following disclaimer. |
14 | | * |
15 | | * Redistributions in binary form must reproduce the above copyright notice, |
16 | | * this list of conditions and the following disclaimer in the documentation |
17 | | * and/or other materials provided with the distribution. |
18 | | * |
19 | | * Neither the name of the NLNET LABS nor the names of its contributors may |
20 | | * be used to endorse or promote products derived from this software without |
21 | | * specific prior written permission. |
22 | | * |
23 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
24 | | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
25 | | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
26 | | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
27 | | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
28 | | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
29 | | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
30 | | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
31 | | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
32 | | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
33 | | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
34 | | */ |
35 | | |
36 | | /** |
37 | | * \file |
38 | | * |
39 | | * This file contains functions for base EDNS options. |
40 | | */ |
41 | | |
42 | | #include "config.h" |
43 | | #include "util/edns.h" |
44 | | #include "util/config_file.h" |
45 | | #include "util/netevent.h" |
46 | | #include "util/net_help.h" |
47 | | #include "util/regional.h" |
48 | | #include "util/rfc_1982.h" |
49 | | #include "util/siphash.h" |
50 | | #include "util/data/msgparse.h" |
51 | | #include "util/data/msgreply.h" |
52 | | #include "sldns/sbuffer.h" |
53 | | |
54 | | struct edns_strings* edns_strings_create(void) |
55 | 0 | { |
56 | 0 | struct edns_strings* edns_strings = calloc(1, |
57 | 0 | sizeof(struct edns_strings)); |
58 | 0 | if(!edns_strings) |
59 | 0 | return NULL; |
60 | 0 | if(!(edns_strings->region = regional_create())) { |
61 | 0 | edns_strings_delete(edns_strings); |
62 | 0 | return NULL; |
63 | 0 | } |
64 | 0 | return edns_strings; |
65 | 0 | } |
66 | | |
67 | | void edns_strings_delete(struct edns_strings* edns_strings) |
68 | 0 | { |
69 | 0 | if(!edns_strings) |
70 | 0 | return; |
71 | 0 | regional_destroy(edns_strings->region); |
72 | 0 | free(edns_strings); |
73 | 0 | } |
74 | | |
75 | | static int |
76 | | edns_strings_client_insert(struct edns_strings* edns_strings, |
77 | | struct sockaddr_storage* addr, socklen_t addrlen, int net, |
78 | | const char* string) |
79 | 0 | { |
80 | 0 | struct edns_string_addr* esa = regional_alloc_zero(edns_strings->region, |
81 | 0 | sizeof(struct edns_string_addr)); |
82 | 0 | if(!esa) |
83 | 0 | return 0; |
84 | 0 | esa->string_len = strlen(string); |
85 | 0 | esa->string = regional_alloc_init(edns_strings->region, string, |
86 | 0 | esa->string_len); |
87 | 0 | if(!esa->string) |
88 | 0 | return 0; |
89 | 0 | if(!addr_tree_insert(&edns_strings->client_strings, &esa->node, addr, |
90 | 0 | addrlen, net)) { |
91 | 0 | verbose(VERB_QUERY, "duplicate EDNS client string ignored."); |
92 | 0 | } |
93 | 0 | return 1; |
94 | 0 | } |
95 | | |
96 | | int edns_strings_apply_cfg(struct edns_strings* edns_strings, |
97 | | struct config_file* config) |
98 | 0 | { |
99 | 0 | struct config_str2list* c; |
100 | 0 | regional_free_all(edns_strings->region); |
101 | 0 | addr_tree_init(&edns_strings->client_strings); |
102 | |
|
103 | 0 | for(c=config->edns_client_strings; c; c=c->next) { |
104 | 0 | struct sockaddr_storage addr; |
105 | 0 | socklen_t addrlen; |
106 | 0 | int net; |
107 | 0 | log_assert(c->str && c->str2); |
108 | |
|
109 | 0 | if(!netblockstrtoaddr(c->str, UNBOUND_DNS_PORT, &addr, &addrlen, |
110 | 0 | &net)) { |
111 | 0 | log_err("cannot parse EDNS client string IP netblock: " |
112 | 0 | "%s", c->str); |
113 | 0 | return 0; |
114 | 0 | } |
115 | 0 | if(!edns_strings_client_insert(edns_strings, &addr, addrlen, |
116 | 0 | net, c->str2)) { |
117 | 0 | log_err("out of memory while adding EDNS strings"); |
118 | 0 | return 0; |
119 | 0 | } |
120 | 0 | } |
121 | 0 | edns_strings->client_string_opcode = config->edns_client_string_opcode; |
122 | |
|
123 | 0 | addr_tree_init_parents(&edns_strings->client_strings); |
124 | 0 | return 1; |
125 | 0 | } |
126 | | |
127 | | struct edns_string_addr* |
128 | | edns_string_addr_lookup(rbtree_type* tree, struct sockaddr_storage* addr, |
129 | | socklen_t addrlen) |
130 | 0 | { |
131 | 0 | return (struct edns_string_addr*)addr_tree_lookup(tree, addr, addrlen); |
132 | 0 | } |
133 | | |
134 | | size_t |
135 | | edns_strings_get_mem(struct edns_strings* edns_strings) |
136 | 0 | { |
137 | 0 | if(!edns_strings) return 0; |
138 | 0 | return regional_get_mem(edns_strings->region) + sizeof(*edns_strings); |
139 | 0 | } |
140 | | |
141 | | void |
142 | | edns_strings_swap_tree(struct edns_strings* edns_strings, |
143 | | struct edns_strings* data) |
144 | 0 | { |
145 | 0 | rbtree_type tree = edns_strings->client_strings; |
146 | 0 | uint16_t opcode = edns_strings->client_string_opcode; |
147 | 0 | struct regional* region = edns_strings->region; |
148 | |
|
149 | 0 | edns_strings->client_strings = data->client_strings; |
150 | 0 | edns_strings->client_string_opcode = data->client_string_opcode; |
151 | 0 | edns_strings->region = data->region; |
152 | 0 | data->client_strings = tree; |
153 | 0 | data->client_string_opcode = opcode; |
154 | 0 | data->region = region; |
155 | 0 | } |
156 | | |
157 | | uint8_t* |
158 | | edns_cookie_server_hash(const uint8_t* in, const uint8_t* secret, int v4, |
159 | | uint8_t* hash) |
160 | 0 | { |
161 | 0 | v4?siphash(in, 20, secret, hash, 8):siphash(in, 32, secret, hash, 8); |
162 | 0 | return hash; |
163 | 0 | } |
164 | | |
165 | | void |
166 | | edns_cookie_server_write(uint8_t* buf, const uint8_t* secret, int v4, |
167 | | uint32_t timestamp) |
168 | 0 | { |
169 | 0 | uint8_t hash[8]; |
170 | 0 | buf[ 8] = 1; /* Version */ |
171 | 0 | buf[ 9] = 0; /* Reserved */ |
172 | 0 | buf[10] = 0; /* Reserved */ |
173 | 0 | buf[11] = 0; /* Reserved */ |
174 | 0 | sldns_write_uint32(buf + 12, timestamp); |
175 | 0 | (void)edns_cookie_server_hash(buf, secret, v4, hash); |
176 | 0 | memcpy(buf + 16, hash, 8); |
177 | 0 | } |
178 | | |
179 | | enum edns_cookie_val_status |
180 | | edns_cookie_server_validate(const uint8_t* cookie, size_t cookie_len, |
181 | | const uint8_t* secret, size_t secret_len, int v4, |
182 | | const uint8_t* hash_input, uint32_t now) |
183 | 0 | { |
184 | 0 | uint8_t hash[8]; |
185 | 0 | uint32_t timestamp; |
186 | 0 | uint32_t subt_1982 = 0; /* Initialize for the compiler; unused value */ |
187 | 0 | int comp_1982; |
188 | 0 | if(cookie_len != 24) |
189 | | /* RFC9018 cookies are 24 bytes long */ |
190 | 0 | return COOKIE_STATUS_CLIENT_ONLY; |
191 | 0 | if(secret_len != 16 || /* RFC9018 cookies have 16 byte secrets */ |
192 | 0 | cookie[8] != 1) /* RFC9018 cookies are cookie version 1 */ |
193 | 0 | return COOKIE_STATUS_INVALID; |
194 | 0 | timestamp = sldns_read_uint32(cookie + 12); |
195 | 0 | if((comp_1982 = compare_1982(now, timestamp)) > 0 |
196 | 0 | && (subt_1982 = subtract_1982(timestamp, now)) > 3600) |
197 | | /* Cookie is older than 1 hour (see RFC9018 Section 4.3.) */ |
198 | 0 | return COOKIE_STATUS_EXPIRED; |
199 | 0 | if(comp_1982 <= 0 && subtract_1982(now, timestamp) > 300) |
200 | | /* Cookie time is more than 5 minutes in the future. |
201 | | * (see RFC9018 Section 4.3.) */ |
202 | 0 | return COOKIE_STATUS_FUTURE; |
203 | 0 | if(memcmp(edns_cookie_server_hash(hash_input, secret, v4, hash), |
204 | 0 | cookie + 16, 8) != 0) |
205 | | /* Hashes do not match */ |
206 | 0 | return COOKIE_STATUS_INVALID; |
207 | 0 | if(comp_1982 > 0 && subt_1982 > 1800) |
208 | | /* Valid cookie but older than 30 minutes, so create a new one |
209 | | * anyway */ |
210 | 0 | return COOKIE_STATUS_VALID_RENEW; |
211 | 0 | return COOKIE_STATUS_VALID; |
212 | 0 | } |
213 | | |
214 | | struct cookie_secrets* |
215 | | cookie_secrets_create(void) |
216 | 0 | { |
217 | 0 | struct cookie_secrets* cookie_secrets = calloc(1, |
218 | 0 | sizeof(*cookie_secrets)); |
219 | 0 | if(!cookie_secrets) |
220 | 0 | return NULL; |
221 | 0 | lock_basic_init(&cookie_secrets->lock); |
222 | 0 | lock_protect(&cookie_secrets->lock, &cookie_secrets->cookie_count, |
223 | 0 | sizeof(cookie_secrets->cookie_count)); |
224 | 0 | lock_protect(&cookie_secrets->lock, cookie_secrets->cookie_secrets, |
225 | 0 | sizeof(cookie_secret_type)*UNBOUND_COOKIE_HISTORY_SIZE); |
226 | 0 | return cookie_secrets; |
227 | 0 | } |
228 | | |
229 | | void |
230 | | cookie_secrets_delete(struct cookie_secrets* cookie_secrets) |
231 | 0 | { |
232 | 0 | if(!cookie_secrets) |
233 | 0 | return; |
234 | 0 | lock_basic_destroy(&cookie_secrets->lock); |
235 | 0 | explicit_bzero(cookie_secrets->cookie_secrets, |
236 | 0 | sizeof(cookie_secret_type)*UNBOUND_COOKIE_HISTORY_SIZE); |
237 | 0 | free(cookie_secrets); |
238 | 0 | } |
239 | | |
240 | | /** Read the cookie secret file */ |
241 | | static int |
242 | | cookie_secret_file_read(struct cookie_secrets* cookie_secrets, |
243 | | char* cookie_secret_file) |
244 | 0 | { |
245 | 0 | char secret[UNBOUND_COOKIE_SECRET_SIZE * 2 + 2/*'\n' and '\0'*/]; |
246 | 0 | FILE* f; |
247 | 0 | int corrupt = 0; |
248 | 0 | size_t count; |
249 | |
|
250 | 0 | log_assert(cookie_secret_file != NULL); |
251 | 0 | cookie_secrets->cookie_count = 0; |
252 | 0 | f = fopen(cookie_secret_file, "r"); |
253 | | /* a non-existing cookie file is not an error */ |
254 | 0 | if( f == NULL ) { |
255 | 0 | if(errno != EPERM) { |
256 | 0 | log_err("Could not read cookie-secret-file '%s': %s", |
257 | 0 | cookie_secret_file, strerror(errno)); |
258 | 0 | return 0; |
259 | 0 | } |
260 | 0 | return 1; |
261 | 0 | } |
262 | | /* cookie secret file exists and is readable */ |
263 | 0 | for( count = 0; count < UNBOUND_COOKIE_HISTORY_SIZE; count++ ) { |
264 | 0 | size_t secret_len = 0; |
265 | 0 | ssize_t decoded_len = 0; |
266 | 0 | if( fgets(secret, sizeof(secret), f) == NULL ) { break; } |
267 | 0 | secret_len = strlen(secret); |
268 | 0 | if( secret_len == 0 ) { break; } |
269 | 0 | log_assert( secret_len <= sizeof(secret) ); |
270 | 0 | secret_len = secret[secret_len - 1] == '\n' ? secret_len - 1 : secret_len; |
271 | 0 | if( secret_len != UNBOUND_COOKIE_SECRET_SIZE * 2 ) { corrupt++; break; } |
272 | | /* needed for `hex_pton`; stripping potential `\n` */ |
273 | 0 | secret[secret_len] = '\0'; |
274 | 0 | decoded_len = hex_pton(secret, cookie_secrets->cookie_secrets[count].cookie_secret, |
275 | 0 | UNBOUND_COOKIE_SECRET_SIZE); |
276 | 0 | if( decoded_len != UNBOUND_COOKIE_SECRET_SIZE ) { corrupt++; break; } |
277 | 0 | cookie_secrets->cookie_count++; |
278 | 0 | } |
279 | 0 | fclose(f); |
280 | 0 | return corrupt == 0; |
281 | 0 | } |
282 | | |
283 | | int |
284 | | cookie_secrets_apply_cfg(struct cookie_secrets* cookie_secrets, |
285 | | char* cookie_secret_file) |
286 | 0 | { |
287 | 0 | if(!cookie_secrets) { |
288 | 0 | if(!cookie_secret_file || !cookie_secret_file[0]) |
289 | 0 | return 1; /* There is nothing to read anyway */ |
290 | 0 | log_err("Could not read cookie secrets, no structure alloced"); |
291 | 0 | return 0; |
292 | 0 | } |
293 | 0 | if(!cookie_secret_file_read(cookie_secrets, cookie_secret_file)) |
294 | 0 | return 0; |
295 | 0 | return 1; |
296 | 0 | } |
297 | | |
298 | | enum edns_cookie_val_status |
299 | | cookie_secrets_server_validate(const uint8_t* cookie, size_t cookie_len, |
300 | | struct cookie_secrets* cookie_secrets, int v4, |
301 | | const uint8_t* hash_input, uint32_t now) |
302 | 0 | { |
303 | 0 | size_t i; |
304 | 0 | enum edns_cookie_val_status cookie_val_status, |
305 | 0 | last = COOKIE_STATUS_INVALID; |
306 | 0 | if(!cookie_secrets) |
307 | 0 | return COOKIE_STATUS_INVALID; /* There are no cookie secrets.*/ |
308 | 0 | lock_basic_lock(&cookie_secrets->lock); |
309 | 0 | if(cookie_secrets->cookie_count == 0) { |
310 | 0 | lock_basic_unlock(&cookie_secrets->lock); |
311 | 0 | return COOKIE_STATUS_INVALID; /* There are no cookie secrets.*/ |
312 | 0 | } |
313 | 0 | for(i=0; i<cookie_secrets->cookie_count; i++) { |
314 | 0 | cookie_val_status = edns_cookie_server_validate(cookie, |
315 | 0 | cookie_len, |
316 | 0 | cookie_secrets->cookie_secrets[i].cookie_secret, |
317 | 0 | UNBOUND_COOKIE_SECRET_SIZE, v4, hash_input, now); |
318 | 0 | if(cookie_val_status == COOKIE_STATUS_VALID || |
319 | 0 | cookie_val_status == COOKIE_STATUS_VALID_RENEW) { |
320 | 0 | lock_basic_unlock(&cookie_secrets->lock); |
321 | | /* For staging cookies, write a fresh cookie. */ |
322 | 0 | if(i != 0) |
323 | 0 | return COOKIE_STATUS_VALID_RENEW; |
324 | 0 | return cookie_val_status; |
325 | 0 | } |
326 | 0 | if(last == COOKIE_STATUS_INVALID) |
327 | 0 | last = cookie_val_status; /* Store more interesting |
328 | | failure to return. */ |
329 | 0 | } |
330 | 0 | lock_basic_unlock(&cookie_secrets->lock); |
331 | 0 | return last; |
332 | 0 | } |
333 | | |
334 | | void add_cookie_secret(struct cookie_secrets* cookie_secrets, |
335 | | uint8_t* secret, size_t secret_len) |
336 | 0 | { |
337 | 0 | log_assert(secret_len == UNBOUND_COOKIE_SECRET_SIZE); |
338 | 0 | (void)secret_len; |
339 | 0 | if(!cookie_secrets) |
340 | 0 | return; |
341 | | |
342 | | /* New cookie secret becomes the staging secret (position 1) |
343 | | * unless there is no active cookie yet, then it becomes the active |
344 | | * secret. If the UNBOUND_COOKIE_HISTORY_SIZE > 2 then all staging cookies |
345 | | * are moved one position down. |
346 | | */ |
347 | 0 | if(cookie_secrets->cookie_count == 0) { |
348 | 0 | memcpy( cookie_secrets->cookie_secrets->cookie_secret |
349 | 0 | , secret, UNBOUND_COOKIE_SECRET_SIZE); |
350 | 0 | cookie_secrets->cookie_count = 1; |
351 | 0 | explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE); |
352 | 0 | return; |
353 | 0 | } |
354 | | #if UNBOUND_COOKIE_HISTORY_SIZE > 2 |
355 | | memmove( &cookie_secrets->cookie_secrets[2], &cookie_secrets->cookie_secrets[1] |
356 | | , sizeof(struct cookie_secret) * (UNBOUND_COOKIE_HISTORY_SIZE - 2)); |
357 | | #endif |
358 | 0 | memcpy( cookie_secrets->cookie_secrets[1].cookie_secret |
359 | 0 | , secret, UNBOUND_COOKIE_SECRET_SIZE); |
360 | 0 | cookie_secrets->cookie_count = cookie_secrets->cookie_count < UNBOUND_COOKIE_HISTORY_SIZE |
361 | 0 | ? cookie_secrets->cookie_count + 1 : UNBOUND_COOKIE_HISTORY_SIZE; |
362 | 0 | explicit_bzero(secret, UNBOUND_COOKIE_SECRET_SIZE); |
363 | 0 | } |
364 | | |
365 | | void activate_cookie_secret(struct cookie_secrets* cookie_secrets) |
366 | 0 | { |
367 | 0 | uint8_t active_secret[UNBOUND_COOKIE_SECRET_SIZE]; |
368 | 0 | if(!cookie_secrets) |
369 | 0 | return; |
370 | | /* The staging secret becomes the active secret. |
371 | | * The active secret becomes a staging secret. |
372 | | * If the UNBOUND_COOKIE_HISTORY_SIZE > 2 then all staging secrets are moved |
373 | | * one position up and the previously active secret becomes the last |
374 | | * staging secret. |
375 | | */ |
376 | 0 | if(cookie_secrets->cookie_count < 2) |
377 | 0 | return; |
378 | 0 | memcpy( active_secret, cookie_secrets->cookie_secrets[0].cookie_secret |
379 | 0 | , UNBOUND_COOKIE_SECRET_SIZE); |
380 | 0 | memmove( &cookie_secrets->cookie_secrets[0], &cookie_secrets->cookie_secrets[1] |
381 | 0 | , sizeof(struct cookie_secret) * (UNBOUND_COOKIE_HISTORY_SIZE - 1)); |
382 | 0 | memcpy( cookie_secrets->cookie_secrets[cookie_secrets->cookie_count - 1].cookie_secret |
383 | 0 | , active_secret, UNBOUND_COOKIE_SECRET_SIZE); |
384 | 0 | explicit_bzero(active_secret, UNBOUND_COOKIE_SECRET_SIZE); |
385 | 0 | } |
386 | | |
387 | | void drop_cookie_secret(struct cookie_secrets* cookie_secrets) |
388 | 0 | { |
389 | 0 | if(!cookie_secrets) |
390 | 0 | return; |
391 | | /* Drops a staging cookie secret. If there are more than one, it will |
392 | | * drop the last staging secret. */ |
393 | 0 | if(cookie_secrets->cookie_count < 2) |
394 | 0 | return; |
395 | 0 | explicit_bzero( cookie_secrets->cookie_secrets[cookie_secrets->cookie_count - 1].cookie_secret |
396 | 0 | , UNBOUND_COOKIE_SECRET_SIZE); |
397 | 0 | cookie_secrets->cookie_count -= 1; |
398 | 0 | } |