mirror of
https://github.com/Cisco-Talos/clamav.git
synced 2025-10-19 10:23:17 +00:00
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):78d4a9985a
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:78d4a9985a/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
This commit is contained in:
parent
51adfb8b61
commit
13c4788f36
33 changed files with 1305 additions and 239 deletions
7
NEWS.md
7
NEWS.md
|
@ -47,12 +47,13 @@ ClamAV 1.5.0 includes the following improvements and changes:
|
|||
Added two new APIs to the public clamav.h header:
|
||||
```c
|
||||
extern cl_error_t cl_cvdverify_ex(const char *file,
|
||||
const char *certs_directory);
|
||||
const char *certs_directory,
|
||||
uint32_t dboptions);
|
||||
|
||||
extern cl_error_t cl_cvdunpack_ex(const char *file,
|
||||
const char *dir,
|
||||
bool dont_verify,
|
||||
const char *certs_directory);
|
||||
const char *certs_directory,
|
||||
uint32_t dboptions);
|
||||
```
|
||||
The original `cl_cvdverify` and `cl_cvdunpack` are deprecated.
|
||||
|
||||
|
|
|
@ -709,6 +709,12 @@ int main(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
if (optget(opts, "FIPSCryptoHashLimits")->enabled) {
|
||||
dboptions |= CL_DB_FIPS_LIMITS;
|
||||
cl_engine_set_num(engine, CL_ENGINE_FIPS_LIMITS, 1);
|
||||
logg(LOGG_INFO_NF, "FIPS crypto hash limits enabled.\n");
|
||||
}
|
||||
|
||||
if ((ret = cl_load(dbdir, engine, &sigs, dboptions))) {
|
||||
logg(LOGG_ERROR, "%s\n", cl_strerror(ret));
|
||||
ret = 1;
|
||||
|
|
|
@ -379,6 +379,10 @@ void help(void)
|
|||
mprintf(LOGG_INFO, " --cvdcertsdir=DIRECTORY Specify a directory containing the root\n");
|
||||
mprintf(LOGG_INFO, " CA cert needed to verify detached CVD digital signatures.\n");
|
||||
mprintf(LOGG_INFO, " If not provided, then clamscan will look in the default directory.\n");
|
||||
mprintf(LOGG_INFO, " --fips-limits Enforce FIPS-like limits on using hash algorithms for\n");
|
||||
mprintf(LOGG_INFO, " cryptographic purposes. Will disable MD5 & SHA1\n");
|
||||
mprintf(LOGG_INFO, " FP sigs and will require '.sign' files to verify CVD\n");
|
||||
mprintf(LOGG_INFO, " authenticity.\n");
|
||||
mprintf(LOGG_INFO, "\n");
|
||||
mprintf(LOGG_INFO, "Environment Variables:\n");
|
||||
mprintf(LOGG_INFO, "\n");
|
||||
|
|
|
@ -1361,6 +1361,11 @@ int scanmanager(const struct optstruct *opts)
|
|||
}
|
||||
}
|
||||
|
||||
if (optget(opts, "fips-limits")->enabled) {
|
||||
dboptions |= CL_DB_FIPS_LIMITS;
|
||||
cl_engine_set_num(engine, CL_ENGINE_FIPS_LIMITS, 1);
|
||||
}
|
||||
|
||||
if (optget(opts, "gen-json")->enabled) {
|
||||
options.general |= CL_SCAN_GENERAL_COLLECT_METADATA;
|
||||
}
|
||||
|
|
|
@ -389,6 +389,8 @@ const struct clam_option __clam_options[] = {
|
|||
|
||||
{"User", NULL, 0, CLOPT_TYPE_STRING, NULL, -1, NULL, 0, OPT_CLAMD | OPT_MILTER, "Run the daemon as a specified user (the process must be started by root).", "clamav"},
|
||||
|
||||
{"FIPSCryptoHashLimits", "fips-limits", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_FRESHCLAM | OPT_CLAMSCAN | OPT_CLAMD | OPT_SIGTOOL | OPT_CLAMONACC, "Apply FIPS-like limitations on which hash algorithms may be used for cryptographic purposes. This essentially disables the legacy CVD digital signature verfication method, and also disables support for MD5 and SHA1 false positive signatures ('.fp' and '.sfp' signatures using MD5 or SHA1).", "yes"},
|
||||
|
||||
/* Scan options */
|
||||
{"Bytecode", "bytecode", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 1, NULL, 0, OPT_CLAMD | OPT_CLAMSCAN, "With this option enabled ClamAV will load bytecode from the database. It is highly recommended you keep this option on, otherwise you'll miss detections for many new viruses.", "yes"},
|
||||
|
||||
|
|
|
@ -102,6 +102,11 @@ Path to a directory containing ClamAV CA certificate files used to verify signed
|
|||
.br
|
||||
Default: @CERTSDIR@
|
||||
.TP
|
||||
\fBFIPSCryptoHashLimits BOOL\fR
|
||||
Enforce FIPS\-like limits on using hash algorithms for cryptographic purposes. Will disable MD5 & SHA1 FP sigs and will require '.sign' files to verify CVD authenticity.
|
||||
.br
|
||||
Default: no
|
||||
.TP
|
||||
\fBOfficialDatabaseOnly BOOL\fR
|
||||
Only load the official signatures published by the ClamAV project.
|
||||
.br
|
||||
|
|
|
@ -291,6 +291,9 @@ Print the file type after each file scanned.
|
|||
.TP
|
||||
\fB\-\-cvdcertsdir=DIR\fR
|
||||
Specify a directory containing the root CA cert needed to verify detached CVD digital signatures. If not provided, then clamscan will look in the default directory.
|
||||
.TP
|
||||
\fB\-\-fips\-limits\fR
|
||||
Enforce FIPS\-like limits on using hash algorithms for cryptographic purposes. Will disable MD5 & SHA1 FP sigs and will require '.sign' files to verify CVD authenticity.
|
||||
|
||||
.SH "ENVIRONMENT VARIABLES"
|
||||
.LP
|
||||
|
|
|
@ -71,6 +71,11 @@ Path to a directory containing ClamAV CA certificate files used to verify signed
|
|||
.br
|
||||
Default: @CERTSDIR@
|
||||
.TP
|
||||
\fBFIPSCryptoHashLimits BOOL\fR
|
||||
Enforce FIPS\-like limits on using hash algorithms for cryptographic purposes. Will disable MD5 & SHA1 FP sigs and will require '.sign' files to verify CVD authenticity.
|
||||
.br
|
||||
Default: no
|
||||
.TP
|
||||
\fBForeground BOOL\fR
|
||||
Don't fork into background.
|
||||
.br
|
||||
|
|
|
@ -145,6 +145,9 @@ Unpack FILE (CVD) to a current directory.
|
|||
.TP
|
||||
\fB\-\-unpack\-current\fR
|
||||
Unpack a local CVD file (main or daily) to current directory.
|
||||
.TP
|
||||
\fB\-\-fips\-limits\fR
|
||||
Enforce FIPS\-like limits on using hash algorithms for cryptographic purposes. Will disable MD5 & SHA1 FP sigs and will require '.sign' files to verify CVD authenticity.
|
||||
|
||||
.SH "COMMANDS FOR WORKING WITH CDIFF PATCH FILES"
|
||||
.LP
|
||||
|
|
|
@ -89,6 +89,12 @@ Example
|
|||
# Default: hardcoded (depends on installation options)
|
||||
#CVDCertsDirectory /etc/clamav/certs
|
||||
|
||||
# Enforce FIPS-like limits on using hash algorithms for cryptographic purposes.
|
||||
# Will disable MD5 & SHA1 FP sigs and will require '.sign' files to verify
|
||||
# CVD authenticity.
|
||||
# Default: no
|
||||
#FIPSCryptoHashLimits yes
|
||||
|
||||
# Only load the official signatures published by the ClamAV project.
|
||||
# Default: no
|
||||
#OfficialDatabaseOnly no
|
||||
|
|
|
@ -22,6 +22,12 @@ Example
|
|||
# Default: hardcoded (depends on installation options)
|
||||
#CVDCertsDirectory /etc/clamav/certs
|
||||
|
||||
# Enforce FIPS-like limits on using hash algorithms for cryptographic purposes.
|
||||
# Will disable MD5 & SHA1 FP sigs and will require '.sign' files to verify
|
||||
# CVD authenticity.
|
||||
# Default: no
|
||||
#FIPSCryptoHashLimits yes
|
||||
|
||||
# Path to the log file (make sure it has proper permissions)
|
||||
# Default: disabled
|
||||
#UpdateLogFile /var/log/freshclam.log
|
||||
|
|
|
@ -45,7 +45,7 @@ int main(int argc, char **argv)
|
|||
|
||||
const char *filename;
|
||||
const char *destination_directory;
|
||||
bool dont_verify = false;
|
||||
uint32_t dboptions = 0;
|
||||
|
||||
switch (argc) {
|
||||
case 2:
|
||||
|
@ -60,7 +60,7 @@ int main(int argc, char **argv)
|
|||
if (strcmp(argv[1], "--no-verify") == 0) {
|
||||
filename = argv[2];
|
||||
destination_directory = argv[3];
|
||||
dont_verify = true;
|
||||
dboptions = CL_DB_UNSIGNED;
|
||||
} else {
|
||||
printf("Usage: %s [--no-verify] file [destination_directory]\n", argv[0]);
|
||||
return CL_EARG;
|
||||
|
@ -73,7 +73,7 @@ int main(int argc, char **argv)
|
|||
|
||||
// Note: using NULL for certs_directory will disable external digital signature verification.
|
||||
|
||||
ret = cl_cvdunpack_ex(filename, destination_directory, dont_verify, NULL);
|
||||
ret = cl_cvdunpack_ex(filename, destination_directory, NULL, dboptions);
|
||||
if (ret != CL_SUCCESS) {
|
||||
printf("ERROR: %s\n", cl_strerror(ret));
|
||||
}
|
||||
|
|
|
@ -1003,6 +1003,8 @@ static fc_error_t initialize(struct optstruct *opts)
|
|||
|
||||
fcConfig.bCompressLocalDatabase = optget(opts, "CompressLocalDatabase")->enabled;
|
||||
|
||||
fcConfig.bFipsLimits = optget(opts, "FIPSCryptoHashLimits")->enabled;
|
||||
|
||||
/*
|
||||
* Initialize libfreshclam.
|
||||
*/
|
||||
|
|
|
@ -2333,7 +2333,7 @@ int asn1_load_mscat(fmap_t *map, struct cl_engine *engine)
|
|||
/* Load the trusted hashes into hm_fp, using the size values
|
||||
* 1 and 2 as sentinel values corresponding to CAB and PE hashes
|
||||
* from .cat files respectively. */
|
||||
if (CL_SUCCESS != hm_addhash_bin(engine->hm_fp, tagval3.content, hm_hashtype, hashed_obj_type, NULL)) {
|
||||
if (CL_SUCCESS != hm_addhash_bin(engine, HASH_PURPOSE_WHOLE_FILE_FP_CHECK, tagval3.content, hm_hashtype, hashed_obj_type, NULL)) {
|
||||
cli_warnmsg("asn1_load_mscat: failed to add hash\n");
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -145,13 +145,17 @@ typedef enum cl_error_t {
|
|||
#define CL_DB_OFFICIAL_ONLY 0x1000
|
||||
#define CL_DB_BYTECODE 0x2000
|
||||
#define CL_DB_SIGNED 0x4000 /** internal */
|
||||
#define CL_DB_BYTECODE_UNSIGNED 0x8000 /** Caution: You should never run bytecode signatures from untrusted sources. Doing so may result in arbitrary code execution. */
|
||||
#define CL_DB_BYTECODE_UNSIGNED 0x8000 /** Caution: You should never run bytecode signatures from untrusted sources.
|
||||
* Doing so may result in arbitrary code execution. */
|
||||
#define CL_DB_UNSIGNED 0x10000 /** internal */
|
||||
#define CL_DB_BYTECODE_STATS 0x20000
|
||||
#define CL_DB_ENHANCED 0x40000
|
||||
#define CL_DB_PCRE_STATS 0x80000
|
||||
#define CL_DB_YARA_EXCLUDE 0x100000
|
||||
#define CL_DB_YARA_ONLY 0x200000
|
||||
#define CL_DB_FIPS_LIMITS 0x400000 /** Disable MD5 and SHA1 methods for trusting files and verifying certificates.
|
||||
* Means: 1. CVD must be signed with external `.sign` file.
|
||||
* 2. Disables support for `.fp` signatures. */
|
||||
|
||||
/* recommended db settings */
|
||||
#define CL_DB_STDOPT (CL_DB_PHISHING | CL_DB_PHISHING_URLS | CL_DB_BYTECODE)
|
||||
|
@ -225,6 +229,7 @@ struct cl_scan_options {
|
|||
#define ENGINE_OPTIONS_DISABLE_PE_CERTS 0x8
|
||||
#define ENGINE_OPTIONS_PE_DUMPCERTS 0x10
|
||||
#define ENGINE_OPTIONS_TMPDIR_RECURSION 0x20
|
||||
#define ENGINE_OPTIONS_FIPS_LIMITS 0x40
|
||||
// clang-format on
|
||||
|
||||
struct cl_engine;
|
||||
|
@ -327,6 +332,7 @@ enum cl_engine_field {
|
|||
CL_ENGINE_PE_DUMPCERTS, /** uint32_t */
|
||||
CL_ENGINE_CVDCERTSDIR, /** (char *) */
|
||||
CL_ENGINE_TMPDIR_RECURSION, /** uint32_t */
|
||||
CL_ENGINE_FIPS_LIMITS, /** uint32_t */
|
||||
};
|
||||
|
||||
enum bytecode_security {
|
||||
|
@ -1930,11 +1936,16 @@ extern cl_error_t cl_cvdverify(const char *file);
|
|||
/**
|
||||
* @brief Verify a CVD file by loading and unloading it.
|
||||
*
|
||||
* @param file Filepath of CVD file.
|
||||
* @param file Directory containing CA certificates required to verify the CVD digital signature.
|
||||
* @return cl_error_t CL_SUCCESS if success, else a CL_E* error code.
|
||||
* May also verify the CVD digital signature.
|
||||
*
|
||||
* @param file Filepath of CVD file.
|
||||
* @param certs_directory Directory containing CA certificates required to verify the CVD digital signature.
|
||||
* @param dboptions Bitmask of flags to modify behavior.
|
||||
* Set CL_DB_FIPS_LIMITS to require the CVD to be signed with a FIPS-compliant external '.sign' file.
|
||||
* Set CL_DB_UNSIGNED to disable verification of CVD digital signatures. Allow load testing unsigned CVD files.
|
||||
* @return cl_error_t CL_SUCCESS if success, else a CL_E* error code.
|
||||
*/
|
||||
extern cl_error_t cl_cvdverify_ex(const char *file, const char *certs_directory);
|
||||
extern cl_error_t cl_cvdverify_ex(const char *file, const char *certs_directory, uint32_t dboptions);
|
||||
|
||||
/**
|
||||
* @brief Free a CVD header struct.
|
||||
|
@ -1966,11 +1977,13 @@ extern cl_error_t cl_cvdunpack(const char *file, const char *dir, bool dont_veri
|
|||
*
|
||||
* @param file Filepath of CVD file.
|
||||
* @param dir Destination directory.
|
||||
* @param dont_verify If true, don't verify the CVD.
|
||||
* @param certs_directory Path where ClamAV public certs are located, needed to verify external digital signatures.
|
||||
* @param dboptions Bitmask of flags to modify behavior.
|
||||
* Set CL_DB_FIPS_LIMITS to require the CVD to be signed with a FIPS-compliant external '.sign' file.
|
||||
* Set CL_DB_UNSIGNED to disable verification of CVD digital signatures. Allow load testing unsigned CVD files.
|
||||
* @return cl_error_t CL_SUCCESS if success, else a CL_E* error code.
|
||||
*/
|
||||
extern cl_error_t cl_cvdunpack_ex(const char *file, const char *dir, bool dont_verify, const char *certs_directory);
|
||||
cl_error_t cl_cvdunpack_ex(const char *file, const char *dir, const char *certs_directory, uint32_t dboptions);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the age of CVD disk data.
|
||||
|
@ -2061,7 +2074,7 @@ extern const char *cl_retver(void);
|
|||
extern const char *cl_strerror(cl_error_t clerror);
|
||||
|
||||
/* ----------------------------------------------------------------------------
|
||||
* Crypto/hashing functions
|
||||
* Hashing functions.
|
||||
*/
|
||||
#define MD5_HASH_SIZE 16
|
||||
#define SHA1_HASH_SIZE 20
|
||||
|
@ -2069,20 +2082,154 @@ extern const char *cl_strerror(cl_error_t clerror);
|
|||
#define SHA384_HASH_SIZE 48
|
||||
#define SHA512_HASH_SIZE 64
|
||||
|
||||
#define CL_HASH_FLAG_NONE 0x00 /* None */
|
||||
#define CL_HASH_FLAG_ALLOCATE 0x01 /* Use CL_HASH_FLAG_ALLOCATE to dynamically allocate the output buffer. \
|
||||
* If this flag is set, the function will allocate a buffer for the hash and return it. \
|
||||
* The caller is responsible for freeing the buffer using free(). \
|
||||
* If this flag is not set, the caller must provide a buffer to store the hash. */
|
||||
#define CL_HASH_FLAG_FIPS_BYPASS 0x02 /* Use CL_HASH_FLAG_FIPS_BYPASS to bypass FIPS restrictions on which algorithms can be \
|
||||
* used. This is useful if you want to use algorithms that are not FIPS-approved. \
|
||||
* For example, you might want to use this flag if you want to use "md5" or "sha1". \
|
||||
* You should only do this when the hash is used for non-cryptographic purposes. \
|
||||
* Note: If OpenSSL's FIPS provider is not available, this flag has no effect. */
|
||||
|
||||
/**
|
||||
* @brief Generate a hash of data.
|
||||
*
|
||||
* @param alg The hashing algorithm to use.
|
||||
* Suggested "alg" names include "md5", "sha1", "sha2-256", "sha2-384", and "sha2-512".
|
||||
* But the underlying hashing library is OpenSSL and you might be able to use
|
||||
* other algorithms supported by OpenSSL's EVP_get_digestbyname() function.
|
||||
* Note: For the `cl_scan*` functions (above) the supported algorithms are
|
||||
* presently limited to "md5", "sha1", "sha2-256".
|
||||
* @param data The data to be hashed.
|
||||
* @param data_len The length of the to-be-hashed data.
|
||||
* @param[inout] hash A buffer to store the generated hash.
|
||||
* Set flags to CL_HASH_FLAG_ALLOCATE to dynamically allocate buffer.
|
||||
* @param[inout] hash_len If providing a buffer, set this to the size of the buffer.
|
||||
* If allocating, this is purely an output parameter and need not be initialized.
|
||||
* @param flags Flags to modify the behavior of the hashing function.
|
||||
* Use CL_HASH_FLAG_ALLOCATE to dynamically allocate the output buffer.
|
||||
* Use CL_HASH_FLAG_FIPS_BYPASS to bypass FIPS restrictions on which algorithms can be used.
|
||||
*
|
||||
* @return cl_error_t CL_SUCCESS if the hash was generated successfully.
|
||||
* CL_E* error code if an error occurred.
|
||||
*/
|
||||
extern cl_error_t cl_hash_data_ex(
|
||||
const char *alg,
|
||||
const uint8_t *data,
|
||||
size_t data_len,
|
||||
uint8_t **hash,
|
||||
size_t *hash_len,
|
||||
uint32_t flags);
|
||||
|
||||
struct cl_hash_ctx;
|
||||
typedef struct cl_hash_ctx cl_hash_ctx_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize a hash context.
|
||||
*
|
||||
* @param alg The hash algorithm to use.
|
||||
* @param flags Flags to modify the behavior of the hashing function.
|
||||
* Use CL_HASH_FLAG_FIPS_BYPASS to bypass FIPS restrictions on which algorithms can be used.
|
||||
* @return cl_error_t CL_SUCCESS if the hash context was successfully initialized.
|
||||
*/
|
||||
extern cl_error_t cl_hash_init_ex(
|
||||
const char *alg,
|
||||
uint32_t flags,
|
||||
cl_hash_ctx_t **ctx_out);
|
||||
|
||||
/**
|
||||
* @brief Update a hash context with new data.
|
||||
*
|
||||
* @param ctx The hash context.
|
||||
* @param data The data to hash.
|
||||
* @param length The size of the data.
|
||||
* @return cl_error_t CL_SUCCESS if the data was successfully added to the hash context.
|
||||
* CL_E* error code if an error occurred.
|
||||
*/
|
||||
extern cl_error_t cl_update_hash_ex(
|
||||
cl_hash_ctx_t *ctx,
|
||||
const uint8_t *data,
|
||||
size_t length);
|
||||
|
||||
/**
|
||||
* @brief Finalize a hash context and get the resulting hash.
|
||||
*
|
||||
* @param ctx The hash context.
|
||||
* @param[inout] hash A buffer to store the generated hash.
|
||||
* Set flags to CL_HASH_FLAG_ALLOCATE to dynamically allocate buffer.
|
||||
* @param[inout] hash_len If providing a buffer, set this to the size of the buffer.
|
||||
* If allocating, this is purely an output parameter and need not be initialized.
|
||||
* @param flags Flags to modify the behavior of the hashing function.
|
||||
* Use CL_HASH_FLAG_ALLOCATE to dynamically allocate the output buffer.
|
||||
*
|
||||
* @return cl_error_t CL_SUCCESS if the hash was successfully finalized.
|
||||
* CL_E* error code if an error occurred.
|
||||
*/
|
||||
extern cl_error_t cl_finish_hash_ex(
|
||||
cl_hash_ctx_t *ctx,
|
||||
uint8_t **hash,
|
||||
size_t *hash_len,
|
||||
uint32_t flags);
|
||||
|
||||
/**
|
||||
* @brief Destroy a hash context.
|
||||
*
|
||||
* @param ctx The hash context.
|
||||
*/
|
||||
extern void cl_hash_destroy_ex(cl_hash_ctx_t *ctx);
|
||||
|
||||
/**
|
||||
* @brief Generate a hash of a file.
|
||||
*
|
||||
* @param alg The hashing algorithm to use.
|
||||
* @param fd The file descriptor.
|
||||
* @param offset The offset in the file to start hashing from.
|
||||
* @param length The length of the data to hash. If 0, the entire file will be hashed.
|
||||
* @param[inout] hash A buffer to store the generated hash.
|
||||
* Set flags to CL_HASH_FLAG_ALLOCATE to dynamically allocate buffer.
|
||||
* @param[inout] hash_len A pointer that stores how long the generated hash is.
|
||||
* @param flags Flags to modify the behavior of the hashing function.
|
||||
* Use CL_HASH_FLAG_ALLOCATE to dynamically allocate the output buffer.
|
||||
* Use CL_HASH_FLAG_FIPS_BYPASS to bypass FIPS restrictions on which algorithms can be used.
|
||||
*
|
||||
* @return cl_error_t CL_SUCCESS if the hash was generated successfully.
|
||||
*/
|
||||
extern cl_error_t cl_hash_file_fd_ex(
|
||||
const char *alg,
|
||||
int fd,
|
||||
size_t offset,
|
||||
size_t length,
|
||||
uint8_t **hash,
|
||||
size_t *hash_len,
|
||||
uint32_t flags);
|
||||
|
||||
/**
|
||||
* @brief Generate a hash of data.
|
||||
*
|
||||
* @deprecated This function is deprecated and will be removed in a future release.
|
||||
* Use `cl_hash_data_ex()` instead.
|
||||
*
|
||||
* Note: This function intentionally bypasses FIPS restrictions on which algorithms can be used.
|
||||
* Do not use this function for cryptographic purposes or for false positive hash checks.
|
||||
*
|
||||
* @param alg The hashing algorithm to use.
|
||||
* @param buf The data to be hashed.
|
||||
* @param len The length of the to-be-hashed data.
|
||||
* @param[out] obuf (optional) A buffer to store the generated hash. Use NULL to dynamically allocate buffer.
|
||||
* If providing, use the *_HASH_SIZE macros above to determine the required buffer size.
|
||||
* @param[out] olen (optional) A pointer that stores how long the generated hash is.
|
||||
* This is purely an output parameter and need not be initialized.
|
||||
* @return A pointer to the generated hash or obuf if obuf is not NULL.
|
||||
*/
|
||||
extern unsigned char *cl_hash_data(const char *alg, const void *buf, size_t len, unsigned char *obuf, unsigned int *olen);
|
||||
|
||||
/**
|
||||
* @brief Generate a hash of a file.
|
||||
* @brief Generate a hash of a file given a file descriptor.
|
||||
*
|
||||
* @deprecated This function is deprecated and will be removed in a future release.
|
||||
* Note: there is no `cl_hash_file_fd_ctx_ex()` function. Use `cl_hash_file_fd_ex()` instead.
|
||||
*
|
||||
* @param ctx A pointer to the OpenSSL EVP_MD_CTX object.
|
||||
* @param fd The file descriptor.
|
||||
|
@ -2092,7 +2239,13 @@ extern unsigned char *cl_hash_data(const char *alg, const void *buf, size_t len,
|
|||
extern unsigned char *cl_hash_file_fd_ctx(EVP_MD_CTX *ctx, int fd, unsigned int *olen);
|
||||
|
||||
/**
|
||||
* @brief Generate a hash of a file.
|
||||
* @brief Generate a hash of a file given a file descriptor.
|
||||
*
|
||||
* @deprecated This function is deprecated and will be removed in a future release.
|
||||
* Use `cl_hash_file_fd_ex()` instead.
|
||||
*
|
||||
* Note: This function intentionally bypasses FIPS restrictions on which algorithms can be used.
|
||||
* Do not use this function for cryptographic purposes or for false positive hash checks.
|
||||
*
|
||||
* @param fd The file descriptor.
|
||||
* @param alg The hashing algorithm to use.
|
||||
|
@ -2102,7 +2255,13 @@ extern unsigned char *cl_hash_file_fd_ctx(EVP_MD_CTX *ctx, int fd, unsigned int
|
|||
extern unsigned char *cl_hash_file_fd(int fd, const char *alg, unsigned int *olen);
|
||||
|
||||
/**
|
||||
* @brief Generate a hash of a file.
|
||||
* @brief Generate a hash of a file given a FILE pointer.
|
||||
*
|
||||
* @deprecated This function is deprecated and will be removed in a future release.
|
||||
* Note: There is no `cl_hash_file_fp_ex()` function. Use `cl_hash_file_fd_ex()` instead.
|
||||
*
|
||||
* Note: This function intentionally bypasses FIPS restrictions on which algorithms can be used.
|
||||
* Do not use this function for cryptographic purposes or for false positive hash checks.
|
||||
*
|
||||
* @param fp A pointer to a FILE object.
|
||||
* @param alg The hashing algorithm to use.
|
||||
|
@ -2114,6 +2273,9 @@ extern unsigned char *cl_hash_file_fp(FILE *fp, const char *alg, unsigned int *o
|
|||
/**
|
||||
* @brief Generate a sha2-256 hash of data.
|
||||
*
|
||||
* @deprecated This function is deprecated and will be removed in a future release.
|
||||
* Use `cl_hash_data_ex()` instead.
|
||||
*
|
||||
* @param buf The data to hash.
|
||||
* @param len The length of the to-be-hashed data.
|
||||
* @param[out] obuf (optional) A pointer to store the generated hash. Use NULL to dynamically allocate buffer.
|
||||
|
@ -2125,6 +2287,9 @@ extern unsigned char *cl_sha256(const void *buf, size_t len, unsigned char *obuf
|
|||
/**
|
||||
* @brief Generate a sha2-384 hash of data.
|
||||
*
|
||||
* @deprecated This function is deprecated and will be removed in a future release.
|
||||
* Use `cl_hash_data_ex()` instead.
|
||||
*
|
||||
* @param buf The data to hash.
|
||||
* @param len The length of the to-be-hashed data.
|
||||
* @param[out] obuf (optional) A pointer to store the generated hash. Use NULL to dynamically allocate buffer.
|
||||
|
@ -2136,6 +2301,9 @@ extern unsigned char *cl_sha384(const void *buf, size_t len, unsigned char *obuf
|
|||
/**
|
||||
* @brief Generate a sha2-512 hash of data.
|
||||
*
|
||||
* @deprecated This function is deprecated and will be removed in a future release.
|
||||
* Use `cl_hash_data_ex()` instead.
|
||||
*
|
||||
* @param buf The data to hash.
|
||||
* @param len The length of the to-be-hashed data.
|
||||
* @param[out] obuf (optional) A pointer to store the generated hash. Use NULL to dynamically allocate buffer.
|
||||
|
@ -2147,6 +2315,12 @@ extern unsigned char *cl_sha512(const void *buf, size_t len, unsigned char *obuf
|
|||
/**
|
||||
* @brief Generate a sha1 hash of data.
|
||||
*
|
||||
* @deprecated This function is deprecated and will be removed in a future release.
|
||||
* Use `cl_hash_data_ex()` instead.
|
||||
*
|
||||
* Note: This function intentionally bypasses FIPS restrictions on which algorithms can be used.
|
||||
* Do not use this function for cryptographic purposes or for false positive hash checks.
|
||||
*
|
||||
* @param buf The data to hash.
|
||||
* @param len The length of the to-be-hashed data.
|
||||
* @param[out] obuf (optional) A pointer to store the generated hash. Use NULL to dynamically allocate buffer.
|
||||
|
@ -2155,6 +2329,59 @@ extern unsigned char *cl_sha512(const void *buf, size_t len, unsigned char *obuf
|
|||
*/
|
||||
extern unsigned char *cl_sha1(const void *buf, size_t len, unsigned char *obuf, unsigned int *olen);
|
||||
|
||||
/**
|
||||
* @brief Initialize a hash context.
|
||||
*
|
||||
* @deprecated This function is deprecated and will be removed in a future release.
|
||||
* Use `cl_hash_init_ex()` instead.
|
||||
*
|
||||
* Note: This function intentionally bypasses FIPS restrictions on which algorithms can be used.
|
||||
* Do not use this function for cryptographic purposes or for false positive hash checks.
|
||||
*
|
||||
* @param alg The hash algorithm to use.
|
||||
* @return void*
|
||||
*/
|
||||
extern void *cl_hash_init(const char *alg);
|
||||
|
||||
/**
|
||||
* @brief Update a hash context with new data.
|
||||
*
|
||||
* @deprecated This function is deprecated and will be removed in a future release.
|
||||
* Use `cl_update_hash_ex()` instead.
|
||||
*
|
||||
* @param ctx The hash context.
|
||||
* @param data The data to hash.
|
||||
* @param sz The size of the data.
|
||||
* @return int 0 on success, -1 on error.
|
||||
*/
|
||||
extern int cl_update_hash(void *ctx, const void *data, size_t sz);
|
||||
|
||||
/**
|
||||
* @brief Finalize a hash context and get the resulting hash.
|
||||
*
|
||||
* @deprecated This function is deprecated and will be removed in a future release.
|
||||
* Use `cl_finish_hash_ex()` instead.
|
||||
*
|
||||
* @param ctx The hash context.
|
||||
* @param buf A buffer to store the resulting hash. Must be large enough to hold the hash.
|
||||
* @return int 0 on success, -1 on error.
|
||||
*/
|
||||
extern int cl_finish_hash(void *ctx, void *buf);
|
||||
|
||||
/**
|
||||
* @brief Destroy a hash context.
|
||||
*
|
||||
* @deprecated This function is deprecated and will be removed in a future release.
|
||||
* Use `cl_hash_destroy_ex()` instead.
|
||||
*
|
||||
* @param ctx The hash context.
|
||||
*/
|
||||
extern void cl_hash_destroy(void *ctx);
|
||||
|
||||
/* -------------------------------------------------------------------------------
|
||||
* Signing and verification functions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Verify validity of signed data.
|
||||
*
|
||||
|
@ -2427,40 +2654,6 @@ extern unsigned char *cl_sign_file_fp(FILE *fp, EVP_PKEY *pkey, const char *alg,
|
|||
*/
|
||||
extern EVP_PKEY *cl_get_pkey_file(char *keypath);
|
||||
|
||||
/**
|
||||
* @brief Initialize a hash context.
|
||||
*
|
||||
* @param alg The hash algorithm to use.
|
||||
* @return void*
|
||||
*/
|
||||
extern void *cl_hash_init(const char *alg);
|
||||
|
||||
/**
|
||||
* @brief Update a hash context with new data.
|
||||
*
|
||||
* @param ctx The hash context.
|
||||
* @param data The data to hash.
|
||||
* @param sz The size of the data.
|
||||
* @return int 0 on success, -1 on error.
|
||||
*/
|
||||
extern int cl_update_hash(void *ctx, const void *data, size_t sz);
|
||||
|
||||
/**
|
||||
* @brief Finalize a hash context and get the resulting hash.
|
||||
*
|
||||
* @param ctx The hash context.
|
||||
* @param buf A buffer to store the resulting hash. Must be large enough to hold the hash.
|
||||
* @return int 0 on success, -1 on error.
|
||||
*/
|
||||
extern int cl_finish_hash(void *ctx, void *buf);
|
||||
|
||||
/**
|
||||
* @brief Destroy a hash context.
|
||||
*
|
||||
* @param ctx The hash context.
|
||||
*/
|
||||
extern void cl_hash_destroy(void *ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
File diff suppressed because it is too large
Load diff
100
libclamav/cvd.c
100
libclamav/cvd.c
|
@ -41,6 +41,7 @@
|
|||
#include "zlib.h"
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <openssl/crypto.h>
|
||||
|
||||
#include "clamav.h"
|
||||
#include "clamav_rust.h"
|
||||
|
@ -394,8 +395,7 @@ struct cl_cvd *cl_cvdhead(const char *file)
|
|||
if ((pt = strpbrk(head, "\n\r")))
|
||||
*pt = 0;
|
||||
|
||||
for (i = bread - 1; i > 0 && (head[i] == ' ' || head[i] == '\n' || head[i] == '\r'); head[i] = 0, i--)
|
||||
;
|
||||
for (i = bread - 1; i > 0 && (head[i] == ' ' || head[i] == '\n' || head[i] == '\r'); head[i] = 0, i--);
|
||||
|
||||
return cl_cvdparse(head);
|
||||
}
|
||||
|
@ -411,10 +411,10 @@ void cl_cvdfree(struct cl_cvd *cvd)
|
|||
|
||||
cl_error_t cl_cvdverify(const char *file)
|
||||
{
|
||||
return cl_cvdverify_ex(file, NULL);
|
||||
return cl_cvdverify_ex(file, NULL, 0);
|
||||
}
|
||||
|
||||
cl_error_t cl_cvdverify_ex(const char *file, const char *certs_directory)
|
||||
cl_error_t cl_cvdverify_ex(const char *file, const char *certs_directory, uint32_t dboptions)
|
||||
{
|
||||
struct cl_engine *engine = NULL;
|
||||
cl_error_t ret;
|
||||
|
@ -455,7 +455,7 @@ cl_error_t cl_cvdverify_ex(const char *file, const char *certs_directory)
|
|||
}
|
||||
}
|
||||
|
||||
ret = cli_cvdload(engine, NULL, CL_DB_STDOPT | CL_DB_PUA, dbtype, file, verifier, 1);
|
||||
ret = cli_cvdload(engine, NULL, dboptions | CL_DB_STDOPT | CL_DB_PUA, dbtype, file, verifier, true);
|
||||
|
||||
done:
|
||||
if (NULL != engine) {
|
||||
|
@ -471,7 +471,14 @@ done:
|
|||
return ret;
|
||||
}
|
||||
|
||||
cl_error_t cli_cvdload(struct cl_engine *engine, unsigned int *signo, unsigned int options, cvd_type dbtype, const char *filename, void *sign_verifier, unsigned int chkonly)
|
||||
cl_error_t cli_cvdload(
|
||||
struct cl_engine *engine,
|
||||
unsigned int *signo,
|
||||
uint32_t options,
|
||||
cvd_type dbtype,
|
||||
const char *filename,
|
||||
void *sign_verifier,
|
||||
bool chkonly)
|
||||
{
|
||||
cl_error_t status = CL_ECVD;
|
||||
cl_error_t ret;
|
||||
|
@ -484,11 +491,14 @@ cl_error_t cli_cvdload(struct cl_engine *engine, unsigned int *signo, unsigned i
|
|||
FFIError *cvd_open_error = NULL;
|
||||
FFIError *cvd_verify_error = NULL;
|
||||
char *signer_name = NULL;
|
||||
bool disable_legacy_dsig = false;
|
||||
|
||||
dbio.hashctx = NULL;
|
||||
|
||||
cli_dbgmsg("in cli_cvdload()\n");
|
||||
|
||||
disable_legacy_dsig = (options & CL_DB_FIPS_LIMITS) || (engine->engine_options & ENGINE_OPTIONS_FIPS_LIMITS);
|
||||
|
||||
/* Open the cvd and read the header */
|
||||
cvd = cvd_open(filename, &cvd_open_error);
|
||||
if (!cvd) {
|
||||
|
@ -501,7 +511,7 @@ cl_error_t cli_cvdload(struct cl_engine *engine, unsigned int *signo, unsigned i
|
|||
if (!cvd_verify(
|
||||
cvd,
|
||||
sign_verifier,
|
||||
false,
|
||||
disable_legacy_dsig,
|
||||
&signer_name,
|
||||
&cvd_verify_error)) {
|
||||
cli_errmsg("cli_cvdload: Can't verify CVD file %s: %s\n", filename, ffierror_fmt(cvd_verify_error));
|
||||
|
@ -641,7 +651,53 @@ done:
|
|||
return status;
|
||||
}
|
||||
|
||||
cl_error_t cli_cvdunpack_and_verify(const char *file, const char *dir, bool dont_verify, void *verifier)
|
||||
cl_error_t cli_cvdverify(
|
||||
const char *file,
|
||||
bool disable_legacy_dsig,
|
||||
void *verifier)
|
||||
{
|
||||
cl_error_t status = CL_SUCCESS;
|
||||
cvd_t *cvd = NULL;
|
||||
FFIError *cvd_open_error = NULL;
|
||||
FFIError *cvd_verify_error = NULL;
|
||||
char *signer_name = NULL;
|
||||
|
||||
cvd = cvd_open(file, &cvd_open_error);
|
||||
if (!cvd) {
|
||||
cli_errmsg("Can't open CVD file %s: %s\n", file, ffierror_fmt(cvd_open_error));
|
||||
return CL_EOPEN;
|
||||
}
|
||||
|
||||
if (!cvd_verify(cvd, verifier, disable_legacy_dsig, &signer_name, &cvd_verify_error)) {
|
||||
cli_errmsg("CVD verification failed: %s\n", ffierror_fmt(cvd_verify_error));
|
||||
status = CL_EVERIFY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
if (NULL != signer_name) {
|
||||
ffi_cstring_free(signer_name);
|
||||
}
|
||||
if (NULL != cvd) {
|
||||
cvd_free(cvd);
|
||||
}
|
||||
if (NULL != cvd_open_error) {
|
||||
ffierror_free(cvd_open_error);
|
||||
}
|
||||
if (NULL != cvd_verify_error) {
|
||||
ffierror_free(cvd_verify_error);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
cl_error_t cli_cvdunpack_and_verify(
|
||||
const char *file,
|
||||
const char *dir,
|
||||
bool dont_verify,
|
||||
bool disable_legacy_dsig,
|
||||
void *verifier)
|
||||
{
|
||||
cl_error_t status = CL_SUCCESS;
|
||||
cvd_t *cvd = NULL;
|
||||
|
@ -657,7 +713,7 @@ cl_error_t cli_cvdunpack_and_verify(const char *file, const char *dir, bool dont
|
|||
}
|
||||
|
||||
if (!dont_verify) {
|
||||
if (!cvd_verify(cvd, verifier, false, &signer_name, &cvd_verify_error)) {
|
||||
if (!cvd_verify(cvd, verifier, disable_legacy_dsig, &signer_name, &cvd_verify_error)) {
|
||||
cli_errmsg("CVD verification failed: %s\n", ffierror_fmt(cvd_verify_error));
|
||||
status = CL_EVERIFY;
|
||||
goto done;
|
||||
|
@ -691,7 +747,7 @@ done:
|
|||
return status;
|
||||
}
|
||||
|
||||
cl_error_t cl_cvdunpack_ex(const char *file, const char *dir, bool dont_verify, const char *certs_directory)
|
||||
cl_error_t cl_cvdunpack_ex(const char *file, const char *dir, const char *certs_directory, uint32_t dboptions)
|
||||
{
|
||||
cl_error_t status = CL_SUCCESS;
|
||||
cvd_t *cvd = NULL;
|
||||
|
@ -707,15 +763,16 @@ cl_error_t cl_cvdunpack_ex(const char *file, const char *dir, bool dont_verify,
|
|||
return CL_EOPEN;
|
||||
}
|
||||
|
||||
if (dont_verify) {
|
||||
// Just unpack the CVD file.
|
||||
if (dboptions & CL_DB_UNSIGNED) {
|
||||
// Just unpack the CVD file and don´t verify the digital signature.
|
||||
if (!cvd_unpack(cvd, dir, &cvd_unpack_error)) {
|
||||
cli_errmsg("CVD unpacking failed: %s\n", ffierror_fmt(cvd_unpack_error));
|
||||
status = CL_EUNPACK;
|
||||
goto done;
|
||||
}
|
||||
} else {
|
||||
// Verify the CVD file and hten unpack it.
|
||||
// Verify the CVD file and then unpack it.
|
||||
bool disable_legacy_dsig = false;
|
||||
|
||||
// The certs directory is optional.
|
||||
// If not provided, then we can't validate external signatures and will have to rely
|
||||
|
@ -728,7 +785,13 @@ cl_error_t cl_cvdunpack_ex(const char *file, const char *dir, bool dont_verify,
|
|||
}
|
||||
}
|
||||
|
||||
status = cli_cvdunpack_and_verify(file, dir, dont_verify, verifier);
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
disable_legacy_dsig = (dboptions & CL_DB_FIPS_LIMITS) || EVP_default_properties_is_fips_enabled(NULL);
|
||||
#else
|
||||
disable_legacy_dsig = (dboptions & CL_DB_FIPS_LIMITS) || FIPS_mode();
|
||||
#endif
|
||||
|
||||
status = cli_cvdunpack_and_verify(file, dir, false, disable_legacy_dsig, verifier);
|
||||
if (status != CL_SUCCESS) {
|
||||
goto done;
|
||||
}
|
||||
|
@ -766,6 +829,7 @@ cl_error_t cl_cvdunpack(const char *file, const char *dir, bool dont_verify)
|
|||
FFIError *cvd_verify_error = NULL;
|
||||
FFIError *cvd_unpack_error = NULL;
|
||||
char *signer_name = NULL;
|
||||
bool disable_legacy_dsig = false;
|
||||
|
||||
cvd = cvd_open(file, &cvd_open_error);
|
||||
if (!cvd) {
|
||||
|
@ -773,8 +837,14 @@ cl_error_t cl_cvdunpack(const char *file, const char *dir, bool dont_verify)
|
|||
return CL_EOPEN;
|
||||
}
|
||||
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
disable_legacy_dsig = EVP_default_properties_is_fips_enabled(NULL);
|
||||
#else
|
||||
disable_legacy_dsig = FIPS_mode();
|
||||
#endif
|
||||
|
||||
if (!dont_verify) {
|
||||
if (!cvd_verify(cvd, NULL, false, &signer_name, &cvd_verify_error)) {
|
||||
if (!cvd_verify(cvd, NULL, disable_legacy_dsig, &signer_name, &cvd_verify_error)) {
|
||||
cli_errmsg("CVD verification failed: %s\n", ffierror_fmt(cvd_verify_error));
|
||||
status = CL_EVERIFY;
|
||||
goto done;
|
||||
|
|
|
@ -52,7 +52,55 @@ typedef enum cvd_type {
|
|||
CVD_TYPE_CUD,
|
||||
} cvd_type;
|
||||
|
||||
cl_error_t cli_cvdload(struct cl_engine *engine, unsigned int *signo, unsigned int options, cvd_type dbtype, const char *filename, void *sign_verifier, unsigned int chkonly);
|
||||
cl_error_t cli_cvdunpack_and_verify(const char *file, const char *dir, bool dont_verify, void *verifier);
|
||||
/**
|
||||
* @brief Load a CVD, CLD, or CUD signature database archive
|
||||
*
|
||||
* @param engine The ClamAV engine to load the CVD into
|
||||
* @param signo Pointer to the signature number
|
||||
* @param options CL_DB_* options for loading the CVD
|
||||
* @param dbtype Type of the database
|
||||
* @param filename Name of the CVD file
|
||||
* @param sign_verifier Pointer to the signature verifier
|
||||
* @param chkonly Check only mode
|
||||
* @return cl_error_t CL_SUCCESS on success, or an error code on failure
|
||||
*/
|
||||
cl_error_t cli_cvdload(
|
||||
struct cl_engine *engine,
|
||||
unsigned int *signo,
|
||||
uint32_t options,
|
||||
cvd_type dbtype,
|
||||
const char *filename,
|
||||
void *sign_verifier,
|
||||
bool chkonly);
|
||||
|
||||
/**
|
||||
* @brief Unpack and verify a CVD, CLD, or CUD signature database archive
|
||||
*
|
||||
* @param file Name of the CVD file
|
||||
* @param dir Directory to unpack the CVD into
|
||||
* @param dont_verify Don't verify signatures
|
||||
* @param disable_legacy_dsig Disable legacy MD5-based digital signature verification
|
||||
* @param verifier Pointer to the signature verifier
|
||||
* @return cl_error_t CL_SUCCESS on success, or an error code on failure
|
||||
*/
|
||||
cl_error_t cli_cvdunpack_and_verify(
|
||||
const char *file,
|
||||
const char *dir,
|
||||
bool dont_verify,
|
||||
bool disable_legacy_dsig,
|
||||
void *verifier);
|
||||
|
||||
/**
|
||||
* @brief Verify a CVD, CLD, or CUD signature database archive
|
||||
*
|
||||
* @param file Name of the CVD file
|
||||
* @param disable_legacy_dsig Disable legacy MD5-based digital signature verification
|
||||
* @param verifier Pointer to the signature verifier
|
||||
* @return cl_error_t CL_SUCCESS on success, or an error code on failure
|
||||
*/
|
||||
cl_error_t cli_cvdverify(
|
||||
const char *file,
|
||||
bool disable_legacy_dsig,
|
||||
void *verifier);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -318,6 +318,7 @@ CLAMAV_PRIVATE {
|
|||
cli_checklimits;
|
||||
cli_matchmeta;
|
||||
cli_cvdunpack_and_verify;
|
||||
cli_cvdverify;
|
||||
|
||||
__cli_strcasestr;
|
||||
__cli_strndup;
|
||||
|
|
|
@ -62,7 +62,8 @@ size_t cli_hash_len(cli_hash_type_t type)
|
|||
}
|
||||
}
|
||||
|
||||
cl_error_t cli_hash_type_from_name(const char* name, cli_hash_type_t *type_out){
|
||||
cl_error_t cli_hash_type_from_name(const char *name, cli_hash_type_t *type_out)
|
||||
{
|
||||
if (!name || !type_out) {
|
||||
return CL_ENULLARG;
|
||||
}
|
||||
|
@ -84,14 +85,14 @@ cl_error_t cli_hash_type_from_name(const char* name, cli_hash_type_t *type_out){
|
|||
return CL_SUCCESS;
|
||||
}
|
||||
|
||||
cl_error_t hm_addhash_str(struct cli_matcher *root, const char *strhash, uint32_t size, const char *virusname)
|
||||
cl_error_t hm_addhash_str(struct cl_engine *engine, hash_purpose_t purpose, const char *strhash, uint32_t size, const char *virusname)
|
||||
{
|
||||
cli_hash_type_t type;
|
||||
char binhash[SHA256_HASH_SIZE];
|
||||
size_t hlen;
|
||||
|
||||
if (!root || !strhash) {
|
||||
cli_errmsg("hm_addhash_str: NULL root or hash\n");
|
||||
if (!engine || !strhash) {
|
||||
cli_errmsg("hm_addhash_str: NULL engine or hash\n");
|
||||
return CL_ENULLARG;
|
||||
}
|
||||
|
||||
|
@ -116,30 +117,64 @@ cl_error_t hm_addhash_str(struct cli_matcher *root, const char *strhash, uint32_
|
|||
cli_errmsg("hm_addhash_str: invalid hash %s -- FIXME!\n", strhash);
|
||||
return CL_EARG;
|
||||
}
|
||||
|
||||
if (cli_hex2str_to(strhash, (char *)binhash, hlen)) {
|
||||
cli_errmsg("hm_addhash_str: invalid hash %s\n", strhash);
|
||||
return CL_EARG;
|
||||
}
|
||||
|
||||
return hm_addhash_bin(root, binhash, type, size, virusname);
|
||||
return hm_addhash_bin(engine, purpose, binhash, type, size, virusname);
|
||||
}
|
||||
|
||||
cl_error_t hm_addhash_bin(struct cli_matcher *root, const void *binhash, cli_hash_type_t type, uint32_t size, const char *virusname)
|
||||
cl_error_t hm_addhash_bin(struct cl_engine *engine, hash_purpose_t purpose, const void *binhash, cli_hash_type_t type, uint32_t size, const char *virusname)
|
||||
{
|
||||
size_t hlen = cli_hash_len(type);
|
||||
const struct cli_htu32_element *item;
|
||||
struct cli_sz_hash *szh;
|
||||
struct cli_htu32 *ht;
|
||||
int i;
|
||||
cl_error_t ret;
|
||||
struct cli_matcher *root;
|
||||
|
||||
if (purpose == HASH_PURPOSE_PE_SECTION_DETECT) {
|
||||
root = engine->hm_mdb;
|
||||
} else if (purpose == HASH_PURPOSE_WHOLE_FILE_DETECT) {
|
||||
root = engine->hm_hdb;
|
||||
} else if (purpose == HASH_PURPOSE_PE_IMPORT_DETECT) {
|
||||
root = engine->hm_imp;
|
||||
} else if (purpose == HASH_PURPOSE_WHOLE_FILE_FP_CHECK) {
|
||||
if ((type == CLI_HASH_MD5 || type == CLI_HASH_SHA1) &&
|
||||
(engine->engine_options & ENGINE_OPTIONS_FIPS_LIMITS)) {
|
||||
return CL_SUCCESS; // No error, just skip adding MD5/SHA1 FP hashes in FIPS mode
|
||||
}
|
||||
root = engine->hm_fp;
|
||||
}
|
||||
|
||||
if (NULL == root) {
|
||||
if (NULL == (root = MPOOL_CALLOC(engine->mempool, 1, sizeof(*root)))) {
|
||||
return CL_EMEM;
|
||||
}
|
||||
#ifdef USE_MPOOL
|
||||
root->mempool = engine->mempool;
|
||||
#endif
|
||||
if (purpose == HASH_PURPOSE_WHOLE_FILE_DETECT) {
|
||||
engine->hm_hdb = root;
|
||||
} else if (purpose == HASH_PURPOSE_PE_SECTION_DETECT) {
|
||||
engine->hm_mdb = root;
|
||||
} else if (purpose == HASH_PURPOSE_PE_IMPORT_DETECT) {
|
||||
engine->hm_imp = root;
|
||||
} else if (purpose == HASH_PURPOSE_WHOLE_FILE_FP_CHECK) {
|
||||
engine->hm_fp = root;
|
||||
}
|
||||
}
|
||||
|
||||
if (size) {
|
||||
/* size non-zero, find sz_hash element in size-driven hashtable */
|
||||
ht = &root->hm.sizehashes[type];
|
||||
if (!root->hm.sizehashes[type].capacity) {
|
||||
i = CLI_HTU32_INIT(ht, 64, root->mempool);
|
||||
if (i) {
|
||||
ret = CLI_HTU32_INIT(ht, 64, root->mempool);
|
||||
if (CL_SUCCESS != ret) {
|
||||
cli_errmsg("hm_addhash_bin: failed to initialize hash table\n");
|
||||
return CL_ERROR;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,11 +189,11 @@ cl_error_t hm_addhash_bin(struct cli_matcher *root, const void *binhash, cli_has
|
|||
|
||||
htitem.key = size;
|
||||
htitem.data.as_ptr = szh;
|
||||
i = CLI_HTU32_INSERT(ht, &htitem, root->mempool);
|
||||
if (i) {
|
||||
ret = CLI_HTU32_INSERT(ht, &htitem, root->mempool);
|
||||
if (CL_SUCCESS != ret) {
|
||||
cli_errmsg("hm_addhash_bin: failed to add item to hashtab");
|
||||
MPOOL_FREE(root->mempool, szh);
|
||||
return CL_ERROR;
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
szh = (struct cli_sz_hash *)item->data.as_ptr;
|
||||
|
|
|
@ -30,6 +30,13 @@
|
|||
#include "matcher-hash-types.h"
|
||||
#include "hashtab.h"
|
||||
|
||||
typedef enum {
|
||||
HASH_PURPOSE_PE_SECTION_DETECT = 0, /** PE section hash malware detection (aka .mdb, .mdu, .msb, .msu) */
|
||||
HASH_PURPOSE_WHOLE_FILE_DETECT, /** Whole file hash malware detection (aka .hdb, .hdu, .hsb, .hsu) */
|
||||
HASH_PURPOSE_WHOLE_FILE_FP_CHECK, /** Whole file false positive prevention (aka .fp, .sfp) */
|
||||
HASH_PURPOSE_PE_IMPORT_DETECT /** PE import hash malware detection (aka .imp) */
|
||||
} hash_purpose_t;
|
||||
|
||||
struct cli_sz_hash {
|
||||
uint8_t *hash_array;
|
||||
const char **virusnames;
|
||||
|
@ -44,8 +51,8 @@ struct cli_hash_wild {
|
|||
struct cli_sz_hash hashes[CLI_HASH_AVAIL_TYPES];
|
||||
};
|
||||
|
||||
cl_error_t hm_addhash_str(struct cli_matcher *root, const char *strhash, uint32_t size, const char *virusname);
|
||||
cl_error_t hm_addhash_bin(struct cli_matcher *root, const void *binhash, cli_hash_type_t type, uint32_t size, const char *virusname);
|
||||
cl_error_t hm_addhash_str(struct cl_engine *engine, hash_purpose_t purpose, const char *strhash, uint32_t size, const char *virusname);
|
||||
cl_error_t hm_addhash_bin(struct cl_engine *engine, hash_purpose_t purpose, const void *binhash, cli_hash_type_t type, uint32_t size, const char *virusname);
|
||||
void hm_flush(struct cli_matcher *root);
|
||||
cl_error_t cli_hm_scan(const uint8_t *digest, uint32_t size, const char **virname, const struct cli_matcher *root, cli_hash_type_t type);
|
||||
cl_error_t cli_hm_scan_wild(const uint8_t *digest, const char **virname, const struct cli_matcher *root, cli_hash_type_t type);
|
||||
|
|
|
@ -311,7 +311,7 @@ int openioc_parse(const char *fname, int fd, struct cl_engine *engine, unsigned
|
|||
|
||||
free(vp);
|
||||
|
||||
rc = hm_addhash_str(engine->hm_hdb, hash, 0, virusname);
|
||||
rc = hm_addhash_str(engine, HASH_PURPOSE_WHOLE_FILE_DETECT, hash, 0, virusname);
|
||||
if (rc != CL_SUCCESS)
|
||||
cli_dbgmsg("openioc_parse: hm_addhash_str failed with %i hash len %i for %s.\n",
|
||||
rc, hashlen, virusname);
|
||||
|
|
|
@ -505,6 +505,13 @@ struct cl_engine *cl_engine_new(void)
|
|||
new->ac_mindepth = CLI_DEFAULT_AC_MINDEPTH;
|
||||
new->ac_maxdepth = CLI_DEFAULT_AC_MAXDEPTH;
|
||||
|
||||
/* Enable FIPS limits if the linked OpenSSL library is in FIPS mode. */
|
||||
#if OPENSSL_VERSION_MAJOR >= 3
|
||||
if (EVP_default_properties_is_fips_enabled(NULL)) new->engine_options |= ENGINE_OPTIONS_FIPS_LIMITS;
|
||||
#else
|
||||
if (FIPS_mode()) new->engine_options |= ENGINE_OPTIONS_FIPS_LIMITS;
|
||||
#endif
|
||||
|
||||
#ifdef USE_MPOOL
|
||||
if (!(new->mempool = mpool_create())) {
|
||||
cli_errmsg("cl_engine_new: Can't allocate memory for memory pool\n");
|
||||
|
@ -851,6 +858,13 @@ cl_error_t cl_engine_set_num(struct cl_engine *engine, enum cl_engine_field fiel
|
|||
engine->engine_options &= ~(ENGINE_OPTIONS_PE_DUMPCERTS);
|
||||
}
|
||||
break;
|
||||
case CL_ENGINE_FIPS_LIMITS:
|
||||
if (num) {
|
||||
engine->engine_options |= ENGINE_OPTIONS_FIPS_LIMITS;
|
||||
} else {
|
||||
engine->engine_options &= ~(ENGINE_OPTIONS_FIPS_LIMITS);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
cli_errmsg("cl_engine_set_num: Incorrect field number\n");
|
||||
return CL_EARG;
|
||||
|
|
|
@ -2820,48 +2820,20 @@ static int cli_loadign(FILE *fs, struct cl_engine *engine, unsigned int options,
|
|||
return CL_SUCCESS;
|
||||
}
|
||||
|
||||
#define MD5_HDB 0
|
||||
#define MD5_MDB 1
|
||||
#define MD5_FP 2
|
||||
#define MD5_IMP 3
|
||||
|
||||
#define MD5_TOKENS 5
|
||||
static int cli_loadhash(FILE *fs, struct cl_engine *engine, unsigned int *signo, unsigned int mode, unsigned int options, struct cli_dbio *dbio, const char *dbname)
|
||||
#define HASH_DB_TOKENS 5
|
||||
static int cli_loadhash(FILE *fs, struct cl_engine *engine, unsigned int *signo, hash_purpose_t purpose, unsigned int options, struct cli_dbio *dbio, const char *dbname)
|
||||
{
|
||||
const char *tokens[MD5_TOKENS + 1];
|
||||
const char *tokens[HASH_DB_TOKENS + 1];
|
||||
char buffer[FILEBUFF], *buffer_cpy = NULL;
|
||||
const char *pt, *virname;
|
||||
int ret = CL_SUCCESS;
|
||||
unsigned int size_field = 1, md5_field = 0, line = 0, sigs = 0, tokens_count;
|
||||
unsigned int size_field = 1, hash_field = 0, line = 0, sigs = 0, tokens_count;
|
||||
unsigned int req_fl = 0;
|
||||
struct cli_matcher *db;
|
||||
unsigned long size;
|
||||
|
||||
if (mode == MD5_MDB) {
|
||||
if (purpose == HASH_PURPOSE_PE_SECTION_DETECT) {
|
||||
size_field = 0;
|
||||
md5_field = 1;
|
||||
db = engine->hm_mdb;
|
||||
} else if (mode == MD5_HDB)
|
||||
db = engine->hm_hdb;
|
||||
else if (mode == MD5_IMP)
|
||||
db = engine->hm_imp;
|
||||
else
|
||||
db = engine->hm_fp;
|
||||
|
||||
if (!db) {
|
||||
if (!(db = MPOOL_CALLOC(engine->mempool, 1, sizeof(*db))))
|
||||
return CL_EMEM;
|
||||
#ifdef USE_MPOOL
|
||||
db->mempool = engine->mempool;
|
||||
#endif
|
||||
if (mode == MD5_HDB)
|
||||
engine->hm_hdb = db;
|
||||
else if (mode == MD5_MDB)
|
||||
engine->hm_mdb = db;
|
||||
else if (mode == MD5_IMP)
|
||||
engine->hm_imp = db;
|
||||
else
|
||||
engine->hm_fp = db;
|
||||
hash_field = 1;
|
||||
}
|
||||
|
||||
if (engine->ignored)
|
||||
|
@ -2878,23 +2850,23 @@ static int cli_loadhash(FILE *fs, struct cl_engine *engine, unsigned int *signo,
|
|||
if (engine->ignored)
|
||||
strcpy(buffer_cpy, buffer);
|
||||
|
||||
tokens_count = cli_strtokenize(buffer, ':', MD5_TOKENS + 1, tokens);
|
||||
tokens_count = cli_strtokenize(buffer, ':', HASH_DB_TOKENS + 1, tokens);
|
||||
if (tokens_count < 3) {
|
||||
ret = CL_EMALFDB;
|
||||
break;
|
||||
}
|
||||
if (tokens_count > MD5_TOKENS - 2) {
|
||||
req_fl = atoi(tokens[MD5_TOKENS - 2]);
|
||||
if (tokens_count > HASH_DB_TOKENS - 2) {
|
||||
req_fl = atoi(tokens[HASH_DB_TOKENS - 2]);
|
||||
|
||||
if (tokens_count > MD5_TOKENS) {
|
||||
if (tokens_count > HASH_DB_TOKENS) {
|
||||
ret = CL_EMALFDB;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cl_retflevel() < req_fl)
|
||||
continue;
|
||||
if (tokens_count == MD5_TOKENS) {
|
||||
int max_fl = atoi(tokens[MD5_TOKENS - 1]);
|
||||
if (tokens_count == HASH_DB_TOKENS) {
|
||||
int max_fl = atoi(tokens[HASH_DB_TOKENS - 1]);
|
||||
if (cl_retflevel() > (unsigned int)max_fl)
|
||||
continue;
|
||||
}
|
||||
|
@ -2914,7 +2886,7 @@ static int cli_loadhash(FILE *fs, struct cl_engine *engine, unsigned int *signo,
|
|||
// is specified. This check doesn't apply to .imp rules, though,
|
||||
// since this rule category wasn't introduced until FLEVEL 90, and
|
||||
// has always supported wildcard usage in rules.
|
||||
if (mode != MD5_IMP && ((tokens_count < MD5_TOKENS - 1) || (req_fl < 73))) {
|
||||
if (purpose != HASH_PURPOSE_PE_IMPORT_DETECT && ((tokens_count < HASH_DB_TOKENS - 1) || (req_fl < 73))) {
|
||||
cli_errmsg("cli_loadhash: Minimum FLEVEL field must be at least 73 for wildcard size hash signatures."
|
||||
" For reference, running FLEVEL is %d\n",
|
||||
cl_retflevel());
|
||||
|
@ -2949,7 +2921,7 @@ static int cli_loadhash(FILE *fs, struct cl_engine *engine, unsigned int *signo,
|
|||
break;
|
||||
}
|
||||
|
||||
if (CL_SUCCESS != (ret = hm_addhash_str(db, tokens[md5_field], size, virname))) {
|
||||
if (CL_SUCCESS != (ret = hm_addhash_str(engine, purpose, tokens[hash_field], size, virname))) {
|
||||
cli_errmsg("cli_loadhash: Malformed hash string at line %u\n", line);
|
||||
MPOOL_FREE(engine->mempool, (void *)virname);
|
||||
break;
|
||||
|
@ -4776,35 +4748,35 @@ cl_error_t cli_load(const char *filename, struct cl_engine *engine, unsigned int
|
|||
ret = cli_loaddb(fs, engine, signo, options, dbio, dbname);
|
||||
|
||||
} else if (cli_strbcasestr(dbname, ".cvd")) {
|
||||
ret = cli_cvdload(engine, signo, options, CVD_TYPE_CVD, filename, sign_verifier, 0);
|
||||
ret = cli_cvdload(engine, signo, options, CVD_TYPE_CVD, filename, sign_verifier, false);
|
||||
|
||||
} else if (cli_strbcasestr(dbname, ".cld")) {
|
||||
ret = cli_cvdload(engine, signo, options, CVD_TYPE_CLD, filename, sign_verifier, 0);
|
||||
ret = cli_cvdload(engine, signo, options, CVD_TYPE_CLD, filename, sign_verifier, false);
|
||||
|
||||
} else if (cli_strbcasestr(dbname, ".cud")) {
|
||||
ret = cli_cvdload(engine, signo, options, CVD_TYPE_CUD, filename, sign_verifier, 0);
|
||||
ret = cli_cvdload(engine, signo, options, CVD_TYPE_CUD, filename, sign_verifier, false);
|
||||
|
||||
} else if (cli_strbcasestr(dbname, ".crb")) {
|
||||
ret = cli_loadcrt(fs, engine, dbio);
|
||||
|
||||
} else if (cli_strbcasestr(dbname, ".hdb") || cli_strbcasestr(dbname, ".hsb")) {
|
||||
ret = cli_loadhash(fs, engine, signo, MD5_HDB, options, dbio, dbname);
|
||||
ret = cli_loadhash(fs, engine, signo, HASH_PURPOSE_WHOLE_FILE_DETECT, options, dbio, dbname);
|
||||
} else if (cli_strbcasestr(dbname, ".hdu") || cli_strbcasestr(dbname, ".hsu")) {
|
||||
if (options & CL_DB_PUA)
|
||||
ret = cli_loadhash(fs, engine, signo, MD5_HDB, options | CL_DB_PUA_MODE, dbio, dbname);
|
||||
ret = cli_loadhash(fs, engine, signo, HASH_PURPOSE_WHOLE_FILE_DETECT, options | CL_DB_PUA_MODE, dbio, dbname);
|
||||
else
|
||||
skipped = 1;
|
||||
|
||||
} else if (cli_strbcasestr(dbname, ".fp") || cli_strbcasestr(dbname, ".sfp")) {
|
||||
ret = cli_loadhash(fs, engine, signo, MD5_FP, options, dbio, dbname);
|
||||
ret = cli_loadhash(fs, engine, signo, HASH_PURPOSE_WHOLE_FILE_FP_CHECK, options, dbio, dbname);
|
||||
} else if (cli_strbcasestr(dbname, ".mdb") || cli_strbcasestr(dbname, ".msb")) {
|
||||
ret = cli_loadhash(fs, engine, signo, MD5_MDB, options, dbio, dbname);
|
||||
ret = cli_loadhash(fs, engine, signo, HASH_PURPOSE_PE_SECTION_DETECT, options, dbio, dbname);
|
||||
} else if (cli_strbcasestr(dbname, ".imp")) {
|
||||
ret = cli_loadhash(fs, engine, signo, MD5_IMP, options, dbio, dbname);
|
||||
ret = cli_loadhash(fs, engine, signo, HASH_PURPOSE_PE_IMPORT_DETECT, options, dbio, dbname);
|
||||
|
||||
} else if (cli_strbcasestr(dbname, ".mdu") || cli_strbcasestr(dbname, ".msu")) {
|
||||
if (options & CL_DB_PUA)
|
||||
ret = cli_loadhash(fs, engine, signo, MD5_MDB, options | CL_DB_PUA_MODE, dbio, dbname);
|
||||
ret = cli_loadhash(fs, engine, signo, HASH_PURPOSE_PE_SECTION_DETECT, options | CL_DB_PUA_MODE, dbio, dbname);
|
||||
else
|
||||
skipped = 1;
|
||||
|
||||
|
|
|
@ -437,7 +437,7 @@ impl CVD {
|
|||
pub fn verify(
|
||||
&mut self,
|
||||
verifier: Option<&Verifier>,
|
||||
disable_md5: bool,
|
||||
disable_legacy_dsig: bool,
|
||||
) -> Result<String, Error> {
|
||||
// First try to verify the CVD with the detached signature file.
|
||||
// If that fails, fall back to verifying with the MD5-based attached RSA digital signature.
|
||||
|
@ -464,7 +464,7 @@ impl CVD {
|
|||
debug!("No certs directory provided. Skipping external signature verification.");
|
||||
}
|
||||
|
||||
if disable_md5 {
|
||||
if disable_legacy_dsig {
|
||||
warn!("Unable to verify CVD with detached signature file and MD5 verification is disabled");
|
||||
return Err(Error::CannotVerify("Unable to verify CVD with detached signature file and MD5 verification is disabled".to_string()));
|
||||
}
|
||||
|
@ -497,7 +497,7 @@ pub unsafe extern "C" fn cvd_check(
|
|||
cvd_file_path_str: *const c_char,
|
||||
certs_directory_str: *const c_char,
|
||||
skip_sign_verify: bool,
|
||||
disable_md5: bool,
|
||||
disable_legacy_dsig: bool,
|
||||
signer_name: *mut *mut c_char,
|
||||
err: *mut *mut FFIError,
|
||||
) -> bool {
|
||||
|
@ -537,7 +537,7 @@ pub unsafe extern "C" fn cvd_check(
|
|||
return true;
|
||||
}
|
||||
|
||||
match cvd.verify(Some(&verifier), disable_md5) {
|
||||
match cvd.verify(Some(&verifier), disable_legacy_dsig) {
|
||||
Ok(signer) => {
|
||||
let signer_cstr = std::ffi::CString::new(signer).unwrap();
|
||||
*signer_name = signer_cstr.into_raw();
|
||||
|
@ -644,14 +644,14 @@ pub unsafe extern "C" fn cvd_open(
|
|||
pub unsafe extern "C" fn cvd_verify(
|
||||
cvd: *const c_void,
|
||||
verifier_ptr: *const c_void,
|
||||
disable_md5: bool,
|
||||
disable_legacy_dsig: bool,
|
||||
signer_name: *mut *mut c_char,
|
||||
err: *mut *mut FFIError,
|
||||
) -> bool {
|
||||
let mut cvd = ManuallyDrop::new(Box::from_raw(cvd as *mut CVD));
|
||||
|
||||
if verifier_ptr.is_null() {
|
||||
match cvd.verify(None, disable_md5) {
|
||||
match cvd.verify(None, disable_legacy_dsig) {
|
||||
Ok(signer) => {
|
||||
let signer_cstr = std::ffi::CString::new(signer).unwrap();
|
||||
*signer_name = signer_cstr.into_raw();
|
||||
|
@ -664,7 +664,7 @@ pub unsafe extern "C" fn cvd_verify(
|
|||
} else {
|
||||
let verifier = ManuallyDrop::new(Box::from_raw(verifier_ptr as *mut Verifier));
|
||||
|
||||
match cvd.verify(Some(&verifier), disable_md5) {
|
||||
match cvd.verify(Some(&verifier), disable_legacy_dsig) {
|
||||
Ok(signer) => {
|
||||
let signer_cstr = std::ffi::CString::new(signer).unwrap();
|
||||
*signer_name = signer_cstr.into_raw();
|
||||
|
|
|
@ -281,6 +281,8 @@ fc_error_t fc_initialize(fc_config *fcConfig)
|
|||
|
||||
g_bCompressLocalDatabase = fcConfig->bCompressLocalDatabase;
|
||||
|
||||
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");
|
||||
|
@ -464,6 +466,7 @@ fc_error_t fc_test_database(const char *dbFilename, int bBytecodeEnabled)
|
|||
struct cl_engine *engine = NULL;
|
||||
unsigned newsigs = 0;
|
||||
cl_error_t cl_ret;
|
||||
unsigned int dboptions = 0;
|
||||
|
||||
if ((NULL == dbFilename)) {
|
||||
logg(LOGG_WARNING, "fc_test_database: Invalid arguments.\n");
|
||||
|
@ -482,10 +485,16 @@ fc_error_t fc_test_database(const char *dbFilename, int bBytecodeEnabled)
|
|||
|
||||
cl_engine_set_clcb_stats_submit(engine, NULL);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (CL_SUCCESS != (cl_ret = cl_load(
|
||||
dbFilename, engine, &newsigs,
|
||||
CL_DB_PHISHING | CL_DB_PHISHING_URLS | CL_DB_BYTECODE |
|
||||
CL_DB_PUA | CL_DB_ENHANCED))) {
|
||||
dbFilename,
|
||||
engine,
|
||||
&newsigs,
|
||||
dboptions))) {
|
||||
logg(LOGG_ERROR, "Failed to load new database: %s\n", cl_strerror(cl_ret));
|
||||
status = FC_ETESTFAIL;
|
||||
goto done;
|
||||
|
|
|
@ -60,7 +60,10 @@ typedef struct fc_config_ {
|
|||
const char *proxyPassword; /**< (optional) Password for proxy server authentication. */
|
||||
const char *databaseDirectory; /**< Filepath of database directory. */
|
||||
const char *tempDirectory; /**< Filepath to store temp files. */
|
||||
const char *certsDirectory; /**< Filepath of clamav ca certificates directory to verify database external digital signatures. */
|
||||
const char *certsDirectory; /**< Filepath of clamav ca certificates directory to verify database external
|
||||
* digital signatures. */
|
||||
bool bFipsLimits; /**< If true, enable FIPS cryptographic hashing limitations that will require CVDs
|
||||
* to be signed with FIPS-compliant external '.sign' file. */
|
||||
} fc_config;
|
||||
|
||||
typedef enum fc_error_tag {
|
||||
|
|
|
@ -121,6 +121,8 @@ freshclam_dat_v1_t *g_freshclamDat = NULL;
|
|||
|
||||
uint8_t g_lastRay[CFRAY_LEN + 1] = {0};
|
||||
|
||||
bool g_bFipsLimits = false;
|
||||
|
||||
/** @brief Generate a Version 4 UUID according to RFC-4122
|
||||
*
|
||||
* Uses the openssl RAND_bytes function to generate a Version 4 UUID.
|
||||
|
@ -1550,7 +1552,7 @@ static fc_error_t getcvd(
|
|||
}
|
||||
|
||||
// Now that we have the cvd and the sign file, we can verify the cvd.
|
||||
if (CL_SUCCESS != (cl_ret = cl_cvdverify(tmpfile))) {
|
||||
if (CL_SUCCESS != (cl_ret = cli_cvdverify(tmpfile, g_bFipsLimits, g_signVerifier))) {
|
||||
logg(LOGG_ERROR, "Verification: %s\n", cl_strerror(cl_ret));
|
||||
status = FC_EBADCVD;
|
||||
goto done;
|
||||
|
@ -1663,7 +1665,7 @@ static fc_error_t mkdir_and_chdir_for_cdiff_tmp(const char *database, const char
|
|||
/*
|
||||
* 3) Unpack the existing CVD/CLD database to this directory.
|
||||
*/
|
||||
if (CL_SUCCESS != cli_cvdunpack_and_verify(cvdfile, tmpdir, is_cld == true, g_signVerifier)) {
|
||||
if (CL_SUCCESS != cli_cvdunpack_and_verify(cvdfile, tmpdir, is_cld == true, g_bFipsLimits, g_signVerifier)) {
|
||||
logg(LOGG_ERROR, "mkdir_and_chdir_for_cdiff_tmp: Can't unpack %s into %s\n", cvdfile, tmpdir);
|
||||
cli_rmdirs(tmpdir);
|
||||
goto done;
|
||||
|
|
|
@ -70,6 +70,8 @@ extern uint32_t g_bCompressLocalDatabase;
|
|||
extern freshclam_dat_v1_t *g_freshclamDat;
|
||||
extern uint8_t g_lastRay[CFRAY_LEN + 1];
|
||||
|
||||
extern bool g_bFipsLimits;
|
||||
|
||||
fc_error_t load_freshclam_dat(void);
|
||||
fc_error_t save_freshclam_dat(void);
|
||||
fc_error_t new_freshclam_dat(void);
|
||||
|
|
|
@ -1155,6 +1155,7 @@ static int build(const struct optstruct *opts)
|
|||
unsigned int dblist2cnt = 0;
|
||||
DIR *dd;
|
||||
struct dirent *dent;
|
||||
unsigned int dboptions = 0;
|
||||
|
||||
#define FREE_LS(x) \
|
||||
for (i = 0; i < dblist2cnt; i++) \
|
||||
|
@ -1194,7 +1195,13 @@ static int build(const struct optstruct *opts)
|
|||
}
|
||||
}
|
||||
|
||||
if ((ret = cl_load(".", engine, &sigs, CL_DB_STDOPT | CL_DB_PUA | CL_DB_SIGNED))) {
|
||||
dboptions = CL_DB_STDOPT | CL_DB_PUA | CL_DB_SIGNED;
|
||||
|
||||
if (optget(opts, "fips-limits")->enabled) {
|
||||
dboptions |= CL_DB_FIPS_LIMITS;
|
||||
}
|
||||
|
||||
if ((ret = cl_load(".", engine, &sigs, dboptions))) {
|
||||
mprintf(LOGG_ERROR, "build: Can't load database: %s\n", cl_strerror(ret));
|
||||
cl_engine_free(engine);
|
||||
return -1;
|
||||
|
@ -1541,7 +1548,7 @@ static int build(const struct optstruct *opts)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (CL_SUCCESS != cl_cvdunpack_ex(olddb, pt, true, NULL)) {
|
||||
if (CL_SUCCESS != cl_cvdunpack_ex(olddb, pt, NULL, CL_DB_UNSIGNED)) {
|
||||
mprintf(LOGG_ERROR, "build: Can't unpack CVD file %s\n", olddb);
|
||||
removeTempDir(opts, pt);
|
||||
free(pt);
|
||||
|
@ -1556,7 +1563,7 @@ static int build(const struct optstruct *opts)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (CL_SUCCESS != cl_cvdunpack_ex(newcvd, pt, true, NULL)) {
|
||||
if (CL_SUCCESS != cl_cvdunpack_ex(newcvd, pt, NULL, CL_DB_UNSIGNED)) {
|
||||
mprintf(LOGG_ERROR, "build: Can't unpack CVD file %s\n", newcvd);
|
||||
removeTempDir(opts, pt);
|
||||
free(pt);
|
||||
|
@ -1605,7 +1612,9 @@ static int build(const struct optstruct *opts)
|
|||
static int unpack(const struct optstruct *opts)
|
||||
{
|
||||
char name[512], *dbdir;
|
||||
const char *localdbdir = NULL;
|
||||
const char *localdbdir = NULL;
|
||||
const char *certs_directory = NULL;
|
||||
uint32_t dboptions = 0;
|
||||
|
||||
if (optget(opts, "datadir")->active)
|
||||
localdbdir = optget(opts, "datadir")->strarg;
|
||||
|
@ -1628,12 +1637,27 @@ static int unpack(const struct optstruct *opts)
|
|||
name[sizeof(name) - 1] = '\0';
|
||||
}
|
||||
|
||||
if (cl_cvdverify_ex(name, g_cvdcertsdir) != CL_SUCCESS) {
|
||||
certs_directory = optget(opts, "cvdcertsdir")->strarg;
|
||||
if (NULL == certs_directory) {
|
||||
// Check if the CVD_CERTS_DIR environment variable is set
|
||||
certs_directory = getenv("CVD_CERTS_DIR");
|
||||
|
||||
// If not, use the default value
|
||||
if (NULL == certs_directory) {
|
||||
certs_directory = OPT_CERTSDIR;
|
||||
}
|
||||
}
|
||||
|
||||
if (optget(opts, "fips-limits")->enabled) {
|
||||
dboptions |= CL_DB_FIPS_LIMITS;
|
||||
}
|
||||
|
||||
if (cl_cvdverify_ex(name, g_cvdcertsdir, dboptions) != CL_SUCCESS) {
|
||||
mprintf(LOGG_ERROR, "unpack: %s is not a valid CVD\n", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (CL_SUCCESS != cl_cvdunpack_ex(name, ".", true, NULL)) {
|
||||
if (CL_SUCCESS != cl_cvdunpack_ex(name, ".", NULL, CL_DB_UNSIGNED)) {
|
||||
mprintf(LOGG_ERROR, "unpack: Can't unpack file %s\n", name);
|
||||
return -1;
|
||||
}
|
||||
|
@ -1646,6 +1670,8 @@ static int cvdinfo(const struct optstruct *opts)
|
|||
struct cl_cvd *cvd;
|
||||
char *pt;
|
||||
int ret;
|
||||
const char *certs_directory = NULL;
|
||||
uint32_t dboptions = 0;
|
||||
|
||||
pt = optget(opts, "info")->strarg;
|
||||
if ((cvd = cl_cvdhead(pt)) == NULL) {
|
||||
|
@ -1672,13 +1698,32 @@ static int cvdinfo(const struct optstruct *opts)
|
|||
mprintf(LOGG_INFO, "Digital signature: %s\n", cvd->dsig);
|
||||
}
|
||||
cl_cvdfree(cvd);
|
||||
if (cli_strbcasestr(pt, ".cud"))
|
||||
|
||||
certs_directory = optget(opts, "cvdcertsdir")->strarg;
|
||||
if (NULL == certs_directory) {
|
||||
// Check if the CVD_CERTS_DIR environment variable is set
|
||||
certs_directory = getenv("CVD_CERTS_DIR");
|
||||
|
||||
// If not, use the default value
|
||||
if (NULL == certs_directory) {
|
||||
certs_directory = OPT_CERTSDIR;
|
||||
}
|
||||
}
|
||||
|
||||
if (optget(opts, "fips-limits")->enabled) {
|
||||
dboptions |= CL_DB_FIPS_LIMITS;
|
||||
}
|
||||
|
||||
if (cli_strbcasestr(pt, ".cud")) {
|
||||
mprintf(LOGG_INFO, "Verification: Unsigned container\n");
|
||||
else if ((ret = cl_cvdverify(pt))) {
|
||||
|
||||
} else if ((ret = cl_cvdverify_ex(pt, certs_directory, dboptions))) {
|
||||
mprintf(LOGG_ERROR, "cvdinfo: Verification: %s\n", cl_strerror(ret));
|
||||
return -1;
|
||||
} else
|
||||
|
||||
} else {
|
||||
mprintf(LOGG_INFO, "Verification OK.\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1780,7 +1825,7 @@ static int listdb(const struct optstruct *opts, const char *filename, const rege
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (CL_SUCCESS != cl_cvdunpack_ex(filename, dir, true, NULL)) {
|
||||
if (CL_SUCCESS != cl_cvdunpack_ex(filename, dir, NULL, CL_DB_UNSIGNED)) {
|
||||
mprintf(LOGG_ERROR, "listdb: Can't unpack CVD file %s\n", filename);
|
||||
removeTempDir(opts, dir);
|
||||
free(dir);
|
||||
|
@ -2582,7 +2627,7 @@ static int verifydiff(const struct optstruct *opts, const char *diff, const char
|
|||
created_temp_dir = true;
|
||||
|
||||
if (cvd) {
|
||||
if (CL_SUCCESS != cl_cvdunpack_ex(cvd, tempdir, true, NULL)) {
|
||||
if (CL_SUCCESS != cl_cvdunpack_ex(cvd, tempdir, NULL, CL_DB_UNSIGNED)) {
|
||||
mprintf(LOGG_ERROR, "verifydiff: Can't unpack CVD file %s\n", cvd);
|
||||
goto done;
|
||||
}
|
||||
|
@ -3818,7 +3863,7 @@ static int makediff(const struct optstruct *opts)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (CL_SUCCESS != cl_cvdunpack_ex(optget(opts, "diff")->strarg, odir, true, NULL)) {
|
||||
if (CL_SUCCESS != cl_cvdunpack_ex(optget(opts, "diff")->strarg, odir, NULL, CL_DB_UNSIGNED)) {
|
||||
mprintf(LOGG_ERROR, "makediff: Can't unpack CVD file %s\n", optget(opts, "diff")->strarg);
|
||||
removeTempDir(opts, odir);
|
||||
free(odir);
|
||||
|
@ -3829,7 +3874,7 @@ static int makediff(const struct optstruct *opts)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (CL_SUCCESS != cl_cvdunpack_ex(opts->filename[0], ndir, true, NULL)) {
|
||||
if (CL_SUCCESS != cl_cvdunpack_ex(opts->filename[0], ndir, NULL, CL_DB_UNSIGNED)) {
|
||||
mprintf(LOGG_ERROR, "makediff: Can't unpack CVD file %s\n", opts->filename[0]);
|
||||
removeTempDir(opts, odir);
|
||||
removeTempDir(opts, ndir);
|
||||
|
@ -4074,6 +4119,11 @@ static void help(void)
|
|||
mprintf(LOGG_INFO, "\n");
|
||||
mprintf(LOGG_INFO, " --unpack-current=SHORTNAME Unpack local CVD/CLD into cwd\n");
|
||||
mprintf(LOGG_INFO, "\n");
|
||||
mprintf(LOGG_INFO, " --fips-limits Enforce FIPS-like limits on using hash algorithms for\n");
|
||||
mprintf(LOGG_INFO, " cryptographic purposes. Will disable MD5 & SHA1\n");
|
||||
mprintf(LOGG_INFO, " FP sigs and will require '.sign' files to verify CVD\n");
|
||||
mprintf(LOGG_INFO, " authenticity.\n");
|
||||
mprintf(LOGG_INFO, "\n");
|
||||
mprintf(LOGG_INFO, " Commands for working with CDIFF patch files:\n");
|
||||
mprintf(LOGG_INFO, "\n");
|
||||
mprintf(LOGG_INFO, " --diff=OLD NEW -d OLD NEW Create diff for OLD and NEW CVDs\n");
|
||||
|
|
|
@ -434,7 +434,7 @@ START_TEST(test_cl_load)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
/* cl_error_t cl_cvdverify(const char *file) */
|
||||
/* cl_error_t cl_cvdverify_ex(const char *file, const char *certs_directory, uint32_t dboptions) */
|
||||
START_TEST(test_cl_cvdverify)
|
||||
{
|
||||
cl_error_t ret;
|
||||
|
@ -450,22 +450,22 @@ START_TEST(test_cl_cvdverify)
|
|||
|
||||
// Should be able to verify this cvd
|
||||
testfile = SRCDIR "/input/freshclam_testfiles/test-1.cvd";
|
||||
ret = cl_cvdverify_ex(testfile, cvdcertsdir);
|
||||
ret = cl_cvdverify_ex(testfile, cvdcertsdir, 0);
|
||||
ck_assert_msg(CL_SUCCESS == ret, "cl_cvdverify_ex failed for: %s -- %s", testfile, cl_strerror(ret));
|
||||
|
||||
// Can't verify a cvd that doesn't exist
|
||||
testfile = SRCDIR "/input/freshclam_testfiles/test-na.cvd";
|
||||
ret = cl_cvdverify_ex(testfile, cvdcertsdir);
|
||||
ret = cl_cvdverify_ex(testfile, cvdcertsdir, 0);
|
||||
ck_assert_msg(CL_ECVD == ret, "cl_cvdverify_ex should have failed for: %s -- %s", testfile, cl_strerror(ret));
|
||||
|
||||
// A cdiff is not a cvd. Cannot verify with cl_cvdverify_ex!
|
||||
testfile = SRCDIR "/input/freshclam_testfiles/test-2.cdiff";
|
||||
ret = cl_cvdverify_ex(testfile, cvdcertsdir);
|
||||
ret = cl_cvdverify_ex(testfile, cvdcertsdir, 0);
|
||||
ck_assert_msg(CL_ECVD == ret, "cl_cvdverify_ex should have failed for: %s -- %s", testfile, cl_strerror(ret));
|
||||
|
||||
// Can't verify an hdb file
|
||||
testfile = SRCDIR "/input/clamav.hdb";
|
||||
ret = cl_cvdverify_ex(testfile, cvdcertsdir);
|
||||
ret = cl_cvdverify_ex(testfile, cvdcertsdir, 0);
|
||||
ck_assert_msg(CL_ECVD == ret, "cl_cvdverify_ex should have failed for: %s -- %s", testfile, cl_strerror(ret));
|
||||
|
||||
// Modify the cvd to make it invalid
|
||||
|
@ -485,12 +485,12 @@ START_TEST(test_cl_cvdverify)
|
|||
fclose(new_fs);
|
||||
|
||||
// Now verify the modified cvd
|
||||
ret = cl_cvdverify(newtestfile);
|
||||
ck_assert_msg(CL_EVERIFY == ret, "cl_cvdverify should have failed for: %s -- %s", newtestfile, cl_strerror(ret));
|
||||
ret = cl_cvdverify_ex(newtestfile, cvdcertsdir, 0);
|
||||
ck_assert_msg(CL_EVERIFY == ret, "cl_cvdverify_ex should have failed for: %s -- %s", newtestfile, cl_strerror(ret));
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* cl_error_t cl_cvdunpack_ex(const char *file, const char *dir, bool dont_verify, const char* certs_directory) */
|
||||
/* cl_error_t cl_cvdunpack_ex(const char *file, const char *dir, const char *certs_directory, uint32_t dboptions) */
|
||||
START_TEST(test_cl_cvdunpack_ex)
|
||||
{
|
||||
cl_error_t ret;
|
||||
|
@ -499,12 +499,12 @@ START_TEST(test_cl_cvdunpack_ex)
|
|||
const char *testfile;
|
||||
|
||||
testfile = SRCDIR "/input/freshclam_testfiles/test-1.cvd";
|
||||
ret = cl_cvdunpack_ex(testfile, tmpdir, true, NULL);
|
||||
ret = cl_cvdunpack_ex(testfile, tmpdir, NULL, CL_DB_UNSIGNED);
|
||||
ck_assert_msg(CL_SUCCESS == ret, "cl_cvdunpack_ex: failed for: %s -- %s", testfile, cl_strerror(ret));
|
||||
|
||||
// Can't unpack a cdiff
|
||||
testfile = SRCDIR "/input/freshclam_testfiles/test-2.cdiff";
|
||||
ret = cl_cvdunpack_ex(testfile, tmpdir, true, NULL);
|
||||
ret = cl_cvdunpack_ex(testfile, tmpdir, NULL, CL_DB_UNSIGNED);
|
||||
ck_assert_msg(CL_ECVD == ret, "cl_cvdunpack_ex: should have failed for: %s -- %s", testfile, cl_strerror(ret));
|
||||
}
|
||||
END_TEST
|
||||
|
|
|
@ -77,6 +77,12 @@ Example
|
|||
# Default: "certs"
|
||||
#CVDCertsDirectory "C:\Program Files\ClamAV\certs"
|
||||
|
||||
# Enforce FIPS-like limits on using hash algorithms for cryptographic purposes.
|
||||
# Will disable MD5 & SHA1 FP sigs and will require '.sign' files to verify
|
||||
# CVD authenticity.
|
||||
# Default: no
|
||||
#FIPSCryptoHashLimits yes
|
||||
|
||||
# Only load the official signatures published by the ClamAV project.
|
||||
# Default: no
|
||||
#OfficialDatabaseOnly no
|
||||
|
|
|
@ -22,6 +22,12 @@ Example
|
|||
# Default: "certs"
|
||||
#CVDCertsDirectory "C:\Program Files\ClamAV\certs"
|
||||
|
||||
# Enforce FIPS-like limits on using hash algorithms for cryptographic purposes.
|
||||
# Will disable MD5 & SHA1 FP sigs and will require '.sign' files to verify
|
||||
# CVD authenticity.
|
||||
# Default: no
|
||||
#FIPSCryptoHashLimits yes
|
||||
|
||||
# Path to the log file (make sure it has proper permissions)
|
||||
# Default: disabled
|
||||
#UpdateLogFile "C:\Program Files\ClamAV\freshclam.log"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue