From c26a818ce4d1aa32d9eee714bae9ab6f7d37ea6f Mon Sep 17 00:00:00 2001 From: "Val S." Date: Sun, 5 Oct 2025 17:42:59 -0400 Subject: [PATCH 1/2] Freshclam: Download missing .sign files for up-to-date .cvd's (#1587) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the database directory has an up-to-date .cvd (not .cld) which lacks a .sign file, then Freshclam should try to download the .cvd.sign file. If no .sign file is available, it will debug-log it and will not complain loudly. Example output: ``` ❯ ./install/bin/freshclam ClamAV update process started at Fri Oct 3 17:20:04 2025 daily.cvd database is up-to-date (version: 27780, sigs: 2076928, f-level: 90, builder: tomjudge) Time: 0.2s, ETA: 0.0s [========================>] 8.87KiB/8.87KiB Downloaded missing CVD .sign file daily-27780.cvd.sign main.cvd database is up-to-date (version: 62, sigs: 6647427, f-level: 90, builder: sigmgr) Time: 0.1s, ETA: 0.0s [========================>] 8.87KiB/8.87KiB Downloaded missing CVD .sign file main-62.cvd.sign bytecode.cvd database is up-to-date (version: 339, sigs: 80, f-level: 90, builder: nrandolp) Time: 0.5s, ETA: 0.0s [========================>] 8.87KiB/8.87KiB Downloaded missing CVD .sign file bytecode-339.cvd.sign ``` --- libfreshclam/libfreshclam_internal.c | 69 ++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/libfreshclam/libfreshclam_internal.c b/libfreshclam/libfreshclam_internal.c index f1accb411..2873ff8b6 100644 --- a/libfreshclam/libfreshclam_internal.c +++ b/libfreshclam/libfreshclam_internal.c @@ -1695,7 +1695,7 @@ static fc_error_t downloadPatchAndApply( fc_error_t status = FC_EARG; char patch[DB_FILENAME_MAX]; - char patch_sign_file[DB_FILENAME_MAX + 5]; + char patch_sign_file[DB_FILENAME_MAX + 5 /* ".sign" */ + 1]; char olddir[PATH_MAX]; char *url = NULL; @@ -1735,6 +1735,12 @@ static fc_error_t downloadPatchAndApply( urlLen = strlen(server) + strlen("/") + strlen(patch); url = malloc(urlLen + 1); + if (NULL == url) { + logg(LOGG_ERROR, "downloadPatchAndApply: Can't allocate memory for URL\n"); + status = FC_EMEM; + goto done; + } + snprintf(url, urlLen + 1, "%s/%s", server, patch); if (FC_SUCCESS != (ret = downloadFile(url, patch, 1, logerr, 0, 0))) { @@ -1751,9 +1757,16 @@ static fc_error_t downloadPatchAndApply( * Download the patch sign file. */ snprintf(patch_sign_file, sizeof(patch_sign_file), "%s.sign", patch); + patch_sign_file[sizeof(patch_sign_file) - 1] = 0; sign_urlLen = strlen(server) + strlen("/") + strlen(patch_sign_file); sign_url = malloc(sign_urlLen + 1); + if (NULL == sign_url) { + logg(LOGG_ERROR, "downloadPatchAndApply: Can't allocate memory for sign URL\n"); + status = FC_EMEM; + goto done; + } + snprintf(sign_url, sign_urlLen + 1, "%s/%s", server, patch_sign_file); if (FC_SUCCESS != (ret = downloadFile(sign_url, patch_sign_file, 1, logerr, 1, 0))) { @@ -2395,9 +2408,57 @@ fc_error_t updatedb( goto done; } - if ((localVersion >= remoteVersion) && (NULL != localFilename)) { - *dbFilename = cli_safer_strdup(localFilename); - goto up_to_date; + if (NULL != localFilename) { + if (localVersion == remoteVersion) { + *dbFilename = cli_safer_strdup(localFilename); + + /* check if localFilename ends with ".cvd" (i.e., not ".cld") */ + if (NULL != strstr(localFilename, ".cvd")) { + /* CVD file detected, lets see if we have the .sign file. + Just in case one was published for the database we have and we missed it. */ + char cvd_sign_file[DB_FILENAME_MAX + 5 /* ".sign" */ + 1]; + snprintf(cvd_sign_file, sizeof(cvd_sign_file), "%s-%d.cvd.sign", database, localVersion); + cvd_sign_file[sizeof(cvd_sign_file) - 1] = 0; + + if (-1 == access(cvd_sign_file, R_OK)) { + /* CVD .sign file not found. We should try to download it. */ + char *sign_url = NULL; + size_t sign_urlLen = 0; + + sign_urlLen = strlen(server) + strlen("/") + strlen(cvd_sign_file); + sign_url = malloc(sign_urlLen + 1); + if (NULL == sign_url) { + logg(LOGG_ERROR, "updatedb: Can't allocate memory for sign URL\n"); + status = FC_EMEM; + goto done; + } + + snprintf(sign_url, sign_urlLen + 1, "%s/%s", server, cvd_sign_file); + + logg(LOGG_DEBUG, "Trying to download missing CVD .sign file %s\n", sign_url); + ret = downloadFile( + sign_url, + cvd_sign_file, + 1, + logerr, + 1, + 0); + if (FC_SUCCESS != ret) { + // Not a big deal if we can't get it, just debug-log it, and move on. + logg(LOGG_DEBUG, "No .sign file found for %s\n", localFilename); + } else { + logg(LOGG_INFO, "Downloaded missing CVD .sign file %s\n", cvd_sign_file); + } + + free(sign_url); + } + } + + goto up_to_date; + } else if (localVersion > remoteVersion) { + *dbFilename = cli_safer_strdup(localFilename); + goto up_to_date; + } } /* From 83fd7f14fb8098826108be124219400ff08f44f7 Mon Sep 17 00:00:00 2001 From: "Val S." Date: Sun, 5 Oct 2025 23:19:27 -0400 Subject: [PATCH 2/2] Fix issue using non-FIPS algorithsm in some FIPS environments (#1589) Despite using "-fips" for `EVP_MD_fetch()` with OpenSSL 3, we are seeing this error in some FIPS-enabled environments: LibClamAV Error: cli_scan_fmap: Error initializing md5 hash context The fix seems to be to create a new OpenSSL context rather than passing NULL for the default context. See: https://docs.openssl.org/3.0/man7/fips_module/#programmatically-loading-the-fips-module-nondefault-library-context Special thanks to Tom Judge for identifying this fix. CLAM-2879 --- libclamav/crypto.c | 91 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 12 deletions(-) diff --git a/libclamav/crypto.c b/libclamav/crypto.c index 81e828c7a..8b398e402 100644 --- a/libclamav/crypto.c +++ b/libclamav/crypto.c @@ -196,7 +196,8 @@ extern cl_error_t cl_hash_data_ex( EVP_MD_CTX *ctx = NULL; #if OPENSSL_VERSION_MAJOR >= 3 - EVP_MD *md = NULL; + OSSL_LIB_CTX *ossl_ctx = NULL; + EVP_MD *md = NULL; #else const EVP_MD *md = NULL; #endif @@ -219,7 +220,14 @@ extern cl_error_t cl_hash_data_ex( #if OPENSSL_VERSION_MAJOR >= 3 if (flags & CL_HASH_FLAG_FIPS_BYPASS) { /* Bypass FIPS restrictions the OpenSSL 3.0 way */ - md = EVP_MD_fetch(NULL, to_openssl_alg(alg), "-fips"); + ossl_ctx = OSSL_LIB_CTX_new(); + if (NULL == ossl_ctx) { + cli_errmsg("cl_hash_data_ex: Failed to create new OpenSSL library context\n"); + status = CL_EMEM; + goto done; + } + + md = EVP_MD_fetch(ossl_ctx, to_openssl_alg(alg), "-fips"); } else { /* Use FIPS compliant algorithms */ md = EVP_MD_fetch(NULL, to_openssl_alg(alg), NULL); @@ -322,6 +330,9 @@ done: if (NULL != md) { EVP_MD_free(md); } + if (NULL != ossl_ctx) { + OSSL_LIB_CTX_free(ossl_ctx); + } #endif return status; } @@ -345,7 +356,8 @@ extern cl_error_t cl_hash_init_ex( EVP_MD_CTX *ctx = NULL; #if OPENSSL_VERSION_MAJOR >= 3 - EVP_MD *md = NULL; + OSSL_LIB_CTX *ossl_ctx = NULL; + EVP_MD *md = NULL; #else const EVP_MD *md = NULL; #endif @@ -359,7 +371,14 @@ extern cl_error_t cl_hash_init_ex( #if OPENSSL_VERSION_MAJOR >= 3 if (flags & CL_HASH_FLAG_FIPS_BYPASS) { /* Bypass FIPS restrictions the OpenSSL 3.0 way */ - md = EVP_MD_fetch(NULL, to_openssl_alg(alg), "-fips"); + ossl_ctx = OSSL_LIB_CTX_new(); + if (NULL == ossl_ctx) { + cli_errmsg("cl_hash_data_ex: Failed to create new OpenSSL library context\n"); + status = CL_EMEM; + goto done; + } + + md = EVP_MD_fetch(ossl_ctx, to_openssl_alg(alg), "-fips"); } else { /* Use FIPS compliant algorithms */ md = EVP_MD_fetch(NULL, to_openssl_alg(alg), NULL); @@ -406,6 +425,9 @@ done: if (NULL != md) { EVP_MD_free(md); } + if (NULL != ossl_ctx) { + OSSL_LIB_CTX_free(ossl_ctx); + } #endif return status; } @@ -570,7 +592,8 @@ extern cl_error_t cl_hash_file_fd_ex( EVP_MD_CTX *ctx = NULL; #if OPENSSL_VERSION_MAJOR >= 3 - EVP_MD *md = NULL; + OSSL_LIB_CTX *ossl_ctx = NULL; + EVP_MD *md = NULL; #else const EVP_MD *md = NULL; #endif @@ -620,7 +643,14 @@ extern cl_error_t cl_hash_file_fd_ex( #if OPENSSL_VERSION_MAJOR >= 3 if (flags & CL_HASH_FLAG_FIPS_BYPASS) { /* Bypass FIPS restrictions the OpenSSL 3.0 way */ - md = EVP_MD_fetch(NULL, to_openssl_alg(alg), "-fips"); + ossl_ctx = OSSL_LIB_CTX_new(); + if (NULL == ossl_ctx) { + cli_errmsg("cl_hash_data_ex: Failed to create new OpenSSL library context\n"); + status = CL_EMEM; + goto done; + } + + md = EVP_MD_fetch(ossl_ctx, to_openssl_alg(alg), "-fips"); } else { /* Use FIPS compliant algorithms */ md = EVP_MD_fetch(NULL, to_openssl_alg(alg), NULL); @@ -750,6 +780,9 @@ done: if (NULL != md) { EVP_MD_free(md); } + if (NULL != ossl_ctx) { + OSSL_LIB_CTX_free(ossl_ctx); + } #endif return status; } @@ -761,7 +794,8 @@ unsigned char *cl_hash_data(const char *alg, const void *buf, size_t len, unsign size_t mdsz; #if OPENSSL_VERSION_MAJOR >= 3 - EVP_MD *md = NULL; + OSSL_LIB_CTX *ossl_ctx = NULL; + EVP_MD *md = NULL; #else const EVP_MD *md = NULL; #endif @@ -774,7 +808,13 @@ unsigned char *cl_hash_data(const char *alg, const void *buf, size_t len, unsign #if OPENSSL_VERSION_MAJOR >= 3 /* Bypass FIPS restrictions the OpenSSL 3.0 way */ - md = EVP_MD_fetch(NULL, to_openssl_alg(alg), "-fips"); + ossl_ctx = OSSL_LIB_CTX_new(); + if (NULL == ossl_ctx) { + cli_errmsg("cl_hash_data_ex: Failed to create new OpenSSL library context\n"); + return NULL; + } + + md = EVP_MD_fetch(ossl_ctx, to_openssl_alg(alg), "-fips"); #else md = EVP_get_digestbyname(to_openssl_alg(alg)); #endif @@ -787,6 +827,7 @@ unsigned char *cl_hash_data(const char *alg, const void *buf, size_t len, unsign if (!(ret)) { #if OPENSSL_VERSION_MAJOR >= 3 EVP_MD_free(md); + OSSL_LIB_CTX_free(ossl_ctx); #endif return NULL; } @@ -798,6 +839,7 @@ unsigned char *cl_hash_data(const char *alg, const void *buf, size_t len, unsign #if OPENSSL_VERSION_MAJOR >= 3 EVP_MD_free(md); + OSSL_LIB_CTX_free(ossl_ctx); #endif return NULL; } @@ -818,6 +860,7 @@ unsigned char *cl_hash_data(const char *alg, const void *buf, size_t len, unsign #if OPENSSL_VERSION_MAJOR >= 3 EVP_MD_free(md); + OSSL_LIB_CTX_free(ossl_ctx); #endif EVP_MD_CTX_destroy(ctx); return NULL; @@ -837,6 +880,7 @@ unsigned char *cl_hash_data(const char *alg, const void *buf, size_t len, unsign #if OPENSSL_VERSION_MAJOR >= 3 EVP_MD_free(md); + OSSL_LIB_CTX_free(ossl_ctx); #endif EVP_MD_CTX_destroy(ctx); return NULL; @@ -853,6 +897,7 @@ unsigned char *cl_hash_data(const char *alg, const void *buf, size_t len, unsign #if OPENSSL_VERSION_MAJOR >= 3 EVP_MD_free(md); + OSSL_LIB_CTX_free(ossl_ctx); #endif EVP_MD_CTX_destroy(ctx); return NULL; @@ -871,6 +916,7 @@ unsigned char *cl_hash_data(const char *alg, const void *buf, size_t len, unsign #if OPENSSL_VERSION_MAJOR >= 3 EVP_MD_free(md); + OSSL_LIB_CTX_free(ossl_ctx); #endif EVP_MD_CTX_destroy(ctx); return NULL; @@ -878,6 +924,7 @@ unsigned char *cl_hash_data(const char *alg, const void *buf, size_t len, unsign #if OPENSSL_VERSION_MAJOR >= 3 EVP_MD_free(md); + OSSL_LIB_CTX_free(ossl_ctx); #endif EVP_MD_CTX_destroy(ctx); @@ -892,7 +939,8 @@ unsigned char *cl_hash_file_fd(int fd, const char *alg, unsigned int *olen) EVP_MD_CTX *ctx; #if OPENSSL_VERSION_MAJOR >= 3 - EVP_MD *md = NULL; + OSSL_LIB_CTX *ossl_ctx = NULL; + EVP_MD *md = NULL; #else const EVP_MD *md = NULL; #endif @@ -901,7 +949,13 @@ unsigned char *cl_hash_file_fd(int fd, const char *alg, unsigned int *olen) #if OPENSSL_VERSION_MAJOR >= 3 /* Bypass FIPS restrictions the OpenSSL 3.0 way */ - md = EVP_MD_fetch(NULL, to_openssl_alg(alg), "-fips"); + ossl_ctx = OSSL_LIB_CTX_new(); + if (NULL == ossl_ctx) { + cli_errmsg("cl_hash_data_ex: Failed to create new OpenSSL library context\n"); + return NULL; + } + + md = EVP_MD_fetch(ossl_ctx, to_openssl_alg(alg), "-fips"); #else md = EVP_get_digestbyname(to_openssl_alg(alg)); #endif @@ -912,6 +966,7 @@ unsigned char *cl_hash_file_fd(int fd, const char *alg, unsigned int *olen) if (!(ctx)) { #if OPENSSL_VERSION_MAJOR >= 3 EVP_MD_free(md); + OSSL_LIB_CTX_free(ossl_ctx); #endif return NULL; } @@ -926,6 +981,7 @@ unsigned char *cl_hash_file_fd(int fd, const char *alg, unsigned int *olen) if (!EVP_DigestInit_ex(ctx, md, NULL)) { #if OPENSSL_VERSION_MAJOR >= 3 EVP_MD_free(md); + OSSL_LIB_CTX_free(ossl_ctx); #endif EVP_MD_CTX_free(ctx); return NULL; @@ -934,6 +990,7 @@ unsigned char *cl_hash_file_fd(int fd, const char *alg, unsigned int *olen) res = cl_hash_file_fd_ctx(ctx, fd, olen); #if OPENSSL_VERSION_MAJOR >= 3 EVP_MD_free(md); + OSSL_LIB_CTX_free(ossl_ctx); #endif EVP_MD_CTX_free(ctx); @@ -1788,14 +1845,21 @@ void *cl_hash_init(const char *alg) EVP_MD_CTX *ctx; #if OPENSSL_VERSION_MAJOR >= 3 - EVP_MD *md = NULL; + OSSL_LIB_CTX *ossl_ctx = NULL; + EVP_MD *md = NULL; #else const EVP_MD *md = NULL; #endif #if OPENSSL_VERSION_MAJOR >= 3 /* Bypass FIPS restrictions the OpenSSL 3.0 way */ - md = EVP_MD_fetch(NULL, to_openssl_alg(alg), "-fips"); + ossl_ctx = OSSL_LIB_CTX_new(); + if (NULL == ossl_ctx) { + cli_errmsg("cl_hash_data_ex: Failed to create new OpenSSL library context\n"); + return NULL; + } + + md = EVP_MD_fetch(ossl_ctx, to_openssl_alg(alg), "-fips"); #else md = EVP_get_digestbyname(to_openssl_alg(alg)); #endif @@ -1806,6 +1870,7 @@ void *cl_hash_init(const char *alg) if (!(ctx)) { #if OPENSSL_VERSION_MAJOR >= 3 EVP_MD_free(md); + OSSL_LIB_CTX_free(ossl_ctx); #endif return NULL; } @@ -1820,6 +1885,7 @@ void *cl_hash_init(const char *alg) if (!EVP_DigestInit_ex(ctx, md, NULL)) { #if OPENSSL_VERSION_MAJOR >= 3 EVP_MD_free(md); + OSSL_LIB_CTX_free(ossl_ctx); #endif EVP_MD_CTX_free(ctx); return NULL; @@ -1827,6 +1893,7 @@ void *cl_hash_init(const char *alg) #if OPENSSL_VERSION_MAJOR >= 3 EVP_MD_free(md); + OSSL_LIB_CTX_free(ossl_ctx); #endif return (void *)ctx; }