|
68 | 68 | #include "str.h" |
69 | 69 | #include "iowrap.h" |
70 | 70 |
|
| 71 | +#ifdef CL_THREAD_SAFE |
| 72 | +#include <pthread.h> |
| 73 | +#endif |
| 74 | + |
71 | 75 | #if defined(_WIN32) |
72 | 76 | char *strptime(const char *buf, const char *fmt, struct tm *tm); |
73 | 77 | #endif |
@@ -162,6 +166,95 @@ void cl_cleanup_crypto(void) |
162 | 166 | return; |
163 | 167 | } |
164 | 168 |
|
| 169 | +#if OPENSSL_VERSION_MAJOR >= 3 |
| 170 | +/* |
| 171 | + * Digest handle cache. |
| 172 | + * |
| 173 | + * On OpenSSL 3, EVP_MD_fetch() into a freshly-allocated OSSL_LIB_CTX is |
| 174 | + * expensive: the empty context must bootstrap a provider and construct the |
| 175 | + * method from scratch (on the order of hundreds of microseconds). ClamAV |
| 176 | + * hashes md5/sha1/sha2-256 for essentially every scanned object, so fetching |
| 177 | + * per hash would dominate scan time while doing no actual hashing. |
| 178 | + * |
| 179 | + * Fetch each FIPS-bypass digest once into a single shared non-FIPS library |
| 180 | + * context and reuse it (a fetched EVP_MD is reference counted and safe to share |
| 181 | + * across threads). cli_get_md() returns a reference the caller owns and must |
| 182 | + * release with EVP_MD_free(). |
| 183 | + * |
| 184 | + * Only the bypass digests are cached. The FIPS-compliant path is fetched from |
| 185 | + * the default library context on every call, so it keeps honoring the current |
| 186 | + * default property query -- e.g. if the embedding process enables FIPS at |
| 187 | + * runtime, a non-bypass MD5/SHA1 request must start failing rather than return |
| 188 | + * a stale non-FIPS handle. That default-context fetch is already inexpensive |
| 189 | + * because OpenSSL caches provider methods per library context. |
| 190 | + */ |
| 191 | +#ifdef CL_THREAD_SAFE |
| 192 | +static pthread_mutex_t md_cache_mutex = PTHREAD_MUTEX_INITIALIZER; |
| 193 | +#endif |
| 194 | + |
| 195 | +/* Single shared non-FIPS library context backing all "-fips" (bypass) fetches. */ |
| 196 | +static OSSL_LIB_CTX *nonfips_libctx = NULL; |
| 197 | + |
| 198 | +static struct { |
| 199 | + const char *alg; /* string literal returned by to_openssl_alg(); stable */ |
| 200 | + EVP_MD *md; |
| 201 | +} md_cache[8]; |
| 202 | +static size_t md_cache_len = 0; |
| 203 | + |
| 204 | +static EVP_MD *cli_get_md(const char *alg, int fips_bypass) |
| 205 | +{ |
| 206 | + const char *ossl_alg = to_openssl_alg(alg); |
| 207 | + EVP_MD *result = NULL; |
| 208 | + size_t i; |
| 209 | + |
| 210 | + if (NULL == ossl_alg) { |
| 211 | + return NULL; |
| 212 | + } |
| 213 | + |
| 214 | + if (!fips_bypass) { |
| 215 | + /* FIPS-compliant path: fetch from the default library context every |
| 216 | + * time so the current default property query -- including any runtime |
| 217 | + * FIPS change -- is honored. */ |
| 218 | + return EVP_MD_fetch(NULL, ossl_alg, NULL); |
| 219 | + } |
| 220 | + |
| 221 | +#ifdef CL_THREAD_SAFE |
| 222 | + pthread_mutex_lock(&md_cache_mutex); |
| 223 | +#endif |
| 224 | + |
| 225 | + for (i = 0; i < md_cache_len; i++) { |
| 226 | + if (0 == strcmp(md_cache[i].alg, ossl_alg)) { |
| 227 | + result = md_cache[i].md; |
| 228 | + goto done; |
| 229 | + } |
| 230 | + } |
| 231 | + |
| 232 | + /* Miss: create the shared non-FIPS context on first use and fetch once. */ |
| 233 | + if (NULL == nonfips_libctx) { |
| 234 | + nonfips_libctx = OSSL_LIB_CTX_new(); |
| 235 | + if (NULL == nonfips_libctx) { |
| 236 | + cli_errmsg("cli_get_md: Failed to create OpenSSL library context\n"); |
| 237 | + goto done; |
| 238 | + } |
| 239 | + } |
| 240 | + result = EVP_MD_fetch(nonfips_libctx, ossl_alg, "-fips"); |
| 241 | + if (NULL != result && md_cache_len < sizeof(md_cache) / sizeof(md_cache[0])) { |
| 242 | + md_cache[md_cache_len].alg = ossl_alg; |
| 243 | + md_cache[md_cache_len].md = result; /* cache holds this reference */ |
| 244 | + md_cache_len++; |
| 245 | + } |
| 246 | + |
| 247 | +done: |
| 248 | + if (NULL != result) { |
| 249 | + EVP_MD_up_ref(result); /* give the caller its own reference to free */ |
| 250 | + } |
| 251 | +#ifdef CL_THREAD_SAFE |
| 252 | + pthread_mutex_unlock(&md_cache_mutex); |
| 253 | +#endif |
| 254 | + return result; |
| 255 | +} |
| 256 | +#endif /* OPENSSL_VERSION_MAJOR >= 3 */ |
| 257 | + |
165 | 258 | /** |
166 | 259 | * @brief Generate a hash of data. |
167 | 260 | * |
@@ -218,20 +311,7 @@ extern cl_error_t cl_hash_data_ex( |
218 | 311 | } |
219 | 312 |
|
220 | 313 | #if OPENSSL_VERSION_MAJOR >= 3 |
221 | | - if (flags & CL_HASH_FLAG_FIPS_BYPASS) { |
222 | | - /* Bypass FIPS restrictions the OpenSSL 3.0 way */ |
223 | | - ossl_ctx = OSSL_LIB_CTX_new(); |
224 | | - if (NULL == ossl_ctx) { |
225 | | - cli_errmsg("cl_hash_data_ex: Failed to create new OpenSSL library context\n"); |
226 | | - status = CL_EMEM; |
227 | | - goto done; |
228 | | - } |
229 | | - |
230 | | - md = EVP_MD_fetch(ossl_ctx, to_openssl_alg(alg), "-fips"); |
231 | | - } else { |
232 | | - /* Use FIPS compliant algorithms */ |
233 | | - md = EVP_MD_fetch(NULL, to_openssl_alg(alg), NULL); |
234 | | - } |
| 314 | + md = cli_get_md(alg, (flags & CL_HASH_FLAG_FIPS_BYPASS) ? 1 : 0); |
235 | 315 | #else |
236 | 316 | md = EVP_get_digestbyname(to_openssl_alg(alg)); |
237 | 317 | #endif |
@@ -369,20 +449,7 @@ extern cl_error_t cl_hash_init_ex( |
369 | 449 | } |
370 | 450 |
|
371 | 451 | #if OPENSSL_VERSION_MAJOR >= 3 |
372 | | - if (flags & CL_HASH_FLAG_FIPS_BYPASS) { |
373 | | - /* Bypass FIPS restrictions the OpenSSL 3.0 way */ |
374 | | - ossl_ctx = OSSL_LIB_CTX_new(); |
375 | | - if (NULL == ossl_ctx) { |
376 | | - cli_errmsg("cl_hash_data_ex: Failed to create new OpenSSL library context\n"); |
377 | | - status = CL_EMEM; |
378 | | - goto done; |
379 | | - } |
380 | | - |
381 | | - md = EVP_MD_fetch(ossl_ctx, to_openssl_alg(alg), "-fips"); |
382 | | - } else { |
383 | | - /* Use FIPS compliant algorithms */ |
384 | | - md = EVP_MD_fetch(NULL, to_openssl_alg(alg), NULL); |
385 | | - } |
| 452 | + md = cli_get_md(alg, (flags & CL_HASH_FLAG_FIPS_BYPASS) ? 1 : 0); |
386 | 453 | #else |
387 | 454 | md = EVP_get_digestbyname(to_openssl_alg(alg)); |
388 | 455 | #endif |
@@ -641,20 +708,7 @@ extern cl_error_t cl_hash_file_fd_ex( |
641 | 708 | } |
642 | 709 |
|
643 | 710 | #if OPENSSL_VERSION_MAJOR >= 3 |
644 | | - if (flags & CL_HASH_FLAG_FIPS_BYPASS) { |
645 | | - /* Bypass FIPS restrictions the OpenSSL 3.0 way */ |
646 | | - ossl_ctx = OSSL_LIB_CTX_new(); |
647 | | - if (NULL == ossl_ctx) { |
648 | | - cli_errmsg("cl_hash_data_ex: Failed to create new OpenSSL library context\n"); |
649 | | - status = CL_EMEM; |
650 | | - goto done; |
651 | | - } |
652 | | - |
653 | | - md = EVP_MD_fetch(ossl_ctx, to_openssl_alg(alg), "-fips"); |
654 | | - } else { |
655 | | - /* Use FIPS compliant algorithms */ |
656 | | - md = EVP_MD_fetch(NULL, to_openssl_alg(alg), NULL); |
657 | | - } |
| 711 | + md = cli_get_md(alg, (flags & CL_HASH_FLAG_FIPS_BYPASS) ? 1 : 0); |
658 | 712 | #else |
659 | 713 | md = EVP_get_digestbyname(to_openssl_alg(alg)); |
660 | 714 | #endif |
@@ -807,14 +861,7 @@ unsigned char *cl_hash_data(const char *alg, const void *buf, size_t len, unsign |
807 | 861 | #endif |
808 | 862 |
|
809 | 863 | #if OPENSSL_VERSION_MAJOR >= 3 |
810 | | - /* Bypass FIPS restrictions the OpenSSL 3.0 way */ |
811 | | - ossl_ctx = OSSL_LIB_CTX_new(); |
812 | | - if (NULL == ossl_ctx) { |
813 | | - cli_errmsg("cl_hash_data_ex: Failed to create new OpenSSL library context\n"); |
814 | | - return NULL; |
815 | | - } |
816 | | - |
817 | | - md = EVP_MD_fetch(ossl_ctx, to_openssl_alg(alg), "-fips"); |
| 864 | + md = cli_get_md(alg, 1); |
818 | 865 | #else |
819 | 866 | md = EVP_get_digestbyname(to_openssl_alg(alg)); |
820 | 867 | #endif |
@@ -948,14 +995,7 @@ unsigned char *cl_hash_file_fd(int fd, const char *alg, unsigned int *olen) |
948 | 995 | unsigned char *res; |
949 | 996 |
|
950 | 997 | #if OPENSSL_VERSION_MAJOR >= 3 |
951 | | - /* Bypass FIPS restrictions the OpenSSL 3.0 way */ |
952 | | - ossl_ctx = OSSL_LIB_CTX_new(); |
953 | | - if (NULL == ossl_ctx) { |
954 | | - cli_errmsg("cl_hash_data_ex: Failed to create new OpenSSL library context\n"); |
955 | | - return NULL; |
956 | | - } |
957 | | - |
958 | | - md = EVP_MD_fetch(ossl_ctx, to_openssl_alg(alg), "-fips"); |
| 998 | + md = cli_get_md(alg, 1); |
959 | 999 | #else |
960 | 1000 | md = EVP_get_digestbyname(to_openssl_alg(alg)); |
961 | 1001 | #endif |
@@ -1852,14 +1892,7 @@ void *cl_hash_init(const char *alg) |
1852 | 1892 | #endif |
1853 | 1893 |
|
1854 | 1894 | #if OPENSSL_VERSION_MAJOR >= 3 |
1855 | | - /* Bypass FIPS restrictions the OpenSSL 3.0 way */ |
1856 | | - ossl_ctx = OSSL_LIB_CTX_new(); |
1857 | | - if (NULL == ossl_ctx) { |
1858 | | - cli_errmsg("cl_hash_data_ex: Failed to create new OpenSSL library context\n"); |
1859 | | - return NULL; |
1860 | | - } |
1861 | | - |
1862 | | - md = EVP_MD_fetch(ossl_ctx, to_openssl_alg(alg), "-fips"); |
| 1895 | + md = cli_get_md(alg, 1); |
1863 | 1896 | #else |
1864 | 1897 | md = EVP_get_digestbyname(to_openssl_alg(alg)); |
1865 | 1898 | #endif |
|
0 commit comments