/src/bind9/lib/isc/crypto/ossl3.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) Internet Systems Consortium, Inc. ("ISC") |
3 | | * |
4 | | * SPDX-License-Identifier: MPL-2.0 |
5 | | * |
6 | | * This Source Code Form is subject to the terms of the Mozilla Public |
7 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
8 | | * file, you can obtain one at https://mozilla.org/MPL/2.0/. |
9 | | * |
10 | | * See the COPYRIGHT file distributed with this work for additional |
11 | | * information regarding copyright ownership. |
12 | | */ |
13 | | |
14 | | #include <stdbool.h> |
15 | | #include <stdint.h> |
16 | | |
17 | | #include <openssl/core_names.h> |
18 | | #include <openssl/crypto.h> |
19 | | #include <openssl/err.h> |
20 | | #include <openssl/evp.h> |
21 | | #include <openssl/provider.h> |
22 | | #include <openssl/rand.h> |
23 | | #include <openssl/ssl.h> |
24 | | |
25 | | #include <isc/buffer.h> |
26 | | #include <isc/crypto.h> |
27 | | #include <isc/hmac.h> |
28 | | #include <isc/log.h> |
29 | | #include <isc/magic.h> |
30 | | #include <isc/md.h> |
31 | | #include <isc/mem.h> |
32 | | #include <isc/ossl_wrap.h> |
33 | | #include <isc/region.h> |
34 | | #include <isc/safe.h> |
35 | | #include <isc/util.h> |
36 | | |
37 | | struct isc_hmac_key { |
38 | | uint32_t magic; |
39 | | uint32_t len; |
40 | | isc_mem_t *mctx; |
41 | | const OSSL_PARAM *params; |
42 | | uint8_t secret[]; |
43 | | }; |
44 | | |
45 | | constexpr uint32_t hmac_key_magic = ISC_MAGIC('H', 'M', 'A', 'C'); |
46 | | |
47 | | static OSSL_PROVIDER *base = NULL, *fips = NULL; |
48 | | |
49 | | static EVP_MAC *evp_hmac = NULL; |
50 | | |
51 | | static OSSL_PARAM md_to_hmac_params[ISC_MD_MAX][2] = { |
52 | | [ISC_MD_UNKNOWN] = { OSSL_PARAM_END }, |
53 | | [ISC_MD_MD5] = { |
54 | | OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("MD5"), sizeof("MD5") - 1), |
55 | | OSSL_PARAM_END, |
56 | | }, |
57 | | [ISC_MD_SHA1] = { |
58 | | OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA1"), sizeof("SHA1") - 1), |
59 | | OSSL_PARAM_END, |
60 | | }, |
61 | | [ISC_MD_SHA224] = { |
62 | | OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA2-224"), sizeof("SHA2-224") - 1), |
63 | | OSSL_PARAM_END, |
64 | | }, |
65 | | [ISC_MD_SHA256] = { |
66 | | OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA2-256"), sizeof("SHA2-256") - 1), |
67 | | OSSL_PARAM_END, |
68 | | }, |
69 | | [ISC_MD_SHA384] = { |
70 | | OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA2-384"), sizeof("SHA2-384") - 1), |
71 | | OSSL_PARAM_END, |
72 | | }, |
73 | | [ISC_MD_SHA512] = { |
74 | | OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA2-512"), sizeof("SHA2-512") - 1), |
75 | | OSSL_PARAM_END, |
76 | | }, |
77 | | }; |
78 | | |
79 | | #define md_register_algorithm(alg) \ |
80 | 132 | { \ |
81 | 132 | REQUIRE(isc__crypto_md[ISC_MD_##alg] == NULL); \ |
82 | 132 | isc__crypto_md[ISC_MD_##alg] = EVP_MD_fetch(NULL, #alg, NULL); \ |
83 | 132 | if (isc__crypto_md[ISC_MD_##alg] == NULL) { \ |
84 | 0 | ERR_clear_error(); \ |
85 | 0 | } \ |
86 | 132 | } |
87 | | |
88 | | static isc_result_t |
89 | 22 | register_algorithms(void) { |
90 | 22 | if (!isc_crypto_fips_mode()) { |
91 | 22 | md_register_algorithm(MD5); |
92 | 22 | } |
93 | | |
94 | 22 | md_register_algorithm(SHA1); |
95 | 22 | md_register_algorithm(SHA224); |
96 | 22 | md_register_algorithm(SHA256); |
97 | 22 | md_register_algorithm(SHA384); |
98 | 22 | md_register_algorithm(SHA512); |
99 | | |
100 | | /* We _must_ have HMAC */ |
101 | 22 | evp_hmac = EVP_MAC_fetch(NULL, "HMAC", NULL); |
102 | 22 | if (evp_hmac == NULL) { |
103 | 0 | ERR_clear_error(); |
104 | 0 | FATAL_ERROR("OpenSSL failed to find an HMAC implementation. " |
105 | 0 | "Please make sure the default provider has an " |
106 | 0 | "EVP_MAC-HMAC implementation"); |
107 | 0 | } |
108 | | |
109 | 22 | return ISC_R_SUCCESS; |
110 | 22 | } |
111 | | |
112 | | static void |
113 | 0 | unregister_algorithms(void) { |
114 | 0 | for (size_t i = 0; i < ISC_MD_MAX; i++) { |
115 | 0 | if (isc__crypto_md[i] != NULL) { |
116 | 0 | EVP_MD_free(isc__crypto_md[i]); |
117 | 0 | isc__crypto_md[i] = NULL; |
118 | 0 | } |
119 | 0 | } |
120 | |
|
121 | 0 | INSIST(evp_hmac != NULL); |
122 | 0 | EVP_MAC_free(evp_hmac); |
123 | 0 | evp_hmac = NULL; |
124 | 0 | } |
125 | | |
126 | | #undef md_register_algorithm |
127 | | |
128 | | /* |
129 | | * HMAC |
130 | | */ |
131 | | |
132 | | /* |
133 | | * Do not call EVP_Q_mac or HMAC (since it calls EVP_Q_mac internally) |
134 | | * |
135 | | * Each invocation of the EVP_Q_mac function causes an explicit fetch. |
136 | | */ |
137 | | isc_result_t |
138 | | isc_hmac(isc_md_type_t type, const void *key, const size_t keylen, |
139 | | const unsigned char *buf, const size_t len, unsigned char *digest, |
140 | 132 | unsigned int *digestlen) { |
141 | 132 | EVP_MAC_CTX *ctx; |
142 | 132 | size_t maclen; |
143 | | |
144 | 132 | REQUIRE(type < ISC_MD_MAX); |
145 | | |
146 | 132 | if (isc__crypto_md[type] == NULL) { |
147 | 0 | return ISC_R_NOTIMPLEMENTED; |
148 | 0 | } |
149 | | |
150 | 132 | ctx = EVP_MAC_CTX_new(evp_hmac); |
151 | 132 | RUNTIME_CHECK(ctx != NULL); |
152 | | |
153 | 132 | if (EVP_MAC_init(ctx, key, keylen, md_to_hmac_params[type]) != 1) { |
154 | 0 | goto fail; |
155 | 0 | } |
156 | | |
157 | 132 | if (EVP_MAC_update(ctx, buf, len) != 1) { |
158 | 0 | goto fail; |
159 | 0 | } |
160 | | |
161 | 132 | maclen = *digestlen; |
162 | 132 | if (EVP_MAC_final(ctx, digest, &maclen, maclen) != 1) { |
163 | 0 | goto fail; |
164 | 0 | } |
165 | | |
166 | 132 | *digestlen = maclen; |
167 | | |
168 | 132 | EVP_MAC_CTX_free(ctx); |
169 | 132 | return ISC_R_SUCCESS; |
170 | | |
171 | 0 | fail: |
172 | 0 | ERR_clear_error(); |
173 | 0 | EVP_MAC_CTX_free(ctx); |
174 | 0 | return ISC_R_CRYPTOFAILURE; |
175 | 132 | } |
176 | | |
177 | | /* |
178 | | * You do not need to process the key to fit the block size. |
179 | | * |
180 | | * https://github.com/openssl/openssl/blob/925e4fba1098036e8f8d22652cff6f64c5c7d571/crypto/hmac/hmac.c#L61-L80 |
181 | | */ |
182 | | isc_result_t |
183 | | isc_hmac_key_create(isc_md_type_t type, const void *secret, const size_t len, |
184 | 2 | isc_mem_t *mctx, isc_hmac_key_t **keyp) { |
185 | 2 | isc_hmac_key_t *key; |
186 | 2 | uint8_t digest[ISC_MAX_MD_SIZE]; |
187 | 2 | unsigned int digest_len = sizeof(digest); |
188 | 2 | size_t key_len; |
189 | | |
190 | 2 | REQUIRE(keyp != NULL && *keyp == NULL); |
191 | 2 | REQUIRE(type < ISC_MD_MAX); |
192 | | |
193 | 2 | if (isc__crypto_md[type] == NULL) { |
194 | 0 | return ISC_R_NOTIMPLEMENTED; |
195 | 0 | } |
196 | | |
197 | 2 | if (len > (size_t)EVP_MD_block_size(isc__crypto_md[type])) { |
198 | 0 | RETERR(isc_md(type, secret, len, digest, &digest_len)); |
199 | 0 | secret = digest; |
200 | 0 | key_len = digest_len; |
201 | 2 | } else { |
202 | 2 | key_len = len; |
203 | 2 | } |
204 | | |
205 | 2 | key = isc_mem_get(mctx, STRUCT_FLEX_SIZE(key, secret, key_len)); |
206 | 2 | *key = (isc_hmac_key_t){ |
207 | 2 | .magic = hmac_key_magic, |
208 | 2 | .len = key_len, |
209 | 2 | .params = md_to_hmac_params[type], |
210 | 2 | }; |
211 | 2 | memmove(key->secret, secret, key_len); |
212 | 2 | isc_mem_attach(mctx, &key->mctx); |
213 | | |
214 | 2 | *keyp = key; |
215 | | |
216 | 2 | return ISC_R_SUCCESS; |
217 | 2 | } |
218 | | |
219 | | void |
220 | 0 | isc_hmac_key_destroy(isc_hmac_key_t **keyp) { |
221 | 0 | isc_hmac_key_t *key; |
222 | |
|
223 | 0 | REQUIRE(keyp != NULL && *keyp != NULL); |
224 | 0 | REQUIRE((*keyp)->magic == hmac_key_magic); |
225 | |
|
226 | 0 | key = *keyp; |
227 | 0 | *keyp = NULL; |
228 | |
|
229 | 0 | key->magic = 0x00; |
230 | |
|
231 | 0 | isc_safe_memwipe(key->secret, key->len); |
232 | 0 | isc_mem_putanddetach(&key->mctx, key, |
233 | 0 | STRUCT_FLEX_SIZE(key, secret, key->len)); |
234 | 0 | } |
235 | | |
236 | | isc_region_t |
237 | 4 | isc_hmac_key_expose(isc_hmac_key_t *key) { |
238 | 4 | REQUIRE(key != NULL && key->magic == hmac_key_magic); |
239 | | |
240 | 4 | return (isc_region_t){ .base = key->secret, .length = key->len }; |
241 | 4 | } |
242 | | |
243 | | bool |
244 | 0 | isc_hmac_key_equal(isc_hmac_key_t *a, isc_hmac_key_t *b) { |
245 | 0 | REQUIRE(a != NULL && a->magic == hmac_key_magic); |
246 | 0 | REQUIRE(b != NULL && b->magic == hmac_key_magic); |
247 | |
|
248 | 0 | if (a->params != b->params) { |
249 | 0 | return false; |
250 | 0 | } |
251 | | |
252 | 0 | if (a->len != b->len) { |
253 | 0 | return false; |
254 | 0 | } |
255 | | |
256 | 0 | return isc_safe_memequal(a->secret, b->secret, a->len); |
257 | 0 | } |
258 | | |
259 | | isc_hmac_t * |
260 | 46 | isc_hmac_new(void) { |
261 | 46 | EVP_MAC_CTX *ctx = EVP_MAC_CTX_new(evp_hmac); |
262 | 46 | RUNTIME_CHECK(ctx != NULL); |
263 | 46 | return ctx; |
264 | 46 | } |
265 | | |
266 | | void |
267 | 46 | isc_hmac_free(isc_hmac_t *hmac) { |
268 | 46 | EVP_MAC_CTX_free(hmac); |
269 | 46 | } |
270 | | |
271 | | isc_result_t |
272 | 46 | isc_hmac_init(isc_hmac_t *hmac, isc_hmac_key_t *key) { |
273 | 46 | REQUIRE(key != NULL && key->magic == hmac_key_magic); |
274 | 46 | REQUIRE(hmac != NULL); |
275 | | |
276 | 46 | if (EVP_MAC_init(hmac, key->secret, key->len, key->params) != 1) { |
277 | 0 | ERR_clear_error(); |
278 | 0 | return ISC_R_CRYPTOFAILURE; |
279 | 0 | } |
280 | | |
281 | 46 | return ISC_R_SUCCESS; |
282 | 46 | } |
283 | | |
284 | | isc_result_t |
285 | 311 | isc_hmac_update(isc_hmac_t *hmac, const unsigned char *buf, const size_t len) { |
286 | 311 | REQUIRE(hmac != NULL); |
287 | | |
288 | 311 | if (buf == NULL || len == 0) { |
289 | 0 | return ISC_R_SUCCESS; |
290 | 0 | } |
291 | | |
292 | 311 | if (EVP_MAC_update(hmac, buf, len) != 1) { |
293 | 0 | ERR_clear_error(); |
294 | 0 | return ISC_R_CRYPTOFAILURE; |
295 | 0 | } |
296 | | |
297 | 311 | return ISC_R_SUCCESS; |
298 | 311 | } |
299 | | |
300 | | isc_result_t |
301 | 46 | isc_hmac_final(isc_hmac_t *hmac, isc_buffer_t *out) { |
302 | 46 | size_t len; |
303 | | |
304 | 46 | REQUIRE(hmac != NULL); |
305 | | |
306 | 46 | len = isc_buffer_availablelength(out); |
307 | 46 | if (len < EVP_MAC_CTX_get_mac_size(hmac)) { |
308 | 0 | return ISC_R_NOSPACE; |
309 | 0 | } |
310 | | |
311 | 46 | if (EVP_MAC_final(hmac, isc_buffer_used(out), &len, len) != 1) { |
312 | 0 | ERR_clear_error(); |
313 | 0 | return ISC_R_CRYPTOFAILURE; |
314 | 0 | } |
315 | | |
316 | 46 | isc_buffer_add(out, len); |
317 | | |
318 | 46 | return ISC_R_SUCCESS; |
319 | 46 | } |
320 | | |
321 | | bool |
322 | 22 | isc_crypto_fips_mode(void) { |
323 | 22 | return EVP_default_properties_is_fips_enabled(NULL) != 0; |
324 | 22 | } |
325 | | |
326 | | isc_result_t |
327 | 0 | isc_crypto_fips_enable(void) { |
328 | 0 | if (isc_crypto_fips_mode()) { |
329 | 0 | return ISC_R_SUCCESS; |
330 | 0 | } |
331 | | |
332 | 0 | INSIST(fips == NULL); |
333 | 0 | fips = OSSL_PROVIDER_load(NULL, "fips"); |
334 | 0 | if (fips == NULL) { |
335 | 0 | return isc_ossl_wrap_logged_toresult( |
336 | 0 | ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO, |
337 | 0 | "OSSL_PROVIDER_load", ISC_R_CRYPTOFAILURE); |
338 | 0 | } |
339 | | |
340 | 0 | INSIST(base == NULL); |
341 | 0 | base = OSSL_PROVIDER_load(NULL, "base"); |
342 | 0 | if (base == NULL) { |
343 | 0 | OSSL_PROVIDER_unload(fips); |
344 | 0 | return isc_ossl_wrap_logged_toresult( |
345 | 0 | ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO, |
346 | 0 | "OSS_PROVIDER_load", ISC_R_CRYPTOFAILURE); |
347 | 0 | } |
348 | | |
349 | 0 | if (EVP_default_properties_enable_fips(NULL, 1) == 0) { |
350 | 0 | return isc_ossl_wrap_logged_toresult( |
351 | 0 | ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO, |
352 | 0 | "EVP_default_properties_enable_fips", |
353 | 0 | ISC_R_CRYPTOFAILURE); |
354 | 0 | } |
355 | | |
356 | 0 | unregister_algorithms(); |
357 | 0 | register_algorithms(); |
358 | |
|
359 | 0 | return ISC_R_SUCCESS; |
360 | 0 | } |
361 | | |
362 | | /* |
363 | | * OPENSSL_cleanup() in OpenSSL 4 doesn't free the memory, which is not |
364 | | * compatible with BIND 9's memory leak detection code, that is why the memory |
365 | | * tracking has been disabled in this module, and this function is a no-op. |
366 | | * This can be cleaned up once OpenSSL 1.1.x support is removed. |
367 | | * |
368 | | * See https://github.com/openssl/openssl/pull/29721 |
369 | | */ |
370 | | void |
371 | 0 | isc__crypto_setdestroycheck(bool check) { |
372 | 0 | UNUSED(check); |
373 | 0 | } |
374 | | |
375 | | void |
376 | 22 | isc__crypto_initialize(void) { |
377 | | /* |
378 | | * We call OPENSSL_cleanup() manually, in a correct order, thus disable |
379 | | * the automatic atexit() handler. |
380 | | */ |
381 | 22 | uint64_t opts = OPENSSL_INIT_LOAD_CONFIG | OPENSSL_INIT_NO_ATEXIT; |
382 | | |
383 | 22 | RUNTIME_CHECK(OPENSSL_init_ssl(opts, NULL) == 1); |
384 | | |
385 | 22 | register_algorithms(); |
386 | | |
387 | | #if defined(ENABLE_FIPS_MODE) |
388 | | if (isc_crypto_fips_enable() != ISC_R_SUCCESS) { |
389 | | ERR_clear_error(); |
390 | | FATAL_ERROR("Failed to toggle FIPS mode but is " |
391 | | "required for this build"); |
392 | | } |
393 | | #endif |
394 | | |
395 | | /* Protect ourselves against unseeded PRNG */ |
396 | 22 | if (RAND_status() != 1) { |
397 | 0 | isc_ossl_wrap_logged_toresult( |
398 | 0 | ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO, |
399 | 0 | "RAND_status", ISC_R_CRYPTOFAILURE); |
400 | 0 | FATAL_ERROR("OpenSSL pseudorandom number generator " |
401 | 0 | "cannot be initialized (see the `PRNG not " |
402 | 0 | "seeded' message in the OpenSSL FAQ)"); |
403 | 0 | } |
404 | 22 | } |
405 | | |
406 | | void |
407 | 0 | isc__crypto_shutdown(void) { |
408 | 0 | unregister_algorithms(); |
409 | |
|
410 | 0 | if (base != NULL) { |
411 | 0 | OSSL_PROVIDER_unload(base); |
412 | 0 | } |
413 | |
|
414 | 0 | if (fips != NULL) { |
415 | 0 | OSSL_PROVIDER_unload(fips); |
416 | 0 | } |
417 | |
|
418 | 0 | OPENSSL_cleanup(); |
419 | 0 | } |