/src/bind9/lib/dns/transport.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) Internet Systems Consortium, Inc. ("ISC") |
3 | | * |
4 | | * SPDX-License-Identifier: MPL-2.0 |
5 | | * |
6 | | * This Source Code Form is subject to the terms of the Mozilla Public |
7 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
8 | | * file, you can obtain one at https://mozilla.org/MPL/2.0/. |
9 | | * |
10 | | * See the COPYRIGHT file distributed with this work for additional |
11 | | * information regarding copyright ownership. |
12 | | */ |
13 | | |
14 | | #include <inttypes.h> |
15 | | |
16 | | #include <isc/hashmap.h> |
17 | | #include <isc/list.h> |
18 | | #include <isc/mem.h> |
19 | | #include <isc/netaddr.h> |
20 | | #include <isc/refcount.h> |
21 | | #include <isc/result.h> |
22 | | #include <isc/rwlock.h> |
23 | | #include <isc/sockaddr.h> |
24 | | #include <isc/util.h> |
25 | | |
26 | | #include <dns/fixedname.h> |
27 | | #include <dns/name.h> |
28 | | #include <dns/transport.h> |
29 | | |
30 | 0 | #define TRANSPORT_MAGIC ISC_MAGIC('T', 'r', 'n', 's') |
31 | | #define VALID_TRANSPORT(ptr) ISC_MAGIC_VALID(ptr, TRANSPORT_MAGIC) |
32 | | |
33 | 0 | #define TRANSPORT_LIST_MAGIC ISC_MAGIC('T', 'r', 'L', 's') |
34 | | #define VALID_TRANSPORT_LIST(ptr) ISC_MAGIC_VALID(ptr, TRANSPORT_LIST_MAGIC) |
35 | | |
36 | | struct dns_transport_list { |
37 | | unsigned int magic; |
38 | | isc_refcount_t references; |
39 | | isc_mem_t *mctx; |
40 | | isc_rwlock_t lock; |
41 | | isc_hashmap_t *transports[DNS_TRANSPORT_COUNT]; |
42 | | }; |
43 | | |
44 | | typedef enum ternary { ter_none = 0, ter_true = 1, ter_false = 2 } ternary_t; |
45 | | |
46 | | struct dns_transport { |
47 | | unsigned int magic; |
48 | | isc_refcount_t references; |
49 | | isc_mem_t *mctx; |
50 | | dns_transport_type_t type; |
51 | | dns_fixedname_t fn; |
52 | | dns_name_t *name; |
53 | | struct { |
54 | | char *tlsname; |
55 | | char *certfile; |
56 | | char *keyfile; |
57 | | char *cafile; |
58 | | char *remote_hostname; |
59 | | char *ciphers; |
60 | | char *cipher_suites; |
61 | | uint32_t protocol_versions; |
62 | | ternary_t prefer_server_ciphers; |
63 | | bool always_verify_remote; |
64 | | } tls; |
65 | | struct { |
66 | | char *endpoint; |
67 | | dns_http_mode_t mode; |
68 | | } doh; |
69 | | }; |
70 | | |
71 | | static bool |
72 | 0 | transport_match(void *node, const void *key) { |
73 | 0 | dns_transport_t *transport = node; |
74 | |
|
75 | 0 | return dns_name_equal(transport->name, key); |
76 | 0 | } |
77 | | |
78 | | static isc_result_t |
79 | | list_add(dns_transport_list_t *list, const dns_name_t *name, |
80 | 0 | const dns_transport_type_t type, dns_transport_t *transport) { |
81 | 0 | isc_result_t result; |
82 | 0 | isc_hashmap_t *hm = NULL; |
83 | |
|
84 | 0 | RWLOCK(&list->lock, isc_rwlocktype_write); |
85 | 0 | hm = list->transports[type]; |
86 | 0 | INSIST(hm != NULL); |
87 | |
|
88 | 0 | transport->name = dns_fixedname_initname(&transport->fn); |
89 | 0 | dns_name_copy(name, transport->name); |
90 | 0 | result = isc_hashmap_add(hm, dns_name_hash(name), transport_match, name, |
91 | 0 | transport, NULL); |
92 | 0 | RWUNLOCK(&list->lock, isc_rwlocktype_write); |
93 | |
|
94 | 0 | return result; |
95 | 0 | } |
96 | | |
97 | | dns_transport_type_t |
98 | 0 | dns_transport_get_type(const dns_transport_t *transport) { |
99 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
100 | |
|
101 | 0 | return transport->type; |
102 | 0 | } |
103 | | |
104 | | char * |
105 | 0 | dns_transport_get_certfile(const dns_transport_t *transport) { |
106 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
107 | |
|
108 | 0 | return transport->tls.certfile; |
109 | 0 | } |
110 | | |
111 | | char * |
112 | 0 | dns_transport_get_keyfile(const dns_transport_t *transport) { |
113 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
114 | |
|
115 | 0 | return transport->tls.keyfile; |
116 | 0 | } |
117 | | |
118 | | char * |
119 | 0 | dns_transport_get_cafile(const dns_transport_t *transport) { |
120 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
121 | |
|
122 | 0 | return transport->tls.cafile; |
123 | 0 | } |
124 | | |
125 | | char * |
126 | 0 | dns_transport_get_remote_hostname(const dns_transport_t *transport) { |
127 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
128 | |
|
129 | 0 | return transport->tls.remote_hostname; |
130 | 0 | } |
131 | | |
132 | | char * |
133 | 0 | dns_transport_get_endpoint(const dns_transport_t *transport) { |
134 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
135 | |
|
136 | 0 | return transport->doh.endpoint; |
137 | 0 | } |
138 | | |
139 | | dns_http_mode_t |
140 | 0 | dns_transport_get_mode(const dns_transport_t *transport) { |
141 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
142 | |
|
143 | 0 | return transport->doh.mode; |
144 | 0 | } |
145 | | |
146 | | dns_transport_t * |
147 | | dns_transport_new(const dns_name_t *name, dns_transport_type_t type, |
148 | 0 | dns_transport_list_t *list) { |
149 | 0 | dns_transport_t *transport = isc_mem_get(list->mctx, |
150 | 0 | sizeof(*transport)); |
151 | 0 | *transport = (dns_transport_t){ .type = type }; |
152 | 0 | isc_refcount_init(&transport->references, 1); |
153 | 0 | isc_mem_attach(list->mctx, &transport->mctx); |
154 | 0 | transport->magic = TRANSPORT_MAGIC; |
155 | |
|
156 | 0 | list_add(list, name, type, transport); |
157 | |
|
158 | 0 | return transport; |
159 | 0 | } |
160 | | |
161 | | void |
162 | 0 | dns_transport_set_certfile(dns_transport_t *transport, const char *certfile) { |
163 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
164 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS || |
165 | 0 | transport->type == DNS_TRANSPORT_HTTP); |
166 | |
|
167 | 0 | if (transport->tls.certfile != NULL) { |
168 | 0 | isc_mem_free(transport->mctx, transport->tls.certfile); |
169 | 0 | } |
170 | |
|
171 | 0 | if (certfile != NULL) { |
172 | 0 | transport->tls.certfile = isc_mem_strdup(transport->mctx, |
173 | 0 | certfile); |
174 | 0 | } |
175 | 0 | } |
176 | | |
177 | | void |
178 | 0 | dns_transport_set_keyfile(dns_transport_t *transport, const char *keyfile) { |
179 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
180 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS || |
181 | 0 | transport->type == DNS_TRANSPORT_HTTP); |
182 | |
|
183 | 0 | if (transport->tls.keyfile != NULL) { |
184 | 0 | isc_mem_free(transport->mctx, transport->tls.keyfile); |
185 | 0 | } |
186 | |
|
187 | 0 | if (keyfile != NULL) { |
188 | 0 | transport->tls.keyfile = isc_mem_strdup(transport->mctx, |
189 | 0 | keyfile); |
190 | 0 | } |
191 | 0 | } |
192 | | |
193 | | void |
194 | 0 | dns_transport_set_cafile(dns_transport_t *transport, const char *cafile) { |
195 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
196 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS || |
197 | 0 | transport->type == DNS_TRANSPORT_HTTP); |
198 | |
|
199 | 0 | if (transport->tls.cafile != NULL) { |
200 | 0 | isc_mem_free(transport->mctx, transport->tls.cafile); |
201 | 0 | } |
202 | |
|
203 | 0 | if (cafile != NULL) { |
204 | 0 | transport->tls.cafile = isc_mem_strdup(transport->mctx, cafile); |
205 | 0 | } |
206 | 0 | } |
207 | | |
208 | | void |
209 | | dns_transport_set_remote_hostname(dns_transport_t *transport, |
210 | 0 | const char *hostname) { |
211 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
212 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS || |
213 | 0 | transport->type == DNS_TRANSPORT_HTTP); |
214 | |
|
215 | 0 | if (transport->tls.remote_hostname != NULL) { |
216 | 0 | isc_mem_free(transport->mctx, transport->tls.remote_hostname); |
217 | 0 | } |
218 | |
|
219 | 0 | if (hostname != NULL) { |
220 | 0 | transport->tls.remote_hostname = isc_mem_strdup(transport->mctx, |
221 | 0 | hostname); |
222 | 0 | } |
223 | 0 | } |
224 | | |
225 | | void |
226 | 0 | dns_transport_set_endpoint(dns_transport_t *transport, const char *endpoint) { |
227 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
228 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_HTTP); |
229 | |
|
230 | 0 | if (transport->doh.endpoint != NULL) { |
231 | 0 | isc_mem_free(transport->mctx, transport->doh.endpoint); |
232 | 0 | } |
233 | |
|
234 | 0 | if (endpoint != NULL) { |
235 | 0 | transport->doh.endpoint = isc_mem_strdup(transport->mctx, |
236 | 0 | endpoint); |
237 | 0 | } |
238 | 0 | } |
239 | | |
240 | | void |
241 | 0 | dns_transport_set_mode(dns_transport_t *transport, dns_http_mode_t mode) { |
242 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
243 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_HTTP); |
244 | |
|
245 | 0 | transport->doh.mode = mode; |
246 | 0 | } |
247 | | |
248 | | void |
249 | | dns_transport_set_tls_versions(dns_transport_t *transport, |
250 | 0 | const uint32_t tls_versions) { |
251 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
252 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_HTTP || |
253 | 0 | transport->type == DNS_TRANSPORT_TLS); |
254 | |
|
255 | 0 | transport->tls.protocol_versions = tls_versions; |
256 | 0 | } |
257 | | |
258 | | uint32_t |
259 | 0 | dns_transport_get_tls_versions(const dns_transport_t *transport) { |
260 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
261 | |
|
262 | 0 | return transport->tls.protocol_versions; |
263 | 0 | } |
264 | | |
265 | | void |
266 | 0 | dns_transport_set_ciphers(dns_transport_t *transport, const char *ciphers) { |
267 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
268 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS || |
269 | 0 | transport->type == DNS_TRANSPORT_HTTP); |
270 | |
|
271 | 0 | if (transport->tls.ciphers != NULL) { |
272 | 0 | isc_mem_free(transport->mctx, transport->tls.ciphers); |
273 | 0 | } |
274 | |
|
275 | 0 | if (ciphers != NULL) { |
276 | 0 | transport->tls.ciphers = isc_mem_strdup(transport->mctx, |
277 | 0 | ciphers); |
278 | 0 | } |
279 | 0 | } |
280 | | |
281 | | void |
282 | 0 | dns_transport_set_tlsname(dns_transport_t *transport, const char *tlsname) { |
283 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
284 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS || |
285 | 0 | transport->type == DNS_TRANSPORT_HTTP); |
286 | |
|
287 | 0 | if (transport->tls.tlsname != NULL) { |
288 | 0 | isc_mem_free(transport->mctx, transport->tls.tlsname); |
289 | 0 | } |
290 | |
|
291 | 0 | if (tlsname != NULL) { |
292 | 0 | transport->tls.tlsname = isc_mem_strdup(transport->mctx, |
293 | 0 | tlsname); |
294 | 0 | } |
295 | 0 | } |
296 | | |
297 | | char * |
298 | 0 | dns_transport_get_ciphers(const dns_transport_t *transport) { |
299 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
300 | |
|
301 | 0 | return transport->tls.ciphers; |
302 | 0 | } |
303 | | |
304 | | void |
305 | | dns_transport_set_cipher_suites(dns_transport_t *transport, |
306 | 0 | const char *cipher_suites) { |
307 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
308 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS || |
309 | 0 | transport->type == DNS_TRANSPORT_HTTP); |
310 | |
|
311 | 0 | if (transport->tls.cipher_suites != NULL) { |
312 | 0 | isc_mem_free(transport->mctx, transport->tls.cipher_suites); |
313 | 0 | } |
314 | |
|
315 | 0 | if (cipher_suites != NULL) { |
316 | 0 | transport->tls.cipher_suites = isc_mem_strdup(transport->mctx, |
317 | 0 | cipher_suites); |
318 | 0 | } |
319 | 0 | } |
320 | | |
321 | | char * |
322 | 0 | dns_transport_get_cipher_suites(const dns_transport_t *transport) { |
323 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
324 | |
|
325 | 0 | return transport->tls.cipher_suites; |
326 | 0 | } |
327 | | |
328 | | char * |
329 | 0 | dns_transport_get_tlsname(const dns_transport_t *transport) { |
330 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
331 | |
|
332 | 0 | return transport->tls.tlsname; |
333 | 0 | } |
334 | | |
335 | | void |
336 | | dns_transport_set_prefer_server_ciphers(dns_transport_t *transport, |
337 | 0 | const bool prefer) { |
338 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
339 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS || |
340 | 0 | transport->type == DNS_TRANSPORT_HTTP); |
341 | |
|
342 | 0 | transport->tls.prefer_server_ciphers = prefer ? ter_true : ter_false; |
343 | 0 | } |
344 | | |
345 | | bool |
346 | | dns_transport_get_prefer_server_ciphers(const dns_transport_t *transport, |
347 | 0 | bool *preferp) { |
348 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
349 | 0 | REQUIRE(preferp != NULL); |
350 | 0 | if (transport->tls.prefer_server_ciphers == ter_none) { |
351 | 0 | return false; |
352 | 0 | } else if (transport->tls.prefer_server_ciphers == ter_true) { |
353 | 0 | *preferp = true; |
354 | 0 | return true; |
355 | 0 | } else if (transport->tls.prefer_server_ciphers == ter_false) { |
356 | 0 | *preferp = false; |
357 | 0 | return true; |
358 | 0 | } |
359 | | |
360 | 0 | UNREACHABLE(); |
361 | 0 | return false; |
362 | 0 | } |
363 | | |
364 | | void |
365 | | dns_transport_set_always_verify_remote(dns_transport_t *transport, |
366 | 0 | const bool always_verify_remote) { |
367 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
368 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS || |
369 | 0 | transport->type == DNS_TRANSPORT_HTTP); |
370 | |
|
371 | 0 | transport->tls.always_verify_remote = always_verify_remote; |
372 | 0 | } |
373 | | |
374 | | bool |
375 | 0 | dns_transport_get_always_verify_remote(dns_transport_t *transport) { |
376 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
377 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS || |
378 | 0 | transport->type == DNS_TRANSPORT_HTTP); |
379 | |
|
380 | 0 | return transport->tls.always_verify_remote; |
381 | 0 | } |
382 | | |
383 | | isc_result_t |
384 | | dns_transport_get_tlsctx(dns_transport_t *transport, const isc_sockaddr_t *peer, |
385 | | isc_tlsctx_cache_t *tlsctx_cache, isc_mem_t *mctx, |
386 | | isc_tlsctx_t **pctx, |
387 | 0 | isc_tlsctx_client_session_cache_t **psess_cache) { |
388 | 0 | isc_result_t result = ISC_R_FAILURE; |
389 | 0 | isc_tlsctx_t *tlsctx = NULL, *found = NULL; |
390 | 0 | isc_tls_cert_store_t *store = NULL, *found_store = NULL; |
391 | 0 | isc_tlsctx_client_session_cache_t *sess_cache = NULL; |
392 | 0 | isc_tlsctx_client_session_cache_t *found_sess_cache = NULL; |
393 | 0 | uint32_t tls_versions; |
394 | 0 | const char *ciphers = NULL; |
395 | 0 | const char *cipher_suites = NULL; |
396 | 0 | bool prefer_server_ciphers; |
397 | 0 | uint16_t family; |
398 | 0 | const char *tlsname = NULL; |
399 | |
|
400 | 0 | REQUIRE(VALID_TRANSPORT(transport)); |
401 | 0 | REQUIRE(transport->type == DNS_TRANSPORT_TLS); |
402 | 0 | REQUIRE(peer != NULL); |
403 | 0 | REQUIRE(tlsctx_cache != NULL); |
404 | 0 | REQUIRE(mctx != NULL); |
405 | 0 | REQUIRE(pctx != NULL && *pctx == NULL); |
406 | 0 | REQUIRE(psess_cache != NULL && *psess_cache == NULL); |
407 | |
|
408 | 0 | family = (isc_sockaddr_pf(peer) == PF_INET6) ? AF_INET6 : AF_INET; |
409 | |
|
410 | 0 | tlsname = dns_transport_get_tlsname(transport); |
411 | 0 | INSIST(tlsname != NULL && *tlsname != '\0'); |
412 | | |
413 | | /* |
414 | | * Let's try to re-use the already created context. This way |
415 | | * we have a chance to resume the TLS session, bypassing the |
416 | | * full TLS handshake procedure, making establishing |
417 | | * subsequent TLS connections faster. |
418 | | */ |
419 | 0 | result = isc_tlsctx_cache_find(tlsctx_cache, tlsname, |
420 | 0 | isc_tlsctx_cache_tls, family, &found, |
421 | 0 | &found_store, &found_sess_cache); |
422 | 0 | if (result != ISC_R_SUCCESS) { |
423 | 0 | const char *hostname = |
424 | 0 | dns_transport_get_remote_hostname(transport); |
425 | 0 | const char *ca_file = dns_transport_get_cafile(transport); |
426 | 0 | const char *cert_file = dns_transport_get_certfile(transport); |
427 | 0 | const char *key_file = dns_transport_get_keyfile(transport); |
428 | 0 | const bool always_verify_remote = |
429 | 0 | dns_transport_get_always_verify_remote(transport); |
430 | 0 | char peer_addr_str[INET6_ADDRSTRLEN] = { 0 }; |
431 | 0 | isc_netaddr_t peer_netaddr = { 0 }; |
432 | 0 | bool hostname_ignore_subject; |
433 | | |
434 | | /* |
435 | | * So, no context exists. Let's create one using the |
436 | | * parameters from the configuration file and try to |
437 | | * store it for further reuse. |
438 | | */ |
439 | 0 | CHECK(isc_tlsctx_createclient(&tlsctx)); |
440 | 0 | tls_versions = dns_transport_get_tls_versions(transport); |
441 | 0 | if (tls_versions != 0) { |
442 | 0 | isc_tlsctx_set_protocols(tlsctx, tls_versions); |
443 | 0 | } |
444 | 0 | ciphers = dns_transport_get_ciphers(transport); |
445 | 0 | if (ciphers != NULL) { |
446 | 0 | isc_tlsctx_set_cipherlist(tlsctx, ciphers); |
447 | 0 | } |
448 | 0 | cipher_suites = dns_transport_get_cipher_suites(transport); |
449 | 0 | if (cipher_suites != NULL) { |
450 | 0 | isc_tlsctx_set_cipher_suites(tlsctx, cipher_suites); |
451 | 0 | } |
452 | |
|
453 | 0 | if (dns_transport_get_prefer_server_ciphers( |
454 | 0 | transport, &prefer_server_ciphers)) |
455 | 0 | { |
456 | 0 | isc_tlsctx_prefer_server_ciphers(tlsctx, |
457 | 0 | prefer_server_ciphers); |
458 | 0 | } |
459 | |
|
460 | 0 | if (always_verify_remote || hostname != NULL || ca_file != NULL) |
461 | 0 | { |
462 | | /* |
463 | | * The situation when 'found_store != NULL' while |
464 | | * 'found == NULL' may occur as there is a one-to-many |
465 | | * relation between cert stores and per-transport TLS |
466 | | * contexts. That is, there could be one store |
467 | | * shared between multiple contexts. |
468 | | */ |
469 | 0 | if (found_store == NULL) { |
470 | | /* |
471 | | * 'ca_file' can equal 'NULL' here, in |
472 | | * which case the store with system-wide |
473 | | * CA certificates will be created. |
474 | | */ |
475 | 0 | CHECK(isc_tls_cert_store_create(ca_file, |
476 | 0 | &store)); |
477 | 0 | } else { |
478 | 0 | store = found_store; |
479 | 0 | } |
480 | | |
481 | 0 | INSIST(store != NULL); |
482 | 0 | if (hostname == NULL) { |
483 | | /* |
484 | | * If hostname is not specified, then use the |
485 | | * peer IP address for validation. |
486 | | */ |
487 | 0 | isc_netaddr_fromsockaddr(&peer_netaddr, peer); |
488 | 0 | isc_netaddr_format(&peer_netaddr, peer_addr_str, |
489 | 0 | sizeof(peer_addr_str)); |
490 | 0 | hostname = peer_addr_str; |
491 | 0 | } |
492 | | |
493 | | /* |
494 | | * According to RFC 8310, Subject field MUST NOT |
495 | | * be inspected when verifying hostname for DoT. |
496 | | * Only SubjectAltName must be checked. |
497 | | */ |
498 | 0 | hostname_ignore_subject = true; |
499 | 0 | CHECK(isc_tlsctx_enable_peer_verification( |
500 | 0 | tlsctx, false, store, hostname, |
501 | 0 | hostname_ignore_subject)); |
502 | | |
503 | | /* |
504 | | * Let's load client certificate and enable |
505 | | * Mutual TLS. We do that only in the case when |
506 | | * Strict TLS is enabled, because Mutual TLS is |
507 | | * an extension of it. |
508 | | */ |
509 | 0 | if (cert_file != NULL) { |
510 | 0 | INSIST(key_file != NULL); |
511 | |
|
512 | 0 | CHECK(isc_tlsctx_load_certificate( |
513 | 0 | tlsctx, key_file, cert_file)); |
514 | 0 | } |
515 | 0 | } |
516 | | |
517 | 0 | isc_tlsctx_enable_dot_client_alpn(tlsctx); |
518 | |
|
519 | 0 | isc_tlsctx_client_session_cache_create( |
520 | 0 | mctx, tlsctx, |
521 | 0 | ISC_TLSCTX_CLIENT_SESSION_CACHE_DEFAULT_SIZE, |
522 | 0 | &sess_cache); |
523 | |
|
524 | 0 | found_store = NULL; |
525 | 0 | result = isc_tlsctx_cache_add(tlsctx_cache, tlsname, |
526 | 0 | isc_tlsctx_cache_tls, family, |
527 | 0 | tlsctx, store, sess_cache, &found, |
528 | 0 | &found_store, &found_sess_cache); |
529 | 0 | if (result == ISC_R_EXISTS) { |
530 | | /* |
531 | | * It seems the entry has just been created from |
532 | | * within another thread while we were initialising |
533 | | * ours. Although this is unlikely, it could happen |
534 | | * after startup/re-initialisation. In such a case, |
535 | | * discard the new context and associated data and use |
536 | | * the already established one from now on. |
537 | | * |
538 | | * Such situation will not occur after the |
539 | | * initial 'warm-up', so it is not critical |
540 | | * performance-wise. |
541 | | */ |
542 | 0 | INSIST(found != NULL); |
543 | 0 | isc_tlsctx_free(&tlsctx); |
544 | | /* |
545 | | * The 'store' variable can be 'NULL' when remote server |
546 | | * verification is not enabled (that is, when Strict or |
547 | | * Mutual TLS are not used). |
548 | | * |
549 | | * The 'found_store' might be equal to 'store' as there |
550 | | * is one-to-many relation between a store and |
551 | | * per-transport TLS contexts. In that case, the call to |
552 | | * 'isc_tlsctx_cache_find()' above could have returned a |
553 | | * store via the 'found_store' variable, whose value we |
554 | | * can assign to 'store' later. In that case, |
555 | | * 'isc_tlsctx_cache_add()' will return the same value. |
556 | | * When that happens, we should not free the store |
557 | | * object, as it is managed by the TLS context cache. |
558 | | */ |
559 | 0 | if (store != NULL && store != found_store) { |
560 | 0 | isc_tls_cert_store_free(&store); |
561 | 0 | } |
562 | 0 | isc_tlsctx_client_session_cache_detach(&sess_cache); |
563 | | /* Let's return the data from the cache. */ |
564 | 0 | *psess_cache = found_sess_cache; |
565 | 0 | *pctx = found; |
566 | 0 | } else { |
567 | | /* |
568 | | * Adding the fresh values into the cache has been |
569 | | * successful, let's return them |
570 | | */ |
571 | 0 | INSIST(result == ISC_R_SUCCESS); |
572 | 0 | *psess_cache = sess_cache; |
573 | 0 | *pctx = tlsctx; |
574 | 0 | } |
575 | 0 | } else { |
576 | | /* |
577 | | * The cache lookup has been successful, let's return the |
578 | | * results. |
579 | | */ |
580 | 0 | INSIST(result == ISC_R_SUCCESS); |
581 | 0 | *psess_cache = found_sess_cache; |
582 | 0 | *pctx = found; |
583 | 0 | } |
584 | | |
585 | 0 | return ISC_R_SUCCESS; |
586 | | |
587 | 0 | cleanup: |
588 | 0 | if (tlsctx != NULL) { |
589 | 0 | isc_tlsctx_free(&tlsctx); |
590 | 0 | } |
591 | | |
592 | | /* |
593 | | * The 'found_store' is being managed by the TLS context |
594 | | * cache. Thus, we should keep it as it is, as it will get |
595 | | * destroyed alongside the cache. As there is one store per |
596 | | * multiple TLS contexts, we need to handle store deletion in a |
597 | | * special way. |
598 | | */ |
599 | 0 | if (store != NULL && store != found_store) { |
600 | 0 | isc_tls_cert_store_free(&store); |
601 | 0 | } |
602 | |
|
603 | 0 | return result; |
604 | 0 | } |
605 | | |
606 | | static void |
607 | 0 | transport_destroy(dns_transport_t *transport) { |
608 | 0 | isc_refcount_destroy(&transport->references); |
609 | 0 | transport->magic = 0; |
610 | |
|
611 | 0 | if (transport->doh.endpoint != NULL) { |
612 | 0 | isc_mem_free(transport->mctx, transport->doh.endpoint); |
613 | 0 | } |
614 | 0 | if (transport->tls.remote_hostname != NULL) { |
615 | 0 | isc_mem_free(transport->mctx, transport->tls.remote_hostname); |
616 | 0 | } |
617 | 0 | if (transport->tls.cafile != NULL) { |
618 | 0 | isc_mem_free(transport->mctx, transport->tls.cafile); |
619 | 0 | } |
620 | 0 | if (transport->tls.keyfile != NULL) { |
621 | 0 | isc_mem_free(transport->mctx, transport->tls.keyfile); |
622 | 0 | } |
623 | 0 | if (transport->tls.certfile != NULL) { |
624 | 0 | isc_mem_free(transport->mctx, transport->tls.certfile); |
625 | 0 | } |
626 | 0 | if (transport->tls.ciphers != NULL) { |
627 | 0 | isc_mem_free(transport->mctx, transport->tls.ciphers); |
628 | 0 | } |
629 | 0 | if (transport->tls.cipher_suites != NULL) { |
630 | 0 | isc_mem_free(transport->mctx, transport->tls.cipher_suites); |
631 | 0 | } |
632 | |
|
633 | 0 | if (transport->tls.tlsname != NULL) { |
634 | 0 | isc_mem_free(transport->mctx, transport->tls.tlsname); |
635 | 0 | } |
636 | |
|
637 | 0 | isc_mem_putanddetach(&transport->mctx, transport, sizeof(*transport)); |
638 | 0 | } |
639 | | |
640 | | void |
641 | 0 | dns_transport_attach(dns_transport_t *source, dns_transport_t **targetp) { |
642 | 0 | REQUIRE(source != NULL); |
643 | 0 | REQUIRE(targetp != NULL && *targetp == NULL); |
644 | |
|
645 | 0 | isc_refcount_increment(&source->references); |
646 | |
|
647 | 0 | *targetp = source; |
648 | 0 | } |
649 | | |
650 | | void |
651 | 0 | dns_transport_detach(dns_transport_t **transportp) { |
652 | 0 | dns_transport_t *transport = NULL; |
653 | |
|
654 | 0 | REQUIRE(transportp != NULL); |
655 | 0 | REQUIRE(VALID_TRANSPORT(*transportp)); |
656 | |
|
657 | 0 | transport = *transportp; |
658 | 0 | *transportp = NULL; |
659 | |
|
660 | 0 | if (isc_refcount_decrement(&transport->references) == 1) { |
661 | 0 | transport_destroy(transport); |
662 | 0 | } |
663 | 0 | } |
664 | | |
665 | | dns_transport_t * |
666 | | dns_transport_find(const dns_transport_type_t type, const dns_name_t *name, |
667 | 0 | dns_transport_list_t *list) { |
668 | 0 | isc_result_t result; |
669 | 0 | dns_transport_t *transport = NULL; |
670 | 0 | isc_hashmap_t *hm = NULL; |
671 | |
|
672 | 0 | REQUIRE(VALID_TRANSPORT_LIST(list)); |
673 | 0 | REQUIRE(list->transports[type] != NULL); |
674 | |
|
675 | 0 | hm = list->transports[type]; |
676 | |
|
677 | 0 | RWLOCK(&list->lock, isc_rwlocktype_read); |
678 | 0 | result = isc_hashmap_find(hm, dns_name_hash(name), transport_match, |
679 | 0 | name, (void **)&transport); |
680 | 0 | if (result == ISC_R_SUCCESS) { |
681 | 0 | isc_refcount_increment(&transport->references); |
682 | 0 | } |
683 | 0 | RWUNLOCK(&list->lock, isc_rwlocktype_read); |
684 | |
|
685 | 0 | return transport; |
686 | 0 | } |
687 | | |
688 | | dns_transport_list_t * |
689 | 0 | dns_transport_list_new(isc_mem_t *mctx) { |
690 | 0 | dns_transport_list_t *list = isc_mem_get(mctx, sizeof(*list)); |
691 | |
|
692 | 0 | *list = (dns_transport_list_t){ 0 }; |
693 | |
|
694 | 0 | isc_rwlock_init(&list->lock); |
695 | |
|
696 | 0 | isc_mem_attach(mctx, &list->mctx); |
697 | 0 | isc_refcount_init(&list->references, 1); |
698 | |
|
699 | 0 | list->magic = TRANSPORT_LIST_MAGIC; |
700 | |
|
701 | 0 | for (size_t type = 0; type < DNS_TRANSPORT_COUNT; type++) { |
702 | 0 | isc_hashmap_create(list->mctx, 10, &list->transports[type]); |
703 | 0 | } |
704 | |
|
705 | 0 | return list; |
706 | 0 | } |
707 | | |
708 | | void |
709 | | dns_transport_list_attach(dns_transport_list_t *source, |
710 | 0 | dns_transport_list_t **targetp) { |
711 | 0 | REQUIRE(VALID_TRANSPORT_LIST(source)); |
712 | 0 | REQUIRE(targetp != NULL && *targetp == NULL); |
713 | |
|
714 | 0 | isc_refcount_increment(&source->references); |
715 | |
|
716 | 0 | *targetp = source; |
717 | 0 | } |
718 | | |
719 | | static void |
720 | 0 | transport_list_destroy(dns_transport_list_t *list) { |
721 | 0 | isc_refcount_destroy(&list->references); |
722 | 0 | list->magic = 0; |
723 | |
|
724 | 0 | for (size_t type = 0; type < DNS_TRANSPORT_COUNT; type++) { |
725 | 0 | isc_result_t result; |
726 | 0 | isc_hashmap_iter_t *it = NULL; |
727 | |
|
728 | 0 | if (list->transports[type] == NULL) { |
729 | 0 | continue; |
730 | 0 | } |
731 | | |
732 | 0 | isc_hashmap_iter_create(list->transports[type], &it); |
733 | 0 | for (result = isc_hashmap_iter_first(it); |
734 | 0 | result == ISC_R_SUCCESS; |
735 | 0 | result = isc_hashmap_iter_delcurrent_next(it)) |
736 | 0 | { |
737 | 0 | dns_transport_t *transport = NULL; |
738 | 0 | isc_hashmap_iter_current(it, (void **)&transport); |
739 | 0 | dns_transport_detach(&transport); |
740 | 0 | } |
741 | 0 | isc_hashmap_iter_destroy(&it); |
742 | 0 | isc_hashmap_destroy(&list->transports[type]); |
743 | 0 | } |
744 | 0 | isc_rwlock_destroy(&list->lock); |
745 | 0 | isc_mem_putanddetach(&list->mctx, list, sizeof(*list)); |
746 | 0 | } |
747 | | |
748 | | void |
749 | 0 | dns_transport_list_detach(dns_transport_list_t **listp) { |
750 | 0 | dns_transport_list_t *list = NULL; |
751 | |
|
752 | 0 | REQUIRE(listp != NULL); |
753 | 0 | REQUIRE(VALID_TRANSPORT_LIST(*listp)); |
754 | |
|
755 | 0 | list = *listp; |
756 | 0 | *listp = NULL; |
757 | |
|
758 | 0 | if (isc_refcount_decrement(&list->references) == 1) { |
759 | 0 | transport_list_destroy(list); |
760 | 0 | } |
761 | 0 | } |
762 | | |
763 | | const char * |
764 | 0 | dns_transport_totext(dns_transport_type_t type) { |
765 | 0 | switch (type) { |
766 | 0 | case DNS_TRANSPORT_UDP: |
767 | 0 | return "udp"; |
768 | 0 | case DNS_TRANSPORT_TCP: |
769 | 0 | return "tcp"; |
770 | 0 | case DNS_TRANSPORT_TLS: |
771 | 0 | return "tls"; |
772 | 0 | case DNS_TRANSPORT_HTTP: |
773 | 0 | return "https"; |
774 | 0 | default: |
775 | 0 | UNREACHABLE(); |
776 | 0 | } |
777 | 0 | } |