Commit graph

11 commits

Author SHA1 Message Date
Val S.
23c3cc05f1
Windows: fix number of arguments in function call (#1586)
Some checks failed
clang-format / check-16 (push) Has been cancelled
CMake Build / build-windows (push) Has been cancelled
CMake Build / build-macos (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
CMake Build / build-ubuntu (push) Has been cancelled
clang-format / check (push) Has been cancelled
clang-format / check-1 (push) Has been cancelled
clang-format / check-2 (push) Has been cancelled
clang-format / check-3 (push) Has been cancelled
clang-format / check-4 (push) Has been cancelled
clang-format / check-5 (push) Has been cancelled
clang-format / check-6 (push) Has been cancelled
clang-format / check-7 (push) Has been cancelled
clang-format / check-8 (push) Has been cancelled
clang-format / check-9 (push) Has been cancelled
clang-format / check-10 (push) Has been cancelled
clang-format / check-11 (push) Has been cancelled
clang-format / check-12 (push) Has been cancelled
clang-format / check-13 (push) Has been cancelled
clang-format / check-14 (push) Has been cancelled
clang-format / check-15 (push) Has been cancelled
CodeQL / Analyze-1 (push) Has been cancelled
Fix the number of NULL arguments in `scanmem.c` call to `cl_scandesc_ex()`.
2025-10-04 22:33:01 -04:00
Valerie Snyder
27fe03c751
Fix OpenSSL 1 compatibility issue, plus minor improvements
For OpenSSL 1, `EVP_get_digestbyname()` will fail with "sha2-*" algorithm names.
Must use "sha256", etc.

I made a shim that does the conversion, and I made an improvement to ignore case
when converting alg names to our hash type enumeration.

Other fixes for a few warnings.
2025-08-18 12:27:10 -04:00
Valerie Snyder
0ea66b540a
libclamav: Fix issue reporting trusted verdicts
If the outermost layer is trusted (e.g. using an FP signature), the verdict passed
back by the `cl_scan*_ex()` functions should be CL_VERDICT_TRUSTED.
To ensure this, and other correct verdicts, I moved the logic setting the verdict
to occur when adding indicators, or trusting a given layer. Then at the end of a
scan, it will set the output verdict parameter to the top level verdict.

This commit also:

* Fixes a bug in the `ex_scan_callbacks` program where a crash would happen when
  a hash was retrieved for an inner layer, but isn't for the container.

* Added debug logs whenever a hash is calculated or set, printing the hash type
  and hash string.

* When a layer is trusted, in addition to removing evidence for that layer, it
  will also index the metadata JSON (if that feature is enabled) and will rename
  any "Viruses" to "IgnoredAlerts", and rename "ContainedIndicators" to
  "IgnoredContainedIndicators".

* Fixed an issue where setting the hash algorithm with extra characters, like
  setting to "sha256789" would ignore the extra characters, and report the hash
  type as the same. It will now fail if the string length differs from the known
  hash algorithm.
2025-08-14 22:40:48 -04:00
Valerie Snyder
39fa61869a
Example Program: Add --disable-cache feature
Also minor fixes to sys.rs and clamav.h formatting
2025-08-14 22:40:48 -04:00
Valerie Snyder
ed3e1e55f6
Added additional ex_scan_callbacks test and fixed a couple related bugs
Improvements to the ex_scan_callbacks.c program:
- Print the verdict enum variant names to be more explicit.
- Add the file_props callback (aka metadata JSON) with --gen-json option.
- Add a --debug option.
- Use '-' in option names instead of '_' to be consistent with other programs.
- Add option to disable allmatch, which I named --one-match. :)

Tests: Add ex_scan_callbacks test where --allmatch is disabled.
Verify that CL_VIRUS is returned when a match occurs.
I found a few bugs and inconsistencies from this test and went and fixed
them, and improved the clamav.h function comments as well.
Largely this resulted in cleanup in `cli_magic_scan()` to make sure we
don't accidentally overwrite the return code.
But it also meant making sure that callback functions which are supposed
to trust a file actually clear the evidence/verdict and don't return
CL_VIRUS.
2025-08-14 22:40:47 -04:00
Valerie Snyder
6d9b57eeeb
libclamav: cl_scan*_ex() functions provide verdict separate from errors
It is a shortcoming of existing scan APIs that it is not possible
to return an error without masking a verdict.
We presently work around this limitation by counting up detections at
the end and then overriding the error code with `CL_VIRUS`, if necessary.

The `cl_scanfile_ex()`, `cl_scandesc_ex()`, and `cl_scanmap_ex()` functions
should provide the scan verdict separately from the error code.

This introduces a new enum for recording and reporting a verdict:
`cl_verdict_t` with options:

- `CL_VERDICT_NOTHING_FOUND`
- `CL_VERDICT_TRUSTED`
- `CL_VERDICT_STRONG_INDICATOR`
- `CL_VERDICT_POTENTIALLY_UNWANTED`

Notably, the newer scan APIs may set the verdict to `CL_VERDICT_TRUSTED`
if there is a (hash-based) FP signature for a file, or in the cause where
Authenticode or similar certificate-based verification was performed, or
in the case where an application scan callback returned `CL_VERIFIED`.

CLAM-763
CLAM-865
2025-08-14 22:40:46 -04:00
Valerie Snyder
9d253673f4
Add missing message string when printing CL_BREAK code
Add string message to `cl_strerror()` and in example program for
cl_error_t::CL_BREAK enum variant.

Also fix uninitialized memory use issue in example program.
2025-08-14 22:40:46 -04:00
Valerie Snyder
e223ddb66a
Example program: demonstrate more features and support scripted inputs
Scripted inputs may be used for automated tests.

Added automated tests for the example program to verify correct behavior
using different callback return codes and also using the new scan layer and
fmap API's.

Fixed a bug in ClamAV's evidence module (recording strong, PUA, and
weak indicators for each layer). Rust HashMaps are unordered so the
feature to get the last alert would return a random alert and not
specifically the last one. Switching to IndexMap resolves this, and
allows us to maintain insertion-order for iterating keys even when
removing a key.
2025-08-14 22:40:45 -04:00
Valerie Snyder
4660141186
Auto-format touch-up 2025-08-14 22:39:16 -04:00
Valerie Snyder
51adfb8b61
ClamScan & libclamav: improve precision of bytes-scanned, bytes-read
The ClamScan scan summary prints bytes scanned and bytes read in
multiples of 4096 (aka `CL_COUNT_PRECISION`), as is provided by the
`cl_scanfile()`, `cl_scandesc()`, `cl_scanfile_callback()`, and
`cl_scandesc_callback()` functions.

I believe this imprecision was the result of using an `unsigned long int`
which may be 64bit or 32bit, depending on platform. I believe the
intention was to be able to support scanning more than 4 GiB of data.

Since the new `cl_scan*_ex()` functions use a `uint64_t`, which
guarantees a 64bit integer and supports ~16,777,216 terabytes, I find no
reason not to report an accurate count.

For the legacy scan functions (above) I've kept the `CL_COUNT_PRECISION`
behavior to maintain backwards compatibility.

I have also improved the bytes scanned/read output to report GiB, MiB,
KiB, or B as appropriate. Previously, it always report "MB".

CLAM-1433
2025-08-14 22:39:15 -04:00
Valerie Snyder
f05770fb51
libclamav: scan-layer callback API functions
Add the following scan callbacks:
```c
  cl_engine_set_scan_callback(engine, &pre_hash_callback, CL_SCAN_CALLBACK_PRE_HASH);
  cl_engine_set_scan_callback(engine, &pre_scan_callback, CL_SCAN_CALLBACK_PRE_SCAN);
  cl_engine_set_scan_callback(engine, &post_scan_callback, CL_SCAN_CALLBACK_POST_SCAN);
  cl_engine_set_scan_callback(engine, &alert_callback, CL_SCAN_CALLBACK_ALERT);
  cl_engine_set_scan_callback(engine, &file_type_callback, CL_SCAN_CALLBACK_FILE_TYPE);
```

Each callback may alter scan behavior using the following return codes:

* CL_BREAK

  Scan aborted by callback (the rest of the scan is skipped).
  This does not mark the file as clean or infected, it just skips the rest of the scan.

* CL_SUCCESS / CL_CLEAN

  File scan will continue.
  This is different than CL_VERIFIED because it does not affect prior or future alerts.
  Return CL_VERIFIED instead if you want to remove prior alerts for this layer and skip
  the rest of the scan for this layer.

