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