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 | | #include <netinet/in.h> |
16 | | #include <stdlib.h> |
17 | | #include <string.h> |
18 | | #include <sys/socket.h> |
19 | | #if HAVE_LIBNGHTTP2 |
20 | | #include <nghttp2/nghttp2.h> |
21 | | #endif /* HAVE_LIBNGHTTP2 */ |
22 | | #include <arpa/inet.h> |
23 | | |
24 | | #include <openssl/bn.h> |
25 | | #include <openssl/conf.h> |
26 | | #include <openssl/crypto.h> |
27 | | #include <openssl/dh.h> |
28 | | #include <openssl/err.h> |
29 | | #include <openssl/evp.h> |
30 | | #include <openssl/opensslv.h> |
31 | | #include <openssl/rand.h> |
32 | | #include <openssl/rsa.h> |
33 | | #include <openssl/x509_vfy.h> |
34 | | #include <openssl/x509v3.h> |
35 | | |
36 | | #include <isc/atomic.h> |
37 | | #include <isc/crypto.h> |
38 | | #include <isc/ht.h> |
39 | | #include <isc/log.h> |
40 | | #include <isc/magic.h> |
41 | | #include <isc/md.h> |
42 | | #include <isc/mem.h> |
43 | | #include <isc/mutex.h> |
44 | | #include <isc/once.h> |
45 | | #include <isc/ossl_wrap.h> |
46 | | #include <isc/random.h> |
47 | | #include <isc/refcount.h> |
48 | | #include <isc/rwlock.h> |
49 | | #include <isc/sockaddr.h> |
50 | | #include <isc/thread.h> |
51 | | #include <isc/tls.h> |
52 | | #include <isc/util.h> |
53 | | |
54 | | #include "openssl_shim.h" |
55 | | |
56 | | #define COMMON_SSL_OPTIONS \ |
57 | 0 | (SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION) |
58 | | |
59 | | void |
60 | 0 | isc_tlsctx_free(isc_tlsctx_t **ctxp) { |
61 | 0 | SSL_CTX *ctx = NULL; |
62 | 0 | REQUIRE(ctxp != NULL && *ctxp != NULL); |
63 | |
|
64 | 0 | ctx = *ctxp; |
65 | 0 | *ctxp = NULL; |
66 | |
|
67 | 0 | SSL_CTX_free(ctx); |
68 | 0 | } |
69 | | |
70 | | void |
71 | 0 | isc_tlsctx_attach(isc_tlsctx_t *src, isc_tlsctx_t **ptarget) { |
72 | 0 | REQUIRE(src != NULL); |
73 | 0 | REQUIRE(ptarget != NULL && *ptarget == NULL); |
74 | |
|
75 | 0 | RUNTIME_CHECK(SSL_CTX_up_ref(src) == 1); |
76 | |
|
77 | 0 | *ptarget = src; |
78 | 0 | } |
79 | | |
80 | | /* |
81 | | * Callback invoked by the SSL library whenever a new TLS pre-master secret |
82 | | * needs to be logged. |
83 | | */ |
84 | | static void |
85 | 0 | sslkeylogfile_append(const SSL *ssl ISC_ATTR_UNUSED, const char *line) { |
86 | 0 | isc_log_write(ISC_LOGCATEGORY_SSLKEYLOG, ISC_LOGMODULE_CRYPTO, |
87 | 0 | ISC_LOG_INFO, "%s", line); |
88 | 0 | } |
89 | | |
90 | | /* |
91 | | * Enable TLS pre-master secret logging if the SSLKEYLOGFILE environment |
92 | | * variable is set. This needs to be done on a per-context basis as that is |
93 | | * how SSL_CTX_set_keylog_callback() works. |
94 | | */ |
95 | | static void |
96 | 0 | sslkeylogfile_init(isc_tlsctx_t *ctx) { |
97 | 0 | if (getenv("SSLKEYLOGFILE") != NULL) { |
98 | 0 | SSL_CTX_set_keylog_callback(ctx, sslkeylogfile_append); |
99 | 0 | } |
100 | 0 | } |
101 | | |
102 | | isc_result_t |
103 | 0 | isc_tlsctx_createclient(isc_tlsctx_t **ctxp) { |
104 | 0 | unsigned long err; |
105 | 0 | char errbuf[256]; |
106 | 0 | SSL_CTX *ctx = NULL; |
107 | 0 | const SSL_METHOD *method = NULL; |
108 | |
|
109 | 0 | REQUIRE(ctxp != NULL && *ctxp == NULL); |
110 | |
|
111 | 0 | method = TLS_client_method(); |
112 | 0 | if (method == NULL) { |
113 | 0 | goto ssl_error; |
114 | 0 | } |
115 | 0 | ctx = SSL_CTX_new(method); |
116 | 0 | if (ctx == NULL) { |
117 | 0 | goto ssl_error; |
118 | 0 | } |
119 | | |
120 | 0 | SSL_CTX_set_options(ctx, COMMON_SSL_OPTIONS); |
121 | |
|
122 | 0 | SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); |
123 | |
|
124 | 0 | sslkeylogfile_init(ctx); |
125 | |
|
126 | 0 | *ctxp = ctx; |
127 | |
|
128 | 0 | return ISC_R_SUCCESS; |
129 | | |
130 | 0 | ssl_error: |
131 | 0 | err = ERR_get_error(); |
132 | 0 | ERR_error_string_n(err, errbuf, sizeof(errbuf)); |
133 | 0 | isc_log_write(ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO, |
134 | 0 | ISC_LOG_ERROR, "Error initializing TLS context: %s", |
135 | 0 | errbuf); |
136 | |
|
137 | 0 | return ISC_R_TLSERROR; |
138 | 0 | } |
139 | | |
140 | | isc_result_t |
141 | | isc_tlsctx_load_certificate(isc_tlsctx_t *ctx, const char *keyfile, |
142 | 0 | const char *certfile) { |
143 | 0 | int rv; |
144 | 0 | REQUIRE(ctx != NULL); |
145 | 0 | REQUIRE(keyfile != NULL); |
146 | 0 | REQUIRE(certfile != NULL); |
147 | |
|
148 | 0 | rv = SSL_CTX_use_certificate_chain_file(ctx, certfile); |
149 | 0 | if (rv != 1) { |
150 | 0 | unsigned long err = ERR_peek_last_error(); |
151 | 0 | char errbuf[1024] = { 0 }; |
152 | 0 | ERR_error_string_n(err, errbuf, sizeof(errbuf)); |
153 | 0 | isc_log_write( |
154 | 0 | ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR, |
155 | 0 | ISC_LOG_ERROR, |
156 | 0 | "SSL_CTX_use_certificate_chain_file: '%s' failed: %s", |
157 | 0 | certfile, errbuf); |
158 | 0 | return ISC_R_TLSERROR; |
159 | 0 | } |
160 | 0 | rv = SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM); |
161 | 0 | if (rv != 1) { |
162 | 0 | unsigned long err = ERR_peek_last_error(); |
163 | 0 | char errbuf[1024] = { 0 }; |
164 | 0 | ERR_error_string_n(err, errbuf, sizeof(errbuf)); |
165 | 0 | isc_log_write(ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR, |
166 | 0 | ISC_LOG_ERROR, |
167 | 0 | "SSL_CTX_use_PrivateKey_file: '%s' failed: %s", |
168 | 0 | keyfile, errbuf); |
169 | 0 | return ISC_R_TLSERROR; |
170 | 0 | } |
171 | | |
172 | 0 | return ISC_R_SUCCESS; |
173 | 0 | } |
174 | | |
175 | | isc_result_t |
176 | | isc_tlsctx_createserver(const char *keyfile, const char *certfile, |
177 | 0 | isc_tlsctx_t **ctxp) { |
178 | 0 | int rv; |
179 | 0 | unsigned long err; |
180 | 0 | bool ephemeral = (keyfile == NULL && certfile == NULL); |
181 | 0 | X509 *cert = NULL; |
182 | 0 | EVP_PKEY *pkey = NULL; |
183 | 0 | SSL_CTX *ctx = NULL; |
184 | 0 | char errbuf[256]; |
185 | 0 | const SSL_METHOD *method = NULL; |
186 | |
|
187 | 0 | REQUIRE(ctxp != NULL && *ctxp == NULL); |
188 | 0 | REQUIRE((keyfile == NULL) == (certfile == NULL)); |
189 | |
|
190 | 0 | method = TLS_server_method(); |
191 | 0 | if (method == NULL) { |
192 | 0 | goto ssl_error; |
193 | 0 | } |
194 | 0 | ctx = SSL_CTX_new(method); |
195 | 0 | if (ctx == NULL) { |
196 | 0 | goto ssl_error; |
197 | 0 | } |
198 | 0 | RUNTIME_CHECK(ctx != NULL); |
199 | |
|
200 | 0 | SSL_CTX_set_options(ctx, COMMON_SSL_OPTIONS); |
201 | |
|
202 | 0 | SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); |
203 | |
|
204 | 0 | if (ephemeral) { |
205 | 0 | if (isc_ossl_wrap_generate_p256_key(&pkey) != ISC_R_SUCCESS) { |
206 | 0 | goto ssl_error; |
207 | 0 | } |
208 | | |
209 | 0 | cert = X509_new(); |
210 | 0 | if (cert == NULL) { |
211 | 0 | goto ssl_error; |
212 | 0 | } |
213 | | |
214 | 0 | ASN1_INTEGER_set(X509_get_serialNumber(cert), |
215 | 0 | (long)isc_random32()); |
216 | | |
217 | | /* |
218 | | * Set the "not before" property 5 minutes into the past to |
219 | | * accommodate with some possible clock skew across systems. |
220 | | */ |
221 | 0 | X509_gmtime_adj(X509_getm_notBefore(cert), -300); |
222 | | |
223 | | /* |
224 | | * We set the vailidy for 10 years. |
225 | | */ |
226 | 0 | X509_gmtime_adj(X509_getm_notAfter(cert), 3650 * 24 * 3600); |
227 | |
|
228 | 0 | X509_set_pubkey(cert, pkey); |
229 | |
|
230 | 0 | X509_NAME *name = X509_get_subject_name(cert); |
231 | |
|
232 | 0 | X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, |
233 | 0 | (const unsigned char *)"AQ", -1, -1, |
234 | 0 | 0); |
235 | 0 | X509_NAME_add_entry_by_txt( |
236 | 0 | name, "O", MBSTRING_ASC, |
237 | 0 | (const unsigned char *)"BIND9 ephemeral " |
238 | 0 | "certificate", |
239 | 0 | -1, -1, 0); |
240 | 0 | X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, |
241 | 0 | (const unsigned char *)"bind9.local", |
242 | 0 | -1, -1, 0); |
243 | |
|
244 | 0 | X509_set_issuer_name(cert, name); |
245 | 0 | X509_sign(cert, pkey, isc__crypto_md[ISC_MD_SHA256]); |
246 | 0 | rv = SSL_CTX_use_certificate(ctx, cert); |
247 | 0 | if (rv != 1) { |
248 | 0 | goto ssl_error; |
249 | 0 | } |
250 | 0 | rv = SSL_CTX_use_PrivateKey(ctx, pkey); |
251 | 0 | if (rv != 1) { |
252 | 0 | goto ssl_error; |
253 | 0 | } |
254 | | |
255 | 0 | X509_free(cert); |
256 | 0 | EVP_PKEY_free(pkey); |
257 | 0 | } else { |
258 | 0 | isc_result_t result; |
259 | 0 | result = isc_tlsctx_load_certificate(ctx, keyfile, certfile); |
260 | 0 | if (result != ISC_R_SUCCESS) { |
261 | 0 | goto ssl_error; |
262 | 0 | } |
263 | 0 | } |
264 | | |
265 | 0 | sslkeylogfile_init(ctx); |
266 | |
|
267 | 0 | *ctxp = ctx; |
268 | 0 | return ISC_R_SUCCESS; |
269 | | |
270 | 0 | ssl_error: |
271 | 0 | err = ERR_get_error(); |
272 | 0 | ERR_error_string_n(err, errbuf, sizeof(errbuf)); |
273 | 0 | isc_log_write(ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO, |
274 | 0 | ISC_LOG_ERROR, "Error initializing TLS context: %s", |
275 | 0 | errbuf); |
276 | |
|
277 | 0 | if (ctx != NULL) { |
278 | 0 | SSL_CTX_free(ctx); |
279 | 0 | } |
280 | 0 | if (cert != NULL) { |
281 | 0 | X509_free(cert); |
282 | 0 | } |
283 | 0 | if (pkey != NULL) { |
284 | 0 | EVP_PKEY_free(pkey); |
285 | 0 | } |
286 | |
|
287 | 0 | return ISC_R_TLSERROR; |
288 | 0 | } |
289 | | |
290 | | static long |
291 | 0 | get_tls_version_disable_bit(const isc_tls_protocol_version_t tls_ver) { |
292 | 0 | long bit = 0; |
293 | |
|
294 | 0 | switch (tls_ver) { |
295 | 0 | case ISC_TLS_PROTO_VER_1_2: |
296 | 0 | #ifdef SSL_OP_NO_TLSv1_2 |
297 | 0 | bit = SSL_OP_NO_TLSv1_2; |
298 | | #else |
299 | | bit = 0; |
300 | | #endif |
301 | 0 | break; |
302 | 0 | case ISC_TLS_PROTO_VER_1_3: |
303 | 0 | #ifdef SSL_OP_NO_TLSv1_3 |
304 | 0 | bit = SSL_OP_NO_TLSv1_3; |
305 | | #else |
306 | | bit = 0; |
307 | | #endif |
308 | 0 | break; |
309 | 0 | default: |
310 | 0 | UNREACHABLE(); |
311 | 0 | break; |
312 | 0 | }; |
313 | |
|
314 | 0 | return bit; |
315 | 0 | } |
316 | | |
317 | | bool |
318 | 0 | isc_tls_protocol_supported(const isc_tls_protocol_version_t tls_ver) { |
319 | 0 | return get_tls_version_disable_bit(tls_ver) != 0; |
320 | 0 | } |
321 | | |
322 | | isc_tls_protocol_version_t |
323 | 0 | isc_tls_protocol_name_to_version(const char *name) { |
324 | 0 | REQUIRE(name != NULL); |
325 | |
|
326 | 0 | if (strcasecmp(name, "TLSv1.2") == 0) { |
327 | 0 | return ISC_TLS_PROTO_VER_1_2; |
328 | 0 | } else if (strcasecmp(name, "TLSv1.3") == 0) { |
329 | 0 | return ISC_TLS_PROTO_VER_1_3; |
330 | 0 | } |
331 | | |
332 | 0 | return ISC_TLS_PROTO_VER_UNDEFINED; |
333 | 0 | } |
334 | | |
335 | | void |
336 | 0 | isc_tlsctx_set_protocols(isc_tlsctx_t *ctx, const uint32_t tls_versions) { |
337 | 0 | REQUIRE(ctx != NULL); |
338 | 0 | REQUIRE(tls_versions != 0); |
339 | 0 | long set_options = 0; |
340 | 0 | long clear_options = 0; |
341 | 0 | uint32_t versions = tls_versions; |
342 | | |
343 | | /* |
344 | | * The code below might be initially hard to follow because of the |
345 | | * double negation that OpenSSL enforces. |
346 | | * |
347 | | * Taking into account that OpenSSL provides bits to *disable* |
348 | | * specific protocol versions, like SSL_OP_NO_TLSv1_2, |
349 | | * SSL_OP_NO_TLSv1_3, etc., the code has the following logic: |
350 | | * |
351 | | * If a protocol version is not specified in the bitmask, get the |
352 | | * bit that disables it and add it to the set of TLS options to |
353 | | * set ('set_options'). Otherwise, if a protocol version is set, |
354 | | * add the bit to the set of options to clear ('clear_options'). |
355 | | */ |
356 | | |
357 | | /* TLS protocol versions are defined as powers of two. */ |
358 | 0 | for (uint32_t tls_ver = ISC_TLS_PROTO_VER_1_2; |
359 | 0 | tls_ver < ISC_TLS_PROTO_VER_UNDEFINED; tls_ver <<= 1) |
360 | 0 | { |
361 | 0 | if ((tls_versions & tls_ver) == 0) { |
362 | 0 | set_options |= get_tls_version_disable_bit(tls_ver); |
363 | 0 | } else { |
364 | | /* |
365 | | * Only supported versions should ever be passed to the |
366 | | * function SSL_CTX_clear_options. For example, in order |
367 | | * to enable TLS v1.2, we have to clear |
368 | | * SSL_OP_NO_TLSv1_2. Insist that the configuration file |
369 | | * was verified properly, so we are not trying to enable |
370 | | * an unsupported TLS version. |
371 | | */ |
372 | 0 | INSIST(isc_tls_protocol_supported(tls_ver)); |
373 | 0 | clear_options |= get_tls_version_disable_bit(tls_ver); |
374 | 0 | } |
375 | 0 | versions &= ~(tls_ver); |
376 | 0 | } |
377 | | |
378 | | /* All versions should be processed at this point, thus the value |
379 | | * must equal zero. If it is not, then some garbage has been |
380 | | * passed to the function; this situation is worth |
381 | | * investigation. */ |
382 | 0 | INSIST(versions == 0); |
383 | |
|
384 | 0 | (void)SSL_CTX_set_options(ctx, set_options); |
385 | 0 | (void)SSL_CTX_clear_options(ctx, clear_options); |
386 | 0 | } |
387 | | |
388 | | bool |
389 | 0 | isc_tlsctx_load_dhparams(isc_tlsctx_t *ctx, const char *dhparams_file) { |
390 | 0 | REQUIRE(ctx != NULL); |
391 | 0 | REQUIRE(dhparams_file != NULL); |
392 | 0 | REQUIRE(*dhparams_file != '\0'); |
393 | |
|
394 | | #if OPENSSL_VERSION_NUMBER < 0x30000000L |
395 | | /* OpenSSL < 3.0 */ |
396 | | DH *dh = NULL; |
397 | | FILE *paramfile; |
398 | | |
399 | | paramfile = fopen(dhparams_file, "r"); |
400 | | |
401 | | if (paramfile) { |
402 | | int check = 0; |
403 | | dh = PEM_read_DHparams(paramfile, NULL, NULL, NULL); |
404 | | fclose(paramfile); |
405 | | |
406 | | if (dh == NULL) { |
407 | | return false; |
408 | | } else if (DH_check(dh, &check) != 1 || check != 0) { |
409 | | DH_free(dh); |
410 | | return false; |
411 | | } |
412 | | } else { |
413 | | return false; |
414 | | } |
415 | | |
416 | | if (SSL_CTX_set_tmp_dh(ctx, dh) != 1) { |
417 | | DH_free(dh); |
418 | | return false; |
419 | | } |
420 | | |
421 | | DH_free(dh); |
422 | | #else |
423 | | /* OpenSSL >= 3.0: low level DH APIs are deprecated in OpenSSL 3.0 */ |
424 | 0 | EVP_PKEY *dh = NULL; |
425 | 0 | BIO *bio = NULL; |
426 | |
|
427 | 0 | bio = BIO_new_file(dhparams_file, "r"); |
428 | 0 | if (bio == NULL) { |
429 | 0 | return false; |
430 | 0 | } |
431 | | |
432 | 0 | dh = PEM_read_bio_Parameters(bio, NULL); |
433 | 0 | if (dh == NULL) { |
434 | 0 | BIO_free(bio); |
435 | 0 | return false; |
436 | 0 | } |
437 | | |
438 | 0 | if (SSL_CTX_set0_tmp_dh_pkey(ctx, dh) != 1) { |
439 | 0 | BIO_free(bio); |
440 | 0 | EVP_PKEY_free(dh); |
441 | 0 | return false; |
442 | 0 | } |
443 | | |
444 | | /* No need to call EVP_PKEY_free(dh) as the "dh" is owned by the |
445 | | * SSL context at this point. */ |
446 | | |
447 | 0 | BIO_free(bio); |
448 | 0 | #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */ |
449 | |
|
450 | 0 | return true; |
451 | 0 | } |
452 | | |
453 | | bool |
454 | 0 | isc_tls_cipherlist_valid(const char *cipherlist) { |
455 | 0 | isc_tlsctx_t *tmp_ctx = NULL; |
456 | 0 | const SSL_METHOD *method = NULL; |
457 | 0 | bool result; |
458 | 0 | REQUIRE(cipherlist != NULL); |
459 | |
|
460 | 0 | if (*cipherlist == '\0') { |
461 | 0 | return false; |
462 | 0 | } |
463 | | |
464 | 0 | method = TLS_server_method(); |
465 | 0 | if (method == NULL) { |
466 | 0 | return false; |
467 | 0 | } |
468 | 0 | tmp_ctx = SSL_CTX_new(method); |
469 | 0 | if (tmp_ctx == NULL) { |
470 | 0 | return false; |
471 | 0 | } |
472 | | |
473 | 0 | result = SSL_CTX_set_cipher_list(tmp_ctx, cipherlist) == 1; |
474 | |
|
475 | 0 | isc_tlsctx_free(&tmp_ctx); |
476 | |
|
477 | 0 | return result; |
478 | 0 | } |
479 | | |
480 | | void |
481 | 0 | isc_tlsctx_set_cipherlist(isc_tlsctx_t *ctx, const char *cipherlist) { |
482 | 0 | REQUIRE(ctx != NULL); |
483 | 0 | REQUIRE(cipherlist != NULL); |
484 | 0 | REQUIRE(*cipherlist != '\0'); |
485 | |
|
486 | 0 | RUNTIME_CHECK(SSL_CTX_set_cipher_list(ctx, cipherlist) == 1); |
487 | 0 | } |
488 | | |
489 | | bool |
490 | 0 | isc_tls_cipher_suites_valid(const char *cipher_suites) { |
491 | 0 | isc_tlsctx_t *tmp_ctx = NULL; |
492 | 0 | const SSL_METHOD *method = NULL; |
493 | 0 | bool result; |
494 | 0 | REQUIRE(cipher_suites != NULL); |
495 | |
|
496 | 0 | if (*cipher_suites == '\0') { |
497 | 0 | return false; |
498 | 0 | } |
499 | | |
500 | 0 | method = TLS_server_method(); |
501 | 0 | if (method == NULL) { |
502 | 0 | return false; |
503 | 0 | } |
504 | 0 | tmp_ctx = SSL_CTX_new(method); |
505 | 0 | if (tmp_ctx == NULL) { |
506 | 0 | return false; |
507 | 0 | } |
508 | | |
509 | 0 | result = SSL_CTX_set_ciphersuites(tmp_ctx, cipher_suites) == 1; |
510 | |
|
511 | 0 | isc_tlsctx_free(&tmp_ctx); |
512 | |
|
513 | 0 | return result; |
514 | 0 | } |
515 | | |
516 | | void |
517 | 0 | isc_tlsctx_set_cipher_suites(isc_tlsctx_t *ctx, const char *cipher_suites) { |
518 | 0 | REQUIRE(ctx != NULL); |
519 | 0 | REQUIRE(cipher_suites != NULL); |
520 | 0 | REQUIRE(*cipher_suites != '\0'); |
521 | |
|
522 | 0 | RUNTIME_CHECK(SSL_CTX_set_ciphersuites(ctx, cipher_suites) == 1); |
523 | 0 | } |
524 | | |
525 | | void |
526 | 0 | isc_tlsctx_prefer_server_ciphers(isc_tlsctx_t *ctx, const bool prefer) { |
527 | 0 | REQUIRE(ctx != NULL); |
528 | |
|
529 | 0 | if (prefer) { |
530 | 0 | (void)SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); |
531 | 0 | } else { |
532 | 0 | (void)SSL_CTX_clear_options(ctx, |
533 | 0 | SSL_OP_CIPHER_SERVER_PREFERENCE); |
534 | 0 | } |
535 | 0 | } |
536 | | |
537 | | void |
538 | 0 | isc_tlsctx_session_tickets(isc_tlsctx_t *ctx, const bool use) { |
539 | 0 | REQUIRE(ctx != NULL); |
540 | |
|
541 | 0 | if (!use) { |
542 | 0 | (void)SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET); |
543 | 0 | } else { |
544 | 0 | (void)SSL_CTX_clear_options(ctx, SSL_OP_NO_TICKET); |
545 | 0 | } |
546 | 0 | } |
547 | | |
548 | | isc_tls_t * |
549 | 0 | isc_tls_create(isc_tlsctx_t *ctx) { |
550 | 0 | isc_tls_t *newctx = NULL; |
551 | |
|
552 | 0 | REQUIRE(ctx != NULL); |
553 | |
|
554 | 0 | newctx = SSL_new(ctx); |
555 | 0 | if (newctx == NULL) { |
556 | 0 | char errbuf[256]; |
557 | 0 | unsigned long err = ERR_get_error(); |
558 | |
|
559 | 0 | ERR_error_string_n(err, errbuf, sizeof(errbuf)); |
560 | 0 | fprintf(stderr, "%s:SSL_new(%p) -> %s\n", __func__, ctx, |
561 | 0 | errbuf); |
562 | 0 | } |
563 | |
|
564 | 0 | return newctx; |
565 | 0 | } |
566 | | |
567 | | void |
568 | 0 | isc_tls_free(isc_tls_t **tlsp) { |
569 | 0 | isc_tls_t *tls = NULL; |
570 | 0 | REQUIRE(tlsp != NULL && *tlsp != NULL); |
571 | |
|
572 | 0 | tls = *tlsp; |
573 | 0 | *tlsp = NULL; |
574 | 0 | SSL_free(tls); |
575 | 0 | } |
576 | | |
577 | | const char * |
578 | 0 | isc_tls_verify_peer_result_string(isc_tls_t *tls) { |
579 | 0 | REQUIRE(tls != NULL); |
580 | |
|
581 | 0 | return X509_verify_cert_error_string(SSL_get_verify_result(tls)); |
582 | 0 | } |
583 | | |
584 | | #if HAVE_LIBNGHTTP2 |
585 | | #ifndef OPENSSL_NO_NEXTPROTONEG |
586 | | /* |
587 | | * NPN TLS extension client callback. |
588 | | */ |
589 | | static int |
590 | | select_next_proto_cb(SSL *ssl, unsigned char **out, unsigned char *outlen, |
591 | 0 | const unsigned char *in, unsigned int inlen, void *arg) { |
592 | 0 | UNUSED(ssl); |
593 | 0 | UNUSED(arg); |
594 | |
|
595 | 0 | if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) { |
596 | 0 | return SSL_TLSEXT_ERR_NOACK; |
597 | 0 | } |
598 | 0 | return SSL_TLSEXT_ERR_OK; |
599 | 0 | } |
600 | | #endif /* !OPENSSL_NO_NEXTPROTONEG */ |
601 | | |
602 | | void |
603 | 0 | isc_tlsctx_enable_http2client_alpn(isc_tlsctx_t *ctx) { |
604 | 0 | REQUIRE(ctx != NULL); |
605 | |
|
606 | 0 | #ifndef OPENSSL_NO_NEXTPROTONEG |
607 | 0 | SSL_CTX_set_next_proto_select_cb(ctx, select_next_proto_cb, NULL); |
608 | 0 | #endif /* !OPENSSL_NO_NEXTPROTONEG */ |
609 | |
|
610 | 0 | SSL_CTX_set_alpn_protos(ctx, (const unsigned char *)NGHTTP2_PROTO_ALPN, |
611 | 0 | NGHTTP2_PROTO_ALPN_LEN); |
612 | 0 | } |
613 | | |
614 | | #ifndef OPENSSL_NO_NEXTPROTONEG |
615 | | static int |
616 | | next_proto_cb(isc_tls_t *ssl, const unsigned char **data, unsigned int *len, |
617 | 0 | void *arg) { |
618 | 0 | UNUSED(ssl); |
619 | 0 | UNUSED(arg); |
620 | |
|
621 | 0 | *data = (const unsigned char *)NGHTTP2_PROTO_ALPN; |
622 | 0 | *len = (unsigned int)NGHTTP2_PROTO_ALPN_LEN; |
623 | 0 | return SSL_TLSEXT_ERR_OK; |
624 | 0 | } |
625 | | #endif /* !OPENSSL_NO_NEXTPROTONEG */ |
626 | | |
627 | | static int |
628 | | alpn_select_proto_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, |
629 | 0 | const unsigned char *in, unsigned int inlen, void *arg) { |
630 | 0 | int ret; |
631 | |
|
632 | 0 | UNUSED(ssl); |
633 | 0 | UNUSED(arg); |
634 | |
|
635 | 0 | ret = nghttp2_select_next_protocol((unsigned char **)(uintptr_t)out, |
636 | 0 | outlen, in, inlen); |
637 | |
|
638 | 0 | if (ret != 1) { |
639 | 0 | return SSL_TLSEXT_ERR_NOACK; |
640 | 0 | } |
641 | | |
642 | 0 | return SSL_TLSEXT_ERR_OK; |
643 | 0 | } |
644 | | |
645 | | void |
646 | 0 | isc_tlsctx_enable_http2server_alpn(isc_tlsctx_t *tls) { |
647 | 0 | REQUIRE(tls != NULL); |
648 | |
|
649 | 0 | #ifndef OPENSSL_NO_NEXTPROTONEG |
650 | 0 | SSL_CTX_set_next_protos_advertised_cb(tls, next_proto_cb, NULL); |
651 | 0 | #endif // OPENSSL_NO_NEXTPROTONEG |
652 | 0 | SSL_CTX_set_alpn_select_cb(tls, alpn_select_proto_cb, NULL); |
653 | 0 | } |
654 | | #endif /* HAVE_LIBNGHTTP2 */ |
655 | | |
656 | | void |
657 | | isc_tls_get_selected_alpn(isc_tls_t *tls, const unsigned char **alpn, |
658 | 0 | unsigned int *alpnlen) { |
659 | 0 | REQUIRE(tls != NULL); |
660 | 0 | REQUIRE(alpn != NULL); |
661 | 0 | REQUIRE(alpnlen != NULL); |
662 | |
|
663 | 0 | #ifndef OPENSSL_NO_NEXTPROTONEG |
664 | 0 | SSL_get0_next_proto_negotiated(tls, alpn, alpnlen); |
665 | 0 | #endif |
666 | 0 | if (*alpn == NULL) { |
667 | 0 | SSL_get0_alpn_selected(tls, alpn, alpnlen); |
668 | 0 | } |
669 | 0 | } |
670 | | |
671 | | static bool |
672 | | protoneg_check_protocol(const uint8_t **pout, uint8_t *pout_len, |
673 | | const uint8_t *in, size_t in_len, const uint8_t *key, |
674 | 0 | size_t key_len) { |
675 | 0 | for (size_t i = 0; i + key_len <= in_len; i += (size_t)(in[i] + 1)) { |
676 | 0 | if (memcmp(&in[i], key, key_len) == 0) { |
677 | 0 | *pout = (const uint8_t *)(&in[i + 1]); |
678 | 0 | *pout_len = in[i]; |
679 | 0 | return true; |
680 | 0 | } |
681 | 0 | } |
682 | 0 | return false; |
683 | 0 | } |
684 | | |
685 | | /* dot prepended by its length (3 bytes) */ |
686 | 0 | #define DOT_PROTO_ALPN "\x3" ISC_TLS_DOT_PROTO_ALPN_ID |
687 | 0 | #define DOT_PROTO_ALPN_LEN (sizeof(DOT_PROTO_ALPN) - 1) |
688 | | |
689 | | static bool |
690 | | dot_select_next_protocol(const uint8_t **pout, uint8_t *pout_len, |
691 | 0 | const uint8_t *in, size_t in_len) { |
692 | 0 | return protoneg_check_protocol(pout, pout_len, in, in_len, |
693 | 0 | (const uint8_t *)DOT_PROTO_ALPN, |
694 | 0 | DOT_PROTO_ALPN_LEN); |
695 | 0 | } |
696 | | |
697 | | void |
698 | 0 | isc_tlsctx_enable_dot_client_alpn(isc_tlsctx_t *ctx) { |
699 | 0 | REQUIRE(ctx != NULL); |
700 | |
|
701 | 0 | SSL_CTX_set_alpn_protos(ctx, (const uint8_t *)DOT_PROTO_ALPN, |
702 | 0 | DOT_PROTO_ALPN_LEN); |
703 | 0 | } |
704 | | |
705 | | static int |
706 | | dot_alpn_select_proto_cb(SSL *ssl, const unsigned char **out, |
707 | | unsigned char *outlen, const unsigned char *in, |
708 | 0 | unsigned int inlen, void *arg) { |
709 | 0 | bool ret; |
710 | |
|
711 | 0 | UNUSED(ssl); |
712 | 0 | UNUSED(arg); |
713 | |
|
714 | 0 | ret = dot_select_next_protocol(out, outlen, in, inlen); |
715 | |
|
716 | 0 | if (!ret) { |
717 | 0 | return SSL_TLSEXT_ERR_NOACK; |
718 | 0 | } |
719 | | |
720 | 0 | return SSL_TLSEXT_ERR_OK; |
721 | 0 | } |
722 | | |
723 | | void |
724 | 0 | isc_tlsctx_enable_dot_server_alpn(isc_tlsctx_t *tls) { |
725 | 0 | REQUIRE(tls != NULL); |
726 | |
|
727 | 0 | SSL_CTX_set_alpn_select_cb(tls, dot_alpn_select_proto_cb, NULL); |
728 | 0 | } |
729 | | |
730 | | isc_result_t |
731 | | isc_tlsctx_enable_peer_verification(isc_tlsctx_t *tlsctx, const bool is_server, |
732 | | isc_tls_cert_store_t *store, |
733 | | const char *hostname, |
734 | 0 | bool hostname_ignore_subject) { |
735 | 0 | int ret = 0; |
736 | 0 | REQUIRE(tlsctx != NULL); |
737 | 0 | REQUIRE(store != NULL); |
738 | | |
739 | | /* Set the hostname/IP address. */ |
740 | 0 | if (!is_server && hostname != NULL && *hostname != '\0') { |
741 | 0 | struct in6_addr sa6; |
742 | 0 | struct in_addr sa; |
743 | 0 | X509_VERIFY_PARAM *param = SSL_CTX_get0_param(tlsctx); |
744 | 0 | unsigned int hostflags = X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS; |
745 | | |
746 | | /* It might be an IP address. */ |
747 | 0 | if (inet_pton(AF_INET6, hostname, &sa6) == 1 || |
748 | 0 | inet_pton(AF_INET, hostname, &sa) == 1) |
749 | 0 | { |
750 | 0 | ret = X509_VERIFY_PARAM_set1_ip_asc(param, hostname); |
751 | 0 | } else { |
752 | | /* It seems that it is a host name. Let's set it. */ |
753 | 0 | ret = X509_VERIFY_PARAM_set1_host(param, hostname, 0); |
754 | 0 | } |
755 | 0 | if (ret != 1) { |
756 | 0 | ERR_clear_error(); |
757 | 0 | return ISC_R_FAILURE; |
758 | 0 | } |
759 | | |
760 | 0 | #ifdef X509_CHECK_FLAG_NEVER_CHECK_SUBJECT |
761 | | /* |
762 | | * According to the RFC 8310, Section 8.1, Subject field MUST |
763 | | * NOT be inspected when verifying a hostname when using |
764 | | * DoT. Only SubjectAltName must be checked instead. That is |
765 | | * not the case for HTTPS, though. |
766 | | * |
767 | | * Unfortunately, some quite old versions of OpenSSL (< 1.1.1) |
768 | | * might lack the functionality to implement that. It should |
769 | | * have very little real-world consequences, as most of the |
770 | | * production-ready certificates issued by real CAs will have |
771 | | * SubjectAltName set. In such a case, the Subject field is |
772 | | * ignored. |
773 | | */ |
774 | 0 | if (hostname_ignore_subject) { |
775 | 0 | hostflags |= X509_CHECK_FLAG_NEVER_CHECK_SUBJECT; |
776 | 0 | } |
777 | | #else |
778 | | UNUSED(hostname_ignore_subject); |
779 | | #endif |
780 | 0 | X509_VERIFY_PARAM_set_hostflags(param, hostflags); |
781 | 0 | } |
782 | | |
783 | | /* "Attach" the cert store to the context */ |
784 | 0 | SSL_CTX_set1_cert_store(tlsctx, store); |
785 | | |
786 | | /* enable verification */ |
787 | 0 | if (is_server) { |
788 | 0 | SSL_CTX_set_verify(tlsctx, |
789 | 0 | SSL_VERIFY_PEER | |
790 | 0 | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, |
791 | 0 | NULL); |
792 | 0 | } else { |
793 | 0 | SSL_CTX_set_verify(tlsctx, SSL_VERIFY_PEER, NULL); |
794 | 0 | } |
795 | |
|
796 | 0 | return ISC_R_SUCCESS; |
797 | 0 | } |
798 | | |
799 | | isc_result_t |
800 | 0 | isc_tlsctx_load_client_ca_names(isc_tlsctx_t *ctx, const char *ca_bundle_file) { |
801 | 0 | STACK_OF(X509_NAME) * cert_names; |
802 | 0 | REQUIRE(ctx != NULL); |
803 | 0 | REQUIRE(ca_bundle_file != NULL); |
804 | |
|
805 | 0 | cert_names = SSL_load_client_CA_file(ca_bundle_file); |
806 | 0 | if (cert_names == NULL) { |
807 | 0 | ERR_clear_error(); |
808 | 0 | return ISC_R_FAILURE; |
809 | 0 | } |
810 | | |
811 | 0 | SSL_CTX_set_client_CA_list(ctx, cert_names); |
812 | |
|
813 | 0 | return ISC_R_SUCCESS; |
814 | 0 | } |
815 | | |
816 | | isc_result_t |
817 | | isc_tls_cert_store_create(const char *ca_bundle_filename, |
818 | 0 | isc_tls_cert_store_t **pstore) { |
819 | 0 | int ret = 0; |
820 | 0 | isc_tls_cert_store_t *store = NULL; |
821 | 0 | REQUIRE(pstore != NULL && *pstore == NULL); |
822 | |
|
823 | 0 | store = X509_STORE_new(); |
824 | 0 | if (store == NULL) { |
825 | 0 | goto error; |
826 | 0 | } |
827 | | |
828 | | /* Let's treat empty string as the default (system wide) store */ |
829 | 0 | if (ca_bundle_filename != NULL && *ca_bundle_filename == '\0') { |
830 | 0 | ca_bundle_filename = NULL; |
831 | 0 | } |
832 | |
|
833 | 0 | if (ca_bundle_filename == NULL) { |
834 | 0 | ret = X509_STORE_set_default_paths(store); |
835 | 0 | } else { |
836 | 0 | ret = X509_STORE_load_locations(store, ca_bundle_filename, |
837 | 0 | NULL); |
838 | 0 | } |
839 | |
|
840 | 0 | if (ret == 0) { |
841 | 0 | goto error; |
842 | 0 | } |
843 | | |
844 | 0 | *pstore = store; |
845 | 0 | return ISC_R_SUCCESS; |
846 | | |
847 | 0 | error: |
848 | 0 | ERR_clear_error(); |
849 | 0 | if (store != NULL) { |
850 | 0 | X509_STORE_free(store); |
851 | 0 | } |
852 | 0 | return ISC_R_FAILURE; |
853 | 0 | } |
854 | | |
855 | | void |
856 | 0 | isc_tls_cert_store_free(isc_tls_cert_store_t **pstore) { |
857 | 0 | isc_tls_cert_store_t *store; |
858 | 0 | REQUIRE(pstore != NULL && *pstore != NULL); |
859 | |
|
860 | 0 | store = *pstore; |
861 | |
|
862 | 0 | X509_STORE_free(store); |
863 | |
|
864 | 0 | *pstore = NULL; |
865 | 0 | } |
866 | | |
867 | 0 | #define TLSCTX_CACHE_MAGIC ISC_MAGIC('T', 'l', 'S', 'c') |
868 | | #define VALID_TLSCTX_CACHE(t) ISC_MAGIC_VALID(t, TLSCTX_CACHE_MAGIC) |
869 | | |
870 | 0 | #define TLSCTX_CLIENT_SESSION_CACHE_MAGIC ISC_MAGIC('T', 'l', 'C', 'c') |
871 | | #define VALID_TLSCTX_CLIENT_SESSION_CACHE(t) \ |
872 | | ISC_MAGIC_VALID(t, TLSCTX_CLIENT_SESSION_CACHE_MAGIC) |
873 | | |
874 | | typedef struct isc_tlsctx_cache_entry { |
875 | | /* |
876 | | * We need a TLS context entry for each transport on both IPv4 and |
877 | | * IPv6 in order to avoid cluttering a context-specific |
878 | | * session-resumption cache. |
879 | | */ |
880 | | isc_tlsctx_t *ctx[isc_tlsctx_cache_count - 1][2]; |
881 | | isc_tlsctx_client_session_cache_t |
882 | | *client_sess_cache[isc_tlsctx_cache_count - 1][2]; |
883 | | /* |
884 | | * One certificate store is enough for all the contexts defined |
885 | | * above. We need that for peer validation. |
886 | | */ |
887 | | isc_tls_cert_store_t *ca_store; |
888 | | } isc_tlsctx_cache_entry_t; |
889 | | |
890 | | struct isc_tlsctx_cache { |
891 | | uint32_t magic; |
892 | | isc_refcount_t references; |
893 | | isc_mem_t *mctx; |
894 | | |
895 | | isc_rwlock_t rwlock; |
896 | | isc_ht_t *data; |
897 | | }; |
898 | | |
899 | | void |
900 | 0 | isc_tlsctx_cache_create(isc_mem_t *mctx, isc_tlsctx_cache_t **cachep) { |
901 | 0 | isc_tlsctx_cache_t *nc; |
902 | |
|
903 | 0 | REQUIRE(cachep != NULL && *cachep == NULL); |
904 | 0 | nc = isc_mem_get(mctx, sizeof(*nc)); |
905 | |
|
906 | 0 | *nc = (isc_tlsctx_cache_t){ .magic = TLSCTX_CACHE_MAGIC }; |
907 | 0 | isc_refcount_init(&nc->references, 1); |
908 | 0 | isc_mem_attach(mctx, &nc->mctx); |
909 | |
|
910 | 0 | isc_ht_init(&nc->data, mctx, 5, ISC_HT_CASE_SENSITIVE); |
911 | 0 | isc_rwlock_init(&nc->rwlock); |
912 | |
|
913 | 0 | *cachep = nc; |
914 | 0 | } |
915 | | |
916 | | void |
917 | | isc_tlsctx_cache_attach(isc_tlsctx_cache_t *source, |
918 | 0 | isc_tlsctx_cache_t **targetp) { |
919 | 0 | REQUIRE(VALID_TLSCTX_CACHE(source)); |
920 | 0 | REQUIRE(targetp != NULL && *targetp == NULL); |
921 | |
|
922 | 0 | isc_refcount_increment(&source->references); |
923 | |
|
924 | 0 | *targetp = source; |
925 | 0 | } |
926 | | |
927 | | static void |
928 | 0 | tlsctx_cache_entry_destroy(isc_mem_t *mctx, isc_tlsctx_cache_entry_t *entry) { |
929 | 0 | size_t i, k; |
930 | |
|
931 | 0 | for (i = 0; i < (isc_tlsctx_cache_count - 1); i++) { |
932 | 0 | for (k = 0; k < 2; k++) { |
933 | 0 | if (entry->ctx[i][k] != NULL) { |
934 | 0 | isc_tlsctx_free(&entry->ctx[i][k]); |
935 | 0 | } |
936 | |
|
937 | 0 | if (entry->client_sess_cache[i][k] != NULL) { |
938 | 0 | isc_tlsctx_client_session_cache_detach( |
939 | 0 | &entry->client_sess_cache[i][k]); |
940 | 0 | } |
941 | 0 | } |
942 | 0 | } |
943 | 0 | if (entry->ca_store != NULL) { |
944 | 0 | isc_tls_cert_store_free(&entry->ca_store); |
945 | 0 | } |
946 | 0 | isc_mem_put(mctx, entry, sizeof(*entry)); |
947 | 0 | } |
948 | | |
949 | | static void |
950 | 0 | tlsctx_cache_destroy(isc_tlsctx_cache_t *cache) { |
951 | 0 | isc_ht_iter_t *it = NULL; |
952 | 0 | isc_result_t result; |
953 | |
|
954 | 0 | cache->magic = 0; |
955 | |
|
956 | 0 | isc_refcount_destroy(&cache->references); |
957 | |
|
958 | 0 | isc_ht_iter_create(cache->data, &it); |
959 | 0 | for (result = isc_ht_iter_first(it); result == ISC_R_SUCCESS; |
960 | 0 | result = isc_ht_iter_delcurrent_next(it)) |
961 | 0 | { |
962 | 0 | isc_tlsctx_cache_entry_t *entry = NULL; |
963 | 0 | isc_ht_iter_current(it, (void **)&entry); |
964 | 0 | tlsctx_cache_entry_destroy(cache->mctx, entry); |
965 | 0 | } |
966 | |
|
967 | 0 | isc_ht_iter_destroy(&it); |
968 | 0 | isc_ht_destroy(&cache->data); |
969 | 0 | isc_rwlock_destroy(&cache->rwlock); |
970 | 0 | isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache)); |
971 | 0 | } |
972 | | |
973 | | void |
974 | 0 | isc_tlsctx_cache_detach(isc_tlsctx_cache_t **cachep) { |
975 | 0 | isc_tlsctx_cache_t *cache = NULL; |
976 | |
|
977 | 0 | REQUIRE(cachep != NULL); |
978 | |
|
979 | 0 | cache = *cachep; |
980 | 0 | *cachep = NULL; |
981 | |
|
982 | 0 | REQUIRE(VALID_TLSCTX_CACHE(cache)); |
983 | |
|
984 | 0 | if (isc_refcount_decrement(&cache->references) == 1) { |
985 | 0 | tlsctx_cache_destroy(cache); |
986 | 0 | } |
987 | 0 | } |
988 | | |
989 | | isc_result_t |
990 | | isc_tlsctx_cache_add( |
991 | | isc_tlsctx_cache_t *cache, const char *name, |
992 | | const isc_tlsctx_cache_transport_t transport, const uint16_t family, |
993 | | isc_tlsctx_t *ctx, isc_tls_cert_store_t *store, |
994 | | isc_tlsctx_client_session_cache_t *client_sess_cache, |
995 | | isc_tlsctx_t **pfound, isc_tls_cert_store_t **pfound_store, |
996 | 0 | isc_tlsctx_client_session_cache_t **pfound_client_sess_cache) { |
997 | 0 | isc_result_t result = ISC_R_FAILURE; |
998 | 0 | size_t name_len, tr_offset; |
999 | 0 | isc_tlsctx_cache_entry_t *entry = NULL; |
1000 | 0 | bool ipv6; |
1001 | |
|
1002 | 0 | REQUIRE(VALID_TLSCTX_CACHE(cache)); |
1003 | 0 | REQUIRE(client_sess_cache == NULL || |
1004 | 0 | VALID_TLSCTX_CLIENT_SESSION_CACHE(client_sess_cache)); |
1005 | 0 | REQUIRE(name != NULL && *name != '\0'); |
1006 | 0 | REQUIRE(transport > isc_tlsctx_cache_none && |
1007 | 0 | transport < isc_tlsctx_cache_count); |
1008 | 0 | REQUIRE(family == AF_INET || family == AF_INET6); |
1009 | 0 | REQUIRE(ctx != NULL); |
1010 | |
|
1011 | 0 | tr_offset = (transport - 1); |
1012 | 0 | ipv6 = (family == AF_INET6); |
1013 | |
|
1014 | 0 | RWLOCK(&cache->rwlock, isc_rwlocktype_write); |
1015 | |
|
1016 | 0 | name_len = strlen(name); |
1017 | 0 | result = isc_ht_find(cache->data, (const uint8_t *)name, name_len, |
1018 | 0 | (void **)&entry); |
1019 | 0 | if (result == ISC_R_SUCCESS && entry->ctx[tr_offset][ipv6] != NULL) { |
1020 | 0 | isc_tlsctx_client_session_cache_t *found_client_sess_cache; |
1021 | | /* The entry exists. */ |
1022 | 0 | if (pfound != NULL) { |
1023 | 0 | INSIST(*pfound == NULL); |
1024 | 0 | *pfound = entry->ctx[tr_offset][ipv6]; |
1025 | 0 | } |
1026 | |
|
1027 | 0 | if (pfound_store != NULL && entry->ca_store != NULL) { |
1028 | 0 | INSIST(*pfound_store == NULL); |
1029 | 0 | *pfound_store = entry->ca_store; |
1030 | 0 | } |
1031 | |
|
1032 | 0 | found_client_sess_cache = |
1033 | 0 | entry->client_sess_cache[tr_offset][ipv6]; |
1034 | 0 | if (pfound_client_sess_cache != NULL && |
1035 | 0 | found_client_sess_cache != NULL) |
1036 | 0 | { |
1037 | 0 | INSIST(*pfound_client_sess_cache == NULL); |
1038 | 0 | *pfound_client_sess_cache = found_client_sess_cache; |
1039 | 0 | } |
1040 | 0 | result = ISC_R_EXISTS; |
1041 | 0 | } else if (result == ISC_R_SUCCESS && |
1042 | 0 | entry->ctx[tr_offset][ipv6] == NULL) |
1043 | 0 | { |
1044 | | /* |
1045 | | * The hash table entry exists, but is not filled for this |
1046 | | * particular transport/IP type combination. |
1047 | | */ |
1048 | 0 | entry->ctx[tr_offset][ipv6] = ctx; |
1049 | 0 | entry->client_sess_cache[tr_offset][ipv6] = client_sess_cache; |
1050 | | /* |
1051 | | * As the passed certificates store object is supposed |
1052 | | * to be internally managed by the cache object anyway, |
1053 | | * we might destroy the unneeded store object right now. |
1054 | | */ |
1055 | 0 | if (store != NULL && store != entry->ca_store) { |
1056 | 0 | isc_tls_cert_store_free(&store); |
1057 | 0 | } |
1058 | 0 | result = ISC_R_SUCCESS; |
1059 | 0 | } else { |
1060 | | /* |
1061 | | * The hash table entry does not exist, let's create one. |
1062 | | */ |
1063 | 0 | INSIST(result != ISC_R_SUCCESS); |
1064 | 0 | entry = isc_mem_get(cache->mctx, sizeof(*entry)); |
1065 | 0 | *entry = (isc_tlsctx_cache_entry_t){ |
1066 | 0 | .ca_store = store, |
1067 | 0 | }; |
1068 | |
|
1069 | 0 | entry->ctx[tr_offset][ipv6] = ctx; |
1070 | 0 | entry->client_sess_cache[tr_offset][ipv6] = client_sess_cache; |
1071 | 0 | RUNTIME_CHECK(isc_ht_add(cache->data, (const uint8_t *)name, |
1072 | 0 | name_len, |
1073 | 0 | (void *)entry) == ISC_R_SUCCESS); |
1074 | 0 | result = ISC_R_SUCCESS; |
1075 | 0 | } |
1076 | |
|
1077 | 0 | RWUNLOCK(&cache->rwlock, isc_rwlocktype_write); |
1078 | |
|
1079 | 0 | return result; |
1080 | 0 | } |
1081 | | |
1082 | | isc_result_t |
1083 | | isc_tlsctx_cache_find( |
1084 | | isc_tlsctx_cache_t *cache, const char *name, |
1085 | | const isc_tlsctx_cache_transport_t transport, const uint16_t family, |
1086 | | isc_tlsctx_t **pctx, isc_tls_cert_store_t **pstore, |
1087 | 0 | isc_tlsctx_client_session_cache_t **pfound_client_sess_cache) { |
1088 | 0 | isc_result_t result = ISC_R_FAILURE; |
1089 | 0 | size_t tr_offset; |
1090 | 0 | isc_tlsctx_cache_entry_t *entry = NULL; |
1091 | 0 | bool ipv6; |
1092 | |
|
1093 | 0 | REQUIRE(VALID_TLSCTX_CACHE(cache)); |
1094 | 0 | REQUIRE(name != NULL && *name != '\0'); |
1095 | 0 | REQUIRE(transport > isc_tlsctx_cache_none && |
1096 | 0 | transport < isc_tlsctx_cache_count); |
1097 | 0 | REQUIRE(family == AF_INET || family == AF_INET6); |
1098 | 0 | REQUIRE(pctx != NULL && *pctx == NULL); |
1099 | |
|
1100 | 0 | tr_offset = (transport - 1); |
1101 | 0 | ipv6 = (family == AF_INET6); |
1102 | |
|
1103 | 0 | RWLOCK(&cache->rwlock, isc_rwlocktype_read); |
1104 | |
|
1105 | 0 | result = isc_ht_find(cache->data, (const uint8_t *)name, strlen(name), |
1106 | 0 | (void **)&entry); |
1107 | |
|
1108 | 0 | if (result == ISC_R_SUCCESS && pstore != NULL && |
1109 | 0 | entry->ca_store != NULL) |
1110 | 0 | { |
1111 | 0 | *pstore = entry->ca_store; |
1112 | 0 | } |
1113 | |
|
1114 | 0 | if (result == ISC_R_SUCCESS && entry->ctx[tr_offset][ipv6] != NULL) { |
1115 | 0 | isc_tlsctx_client_session_cache_t *found_client_sess_cache = |
1116 | 0 | entry->client_sess_cache[tr_offset][ipv6]; |
1117 | |
|
1118 | 0 | *pctx = entry->ctx[tr_offset][ipv6]; |
1119 | |
|
1120 | 0 | if (pfound_client_sess_cache != NULL && |
1121 | 0 | found_client_sess_cache != NULL) |
1122 | 0 | { |
1123 | 0 | INSIST(*pfound_client_sess_cache == NULL); |
1124 | 0 | *pfound_client_sess_cache = found_client_sess_cache; |
1125 | 0 | } |
1126 | 0 | } else if (result == ISC_R_SUCCESS && |
1127 | 0 | entry->ctx[tr_offset][ipv6] == NULL) |
1128 | 0 | { |
1129 | 0 | result = ISC_R_NOTFOUND; |
1130 | 0 | } else { |
1131 | 0 | INSIST(result != ISC_R_SUCCESS); |
1132 | 0 | } |
1133 | |
|
1134 | 0 | RWUNLOCK(&cache->rwlock, isc_rwlocktype_read); |
1135 | |
|
1136 | 0 | return result; |
1137 | 0 | } |
1138 | | |
1139 | | typedef struct client_session_cache_entry client_session_cache_entry_t; |
1140 | | |
1141 | | typedef struct client_session_cache_bucket { |
1142 | | char *bucket_key; |
1143 | | size_t bucket_key_len; |
1144 | | /* Cache entries within the bucket (from the oldest to the newest). */ |
1145 | | ISC_LIST(client_session_cache_entry_t) entries; |
1146 | | } client_session_cache_bucket_t; |
1147 | | |
1148 | | struct client_session_cache_entry { |
1149 | | SSL_SESSION *session; |
1150 | | client_session_cache_bucket_t *bucket; /* "Parent" bucket pointer. */ |
1151 | | ISC_LINK(client_session_cache_entry_t) bucket_link; |
1152 | | ISC_LINK(client_session_cache_entry_t) cache_link; |
1153 | | }; |
1154 | | |
1155 | | struct isc_tlsctx_client_session_cache { |
1156 | | uint32_t magic; |
1157 | | isc_refcount_t references; |
1158 | | isc_mem_t *mctx; |
1159 | | |
1160 | | /* |
1161 | | * We need to keep a reference to the related TLS context in order |
1162 | | * to ensure that it remains valid while the TLS client sessions |
1163 | | * cache object is valid, as every TLS session object |
1164 | | * (SSL_SESSION) is "tied" to a particular context. |
1165 | | */ |
1166 | | isc_tlsctx_t *ctx; |
1167 | | |
1168 | | /* |
1169 | | * The idea is to have one bucket per remote server. Each bucket, |
1170 | | * can maintain multiple TLS sessions to that server, as BIND |
1171 | | * might want to establish multiple TLS connections to the remote |
1172 | | * server at once. |
1173 | | */ |
1174 | | isc_ht_t *buckets; |
1175 | | |
1176 | | /* |
1177 | | * The list of all current entries within the cache maintained in |
1178 | | * LRU-manner, so that the oldest entry might be efficiently |
1179 | | * removed. |
1180 | | */ |
1181 | | ISC_LIST(client_session_cache_entry_t) lru_entries; |
1182 | | /* Number of the entries within the cache. */ |
1183 | | size_t nentries; |
1184 | | /* Maximum number of the entries within the cache. */ |
1185 | | size_t max_entries; |
1186 | | |
1187 | | isc_mutex_t lock; |
1188 | | }; |
1189 | | |
1190 | | void |
1191 | | isc_tlsctx_client_session_cache_create( |
1192 | | isc_mem_t *mctx, isc_tlsctx_t *ctx, const size_t max_entries, |
1193 | 0 | isc_tlsctx_client_session_cache_t **cachep) { |
1194 | 0 | isc_tlsctx_client_session_cache_t *nc; |
1195 | |
|
1196 | 0 | REQUIRE(ctx != NULL); |
1197 | 0 | REQUIRE(max_entries > 0); |
1198 | 0 | REQUIRE(cachep != NULL && *cachep == NULL); |
1199 | |
|
1200 | 0 | nc = isc_mem_get(mctx, sizeof(*nc)); |
1201 | |
|
1202 | 0 | *nc = (isc_tlsctx_client_session_cache_t){ .max_entries = max_entries }; |
1203 | 0 | isc_refcount_init(&nc->references, 1); |
1204 | 0 | isc_mem_attach(mctx, &nc->mctx); |
1205 | 0 | isc_tlsctx_attach(ctx, &nc->ctx); |
1206 | |
|
1207 | 0 | isc_ht_init(&nc->buckets, mctx, 5, ISC_HT_CASE_SENSITIVE); |
1208 | 0 | ISC_LIST_INIT(nc->lru_entries); |
1209 | 0 | isc_mutex_init(&nc->lock); |
1210 | |
|
1211 | 0 | nc->magic = TLSCTX_CLIENT_SESSION_CACHE_MAGIC; |
1212 | |
|
1213 | 0 | *cachep = nc; |
1214 | 0 | } |
1215 | | |
1216 | | void |
1217 | | isc_tlsctx_client_session_cache_attach( |
1218 | | isc_tlsctx_client_session_cache_t *source, |
1219 | 0 | isc_tlsctx_client_session_cache_t **targetp) { |
1220 | 0 | REQUIRE(VALID_TLSCTX_CLIENT_SESSION_CACHE(source)); |
1221 | 0 | REQUIRE(targetp != NULL && *targetp == NULL); |
1222 | |
|
1223 | 0 | isc_refcount_increment(&source->references); |
1224 | |
|
1225 | 0 | *targetp = source; |
1226 | 0 | } |
1227 | | |
1228 | | static void |
1229 | | client_cache_entry_delete(isc_tlsctx_client_session_cache_t *restrict cache, |
1230 | 0 | client_session_cache_entry_t *restrict entry) { |
1231 | 0 | client_session_cache_bucket_t *restrict bucket = entry->bucket; |
1232 | | |
1233 | | /* Unlink and free the cache entry */ |
1234 | 0 | ISC_LIST_UNLINK(bucket->entries, entry, bucket_link); |
1235 | 0 | ISC_LIST_UNLINK(cache->lru_entries, entry, cache_link); |
1236 | 0 | cache->nentries--; |
1237 | 0 | (void)SSL_SESSION_free(entry->session); |
1238 | 0 | isc_mem_put(cache->mctx, entry, sizeof(*entry)); |
1239 | | |
1240 | | /* The bucket is empty - let's remove it */ |
1241 | 0 | if (ISC_LIST_EMPTY(bucket->entries)) { |
1242 | 0 | RUNTIME_CHECK(isc_ht_delete(cache->buckets, |
1243 | 0 | (const uint8_t *)bucket->bucket_key, |
1244 | 0 | bucket->bucket_key_len) == |
1245 | 0 | ISC_R_SUCCESS); |
1246 | |
|
1247 | 0 | isc_mem_free(cache->mctx, bucket->bucket_key); |
1248 | 0 | isc_mem_put(cache->mctx, bucket, sizeof(*bucket)); |
1249 | 0 | } |
1250 | 0 | } |
1251 | | |
1252 | | void |
1253 | | isc_tlsctx_client_session_cache_detach( |
1254 | 0 | isc_tlsctx_client_session_cache_t **cachep) { |
1255 | 0 | isc_tlsctx_client_session_cache_t *cache = NULL; |
1256 | |
|
1257 | 0 | REQUIRE(cachep != NULL); |
1258 | |
|
1259 | 0 | cache = *cachep; |
1260 | 0 | *cachep = NULL; |
1261 | |
|
1262 | 0 | REQUIRE(VALID_TLSCTX_CLIENT_SESSION_CACHE(cache)); |
1263 | |
|
1264 | 0 | if (isc_refcount_decrement(&cache->references) != 1) { |
1265 | 0 | return; |
1266 | 0 | } |
1267 | | |
1268 | 0 | cache->magic = 0; |
1269 | |
|
1270 | 0 | isc_refcount_destroy(&cache->references); |
1271 | |
|
1272 | 0 | ISC_LIST_FOREACH(cache->lru_entries, entry, cache_link) { |
1273 | 0 | client_cache_entry_delete(cache, entry); |
1274 | 0 | } |
1275 | |
|
1276 | 0 | RUNTIME_CHECK(isc_ht_count(cache->buckets) == 0); |
1277 | 0 | isc_ht_destroy(&cache->buckets); |
1278 | |
|
1279 | 0 | isc_mutex_destroy(&cache->lock); |
1280 | 0 | isc_tlsctx_free(&cache->ctx); |
1281 | 0 | isc_mem_putanddetach(&cache->mctx, cache, sizeof(*cache)); |
1282 | 0 | } |
1283 | | |
1284 | | void |
1285 | | isc_tlsctx_client_session_cache_keep(isc_tlsctx_client_session_cache_t *cache, |
1286 | 0 | char *remote_peer_name, isc_tls_t *tls) { |
1287 | 0 | size_t name_len; |
1288 | 0 | isc_result_t result; |
1289 | 0 | SSL_SESSION *sess; |
1290 | 0 | client_session_cache_bucket_t *restrict bucket = NULL; |
1291 | 0 | client_session_cache_entry_t *restrict entry = NULL; |
1292 | |
|
1293 | 0 | REQUIRE(VALID_TLSCTX_CLIENT_SESSION_CACHE(cache)); |
1294 | 0 | REQUIRE(remote_peer_name != NULL && *remote_peer_name != '\0'); |
1295 | 0 | REQUIRE(tls != NULL); |
1296 | |
|
1297 | 0 | sess = SSL_get1_session(tls); |
1298 | 0 | if (sess == NULL) { |
1299 | 0 | ERR_clear_error(); |
1300 | 0 | return; |
1301 | 0 | } else if (SSL_SESSION_is_resumable(sess) == 0) { |
1302 | 0 | SSL_SESSION_free(sess); |
1303 | 0 | return; |
1304 | 0 | } |
1305 | | |
1306 | 0 | SSL_set_session(tls, NULL); |
1307 | |
|
1308 | 0 | isc_mutex_lock(&cache->lock); |
1309 | |
|
1310 | 0 | name_len = strlen(remote_peer_name); |
1311 | 0 | result = isc_ht_find(cache->buckets, (const uint8_t *)remote_peer_name, |
1312 | 0 | name_len, (void **)&bucket); |
1313 | |
|
1314 | 0 | if (result != ISC_R_SUCCESS) { |
1315 | | /* Let's create a new bucket */ |
1316 | 0 | INSIST(bucket == NULL); |
1317 | 0 | bucket = isc_mem_get(cache->mctx, sizeof(*bucket)); |
1318 | 0 | *bucket = (client_session_cache_bucket_t){ |
1319 | 0 | .bucket_key = isc_mem_strdup(cache->mctx, |
1320 | 0 | remote_peer_name), |
1321 | 0 | .bucket_key_len = name_len |
1322 | 0 | }; |
1323 | 0 | ISC_LIST_INIT(bucket->entries); |
1324 | 0 | RUNTIME_CHECK(isc_ht_add(cache->buckets, |
1325 | 0 | (const uint8_t *)remote_peer_name, |
1326 | 0 | name_len, |
1327 | 0 | (void *)bucket) == ISC_R_SUCCESS); |
1328 | 0 | } |
1329 | | |
1330 | | /* Let's add a new cache entry to the new/found bucket */ |
1331 | 0 | entry = isc_mem_get(cache->mctx, sizeof(*entry)); |
1332 | 0 | *entry = (client_session_cache_entry_t){ .session = sess, |
1333 | 0 | .bucket = bucket }; |
1334 | 0 | ISC_LINK_INIT(entry, bucket_link); |
1335 | 0 | ISC_LINK_INIT(entry, cache_link); |
1336 | |
|
1337 | 0 | ISC_LIST_APPEND(bucket->entries, entry, bucket_link); |
1338 | |
|
1339 | 0 | ISC_LIST_APPEND(cache->lru_entries, entry, cache_link); |
1340 | 0 | cache->nentries++; |
1341 | |
|
1342 | 0 | if (cache->nentries > cache->max_entries) { |
1343 | | /* |
1344 | | * Cache overrun. We need to remove the oldest entry from the |
1345 | | * cache |
1346 | | */ |
1347 | 0 | client_session_cache_entry_t *restrict oldest; |
1348 | 0 | INSIST((cache->nentries - 1) == cache->max_entries); |
1349 | |
|
1350 | 0 | oldest = ISC_LIST_HEAD(cache->lru_entries); |
1351 | 0 | client_cache_entry_delete(cache, oldest); |
1352 | 0 | } |
1353 | |
|
1354 | 0 | isc_mutex_unlock(&cache->lock); |
1355 | 0 | } |
1356 | | |
1357 | | void |
1358 | | isc_tlsctx_client_session_cache_reuse(isc_tlsctx_client_session_cache_t *cache, |
1359 | 0 | char *remote_peer_name, isc_tls_t *tls) { |
1360 | 0 | client_session_cache_bucket_t *restrict bucket = NULL; |
1361 | 0 | client_session_cache_entry_t *restrict entry; |
1362 | 0 | size_t name_len; |
1363 | 0 | isc_result_t result; |
1364 | |
|
1365 | 0 | REQUIRE(VALID_TLSCTX_CLIENT_SESSION_CACHE(cache)); |
1366 | 0 | REQUIRE(remote_peer_name != NULL && *remote_peer_name != '\0'); |
1367 | 0 | REQUIRE(tls != NULL); |
1368 | |
|
1369 | 0 | isc_mutex_lock(&cache->lock); |
1370 | | |
1371 | | /* Let's find the bucket */ |
1372 | 0 | name_len = strlen(remote_peer_name); |
1373 | 0 | result = isc_ht_find(cache->buckets, (const uint8_t *)remote_peer_name, |
1374 | 0 | name_len, (void **)&bucket); |
1375 | |
|
1376 | 0 | if (result != ISC_R_SUCCESS) { |
1377 | 0 | goto exit; |
1378 | 0 | } |
1379 | | |
1380 | 0 | INSIST(bucket != NULL); |
1381 | | |
1382 | | /* |
1383 | | * If the bucket has been found, let's use the newest session from |
1384 | | * the bucket, as it has the highest chance to be successfully |
1385 | | * resumed. |
1386 | | */ |
1387 | 0 | INSIST(!ISC_LIST_EMPTY(bucket->entries)); |
1388 | 0 | entry = ISC_LIST_TAIL(bucket->entries); |
1389 | 0 | RUNTIME_CHECK(SSL_set_session(tls, entry->session) == 1); |
1390 | 0 | client_cache_entry_delete(cache, entry); |
1391 | |
|
1392 | 0 | exit: |
1393 | 0 | isc_mutex_unlock(&cache->lock); |
1394 | 0 | } |
1395 | | |
1396 | | void |
1397 | | isc_tlsctx_client_session_cache_keep_sockaddr( |
1398 | | isc_tlsctx_client_session_cache_t *cache, isc_sockaddr_t *remote_peer, |
1399 | 0 | isc_tls_t *tls) { |
1400 | 0 | char peername[ISC_SOCKADDR_FORMATSIZE] = { 0 }; |
1401 | |
|
1402 | 0 | REQUIRE(remote_peer != NULL); |
1403 | |
|
1404 | 0 | isc_sockaddr_format(remote_peer, peername, sizeof(peername)); |
1405 | |
|
1406 | 0 | isc_tlsctx_client_session_cache_keep(cache, peername, tls); |
1407 | 0 | } |
1408 | | |
1409 | | void |
1410 | | isc_tlsctx_client_session_cache_reuse_sockaddr( |
1411 | | isc_tlsctx_client_session_cache_t *cache, isc_sockaddr_t *remote_peer, |
1412 | 0 | isc_tls_t *tls) { |
1413 | 0 | char peername[ISC_SOCKADDR_FORMATSIZE] = { 0 }; |
1414 | |
|
1415 | 0 | REQUIRE(remote_peer != NULL); |
1416 | |
|
1417 | 0 | isc_sockaddr_format(remote_peer, peername, sizeof(peername)); |
1418 | |
|
1419 | 0 | isc_tlsctx_client_session_cache_reuse(cache, peername, tls); |
1420 | 0 | } |
1421 | | |
1422 | | const isc_tlsctx_t * |
1423 | | isc_tlsctx_client_session_cache_getctx( |
1424 | 0 | isc_tlsctx_client_session_cache_t *cache) { |
1425 | 0 | REQUIRE(VALID_TLSCTX_CLIENT_SESSION_CACHE(cache)); |
1426 | 0 | return cache->ctx; |
1427 | 0 | } |
1428 | | |
1429 | | void |
1430 | 0 | isc_tlsctx_set_random_session_id_context(isc_tlsctx_t *ctx) { |
1431 | 0 | uint8_t session_id_ctx[SSL_MAX_SID_CTX_LENGTH] = { 0 }; |
1432 | 0 | const size_t len = ISC_MIN(20, sizeof(session_id_ctx)); |
1433 | |
|
1434 | 0 | REQUIRE(ctx != NULL); |
1435 | |
|
1436 | 0 | RUNTIME_CHECK(RAND_bytes(session_id_ctx, len) == 1); |
1437 | |
|
1438 | 0 | RUNTIME_CHECK( |
1439 | 0 | SSL_CTX_set_session_id_context(ctx, session_id_ctx, len) == 1); |
1440 | 0 | } |
1441 | | |
1442 | | bool |
1443 | 0 | isc_tls_valid_sni_hostname(const char *hostname) { |
1444 | 0 | struct sockaddr_in sa_v4 = { 0 }; |
1445 | 0 | struct sockaddr_in6 sa_v6 = { 0 }; |
1446 | 0 | int ret = 0; |
1447 | |
|
1448 | 0 | if (hostname == NULL) { |
1449 | 0 | return false; |
1450 | 0 | } |
1451 | | |
1452 | 0 | ret = inet_pton(AF_INET, hostname, &sa_v4.sin_addr); |
1453 | 0 | if (ret == 1) { |
1454 | 0 | return false; |
1455 | 0 | } |
1456 | | |
1457 | 0 | ret = inet_pton(AF_INET6, hostname, &sa_v6.sin6_addr); |
1458 | 0 | if (ret == 1) { |
1459 | 0 | return false; |
1460 | 0 | } |
1461 | | |
1462 | 0 | return true; |
1463 | 0 | } |