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