clamav/libfreshclam/libfreshclam.c

1087 lines
38 KiB
C
Raw Normal View History

/*
2025-02-14 10:24:30 -05:00
* Copyright (C) 2013-2025 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2007-2013 Sourcefire, Inc.
* Copyright (C) 2002-2007 Tomasz Kojm <tkojm@clamav.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
2019-03-26 15:09:52 -04:00
#include <dirent.h>
#ifndef _WIN32
#include <sys/wait.h>
#endif
#include <sys/stat.h>
#include <fcntl.h>
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#ifdef HAVE_GRP_H
#include <grp.h>
#endif
#if defined(USE_SYSLOG) && !defined(C_AIX)
#include <syslog.h>
#endif
2019-03-26 15:09:52 -04:00
#include <curl/curl.h>
#include "target.h"
Add CMake build tooling This patch adds experimental-quality CMake build tooling. The libmspack build required a modification to use "" instead of <> for header #includes. This will hopefully be included in the libmspack upstream project when adding CMake build tooling to libmspack. Removed use of libltdl when using CMake. Flex & Bison are now required to build. If -DMAINTAINER_MODE, then GPERF is also required, though it currently doesn't actually do anything. TODO! I found that the autotools build system was generating the lexer output but not actually compiling it, instead using previously generated (and manually renamed) lexer c source. As a consequence, changes to the .l and .y files weren't making it into the build. To resolve this, I removed generated flex/bison files and fixed the tooling to use the freshly generated files. Flex and bison are now required build tools. On Windows, this adds a dependency on the winflexbison package, which can be obtained using Chocolatey or may be manually installed. CMake tooling only has partial support for building with external LLVM library, and no support for the internal LLVM (to be removed in the future). I.e. The CMake build currently only supports the bytecode interpreter. Many files used include paths relative to the top source directory or relative to the current project, rather than relative to each build target. Modern CMake support requires including internal dependency headers the same way you would external dependency headers (albeit with "" instead of <>). This meant correcting all header includes to be relative to the build targets and not relative to the workspace. For example, ... ```c include "../libclamav/clamav.h" include "clamd/clamd_others.h" ``` ... becomes: ```c // libclamav include "clamav.h" // clamd include "clamd_others.h" ``` Fixes header name conflicts by renaming a few of the files. Converted the "shared" code into a static library, which depends on libclamav. The ironically named "shared" static library provides features common to the ClamAV apps which are not required in libclamav itself and are not intended for use by downstream projects. This change was required for correct modern CMake practices but was also required to use the automake "subdir-objects" option. This eliminates warnings when running autoreconf which, in the next version of autoconf & automake are likely to break the build. libclamav used to build in multiple stages where an earlier stage is a static library containing utils required by the "shared" code. Linking clamdscan and clamdtop with this libclamav utils static lib allowed these two apps to function without libclamav. While this is nice in theory, the practical gains are minimal and it complicates the build system. As such, the autotools and CMake tooling was simplified for improved maintainability and this feature was thrown out. clamdtop and clamdscan now require libclamav to function. Removed the nopthreads version of the autotools libclamav_internal_utils static library and added pthread linking to a couple apps that may have issues building on some platforms without it, with the intention of removing needless complexity from the source. Kept the regular version of libclamav_internal_utils.la though it is no longer used anywhere but in libclamav. Added an experimental doxygen build option which attempts to build clamav.h and libfreshclam doxygen html docs. The CMake build tooling also may build the example program(s), which isn't a feature in the Autotools build system. Changed C standard to C90+ due to inline linking issues with socket.h when linking libfreshclam.so on Linux. Generate common.rc for win32. Fix tabs/spaces in shared Makefile.am, and remove vestigial ifndef from misc.c. Add CMake files to the automake dist, so users can try the new CMake tooling w/out having to build from a git clone. clamonacc changes: - Renamed FANOTIFY macro to HAVE_SYS_FANOTIFY_H to better match other similar macros. - Added a new clamav-clamonacc.service systemd unit file, based on the work of ChadDevOps & Aaron Brighton. - Added missing clamonacc man page. Updates to clamdscan man page, add missing options. Remove vestigial CL_NOLIBCLAMAV definitions (all apps now use libclamav). Rename Windows mspack.dll to libmspack.dll so all ClamAV-built libraries have the lib-prefix with Visual Studio as with CMake.
2020-08-13 00:25:34 -07:00
// libclamav
#include "clamav.h"
#include "clamav_rust.h"
Add CMake build tooling This patch adds experimental-quality CMake build tooling. The libmspack build required a modification to use "" instead of <> for header #includes. This will hopefully be included in the libmspack upstream project when adding CMake build tooling to libmspack. Removed use of libltdl when using CMake. Flex & Bison are now required to build. If -DMAINTAINER_MODE, then GPERF is also required, though it currently doesn't actually do anything. TODO! I found that the autotools build system was generating the lexer output but not actually compiling it, instead using previously generated (and manually renamed) lexer c source. As a consequence, changes to the .l and .y files weren't making it into the build. To resolve this, I removed generated flex/bison files and fixed the tooling to use the freshly generated files. Flex and bison are now required build tools. On Windows, this adds a dependency on the winflexbison package, which can be obtained using Chocolatey or may be manually installed. CMake tooling only has partial support for building with external LLVM library, and no support for the internal LLVM (to be removed in the future). I.e. The CMake build currently only supports the bytecode interpreter. Many files used include paths relative to the top source directory or relative to the current project, rather than relative to each build target. Modern CMake support requires including internal dependency headers the same way you would external dependency headers (albeit with "" instead of <>). This meant correcting all header includes to be relative to the build targets and not relative to the workspace. For example, ... ```c include "../libclamav/clamav.h" include "clamd/clamd_others.h" ``` ... becomes: ```c // libclamav include "clamav.h" // clamd include "clamd_others.h" ``` Fixes header name conflicts by renaming a few of the files. Converted the "shared" code into a static library, which depends on libclamav. The ironically named "shared" static library provides features common to the ClamAV apps which are not required in libclamav itself and are not intended for use by downstream projects. This change was required for correct modern CMake practices but was also required to use the automake "subdir-objects" option. This eliminates warnings when running autoreconf which, in the next version of autoconf & automake are likely to break the build. libclamav used to build in multiple stages where an earlier stage is a static library containing utils required by the "shared" code. Linking clamdscan and clamdtop with this libclamav utils static lib allowed these two apps to function without libclamav. While this is nice in theory, the practical gains are minimal and it complicates the build system. As such, the autotools and CMake tooling was simplified for improved maintainability and this feature was thrown out. clamdtop and clamdscan now require libclamav to function. Removed the nopthreads version of the autotools libclamav_internal_utils static library and added pthread linking to a couple apps that may have issues building on some platforms without it, with the intention of removing needless complexity from the source. Kept the regular version of libclamav_internal_utils.la though it is no longer used anywhere but in libclamav. Added an experimental doxygen build option which attempts to build clamav.h and libfreshclam doxygen html docs. The CMake build tooling also may build the example program(s), which isn't a feature in the Autotools build system. Changed C standard to C90+ due to inline linking issues with socket.h when linking libfreshclam.so on Linux. Generate common.rc for win32. Fix tabs/spaces in shared Makefile.am, and remove vestigial ifndef from misc.c. Add CMake files to the automake dist, so users can try the new CMake tooling w/out having to build from a git clone. clamonacc changes: - Renamed FANOTIFY macro to HAVE_SYS_FANOTIFY_H to better match other similar macros. - Added a new clamav-clamonacc.service systemd unit file, based on the work of ChadDevOps & Aaron Brighton. - Added missing clamonacc man page. Updates to clamdscan man page, add missing options. Remove vestigial CL_NOLIBCLAMAV definitions (all apps now use libclamav). Rename Windows mspack.dll to libmspack.dll so all ClamAV-built libraries have the lib-prefix with Visual Studio as with CMake.
2020-08-13 00:25:34 -07:00
#include "others.h"
#include "regex_list.h"
#include "str.h"
// common
Add CMake build tooling This patch adds experimental-quality CMake build tooling. The libmspack build required a modification to use "" instead of <> for header #includes. This will hopefully be included in the libmspack upstream project when adding CMake build tooling to libmspack. Removed use of libltdl when using CMake. Flex & Bison are now required to build. If -DMAINTAINER_MODE, then GPERF is also required, though it currently doesn't actually do anything. TODO! I found that the autotools build system was generating the lexer output but not actually compiling it, instead using previously generated (and manually renamed) lexer c source. As a consequence, changes to the .l and .y files weren't making it into the build. To resolve this, I removed generated flex/bison files and fixed the tooling to use the freshly generated files. Flex and bison are now required build tools. On Windows, this adds a dependency on the winflexbison package, which can be obtained using Chocolatey or may be manually installed. CMake tooling only has partial support for building with external LLVM library, and no support for the internal LLVM (to be removed in the future). I.e. The CMake build currently only supports the bytecode interpreter. Many files used include paths relative to the top source directory or relative to the current project, rather than relative to each build target. Modern CMake support requires including internal dependency headers the same way you would external dependency headers (albeit with "" instead of <>). This meant correcting all header includes to be relative to the build targets and not relative to the workspace. For example, ... ```c include "../libclamav/clamav.h" include "clamd/clamd_others.h" ``` ... becomes: ```c // libclamav include "clamav.h" // clamd include "clamd_others.h" ``` Fixes header name conflicts by renaming a few of the files. Converted the "shared" code into a static library, which depends on libclamav. The ironically named "shared" static library provides features common to the ClamAV apps which are not required in libclamav itself and are not intended for use by downstream projects. This change was required for correct modern CMake practices but was also required to use the automake "subdir-objects" option. This eliminates warnings when running autoreconf which, in the next version of autoconf & automake are likely to break the build. libclamav used to build in multiple stages where an earlier stage is a static library containing utils required by the "shared" code. Linking clamdscan and clamdtop with this libclamav utils static lib allowed these two apps to function without libclamav. While this is nice in theory, the practical gains are minimal and it complicates the build system. As such, the autotools and CMake tooling was simplified for improved maintainability and this feature was thrown out. clamdtop and clamdscan now require libclamav to function. Removed the nopthreads version of the autotools libclamav_internal_utils static library and added pthread linking to a couple apps that may have issues building on some platforms without it, with the intention of removing needless complexity from the source. Kept the regular version of libclamav_internal_utils.la though it is no longer used anywhere but in libclamav. Added an experimental doxygen build option which attempts to build clamav.h and libfreshclam doxygen html docs. The CMake build tooling also may build the example program(s), which isn't a feature in the Autotools build system. Changed C standard to C90+ due to inline linking issues with socket.h when linking libfreshclam.so on Linux. Generate common.rc for win32. Fix tabs/spaces in shared Makefile.am, and remove vestigial ifndef from misc.c. Add CMake files to the automake dist, so users can try the new CMake tooling w/out having to build from a git clone. clamonacc changes: - Renamed FANOTIFY macro to HAVE_SYS_FANOTIFY_H to better match other similar macros. - Added a new clamav-clamonacc.service systemd unit file, based on the work of ChadDevOps & Aaron Brighton. - Added missing clamonacc man page. Updates to clamdscan man page, add missing options. Remove vestigial CL_NOLIBCLAMAV definitions (all apps now use libclamav). Rename Windows mspack.dll to libmspack.dll so all ClamAV-built libraries have the lib-prefix with Visual Studio as with CMake.
2020-08-13 00:25:34 -07:00
#include "cert_util.h"
#include "output.h"
#include "misc.h"
2019-03-26 15:09:52 -04:00
#include "libfreshclam.h"
#include "libfreshclam_internal.h"
#include "dns.h"
/*
* Private functions
*/
2019-03-26 15:09:52 -04:00
/*
* libclamav API functions
*/
const char *fc_strerror(fc_error_t fcerror)
{
switch (fcerror) {
case FC_SUCCESS:
return "Success";
case FC_UPTODATE:
return "Up-to-date";
case FC_EINIT:
2023-11-26 15:01:19 -08:00
return "Failed to initialize";
2019-03-26 15:09:52 -04:00
case FC_EDIRECTORY:
2023-11-26 15:01:19 -08:00
return "Invalid, nonexistent, or inaccessible directory";
2019-03-26 15:09:52 -04:00
case FC_EFILE:
2023-11-26 15:01:19 -08:00
return "Invalid, nonexistent, or inaccessible file";
2019-03-26 15:09:52 -04:00
case FC_ECONNECTION:
return "Connection failed";
case FC_EEMPTYFILE:
return "Empty file";
case FC_EBADCVD:
return "Invalid or corrupted CVD/CLD database";
case FC_ETESTFAIL:
return "Test failed";
case FC_ECONFIG:
return "Invalid configuration settings(s)";
case FC_EDBDIRACCESS:
return "Failed to read/write file to database directory";
case FC_EFAILEDGET:
return "HTTP GET failed";
case FC_EMIRRORNOTSYNC:
return "Downloaded database had lower version than advertised";
case FC_ELOGGING:
return "Failed to write to log";
case FC_EFAILEDUPDATE:
return "Failed to update database";
case FC_EMEM:
return "Memory allocation error";
case FC_EARG:
return "Invalid argument(s)";
case FC_EFORBIDDEN:
return "Forbidden; Blocked by CDN";
case FC_ERETRYLATER:
return "Too many requests; Retry later";
2019-03-26 15:09:52 -04:00
default:
return "Unknown libfreshclam error code!";
}
}
2019-03-26 15:09:52 -04:00
fc_error_t fc_initialize(fc_config *fcConfig)
{
2019-03-26 15:09:52 -04:00
fc_error_t status = FC_EARG;
STATBUF statbuf;
FIPS-compliant CVD signing and verification Add X509 certificate chain based signing with PKCS7-PEM external signatures distributed alongside CVD's in a custom .cvd.sign format. This new signing and verification mechanism is primarily in support of FIPS compliance. Fixes: https://github.com/Cisco-Talos/clamav/issues/564 Add a Rust implementation for parsing, verifying, and unpacking CVD files. Now installs a 'certs' directory in the app config directory (e.g. <prefix>/etc/certs). The install location is configurable. The CMake option to configure the CVD certs directory is: `-D CVD_CERTS_DIRECTORY=PATH` New options to set an alternative CVD certs directory: - Commandline for freshclam, clamd, clamscan, and sigtool is: `--cvdcertsdir PATH` - Env variable for freshclam, clamd, clamscan, and sigtool is: `CVD_CERTS_DIR` - Config option for freshclam and clamd is: `CVDCertsDirectory PATH` Sigtool: - Add sign/verify commands. - Also verify CDIFF external digital signatures when applying CDIFFs. - Place commonly used commands at the top of --help string. - Fix up manpage. Freshclam: - Will try to download .sign files to verify CVDs and CDIFFs. - Fix an issue where making a CLD would only include the CFG file for daily and not if patching any other database. libclamav.so: - Bump version to 13:0:1 (aka 12.1.0). - Also remove libclamav.map versioning. Resolves: https://github.com/Cisco-Talos/clamav/issues/1304 - Add two new API's to the public clamav.h header: ```c extern cl_error_t cl_cvdverify_ex(const char *file, const char *certs_directory); extern cl_error_t cl_cvdunpack_ex(const char *file, const char *dir, bool dont_verify, const char *certs_directory); ``` The original `cl_cvdverify` and `cl_cvdunpack` are deprecated. - Add `cl_engine_field` enum option `CL_ENGINE_CVDCERTSDIR`. You may set this option with `cl_engine_set_str` and get it with `cl_engine_get_str`, to override the compiled in default CVD certs directory. libfreshclam.so: Bump version to 4:0:0 (aka 4.0.0). Add sigtool sign/verify tests and test certs. Make it so downloadFile doesn't throw a warning if the server doesn't have the .sign file. Replace use of md5-based FP signatures in the unit tests with sha256-based FP signatures because the md5 implementation used by Python may be disabled in FIPS mode. Fixes: https://github.com/Cisco-Talos/clamav/issues/1411 CMake: Add logic to enable the Rust openssl-sys / openssl-rs crates to build against the same OpenSSL library as is used for the C build. The Rust unit test application must also link directly with libcrypto and libssl. Fix some log messages with missing new lines. Fix missing environment variable notes in --help messages and manpages. Deconflict CONFDIR/DATADIR/CERTSDIR variable names that are defined in clamav-config.h.in for libclamav from variable that had the same name for use in clamav applications that use the optparser. The 'clamav-test' certs for the unit tests will live for 10 years. The 'clamav-beta.crt' public cert will only live for 120 days and will be replaced before the stable release with a production 'clamav.crt'.
2024-11-21 14:01:09 -05:00
char *certsDirectory = NULL;
FFIError *new_verifier_error = NULL;
2019-03-26 15:09:52 -04:00
if (NULL == fcConfig) {
printf("fc_initialize: Invalid arguments.\n");
return status;
}
/* Rust logging initialization */
if (!clrs_log_init()) {
cli_dbgmsg("Unexpected problem occurred while setting up rust logging... continuing without rust logging. \
Please submit an issue to https://github.com/Cisco-Talos/clamav");
}
2023-11-26 15:01:19 -08:00
/* Initialize libcurl */
2019-03-26 15:09:52 -04:00
curl_global_init(CURL_GLOBAL_ALL);
/* Initialize mprintf options */
if (fcConfig->msgFlags & FC_CONFIG_MSG_DEBUG) cl_debug();
mprintf_verbose = (fcConfig->msgFlags & FC_CONFIG_MSG_VERBOSE) ? 1 : 0;
mprintf_quiet = (fcConfig->msgFlags & FC_CONFIG_MSG_QUIET) ? 1 : 0;
mprintf_nowarn = (fcConfig->msgFlags & FC_CONFIG_MSG_NOWARN) ? 1 : 0;
mprintf_stdout = (fcConfig->msgFlags & FC_CONFIG_MSG_STDOUT) ? 1 : 0;
mprintf_progress = (fcConfig->msgFlags & FC_CONFIG_MSG_SHOWPROGRESS) ? 1 : 0;
/* Initialize logger */
logg_verbose = (fcConfig->logFlags & FC_CONFIG_LOG_VERBOSE) ? 1 : 0;
logg_nowarn = (fcConfig->logFlags & FC_CONFIG_LOG_NOWARN) ? 1 : 0;
logg_time = (fcConfig->logFlags & FC_CONFIG_LOG_TIME) ? 1 : 0;
logg_rotate = (fcConfig->logFlags & FC_CONFIG_LOG_ROTATE) ? 1 : 0;
logg_size = fcConfig->maxLogSize;
/* Set a log file if requested, and is not already set */
if ((NULL == logg_file) && (NULL != fcConfig->logFile)) {
logg_file = cli_safer_strdup(fcConfig->logFile);
if (0 != logg(LOGG_INFO_NF, "--------------------------------------\n")) {
mprintf(LOGG_ERROR, "Problem with internal logger (UpdateLogFile = %s).\n", logg_file);
2019-03-26 15:09:52 -04:00
status = FC_ELOGGING;
goto done;
}
}
#if defined(USE_SYSLOG) && !defined(C_AIX)
/* Initialize syslog if available and requested, and is not already set */
2019-03-26 15:09:52 -04:00
if (fcConfig->logFlags & FC_CONFIG_LOG_SYSLOG) {
int logFacility = LOG_LOCAL6;
if ((0 == logg_syslog) && (NULL != fcConfig->logFacility) && (-1 == (logFacility = logg_facility(fcConfig->logFacility)))) {
mprintf(LOGG_ERROR, "LogFacility: %s: No such facility.\n", fcConfig->logFacility);
2019-03-26 15:09:52 -04:00
status = FC_ELOGGING;
goto done;
}
openlog("freshclam", LOG_PID, logFacility);
logg_syslog = 1;
}
#endif
2019-03-26 15:09:52 -04:00
/* Optional connection settings. */
if (NULL != fcConfig->localIP) {
#if !((LIBCURL_VERSION_MAJOR > 7) || ((LIBCURL_VERSION_MAJOR == 7) && (LIBCURL_VERSION_MINOR >= 33)))
mprintf(LOGG_ERROR, "The LocalIP feature was requested but this local IP support is not presently available.\n");
mprintf(LOGG_ERROR, "Your installation was built with libcurl version %u.%u.%u.\n", LIBCURL_VERSION_MAJOR, LIBCURL_VERSION_MINOR, LIBCURL_VERSION_PATCH);
mprintf(LOGG_ERROR, "LocalIP requires libcurl version 7.33.0 or higher and must include the c-ares optional dependency.\n");
2019-03-26 15:09:52 -04:00
#else
g_localIP = cli_safer_strdup(fcConfig->localIP);
2019-03-26 15:09:52 -04:00
#endif
}
if (NULL != fcConfig->userAgent) {
g_userAgent = cli_safer_strdup(fcConfig->userAgent);
2019-03-26 15:09:52 -04:00
}
if (NULL != fcConfig->proxyServer) {
g_proxyServer = cli_safer_strdup(fcConfig->proxyServer);
2019-03-26 15:09:52 -04:00
if (0 != fcConfig->proxyPort) {
g_proxyPort = fcConfig->proxyPort;
} else {
/*
* Proxy port not provided. Look up the default port for
* webcache in /etc/services.
* Default to 8080 if not provided.
*/
const struct servent *webcache = getservbyname("webcache", "TCP");
if (webcache)
g_proxyPort = ntohs(webcache->s_port);
else
g_proxyPort = 8080;
endservent();
}
}
if (NULL != fcConfig->proxyUsername) {
g_proxyUsername = cli_safer_strdup(fcConfig->proxyUsername);
2019-03-26 15:09:52 -04:00
}
if (NULL != fcConfig->proxyPassword) {
g_proxyPassword = cli_safer_strdup(fcConfig->proxyPassword);
2019-03-26 15:09:52 -04:00
}
#ifdef _WIN32
if ((fcConfig->databaseDirectory[strlen(fcConfig->databaseDirectory) - 1] != '/') &&
((fcConfig->databaseDirectory[strlen(fcConfig->databaseDirectory) - 1] != '\\'))) {
#else
if (fcConfig->databaseDirectory[strlen(fcConfig->databaseDirectory) - 1] != '/') {
#endif
g_databaseDirectory = malloc(strlen(fcConfig->databaseDirectory) + strlen(PATHSEP) + 1);
snprintf(
g_databaseDirectory,
strlen(fcConfig->databaseDirectory) + strlen(PATHSEP) + 1,
"%s" PATHSEP,
fcConfig->databaseDirectory);
} else {
g_databaseDirectory = cli_safer_strdup(fcConfig->databaseDirectory);
}
2019-03-26 15:09:52 -04:00
/* Validate that the database directory exists, and store it. */
if (LSTAT(g_databaseDirectory, &statbuf) == -1) {
logg(LOGG_ERROR, "Database directory does not exist: %s\n", g_databaseDirectory);
2019-03-26 15:09:52 -04:00
status = FC_EDIRECTORY;
goto done;
}
if (!S_ISDIR(statbuf.st_mode)) {
logg(LOGG_ERROR, "Database directory is not a directory: %s\n", g_databaseDirectory);
2019-03-26 15:09:52 -04:00
status = FC_EDIRECTORY;
goto done;
}
FIPS-compliant CVD signing and verification Add X509 certificate chain based signing with PKCS7-PEM external signatures distributed alongside CVD's in a custom .cvd.sign format. This new signing and verification mechanism is primarily in support of FIPS compliance. Fixes: https://github.com/Cisco-Talos/clamav/issues/564 Add a Rust implementation for parsing, verifying, and unpacking CVD files. Now installs a 'certs' directory in the app config directory (e.g. <prefix>/etc/certs). The install location is configurable. The CMake option to configure the CVD certs directory is: `-D CVD_CERTS_DIRECTORY=PATH` New options to set an alternative CVD certs directory: - Commandline for freshclam, clamd, clamscan, and sigtool is: `--cvdcertsdir PATH` - Env variable for freshclam, clamd, clamscan, and sigtool is: `CVD_CERTS_DIR` - Config option for freshclam and clamd is: `CVDCertsDirectory PATH` Sigtool: - Add sign/verify commands. - Also verify CDIFF external digital signatures when applying CDIFFs. - Place commonly used commands at the top of --help string. - Fix up manpage. Freshclam: - Will try to download .sign files to verify CVDs and CDIFFs. - Fix an issue where making a CLD would only include the CFG file for daily and not if patching any other database. libclamav.so: - Bump version to 13:0:1 (aka 12.1.0). - Also remove libclamav.map versioning. Resolves: https://github.com/Cisco-Talos/clamav/issues/1304 - Add two new API's to the public clamav.h header: ```c extern cl_error_t cl_cvdverify_ex(const char *file, const char *certs_directory); extern cl_error_t cl_cvdunpack_ex(const char *file, const char *dir, bool dont_verify, const char *certs_directory); ``` The original `cl_cvdverify` and `cl_cvdunpack` are deprecated. - Add `cl_engine_field` enum option `CL_ENGINE_CVDCERTSDIR`. You may set this option with `cl_engine_set_str` and get it with `cl_engine_get_str`, to override the compiled in default CVD certs directory. libfreshclam.so: Bump version to 4:0:0 (aka 4.0.0). Add sigtool sign/verify tests and test certs. Make it so downloadFile doesn't throw a warning if the server doesn't have the .sign file. Replace use of md5-based FP signatures in the unit tests with sha256-based FP signatures because the md5 implementation used by Python may be disabled in FIPS mode. Fixes: https://github.com/Cisco-Talos/clamav/issues/1411 CMake: Add logic to enable the Rust openssl-sys / openssl-rs crates to build against the same OpenSSL library as is used for the C build. The Rust unit test application must also link directly with libcrypto and libssl. Fix some log messages with missing new lines. Fix missing environment variable notes in --help messages and manpages. Deconflict CONFDIR/DATADIR/CERTSDIR variable names that are defined in clamav-config.h.in for libclamav from variable that had the same name for use in clamav applications that use the optparser. The 'clamav-test' certs for the unit tests will live for 10 years. The 'clamav-beta.crt' public cert will only live for 120 days and will be replaced before the stable release with a production 'clamav.crt'.
2024-11-21 14:01:09 -05:00
#ifdef _WIN32
if ((fcConfig->certsDirectory[strlen(fcConfig->certsDirectory) - 1] != '/') &&
((fcConfig->certsDirectory[strlen(fcConfig->certsDirectory) - 1] != '\\'))) {
#else
if (fcConfig->certsDirectory[strlen(fcConfig->certsDirectory) - 1] != '/') {
#endif
certsDirectory = malloc(strlen(fcConfig->certsDirectory) + strlen(PATHSEP) + 1);
snprintf(
certsDirectory,
strlen(fcConfig->certsDirectory) + strlen(PATHSEP) + 1,
"%s" PATHSEP,
fcConfig->certsDirectory);
} else {
certsDirectory = cli_safer_strdup(fcConfig->certsDirectory);
}
if (!codesign_verifier_new(certsDirectory, &g_signVerifier, &new_verifier_error)) {
logg(LOGG_ERROR, "Failed to create a new code-signature verifier: %s\n", ffierror_fmt(new_verifier_error));
status = FC_EINIT;
goto done;
}
g_tempDirectory = cli_safer_strdup(fcConfig->tempDirectory);
2019-03-26 15:09:52 -04:00
g_maxAttempts = fcConfig->maxAttempts;
g_connectTimeout = fcConfig->connectTimeout;
g_requestTimeout = fcConfig->requestTimeout;
g_bCompressLocalDatabase = fcConfig->bCompressLocalDatabase;
FIPS & FIPS-like limits on hash algs for cryptographic uses ClamAV will not function when using a FIPS-enabled OpenSSL 3.x. This is because ClamAV uses MD5 and SHA1 algorithms for a variety of purposes including matching for malware detection, matching to prevent false positives on known-clean files, and for verification of MD5-based RSA digital signatures for determining CVD (signature database archive) authenticity. Interestingly, FIPS had been intentionally bypassed when creating hashes based whole buffers and whole files (by descriptor or `FILE`-pointer): https://github.com/Cisco-Talos/clamav/commit/78d4a9985a06a418dd1338c94ee5db461035d75b Note: this bypassed FIPS the 1.x way with: `EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);` It was NOT disabled when using `cl_hash_init()` / `cl_update_hash()` / `cl_finish_hash()`. That likely worked by coincidence in that the hash was already calculated most of the time. It certainly would have made use of those functions if the hash had not been calculated prior: https://github.com/Cisco-Talos/clamav/blob/78d4a9985a06a418dd1338c94ee5db461035d75b/libclamav/matcher.c#L743 Regardless, bypassing FIPS entirely is not the correct solution. The FIPS restrictions against using MD5 and SHA1 are valid, particularly when verifying CVD digital siganatures, but also I think when using a hash to determine if the file is known-clean (i.e. the "clean cache" and also MD5-based and SHA1-based FP signatures). This commit extends the work to bypass FIPS using the newer 3.x method: `md = EVP_MD_fetch(NULL, alg, "-fips");` It does this for the legacy `cl_hash*()` functions including `cl_hash_init()` / `cl_update_hash()` / `cl_finish_hash()`. It also introduces extended versions that allow the caller to choose if they want to bypass FIPS: - `cl_hash_data_ex()` - `cl_hash_init_ex()` - `cl_update_hash_ex()` - `cl_finish_hash_ex()` - `cl_hash_destroy_ex()` - `cl_hash_file_fd_ex()` See the `flags` parameter for each. Ironically, this commit does NOT use the new functions at this time. The rational is that ClamAV may need MD5, SHA1, and SHA-256 hashes of the same files both for determining if the file is malware, and for determining if the file is clean. So instead, this commit will do a checks when: 1. Creating a new ClamAV scanning engine. If FIPS-mode enabled, it will automatically toggle the "FIPS limits" engine option. When loading signatures, if the engine "FIPS limits" option is enabled, then MD5 and SHA1 FP signatures will be skipped. 2. Before verifying a CVD (e.g. also for loading, unpacking when verification enabled). If "FIPS limits" or FIPS-mode are enabled, then the legacy MD5-based RSA method is disabled. Note: This commit also refactors the interface for `cl_cvdverify_ex()` and `cl_cvdunpack_ex()` so they take a `flags` parameters, rather than a single `bool`. As these functions are new in this version, it does not break the ABI. The cache was already switched to use SHA2-256, so that's not a concern for checking FIPS-mode / FIPS limits options. This adds an option for `freshclam.conf` and `clamd.conf`: FIPSCryptoHashLimits yes And an equivalent command-line option for `clamscan` and `sigtool`: --fips-limits You may programmatically enable FIPS-limits for a ClamAV engine like this: ```C cl_engine_set_num(engine, CL_ENGINE_FIPS_LIMITS, 1); ``` CLAM-2792
2025-07-01 20:41:47 -04:00
g_bFipsLimits = fcConfig->bFipsLimits;
/* Load or create freshclam.dat */
if (FC_SUCCESS != load_freshclam_dat()) {
logg(LOGG_DEBUG, "Failed to load freshclam.dat; will create a new freshclam.dat\n");
if (FC_SUCCESS != new_freshclam_dat()) {
logg(LOGG_WARNING, "Failed to create a new freshclam.dat!\n");
status = FC_EINIT;
goto done;
}
}
2019-03-26 15:09:52 -04:00
status = FC_SUCCESS;
done:
if (FC_SUCCESS != status) {
fc_cleanup();
}
FIPS-compliant CVD signing and verification Add X509 certificate chain based signing with PKCS7-PEM external signatures distributed alongside CVD's in a custom .cvd.sign format. This new signing and verification mechanism is primarily in support of FIPS compliance. Fixes: https://github.com/Cisco-Talos/clamav/issues/564 Add a Rust implementation for parsing, verifying, and unpacking CVD files. Now installs a 'certs' directory in the app config directory (e.g. <prefix>/etc/certs). The install location is configurable. The CMake option to configure the CVD certs directory is: `-D CVD_CERTS_DIRECTORY=PATH` New options to set an alternative CVD certs directory: - Commandline for freshclam, clamd, clamscan, and sigtool is: `--cvdcertsdir PATH` - Env variable for freshclam, clamd, clamscan, and sigtool is: `CVD_CERTS_DIR` - Config option for freshclam and clamd is: `CVDCertsDirectory PATH` Sigtool: - Add sign/verify commands. - Also verify CDIFF external digital signatures when applying CDIFFs. - Place commonly used commands at the top of --help string. - Fix up manpage. Freshclam: - Will try to download .sign files to verify CVDs and CDIFFs. - Fix an issue where making a CLD would only include the CFG file for daily and not if patching any other database. libclamav.so: - Bump version to 13:0:1 (aka 12.1.0). - Also remove libclamav.map versioning. Resolves: https://github.com/Cisco-Talos/clamav/issues/1304 - Add two new API's to the public clamav.h header: ```c extern cl_error_t cl_cvdverify_ex(const char *file, const char *certs_directory); extern cl_error_t cl_cvdunpack_ex(const char *file, const char *dir, bool dont_verify, const char *certs_directory); ``` The original `cl_cvdverify` and `cl_cvdunpack` are deprecated. - Add `cl_engine_field` enum option `CL_ENGINE_CVDCERTSDIR`. You may set this option with `cl_engine_set_str` and get it with `cl_engine_get_str`, to override the compiled in default CVD certs directory. libfreshclam.so: Bump version to 4:0:0 (aka 4.0.0). Add sigtool sign/verify tests and test certs. Make it so downloadFile doesn't throw a warning if the server doesn't have the .sign file. Replace use of md5-based FP signatures in the unit tests with sha256-based FP signatures because the md5 implementation used by Python may be disabled in FIPS mode. Fixes: https://github.com/Cisco-Talos/clamav/issues/1411 CMake: Add logic to enable the Rust openssl-sys / openssl-rs crates to build against the same OpenSSL library as is used for the C build. The Rust unit test application must also link directly with libcrypto and libssl. Fix some log messages with missing new lines. Fix missing environment variable notes in --help messages and manpages. Deconflict CONFDIR/DATADIR/CERTSDIR variable names that are defined in clamav-config.h.in for libclamav from variable that had the same name for use in clamav applications that use the optparser. The 'clamav-test' certs for the unit tests will live for 10 years. The 'clamav-beta.crt' public cert will only live for 120 days and will be replaced before the stable release with a production 'clamav.crt'.
2024-11-21 14:01:09 -05:00
if (NULL != certsDirectory) {
free(certsDirectory);
}
if (NULL != new_verifier_error) {
ffierror_free(new_verifier_error);
}
2019-03-26 15:09:52 -04:00
return status;
}
void fc_cleanup(void)
{
/* Cleanup libcurl */
curl_global_cleanup();
if (NULL != logg_file) {
free((void *)logg_file);
logg_file = NULL;
}
if (NULL != g_localIP) {
free(g_localIP);
g_localIP = NULL;
}
if (NULL != g_userAgent) {
free(g_userAgent);
g_userAgent = NULL;
}
if (NULL != g_proxyServer) {
free(g_proxyServer);
g_proxyServer = NULL;
}
if (NULL != g_proxyUsername) {
free(g_proxyUsername);
g_proxyUsername = NULL;
}
if (NULL != g_proxyPassword) {
free(g_proxyPassword);
g_proxyPassword = NULL;
}
if (NULL != g_databaseDirectory) {
free(g_databaseDirectory);
g_databaseDirectory = NULL;
}
if (NULL != g_tempDirectory) {
free(g_tempDirectory);
g_tempDirectory = NULL;
}
if (NULL != g_freshclamDat) {
free(g_freshclamDat);
g_freshclamDat = NULL;
}
FIPS-compliant CVD signing and verification Add X509 certificate chain based signing with PKCS7-PEM external signatures distributed alongside CVD's in a custom .cvd.sign format. This new signing and verification mechanism is primarily in support of FIPS compliance. Fixes: https://github.com/Cisco-Talos/clamav/issues/564 Add a Rust implementation for parsing, verifying, and unpacking CVD files. Now installs a 'certs' directory in the app config directory (e.g. <prefix>/etc/certs). The install location is configurable. The CMake option to configure the CVD certs directory is: `-D CVD_CERTS_DIRECTORY=PATH` New options to set an alternative CVD certs directory: - Commandline for freshclam, clamd, clamscan, and sigtool is: `--cvdcertsdir PATH` - Env variable for freshclam, clamd, clamscan, and sigtool is: `CVD_CERTS_DIR` - Config option for freshclam and clamd is: `CVDCertsDirectory PATH` Sigtool: - Add sign/verify commands. - Also verify CDIFF external digital signatures when applying CDIFFs. - Place commonly used commands at the top of --help string. - Fix up manpage. Freshclam: - Will try to download .sign files to verify CVDs and CDIFFs. - Fix an issue where making a CLD would only include the CFG file for daily and not if patching any other database. libclamav.so: - Bump version to 13:0:1 (aka 12.1.0). - Also remove libclamav.map versioning. Resolves: https://github.com/Cisco-Talos/clamav/issues/1304 - Add two new API's to the public clamav.h header: ```c extern cl_error_t cl_cvdverify_ex(const char *file, const char *certs_directory); extern cl_error_t cl_cvdunpack_ex(const char *file, const char *dir, bool dont_verify, const char *certs_directory); ``` The original `cl_cvdverify` and `cl_cvdunpack` are deprecated. - Add `cl_engine_field` enum option `CL_ENGINE_CVDCERTSDIR`. You may set this option with `cl_engine_set_str` and get it with `cl_engine_get_str`, to override the compiled in default CVD certs directory. libfreshclam.so: Bump version to 4:0:0 (aka 4.0.0). Add sigtool sign/verify tests and test certs. Make it so downloadFile doesn't throw a warning if the server doesn't have the .sign file. Replace use of md5-based FP signatures in the unit tests with sha256-based FP signatures because the md5 implementation used by Python may be disabled in FIPS mode. Fixes: https://github.com/Cisco-Talos/clamav/issues/1411 CMake: Add logic to enable the Rust openssl-sys / openssl-rs crates to build against the same OpenSSL library as is used for the C build. The Rust unit test application must also link directly with libcrypto and libssl. Fix some log messages with missing new lines. Fix missing environment variable notes in --help messages and manpages. Deconflict CONFDIR/DATADIR/CERTSDIR variable names that are defined in clamav-config.h.in for libclamav from variable that had the same name for use in clamav applications that use the optparser. The 'clamav-test' certs for the unit tests will live for 10 years. The 'clamav-beta.crt' public cert will only live for 120 days and will be replaced before the stable release with a production 'clamav.crt'.
2024-11-21 14:01:09 -05:00
if (NULL != g_signVerifier) {
codesign_verifier_free(g_signVerifier);
}
2019-03-26 15:09:52 -04:00
}
fc_error_t fc_prune_database_directory(char **databaseList, uint32_t nDatabases)
{
fc_error_t status = FC_EARG;
DIR *dir = NULL;
struct dirent *dent;
char *extension = NULL;
/* Change directory to database directory */
if (chdir(g_databaseDirectory)) {
logg(LOGG_ERROR, "Can't change dir to %s\n", g_databaseDirectory);
status = FC_EDIRECTORY;
goto done;
}
logg(LOGG_DEBUG, "Current working dir is %s\n", g_databaseDirectory);
2019-03-26 15:09:52 -04:00
if (!(dir = opendir(g_databaseDirectory))) {
logg(LOGG_ERROR, "checkdbdir: Can't open directory %s\n", g_databaseDirectory);
2019-03-26 15:09:52 -04:00
status = FC_EDBDIRACCESS;
goto done;
}
while ((dent = readdir(dir))) {
if (dent->d_ino) {
FIPS-compliant CVD signing and verification Add X509 certificate chain based signing with PKCS7-PEM external signatures distributed alongside CVD's in a custom .cvd.sign format. This new signing and verification mechanism is primarily in support of FIPS compliance. Fixes: https://github.com/Cisco-Talos/clamav/issues/564 Add a Rust implementation for parsing, verifying, and unpacking CVD files. Now installs a 'certs' directory in the app config directory (e.g. <prefix>/etc/certs). The install location is configurable. The CMake option to configure the CVD certs directory is: `-D CVD_CERTS_DIRECTORY=PATH` New options to set an alternative CVD certs directory: - Commandline for freshclam, clamd, clamscan, and sigtool is: `--cvdcertsdir PATH` - Env variable for freshclam, clamd, clamscan, and sigtool is: `CVD_CERTS_DIR` - Config option for freshclam and clamd is: `CVDCertsDirectory PATH` Sigtool: - Add sign/verify commands. - Also verify CDIFF external digital signatures when applying CDIFFs. - Place commonly used commands at the top of --help string. - Fix up manpage. Freshclam: - Will try to download .sign files to verify CVDs and CDIFFs. - Fix an issue where making a CLD would only include the CFG file for daily and not if patching any other database. libclamav.so: - Bump version to 13:0:1 (aka 12.1.0). - Also remove libclamav.map versioning. Resolves: https://github.com/Cisco-Talos/clamav/issues/1304 - Add two new API's to the public clamav.h header: ```c extern cl_error_t cl_cvdverify_ex(const char *file, const char *certs_directory); extern cl_error_t cl_cvdunpack_ex(const char *file, const char *dir, bool dont_verify, const char *certs_directory); ``` The original `cl_cvdverify` and `cl_cvdunpack` are deprecated. - Add `cl_engine_field` enum option `CL_ENGINE_CVDCERTSDIR`. You may set this option with `cl_engine_set_str` and get it with `cl_engine_get_str`, to override the compiled in default CVD certs directory. libfreshclam.so: Bump version to 4:0:0 (aka 4.0.0). Add sigtool sign/verify tests and test certs. Make it so downloadFile doesn't throw a warning if the server doesn't have the .sign file. Replace use of md5-based FP signatures in the unit tests with sha256-based FP signatures because the md5 implementation used by Python may be disabled in FIPS mode. Fixes: https://github.com/Cisco-Talos/clamav/issues/1411 CMake: Add logic to enable the Rust openssl-sys / openssl-rs crates to build against the same OpenSSL library as is used for the C build. The Rust unit test application must also link directly with libcrypto and libssl. Fix some log messages with missing new lines. Fix missing environment variable notes in --help messages and manpages. Deconflict CONFDIR/DATADIR/CERTSDIR variable names that are defined in clamav-config.h.in for libclamav from variable that had the same name for use in clamav applications that use the optparser. The 'clamav-test' certs for the unit tests will live for 10 years. The 'clamav-beta.crt' public cert will only live for 120 days and will be replaced before the stable release with a production 'clamav.crt'.
2024-11-21 14:01:09 -05:00
// prune any CVD/CLD files that are not in the database list
2019-03-26 15:09:52 -04:00
if ((NULL != (extension = strstr(dent->d_name, ".cld"))) ||
(NULL != (extension = strstr(dent->d_name, ".cvd")))) {
FIPS-compliant CVD signing and verification Add X509 certificate chain based signing with PKCS7-PEM external signatures distributed alongside CVD's in a custom .cvd.sign format. This new signing and verification mechanism is primarily in support of FIPS compliance. Fixes: https://github.com/Cisco-Talos/clamav/issues/564 Add a Rust implementation for parsing, verifying, and unpacking CVD files. Now installs a 'certs' directory in the app config directory (e.g. <prefix>/etc/certs). The install location is configurable. The CMake option to configure the CVD certs directory is: `-D CVD_CERTS_DIRECTORY=PATH` New options to set an alternative CVD certs directory: - Commandline for freshclam, clamd, clamscan, and sigtool is: `--cvdcertsdir PATH` - Env variable for freshclam, clamd, clamscan, and sigtool is: `CVD_CERTS_DIR` - Config option for freshclam and clamd is: `CVDCertsDirectory PATH` Sigtool: - Add sign/verify commands. - Also verify CDIFF external digital signatures when applying CDIFFs. - Place commonly used commands at the top of --help string. - Fix up manpage. Freshclam: - Will try to download .sign files to verify CVDs and CDIFFs. - Fix an issue where making a CLD would only include the CFG file for daily and not if patching any other database. libclamav.so: - Bump version to 13:0:1 (aka 12.1.0). - Also remove libclamav.map versioning. Resolves: https://github.com/Cisco-Talos/clamav/issues/1304 - Add two new API's to the public clamav.h header: ```c extern cl_error_t cl_cvdverify_ex(const char *file, const char *certs_directory); extern cl_error_t cl_cvdunpack_ex(const char *file, const char *dir, bool dont_verify, const char *certs_directory); ``` The original `cl_cvdverify` and `cl_cvdunpack` are deprecated. - Add `cl_engine_field` enum option `CL_ENGINE_CVDCERTSDIR`. You may set this option with `cl_engine_set_str` and get it with `cl_engine_get_str`, to override the compiled in default CVD certs directory. libfreshclam.so: Bump version to 4:0:0 (aka 4.0.0). Add sigtool sign/verify tests and test certs. Make it so downloadFile doesn't throw a warning if the server doesn't have the .sign file. Replace use of md5-based FP signatures in the unit tests with sha256-based FP signatures because the md5 implementation used by Python may be disabled in FIPS mode. Fixes: https://github.com/Cisco-Talos/clamav/issues/1411 CMake: Add logic to enable the Rust openssl-sys / openssl-rs crates to build against the same OpenSSL library as is used for the C build. The Rust unit test application must also link directly with libcrypto and libssl. Fix some log messages with missing new lines. Fix missing environment variable notes in --help messages and manpages. Deconflict CONFDIR/DATADIR/CERTSDIR variable names that are defined in clamav-config.h.in for libclamav from variable that had the same name for use in clamav applications that use the optparser. The 'clamav-test' certs for the unit tests will live for 10 years. The 'clamav-beta.crt' public cert will only live for 120 days and will be replaced before the stable release with a production 'clamav.crt'.
2024-11-21 14:01:09 -05:00
// find the first '-' or '.' in the filename
// Use this to determine the database name.
// We need this so we can ALSO prune the .sign files for unwanted databases.
// Will also be useful in case the database filename includes a hyphenated version number.
const char *first_dash_or_dot = strchr(dent->d_name, '-');
if (NULL == first_dash_or_dot) {
first_dash_or_dot = extension;
}
2019-03-26 15:09:52 -04:00
uint32_t i;
int bFound = 0;
for (i = 0; i < nDatabases; i++) {
FIPS-compliant CVD signing and verification Add X509 certificate chain based signing with PKCS7-PEM external signatures distributed alongside CVD's in a custom .cvd.sign format. This new signing and verification mechanism is primarily in support of FIPS compliance. Fixes: https://github.com/Cisco-Talos/clamav/issues/564 Add a Rust implementation for parsing, verifying, and unpacking CVD files. Now installs a 'certs' directory in the app config directory (e.g. <prefix>/etc/certs). The install location is configurable. The CMake option to configure the CVD certs directory is: `-D CVD_CERTS_DIRECTORY=PATH` New options to set an alternative CVD certs directory: - Commandline for freshclam, clamd, clamscan, and sigtool is: `--cvdcertsdir PATH` - Env variable for freshclam, clamd, clamscan, and sigtool is: `CVD_CERTS_DIR` - Config option for freshclam and clamd is: `CVDCertsDirectory PATH` Sigtool: - Add sign/verify commands. - Also verify CDIFF external digital signatures when applying CDIFFs. - Place commonly used commands at the top of --help string. - Fix up manpage. Freshclam: - Will try to download .sign files to verify CVDs and CDIFFs. - Fix an issue where making a CLD would only include the CFG file for daily and not if patching any other database. libclamav.so: - Bump version to 13:0:1 (aka 12.1.0). - Also remove libclamav.map versioning. Resolves: https://github.com/Cisco-Talos/clamav/issues/1304 - Add two new API's to the public clamav.h header: ```c extern cl_error_t cl_cvdverify_ex(const char *file, const char *certs_directory); extern cl_error_t cl_cvdunpack_ex(const char *file, const char *dir, bool dont_verify, const char *certs_directory); ``` The original `cl_cvdverify` and `cl_cvdunpack` are deprecated. - Add `cl_engine_field` enum option `CL_ENGINE_CVDCERTSDIR`. You may set this option with `cl_engine_set_str` and get it with `cl_engine_get_str`, to override the compiled in default CVD certs directory. libfreshclam.so: Bump version to 4:0:0 (aka 4.0.0). Add sigtool sign/verify tests and test certs. Make it so downloadFile doesn't throw a warning if the server doesn't have the .sign file. Replace use of md5-based FP signatures in the unit tests with sha256-based FP signatures because the md5 implementation used by Python may be disabled in FIPS mode. Fixes: https://github.com/Cisco-Talos/clamav/issues/1411 CMake: Add logic to enable the Rust openssl-sys / openssl-rs crates to build against the same OpenSSL library as is used for the C build. The Rust unit test application must also link directly with libcrypto and libssl. Fix some log messages with missing new lines. Fix missing environment variable notes in --help messages and manpages. Deconflict CONFDIR/DATADIR/CERTSDIR variable names that are defined in clamav-config.h.in for libclamav from variable that had the same name for use in clamav applications that use the optparser. The 'clamav-test' certs for the unit tests will live for 10 years. The 'clamav-beta.crt' public cert will only live for 120 days and will be replaced before the stable release with a production 'clamav.crt'.
2024-11-21 14:01:09 -05:00
// check that the database name is in the database list
if (0 == strncmp(databaseList[i], dent->d_name, first_dash_or_dot - dent->d_name)) {
2019-03-26 15:09:52 -04:00
bFound = 1;
}
}
if (!bFound) {
/* Prune CVD/CLD */
mprintf(LOGG_INFO, "Pruning unwanted or deprecated database file %s.\n", dent->d_name);
if (unlink(dent->d_name)) {
mprintf(LOGG_ERROR, "Failed to prune unwanted database file %s, consider removing it manually.\n", dent->d_name);
2019-03-26 15:09:52 -04:00
status = FC_EDBDIRACCESS;
goto done;
}
}
}
}
}
2019-03-26 15:09:52 -04:00
status = FC_SUCCESS;
done:
if (NULL != dir) {
closedir(dir);
}
2019-03-26 15:09:52 -04:00
return status;
}
/**
* @brief Compare two version strings.
*
* @param v1 Version string 1
* @param v2 Version string 2
* @return int 1 if v1 is greater, 0 if equal, -1 if smaller.
*/
int version_string_compare(char *v1, size_t v1_len, char *v2, size_t v2_len)
{
size_t i, j;
int vnum1 = 0, vnum2 = 0;
for (i = 0, j = 0; (i < v1_len || j < v2_len);) {
while (i < v1_len && v1[i] != '.') {
vnum1 = vnum1 * 10 + (v1[i] - '0');
i++;
}
while (j < v2_len && v2[j] != '.') {
vnum2 = vnum2 * 10 + (v2[j] - '0');
j++;
}
if (vnum1 > vnum2)
return 1;
if (vnum2 > vnum1)
return -1;
vnum1 = vnum2 = 0;
i++;
j++;
}
return 0;
}
2019-03-26 15:09:52 -04:00
fc_error_t fc_test_database(const char *dbFilename, int bBytecodeEnabled)
{
2019-03-26 15:09:52 -04:00
fc_error_t status = FC_EARG;
struct cl_engine *engine = NULL;
unsigned newsigs = 0;
cl_error_t cl_ret;
FIPS & FIPS-like limits on hash algs for cryptographic uses ClamAV will not function when using a FIPS-enabled OpenSSL 3.x. This is because ClamAV uses MD5 and SHA1 algorithms for a variety of purposes including matching for malware detection, matching to prevent false positives on known-clean files, and for verification of MD5-based RSA digital signatures for determining CVD (signature database archive) authenticity. Interestingly, FIPS had been intentionally bypassed when creating hashes based whole buffers and whole files (by descriptor or `FILE`-pointer): https://github.com/Cisco-Talos/clamav/commit/78d4a9985a06a418dd1338c94ee5db461035d75b Note: this bypassed FIPS the 1.x way with: `EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);` It was NOT disabled when using `cl_hash_init()` / `cl_update_hash()` / `cl_finish_hash()`. That likely worked by coincidence in that the hash was already calculated most of the time. It certainly would have made use of those functions if the hash had not been calculated prior: https://github.com/Cisco-Talos/clamav/blob/78d4a9985a06a418dd1338c94ee5db461035d75b/libclamav/matcher.c#L743 Regardless, bypassing FIPS entirely is not the correct solution. The FIPS restrictions against using MD5 and SHA1 are valid, particularly when verifying CVD digital siganatures, but also I think when using a hash to determine if the file is known-clean (i.e. the "clean cache" and also MD5-based and SHA1-based FP signatures). This commit extends the work to bypass FIPS using the newer 3.x method: `md = EVP_MD_fetch(NULL, alg, "-fips");` It does this for the legacy `cl_hash*()` functions including `cl_hash_init()` / `cl_update_hash()` / `cl_finish_hash()`. It also introduces extended versions that allow the caller to choose if they want to bypass FIPS: - `cl_hash_data_ex()` - `cl_hash_init_ex()` - `cl_update_hash_ex()` - `cl_finish_hash_ex()` - `cl_hash_destroy_ex()` - `cl_hash_file_fd_ex()` See the `flags` parameter for each. Ironically, this commit does NOT use the new functions at this time. The rational is that ClamAV may need MD5, SHA1, and SHA-256 hashes of the same files both for determining if the file is malware, and for determining if the file is clean. So instead, this commit will do a checks when: 1. Creating a new ClamAV scanning engine. If FIPS-mode enabled, it will automatically toggle the "FIPS limits" engine option. When loading signatures, if the engine "FIPS limits" option is enabled, then MD5 and SHA1 FP signatures will be skipped. 2. Before verifying a CVD (e.g. also for loading, unpacking when verification enabled). If "FIPS limits" or FIPS-mode are enabled, then the legacy MD5-based RSA method is disabled. Note: This commit also refactors the interface for `cl_cvdverify_ex()` and `cl_cvdunpack_ex()` so they take a `flags` parameters, rather than a single `bool`. As these functions are new in this version, it does not break the ABI. The cache was already switched to use SHA2-256, so that's not a concern for checking FIPS-mode / FIPS limits options. This adds an option for `freshclam.conf` and `clamd.conf`: FIPSCryptoHashLimits yes And an equivalent command-line option for `clamscan` and `sigtool`: --fips-limits You may programmatically enable FIPS-limits for a ClamAV engine like this: ```C cl_engine_set_num(engine, CL_ENGINE_FIPS_LIMITS, 1); ``` CLAM-2792
2025-07-01 20:41:47 -04:00
unsigned int dboptions = 0;
2019-03-26 15:09:52 -04:00
if ((NULL == dbFilename)) {
logg(LOGG_WARNING, "fc_test_database: Invalid arguments.\n");
2019-03-26 15:09:52 -04:00
goto done;
}
logg(LOGG_DEBUG, "Loading signatures from %s\n", dbFilename);
2019-03-26 15:09:52 -04:00
if (NULL == (engine = cl_engine_new())) {
status = FC_ETESTFAIL;
goto done;
}
// Disable cache as testing the database doesn't need caching,
// having cache will only waste time and memory.
engine->engine_options |= ENGINE_OPTIONS_DISABLE_CACHE;
2019-03-26 15:09:52 -04:00
cl_engine_set_clcb_stats_submit(engine, NULL);
FIPS & FIPS-like limits on hash algs for cryptographic uses ClamAV will not function when using a FIPS-enabled OpenSSL 3.x. This is because ClamAV uses MD5 and SHA1 algorithms for a variety of purposes including matching for malware detection, matching to prevent false positives on known-clean files, and for verification of MD5-based RSA digital signatures for determining CVD (signature database archive) authenticity. Interestingly, FIPS had been intentionally bypassed when creating hashes based whole buffers and whole files (by descriptor or `FILE`-pointer): https://github.com/Cisco-Talos/clamav/commit/78d4a9985a06a418dd1338c94ee5db461035d75b Note: this bypassed FIPS the 1.x way with: `EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);` It was NOT disabled when using `cl_hash_init()` / `cl_update_hash()` / `cl_finish_hash()`. That likely worked by coincidence in that the hash was already calculated most of the time. It certainly would have made use of those functions if the hash had not been calculated prior: https://github.com/Cisco-Talos/clamav/blob/78d4a9985a06a418dd1338c94ee5db461035d75b/libclamav/matcher.c#L743 Regardless, bypassing FIPS entirely is not the correct solution. The FIPS restrictions against using MD5 and SHA1 are valid, particularly when verifying CVD digital siganatures, but also I think when using a hash to determine if the file is known-clean (i.e. the "clean cache" and also MD5-based and SHA1-based FP signatures). This commit extends the work to bypass FIPS using the newer 3.x method: `md = EVP_MD_fetch(NULL, alg, "-fips");` It does this for the legacy `cl_hash*()` functions including `cl_hash_init()` / `cl_update_hash()` / `cl_finish_hash()`. It also introduces extended versions that allow the caller to choose if they want to bypass FIPS: - `cl_hash_data_ex()` - `cl_hash_init_ex()` - `cl_update_hash_ex()` - `cl_finish_hash_ex()` - `cl_hash_destroy_ex()` - `cl_hash_file_fd_ex()` See the `flags` parameter for each. Ironically, this commit does NOT use the new functions at this time. The rational is that ClamAV may need MD5, SHA1, and SHA-256 hashes of the same files both for determining if the file is malware, and for determining if the file is clean. So instead, this commit will do a checks when: 1. Creating a new ClamAV scanning engine. If FIPS-mode enabled, it will automatically toggle the "FIPS limits" engine option. When loading signatures, if the engine "FIPS limits" option is enabled, then MD5 and SHA1 FP signatures will be skipped. 2. Before verifying a CVD (e.g. also for loading, unpacking when verification enabled). If "FIPS limits" or FIPS-mode are enabled, then the legacy MD5-based RSA method is disabled. Note: This commit also refactors the interface for `cl_cvdverify_ex()` and `cl_cvdunpack_ex()` so they take a `flags` parameters, rather than a single `bool`. As these functions are new in this version, it does not break the ABI. The cache was already switched to use SHA2-256, so that's not a concern for checking FIPS-mode / FIPS limits options. This adds an option for `freshclam.conf` and `clamd.conf`: FIPSCryptoHashLimits yes And an equivalent command-line option for `clamscan` and `sigtool`: --fips-limits You may programmatically enable FIPS-limits for a ClamAV engine like this: ```C cl_engine_set_num(engine, CL_ENGINE_FIPS_LIMITS, 1); ``` CLAM-2792
2025-07-01 20:41:47 -04:00
dboptions = CL_DB_PHISHING | CL_DB_PHISHING_URLS | CL_DB_BYTECODE | CL_DB_PUA | CL_DB_ENHANCED;
if (g_bFipsLimits) {
dboptions |= CL_DB_FIPS_LIMITS;
}
2019-03-26 15:09:52 -04:00
if (CL_SUCCESS != (cl_ret = cl_load(
FIPS & FIPS-like limits on hash algs for cryptographic uses ClamAV will not function when using a FIPS-enabled OpenSSL 3.x. This is because ClamAV uses MD5 and SHA1 algorithms for a variety of purposes including matching for malware detection, matching to prevent false positives on known-clean files, and for verification of MD5-based RSA digital signatures for determining CVD (signature database archive) authenticity. Interestingly, FIPS had been intentionally bypassed when creating hashes based whole buffers and whole files (by descriptor or `FILE`-pointer): https://github.com/Cisco-Talos/clamav/commit/78d4a9985a06a418dd1338c94ee5db461035d75b Note: this bypassed FIPS the 1.x way with: `EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);` It was NOT disabled when using `cl_hash_init()` / `cl_update_hash()` / `cl_finish_hash()`. That likely worked by coincidence in that the hash was already calculated most of the time. It certainly would have made use of those functions if the hash had not been calculated prior: https://github.com/Cisco-Talos/clamav/blob/78d4a9985a06a418dd1338c94ee5db461035d75b/libclamav/matcher.c#L743 Regardless, bypassing FIPS entirely is not the correct solution. The FIPS restrictions against using MD5 and SHA1 are valid, particularly when verifying CVD digital siganatures, but also I think when using a hash to determine if the file is known-clean (i.e. the "clean cache" and also MD5-based and SHA1-based FP signatures). This commit extends the work to bypass FIPS using the newer 3.x method: `md = EVP_MD_fetch(NULL, alg, "-fips");` It does this for the legacy `cl_hash*()` functions including `cl_hash_init()` / `cl_update_hash()` / `cl_finish_hash()`. It also introduces extended versions that allow the caller to choose if they want to bypass FIPS: - `cl_hash_data_ex()` - `cl_hash_init_ex()` - `cl_update_hash_ex()` - `cl_finish_hash_ex()` - `cl_hash_destroy_ex()` - `cl_hash_file_fd_ex()` See the `flags` parameter for each. Ironically, this commit does NOT use the new functions at this time. The rational is that ClamAV may need MD5, SHA1, and SHA-256 hashes of the same files both for determining if the file is malware, and for determining if the file is clean. So instead, this commit will do a checks when: 1. Creating a new ClamAV scanning engine. If FIPS-mode enabled, it will automatically toggle the "FIPS limits" engine option. When loading signatures, if the engine "FIPS limits" option is enabled, then MD5 and SHA1 FP signatures will be skipped. 2. Before verifying a CVD (e.g. also for loading, unpacking when verification enabled). If "FIPS limits" or FIPS-mode are enabled, then the legacy MD5-based RSA method is disabled. Note: This commit also refactors the interface for `cl_cvdverify_ex()` and `cl_cvdunpack_ex()` so they take a `flags` parameters, rather than a single `bool`. As these functions are new in this version, it does not break the ABI. The cache was already switched to use SHA2-256, so that's not a concern for checking FIPS-mode / FIPS limits options. This adds an option for `freshclam.conf` and `clamd.conf`: FIPSCryptoHashLimits yes And an equivalent command-line option for `clamscan` and `sigtool`: --fips-limits You may programmatically enable FIPS-limits for a ClamAV engine like this: ```C cl_engine_set_num(engine, CL_ENGINE_FIPS_LIMITS, 1); ``` CLAM-2792
2025-07-01 20:41:47 -04:00
dbFilename,
engine,
&newsigs,
dboptions))) {
logg(LOGG_ERROR, "Failed to load new database: %s\n", cl_strerror(cl_ret));
2019-03-26 15:09:52 -04:00
status = FC_ETESTFAIL;
goto done;
}
2019-03-26 15:09:52 -04:00
if (bBytecodeEnabled && (CL_SUCCESS != (cl_ret = cli_bytecode_prepare2(
engine, &engine->bcs,
engine->dconf->bytecode
/*FIXME: dconf has no sense here */)))) {
logg(LOGG_ERROR, "Failed to compile/load bytecode: %s\n", cl_strerror(cl_ret));
2019-03-26 15:09:52 -04:00
status = FC_ETESTFAIL;
goto done;
}
logg(LOGG_DEBUG, "Properly loaded %u signatures from %s\n", newsigs, dbFilename);
2019-03-26 15:09:52 -04:00
status = FC_SUCCESS;
2019-03-26 15:09:52 -04:00
done:
2019-03-26 15:09:52 -04:00
if (NULL != engine) {
Swap clean cache from MD5 to SHA2-256 Change the clean-cache to use SHA2-256 instead of MD5. Note that all references are changed to specify "SHA2-256" now instead of "SHA256", for clarity. But there is no plan to add support for SHA3 algorithms at this time. Significant code cleanup. E.g.: - Implemented goto-done error handling. - Used `uint8_t *` instead of `unsigned char *`. - Use `bool` for boolean checks, rather than `int. - Used `#defines` instead of magic numbers. - Removed duplicate `#defines` for things like hash length. Add new option to calculate and record additional hash types when the "generate metadata JSON" feature is enabled: - libclamav option: `CL_SCAN_GENERAL_STORE_EXTRA_HASHES` - clamscan option: `--json-store-extra-hashes` (default off) - clamd.conf option: `JsonStoreExtraHashes` (default 'no') Renamed the sigtool option `--sha256` to `--sha2-256`. The original option is still functional, but is deprecated. For the "generate metadata JSON" feature, the file hash is now stored as "sha2-256" instead of "FileMD5". If you enable the "extra hashes" option, then it will also record "md5" and "sha1". Deprecate and disable the internal "SHA collect" feature. This option had been hidden behind C #ifdef checks for an option that wasn't exposed through CMake, so it was basically unavailable anyways. Changes to calculate file hashes when they're needed and no sooner. For the FP feature in the matcher module, I have mimiced the optimization in the FMAP scan routine which makes it so that it can calculate multiple hashes in a single pass of the file. The `HandlerType` feature stores a hash of the file in the scan ctx to prevent retyping the exact same data more than once. I removed that hash field and replaced it with an attribute flag that is applied to the new recursion stack layer when retyping a file. This also closes a minor bug that would prevent retyping a file with an all-zero hash. :) The work upgrading cache.c to support SHA2-256 sized hashes thanks to: https://github.com/m-sola CLAM-255 CLAM-1858 CLAM-1859 CLAM-1860
2025-06-03 19:03:20 -04:00
if (engine->domain_list_matcher && engine->domain_list_matcher->sha2_256_pfx_set.keys)
cli_hashset_destroy(&engine->domain_list_matcher->sha2_256_pfx_set);
2019-03-26 15:09:52 -04:00
cl_engine_free(engine);
}
2019-03-26 15:09:52 -04:00
return status;
}
2019-03-26 15:09:52 -04:00
fc_error_t fc_dns_query_update_info(
const char *dnsUpdateInfoServer,
char **dnsUpdateInfo,
char **newVersion)
{
fc_error_t status = FC_EFAILEDGET;
char *dnsReply = NULL;
#ifdef HAVE_RESOLV_H
unsigned int ttl;
char *reply_token = NULL;
int recordTime;
time_t currentTime;
int vwarning = 1;
char version_string[32];
#endif /* HAVE_RESOLV_H */
if ((NULL == dnsUpdateInfo) || (NULL == newVersion)) {
logg(LOGG_WARNING, "dns_query_update_info: Invalid arguments.\n");
2019-03-26 15:09:52 -04:00
status = FC_EARG;
goto done;
}
2019-03-26 15:09:52 -04:00
*dnsUpdateInfo = NULL;
*newVersion = NULL;
2019-03-26 15:09:52 -04:00
#ifdef HAVE_RESOLV_H
if (dnsUpdateInfoServer == NULL) {
logg(LOGG_WARNING, "DNS Update Info disabled. Falling back to HTTP mode.\n");
2019-03-26 15:09:52 -04:00
goto done;
}
if (NULL == (dnsReply = dnsquery(dnsUpdateInfoServer, T_TXT, &ttl))) {
logg(LOGG_WARNING, "Invalid DNS reply. Falling back to HTTP mode.\n");
2019-03-26 15:09:52 -04:00
goto done;
}
logg(LOGG_DEBUG, "TTL: %d\n", ttl);
2019-03-26 15:09:52 -04:00
/*
* Check Record Time.
*/
if (NULL == (reply_token = cli_strtok(dnsReply, DNS_UPDATEINFO_RECORDTIME, ":"))) {
logg(LOGG_WARNING, "Failed to find Record Time field in DNS Update Info.\n");
2019-03-26 15:09:52 -04:00
goto done;
}
recordTime = atoi(reply_token);
free(reply_token);
reply_token = NULL;
time(&currentTime);
if ((int)currentTime - recordTime > DNS_WARNING_THRESHOLD_SECONDS) {
logg(LOGG_WARNING, "DNS record is older than %d hours.\n", DNS_WARNING_THRESHOLD_HOURS);
2019-03-26 15:09:52 -04:00
goto done;
}
/*
* Check Version Warning Flag.
*/
if (NULL == (reply_token = cli_strtok(dnsReply, DNS_UPDATEINFO_VERSIONWARNING, ":"))) {
logg(LOGG_WARNING, "Failed to find Version Warning Flag in DNS Update Info.\n");
2019-03-26 15:09:52 -04:00
goto done;
}
if (*reply_token == '0')
vwarning = 0;
free(reply_token);
reply_token = NULL;
/*
* Check the latest available ClamAV software version.
*/
if (NULL == (reply_token = cli_strtok(dnsReply, DNS_UPDATEINFO_NEWVERSION, ":"))) {
logg(LOGG_WARNING, "Failed to find New Version field in DNS Update Info.\n");
2019-03-26 15:09:52 -04:00
goto done;
}
logg(LOGG_DEBUG, "fc_dns_query_update_info: Software version from DNS: %s\n", reply_token);
2019-03-26 15:09:52 -04:00
/*
* Compare the latest available ClamAV version with this ClamAV version.
* Only throw a warning if the Version Warning Flag was set,
* and this is not a beta, release candidate, or development version.
*/
strncpy(version_string, get_version(), sizeof(version_string));
version_string[31] = 0;
if (vwarning) {
if (!strstr(version_string, "devel") &&
!strstr(version_string, "beta") &&
!strstr(version_string, "rc")) {
char *suffix = strchr(version_string, '-');
if ((suffix && (0 > version_string_compare(version_string, suffix - version_string, reply_token, strlen(reply_token)))) ||
(!suffix && (0 > version_string_compare(version_string, strlen(version_string), reply_token, strlen(reply_token))))) {
2019-03-26 15:09:52 -04:00
logg(LOGG_WARNING, "Your ClamAV installation is OUTDATED!\n");
logg(LOGG_WARNING, "Local version: %s Recommended version: %s\n", version_string, reply_token);
logg(LOGG_INFO, "DON'T PANIC! Read https://docs.clamav.net/manual/Installing.html\n");
*newVersion = cli_safer_strdup(reply_token);
}
}
2019-03-26 15:09:52 -04:00
}
2019-03-26 15:09:52 -04:00
free(reply_token);
reply_token = NULL;
*dnsUpdateInfo = dnsReply;
status = FC_SUCCESS;
#endif /* HAVE_RESOLV_H */
done:
if (FC_SUCCESS != status) {
free(dnsReply);
}
2019-03-26 15:09:52 -04:00
return status;
}
fc_error_t fc_update_database(
const char *database,
char **serverList,
uint32_t nServers,
int bPrivateMirror,
const char *dnsUpdateInfo,
int bScriptedUpdates,
void *context,
int *bUpdated)
{
fc_error_t ret;
fc_error_t status = FC_EARG;
char *dbFilename = NULL;
2020-01-03 15:53:29 -05:00
int signo = 0;
long attempt = 1;
2019-03-26 15:09:52 -04:00
uint32_t i;
if ((NULL == database) || (NULL == serverList) || (NULL == bUpdated)) {
logg(LOGG_WARNING, "fc_update_database: Invalid arguments.\n");
2019-03-26 15:09:52 -04:00
goto done;
}
*bUpdated = 0;
/* Change directory to database directory */
if (chdir(g_databaseDirectory)) {
logg(LOGG_ERROR, "Can't change dir to %s\n", g_databaseDirectory);
2019-03-26 15:09:52 -04:00
status = FC_EDIRECTORY;
goto done;
}
logg(LOGG_DEBUG, "Current working dir is %s\n", g_databaseDirectory);
2019-03-26 15:09:52 -04:00
/*
* Attempt to update official database using DatabaseMirrors or PrivateMirrors.
*/
for (i = 0; i < nServers; i++) {
for (attempt = 1; attempt <= g_maxAttempts; attempt++) {
ret = updatedb(
database,
dnsUpdateInfo,
serverList[i],
bPrivateMirror,
context,
bScriptedUpdates,
attempt == g_maxAttempts ? 1 : 0,
&signo,
&dbFilename,
bUpdated);
switch (ret) {
case FC_SUCCESS: {
if (*bUpdated) {
logg(LOGG_DEBUG, "fc_update_database: %s updated.\n", dbFilename);
2019-03-26 15:09:52 -04:00
} else {
logg(LOGG_DEBUG, "fc_update_database: %s already up-to-date.\n", dbFilename);
2019-03-26 15:09:52 -04:00
}
goto success;
}
case FC_ECONNECTION:
case FC_EBADCVD:
case FC_EFAILEDGET: {
2019-03-26 15:09:52 -04:00
if (attempt < g_maxAttempts) {
logg(LOGG_INFO, "Trying again in 5 secs...\n");
2019-03-26 15:09:52 -04:00
sleep(5);
} else {
logg(LOGG_INFO, "Giving up on %s...\n", serverList[i]);
2019-03-26 15:09:52 -04:00
if (i == nServers - 1) {
logg(LOGG_ERROR, "Update failed for database: %s\n", database);
2019-03-26 15:09:52 -04:00
status = ret;
goto done;
}
}
2019-05-21 17:40:45 -04:00
break;
2019-03-26 15:09:52 -04:00
}
case FC_EMIRRORNOTSYNC: {
logg(LOGG_INFO, "Received an older %s CVD than was advertised. We'll retry so the incremental update will ensure we're up-to-date.\n", database);
break;
}
case FC_EFORBIDDEN: {
char retry_after_string[26];
struct tm *tm_info;
tm_info = localtime(&g_freshclamDat->retry_after);
if (NULL == tm_info) {
logg(LOGG_ERROR, "Failed to query the local time for the retry-after date!\n");
status = FC_ERROR;
goto done;
}
strftime(retry_after_string, 26, "%Y-%m-%d %H:%M:%S", tm_info);
logg(LOGG_WARNING, "FreshClam received error code 403 from the ClamAV Content Delivery Network (CDN).\n");
logg(LOGG_INFO, "This could mean several things:\n");
logg(LOGG_INFO, " 1. You are running an out-of-date version of ClamAV / FreshClam.\n");
logg(LOGG_INFO, " Ensure you are the most updated version by visiting https://www.clamav.net/downloads\n");
logg(LOGG_INFO, " 2. Your network is explicitly denied by the FreshClam CDN.\n");
logg(LOGG_INFO, " In order to rectify this please check that you are:\n");
logg(LOGG_INFO, " a. Running an up-to-date version of FreshClam\n");
logg(LOGG_INFO, " b. Running FreshClam no more than once an hour\n");
logg(LOGG_INFO, " c. Connecting from an IP in a blocked region\n");
logg(LOGG_INFO, " Please see https://www.cisco.com/c/m/en_us/crisissupport.html\n");
logg(LOGG_INFO, " d. If you have checked (a), (b) and (c), please open a ticket at\n");
logg(LOGG_INFO, " https://github.com/Cisco-Talos/clamav/issues\n");
logg(LOGG_INFO, " and we will investigate why your network is blocked.\n");
if (0 != g_lastRay[0]) {
logg(LOGG_INFO, " Please provide the following cf-ray id with your ticket: %s\n", g_lastRay);
logg(LOGG_INFO, "\n");
}
logg(LOGG_WARNING, "You are on cool-down until after: %s\n", retry_after_string);
status = ret;
goto done;
break;
}
case FC_ERETRYLATER: {
char retry_after_string[26];
struct tm *tm_info;
tm_info = localtime(&g_freshclamDat->retry_after);
if (NULL == tm_info) {
logg(LOGG_ERROR, "Failed to query the local time for the retry-after date!\n");
status = FC_ERROR;
goto done;
}
strftime(retry_after_string, 26, "%Y-%m-%d %H:%M:%S", tm_info);
logg(LOGG_WARNING, "FreshClam received error code 429 from the ClamAV Content Delivery Network (CDN).\n");
logg(LOGG_INFO, "This means that you have been rate limited by the CDN.\n");
logg(LOGG_INFO, " 1. Run FreshClam no more than once an hour to check for updates.\n");
logg(LOGG_INFO, " FreshClam should check DNS first to see if an update is needed.\n");
logg(LOGG_INFO, " 2. If you have more than 10 hosts on your network attempting to download,\n");
logg(LOGG_INFO, " it is recommended that you set up a private mirror on your network using\n");
logg(LOGG_INFO, " cvdupdate (https://pypi.org/project/cvdupdate/) to save bandwidth on the\n");
logg(LOGG_INFO, " CDN and your own network.\n");
logg(LOGG_INFO, " 3. Please do not open a ticket asking for an exemption from the rate limit,\n");
logg(LOGG_INFO, " it will not be granted.\n");
logg(LOGG_WARNING, "You are on cool-down until after: %s\n", retry_after_string);
goto success;
break;
}
2019-03-26 15:09:52 -04:00
default: {
logg(LOGG_ERROR, "Unexpected error when attempting to update %s: %s\n", database, fc_strerror(ret));
2019-03-26 15:09:52 -04:00
status = ret;
goto done;
}
}
}
2019-03-26 15:09:52 -04:00
}
2019-03-26 15:09:52 -04:00
success:
status = FC_SUCCESS;
done:
if (NULL != dbFilename) {
free(dbFilename);
}
return status;
}
fc_error_t fc_update_databases(
char **databaseList,
uint32_t nDatabases,
char **serverList,
uint32_t nServers,
int bPrivateMirror,
const char *dnsUpdateInfo,
int bScriptedUpdates,
void *context,
uint32_t *nUpdated)
{
fc_error_t ret;
fc_error_t status = FC_EARG;
uint32_t i;
int bUpdated = 0;
uint32_t numUpdated = 0;
if ((NULL == databaseList) || (0 == nDatabases) || (NULL == serverList) || (NULL == nUpdated)) {
logg(LOGG_WARNING, "fc_update_databases: Invalid arguments.\n");
2019-03-26 15:09:52 -04:00
goto done;
}
2019-03-26 15:09:52 -04:00
*nUpdated = 0;
if (g_freshclamDat->retry_after > 0) {
if (g_freshclamDat->retry_after > time(NULL)) {
/* We're on cool-down, try again later. */
char retry_after_string[26];
struct tm *tm_info;
tm_info = localtime(&g_freshclamDat->retry_after);
if (NULL == tm_info) {
logg(LOGG_ERROR, "Failed to query the local time for the retry-after date!\n");
status = FC_ERROR;
goto done;
}
strftime(retry_after_string, 26, "%Y-%m-%d %H:%M:%S", tm_info);
logg(LOGG_WARNING, "FreshClam previously received error code 429 or 403 from the ClamAV Content Delivery Network (CDN).\n");
logg(LOGG_INFO, "This means that you have been rate limited or blocked by the CDN.\n");
logg(LOGG_INFO, " 1. Verify that you're running a supported ClamAV version.\n");
logg(LOGG_INFO, " See https://docs.clamav.net/faq/faq-eol.html for details.\n");
logg(LOGG_INFO, " 2. Run FreshClam no more than once an hour to check for updates.\n");
logg(LOGG_INFO, " FreshClam should check DNS first to see if an update is needed.\n");
logg(LOGG_INFO, " 3. If you have more than 10 hosts on your network attempting to download,\n");
logg(LOGG_INFO, " it is recommended that you set up a private mirror on your network using\n");
logg(LOGG_INFO, " cvdupdate (https://pypi.org/project/cvdupdate/) to save bandwidth on the\n");
logg(LOGG_INFO, " CDN and your own network.\n");
logg(LOGG_INFO, " 4. Please do not open a ticket asking for an exemption from the rate limit,\n");
logg(LOGG_INFO, " it will not be granted.\n");
if (0 != g_lastRay[0]) {
logg(LOGG_INFO, " 5. If you have verified that you are not blocked due to your region, and have\n");
logg(LOGG_INFO, " not exceeded the rate limit, please provide the following cf-ray id when\n");
logg(LOGG_INFO, " submitting a ticket: %s\n", g_lastRay);
logg(LOGG_INFO, "\n");
}
logg(LOGG_WARNING, "You are still on cool-down until after: %s\n", retry_after_string);
status = FC_SUCCESS;
goto done;
} else {
g_freshclamDat->retry_after = 0;
logg(LOGG_WARNING, "Cool-down expired, ok to try again.\n");
save_freshclam_dat();
}
}
/* Clear the old cf-ray ids. This is really only so that
* we don't have stale ones when we are running in daemon mode. */
memset(&g_lastRay, 0, sizeof(g_lastRay));
2019-03-26 15:09:52 -04:00
for (i = 0; i < nDatabases; i++) {
if (FC_SUCCESS != (ret = fc_update_database(
databaseList[i],
serverList,
nServers,
bPrivateMirror,
dnsUpdateInfo,
bScriptedUpdates,
context,
&bUpdated))) {
status = ret;
goto done;
}
2019-03-26 15:09:52 -04:00
if (bUpdated) {
numUpdated++;
}
}
*nUpdated = numUpdated;
status = FC_SUCCESS;
done:
return status;
}
fc_error_t fc_download_url_database(
const char *urlDatabase,
void *context,
int *bUpdated)
{
fc_error_t ret;
fc_error_t status = FC_EARG;
long attempt = 1;
char *dbFilename = NULL;
if ((NULL == urlDatabase) || (NULL == bUpdated)) {
logg(LOGG_WARNING, "fc_download_url_database: Invalid arguments.\n");
2019-03-26 15:09:52 -04:00
goto done;
}
*bUpdated = 0;
2019-03-26 15:09:52 -04:00
/* Change directory to database directory */
if (chdir(g_databaseDirectory)) {
logg(LOGG_ERROR, "Can't change dir to %s\n", g_databaseDirectory);
2019-03-26 15:09:52 -04:00
status = FC_EDIRECTORY;
goto done;
}
logg(LOGG_DEBUG, "Current working dir is %s\n", g_databaseDirectory);
2019-03-26 15:09:52 -04:00
/*
* Attempt to update official database using DatabaseMirrors or PrivateMirrors.
*/
for (attempt = 1; attempt <= g_maxAttempts; attempt++) {
int signo = 0;
ret = updatecustomdb(
urlDatabase,
context,
attempt == g_maxAttempts ? 1 : 0,
&signo,
&dbFilename,
bUpdated);
switch (ret) {
case FC_SUCCESS: {
if (*bUpdated) {
logg(LOGG_DEBUG, "fc_download_url_database: %s updated.\n", dbFilename);
2019-03-26 15:09:52 -04:00
} else {
logg(LOGG_DEBUG, "fc_download_url_database: %s already up-to-date.\n", dbFilename);
2019-03-26 15:09:52 -04:00
}
goto success;
}
2019-03-26 15:09:52 -04:00
case FC_ECONNECTION:
case FC_EBADCVD:
case FC_EFAILEDGET: {
if (attempt < g_maxAttempts) {
logg(LOGG_INFO, "Trying again in 5 secs...\n");
2019-03-26 15:09:52 -04:00
sleep(5);
} else {
logg(LOGG_INFO, "Update failed for custom database URL: %s\n", urlDatabase);
2019-03-26 15:09:52 -04:00
status = ret;
goto done;
}
2019-05-21 17:40:45 -04:00
break;
}
case FC_EFORBIDDEN: {
char retry_after_string[26];
struct tm *tm_info;
tm_info = localtime(&g_freshclamDat->retry_after);
if (NULL == tm_info) {
logg(LOGG_ERROR, "Failed to query the local time for the retry-after date!\n");
status = FC_ERROR;
goto done;
}
strftime(retry_after_string, 26, "%Y-%m-%d %H:%M:%S", tm_info);
logg(LOGG_WARNING, "FreshClam received error code 403 from the ClamAV Content Delivery Network (CDN).\n");
logg(LOGG_INFO, "This could mean several things:\n");
logg(LOGG_INFO, " 1. You are running an out-of-date version of ClamAV / FreshClam.\n");
logg(LOGG_INFO, " Ensure you are the most updated version by visiting https://www.clamav.net/downloads\n");
logg(LOGG_INFO, " 2. Your network is explicitly denied by the FreshClam CDN.\n");
logg(LOGG_INFO, " In order to rectify this please check that you are:\n");
logg(LOGG_INFO, " a. Running an up-to-date version of FreshClam\n");
logg(LOGG_INFO, " b. Running FreshClam no more than once an hour\n");
logg(LOGG_INFO, " c. If you have checked (a) and (b), please open a ticket at\n");
logg(LOGG_INFO, " https://github.com/Cisco-Talos/clamav/issues\n");
logg(LOGG_INFO, " and we will investigate why your network is blocked.\n");
if (0 != g_lastRay[0]) {
logg(LOGG_INFO, " Please provide the following cf-ray id with your ticket: %s\n", g_lastRay);
logg(LOGG_INFO, "\n");
}
logg(LOGG_WARNING, "You are on cool-down until after: %s\n", retry_after_string);
status = ret;
goto done;
break;
}
case FC_ERETRYLATER: {
char retry_after_string[26];
struct tm *tm_info;
tm_info = localtime(&g_freshclamDat->retry_after);
if (NULL == tm_info) {
logg(LOGG_ERROR, "Failed to query the local time for the retry-after date!\n");
status = FC_ERROR;
goto done;
}
strftime(retry_after_string, 26, "%Y-%m-%d %H:%M:%S", tm_info);
logg(LOGG_WARNING, "FreshClam received error code 429 from the ClamAV Content Delivery Network (CDN).\n");
logg(LOGG_INFO, "This means that you have been rate limited by the CDN.\n");
logg(LOGG_INFO, " 1. Run FreshClam no more than once an hour to check for updates.\n");
logg(LOGG_INFO, " FreshClam should check DNS first to see if an update is needed.\n");
logg(LOGG_INFO, " 2. If you have more than 10 hosts on your network attempting to download,\n");
logg(LOGG_INFO, " it is recommended that you set up a private mirror on your network using\n");
logg(LOGG_INFO, " cvdupdate (https://pypi.org/project/cvdupdate/) to save bandwidth on the\n");
logg(LOGG_INFO, " CDN and your own network.\n");
logg(LOGG_INFO, " 3. Please do not open a ticket asking for an exemption from the rate limit,\n");
logg(LOGG_INFO, " it will not be granted.\n");
logg(LOGG_WARNING, "You are on cool-down until after: %s\n", retry_after_string);
goto success;
break;
}
2019-03-26 15:09:52 -04:00
default: {
logg(LOGG_INFO, "Unexpected error when attempting to update from custom database URL: %s\n", urlDatabase);
2019-03-26 15:09:52 -04:00
status = ret;
goto done;
}
}
}
success:
status = FC_SUCCESS;
done:
if (NULL != dbFilename) {
free(dbFilename);
}
return status;
}
fc_error_t fc_download_url_databases(
char **urlDatabaseList,
uint32_t nUrlDatabases,
void *context,
uint32_t *nUpdated)
{
fc_error_t ret;
fc_error_t status = FC_EARG;
int bUpdated = 0;
uint32_t numUpdated = 0;
uint32_t i;
if ((NULL == urlDatabaseList) || (0 == nUrlDatabases) || (NULL == nUpdated)) {
logg(LOGG_WARNING, "fc_download_url_databases: Invalid arguments.\n");
2019-03-26 15:09:52 -04:00
goto done;
}
2019-03-26 15:09:52 -04:00
*nUpdated = 0;
2019-03-26 15:09:52 -04:00
for (i = 0; i < nUrlDatabases; i++) {
if (FC_SUCCESS != (ret = fc_download_url_database(
urlDatabaseList[i],
context,
&bUpdated))) {
logg(LOGG_WARNING, "fc_download_url_databases: fc_download_url_database failed: %s (%d)\n", fc_strerror(ret), ret);
2019-03-26 15:09:52 -04:00
status = ret;
goto done;
}
if (bUpdated) {
numUpdated++;
}
}
2019-03-26 15:09:52 -04:00
*nUpdated = numUpdated;
status = FC_SUCCESS;
done:
2019-03-26 15:09:52 -04:00
return status;
}
void fc_set_fccb_download_complete(fccb_download_complete callback)
{
g_cb_download_complete = callback;
}