* CL_VIRUS

  This means you don't trust the file. A new alert will be added.
  For CL_SCAN_CALLBACK_ALERT: Means you agree with the alert (no extra alert needed).

* CL_VERIFIED

  Layer explicitly trusted by the callback and previous alerts removed FOR THIS layer.
  You might want to do this if you trust the hash or verified a digital signature.
  The rest of the scan will be skipped FOR THIS layer.
  For contained files, this does NOT mean that the parent or adjacent layers are trusted.

Each callback is given a pointer to the current scan layer from which
they can get previous layers, can get the the layer's fmap, and then
various attributes of the layer and of the fmap such as:
- layer recursion level
- layer object id
- layer file type
- layer attributes (was decerypted, normalized, embedded, or re-typed)
- layer last alert
- fmap name
- fmap hash (md5, sha1, or sha2-256)
- fmap data (pointer and size)
- fmap file descriptor, if any (fd, offset, size)
- fmap filepath, if any (filepath, offset, size)

To make this possible, this commits introduced a handful of new APIs to
query scan-layer details and fmap details:
- `cl_error_t cl_fmap_set_name(cl_fmap_t *map, const char *name);`
- `cl_error_t cl_fmap_get_name(cl_fmap_t *map, const char **name_out);`
- `cl_error_t cl_fmap_set_path(cl_fmap_t *map, const char *path);`
- `cl_error_t cl_fmap_get_path(cl_fmap_t *map, const char **path_out, size_t *offset_out, size_t *len_out);`
- `cl_error_t cl_fmap_get_fd(const cl_fmap_t *map, int *fd_out, size_t *offset_out, size_t *len_out);`
- `cl_error_t cl_fmap_get_size(const cl_fmap_t *map, size_t *size_out);`
- `cl_error_t cl_fmap_set_hash(const cl_fmap_t *map, const char *hash_alg, char hash);`
- `cl_error_t cl_fmap_have_hash(const cl_fmap_t *map, const char *hash_alg, bool *have_hash_out);`
- `cl_error_t cl_fmap_will_need_hash_later(const cl_fmap_t *map, const char *hash_alg);`
- `cl_error_t cl_fmap_get_hash(const cl_fmap_t *map, const char *hash_alg, const char **hash_out);`
- `cl_error_t cl_fmap_get_data(const cl_fmap_t *map, size_t offset, size_t len, const uint8_t **data_out, size_t *data_len_out);`
- `cl_error_t cl_scan_layer_get_fmap(cl_scan_layer_t *layer, cl_fmap_t **fmap_out);`
- `cl_error_t cl_scan_layer_get_parent_layer(cl_scan_layer_t *layer, cl_scan_layer_t **parent_layer_out);`
- `cl_error_t cl_scan_layer_get_type(cl_scan_layer_t *layer, const char **type_out);`
- `cl_error_t cl_scan_layer_get_recursion_level(cl_scan_layer_t *layer, uint32_t *recursion_level_out);`
- `cl_error_t cl_scan_layer_get_object_id(cl_scan_layer_t *layer, uint64_t *object_id_out);`
- `cl_error_t cl_scan_layer_get_last_alert(cl_scan_layer_t *layer, const char **alert_name_out);`
- `cl_error_t cl_scan_layer_get_attributes(cl_scan_layer_t *layer, uint32_t *attributes_out);`

This commit deprecates but does not remove the existing scan callbacks:
- `void cl_engine_set_clcb_pre_cache(struct cl_engine *engine, clcb_pre_cache callback);`
- `void cl_engine_set_clcb_file_inspection(struct cl_engine *engine, clcb_file_inspection callback);`
- `void cl_engine_set_clcb_pre_scan(struct cl_engine *engine, clcb_pre_scan callback);`
- `void cl_engine_set_clcb_post_scan(struct cl_engine *engine, clcb_post_scan callback);`
- `void cl_engine_set_clcb_virus_found(struct cl_engine *engine, clcb_virus_found callback);`
- `void cl_engine_set_clcb_hash(struct cl_engine *engine, clcb_hash callback);`

This commit also adds an interactive test program to demonstrate the callbacks.
See: `examples/ex_scan_callbacks.c`

CLAM-255
CLAM-2485
CLAM-2626
2025-08-14 22:39:14 -04:00