diff --git a/CMakeLists.txt b/CMakeLists.txt
index 61612cb6e8..cc6e7a7ec9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -370,8 +370,6 @@ elseif(APPLE)
)
if(${MARKDOWN_MODULE_EXIT_CODE} EQUAL 0)
# The markdown module is installed, we can do the conversion.
- set(PythonTest_COMMAND "${Python3_EXECUTABLE};-m;markdown;-v")
-
execute_process(
COMMAND echo "
"
OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/prefix.html)
@@ -891,6 +889,7 @@ else()
extract_valid_cxx_flags(WARNCXXFLAGS
-Wall
-Wformat-security
+ -Wno-comment # Disabled because LLVM's CFG.h has a warning about a multiline comment because of ascii-art of a graph with a `\` in it.
)
endif()
diff --git a/clambc/bcrun.c b/clambc/bcrun.c
index dbf88b1338..4d7d55a8c9 100644
--- a/clambc/bcrun.c
+++ b/clambc/bcrun.c
@@ -394,8 +394,8 @@ int main(int argc, char *argv[])
fprintf(stderr, "Out of memory\n");
exit(3);
}
- ctx->ctx = &cctx;
- cctx.engine = engine;
+ ctx->ctx = &cctx;
+ cctx.engine = engine;
cctx.evidence = evidence_new();
cctx.recursion_stack_size = cctx.engine->max_recursion_level;
diff --git a/common/cert_util.c b/common/cert_util.c
index 78f8d657ca..567863abbe 100644
--- a/common/cert_util.c
+++ b/common/cert_util.c
@@ -85,7 +85,7 @@ static cl_error_t _x509_to_pem(X509 *cert,
ret = CL_SUCCESS;
done:
- return (0);
+ return ret;
}
/**
@@ -125,7 +125,7 @@ static cl_error_t _x509_to_pem_append(X509 *ca_cert,
current_len = *total_buf_len;
- if (_x509_to_pem(ca_cert, &pem_data, &pem_data_len) != 0) {
+ if (CL_SUCCESS != _x509_to_pem(ca_cert, &pem_data, &pem_data_len)) {
mprintf(LOGG_ERROR, "Failed to convert x509 certificate to PEM\n");
goto done;
}
diff --git a/libclamav/7z_iface.c b/libclamav/7z_iface.c
index 69a3c8c721..abe93a9207 100644
--- a/libclamav/7z_iface.c
+++ b/libclamav/7z_iface.c
@@ -90,7 +90,6 @@ int cli_7unz(cli_ctx *ctx, size_t offset)
int namelen = UTFBUFSZ;
cl_error_t found = CL_CLEAN;
Int64 begin_of_archive = offset;
- UInt32 viruses_found = 0;
/* Replacement for
FileInStream_CreateVTable(&archiveStream); */
@@ -111,7 +110,7 @@ int cli_7unz(cli_ctx *ctx, size_t offset)
res = SzArEx_Open(&db, &lookStream.s, &allocImp, &allocTempImp);
if (res == SZ_ERROR_ENCRYPTED && SCAN_HEURISTIC_ENCRYPTED_ARCHIVE) {
cli_dbgmsg("cli_7unz: Encrypted header found in archive.\n");
- found = cli_append_virus(ctx, "Heuristics.Encrypted.7Zip");
+ found = cli_append_potentially_unwanted(ctx, "Heuristics.Encrypted.7Zip");
} else if (res == SZ_OK) {
UInt32 i, blockIndex = 0xFFFFFFFF;
Byte *outBuffer = 0;
@@ -127,12 +126,14 @@ int cli_7unz(cli_ctx *ctx, size_t offset)
size_t j;
int newnamelen, fd;
+ // abort if we would exceed max files or max scan time.
if ((found = cli_checklimits("7unz", ctx, 0, 0, 0)))
break;
if (f->IsDir)
continue;
+ // skip this file if we would exceed max file size or max scan size. (we already checked for the max files and max scan time)
if (cli_checklimits("7unz", ctx, f->Size, 0, 0))
continue;
@@ -164,21 +165,15 @@ int cli_7unz(cli_ctx *ctx, size_t offset)
encrypted = 1;
if (SCAN_HEURISTIC_ENCRYPTED_ARCHIVE) {
cli_dbgmsg("cli_7unz: Encrypted files found in archive.\n");
- found = cli_append_virus(ctx, "Heuristics.Encrypted.7Zip");
- if (found != CL_CLEAN) {
- if (found == CL_VIRUS) {
- if (SCAN_ALLMATCHES)
- viruses_found++;
- } else
- break;
+ found = cli_append_potentially_unwanted(ctx, "Heuristics.Encrypted.7Zip");
+ if (found != CL_SUCCESS) {
+ break;
}
}
}
- if (cli_matchmeta(ctx, name, 0, f->Size, encrypted, i, f->CrcDefined ? f->Crc : 0, NULL)) {
+ if (CL_VIRUS == cli_matchmeta(ctx, name, 0, f->Size, encrypted, i, f->CrcDefined ? f->Crc : 0, NULL)) {
found = CL_VIRUS;
- viruses_found++;
- if (!SCAN_ALLMATCHES)
- break;
+ break;
}
if (res != SZ_OK)
cli_dbgmsg("cli_unz: extraction failed with %d\n", res);
@@ -189,18 +184,19 @@ int cli_7unz(cli_ctx *ctx, size_t offset)
break;
cli_dbgmsg("cli_7unz: Saving to %s\n", tmp_name);
- if (cli_writen(fd, outBuffer + offset, outSizeProcessed) != outSizeProcessed)
+ if (cli_writen(fd, outBuffer + offset, outSizeProcessed) != outSizeProcessed) {
found = CL_EWRITE;
- else if ((found = cli_magic_scan_desc(fd, tmp_name, ctx, name)) == CL_VIRUS)
- viruses_found++;
+ }
+
+ found = cli_magic_scan_desc(fd, tmp_name, ctx, name);
+
close(fd);
if (!ctx->engine->keeptmp && cli_unlink(tmp_name))
found = CL_EUNLINK;
free(tmp_name);
- if (found != CL_CLEAN)
- if (!(SCAN_ALLMATCHES && found == CL_VIRUS))
- break;
+ if (found != CL_SUCCESS)
+ break;
}
}
IAlloc_Free(&allocImp, outBuffer);
@@ -222,7 +218,5 @@ int cli_7unz(cli_ctx *ctx, size_t offset)
else
cli_dbgmsg("cli_7unz: error %d\n", res);
- if (SCAN_ALLMATCHES && viruses_found)
- return CL_VIRUS;
return found;
}
diff --git a/libclamav/CMakeLists.txt b/libclamav/CMakeLists.txt
index 3a20801cf9..58423b1cd9 100644
--- a/libclamav/CMakeLists.txt
+++ b/libclamav/CMakeLists.txt
@@ -319,7 +319,7 @@ set(LIBCLAMAV_SOURCES
matcher-ac.c matcher-ac.h
matcher-bm.c matcher-bm.h
matcher-byte-comp.c matcher-byte-comp.h
- matcher-hash.c matcher-hash.h
+ matcher-hash.c matcher-hash.h matcher-hash-types.h
matcher-pcre.c matcher-pcre.h
matcher.c matcher.h
regex_pcre.c regex_pcre.h
diff --git a/libclamav/apm.c b/libclamav/apm.c
index c4f738df4c..bf74cda514 100644
--- a/libclamav/apm.c
+++ b/libclamav/apm.c
@@ -45,13 +45,14 @@
#define apm_parsemsg(...) ;
#endif
-static int apm_partition_intersection(cli_ctx *ctx, struct apm_partition_info *aptable, size_t sectorsize, int old_school);
+static cl_error_t apm_partition_intersection(cli_ctx *ctx, struct apm_partition_info *aptable, size_t sectorsize, bool old_school);
-int cli_scanapm(cli_ctx *ctx)
+cl_error_t cli_scanapm(cli_ctx *ctx)
{
+ cl_error_t status = CL_SUCCESS;
struct apm_driver_desc_map ddm;
struct apm_partition_info aptable, apentry;
- int ret = CL_CLEAN, detection = CL_CLEAN, old_school = 0;
+ bool old_school = false;
size_t sectorsize, maplen, partsize;
size_t pos = 0, partoff = 0;
unsigned i;
@@ -59,13 +60,15 @@ int cli_scanapm(cli_ctx *ctx)
if (!ctx || !ctx->fmap) {
cli_errmsg("cli_scanapm: Invalid context\n");
- return CL_ENULLARG;
+ status = CL_ENULLARG;
+ goto done;
}
/* read driver description map at sector 0 */
if (fmap_readn(ctx->fmap, &ddm, pos, sizeof(ddm)) != sizeof(ddm)) {
cli_dbgmsg("cli_scanapm: Invalid Apple driver description map\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* convert driver description map big-endian to host */
@@ -76,7 +79,8 @@ int cli_scanapm(cli_ctx *ctx)
/* check DDM signature */
if (ddm.signature != DDM_SIGNATURE) {
cli_dbgmsg("cli_scanapm: Apple driver description map signature mismatch\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* sector size is determined by the ddm */
@@ -87,20 +91,22 @@ int cli_scanapm(cli_ctx *ctx)
if ((ddm.blockSize * ddm.blockCount) != maplen) {
cli_dbgmsg("cli_scanapm: File described %u size does not match %lu actual size\n",
(ddm.blockSize * ddm.blockCount), (unsigned long)maplen);
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* check for old-school partition map */
if (sectorsize == 2048) {
if (fmap_readn(ctx->fmap, &aptable, APM_FALLBACK_SECTOR_SIZE, sizeof(aptable)) != sizeof(aptable)) {
cli_dbgmsg("cli_scanapm: Invalid Apple partition entry\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
aptable.signature = be16_to_host(aptable.signature);
if (aptable.signature == APM_SIGNATURE) {
sectorsize = APM_FALLBACK_SECTOR_SIZE;
- old_school = 1;
+ old_school = true;
}
}
@@ -109,7 +115,8 @@ int cli_scanapm(cli_ctx *ctx)
if (fmap_readn(ctx->fmap, &aptable, pos, sizeof(aptable)) != sizeof(aptable)) {
cli_dbgmsg("cli_scanapm: Invalid Apple partition table\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* convert partition table big endian to host */
@@ -121,7 +128,8 @@ int cli_scanapm(cli_ctx *ctx)
/* check the partition entry signature */
if (aptable.signature != APM_SIGNATURE) {
cli_dbgmsg("cli_scanapm: Apple partition table signature mismatch\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* check if partition table partition */
@@ -129,17 +137,15 @@ int cli_scanapm(cli_ctx *ctx)
strncmp((char *)aptable.type, "Apple_partition_map", 32) &&
strncmp((char *)aptable.type, "Apple_patition_map", 32)) {
cli_dbgmsg("cli_scanapm: Initial Apple Partition Map partition is not detected\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* check that the partition table fits in the space specified - HEURISTICS */
if (SCAN_HEURISTIC_PARTITION_INTXN && (ctx->dconf->other & OTHER_CONF_PRTNINTXN)) {
- ret = apm_partition_intersection(ctx, &aptable, sectorsize, old_school);
- if (ret != CL_CLEAN) {
- if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
- detection = CL_VIRUS;
- else
- return ret;
+ status = apm_partition_intersection(ctx, &aptable, sectorsize, old_school);
+ if (status != CL_SUCCESS) {
+ goto done;
}
}
@@ -167,7 +173,8 @@ int cli_scanapm(cli_ctx *ctx)
pos = i * sectorsize;
if (fmap_readn(ctx->fmap, &apentry, pos, sizeof(apentry)) != sizeof(apentry)) {
cli_dbgmsg("cli_scanapm: Invalid Apple partition entry\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* convert partition entry big endian to host */
@@ -180,7 +187,8 @@ int cli_scanapm(cli_ctx *ctx)
/* check the partition entry signature */
if (aptable.signature != APM_SIGNATURE) {
cli_dbgmsg("cli_scanapm: Apple partition entry signature mismatch\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* check if a out-of-order partition map */
@@ -223,12 +231,9 @@ int cli_scanapm(cli_ctx *ctx)
apentry.pBlockStart, apentry.pBlockCount, partoff, partsize);
/* send the partition to cli_magic_scan_nested_fmap_type */
- ret = cli_magic_scan_nested_fmap_type(ctx->fmap, partoff, partsize, ctx, CL_TYPE_PART_ANY, (const char *)apentry.name);
- if (ret != CL_CLEAN) {
- if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
- detection = CL_VIRUS;
- else
- return ret;
+ status = cli_magic_scan_nested_fmap_type(ctx->fmap, partoff, partsize, ctx, CL_TYPE_PART_ANY, (const char *)apentry.name);
+ if (status != CL_SUCCESS) {
+ goto done;
}
}
@@ -236,18 +241,20 @@ int cli_scanapm(cli_ctx *ctx)
cli_dbgmsg("cli_scanapm: max partitions reached\n");
}
- return detection;
+done:
+
+ return status;
}
-static int apm_partition_intersection(cli_ctx *ctx, struct apm_partition_info *aptable, size_t sectorsize, int old_school)
+static cl_error_t apm_partition_intersection(cli_ctx *ctx, struct apm_partition_info *aptable, size_t sectorsize, bool old_school)
{
+ cl_error_t status = CL_SUCCESS;
+ cl_error_t ret;
partition_intersection_list_t prtncheck;
struct apm_partition_info apentry;
unsigned i, pitxn;
- int ret = CL_CLEAN, tmp = CL_CLEAN;
size_t pos;
uint32_t max_prtns = 0;
- int virus_found = 0;
partition_intersection_list_init(&prtncheck);
@@ -264,7 +271,8 @@ static int apm_partition_intersection(cli_ctx *ctx, struct apm_partition_info *a
if (fmap_readn(ctx->fmap, &apentry, pos, sizeof(apentry)) != sizeof(apentry)) {
cli_dbgmsg("cli_scanapm: Invalid Apple partition entry\n");
partition_intersection_list_free(&prtncheck);
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* convert necessary info big endian to host */
@@ -283,33 +291,31 @@ static int apm_partition_intersection(cli_ctx *ctx, struct apm_partition_info *a
}
}
- tmp = partition_intersection_list_check(&prtncheck, &pitxn, apentry.pBlockStart, apentry.pBlockCount);
- if (tmp != CL_CLEAN) {
- if (tmp == CL_VIRUS) {
+ ret = partition_intersection_list_check(&prtncheck, &pitxn, apentry.pBlockStart, apentry.pBlockCount);
+ if (ret != CL_CLEAN) {
+ if (ret == CL_VIRUS) {
apm_parsemsg("Name: %s\n", (char *)aptable.name);
apm_parsemsg("Type: %s\n", (char *)aptable.type);
cli_dbgmsg("cli_scanapm: detected intersection with partitions "
"[%u, %u]\n",
pitxn, i);
- ret = cli_append_virus(ctx, PRTN_INTXN_DETECTION);
- if (ret == CL_VIRUS)
- virus_found = 1;
- if (SCAN_ALLMATCHES || ret == CL_CLEAN)
- tmp = 0;
- else
- goto leave;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.APMPartitionIntersection");
+ if (status != CL_SUCCESS) {
+ goto done;
+ }
} else {
- ret = tmp;
- goto leave;
+ status = ret;
+ goto done;
}
}
+
+ /* increment the offsets to next partition entry */
pos += sectorsize;
}
-leave:
+done:
partition_intersection_list_free(&prtncheck);
- if (virus_found)
- return CL_VIRUS;
- return ret;
+
+ return status;
}
diff --git a/libclamav/apm.h b/libclamav/apm.h
index 113eef3081..248df275bf 100644
--- a/libclamav/apm.h
+++ b/libclamav/apm.h
@@ -25,7 +25,7 @@
#include "clamav-config.h"
#endif
-#include "clamav-types.h"
+#include "clamav.h"
#include "others.h"
#define APM_FALLBACK_SECTOR_SIZE 512
@@ -112,6 +112,6 @@ struct apm_partition_info {
#pragma pack
#endif
-int cli_scanapm(cli_ctx *ctx);
+cl_error_t cli_scanapm(cli_ctx *ctx);
#endif
diff --git a/libclamav/asn1.c b/libclamav/asn1.c
index d49a0229c0..1eec3b0ed0 100644
--- a/libclamav/asn1.c
+++ b/libclamav/asn1.c
@@ -1620,7 +1620,7 @@ static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t
cli_dbgmsg("asn1_parse_mscat: Found Authenticode certificate blocked by %s\n", crt->name ? crt->name : "(unnamed CRB rule)");
if (NULL != ctx) {
ret = cli_append_virus(ctx, crt->name ? crt->name : "(unnamed CRB rule)");
- if ((ret == CL_VIRUS) && !SCAN_ALLMATCHES) {
+ if (ret == CL_VIRUS) {
crtmgr_free(&newcerts);
goto finish;
}
@@ -1664,10 +1664,6 @@ static cl_error_t asn1_parse_mscat(struct cl_engine *engine, fmap_t *map, size_t
break;
}
- /* In the SCAN_ALLMATCHES case, we'd get here with
- * ret == CL_VIRUS if a match occurred but we wanted
- * to keep looping to look for other matches. In that
- * case, bail here. */
if (CL_VIRUS == ret) {
crtmgr_free(&newcerts);
break;
@@ -2213,7 +2209,7 @@ int asn1_load_mscat(fmap_t *map, struct cl_engine *engine)
struct cli_asn1 tagval1, tagval2, tagval3;
int hashed_obj_type;
cli_crt_hashtype hashtype;
- enum CLI_HASH_TYPE hm_hashtype;
+ cli_hash_type_t hm_hashtype;
unsigned int hashsize;
if (asn1_expect_objtype(map, tag.content, &tag.size, &tagval1, ASN1_TYPE_SEQUENCE))
diff --git a/libclamav/autoit.c b/libclamav/autoit.c
index 5d93517c25..1a3323db9a 100644
--- a/libclamav/autoit.c
+++ b/libclamav/autoit.c
@@ -638,39 +638,49 @@ static uint32_t getbits(struct UNP *UNP, uint32_t size)
autoit3 EA05 handler
*********************/
-static int ea05(cli_ctx *ctx, const uint8_t *base, char *tmpd)
+static cl_error_t ea05(cli_ctx *ctx, const uint8_t *base, char *tmpd)
{
- uint8_t b[300], comp;
+ cl_error_t status = CL_SUCCESS;
+ cl_error_t ret;
+ uint8_t b[300];
+ uint8_t comp;
uint32_t s, m4sum = 0;
- int i, ret, det = 0;
- unsigned int files = 0;
- char tempfile[1024];
- struct UNP UNP;
- fmap_t *map = ctx->fmap;
-
- if (!fmap_need_ptr_once(map, base, 16))
- return CL_CLEAN;
+ int i;
+ unsigned int files = 0;
+ char tempfile[1024] = {0};
+ int tempfd = -1;
+ struct UNP UNP = {0};
+ fmap_t *map = ctx->fmap;
+
+ if (!fmap_need_ptr_once(map, base, 16)) {
+ goto done;
+ }
for (i = 0; i < 16; i++)
m4sum += *base++;
- while ((ret = cli_checklimits("autoit", ctx, 0, 0, 0)) == CL_CLEAN) {
- if (!fmap_need_ptr_once(map, base, 8))
- return (det ? CL_VIRUS : CL_CLEAN);
+ // While we have not exceeded the max files limit or the max time limit...
+ while (CL_SUCCESS == (status = cli_checklimits("autoit", ctx, 0, 0, 0))) {
+ if (!fmap_need_ptr_once(map, base, 8)) {
+ goto done;
+ }
/* MT_decrypt(buf,4,0x16fa); waste of time */
if ((uint32_t)cli_readint32(base) != 0xceb06dff) {
cli_dbgmsg("autoit: no FILE magic found, extraction complete\n");
- return (det ? CL_VIRUS : CL_CLEAN);
+ goto done;
}
s = cli_readint32(base + 4) ^ 0x29bc;
- if ((int32_t)s < 0)
- return (det ? CL_VIRUS : CL_CLEAN); /* the original code wouldn't seek back here */
+ if ((int32_t)s < 0) {
+ /* the original code wouldn't seek back here */
+ goto done;
+ }
base += 8;
if (cli_debug_flag && s < sizeof(b)) {
- if (!fmap_need_ptr_once(map, base, s))
- return (det ? CL_VIRUS : CL_CLEAN);
+ if (!fmap_need_ptr_once(map, base, s)) {
+ goto done;
+ }
memcpy(b, base, s);
MT_decrypt(b, s, s + 0xa25e);
b[s] = '\0';
@@ -678,15 +688,20 @@ static int ea05(cli_ctx *ctx, const uint8_t *base, char *tmpd)
}
base += s;
- if (!fmap_need_ptr_once(map, base, 4))
- return (det ? CL_VIRUS : CL_CLEAN);
+ if (!fmap_need_ptr_once(map, base, 4)) {
+ goto done;
+ }
s = cli_readint32(base) ^ 0x29ac;
- if ((int32_t)s < 0)
- return (det ? CL_VIRUS : CL_CLEAN); /* the original code wouldn't seek back here */
+ if ((int32_t)s < 0) {
+ /* the original code wouldn't seek back here */
+ goto done;
+ }
base += 4;
if (cli_debug_flag && s < sizeof(b)) {
- if (!fmap_need_ptr_once(map, base, s))
- return (det ? CL_VIRUS : CL_CLEAN);
+ if (!fmap_need_ptr_once(map, base, s)) {
+ goto done;
+ }
+
memcpy(b, base, s);
MT_decrypt(b, s, s + 0xf25e);
b[s] = '\0';
@@ -694,13 +709,15 @@ static int ea05(cli_ctx *ctx, const uint8_t *base, char *tmpd)
}
base += s;
- if (!fmap_need_ptr_once(map, base, 13))
- return (det ? CL_VIRUS : CL_CLEAN);
+ if (!fmap_need_ptr_once(map, base, 13)) {
+ goto done;
+ }
+
comp = *base;
UNP.csize = cli_readint32(base + 1) ^ 0x45aa;
if ((int32_t)UNP.csize < 0) {
cli_dbgmsg("autoit: bad file size - giving up\n");
- return (det ? CL_VIRUS : CL_CLEAN);
+ goto done;
}
if (!UNP.csize) {
@@ -724,36 +741,42 @@ static int ea05(cli_ctx *ctx, const uint8_t *base, char *tmpd)
continue;
}
- if (!(UNP.inputbuf = cli_malloc(UNP.csize)))
- return CL_EMEM;
+ if (!(UNP.inputbuf = cli_malloc(UNP.csize))) {
+ status = CL_EMEM;
+ goto done;
+ }
if (!fmap_need_ptr_once(map, base, UNP.csize)) {
cli_dbgmsg("autoit: failed to read compressed stream. broken/truncated file?\n");
- free(UNP.inputbuf);
- return (det ? CL_VIRUS : CL_CLEAN);
+ goto done;
}
+
memcpy(UNP.inputbuf, base, UNP.csize);
base += UNP.csize;
MT_decrypt(UNP.inputbuf, UNP.csize, 0x22af + m4sum);
if (comp == 1) {
+ /*
+ * File is compressed. Decompress!
+ */
cli_dbgmsg("autoit: file is compressed\n");
if (cli_readint32(UNP.inputbuf) != 0x35304145) {
cli_dbgmsg("autoit: bad magic or unsupported version\n");
- free(UNP.inputbuf);
continue;
}
- if (!(UNP.usize = be32_to_host(*(uint32_t *)(UNP.inputbuf + 4))))
+ if (!(UNP.usize = be32_to_host(*(uint32_t *)(UNP.inputbuf + 4)))) {
UNP.usize = UNP.csize; /* only a specifically crafted or badly corrupted sample should land here */
+ }
+
if (cli_checklimits("autoit", ctx, UNP.usize, 0, 0) != CL_CLEAN) {
- free(UNP.inputbuf);
continue;
}
if (!(UNP.outputbuf = cli_malloc(UNP.usize))) {
- free(UNP.inputbuf);
- return CL_EMEM;
+ status = CL_EMEM;
+ goto done;
}
+
cli_dbgmsg("autoit: uncompressed size again: %x\n", UNP.usize);
UNP.cur_output = 0;
@@ -806,6 +829,8 @@ static int ea05(cli_ctx *ctx, const uint8_t *base, char *tmpd)
}
free(UNP.inputbuf);
+ UNP.inputbuf = NULL;
+
/* Sometimes the autoit exe is in turn packed/lamed with a runtime compressor and similar shit.
* However, since the autoit script doesn't compress a second time very well, chances are we're
* still able to match the headers and unpack something (see sample 0811129)
@@ -818,6 +843,9 @@ static int ea05(cli_ctx *ctx, const uint8_t *base, char *tmpd)
UNP.usize = UNP.cur_output;
}
} else {
+ /*
+ * File is NOT compressed.
+ */
cli_dbgmsg("autoit: file is not compressed\n");
UNP.outputbuf = UNP.inputbuf;
UNP.usize = UNP.csize;
@@ -836,41 +864,62 @@ static int ea05(cli_ctx *ctx, const uint8_t *base, char *tmpd)
snprintf(tempfile, 1023, "%s" PATHSEP "autoit.%.3u", tmpd, files);
tempfile[1023] = '\0';
- if ((i = open(tempfile, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR)) < 0) {
+
+ tempfd = open(tempfile, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR);
+ if (tempfd < 0) {
cli_dbgmsg("autoit: Can't create file %s\n", tempfile);
- free(UNP.outputbuf);
- return CL_ECREAT;
+ status = CL_ECREAT;
+ goto done;
}
- if (cli_writen(i, UNP.outputbuf, UNP.usize) != UNP.usize) {
+
+ if (cli_writen(tempfd, UNP.outputbuf, UNP.usize) != UNP.usize) {
cli_dbgmsg("autoit: cannot write %d bytes\n", UNP.usize);
- close(i);
- free(UNP.outputbuf);
- return CL_EWRITE;
+ status = CL_EWRITE;
+ goto done;
}
+
free(UNP.outputbuf);
- if (ctx->engine->keeptmp)
+ UNP.outputbuf = NULL;
+
+ if (ctx->engine->keeptmp) {
cli_dbgmsg("autoit: file extracted to %s\n", tempfile);
- else
+ } else {
cli_dbgmsg("autoit: file successfully extracted\n");
- if (lseek(i, 0, SEEK_SET) == -1) {
+ }
+
+ if (lseek(tempfd, 0, SEEK_SET) == -1) {
cli_dbgmsg("autoit: call to lseek() has failed\n");
- close(i);
- return CL_ESEEK;
+ status = CL_ESEEK;
+ goto done;
}
- if (cli_magic_scan_desc(i, tempfile, ctx, NULL) == CL_VIRUS) {
- if (!SCAN_ALLMATCHES) {
- close(i);
- if (!ctx->engine->keeptmp)
- if (cli_unlink(tempfile)) return CL_EUNLINK;
- return CL_VIRUS;
- }
- det = 1;
+
+ ret = cli_magic_scan_desc(tempfd, tempfile, ctx, NULL);
+ if (CL_SUCCESS != ret) {
+ status = ret;
+ goto done;
+ }
+
+ close(tempfd);
+ tempfd = -1;
+ if (!ctx->engine->keeptmp) {
+ (void)cli_unlink(tempfile);
}
- close(i);
- if (!ctx->engine->keeptmp)
- if (cli_unlink(tempfile)) return CL_EUNLINK;
}
- return (det ? CL_VIRUS : ret);
+
+done:
+ if (NULL != UNP.inputbuf) {
+ free(UNP.inputbuf);
+ }
+ if (NULL != UNP.outputbuf) {
+ free(UNP.outputbuf);
+ }
+ if (tempfd >= 0) {
+ close(tempfd);
+ if (!ctx->engine->keeptmp) {
+ (void)cli_unlink(tempfile);
+ }
+ }
+ return status;
}
/*********************
@@ -964,9 +1013,10 @@ static void LAME_decrypt(uint8_t *cypher, uint32_t size, uint16_t seed)
static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
{
- uint8_t b[600], comp, script, *buf;
+ cl_error_t ret;
+ uint8_t b[600], comp, *buf;
uint32_t s;
- int i, ret, det = 0;
+ int i;
unsigned int files = 0;
char tempfile[1024];
const char prefixes[] = {'\0', '\0', '@', '$', '\0', '.', '"', '\0'};
@@ -981,60 +1031,81 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
/* buf+=0x10; */
base += 16; /* for now we just skip the garbage */
- while ((ret = cli_checklimits("cli_autoit", ctx, 0, 0, 0)) == CL_CLEAN) {
- if (!fmap_need_ptr_once(map, base, 8))
- return (det ? CL_VIRUS : CL_CLEAN);
+ while (CL_SUCCESS == (ret = cli_checklimits("cli_autoit", ctx, 0, 0, 0))) {
+ bool script = false;
+
+ if (!fmap_need_ptr_once(map, base, 8)) {
+ return CL_SUCCESS;
+ }
+
/* LAME_decrypt(buf, 4, 0x18ee); waste of time */
if (cli_readint32(base) != 0x52ca436b) {
cli_dbgmsg("autoit: no FILE magic found, giving up (got 0x%08x)\n", cli_readint32(base));
- return (det ? CL_VIRUS : CL_CLEAN);
+ return CL_SUCCESS;
}
- script = 0;
-
s = cli_readint32(base + 4) ^ 0xadbc;
- if ((int32_t)(s * 2) < 0)
- return (det ? CL_VIRUS : CL_CLEAN); /* the original code wouldn't seek back here */
+ if ((int32_t)(s * 2) < 0) {
+ return CL_SUCCESS; /* the original code wouldn't seek back here */
+ }
+
base += 8;
+
if (s < sizeof(b) / 2) {
- if (!fmap_need_ptr_once(map, base, s * 2))
- return (det ? CL_VIRUS : CL_CLEAN);
+ if (!fmap_need_ptr_once(map, base, s * 2)) {
+ return CL_SUCCESS;
+ }
+
memcpy(b, base, s * 2);
LAME_decrypt(b, s * 2, s + 0xb33f);
u2a(b, s * 2);
cli_dbgmsg("autoit: magic string '%s'\n", b);
- if (s == 19 && !memcmp(">>>AUTOIT SCRIPT<<<", b, 19))
- script = 1;
+
+ if (s == 19 && !memcmp(">>>AUTOIT SCRIPT<<<", b, 19)) {
+ script = true;
+ }
} else {
cli_dbgmsg("autoit: magic string too long to print\n");
}
+
base += s * 2;
- if (!fmap_need_ptr_once(map, base, 4))
- return (det ? CL_VIRUS : CL_CLEAN);
+ if (!fmap_need_ptr_once(map, base, 4)) {
+ return CL_SUCCESS;
+ }
+
s = cli_readint32(base) ^ 0xf820;
- if ((int32_t)(s * 2) < 0)
- return (det ? CL_VIRUS : CL_CLEAN); /* the original code wouldn't seek back here */
+ if ((int32_t)(s * 2) < 0) {
+ return CL_SUCCESS; /* the original code wouldn't seek back here */
+ }
+
base += 4;
+
if (cli_debug_flag && s < sizeof(b) / 2) {
- if (!fmap_need_ptr_once(map, base, s * 2))
- return (det ? CL_VIRUS : CL_CLEAN);
+ if (!fmap_need_ptr_once(map, base, s * 2)) {
+ return CL_SUCCESS;
+ }
+
memcpy(b, base, s * 2);
LAME_decrypt(b, s * 2, s + 0xf479);
b[s * 2] = '\0';
b[s * 2 + 1] = '\0';
u2a(b, s * 2);
+
cli_dbgmsg("autoit: original filename '%s'\n", b);
}
+
base += s * 2;
- if (!fmap_need_ptr_once(map, base, 13))
- return (det ? CL_VIRUS : CL_CLEAN);
+ if (!fmap_need_ptr_once(map, base, 13)) {
+ return CL_SUCCESS;
+ }
+
comp = *base;
UNP.csize = cli_readint32(base + 1) ^ 0x87bc;
if ((int32_t)UNP.csize < 0) {
cli_dbgmsg("autoit: bad file size - giving up\n");
- return (det ? CL_VIRUS : CL_CLEAN);
+ return CL_SUCCESS;
}
if (!UNP.csize) {
@@ -1042,6 +1113,7 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
base += 13 + 16;
continue;
}
+
cli_dbgmsg("autoit: compressed size: %x\n", UNP.csize);
cli_dbgmsg("autoit: advertised uncompressed size %x\n", cli_readint32(base + 5) ^ 0x87bc);
cli_dbgmsg("autoit: ref chksum: %x\n", cli_readint32(base + 9) ^ 0xa685);
@@ -1060,35 +1132,44 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
files++;
- if (!(UNP.inputbuf = cli_malloc(UNP.csize)))
+ if (!(UNP.inputbuf = cli_malloc(UNP.csize))) {
return CL_EMEM;
+ }
+
if (!fmap_need_ptr_once(map, base, UNP.csize)) {
cli_dbgmsg("autoit: failed to read compressed stream. broken/truncated file?\n");
free(UNP.inputbuf);
- return (det ? CL_VIRUS : CL_CLEAN);
+ return CL_SUCCESS;
}
+
memcpy(UNP.inputbuf, base, UNP.csize);
base += UNP.csize;
+
LAME_decrypt(UNP.inputbuf, UNP.csize, 0x2477 /* + m4sum (broken by design) */);
if (comp == 1) {
cli_dbgmsg("autoit: file is compressed\n");
+
if (cli_readint32(UNP.inputbuf) != 0x36304145) {
cli_dbgmsg("autoit: bad magic or unsupported version\n");
free(UNP.inputbuf);
continue;
}
- if (!(UNP.usize = be32_to_host(*(uint32_t *)(UNP.inputbuf + 4))))
+ if (!(UNP.usize = be32_to_host(*(uint32_t *)(UNP.inputbuf + 4)))) {
UNP.usize = UNP.csize; /* only a specifically crafted or badly corrupted sample should land here */
+ }
+
if (cli_checklimits("autoit", ctx, UNP.usize, 0, 0) != CL_CLEAN) {
free(UNP.inputbuf);
continue;
}
+
if (!(UNP.outputbuf = cli_malloc(UNP.usize))) {
free(UNP.inputbuf);
return CL_EMEM;
}
+
cli_dbgmsg("autoit: uncompressed size again: %x\n", UNP.usize);
UNP.cur_output = 0;
@@ -1131,6 +1212,7 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
UNP.error = 1;
break;
}
+
while (bs--) {
UNP.outputbuf[UNP.cur_output] = UNP.outputbuf[UNP.cur_output - bb];
UNP.cur_output++;
@@ -1166,10 +1248,12 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
free(UNP.outputbuf);
return CL_EMEM;
}
+
UNP.cur_output = 0;
UNP.cur_input = 4;
UNP.bits_avail = cli_readint32((char *)UNP.outputbuf);
UNP.error = 0;
+
cli_dbgmsg("autoit: script has got %u lines\n", UNP.bits_avail);
while (!UNP.error && UNP.bits_avail && UNP.cur_input < UNP.usize) {
@@ -1184,13 +1268,16 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
cli_dbgmsg("autoit: too few bytes present - expected enough for a keyword ID\n");
break;
}
+
keyword_id = cli_readint32((char *)&UNP.outputbuf[UNP.cur_input]);
if (keyword_id >= (sizeof(autoit_keywords) / sizeof(autoit_keywords[0]))) {
UNP.error = 1;
cli_dbgmsg("autoit: unknown AutoIT keyword ID: 0x%x\n", keyword_id);
break;
}
+
UNP.cur_input += 4;
+
keyword_len = strlen(autoit_keywords[keyword_id]);
if (UNP.cur_output + keyword_len + 2 >= UNP.csize) {
uint8_t *newout;
@@ -1201,11 +1288,13 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
}
buf = newout;
}
+
if (cli_debug_flag) {
if (0 == memcmp(autoit_keywords[keyword_id], "UNKNOWN", MIN(strlen("UNKNOWN"), keyword_len))) {
cli_dbgmsg("autoit: encountered use of unknown keyword ID: %s\n", autoit_keywords[keyword_id]);
}
}
+
snprintf((char *)&buf[UNP.cur_output], keyword_len + 2, "%s ", autoit_keywords[keyword_id]);
UNP.cur_output += keyword_len + 1;
break;
@@ -1218,13 +1307,16 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
cli_dbgmsg("autoit: too few bytes present - expected enough for a function ID\n");
break;
}
+
function_id = cli_readint32((char *)&UNP.outputbuf[UNP.cur_input]);
if (function_id >= (sizeof(autoit_functions) / sizeof(autoit_functions[0]))) {
UNP.error = 1;
cli_dbgmsg("autoit: unknown AutoIT function ID: 0x%x\n", function_id);
break;
}
+
UNP.cur_input += 4;
+
function_len = strlen(autoit_functions[function_id]);
if (UNP.cur_output + function_len + 2 >= UNP.csize) {
uint8_t *newout;
@@ -1235,11 +1327,13 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
}
buf = newout;
}
+
if (cli_debug_flag) {
if (0 == memcmp(autoit_functions[function_id], "UNKNOWN", MIN(strlen("UNKNOWN"), function_len))) {
cli_dbgmsg("autoit: encountered use of unknown function ID: %s\n", autoit_functions[function_id]);
}
}
+
snprintf((char *)&buf[UNP.cur_output], function_len + 2, "%s ", autoit_functions[function_id]);
UNP.cur_output += function_len + 1;
break;
@@ -1250,6 +1344,7 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
cli_dbgmsg("autoit: not enough space for an int\n");
break;
}
+
if (UNP.cur_output + 12 >= UNP.csize) {
uint8_t *newout;
UNP.csize += 512;
@@ -1259,6 +1354,7 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
}
buf = newout;
}
+
snprintf((char *)&buf[UNP.cur_output], 12, "0x%08x ", cli_readint32((char *)&UNP.outputbuf[UNP.cur_input]));
UNP.cur_output += 11;
UNP.cur_input += 4;
@@ -1272,6 +1368,7 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
cli_dbgmsg("autoit: not enough space for an int64\n");
break;
}
+
if (UNP.cur_output + 20 >= UNP.csize) {
uint8_t *newout;
UNP.csize += 512;
@@ -1281,6 +1378,7 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
}
buf = newout;
}
+
val = (uint64_t)cli_readint32((char *)&UNP.outputbuf[UNP.cur_input + 4]);
val <<= 32;
val += (uint64_t)cli_readint32((char *)&UNP.outputbuf[UNP.cur_input]);
@@ -1296,6 +1394,7 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
cli_dbgmsg("autoit: not enough space for a double\n");
break;
}
+
if (UNP.cur_output + 40 >= UNP.csize) {
uint8_t *newout;
UNP.csize += 512;
@@ -1305,16 +1404,19 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
}
buf = newout;
}
- if (fpu_words == FPU_ENDIAN_LITTLE)
+
+ if (fpu_words == FPU_ENDIAN_LITTLE) {
snprintf((char *)&buf[UNP.cur_output], 39, "%g ", *(double *)&UNP.outputbuf[UNP.cur_input]);
- else
+ } else
do {
double x;
uint8_t *j = (uint8_t *)&x;
unsigned int i;
- for (i = 0; i < 8; i++)
+ for (i = 0; i < 8; i++) {
j[7 - i] = UNP.outputbuf[UNP.cur_input + i];
+ }
+
snprintf((char *)&buf[UNP.cur_output], 39, "%g ", x); /* FIXME: check */
} while (0);
buf[UNP.cur_output + 38] = ' ';
@@ -1339,6 +1441,7 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
cli_dbgmsg("autoit: not enough space for size\n");
break;
}
+
chars = cli_readint32((char *)&UNP.outputbuf[UNP.cur_input]);
dchars = chars * 2;
UNP.cur_input += 4;
@@ -1348,6 +1451,7 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
cli_dbgmsg("autoit: size too big - needed %d, total %d, avail %d\n", dchars, UNP.usize, UNP.usize - UNP.cur_input);
break;
}
+
if (UNP.cur_output + chars + 3 >= UNP.csize) {
uint8_t *newout;
UNP.csize += chars + 512;
@@ -1358,8 +1462,9 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
buf = newout;
}
- if (prefixes[op - 0x30])
+ if (prefixes[op - 0x30]) {
buf[UNP.cur_output++] = prefixes[op - 0x30];
+ }
if (chars) {
for (i = 0; i < dchars; i += 2) {
@@ -1371,11 +1476,14 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
UNP.cur_output += chars;
UNP.cur_input += dchars;
}
- if (op == 0x36)
+
+ if (op == 0x36) {
// TODO: Mask possible double quotes inside the string: >Say:"Hi "< ==> >"Say:""Hi"" "<
buf[UNP.cur_output++] = '"';
- if (op != 0x34)
+ }
+ if (op != 0x34) {
buf[UNP.cur_output++] = ' ';
+ }
} break;
case 0x40: /* , */
@@ -1442,8 +1550,9 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
}
}
- if (UNP.error)
+ if (UNP.error) {
cli_dbgmsg("autoit: decompilation aborted - partial script may exist\n");
+ }
free(UNP.outputbuf);
} else {
@@ -1464,40 +1573,51 @@ static int ea06(cli_ctx *ctx, const uint8_t *base, char *tmpd)
free(buf);
return CL_EWRITE;
}
+
free(buf);
- if (ctx->engine->keeptmp)
+
+ if (ctx->engine->keeptmp) {
cli_dbgmsg("autoit: %s extracted to %s\n", (script) ? "script" : "file", tempfile);
- else
+ } else {
cli_dbgmsg("autoit: %s successfully extracted\n", (script) ? "script" : "file");
+ }
+
if (lseek(i, 0, SEEK_SET) == -1) {
cli_dbgmsg("autoit: call to lseek() has failed\n");
close(i);
return CL_ESEEK;
}
- if (cli_magic_scan_desc(i, tempfile, ctx, NULL) == CL_VIRUS) {
- if (!SCAN_ALLMATCHES) {
- close(i);
- if (!ctx->engine->keeptmp)
- if (cli_unlink(tempfile)) return CL_EUNLINK;
- return CL_VIRUS;
+
+ ret = cli_magic_scan_desc(i, tempfile, ctx, NULL);
+ if (CL_SUCCESS != ret) {
+ close(i);
+ if (!ctx->engine->keeptmp) {
+ if (cli_unlink(tempfile)) {
+ return CL_EUNLINK;
+ }
}
- det = 1;
+ return CL_VIRUS;
}
+
close(i);
- if (!ctx->engine->keeptmp)
- if (cli_unlink(tempfile)) return CL_EUNLINK;
+
+ if (!ctx->engine->keeptmp) {
+ if (cli_unlink(tempfile)) {
+ return CL_EUNLINK;
+ }
+ }
}
- return (det ? CL_VIRUS : ret);
+ return ret;
}
/*********************
autoit3 wrapper
*********************/
-int cli_scanautoit(cli_ctx *ctx, off_t offset)
+cl_error_t cli_scanautoit(cli_ctx *ctx, off_t offset)
{
+ cl_error_t status = CL_SUCCESS;
const uint8_t *version;
- int r;
char *tmpd;
fmap_t *map = ctx->fmap;
@@ -1518,7 +1638,7 @@ int cli_scanautoit(cli_ctx *ctx, off_t offset)
switch (*version) {
case 0x35:
- r = ea05(ctx, version + 1, tmpd);
+ status = ea05(ctx, version + 1, tmpd);
break;
case 0x36:
if (fpu_words == FPU_ENDIAN_INITME)
@@ -1526,19 +1646,19 @@ int cli_scanautoit(cli_ctx *ctx, off_t offset)
if (fpu_words == FPU_ENDIAN_UNKNOWN) {
cli_dbgmsg("autoit: EA06 support not available"
"(cannot extract ea06 doubles, unknown floating double representation).\n");
- r = CL_CLEAN;
+ status = CL_CLEAN;
} else
- r = ea06(ctx, version + 1, tmpd);
+ status = ea06(ctx, version + 1, tmpd);
break;
default:
/* NOT REACHED */
cli_dbgmsg("autoit: unknown method\n");
- r = CL_CLEAN;
+ status = CL_CLEAN;
}
if (!ctx->engine->keeptmp)
cli_rmdirs(tmpd);
free(tmpd);
- return r;
+ return status;
}
diff --git a/libclamav/autoit.h b/libclamav/autoit.h
index c29d609702..cdffe005ae 100644
--- a/libclamav/autoit.h
+++ b/libclamav/autoit.h
@@ -23,5 +23,5 @@
#define __AUTOIT_H
#include "others.h"
-int cli_scanautoit(cli_ctx *ctx, off_t offset);
+cl_error_t cli_scanautoit(cli_ctx *ctx, off_t offset);
#endif
diff --git a/libclamav/binhex.c b/libclamav/binhex.c
index 98d04a32b5..593c7b60bf 100644
--- a/libclamav/binhex.c
+++ b/libclamav/binhex.c
@@ -123,7 +123,9 @@ int cli_binhex(cli_ctx *ctx)
break;
}
ret = cli_magic_scan_desc(datafd, dname, ctx, NULL);
- if (ret == CL_VIRUS) break;
+ if (ret != CL_SUCCESS) {
+ break;
+ }
}
if (dec_done)
memmove(decoded, &decoded[todo], dec_done);
diff --git a/libclamav/blob.c b/libclamav/blob.c
index 5d583ecd72..13ecd861b0 100644
--- a/libclamav/blob.c
+++ b/libclamav/blob.c
@@ -43,6 +43,7 @@
#include
#endif
+#include "clamav.h"
#include "others.h"
#include "mbox.h"
#include "matcher.h"
@@ -597,7 +598,6 @@ int fileblobAddData(fileblob *fb, const unsigned char *data, size_t len)
fb->bytes_scanned += (unsigned long)len;
if ((len > 5) && cli_updatelimits(ctx, len) == CL_CLEAN && (cli_scan_buff(data, (unsigned int)len, 0, ctx->virname, ctx->engine, CL_TYPE_BINARY_DATA, NULL) == CL_VIRUS)) {
- cli_dbgmsg("fileblobAddData: found %s\n", cli_get_last_virus_str(ctx->virname));
fb->isInfected = 1;
}
}
@@ -632,12 +632,10 @@ void fileblobSetCTX(fileblob *fb, cli_ctx *ctx)
* CL_CLEAN means unknown
* CL_VIRUS means infected
*/
-int fileblobScan(const fileblob *fb)
+cl_error_t fileblobScan(const fileblob *fb)
{
- int rc;
- cli_ctx *ctx = fb->ctx;
+ cl_error_t rc;
STATBUF sb;
- int virus_found = 0;
if (fb->isInfected)
return CL_VIRUS;
@@ -655,18 +653,17 @@ int fileblobScan(const fileblob *fb)
fflush(fb->fp);
lseek(fb->fd, 0, SEEK_SET);
FSTAT(fb->fd, &sb);
- if (cli_matchmeta(fb->ctx, fb->b.name, sb.st_size, sb.st_size, 0, 0, 0, NULL) == CL_VIRUS) {
- if (!SCAN_ALLMATCHES)
- return CL_VIRUS;
- virus_found = 1;
+
+ rc = cli_matchmeta(fb->ctx, fb->b.name, sb.st_size, sb.st_size, 0, 0, 0, NULL);
+ if (rc != CL_SUCCESS) {
+ return rc;
}
rc = cli_magic_scan_desc(fb->fd, fb->fullname, fb->ctx, fb->b.name);
- if (rc == CL_VIRUS || virus_found != 0) {
- cli_dbgmsg("%s is infected\n", fb->fullname);
- return CL_VIRUS;
+ if (rc != CL_SUCCESS) {
+ return rc;
}
- cli_dbgmsg("%s is clean\n", fb->fullname);
+
return CL_BREAK;
}
@@ -688,8 +685,9 @@ void sanitiseName(char *name)
{
char c;
while ((c = *name)) {
- if (c != '.' && c != '_' && (c > 'z' || c < '0' || (c > '9' && c < 'A') || (c > 'Z' && c < 'a')))
+ if (c != '.' && c != '_' && (c > 'z' || c < '0' || (c > '9' && c < 'A') || (c > 'Z' && c < 'a'))) {
*name = '_';
+ }
name++;
}
}
diff --git a/libclamav/blob.h b/libclamav/blob.h
index eccdbe191c..d3b9338aad 100644
--- a/libclamav/blob.h
+++ b/libclamav/blob.h
@@ -22,6 +22,8 @@
#ifndef __BLOB_H
#define __BLOB_H
+#include "clamav.h"
+
/*
* Resizable chunk of memory
*/
@@ -74,7 +76,7 @@ void fileblobPartialSet(fileblob *fb, const char *fullname, const char *arg);
const char *fileblobGetFilename(const fileblob *fb);
void fileblobSetCTX(fileblob *fb, cli_ctx *ctx);
int fileblobAddData(fileblob *fb, const unsigned char *data, size_t len);
-int fileblobScan(const fileblob *fb);
+cl_error_t fileblobScan(const fileblob *fb);
int fileblobInfected(const fileblob *fb);
void sanitiseName(char *name);
diff --git a/libclamav/bytecode.c b/libclamav/bytecode.c
index a3f1eafa0c..aab200d3d4 100644
--- a/libclamav/bytecode.c
+++ b/libclamav/bytecode.c
@@ -102,7 +102,7 @@ static void context_safe(struct cli_bc_ctx *ctx)
ctx->hooks.pedata = &nopedata;
}
-static int cli_bytecode_context_reset(struct cli_bc_ctx *ctx);
+static cl_error_t cli_bytecode_context_reset(struct cli_bc_ctx *ctx);
struct cli_bc_ctx *cli_bytecode_context_alloc(void)
{
struct cli_bc_ctx *ctx = cli_calloc(1, sizeof(*ctx));
@@ -132,7 +132,7 @@ int cli_bytecode_context_getresult_file(struct cli_bc_ctx *ctx, char **tempfilen
}
/* resets bytecode state, so you can run another bytecode with same ctx */
-static int cli_bytecode_context_reset(struct cli_bc_ctx *ctx)
+static cl_error_t cli_bytecode_context_reset(struct cli_bc_ctx *ctx)
{
unsigned i;
@@ -159,7 +159,8 @@ static int cli_bytecode_context_reset(struct cli_bc_ctx *ctx)
if (ctx->jsnormdir) {
char fullname[1025];
cli_ctx *cctx = ctx->ctx;
- int fd, ret = CL_CLEAN;
+ int fd;
+ cl_error_t ret = CL_CLEAN;
if (!ctx->found) {
snprintf(fullname, 1024, "%s" PATHSEP "javascript", ctx->jsnormdir);
@@ -167,14 +168,14 @@ static int cli_bytecode_context_reset(struct cli_bc_ctx *ctx)
if (fd >= 0) {
cctx->next_layer_is_normalized = true; // This flag ingested by cli_recursion_stack_push().
- ret = cli_scan_desc(fd, cctx, CL_TYPE_HTML, 0, NULL, AC_SCAN_VIR, NULL, NULL);
+ ret = cli_scan_desc(fd, cctx, CL_TYPE_HTML, false, NULL, AC_SCAN_VIR, NULL, NULL);
if (ret == CL_CLEAN) {
if (lseek(fd, 0, SEEK_SET) == -1)
cli_dbgmsg("cli_bytecode: call to lseek() has failed\n");
else {
cctx->next_layer_is_normalized = true; // This flag ingested by cli_recursion_stack_push().
- ret = cli_scan_desc(fd, cctx, CL_TYPE_TEXT_ASCII, 0, NULL, AC_SCAN_VIR, NULL, NULL);
+ ret = cli_scan_desc(fd, cctx, CL_TYPE_TEXT_ASCII, false, NULL, AC_SCAN_VIR, NULL, NULL);
}
}
close(fd);
@@ -259,7 +260,7 @@ static int cli_bytecode_context_reset(struct cli_bc_ctx *ctx)
return CL_SUCCESS;
}
-int cli_bytecode_context_clear(struct cli_bc_ctx *ctx)
+cl_error_t cli_bytecode_context_clear(struct cli_bc_ctx *ctx)
{
cli_bytecode_context_reset(ctx);
memset(ctx, 0, sizeof(*ctx));
@@ -313,7 +314,7 @@ static unsigned typealign(const struct cli_bc *bc, uint16_t type)
return bc->types[type - 65].align;
}
-int cli_bytecode_context_setfuncid(struct cli_bc_ctx *ctx, const struct cli_bc *bc, unsigned funcid)
+cl_error_t cli_bytecode_context_setfuncid(struct cli_bc_ctx *ctx, const struct cli_bc *bc, unsigned funcid)
{
unsigned i, s = 0;
const struct cli_bc_func *func;
@@ -353,12 +354,12 @@ int cli_bytecode_context_setfuncid(struct cli_bc_ctx *ctx, const struct cli_bc *
return CL_SUCCESS;
}
-static inline int type_isint(uint16_t type)
+static inline bool type_isint(uint16_t type)
{
return type > 0 && type <= 64;
}
-int cli_bytecode_context_setparam_int(struct cli_bc_ctx *ctx, unsigned i, uint64_t c)
+cl_error_t cli_bytecode_context_setparam_int(struct cli_bc_ctx *ctx, unsigned i, uint64_t c)
{
if (i >= ctx->numParams) {
cli_errmsg("bytecode: param index out of bounds: %u\n", i);
@@ -385,7 +386,7 @@ int cli_bytecode_context_setparam_int(struct cli_bc_ctx *ctx, unsigned i, uint64
return CL_SUCCESS;
}
-int cli_bytecode_context_setparam_ptr(struct cli_bc_ctx *ctx, unsigned i, void *data, unsigned datalen)
+cl_error_t cli_bytecode_context_setparam_ptr(struct cli_bc_ctx *ctx, unsigned i, void *data, unsigned datalen)
{
UNUSEDPARAM(ctx);
UNUSEDPARAM(i);
@@ -395,7 +396,7 @@ int cli_bytecode_context_setparam_ptr(struct cli_bc_ctx *ctx, unsigned i, void *
return CL_EARG;
}
-static inline uint64_t readNumber(const unsigned char *p, unsigned *off, unsigned len, char *ok)
+static inline uint64_t readNumber(const unsigned char *p, unsigned *off, unsigned len, bool *ok)
{
uint64_t n = 0;
unsigned i, newoff, lim, p0 = p[*off], shift = 0;
@@ -403,13 +404,13 @@ static inline uint64_t readNumber(const unsigned char *p, unsigned *off, unsigne
lim = p0 - 0x60;
if (lim > 0x10) {
cli_errmsg("Invalid number type: %c\n", p0);
- *ok = 0;
+ *ok = false;
return 0;
}
newoff = *off + lim + 1;
if (newoff > len) {
cli_errmsg("End of line encountered while reading number\n");
- *ok = 0;
+ *ok = false;
return 0;
}
@@ -422,7 +423,7 @@ static inline uint64_t readNumber(const unsigned char *p, unsigned *off, unsigne
uint64_t v = p[i];
if (UNLIKELY((v & 0xf0) != 0x60)) {
cli_errmsg("Invalid number part: %c\n", (char)v);
- *ok = 0;
+ *ok = false;
return 0;
}
v &= 0xf;
@@ -435,44 +436,44 @@ static inline uint64_t readNumber(const unsigned char *p, unsigned *off, unsigne
}
static inline funcid_t readFuncID(struct cli_bc *bc, unsigned char *p,
- unsigned *off, unsigned len, char *ok)
+ unsigned *off, unsigned len, bool *ok)
{
funcid_t id = readNumber(p, off, len, ok) - 1;
if (*ok && id >= bc->num_func) {
cli_errmsg("Called function out of range: %u >= %u\n", id, bc->num_func);
- *ok = 0;
+ *ok = false;
return ~0;
}
return id;
}
static inline funcid_t readAPIFuncID(struct cli_bc *bc, unsigned char *p,
- unsigned *off, unsigned len, char *ok)
+ unsigned *off, unsigned len, bool *ok)
{
funcid_t id = readNumber(p, off, len, ok) - 1;
if (*ok && !cli_bitset_test(bc->uses_apis, id)) {
cli_errmsg("Called undeclared API function: %u\n", id);
- *ok = 0;
+ *ok = false;
return ~0;
}
return id;
}
static inline unsigned readFixedNumber(const unsigned char *p, unsigned *off,
- unsigned len, char *ok, unsigned width)
+ unsigned len, bool *ok, unsigned width)
{
unsigned i, n = 0, shift = 0;
unsigned newoff = *off + width;
if (newoff > len) {
cli_errmsg("Newline encountered while reading number\n");
- *ok = 0;
+ *ok = false;
return 0;
}
for (i = *off; i < newoff; i++) {
unsigned v = p[i];
if (UNLIKELY((v & 0xf0) != 0x60)) {
cli_errmsg("Invalid number part: %c\n", v);
- *ok = 0;
+ *ok = false;
return 0;
}
v &= 0xf;
@@ -485,7 +486,7 @@ static inline unsigned readFixedNumber(const unsigned char *p, unsigned *off,
}
static inline operand_t readOperand(struct cli_bc_func *func, unsigned char *p,
- unsigned *off, unsigned len, char *ok)
+ unsigned *off, unsigned len, bool *ok)
{
uint64_t v;
if ((p[*off] & 0xf0) == 0x40 || p[*off] == 0x50) {
@@ -495,7 +496,7 @@ static inline operand_t readOperand(struct cli_bc_func *func, unsigned char *p,
/* TODO: unique constants */
func->constants = cli_realloc2(func->constants, (func->numConstants + 1) * sizeof(*func->constants));
if (!func->constants) {
- *ok = 0;
+ *ok = false;
return MAX_OP;
}
v = readNumber(p, off, len, ok);
@@ -524,19 +525,19 @@ static inline operand_t readOperand(struct cli_bc_func *func, unsigned char *p,
return MAX_OP;
if (v >= func->numValues) {
cli_errmsg("Operand index exceeds bounds: %u >= %u!\n", (unsigned)v, (unsigned)func->numValues);
- *ok = 0;
+ *ok = false;
return MAX_OP;
}
return v;
}
-static inline char *readData(const unsigned char *p, unsigned *off, unsigned len, char *ok, unsigned *datalen)
+static inline char *readData(const unsigned char *p, unsigned *off, unsigned len, bool *ok, unsigned *datalen)
{
unsigned char *dat, *q;
unsigned l, newoff, i;
if (p[*off] != '|') {
cli_errmsg("Data start marker missing: %c\n", p[*off]);
- *ok = 0;
+ *ok = false;
return NULL;
}
(*off)++;
@@ -548,13 +549,13 @@ static inline char *readData(const unsigned char *p, unsigned *off, unsigned len
newoff = *off + 2 * l;
if (newoff > len) {
cli_errmsg("Line ended while reading data\n");
- *ok = 0;
+ *ok = false;
return 0;
}
dat = cli_malloc(l);
if (!dat) {
cli_errmsg("Cannot allocate memory for data\n");
- *ok = 0;
+ *ok = false;
return NULL;
}
q = dat;
@@ -563,7 +564,7 @@ static inline char *readData(const unsigned char *p, unsigned *off, unsigned len
const unsigned char v1 = p[i + 1];
if (UNLIKELY((v0 & 0xf0) != 0x60 || (v1 & 0xf0) != 0x60)) {
cli_errmsg("Invalid data part: %c%c\n", v0, v1);
- *ok = 0;
+ *ok = false;
free(dat);
return 0;
}
@@ -574,7 +575,7 @@ static inline char *readData(const unsigned char *p, unsigned *off, unsigned len
return (char *)dat;
}
-static inline char *readString(const unsigned char *p, unsigned *off, unsigned len, char *ok)
+static inline char *readString(const unsigned char *p, unsigned *off, unsigned len, bool *ok)
{
unsigned stringlen = 0;
char *str = readData(p, off, len, ok, &stringlen);
@@ -582,17 +583,17 @@ static inline char *readString(const unsigned char *p, unsigned *off, unsigned l
str[stringlen - 1] = '\0';
cli_errmsg("bytecode: string missing \\0 terminator: %s\n", str);
free(str);
- *ok = 0;
+ *ok = false;
return NULL;
}
return str;
}
-static int parseHeader(struct cli_bc *bc, unsigned char *buffer, unsigned *linelength)
+static cl_error_t parseHeader(struct cli_bc *bc, unsigned char *buffer, unsigned *linelength)
{
uint64_t magic1;
unsigned magic2;
- char ok = 1;
+ bool ok = true;
unsigned offset, len, flevel;
char *pos;
@@ -678,7 +679,7 @@ static int parseHeader(struct cli_bc *bc, unsigned char *buffer, unsigned *linel
return CL_SUCCESS;
}
-static int parseLSig(struct cli_bc *bc, char *buffer)
+static cl_error_t parseLSig(struct cli_bc *bc, char *buffer)
{
// const char *prefix;
// char *vnames;
@@ -698,14 +699,14 @@ static int parseLSig(struct cli_bc *bc, char *buffer)
}
static uint16_t readTypeID(struct cli_bc *bc, unsigned char *buffer,
- unsigned *offset, unsigned len, char *ok)
+ unsigned *offset, unsigned len, bool *ok)
{
uint64_t t = readNumber(buffer, offset, len, ok);
if (!ok)
return ~0;
if (t >= bc->num_types + bc->start_tid) {
cli_errmsg("Invalid type id: %llu\n", (unsigned long long)t);
- *ok = 0;
+ *ok = false;
return ~0;
}
return t;
@@ -713,20 +714,20 @@ static uint16_t readTypeID(struct cli_bc *bc, unsigned char *buffer,
static void parseType(struct cli_bc *bc, struct cli_bc_type *ty,
unsigned char *buffer, unsigned *off, unsigned len,
- char *ok)
+ bool *ok)
{
unsigned j;
ty->numElements = readNumber(buffer, off, len, ok);
if (!*ok) {
cli_errmsg("Error parsing type\n");
- *ok = 0;
+ *ok = false;
return;
}
ty->containedTypes = cli_malloc(sizeof(*ty->containedTypes) * ty->numElements);
if (!ty->containedTypes) {
cli_errmsg("Out of memory allocating %u types\n", ty->numElements);
- *ok = 0;
+ *ok = false;
return;
}
for (j = 0; j < ty->numElements; j++) {
@@ -748,10 +749,10 @@ static void add_static_types(struct cli_bc *bc)
}
}
-static int parseTypes(struct cli_bc *bc, unsigned char *buffer)
+static cl_error_t parseTypes(struct cli_bc *bc, unsigned char *buffer)
{
unsigned i, offset = 1, len = strlen((const char *)buffer);
- char ok = 1;
+ bool ok = true;
if (buffer[0] != 'T') {
cli_errmsg("Invalid function types header: %c\n", buffer[0]);
@@ -845,7 +846,7 @@ static int parseTypes(struct cli_bc *bc, unsigned char *buffer)
/* checks whether the type described by tid is the same as the one described by
* apitid. */
-static int types_equal(const struct cli_bc *bc, uint16_t *apity2ty, uint16_t tid, uint16_t apitid)
+static bool types_equal(const struct cli_bc *bc, uint16_t *apity2ty, uint16_t tid, uint16_t apitid)
{
unsigned i;
const struct cli_bc_type *ty = &bc->types[tid - 65];
@@ -854,37 +855,38 @@ static int types_equal(const struct cli_bc *bc, uint16_t *apity2ty, uint16_t tid
* Since we need to check equality of recursive types, we assume types are
* equal while checking equality of contained types, unless proven
* otherwise. */
- if (apity2ty[apitid] == tid + 1)
- return 1;
+ if (apity2ty[apitid] == tid + 1) {
+ return true;
+ }
apity2ty[apitid] = tid + 1;
if (ty->kind != apity->kind) {
cli_dbgmsg("bytecode: type kind mismatch: %u != %u\n", ty->kind, apity->kind);
- return 0;
+ return false;
}
if (ty->numElements != apity->numElements) {
cli_dbgmsg("bytecode: type numElements mismatch: %u != %u\n", ty->numElements, apity->numElements);
- return 0;
+ return false;
}
for (i = 0; i < ty->numElements; i++) {
if (apity->containedTypes[i] < BC_START_TID) {
if (ty->containedTypes[i] != apity->containedTypes[i]) {
cli_dbgmsg("bytecode: contained type mismatch: %u != %u\n",
ty->containedTypes[i], apity->containedTypes[i]);
- return 0;
+ return false;
}
} else if (!types_equal(bc, apity2ty, ty->containedTypes[i], apity->containedTypes[i] - BC_START_TID))
- return 0;
+ return false;
if (ty->kind == DArrayType)
break; /* validated the contained type already */
}
- return 1;
+ return true;
}
-static int parseApis(struct cli_bc *bc, unsigned char *buffer)
+static cl_error_t parseApis(struct cli_bc *bc, unsigned char *buffer)
{
unsigned i, offset = 1, len = strlen((const char *)buffer), maxapi, calls;
- char ok = 1;
+ bool ok = true;
uint16_t *apity2ty; /*map of api type to current bytecode type ID */
if (buffer[0] != 'E') {
@@ -924,17 +926,17 @@ static int parseApis(struct cli_bc *bc, unsigned char *buffer)
/* validate APIcall prototype */
if (id > maxapi) {
cli_errmsg("bytecode: API id %u out of range, max %u\n", id, maxapi);
- ok = 0;
+ ok = false;
}
/* API ids start from 1 */
id--;
if (ok && name && strcmp(cli_apicalls[id].name, name)) {
cli_errmsg("bytecode: API %u name mismatch: %s expected %s\n", id, name, cli_apicalls[id].name);
- ok = 0;
+ ok = false;
}
if (ok && !types_equal(bc, apity2ty, tid, cli_apicalls[id].type)) {
cli_errmsg("bytecode: API %u prototype doesn't match\n", id);
- ok = 0;
+ ok = false;
}
/* don't need the name anymore */
free(name);
@@ -951,7 +953,7 @@ static int parseApis(struct cli_bc *bc, unsigned char *buffer)
return CL_SUCCESS;
}
-static uint16_t type_components(struct cli_bc *bc, uint16_t id, char *ok)
+static uint16_t type_components(struct cli_bc *bc, uint16_t id, bool *ok)
{
unsigned i, sum = 0;
const struct cli_bc_type *ty;
@@ -963,7 +965,7 @@ static uint16_t type_components(struct cli_bc *bc, uint16_t id, char *ok)
case DFunctionType:
cli_errmsg("bytecode: function type not accepted for constant: %u\n", id);
/* don't accept functions as constant initializers */
- *ok = 0;
+ *ok = false;
return 0;
case DPointerType:
return 2;
@@ -976,14 +978,14 @@ static uint16_t type_components(struct cli_bc *bc, uint16_t id, char *ok)
case DArrayType:
return type_components(bc, ty->containedTypes[0], ok) * ty->numElements;
default:
- *ok = 0;
+ *ok = false;
return 0;
}
}
static void readConstant(struct cli_bc *bc, unsigned i, unsigned comp,
unsigned char *buffer, unsigned *offset,
- unsigned len, char *ok)
+ unsigned len, bool *ok)
{
unsigned j = 0;
if (*ok && buffer[*offset] == 0x40 &&
@@ -996,7 +998,7 @@ static void readConstant(struct cli_bc *bc, unsigned i, unsigned comp,
while (*ok && buffer[*offset] != 0x60) {
if (j >= comp) {
cli_errmsg("bytecode: constant has too many subcomponents, expected %u\n", comp);
- *ok = 0;
+ *ok = false;
return;
}
buffer[*offset] |= 0x20;
@@ -1004,17 +1006,17 @@ static void readConstant(struct cli_bc *bc, unsigned i, unsigned comp,
}
if (*ok && j != comp) {
cli_errmsg("bytecode: constant has too few subcomponents: %u < %u\n", j, comp);
- *ok = 0;
+ *ok = false;
}
(*offset)++;
}
/* parse constant globals with constant initializers */
-static int parseGlobals(struct cli_bc *bc, unsigned char *buffer)
+static cl_error_t parseGlobals(struct cli_bc *bc, unsigned char *buffer)
{
unsigned i, offset = 1, len = strlen((const char *)buffer), numglobals;
unsigned maxglobal;
- char ok = 1;
+ bool ok = true;
if (buffer[0] != 'G') {
cli_errmsg("bytecode: Invalid globals header: %c\n", buffer[0]);
@@ -1060,11 +1062,11 @@ static int parseGlobals(struct cli_bc *bc, unsigned char *buffer)
return CL_SUCCESS;
}
-static int parseMD(struct cli_bc *bc, unsigned char *buffer)
+static cl_error_t parseMD(struct cli_bc *bc, unsigned char *buffer)
{
unsigned offset = 1, len = strlen((const char *)buffer);
unsigned numMD, i, b;
- char ok = 1;
+ bool ok = true;
if (buffer[0] != 'D')
return CL_EMALFDB;
numMD = readNumber(buffer, &offset, len, &ok);
@@ -1111,9 +1113,9 @@ static int parseMD(struct cli_bc *bc, unsigned char *buffer)
return CL_SUCCESS;
}
-static int parseFunctionHeader(struct cli_bc *bc, unsigned fn, unsigned char *buffer)
+static cl_error_t parseFunctionHeader(struct cli_bc *bc, unsigned fn, unsigned char *buffer)
{
- char ok = 1;
+ bool ok = true;
unsigned offset, len, all_locals = 0, i;
struct cli_bc_func *func;
@@ -1192,12 +1194,12 @@ static int parseFunctionHeader(struct cli_bc *bc, unsigned fn, unsigned char *bu
return CL_SUCCESS;
}
-static bbid_t readBBID(struct cli_bc_func *func, const unsigned char *buffer, unsigned *off, unsigned len, char *ok)
+static bbid_t readBBID(struct cli_bc_func *func, const unsigned char *buffer, unsigned *off, unsigned len, bool *ok)
{
unsigned id = readNumber(buffer, off, len, ok);
if (!id || id >= func->numBB) {
cli_errmsg("Basic block ID out of range: %u\n", id);
- *ok = 0;
+ *ok = false;
}
if (!*ok)
return ~0;
@@ -1218,9 +1220,9 @@ static int16_t get_optype(const struct cli_bc_func *bcfunc, operand_t op)
return bcfunc->types[op] & 0x7fff;
}
-static int parseBB(struct cli_bc *bc, unsigned func, unsigned bb, unsigned char *buffer)
+static cl_error_t parseBB(struct cli_bc *bc, unsigned func, unsigned bb, unsigned char *buffer)
{
- char ok = 1;
+ bool ok = true;
unsigned offset, len, i, last = 0;
struct cli_bc_bb *BB;
struct cli_bc_func *bcfunc = &bc->funcs[func];
@@ -1371,7 +1373,7 @@ static int parseBB(struct cli_bc *bc, unsigned func, unsigned bb, unsigned char
break;
default:
cli_errmsg("Opcode %u with too many operands: %u?\n", inst.opcode, numOp);
- ok = 0;
+ ok = false;
break;
}
}
@@ -1577,14 +1579,15 @@ void cli_sigperf_events_destroy()
cli_events_free(g_sigevents);
}
-int cli_bytecode_load(struct cli_bc *bc, FILE *f, struct cli_dbio *dbio, int trust, int sigperf)
+cl_error_t cli_bytecode_load(struct cli_bc *bc, FILE *f, struct cli_dbio *dbio, int trust, int sigperf)
{
unsigned row = 0, current_func = 0, bb = 0;
char *buffer;
unsigned linelength = 0;
char firstbuf[FILEBUFF];
enum parse_state state;
- int rc, end = 0;
+ cl_error_t rc;
+ int end = 0;
memset(bc, 0, sizeof(*bc));
cli_dbgmsg("Loading %s bytecode\n", trust ? "trusted" : "untrusted");
@@ -1779,15 +1782,15 @@ static int register_events(cli_events_t *ev)
return 0;
}
-int cli_bytecode_run(const struct cli_all_bc *bcs, const struct cli_bc *bc, struct cli_bc_ctx *ctx)
+cl_error_t cli_bytecode_run(const struct cli_all_bc *bcs, const struct cli_bc *bc, struct cli_bc_ctx *ctx)
{
- int ret = CL_SUCCESS;
+ cl_error_t ret = CL_SUCCESS;
struct cli_bc_inst inst;
struct cli_bc_func func;
cli_events_t *jit_ev = NULL, *interp_ev = NULL;
- int test_mode = 0;
- cli_ctx *cctx = (cli_ctx *)ctx->ctx;
+ bool test_mode = 0;
+ cli_ctx *cctx = (cli_ctx *)ctx->ctx;
if (!ctx || !ctx->bc || !ctx->func)
return CL_ENULLARG;
@@ -1795,7 +1798,7 @@ int cli_bytecode_run(const struct cli_all_bc *bcs, const struct cli_bc *bc, stru
return CL_ENULLARG;
if (cctx && cctx->engine->bytecode_mode == CL_BYTECODE_MODE_TEST)
- test_mode = 1;
+ test_mode = true;
if (bc->state == bc_loaded) {
cli_errmsg("bytecode has to be prepared either for interpreter or JIT!\n");
@@ -1854,7 +1857,7 @@ int cli_bytecode_run(const struct cli_all_bc *bcs, const struct cli_bc *bc, stru
cli_event_string(interp_ev, BCEV_VIRUSNAME, ctx->virname);
/* need to be called here to catch any extracted but not yet scanned files */
- if (ctx->outfd && (ret != CL_VIRUS || cctx->options->general & CL_SCAN_GENERAL_ALLMATCHES))
+ if (ctx->outfd && (ret != CL_VIRUS))
cli_bcapi_extract_new(ctx, -1);
}
if (bc->state == bc_jit || test_mode) {
@@ -1873,7 +1876,7 @@ int cli_bytecode_run(const struct cli_all_bc *bcs, const struct cli_bc *bc, stru
cli_event_string(jit_ev, BCEV_VIRUSNAME, ctx->virname);
/* need to be called here to catch any extracted but not yet scanned files */
- if (ctx->outfd && (ret != CL_VIRUS || cctx->options->general & CL_SCAN_GENERAL_ALLMATCHES))
+ if (ctx->outfd && (ret != CL_VIRUS))
cli_bcapi_extract_new(ctx, -1);
}
cli_event_time_stop(g_sigevents, bc->sigtime_id);
@@ -1884,18 +1887,18 @@ int cli_bytecode_run(const struct cli_all_bc *bcs, const struct cli_bc *bc, stru
unsigned interp_errors = cli_event_errors(interp_ev);
unsigned jit_errors = cli_event_errors(jit_ev);
unsigned interp_warns = 0, jit_warns = 0;
- int ok = 1;
+ bool ok = true;
enum bc_events evid;
if (interp_errors || jit_errors) {
cli_infomsg(cctx, "bytecode %d encountered %u JIT and %u interpreter errors\n",
bc->id, interp_errors, jit_errors);
- ok = 0;
+ ok = false;
}
if (!ctx->no_diff && cli_event_diff_all(interp_ev, jit_ev, NULL)) {
cli_infomsg(cctx, "bytecode %d execution different with JIT and interpreter, see --debug for details\n",
bc->id);
- ok = 0;
+ ok = false;
}
for (evid = BCEV_API_WARN_BEGIN + 1; evid < BCEV_API_WARN_END; evid++) {
union ev_val v;
@@ -1909,7 +1912,7 @@ int cli_bytecode_run(const struct cli_all_bc *bcs, const struct cli_bc *bc, stru
if (interp_warns || jit_warns) {
cli_infomsg(cctx, "bytecode %d encountered %u JIT and %u interpreter warnings\n",
bc->id, interp_warns, jit_warns);
- ok = 0;
+ ok = false;
}
/*cli_event_debug(jit_ev, BCEV_EXEC_TIME);
cli_event_debug(interp_ev, BCEV_EXEC_TIME);
@@ -2085,12 +2088,12 @@ static int calc_gepz(struct cli_bc *bc, struct cli_bc_func *func, uint16_t tid,
return 1;
}
-static int cli_bytecode_prepare_interpreter(struct cli_bc *bc)
+static cl_error_t cli_bytecode_prepare_interpreter(struct cli_bc *bc)
{
unsigned i, j, k;
uint64_t *gmap;
unsigned bcglobalid = cli_apicall_maxglobal - _FIRST_GLOBAL + 2;
- int ret = CL_SUCCESS;
+ cl_error_t ret = CL_SUCCESS;
bc->numGlobalBytes = 0;
gmap = cli_malloc(bc->num_globals * sizeof(*gmap));
if (!gmap) {
@@ -2358,7 +2361,7 @@ static int cli_bytecode_prepare_interpreter(struct cli_bc *bc)
return ret;
}
-static int add_selfcheck(struct cli_all_bc *bcs)
+static cl_error_t add_selfcheck(struct cli_all_bc *bcs)
{
struct cli_bc_func *func;
struct cli_bc_inst *inst;
@@ -2454,14 +2457,14 @@ static int add_selfcheck(struct cli_all_bc *bcs)
inst->interp_op = inst->opcode * 5;
bc->state = bc_loaded;
- return 0;
+ return CL_SUCCESS;
}
-static int run_selfcheck(struct cli_all_bc *bcs)
+static cl_error_t run_selfcheck(struct cli_all_bc *bcs)
{
struct cli_bc_ctx *ctx;
struct cli_bc *bc = &bcs->all_bcs[bcs->count - 1];
- int rc;
+ cl_error_t rc;
if (bc->state != bc_jit && bc->state != bc_interp) {
cli_errmsg("Failed to prepare selfcheck bytecode\n");
return CL_EBYTECODE;
@@ -2486,10 +2489,10 @@ static int run_selfcheck(struct cli_all_bc *bcs)
return rc;
}
-static int selfcheck(int jit, struct cli_bcengine *engine)
+static cl_error_t selfcheck(bool jit, struct cli_bcengine *engine)
{
struct cli_all_bc bcs;
- int rc;
+ cl_error_t rc;
memset(&bcs, 0, sizeof(bcs));
bcs.all_bcs = NULL;
@@ -2550,7 +2553,7 @@ static int set_mode(struct cl_engine *engine, enum bytecode_mode mode)
/* runs the first bytecode of the specified kind, or the builtin one if no
* bytecode of that kind is loaded */
-static int run_builtin_or_loaded(struct cli_all_bc *bcs, uint8_t kind, const char *builtin_cbc, struct cli_bc_ctx *ctx, const char *desc)
+static cl_error_t run_builtin_or_loaded(struct cli_all_bc *bcs, uint8_t kind, const char *builtin_cbc, struct cli_bc_ctx *ctx, const char *desc)
{
unsigned i, builtin = 0, rc = 0;
struct cli_bc *bc = NULL;
@@ -2616,10 +2619,10 @@ static int run_builtin_or_loaded(struct cli_all_bc *bcs, uint8_t kind, const cha
return rc;
}
-int cli_bytecode_prepare2(struct cl_engine *engine, struct cli_all_bc *bcs, unsigned dconfmask)
+cl_error_t cli_bytecode_prepare2(struct cl_engine *engine, struct cli_all_bc *bcs, unsigned dconfmask)
{
- unsigned i, interp = 0, jitok = 0, jitcount = 0;
- int rc;
+ unsigned i, interp = 0, jitcount = 0;
+ cl_error_t rc;
struct cli_bc_ctx *ctx;
if (!bcs->count) {
@@ -2671,13 +2674,15 @@ int cli_bytecode_prepare2(struct cl_engine *engine, struct cli_all_bc *bcs, unsi
cli_warnmsg("Bytecode: BC_STARTUP failed to run, disabling ALL bytecodes! Please report to https://github.com/Cisco-Talos/clamav/issues\n");
ctx->bytecode_disable_status = 2;
} else {
+ uint64_t context_result;
+
cli_dbgmsg("Bytecode: disable status is %d\n", ctx->bytecode_disable_status);
- rc = cli_bytecode_context_getresult_int(ctx);
+ context_result = cli_bytecode_context_getresult_int(ctx);
/* check magic number, don't use 0 here because it is too easy for a
* buggy bytecode to return 0 */
- if ((unsigned int)rc != (unsigned int)0xda7aba5e) {
- cli_warnmsg("Bytecode: selftest failed with code %08x. Please report to https://github.com/Cisco-Talos/clamav/issues\n",
- rc);
+ if (context_result != (uint64_t)0xda7aba5e) {
+ cli_warnmsg("Bytecode: selftest failed with code " STDx64 ". Please report to https://github.com/Cisco-Talos/clamav/issues\n",
+ context_result);
if (engine->bytecode_mode == CL_BYTECODE_MODE_TEST)
return CL_EBYTECODE_TESTFAIL;
}
@@ -2698,10 +2703,9 @@ int cli_bytecode_prepare2(struct cl_engine *engine, struct cli_all_bc *bcs, unsi
if (engine->bytecode_mode != CL_BYTECODE_MODE_INTERPRETER &&
engine->bytecode_mode != CL_BYTECODE_MODE_OFF) {
- selfcheck(1, bcs->engine);
+ selfcheck(true, bcs->engine);
rc = cli_bytecode_prepare_jit(bcs);
if (rc == CL_SUCCESS) {
- jitok = 1;
cli_dbgmsg("Bytecode: %u bytecode prepared with JIT\n", bcs->count);
if (engine->bytecode_mode != CL_BYTECODE_MODE_TEST)
return CL_SUCCESS;
@@ -2756,9 +2760,9 @@ int cli_bytecode_prepare2(struct cl_engine *engine, struct cli_all_bc *bcs, unsi
return CL_SUCCESS;
}
-int cli_bytecode_init(struct cli_all_bc *allbc)
+cl_error_t cli_bytecode_init(struct cli_all_bc *allbc)
{
- int ret;
+ cl_error_t ret;
memset(allbc, 0, sizeof(*allbc));
ret = cli_bytecode_init_jit(allbc, 0 /*XXX*/);
cli_dbgmsg("Bytecode initialized in %s mode\n",
@@ -2767,25 +2771,25 @@ int cli_bytecode_init(struct cli_all_bc *allbc)
return ret;
}
-int cli_bytecode_done(struct cli_all_bc *allbc)
+cl_error_t cli_bytecode_done(struct cli_all_bc *allbc)
{
return cli_bytecode_done_jit(allbc, 0);
}
-int cli_bytecode_context_setfile(struct cli_bc_ctx *ctx, fmap_t *map)
+cl_error_t cli_bytecode_context_setfile(struct cli_bc_ctx *ctx, fmap_t *map)
{
ctx->fmap = map;
ctx->file_size = map->len;
ctx->hooks.filesize = &ctx->file_size;
- return 0;
+ return CL_SUCCESS;
}
-int cli_bytecode_runlsig(cli_ctx *cctx, struct cli_target_info *tinfo,
- const struct cli_all_bc *bcs, unsigned bc_idx,
- const uint32_t *lsigcnt,
- const uint32_t *lsigsuboff, fmap_t *map)
+cl_error_t cli_bytecode_runlsig(cli_ctx *cctx, struct cli_target_info *tinfo,
+ const struct cli_all_bc *bcs, unsigned bc_idx,
+ const uint32_t *lsigcnt,
+ const uint32_t *lsigsuboff, fmap_t *map)
{
- int ret;
+ cl_error_t ret;
struct cli_bc_ctx ctx;
const struct cli_bc *bc = &bcs->all_bcs[bc_idx - 1];
struct cli_pe_hook_data pehookdata;
@@ -2837,14 +2841,10 @@ int cli_bytecode_runlsig(cli_ctx *cctx, struct cli_target_info *tinfo,
return CL_SUCCESS;
}
if (ctx.virname) {
- int rc;
+ cl_error_t rc;
cli_dbgmsg("Bytecode found virus: %s\n", ctx.virname);
- if (!strncmp(ctx.virname, "BC.Heuristics", 13)) {
- rc = cli_append_potentially_unwanted(cctx, ctx.virname);
- } else {
- rc = cli_append_virus(cctx, ctx.virname);
- }
+ rc = cli_append_virus(cctx, ctx.virname);
cli_bytecode_context_clear(&ctx);
return rc;
@@ -2855,12 +2855,12 @@ int cli_bytecode_runlsig(cli_ctx *cctx, struct cli_target_info *tinfo,
return CL_SUCCESS;
}
-int cli_bytecode_runhook(cli_ctx *cctx, const struct cl_engine *engine, struct cli_bc_ctx *ctx,
- unsigned id, fmap_t *map)
+cl_error_t cli_bytecode_runhook(cli_ctx *cctx, const struct cl_engine *engine, struct cli_bc_ctx *ctx,
+ unsigned id, fmap_t *map)
{
const unsigned *hooks = engine->hooks[id - _BC_START_HOOKS];
unsigned i, hooks_cnt = engine->hooks_cnt[id - _BC_START_HOOKS];
- int ret;
+ cl_error_t ret;
unsigned executed = 0, breakflag = 0, errorflag = 0;
if (!cctx)
@@ -2889,8 +2889,13 @@ int cli_bytecode_runhook(cli_ctx *cctx, const struct cl_engine *engine, struct c
}
if (ctx->virname) {
cli_dbgmsg("Bytecode runhook found virus: %s\n", ctx->virname);
- cli_append_virus(cctx, ctx->virname);
- if (!(cctx->options->general & CL_SCAN_GENERAL_ALLMATCHES)) {
+
+ if (!strncmp(ctx->virname, "BC.Heuristics", 13)) {
+ ret = cli_append_potentially_unwanted(cctx, ctx->virname);
+ } else {
+ ret = cli_append_virus(cctx, ctx->virname);
+ }
+ if (ret == CL_VIRUS) {
cli_bytecode_context_clear(ctx);
return CL_VIRUS;
}
@@ -2906,38 +2911,43 @@ int cli_bytecode_runhook(cli_ctx *cctx, const struct cl_engine *engine, struct c
}
if (!ret) {
char *tempfile;
+
int fd = cli_bytecode_context_getresult_file(ctx, &tempfile);
if (fd && fd != -1) {
- if (cctx->engine->keeptmp)
+ if (cctx->engine->keeptmp) {
cli_dbgmsg("Bytecode %u unpacked file saved in %s\n",
bc->id, tempfile);
- else
+ } else {
cli_dbgmsg("Bytecode %u unpacked file\n", bc->id);
+ }
+
lseek(fd, 0, SEEK_SET);
cli_dbgmsg("***** Scanning unpacked file ******\n");
ret = cli_magic_scan_desc(fd, tempfile, cctx, NULL);
- if (!cctx->engine->keeptmp)
- if (ftruncate(fd, 0) == -1)
+ if (!cctx->engine->keeptmp) {
+ if (ftruncate(fd, 0) == -1) {
cli_dbgmsg("ftruncate failed on %d\n", fd);
+ }
+ }
+
close(fd);
+
if (!cctx->engine->keeptmp) {
- if (tempfile && cli_unlink(tempfile))
+ if (tempfile && cli_unlink(tempfile)) {
ret = CL_EUNLINK;
+ }
}
+
free(tempfile);
- if (ret != CL_CLEAN) {
- if (ret == CL_VIRUS) {
- cli_dbgmsg("Scanning unpacked file by bytecode %u found a virus\n", bc->id);
- if (cctx->options->general & CL_SCAN_GENERAL_ALLMATCHES) {
- cli_bytecode_context_reset(ctx);
- continue;
- }
- cli_bytecode_context_clear(ctx);
- return ret;
- }
+
+ if (ret != CL_SUCCESS) {
+ cli_dbgmsg("Scanning unpacked file by bytecode %u found a reason to stop: %s\n", bc->id, cl_strerror(ret));
+ cli_bytecode_context_clear(ctx);
+ return ret;
}
+
cli_bytecode_context_reset(ctx);
continue;
}
@@ -2948,16 +2958,32 @@ int cli_bytecode_runhook(cli_ctx *cctx, const struct cl_engine *engine, struct c
cli_dbgmsg("Bytecode: executed %u bytecodes for this hook\n", executed);
else
cli_dbgmsg("Bytecode: no logical signature matched, no bytecode executed\n");
+
if (errorflag && cctx->engine->bytecode_mode == CL_BYTECODE_MODE_TEST)
return CL_EBYTECODE_TESTFAIL;
+
return breakflag ? CL_BREAK : CL_CLEAN;
}
-int cli_bytecode_context_setpe(struct cli_bc_ctx *ctx, const struct cli_pe_hook_data *data, const struct cli_exe_section *sections)
+cl_error_t cli_bytecode_context_setpe(struct cli_bc_ctx *ctx, const struct cli_pe_hook_data *data, const struct cli_exe_section *sections)
{
ctx->sections = sections;
ctx->hooks.pedata = data;
- return 0;
+ return CL_SUCCESS;
+}
+
+cl_error_t cli_bytecode_context_setpdf(struct cli_bc_ctx *ctx, unsigned phase,
+ unsigned nobjs,
+ struct pdf_obj **objs, uint32_t *pdf_flags,
+ uint32_t pdfsize, uint32_t pdfstartoff)
+{
+ ctx->pdf_nobjs = nobjs;
+ ctx->pdf_objs = objs;
+ ctx->pdf_flags = pdf_flags;
+ ctx->pdf_size = pdfsize;
+ ctx->pdf_startoff = pdfstartoff;
+ ctx->pdf_phase = phase;
+ return CL_SUCCESS;
}
void cli_bytecode_context_setctx(struct cli_bc_ctx *ctx, void *cctx)
@@ -2972,7 +2998,7 @@ void cli_bytecode_describe(const struct cli_bc *bc)
int cols;
unsigned i;
time_t stamp;
- int had;
+ bool had;
if (!bc) {
printf("(null bytecode)\n");
@@ -3079,7 +3105,7 @@ void cli_bytecode_describe(const struct cli_bc *bc)
printf("\tnumber of debug nodes: %u\n", bc->dbgnode_cnt);
printf("\tbytecode APIs used:");
cols = 0; /* remaining */
- had = 0;
+ had = false;
for (i = 0; i < cli_apicall_maxapi; i++) {
if (cli_bitset_test(bc->uses_apis, i)) {
unsigned len = strlen(cli_apicalls[i].name);
@@ -3090,7 +3116,7 @@ void cli_bytecode_describe(const struct cli_bc *bc)
cols = 72;
}
printf(" %s", cli_apicalls[i].name);
- had = 1;
+ had = true;
cols -= len;
}
}
@@ -3413,7 +3439,7 @@ void cli_byteinst_describe(const struct cli_bc_inst *inst, unsigned *bbnum)
case OP_BC_CALL_DIRECT:
printf("%d = call F.%d (", inst->dest, inst->u.ops.funcid);
for (j = 0; j < inst->u.ops.numOps; ++j) {
- if (j == inst->u.ops.numOps - 1) {
+ if (j == (size_t)(inst->u.ops.numOps - 1)) {
printf("%d", inst->u.ops.ops[j]);
} else {
printf("%d, ", inst->u.ops.ops[j]);
diff --git a/libclamav/bytecode.h b/libclamav/bytecode.h
index 85017f2007..38fb225054 100644
--- a/libclamav/bytecode.h
+++ b/libclamav/bytecode.h
@@ -90,13 +90,14 @@ struct pdf_obj;
struct cli_bc_ctx *cli_bytecode_context_alloc(void);
/* FIXME: we can't include others.h because others.h includes us...*/
void cli_bytecode_context_setctx(struct cli_bc_ctx *ctx, void *cctx);
-int cli_bytecode_context_setfuncid(struct cli_bc_ctx *ctx, const struct cli_bc *bc, unsigned funcid);
-int cli_bytecode_context_setparam_int(struct cli_bc_ctx *ctx, unsigned i, uint64_t c);
-int cli_bytecode_context_setparam_ptr(struct cli_bc_ctx *ctx, unsigned i, void *data, unsigned datalen);
-int cli_bytecode_context_setfile(struct cli_bc_ctx *ctx, fmap_t *map);
-int cli_bytecode_context_setpe(struct cli_bc_ctx *ctx, const struct cli_pe_hook_data *data, const struct cli_exe_section *sections);
-int cli_bytecode_context_setpdf(struct cli_bc_ctx *ctx, unsigned phase, unsigned nobjs, struct pdf_obj **objs, uint32_t *pdf_flags, uint32_t pdfsize, uint32_t pdfstartoff);
-int cli_bytecode_context_clear(struct cli_bc_ctx *ctx);
+cl_error_t cli_bytecode_context_setfuncid(struct cli_bc_ctx *ctx, const struct cli_bc *bc, unsigned funcid);
+cl_error_t cli_bytecode_context_setparam_int(struct cli_bc_ctx *ctx, unsigned i, uint64_t c);
+cl_error_t cli_bytecode_context_setparam_ptr(struct cli_bc_ctx *ctx, unsigned i, void *data, unsigned datalen);
+cl_error_t cli_bytecode_context_setfile(struct cli_bc_ctx *ctx, fmap_t *map);
+cl_error_t cli_bytecode_context_setpe(struct cli_bc_ctx *ctx, const struct cli_pe_hook_data *data, const struct cli_exe_section *sections);
+cl_error_t cli_bytecode_context_setpdf(struct cli_bc_ctx *ctx, unsigned phase, unsigned nobjs, struct pdf_obj **objs, uint32_t *pdf_flags, uint32_t pdfsize, uint32_t pdfstartoff);
+cl_error_t cli_bytecode_context_clear(struct cli_bc_ctx *ctx);
+
/* returns file descriptor, sets tempfile. Caller takes ownership, and is
* responsible for freeing/unlinking */
int cli_bytecode_context_getresult_file(struct cli_bc_ctx *ctx, char **tempfilename);
@@ -106,16 +107,16 @@ void cli_bytecode_context_destroy(struct cli_bc_ctx *ctx);
#ifdef __cplusplus
extern "C" {
#endif
-extern LIBCLAMAV_EXPORT int have_clamjit();
+extern LIBCLAMAV_EXPORT bool have_clamjit();
#ifdef __cplusplus
}
#endif
-int cli_bytecode_init(struct cli_all_bc *allbc);
-int cli_bytecode_load(struct cli_bc *bc, FILE *f, struct cli_dbio *dbio, int security, int sigperf);
-int cli_bytecode_prepare2(struct cl_engine *engine, struct cli_all_bc *allbc, unsigned dconfmask);
-int cli_bytecode_run(const struct cli_all_bc *bcs, const struct cli_bc *bc, struct cli_bc_ctx *ctx);
+cl_error_t cli_bytecode_init(struct cli_all_bc *allbc);
+cl_error_t cli_bytecode_load(struct cli_bc *bc, FILE *f, struct cli_dbio *dbio, int security, int sigperf);
+cl_error_t cli_bytecode_prepare2(struct cl_engine *engine, struct cli_all_bc *allbc, unsigned dconfmask);
+cl_error_t cli_bytecode_run(const struct cli_all_bc *bcs, const struct cli_bc *bc, struct cli_bc_ctx *ctx);
void cli_bytecode_destroy(struct cli_bc *bc);
-int cli_bytecode_done(struct cli_all_bc *allbc);
+cl_error_t cli_bytecode_done(struct cli_all_bc *allbc);
/* Bytecode IR descriptions */
void cli_bytecode_describe(const struct cli_bc *bc);
@@ -128,14 +129,14 @@ void cli_bytefunc_describe(const struct cli_bc *bc, unsigned funcid);
struct cli_exe_info;
struct cli_ctx_tag;
struct cli_target_info;
-int cli_bytecode_runlsig(struct cli_ctx_tag *ctx, struct cli_target_info *info, const struct cli_all_bc *bcs, unsigned bc_idx, const uint32_t *lsigcnt, const uint32_t *lsigsuboff, fmap_t *map);
-int cli_bytecode_runhook(struct cli_ctx_tag *cctx, const struct cl_engine *engine, struct cli_bc_ctx *ctx, unsigned id, fmap_t *map);
+cl_error_t cli_bytecode_runlsig(struct cli_ctx_tag *ctx, struct cli_target_info *info, const struct cli_all_bc *bcs, unsigned bc_idx, const uint32_t *lsigcnt, const uint32_t *lsigsuboff, fmap_t *map);
+cl_error_t cli_bytecode_runhook(struct cli_ctx_tag *cctx, const struct cl_engine *engine, struct cli_bc_ctx *ctx, unsigned id, fmap_t *map);
#ifdef __cplusplus
extern "C" {
#endif
-int bytecode_init(void);
+cl_error_t bytecode_init(void);
/* Bytecode internal debug API */
void cli_bytecode_debug(int argc, char **argv);
void cli_bytecode_printversion(void);
diff --git a/libclamav/bytecode_api.c b/libclamav/bytecode_api.c
index 7662af4ae2..1cb2f83a08 100644
--- a/libclamav/bytecode_api.c
+++ b/libclamav/bytecode_api.c
@@ -626,7 +626,7 @@ int32_t cli_bcapi_hashset_add(struct cli_bc_ctx *ctx, int32_t id, uint32_t key)
struct cli_hashset *s = get_hashset(ctx, id);
if (!s)
return -1;
- return cli_hashset_addkey(s, key);
+ return cli_hashset_addkey(s, key) == CL_SUCCESS ? 0 : -1;
}
int32_t cli_bcapi_hashset_remove(struct cli_bc_ctx *ctx, int32_t id, uint32_t key)
@@ -634,7 +634,7 @@ int32_t cli_bcapi_hashset_remove(struct cli_bc_ctx *ctx, int32_t id, uint32_t ke
struct cli_hashset *s = get_hashset(ctx, id);
if (!s)
return -1;
- return cli_hashset_removekey(s, key);
+ return cli_hashset_removekey(s, key) == CL_SUCCESS ? 0 : -1;
}
int32_t cli_bcapi_hashset_contains(struct cli_bc_ctx *ctx, int32_t id, uint32_t key)
@@ -1402,7 +1402,7 @@ int32_t cli_bcapi_map_new(struct cli_bc_ctx *ctx, int32_t keysize, int32_t value
ctx->maps = s;
ctx->nmaps = n;
s = &s[n - 1];
- cli_map_init(s, keysize, valuesize, 16);
+ (void)cli_map_init(s, keysize, valuesize, 16);
return n - 1;
}
@@ -1415,10 +1415,26 @@ static struct cli_map *get_hashtab(struct cli_bc_ctx *ctx, int32_t id)
int32_t cli_bcapi_map_addkey(struct cli_bc_ctx *ctx, const uint8_t *key, int32_t keysize, int32_t id)
{
+ cl_error_t ret;
struct cli_map *s = get_hashtab(ctx, id);
if (!s)
return -1;
- return cli_map_addkey(s, key, keysize);
+
+ ret = cli_map_addkey(s, key, keysize);
+ switch (ret) {
+ case CL_SUCCESS: {
+ // key didn't exist and was added
+ return 1;
+ }
+ case CL_ECREAT: {
+ // already added
+ return 0;
+ }
+ default: {
+ // error occurred
+ return -1;
+ }
+ }
}
int32_t cli_bcapi_map_setvalue(struct cli_bc_ctx *ctx, const uint8_t *value, int32_t valuesize, int32_t id)
@@ -1426,23 +1442,55 @@ int32_t cli_bcapi_map_setvalue(struct cli_bc_ctx *ctx, const uint8_t *value, int
struct cli_map *s = get_hashtab(ctx, id);
if (!s)
return -1;
- return cli_map_setvalue(s, value, valuesize);
+ return cli_map_setvalue(s, value, valuesize) == CL_SUCCESS ? 0 : -1;
}
int32_t cli_bcapi_map_remove(struct cli_bc_ctx *ctx, const uint8_t *key, int32_t keysize, int32_t id)
{
+ cl_error_t ret;
struct cli_map *s = get_hashtab(ctx, id);
if (!s)
return -1;
- return cli_map_removekey(s, key, keysize);
+
+ ret = cli_map_removekey(s, key, keysize);
+ switch (ret) {
+ case CL_SUCCESS: {
+ // found and removed
+ return 1;
+ }
+ case CL_EUNLINK: {
+ // not found
+ return 0;
+ }
+ default: {
+ // error occurred
+ return -1;
+ }
+ }
}
int32_t cli_bcapi_map_find(struct cli_bc_ctx *ctx, const uint8_t *key, int32_t keysize, int32_t id)
{
+ cl_error_t ret;
struct cli_map *s = get_hashtab(ctx, id);
if (!s)
return -1;
- return cli_map_find(s, key, keysize);
+
+ ret = cli_map_find(s, key, keysize);
+ switch (ret) {
+ case CL_SUCCESS: {
+ // found
+ return 1;
+ }
+ case CL_EACCES: {
+ // not found
+ return 0;
+ }
+ default: {
+ // error occurred
+ return -1;
+ }
+ }
}
int32_t cli_bcapi_map_getvaluesize(struct cli_bc_ctx *ctx, int32_t id)
@@ -1460,7 +1508,7 @@ uint8_t *cli_bcapi_map_getvalue(struct cli_bc_ctx *ctx, int32_t id, int32_t valu
return NULL;
if (cli_map_getvalue_size(s) != valuesize)
return NULL;
- return cli_map_getvalue(s);
+ return (uint8_t *)cli_map_getvalue(s);
}
int32_t cli_bcapi_map_done(struct cli_bc_ctx *ctx, int32_t id)
@@ -1851,20 +1899,6 @@ uint32_t cli_bcapi_check_platform(struct cli_bc_ctx *ctx, uint32_t a, uint32_t b
return ret;
}
-int cli_bytecode_context_setpdf(struct cli_bc_ctx *ctx, unsigned phase,
- unsigned nobjs,
- struct pdf_obj **objs, uint32_t *pdf_flags,
- uint32_t pdfsize, uint32_t pdfstartoff)
-{
- ctx->pdf_nobjs = nobjs;
- ctx->pdf_objs = objs;
- ctx->pdf_flags = pdf_flags;
- ctx->pdf_size = pdfsize;
- ctx->pdf_startoff = pdfstartoff;
- ctx->pdf_phase = phase;
- return 0;
-}
-
int32_t cli_bcapi_pdf_get_obj_num(struct cli_bc_ctx *ctx)
{
if (!ctx->pdf_phase)
diff --git a/libclamav/bytecode_detect.c b/libclamav/bytecode_detect.c
index 867280d8dd..503bce629d 100644
--- a/libclamav/bytecode_detect.c
+++ b/libclamav/bytecode_detect.c
@@ -41,7 +41,7 @@
#define CHECK_ARCH(a) \
if (!strcmp(TARGET_ARCH_TYPE, #a)) env->arch = arch_##a
-extern int have_clamjit();
+extern bool have_clamjit();
static void cli_print_environment(struct cli_environment *env)
{
diff --git a/libclamav/bytecode_nojit.c b/libclamav/bytecode_nojit.c
index 0e836c919f..733b00636f 100644
--- a/libclamav/bytecode_nojit.c
+++ b/libclamav/bytecode_nojit.c
@@ -29,7 +29,7 @@
#include "clamav.h"
#include "others.h"
-int cli_bytecode_prepare_jit(struct cli_all_bc *bcs)
+cl_error_t cli_bytecode_prepare_jit(struct cli_all_bc *bcs)
{
unsigned i;
for (i = 0; i < bcs->count; i++) {
@@ -45,7 +45,7 @@ int cli_bytecode_prepare_jit(struct cli_all_bc *bcs)
return CL_EBYTECODE;
}
-int cli_vm_execute_jit(const struct cli_all_bc *bcs, struct cli_bc_ctx *ctx, const struct cli_bc_func *func)
+cl_error_t cli_vm_execute_jit(const struct cli_all_bc *bcs, struct cli_bc_ctx *ctx, const struct cli_bc_func *func)
{
UNUSEDPARAM(bcs);
UNUSEDPARAM(ctx);
@@ -53,14 +53,14 @@ int cli_vm_execute_jit(const struct cli_all_bc *bcs, struct cli_bc_ctx *ctx, con
return CL_EBYTECODE;
}
-int cli_bytecode_init_jit(struct cli_all_bc *allbc, unsigned dconfmask)
+cl_error_t cli_bytecode_init_jit(struct cli_all_bc *allbc, unsigned dconfmask)
{
UNUSEDPARAM(allbc);
UNUSEDPARAM(dconfmask);
return CL_SUCCESS;
}
-int cli_bytecode_done_jit(struct cli_all_bc *allbc, int partial)
+cl_error_t cli_bytecode_done_jit(struct cli_all_bc *allbc, int partial)
{
UNUSEDPARAM(allbc);
UNUSEDPARAM(partial);
@@ -74,9 +74,9 @@ void cli_bytecode_debug(int argc, char **argv)
UNUSEDPARAM(argv);
}
-int bytecode_init(void)
+cl_error_t bytecode_init(void)
{
- return 0;
+ return CL_SUCCESS;
}
void cli_bytecode_debug_printsrc(const struct cli_bc_ctx *ctx)
@@ -88,9 +88,9 @@ void cli_bytecode_printversion(void)
{
printf("LLVM is not compiled or not linked\n");
}
-int have_clamjit()
+bool have_clamjit()
{
- return 0;
+ return false;
}
void cli_printcxxver()
diff --git a/libclamav/bytecode_priv.h b/libclamav/bytecode_priv.h
index c683403b77..6144ea97da 100644
--- a/libclamav/bytecode_priv.h
+++ b/libclamav/bytecode_priv.h
@@ -242,16 +242,16 @@ struct cli_bc_ctx {
#endif
};
struct cli_all_bc;
-int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct cli_bc_func *func, const struct cli_bc_inst *inst);
+cl_error_t cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct cli_bc_func *func, const struct cli_bc_inst *inst);
#ifdef __cplusplus
extern "C" {
#endif
-int cli_vm_execute_jit(const struct cli_all_bc *bcs, struct cli_bc_ctx *ctx, const struct cli_bc_func *func);
-int cli_bytecode_prepare_jit(struct cli_all_bc *bc);
-int cli_bytecode_init_jit(struct cli_all_bc *bc, unsigned dconfmask);
-int cli_bytecode_done_jit(struct cli_all_bc *bc, int partial);
+cl_error_t cli_vm_execute_jit(const struct cli_all_bc *bcs, struct cli_bc_ctx *ctx, const struct cli_bc_func *func);
+cl_error_t cli_bytecode_prepare_jit(struct cli_all_bc *bc);
+cl_error_t cli_bytecode_init_jit(struct cli_all_bc *bc, unsigned dconfmask);
+cl_error_t cli_bytecode_done_jit(struct cli_all_bc *bc, int partial);
#ifdef __cplusplus
}
diff --git a/libclamav/bytecode_vm.c b/libclamav/bytecode_vm.c
index 74953c852b..adc594f459 100644
--- a/libclamav/bytecode_vm.c
+++ b/libclamav/bytecode_vm.c
@@ -685,7 +685,7 @@ static struct {
{(void *)cli_bcapi_get_pe_section, sizeof(struct cli_exe_section)},
};
-int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct cli_bc_func *func, const struct cli_bc_inst *inst)
+cl_error_t cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct cli_bc_func *func, const struct cli_bc_inst *inst)
{
size_t i;
uint32_t j;
@@ -1162,7 +1162,7 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
READ64(ptr, inst->u.three[1]);
off += (ptr & 0x00000000ffffffffULL);
iptr = (ptr & 0xffffffff00000000ULL) + (uint64_t)(off);
- WRITE64(inst->dest, ptr + off);
+ WRITE64(inst->dest, iptr);
}
break;
}
@@ -1275,8 +1275,8 @@ int cli_vm_execute(const struct cli_bc *bc, struct cli_bc_ctx *ctx, const struct
} else {
READ64(ptr, inst->u.three[1]);
off *= inst->u.three[0];
- off += (ptr & 0x00000000ffffffff);
- iptr = (ptr & 0xffffffff00000000) + (uint64_t)(off);
+ off += (ptr & 0x00000000ffffffffULL);
+ iptr = (ptr & 0xffffffff00000000ULL) + (uint64_t)(off);
WRITE64(inst->dest, iptr);
}
break;
diff --git a/libclamav/c++/bytecode2llvm.cpp b/libclamav/c++/bytecode2llvm.cpp
index 8bd76cf45f..a8929c6d54 100644
--- a/libclamav/c++/bytecode2llvm.cpp
+++ b/libclamav/c++/bytecode2llvm.cpp
@@ -214,9 +214,9 @@ void cli_warnmsg(const char *str, ...);
#endif
#ifdef __GNUC__
-inline void cli_dbgmsg(const char *str, ...) __attribute__((format(printf, 1, 2)));
+void cli_dbgmsg_no_inline(const char *str, ...) __attribute__((format(printf, 1, 2)));
#else
-inline void cli_dbgmsg(const char *str, ...);
+void cli_dbgmsg_no_inline(const char *str, ...);
#endif
}
@@ -398,9 +398,9 @@ class NotifyListener : public JITEventListener
{
if (!cli_debug_flag)
return;
- cli_dbgmsg("[Bytecode JIT]; emitted %s %s of %zd bytes\n",
- Obj.getFileFormatName().str().c_str(),
- Obj.getFileName().str().c_str(), Obj.getData().size());
+ cli_dbgmsg_no_inline("[Bytecode JIT]; emitted %s %s of %zd bytes\n",
+ Obj.getFileFormatName().str().c_str(),
+ Obj.getFileName().str().c_str(), Obj.getData().size());
}
};
@@ -878,7 +878,7 @@ class LLVMCodegen
V->print(ostr);
Ty->print(ostr);
// M->dump();
- cli_dbgmsg("[Bytecode JIT]: operand %d: %s\n", operand, ostr.str().c_str());
+ cli_dbgmsg_no_inline("[Bytecode JIT]: operand %d: %s\n", operand, ostr.str().c_str());
}
llvm_report_error("(libclamav) Type mismatch converting operand");
}
@@ -1051,7 +1051,7 @@ class LLVMCodegen
Value *V = createGEP(Base, ETy, ARef);
if (!V) {
if (cli_debug_flag) {
- cli_dbgmsg("[Bytecode JIT] @%d\n", dest);
+ cli_dbgmsg_no_inline("[Bytecode JIT] @%d\n", dest);
}
return false;
}
@@ -1261,7 +1261,7 @@ class LLVMCodegen
raw_string_ostream ostr(str);
ostr << i << ":" << g << ":" << bc->globals[i][0] << "\n";
Ty->print(ostr);
- cli_dbgmsg("[Bytecode JIT]: %s\n", ostr.str().c_str());
+ cli_dbgmsg_no_inline("[Bytecode JIT]: %s\n", ostr.str().c_str());
}
llvm_report_error("(libclamav) unable to create fake global");
}
@@ -1688,7 +1688,7 @@ class LLVMCodegen
std::string str;
raw_string_ostream ostr(str);
F->print(ostr);
- cli_dbgmsg("[Bytecode JIT]: %s\n", ostr.str().c_str());
+ cli_dbgmsg_no_inline("[Bytecode JIT]: %s\n", ostr.str().c_str());
}
}
}
@@ -1920,7 +1920,7 @@ static void *bytecode_watchdog(void *arg)
char err[128];
pthread_mutex_lock(&watchdog_mutex);
if (cli_debug_flag)
- cli_dbgmsg("bytecode watchdog is running\n");
+ cli_dbgmsg_no_inline("bytecode watchdog is running\n");
do {
struct watchdog_item *item;
gettimeofday(&tv, NULL);
@@ -1966,7 +1966,7 @@ static void *bytecode_watchdog(void *arg)
} while (1);
watchdog_running = 0;
if (cli_debug_flag)
- cli_dbgmsg("bytecode watchdog quiting\n");
+ cli_dbgmsg_no_inline("bytecode watchdog quiting\n");
pthread_mutex_unlock(&watchdog_mutex);
return NULL;
}
@@ -2038,7 +2038,7 @@ static int watchdog_arm(struct watchdog_item *item, int ms, volatile uint8_t *ti
return rc;
}
-static int bytecode_execute(intptr_t code, struct cli_bc_ctx *ctx)
+static cl_error_t bytecode_execute(intptr_t code, struct cli_bc_ctx *ctx)
{
ScopedExceptionHandler handler;
// execute;
@@ -2047,17 +2047,17 @@ static int bytecode_execute(intptr_t code, struct cli_bc_ctx *ctx)
// setup exception handler to longjmp back here
uint32_t result = ((uint32_t(*)(struct cli_bc_ctx *))(intptr_t)code)(ctx);
*(uint32_t *)ctx->values = result;
- return 0;
+ return CL_SUCCESS;
}
HANDLER_END(handler);
cli_warnmsg("[%s]: JITed code intercepted runtime error!\n", MODULE);
return CL_EBYTECODE;
}
-int cli_vm_execute_jit(const struct cli_all_bc *bcs, struct cli_bc_ctx *ctx,
- const struct cli_bc_func *func)
+cl_error_t cli_vm_execute_jit(const struct cli_all_bc *bcs, struct cli_bc_ctx *ctx,
+ const struct cli_bc_func *func)
{
- int ret;
+ cl_error_t ret;
struct timeval tv0, tv1;
struct watchdog_item witem;
// no locks needed here, since LLVM automatically acquires a JIT lock
@@ -2091,7 +2091,7 @@ int cli_vm_execute_jit(const struct cli_all_bc *bcs, struct cli_bc_ctx *ctx,
tv1.tv_sec -= tv0.tv_sec;
tv1.tv_usec -= tv0.tv_usec;
diff = tv1.tv_sec * 1000000 + tv1.tv_usec;
- cli_dbgmsg("bytecode finished in %ld us\n", diff);
+ cli_dbgmsg_no_inline("bytecode finished in %ld us\n", diff);
}
return ctx->timeout ? CL_ETIMEOUT : ret;
} // namespace
@@ -2117,7 +2117,7 @@ static void addFPasses(legacy::FunctionPassManager &FPM, bool trusted, Module *M
FPM.add(createDeadCodeEliminationPass());
}
-int cli_bytecode_prepare_jit(struct cli_all_bc *bcs)
+cl_error_t cli_bytecode_prepare_jit(struct cli_all_bc *bcs)
{
if (!bcs->engine)
return CL_EBYTECODE;
@@ -2313,14 +2313,14 @@ int cli_bytecode_prepare_jit(struct cli_all_bc *bcs)
cli_errmsg("[Bytecode JIT]: Unexpected unknown exception occurred\n");
return CL_EBYTECODE;
}
- return 0;
+ return CL_SUCCESS;
}
HANDLER_END(handler);
cli_errmsg("[Bytecode JIT] *** FATAL error encountered during bytecode generation\n");
return CL_EBYTECODE;
}
-int bytecode_init(void)
+cl_error_t bytecode_init(void)
{
if (!LLVMIsMultithreaded()) {
cli_warnmsg("[%s] bytecode_init: LLVM is compiled without multithreading support\n", MODULE);
@@ -2352,11 +2352,11 @@ int bytecode_init(void)
"should be built for i686, not i386!\n";
cli_warnmsg("[%s] %s", MODULE, warnmsg);
}
- return 0;
+ return CL_SUCCESS;
}
// Called once when loading a new set of BC files
-int cli_bytecode_init_jit(struct cli_all_bc *bcs, unsigned dconfmask)
+cl_error_t cli_bytecode_init_jit(struct cli_all_bc *bcs, unsigned dconfmask)
{
LLVMApiScopedLock scopedLock;
bcs->engine = new (std::nothrow) cli_bcengine;
@@ -2364,10 +2364,10 @@ int cli_bytecode_init_jit(struct cli_all_bc *bcs, unsigned dconfmask)
return CL_EMEM;
bcs->engine->EE = 0;
bcs->engine->Listener = 0;
- return 0;
+ return CL_SUCCESS;
}
-int cli_bytecode_done_jit(struct cli_all_bc *bcs, int partial)
+cl_error_t cli_bytecode_done_jit(struct cli_all_bc *bcs, int partial)
{
LLVMApiScopedLock scopedLock;
if (bcs->engine) {
@@ -2384,7 +2384,7 @@ int cli_bytecode_done_jit(struct cli_all_bc *bcs, int partial)
bcs->engine = 0;
}
}
- return 0;
+ return CL_SUCCESS;
}
void cli_bytecode_debug(int argc, char **argv)
@@ -2460,9 +2460,9 @@ void cli_bytecode_debug_printsrc(const struct cli_bc_ctx *ctx)
assert(ctx->line < lines->linev.size());
}
-int have_clamjit()
+bool have_clamjit()
{
- return 1;
+ return true;
}
void cli_bytecode_printversion()
diff --git a/libclamav/cache.c b/libclamav/cache.c
index 8acb39a842..9c5c66b10f 100644
--- a/libclamav/cache.c
+++ b/libclamav/cache.c
@@ -645,10 +645,9 @@ void clean_cache_add(unsigned char *md5, size_t size, cli_ctx *ctx)
return;
}
- if (!SCAN_COLLECT_METADATA) {
+ if (SCAN_COLLECT_METADATA) {
// Don't cache when using the "collect metadata" feature.
- // TODO: This used to be checked in scanners.c before calling clean_cache_add()
- // I'm not sure this is actually the right call.
+ // We don't cache the JSON, so we can't reproduce it when the cache is positive.
cli_dbgmsg("clean_cache_add: collect metadata feature enabled, skipping cache\n");
return;
}
@@ -736,6 +735,13 @@ cl_error_t clean_cache_check(unsigned char *md5, size_t size, cli_ctx *ctx)
if (!ctx || !ctx->engine || !ctx->engine->cache)
return CL_VIRUS;
+ if (SCAN_COLLECT_METADATA) {
+ // Don't cache when using the "collect metadata" feature.
+ // We don't cache the JSON, so we can't reproduce it when the cache is positive.
+ cli_dbgmsg("clean_cache_check: collect metadata feature enabled, skipping cache\n");
+ return CL_VIRUS;
+ }
+
if (ctx->engine->engine_options & ENGINE_OPTIONS_DISABLE_CACHE) {
cli_dbgmsg("clean_cache_check: Caching disabled. Returning CL_VIRUS.\n");
return CL_VIRUS;
diff --git a/libclamav/clamav.h b/libclamav/clamav.h
index d4be07ea50..9bbb752186 100644
--- a/libclamav/clamav.h
+++ b/libclamav/clamav.h
@@ -1147,7 +1147,7 @@ extern const char *cl_retver(void);
/* ----------------------------------------------------------------------------
* Others.
*/
-extern const char *cl_strerror(int clerror);
+extern const char *cl_strerror(cl_error_t clerror);
/* ----------------------------------------------------------------------------
* Custom data scanning.
diff --git a/libclamav/cpio.c b/libclamav/cpio.c
index 3c6cd2af8b..3aff8230ec 100644
--- a/libclamav/cpio.c
+++ b/libclamav/cpio.c
@@ -95,24 +95,24 @@ static void sanitname(char *name)
}
}
-int cli_scancpio_old(cli_ctx *ctx)
+cl_error_t cli_scancpio_old(cli_ctx *ctx)
{
+ cl_error_t status = CL_SUCCESS;
struct cpio_hdr_old hdr_old;
char *fmap_name = NULL;
char name[513];
unsigned int file = 0, trailer = 0;
uint32_t filesize, namesize, hdr_namesize;
- int ret = CL_CLEAN, conv;
- size_t pos = 0;
- int virus_found = 0;
+ int conv;
+ size_t pos = 0;
memset(name, 0, sizeof(name));
while (fmap_readn(ctx->fmap, &hdr_old, pos, sizeof(hdr_old)) == sizeof(hdr_old)) {
pos += sizeof(hdr_old);
if (!hdr_old.magic && trailer) {
- ret = CL_SUCCESS;
- goto leave;
+ status = CL_SUCCESS;
+ goto done;
}
if (hdr_old.magic == 070707) {
@@ -121,8 +121,8 @@ int cli_scancpio_old(cli_ctx *ctx)
conv = 1;
} else {
cli_dbgmsg("cli_scancpio_old: Invalid magic number\n");
- ret = CL_EFORMAT;
- goto leave;
+ status = CL_EFORMAT;
+ goto done;
}
cli_dbgmsg("CPIO: -- File %u --\n", ++file);
@@ -132,21 +132,25 @@ int cli_scancpio_old(cli_ctx *ctx)
namesize = MIN(sizeof(name), hdr_namesize);
if (fmap_readn(ctx->fmap, &name, pos, namesize) != namesize) {
cli_dbgmsg("cli_scancpio_old: Can't read file name\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
pos += namesize;
name[namesize - 1] = 0;
sanitname(name);
cli_dbgmsg("CPIO: Name: %s\n", name);
- if (!strcmp(name, "TRAILER!!!"))
+ if (!strcmp(name, "TRAILER!!!")) {
trailer = 1;
+ }
if (namesize < hdr_namesize) {
- if (hdr_namesize % 2)
+ if (hdr_namesize % 2) {
hdr_namesize++;
+ }
pos += hdr_namesize - namesize;
- } else if (hdr_namesize % 2)
+ } else if (hdr_namesize % 2) {
pos++;
+ }
fmap_name = name;
}
@@ -155,60 +159,53 @@ int cli_scancpio_old(cli_ctx *ctx)
if (!filesize)
continue;
- if (cli_matchmeta(ctx, name, filesize, filesize, 0, file, 0, NULL) == CL_VIRUS) {
- if (!SCAN_ALLMATCHES)
- return CL_VIRUS;
- virus_found = 1;
+ status = cli_matchmeta(ctx, name, filesize, filesize, 0, file, 0, NULL);
+ if (status != CL_SUCCESS) {
+ goto done;
}
if ((EC16(hdr_old.mode, conv) & 0170000) != 0100000) {
cli_dbgmsg("CPIO: Not a regular file, skipping\n");
} else {
- ret = cli_checklimits("cli_scancpio_old", ctx, filesize, 0, 0);
- if (ret == CL_EMAXFILES) {
- goto leave;
- } else if (ret == CL_SUCCESS) {
- ret = cli_magic_scan_nested_fmap_type(ctx->fmap, pos, filesize, ctx, CL_TYPE_ANY, fmap_name);
- if (ret == CL_VIRUS) {
- if (!SCAN_ALLMATCHES)
- return ret;
- virus_found = 1;
- }
+ status = cli_magic_scan_nested_fmap_type(ctx->fmap, pos, filesize, ctx, CL_TYPE_ANY, fmap_name);
+ if (status != CL_SUCCESS) {
+ goto done;
}
}
- if (filesize % 2)
+ if (filesize % 2) {
filesize++;
+ }
pos += filesize;
}
-leave:
- if (virus_found != 0)
- return CL_VIRUS;
- return ret;
+done:
+
+ return status;
}
-int cli_scancpio_odc(cli_ctx *ctx)
+cl_error_t cli_scancpio_odc(cli_ctx *ctx)
{
+ cl_error_t status = CL_SUCCESS;
struct cpio_hdr_odc hdr_odc;
char name[513] = {0}, buff[12] = {0};
unsigned int file = 0, trailer = 0;
uint32_t filesize = 0, namesize = 0, hdr_namesize = 0;
- int ret = CL_CLEAN;
- size_t pos = 0;
- int virus_found = 0;
+ size_t pos = 0;
memset(&hdr_odc, 0, sizeof(hdr_odc));
while (fmap_readn(ctx->fmap, &hdr_odc, pos, sizeof(hdr_odc)) == sizeof(hdr_odc)) {
pos += sizeof(hdr_odc);
- if (!hdr_odc.magic[0] && trailer)
- goto leave;
+ if (!hdr_odc.magic[0] && trailer) {
+ status = CL_SUCCESS;
+ goto done;
+ }
if (strncmp(hdr_odc.magic, "070707", 6)) {
cli_dbgmsg("cli_scancpio_odc: Invalid magic string\n");
- ret = CL_EFORMAT;
- goto leave;
+ status = CL_EFORMAT;
+ goto done;
}
cli_dbgmsg("CPIO: -- File %u --\n", ++file);
@@ -217,86 +214,81 @@ int cli_scancpio_odc(cli_ctx *ctx)
buff[6] = 0;
if (sscanf(buff, "%o", &hdr_namesize) != 1) {
cli_dbgmsg("cli_scancpio_odc: Can't convert name size\n");
- ret = CL_EFORMAT;
- goto leave;
+ status = CL_EFORMAT;
+ goto done;
}
if (hdr_namesize) {
namesize = MIN(sizeof(name), hdr_namesize);
if (fmap_readn(ctx->fmap, &name, pos, namesize) != namesize) {
cli_dbgmsg("cli_scancpio_odc: Can't read file name\n");
- ret = CL_EFORMAT;
- goto leave;
+ status = CL_EFORMAT;
+ goto done;
}
pos += namesize;
name[namesize - 1] = 0;
sanitname(name);
cli_dbgmsg("CPIO: Name: %s\n", name);
- if (!strcmp(name, "TRAILER!!!"))
+ if (!strcmp(name, "TRAILER!!!")) {
trailer = 1;
+ }
- if (namesize < hdr_namesize)
+ if (namesize < hdr_namesize) {
pos += hdr_namesize - namesize;
+ }
}
strncpy(buff, hdr_odc.filesize, 11);
buff[11] = 0;
if (sscanf(buff, "%o", &filesize) != 1) {
cli_dbgmsg("cli_scancpio_odc: Can't convert file size\n");
- ret = CL_EFORMAT;
- goto leave;
+ status = CL_EFORMAT;
+ goto done;
}
cli_dbgmsg("CPIO: Filesize: %u\n", filesize);
- if (!filesize)
+ if (!filesize) {
continue;
+ }
- if (cli_matchmeta(ctx, name, filesize, filesize, 0, file, 0, NULL) == CL_VIRUS) {
- if (!SCAN_ALLMATCHES)
- return CL_VIRUS;
- virus_found = 1;
+ status = cli_matchmeta(ctx, name, filesize, filesize, 0, file, 0, NULL);
+ if (status == CL_VIRUS) {
+ goto done;
}
- ret = cli_checklimits("cli_scancpio_odc", ctx, filesize, 0, 0);
- if (ret == CL_EMAXFILES) {
- goto leave;
- } else if (ret == CL_SUCCESS) {
- ret = cli_magic_scan_nested_fmap_type(ctx->fmap, pos, filesize, ctx, CL_TYPE_ANY, name);
- if (ret == CL_VIRUS) {
- if (!SCAN_ALLMATCHES)
- return ret;
- virus_found = 1;
- }
+ status = cli_magic_scan_nested_fmap_type(ctx->fmap, pos, filesize, ctx, CL_TYPE_ANY, name);
+ if (status != CL_SUCCESS) {
+ goto done;
}
pos += filesize;
}
-leave:
- if (virus_found != 0)
- return CL_VIRUS;
- return ret;
+done:
+
+ return status;
}
-int cli_scancpio_newc(cli_ctx *ctx, int crc)
+cl_error_t cli_scancpio_newc(cli_ctx *ctx, int crc)
{
+ cl_error_t status = CL_SUCCESS;
struct cpio_hdr_newc hdr_newc;
char name[513], buff[9];
unsigned int file = 0, trailer = 0;
uint32_t filesize, namesize, hdr_namesize, pad;
- int ret = CL_CLEAN;
- size_t pos = 0;
- int virus_found = 0;
+ size_t pos = 0;
memset(name, 0, 513);
while (fmap_readn(ctx->fmap, &hdr_newc, pos, sizeof(hdr_newc)) == sizeof(hdr_newc)) {
pos += sizeof(hdr_newc);
- if (!hdr_newc.magic[0] && trailer)
- goto leave;
+ if (!hdr_newc.magic[0] && trailer) {
+ status = CL_SUCCESS;
+ goto done;
+ }
if ((!crc && strncmp(hdr_newc.magic, "070701", 6)) || (crc && strncmp(hdr_newc.magic, "070702", 6))) {
cli_dbgmsg("cli_scancpio_newc: Invalid magic string\n");
- ret = CL_EFORMAT;
- goto leave;
+ status = CL_EFORMAT;
+ goto done;
}
cli_dbgmsg("CPIO: -- File %u --\n", ++file);
@@ -305,69 +297,65 @@ int cli_scancpio_newc(cli_ctx *ctx, int crc)
buff[8] = 0;
if (sscanf(buff, "%x", &hdr_namesize) != 1) {
cli_dbgmsg("cli_scancpio_newc: Can't convert name size\n");
- ret = CL_EFORMAT;
- goto leave;
+ status = CL_EFORMAT;
+ goto done;
}
if (hdr_namesize) {
namesize = MIN(sizeof(name), hdr_namesize);
if (fmap_readn(ctx->fmap, &name, pos, namesize) != namesize) {
cli_dbgmsg("cli_scancpio_newc: Can't read file name\n");
- ret = CL_EFORMAT;
- goto leave;
+ status = CL_EFORMAT;
+ goto done;
}
pos += namesize;
name[namesize - 1] = 0;
sanitname(name);
cli_dbgmsg("CPIO: Name: %s\n", name);
- if (!strcmp(name, "TRAILER!!!"))
+ if (!strcmp(name, "TRAILER!!!")) {
trailer = 1;
+ }
pad = (4 - (sizeof(hdr_newc) + hdr_namesize) % 4) % 4;
if (namesize < hdr_namesize) {
- if (pad)
+ if (pad) {
hdr_namesize += pad;
+ }
pos += hdr_namesize - namesize;
- } else if (pad)
+ } else if (pad) {
pos += pad;
+ }
}
strncpy(buff, hdr_newc.filesize, 8);
buff[8] = 0;
if (sscanf(buff, "%x", &filesize) != 1) {
cli_dbgmsg("cli_scancpio_newc: Can't convert file size\n");
- ret = CL_EFORMAT;
- goto leave;
+ status = CL_EFORMAT;
+ goto done;
}
cli_dbgmsg("CPIO: Filesize: %u\n", filesize);
- if (!filesize)
+ if (!filesize) {
continue;
+ }
- if (cli_matchmeta(ctx, name, filesize, filesize, 0, file, 0, NULL) == CL_VIRUS) {
- if (!SCAN_ALLMATCHES)
- return CL_VIRUS;
- virus_found = 1;
+ status = cli_matchmeta(ctx, name, filesize, filesize, 0, file, 0, NULL);
+ if (status == CL_VIRUS) {
+ goto done;
}
- ret = cli_checklimits("cli_scancpio_newc", ctx, filesize, 0, 0);
- if (ret == CL_EMAXFILES) {
- goto leave;
- } else if (ret == CL_SUCCESS) {
- ret = cli_magic_scan_nested_fmap_type(ctx->fmap, pos, filesize, ctx, CL_TYPE_ANY, name);
- if (ret == CL_VIRUS) {
- if (!SCAN_ALLMATCHES)
- return ret;
- virus_found = 1;
- }
+ status = cli_magic_scan_nested_fmap_type(ctx->fmap, pos, filesize, ctx, CL_TYPE_ANY, name);
+ if (status != CL_SUCCESS) {
+ goto done;
}
- if ((pad = filesize % 4))
+ if ((pad = filesize % 4)) {
filesize += (4 - pad);
+ }
pos += filesize;
}
-leave:
- if (virus_found != 0)
- return CL_VIRUS;
- return ret;
+done:
+
+ return status;
}
diff --git a/libclamav/cpio.h b/libclamav/cpio.h
index c211e34521..0e77a7b3f8 100644
--- a/libclamav/cpio.h
+++ b/libclamav/cpio.h
@@ -22,10 +22,11 @@
#ifndef __CPIO_H
#define __CPIO_H
+#include "clamav.h"
#include "others.h"
-int cli_scancpio_old(cli_ctx *ctx);
-int cli_scancpio_odc(cli_ctx *ctx);
-int cli_scancpio_newc(cli_ctx *ctx, int crc);
+cl_error_t cli_scancpio_old(cli_ctx *ctx);
+cl_error_t cli_scancpio_odc(cli_ctx *ctx);
+cl_error_t cli_scancpio_newc(cli_ctx *ctx, int crc);
#endif
diff --git a/libclamav/elf.c b/libclamav/elf.c
index f9ead2c0dd..d40e1c500e 100644
--- a/libclamav/elf.c
+++ b/libclamav/elf.c
@@ -99,8 +99,8 @@ static uint64_t cli_rawaddr64(uint64_t vaddr, struct elf_program_hdr64 *ph, uint
}
/* Return converted endian-fixed header, or error code */
-static int cli_elf_fileheader(cli_ctx *ctx, fmap_t *map, union elf_file_hdr *file_hdr,
- uint8_t *do_convert, uint8_t *is64)
+static cl_error_t cli_elf_fileheader(cli_ctx *ctx, fmap_t *map, union elf_file_hdr *file_hdr,
+ uint8_t *do_convert, uint8_t *is64)
{
uint8_t format64, conv;
@@ -127,7 +127,7 @@ static int cli_elf_fileheader(cli_ctx *ctx, fmap_t *map, union elf_file_hdr *fil
break;
default:
cli_dbgmsg("ELF: Unknown ELF class (%u)\n", file_hdr->hdr64.e_ident[4]);
- if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_virus(ctx, "Heuristics.Broken.Executable"))) {
+ if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Executable"))) {
return CL_VIRUS;
}
return CL_BREAK;
@@ -220,7 +220,7 @@ static int cli_elf_ph32(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
cli_dbgmsg("ELF: Number of program headers: %d\n", phnum);
if (phnum > 128) {
cli_dbgmsg("ELF: Suspicious number of program headers\n");
- if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_virus(ctx, "Heuristics.Broken.Executable"))) {
+ if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Executable"))) {
return CL_VIRUS;
}
return CL_EFORMAT;
@@ -232,7 +232,7 @@ static int cli_elf_ph32(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
/* Sanity check */
if (phentsize != sizeof(struct elf_program_hdr32)) {
cli_dbgmsg("ELF: phentsize != sizeof(struct elf_program_hdr32)\n");
- if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_virus(ctx, "Heuristics.Broken.Executable"))) {
+ if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Executable"))) {
return CL_VIRUS;
}
return CL_EFORMAT;
@@ -266,7 +266,7 @@ static int cli_elf_ph32(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
cli_dbgmsg("ELF: Possibly broken ELF file\n");
}
free(program_hdr);
- if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_virus(ctx, "Heuristics.Broken.Executable"))) {
+ if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Executable"))) {
return CL_VIRUS;
}
return CL_BREAK;
@@ -287,7 +287,7 @@ static int cli_elf_ph32(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
free(program_hdr);
if (err) {
cli_dbgmsg("ELF: Can't calculate file offset of entry point\n");
- if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_virus(ctx, "Heuristics.Broken.Executable"))) {
+ if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Executable"))) {
return CL_VIRUS;
}
return CL_EFORMAT;
@@ -306,8 +306,8 @@ static int cli_elf_ph32(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
}
/* Read 64-bit program headers */
-static int cli_elf_ph64(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
- struct elf_file_hdr64 *file_hdr, uint8_t conv)
+static cl_error_t cli_elf_ph64(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
+ struct elf_file_hdr64 *file_hdr, uint8_t conv)
{
struct elf_program_hdr64 *program_hdr = NULL;
uint16_t phnum, phentsize;
@@ -320,7 +320,7 @@ static int cli_elf_ph64(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
cli_dbgmsg("ELF: Number of program headers: %d\n", phnum);
if (phnum > 128) {
cli_dbgmsg("ELF: Suspicious number of program headers\n");
- if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_virus(ctx, "Heuristics.Broken.Executable"))) {
+ if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Executable"))) {
return CL_VIRUS;
}
return CL_EFORMAT;
@@ -332,7 +332,7 @@ static int cli_elf_ph64(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
/* Sanity check */
if (phentsize != sizeof(struct elf_program_hdr64)) {
cli_dbgmsg("ELF: phentsize != sizeof(struct elf_program_hdr64)\n");
- if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_virus(ctx, "Heuristics.Broken.Executable"))) {
+ if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Executable"))) {
return CL_VIRUS;
}
return CL_EFORMAT;
@@ -366,7 +366,7 @@ static int cli_elf_ph64(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
cli_dbgmsg("ELF: Possibly broken ELF file\n");
}
free(program_hdr);
- if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_virus(ctx, "Heuristics.Broken.Executable"))) {
+ if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Executable"))) {
return CL_VIRUS;
}
return CL_BREAK;
@@ -387,7 +387,7 @@ static int cli_elf_ph64(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
free(program_hdr);
if (err) {
cli_dbgmsg("ELF: Can't calculate file offset of entry point\n");
- if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_virus(ctx, "Heuristics.Broken.Executable"))) {
+ if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Executable"))) {
return CL_VIRUS;
}
return CL_EFORMAT;
@@ -430,7 +430,7 @@ static int cli_elf_sh32(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
/* Sanity check */
if (shentsize != sizeof(struct elf_section_hdr32)) {
cli_dbgmsg("ELF: shentsize != sizeof(struct elf_section_hdr32)\n");
- if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_virus(ctx, "Heuristics.Broken.Executable"))) {
+ if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Executable"))) {
return CL_VIRUS;
}
return CL_EFORMAT;
@@ -473,7 +473,7 @@ static int cli_elf_sh32(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
cli_dbgmsg("ELF: Possibly broken ELF file\n");
}
free(section_hdr);
- if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_virus(ctx, "Heuristics.Broken.Executable"))) {
+ if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Executable"))) {
return CL_VIRUS;
}
return CL_BREAK;
@@ -529,7 +529,7 @@ static int cli_elf_sh64(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
/* Sanity check */
if (shentsize != sizeof(struct elf_section_hdr64)) {
cli_dbgmsg("ELF: shentsize != sizeof(struct elf_section_hdr64)\n");
- if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_virus(ctx, "Heuristics.Broken.Executable"))) {
+ if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Executable"))) {
return CL_VIRUS;
}
return CL_EFORMAT;
@@ -572,7 +572,7 @@ static int cli_elf_sh64(cli_ctx *ctx, fmap_t *map, struct cli_exe_info *elfinfo,
cli_dbgmsg("ELF: Possibly broken ELF file\n");
}
free(section_hdr);
- if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_virus(ctx, "Heuristics.Broken.Executable"))) {
+ if (ctx && SCAN_HEURISTIC_BROKEN && (CL_VIRUS == cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Executable"))) {
return CL_VIRUS;
}
return CL_BREAK;
@@ -672,11 +672,11 @@ static void cli_elf_sectionlog(uint32_t sh_type, uint32_t sh_flags)
}
/* Scan function for ELF */
-int cli_scanelf(cli_ctx *ctx)
+cl_error_t cli_scanelf(cli_ctx *ctx)
{
union elf_file_hdr file_hdr;
fmap_t *map = ctx->fmap;
- int ret;
+ cl_error_t ret;
uint8_t conv = 0, is64 = 0;
cli_dbgmsg("in cli_scanelf\n");
@@ -791,11 +791,11 @@ int cli_scanelf(cli_ctx *ctx)
/* ELF header parsing only
* Returns 0 on success, -1 on error
*/
-int cli_elfheader(cli_ctx *ctx, struct cli_exe_info *elfinfo)
+cl_error_t cli_elfheader(cli_ctx *ctx, struct cli_exe_info *elfinfo)
{
union elf_file_hdr file_hdr;
uint8_t conv = 0, is64 = 0;
- int ret;
+ cl_error_t ret = CL_SUCCESS;
cli_dbgmsg("in cli_elfheader\n");
@@ -806,8 +806,8 @@ int cli_elfheader(cli_ctx *ctx, struct cli_exe_info *elfinfo)
}
ret = cli_elf_fileheader(NULL, ctx->fmap, &file_hdr, &conv, &is64);
- if (ret != CL_CLEAN) {
- return -1;
+ if (ret != CL_SUCCESS) {
+ goto done;
}
/* Program headers and Entry */
@@ -816,8 +816,8 @@ int cli_elfheader(cli_ctx *ctx, struct cli_exe_info *elfinfo)
} else {
ret = cli_elf_ph32(NULL, ctx->fmap, elfinfo, &(file_hdr.hdr32.hdr), conv);
}
- if (ret != CL_CLEAN) {
- return -1;
+ if (ret != CL_SUCCESS) {
+ goto done;
}
/* Section Headers */
@@ -826,65 +826,68 @@ int cli_elfheader(cli_ctx *ctx, struct cli_exe_info *elfinfo)
} else {
ret = cli_elf_sh32(NULL, ctx->fmap, elfinfo, &(file_hdr.hdr32.hdr), conv);
}
- if (ret != CL_CLEAN) {
- return -1;
+ if (ret != CL_SUCCESS) {
+ goto done;
}
- return 0;
+done:
+
+ return ret;
}
/*
* ELF file unpacking.
*/
-int cli_unpackelf(cli_ctx *ctx)
+cl_error_t cli_unpackelf(cli_ctx *ctx)
{
- char *tempfile;
- int ndesc;
+ cl_error_t ret = CL_SUCCESS;
+ char *tempfile = NULL;
+ int ndesc = -1;
struct cli_bc_ctx *bc_ctx;
- int ret;
- fmap_t *map = ctx->fmap;
+ bool bc_ctx_set = false;
/* Bytecode BC_ELF_UNPACKER hook */
bc_ctx = cli_bytecode_context_alloc();
if (!bc_ctx) {
cli_errmsg("cli_scanelf: can't allocate memory for bc_ctx\n");
- return CL_EMEM;
+ ret = CL_EMEM;
+ goto done;
}
cli_bytecode_context_setctx(bc_ctx, ctx);
+ bc_ctx_set = true;
cli_dbgmsg("Running bytecode hook\n");
- ret = cli_bytecode_runhook(ctx, ctx->engine, bc_ctx, BC_ELF_UNPACKER, map);
+ ret = cli_bytecode_runhook(ctx, ctx->engine, bc_ctx, BC_ELF_UNPACKER, ctx->fmap);
cli_dbgmsg("Finished running bytecode hook\n");
- switch (ret) {
- case CL_VIRUS:
- cli_bytecode_context_destroy(bc_ctx);
- return CL_VIRUS;
- case CL_SUCCESS:
- ndesc = cli_bytecode_context_getresult_file(bc_ctx, &tempfile);
- cli_bytecode_context_destroy(bc_ctx);
- if (ndesc != -1 && tempfile) {
- if (ctx->engine->keeptmp)
- cli_dbgmsg("cli_scanelf: Unpacked and rebuilt executable saved in %s\n", tempfile);
- else
- cli_dbgmsg("cli_scanelf: Unpacked and rebuilt executable\n");
- lseek(ndesc, 0, SEEK_SET);
- cli_dbgmsg("***** Scanning rebuilt ELF file *****\n");
- if (cli_magic_scan_desc(ndesc, tempfile, ctx, NULL) == CL_VIRUS) {
- close(ndesc);
- CLI_TMPUNLK();
- free(tempfile);
- return CL_VIRUS;
- }
- close(ndesc);
- CLI_TMPUNLK();
- free(tempfile);
- return CL_CLEAN;
- }
- break;
- default:
- cli_bytecode_context_destroy(bc_ctx);
+ if (CL_SUCCESS == ret) {
+ // check for unpacked/rebuilt executable
+ ndesc = cli_bytecode_context_getresult_file(bc_ctx, &tempfile);
+ if (ndesc != -1 && tempfile) {
+ cli_dbgmsg("cli_scanelf: Unpacked and rebuilt ELF executable saved in %s\n", tempfile);
+
+ lseek(ndesc, 0, SEEK_SET);
+
+ cli_dbgmsg("***** Scanning rebuilt ELF file *****\n");
+ ret = cli_magic_scan_desc(ndesc, tempfile, ctx, NULL);
+ }
}
- return CL_CLEAN;
+done:
+ // cli_bytecode_context_getresult_file() gives up ownership of temp file, so we must clean it up.
+ if (-1 != ndesc) {
+ close(ndesc);
+ }
+ if (NULL != tempfile) {
+ if (!ctx->engine->keeptmp) {
+ (void)cli_unlink(tempfile);
+ }
+ free(tempfile);
+ }
+
+ if (bc_ctx_set) {
+ cli_bytecode_context_destroy(bc_ctx);
+ }
+
+ return ret;
}
diff --git a/libclamav/elf.h b/libclamav/elf.h
index 93eb46008b..6cb8267e09 100644
--- a/libclamav/elf.h
+++ b/libclamav/elf.h
@@ -143,10 +143,10 @@ struct elf_section_hdr64 {
/* Exposed functions */
-int cli_scanelf(cli_ctx *ctx);
+cl_error_t cli_scanelf(cli_ctx *ctx);
-int cli_elfheader(cli_ctx *ctx, struct cli_exe_info *elfinfo);
+cl_error_t cli_elfheader(cli_ctx *ctx, struct cli_exe_info *elfinfo);
-int cli_unpackelf(cli_ctx *ctx);
+cl_error_t cli_unpackelf(cli_ctx *ctx);
#endif
diff --git a/libclamav/filetypes.c b/libclamav/filetypes.c
index c218cdeee0..14bc6089e8 100644
--- a/libclamav/filetypes.c
+++ b/libclamav/filetypes.c
@@ -277,7 +277,8 @@ cli_file_t cli_determine_fmap_type(fmap_t *map, const struct cl_engine *engine,
unsigned char buffer[MAGIC_BUFFER_SIZE];
const unsigned char *buff;
unsigned char *decoded;
- int bread, sret;
+ int bread;
+ cli_file_t sret;
cli_file_t ret = CL_TYPE_BINARY_DATA;
struct cli_matcher *root;
struct cli_ac_data mdata;
diff --git a/libclamav/fmap.c b/libclamav/fmap.c
index a026709460..37c2cdf781 100644
--- a/libclamav/fmap.c
+++ b/libclamav/fmap.c
@@ -285,9 +285,11 @@ fmap_t *fmap_duplicate(cl_fmap_t *map, size_t offset, size_t length, const char
}
/* This also means the hash will be different.
- * Clear the have_maphash flag.
+ * Clear the have_ flags.
* It will be calculated when next it is needed. */
- duplicate_map->have_maphash = false;
+ duplicate_map->have_md5 = false;
+ duplicate_map->have_sha1 = false;
+ duplicate_map->have_sha256 = false;
}
if (NULL != name) {
@@ -429,7 +431,9 @@ extern cl_fmap_t *cl_fmap_open_handle(void *handle, size_t offset, size_t len,
m->gets = handle_gets;
m->unneed_off = handle_unneed_off;
m->handle_is_fd = 1;
- m->have_maphash = false;
+ m->have_md5 = false;
+ m->have_sha1 = false;
+ m->have_sha256 = false;
status = CL_SUCCESS;
@@ -1082,7 +1086,45 @@ extern void cl_fmap_close(cl_fmap_t *map)
funmap(map);
}
-cl_error_t fmap_get_MD5(fmap_t *map, unsigned char **hash)
+cl_error_t fmap_set_hash(fmap_t *map, unsigned char *hash, cli_hash_type_t type)
+{
+ cl_error_t status = CL_SUCCESS;
+
+ if (NULL == map) {
+ cli_errmsg("fmap_set_hash: Attempted to set hash for NULL fmap\n");
+ status = CL_EARG;
+ goto done;
+ }
+ if (NULL == hash) {
+ cli_errmsg("fmap_set_hash: Attempted to set hash to NULL\n");
+ status = CL_EARG;
+ goto done;
+ }
+
+ switch (type) {
+ case CLI_HASH_MD5:
+ memcpy(map->md5, hash, CLI_HASHLEN_MD5);
+ map->have_md5 = true;
+ break;
+ case CLI_HASH_SHA1:
+ memcpy(map->sha1, hash, CLI_HASHLEN_SHA1);
+ map->have_sha1 = true;
+ break;
+ case CLI_HASH_SHA256:
+ memcpy(map->sha256, hash, CLI_HASHLEN_SHA256);
+ map->have_sha256 = true;
+ break;
+ default:
+ cli_errmsg("fmap_set_hash: Unsupported hash type %u\n", type);
+ status = CL_EARG;
+ goto done;
+ }
+
+done:
+ return status;
+}
+
+cl_error_t fmap_get_hash(fmap_t *map, unsigned char **hash, cli_hash_type_t type)
{
cl_error_t status = CL_ERROR;
size_t todo, at = 0;
@@ -1090,41 +1132,109 @@ cl_error_t fmap_get_MD5(fmap_t *map, unsigned char **hash)
todo = map->len;
- if (!map->have_maphash) {
- /* Need to calculate the hash */
- hashctx = cl_hash_init("md5");
- if (!(hashctx)) {
- cli_errmsg("fmap_get_MD5: error initializing new md5 hash!\n");
+ switch (type) {
+ case CLI_HASH_MD5:
+ if (map->have_md5) {
+ goto complete;
+ }
+ break;
+ case CLI_HASH_SHA1:
+ if (map->have_md5) {
+ goto complete;
+ }
+ break;
+ case CLI_HASH_SHA256:
+ if (map->have_md5) {
+ goto complete;
+ }
+ break;
+ default:
+ cli_errmsg("fmap_get_hash: Unsupported hash type %u\n", type);
+ status = CL_EARG;
goto done;
- }
+ }
- while (todo) {
- const void *buf;
- size_t readme = todo < 1024 * 1024 * 10 ? todo : 1024 * 1024 * 10;
+ /*
+ * Need to calculate the hash.
+ */
- if (!(buf = fmap_need_off_once(map, at, readme))) {
- cli_errmsg("fmap_get_MD5: error reading while generating hash!\n");
- status = CL_EREAD;
- goto done;
- }
+ switch (type) {
+ case CLI_HASH_MD5:
+ hashctx = cl_hash_init("md5");
+ break;
+ case CLI_HASH_SHA1:
+ hashctx = cl_hash_init("sha1");
+ break;
+ case CLI_HASH_SHA256:
+ hashctx = cl_hash_init("sha256");
+ break;
+ default:
+ cli_errmsg("fmap_get_hash: Unsupported hash type %u\n", type);
+ status = CL_EARG;
+ goto done;
+ }
+ if (!(hashctx)) {
+ cli_errmsg("fmap_get_hash: error initializing new md5 hash!\n");
+ goto done;
+ }
- todo -= readme;
- at += readme;
+ while (todo) {
+ const void *buf;
+ size_t readme = todo < 1024 * 1024 * 10 ? todo : 1024 * 1024 * 10;
- if (cl_update_hash(hashctx, (void *)buf, readme)) {
- cli_errmsg("fmap_get_MD5: error calculating hash!\n");
- status = CL_EREAD;
- goto done;
- }
+ if (!(buf = fmap_need_off_once(map, at, readme))) {
+ cli_errmsg("fmap_get_hash: error reading while generating hash!\n");
+ status = CL_EREAD;
+ goto done;
}
- cl_finish_hash(hashctx, map->maphash);
- hashctx = NULL;
+ todo -= readme;
+ at += readme;
- map->have_maphash = true;
+ if (cl_update_hash(hashctx, (void *)buf, readme)) {
+ cli_errmsg("fmap_get_hash: error calculating hash!\n");
+ status = CL_EREAD;
+ goto done;
+ }
}
- *hash = map->maphash;
+ switch (type) {
+ case CLI_HASH_MD5:
+ cl_finish_hash(hashctx, map->md5);
+ map->have_md5 = true;
+ break;
+ case CLI_HASH_SHA1:
+ cl_finish_hash(hashctx, map->sha1);
+ map->have_sha1 = true;
+ break;
+ case CLI_HASH_SHA256:
+ cl_finish_hash(hashctx, map->sha256);
+ map->have_sha256 = true;
+ break;
+ default:
+ cli_errmsg("fmap_get_hash: Unsupported hash type %u\n", type);
+ status = CL_EARG;
+ goto done;
+ }
+ hashctx = NULL;
+
+complete:
+
+ switch (type) {
+ case CLI_HASH_MD5:
+ *hash = map->md5;
+ break;
+ case CLI_HASH_SHA1:
+ *hash = map->sha1;
+ break;
+ case CLI_HASH_SHA256:
+ *hash = map->sha256;
+ break;
+ default:
+ cli_errmsg("fmap_get_hash: Unsupported hash type %u\n", type);
+ status = CL_EARG;
+ goto done;
+ }
status = CL_SUCCESS;
diff --git a/libclamav/fmap.h b/libclamav/fmap.h
index 6699bed89e..4d1e374cd6 100644
--- a/libclamav/fmap.h
+++ b/libclamav/fmap.h
@@ -37,6 +37,8 @@
#include "clamav.h"
+#include "matcher-hash-types.h"
+
struct cl_fmap;
typedef cl_fmap_t fmap_t;
@@ -84,8 +86,12 @@ struct cl_fmap {
HANDLE fh;
HANDLE mh;
#endif
- bool have_maphash;
- unsigned char maphash[16];
+ bool have_md5;
+ unsigned char md5[CLI_HASHLEN_MD5];
+ bool have_sha1;
+ unsigned char sha1[CLI_HASHLEN_SHA1];
+ bool have_sha256;
+ unsigned char sha256[CLI_HASHLEN_SHA256];
uint64_t *bitmap;
char *name;
};
@@ -426,14 +432,25 @@ cl_error_t fmap_dump_to_file(fmap_t *map, const char *filepath, const char *tmpd
int fmap_fd(fmap_t *m);
/**
- * @brief Get a pointer to the fmap hash.
+ * @brief Get a pointer to the fmap hash.
*
* Will calculate the hash if not already previously calculated.
*
* @param map The map in question.
* @param[out] hash A pointer to the hash.
+ * @param type The type of hash to calculate.
* @return cl_error_t CL_SUCCESS if was able to get the hash, else some error.
*/
-cl_error_t fmap_get_MD5(fmap_t *map, unsigned char **hash);
+cl_error_t fmap_get_hash(fmap_t *map, unsigned char **hash, cli_hash_type_t type);
+
+/**
+ * @brief Set the hash for the fmap that was previously calculated.
+ *
+ * @param map The map in question.
+ * @param hash The hash to set.
+ * @param type The type of hash to calculate.
+ * @return cl_error_t CL_SUCCESS if was able to set the hash, else some error.
+ */
+cl_error_t fmap_set_hash(fmap_t *map, unsigned char *hash, cli_hash_type_t type);
#endif
diff --git a/libclamav/gif.c b/libclamav/gif.c
index 6f0d1e86d2..d6c539cc2d 100644
--- a/libclamav/gif.c
+++ b/libclamav/gif.c
@@ -163,6 +163,7 @@ struct gif_image_descriptor {
cl_error_t cli_parsegif(cli_ctx *ctx)
{
cl_error_t status = CL_SUCCESS;
+ bool parse_error = false;
fmap_t *map = NULL;
size_t offset = 0;
@@ -210,8 +211,8 @@ cl_error_t cli_parsegif(cli_ctx *ctx)
*/
if (fmap_readn(map, &screen_desc, offset, sizeof(screen_desc)) != sizeof(screen_desc)) {
cli_errmsg("GIF: Can't read logical screen description, file truncated?\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.GIF.TruncatedScreenDescriptor");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.GIF.TruncatedScreenDescriptor");
+ parse_error = true;
goto scan_overlay;
}
offset += sizeof(screen_desc);
@@ -226,8 +227,8 @@ cl_error_t cli_parsegif(cli_ctx *ctx)
if (offset + (size_t)global_color_table_size > map->len) {
cli_errmsg("GIF: EOF in the middle of the global color table, file truncated?\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.GIF.TruncatedGlobalColorTable");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.GIF.TruncatedGlobalColorTable");
+ parse_error = true;
goto scan_overlay;
}
offset += global_color_table_size;
@@ -248,9 +249,9 @@ cl_error_t cli_parsegif(cli_ctx *ctx)
cli_dbgmsg("GIF: Missing GIF trailer, slightly (but acceptably) malformed.\n");
} else {
cli_errmsg("GIF: Can't read block label, EOF before image data. File truncated?\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.GIF.MissingImageData");
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.GIF.MissingImageData");
}
- status = CL_EPARSE;
+ parse_error = true;
goto scan_overlay;
}
offset += sizeof(block_label);
@@ -270,8 +271,8 @@ cl_error_t cli_parsegif(cli_ctx *ctx)
if (fmap_readn(map, &extension_label, offset, sizeof(extension_label)) != sizeof(extension_label)) {
cli_errmsg("GIF: Failed to read the extension block label, file truncated?\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.GIF.TruncatedExtension");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.GIF.TruncatedExtension");
+ parse_error = true;
goto scan_overlay;
}
offset += sizeof(extension_label);
@@ -304,8 +305,8 @@ cl_error_t cli_parsegif(cli_ctx *ctx)
uint8_t extension_block_size = 0;
if (fmap_readn(map, &extension_block_size, offset, sizeof(extension_block_size)) != sizeof(extension_block_size)) {
cli_errmsg("GIF: EOF while attempting to read the block size for an extension, file truncated?\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.GIF.TruncatedExtension");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.GIF.TruncatedExtension");
+ parse_error = true;
goto scan_overlay;
} else {
offset += sizeof(extension_block_size);
@@ -319,8 +320,8 @@ cl_error_t cli_parsegif(cli_ctx *ctx)
if (offset + (size_t)extension_block_size > map->len) {
cli_errmsg("GIF: EOF in the middle of a graphic control extension sub-block, file truncated?\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.GIF.TruncatedExtensionSubBlock");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.GIF.TruncatedExtensionSubBlock");
+ parse_error = true;
goto scan_overlay;
}
offset += extension_block_size;
@@ -335,8 +336,8 @@ cl_error_t cli_parsegif(cli_ctx *ctx)
cli_dbgmsg("GIF: Found an image descriptor.\n");
if (fmap_readn(map, &image_desc, offset, sizeof(image_desc)) != sizeof(image_desc)) {
cli_errmsg("GIF: Can't read image descriptor, file truncated?\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.GIF.TruncatedImageDescriptor");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.GIF.TruncatedImageDescriptor");
+ parse_error = true;
goto scan_overlay;
} else {
offset += sizeof(image_desc);
@@ -368,8 +369,8 @@ cl_error_t cli_parsegif(cli_ctx *ctx)
uint8_t image_data_block_size = 0;
if (fmap_readn(map, &image_data_block_size, offset, sizeof(image_data_block_size)) != sizeof(image_data_block_size)) {
cli_errmsg("GIF: EOF while attempting to read the block size for an image data block, file truncated?\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.GIF.TruncatedImageDataBlock");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.GIF.TruncatedImageDataBlock");
+ parse_error = true;
goto scan_overlay;
} else {
offset += sizeof(image_data_block_size);
@@ -383,8 +384,8 @@ cl_error_t cli_parsegif(cli_ctx *ctx)
if (offset + (size_t)image_data_block_size > map->len) {
cli_errmsg("GIF: EOF in the middle of an image data sub-block, file truncated?\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.GIF.TruncatedImageDataBlock");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.GIF.TruncatedImageDataBlock");
+ parse_error = true;
goto scan_overlay;
}
offset += image_data_block_size;
@@ -395,29 +396,29 @@ cl_error_t cli_parsegif(cli_ctx *ctx)
default: {
// An unknown code: break.
cli_errmsg("GIF: Found an unfamiliar block label: 0x%x\n", block_label);
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.GIF.UnknownBlockLabel");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.GIF.UnknownBlockLabel");
+ parse_error = true;
goto scan_overlay;
}
}
}
scan_overlay:
- if (status == CL_EPARSE) {
- /* We added with cli_append_potentially_unwanted so it will alert at the end if nothing else matches. */
- status = CL_CLEAN;
- // Some recovery (I saw some "GIF89a;" or things like this)
- if (offset == (strlen("GIF89a") + sizeof(screen_desc) + 1)) {
- offset = strlen("GIF89a");
+ if (CL_SUCCESS == status) {
+ if (parse_error) {
+ // Some recovery (I saw some "GIF89a;" or things like this)
+ if (offset == (strlen("GIF89a") + sizeof(screen_desc) + 1)) {
+ offset = strlen("GIF89a");
+ }
}
- }
- // Is there an overlay?
- if (offset < map->len) {
- cli_dbgmsg("GIF: Found extra data after the end of the GIF data stream: %zu bytes, we'll scan it!\n", map->len - offset);
- cl_error_t nested_scan_result = cli_magic_scan_nested_fmap_type(map, offset, map->len - offset, ctx, CL_TYPE_ANY, NULL);
- status = nested_scan_result != CL_SUCCESS ? nested_scan_result : status;
+ // Is there an overlay?
+ if (offset < map->len) {
+ cli_dbgmsg("GIF: Found extra data after the end of the GIF data stream: %zu bytes, we'll scan it!\n", map->len - offset);
+ status = cli_magic_scan_nested_fmap_type(map, offset, map->len - offset, ctx, CL_TYPE_ANY, NULL);
+ goto done;
+ }
}
done:
diff --git a/libclamav/gpt.c b/libclamav/gpt.c
index 0540d26b08..d1ae7c256a 100644
--- a/libclamav/gpt.c
+++ b/libclamav/gpt.c
@@ -63,12 +63,12 @@ enum GPT_SCANSTATE {
BOTH
};
-static int gpt_scan_partitions(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize);
-static int gpt_validate_header(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize);
-static int gpt_check_mbr(cli_ctx *ctx, size_t sectorsize);
+static cl_error_t gpt_scan_partitions(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize);
+static cl_error_t gpt_validate_header(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize);
+static cl_error_t gpt_check_mbr(cli_ctx *ctx, size_t sectorsize);
static void gpt_printSectors(cli_ctx *ctx, size_t sectorsize);
static void gpt_printGUID(uint8_t GUID[], const char *msg);
-static int gpt_partition_intersection(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize);
+static cl_error_t gpt_partition_intersection(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize);
/* returns 0 on failing to detect sectorsize */
size_t gpt_detect_size(fmap_t *map)
@@ -99,11 +99,11 @@ size_t gpt_detect_size(fmap_t *map)
}
/* attempts to detect sector size is input as 0 */
-int cli_scangpt(cli_ctx *ctx, size_t sectorsize)
+cl_error_t cli_scangpt(cli_ctx *ctx, size_t sectorsize)
{
+ cl_error_t status = CL_SUCCESS;
struct gpt_header phdr, shdr;
enum GPT_SCANSTATE state = INVALID;
- int ret = CL_CLEAN, detection = CL_CLEAN;
size_t maplen;
off_t pos = 0;
@@ -111,7 +111,8 @@ int cli_scangpt(cli_ctx *ctx, size_t sectorsize)
if (!ctx || !ctx->fmap) {
cli_errmsg("cli_scangpt: Invalid context\n");
- return CL_ENULLARG;
+ status = CL_ENULLARG;
+ goto done;
}
/* sector size calculation */
@@ -121,7 +122,8 @@ int cli_scangpt(cli_ctx *ctx, size_t sectorsize)
}
if (sectorsize == 0) {
cli_dbgmsg("cli_scangpt: could not determine sector size\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* size of total file must be a multiple of the sector size */
@@ -129,16 +131,14 @@ int cli_scangpt(cli_ctx *ctx, size_t sectorsize)
if ((maplen % sectorsize) != 0) {
cli_dbgmsg("cli_scangpt: File sized %lu is not a multiple of sector size %lu\n",
(unsigned long)maplen, (unsigned long)sectorsize);
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* check the protective mbr */
- ret = gpt_check_mbr(ctx, sectorsize);
- if (ret != CL_CLEAN) {
- if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
- detection = CL_VIRUS;
- else
- return ret;
+ status = gpt_check_mbr(ctx, sectorsize);
+ if (status != CL_SUCCESS) {
+ goto done;
}
pos = GPT_PRIMARY_HDR_LBA * sectorsize; /* sector 1 (second sector) is the primary gpt header */
@@ -147,7 +147,8 @@ int cli_scangpt(cli_ctx *ctx, size_t sectorsize)
cli_dbgmsg("cli_scangpt: Using primary GPT header\n");
if (fmap_readn(ctx->fmap, &phdr, pos, sizeof(phdr)) != sizeof(phdr)) {
cli_dbgmsg("cli_scangpt: Invalid primary GPT header\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
pos = maplen - sectorsize; /* last sector is the secondary gpt header */
@@ -161,13 +162,15 @@ int cli_scangpt(cli_ctx *ctx, size_t sectorsize)
/* read secondary gpt header */
if (fmap_readn(ctx->fmap, &shdr, pos, sizeof(shdr)) != sizeof(shdr)) {
cli_dbgmsg("cli_scangpt: Invalid secondary GPT header\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
if (gpt_validate_header(ctx, shdr, sectorsize)) {
cli_dbgmsg("cli_scangpt: Secondary GPT header is invalid\n");
cli_dbgmsg("cli_scangpt: Disk is unusable\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
} else {
cli_dbgmsg("cli_scangpt: Checking secondary GPT header\n");
@@ -194,19 +197,13 @@ int cli_scangpt(cli_ctx *ctx, size_t sectorsize)
/* check that the partition table has no intersections - HEURISTICS */
if (SCAN_HEURISTIC_PARTITION_INTXN && (ctx->dconf->other & OTHER_CONF_PRTNINTXN)) {
- ret = gpt_partition_intersection(ctx, phdr, sectorsize);
- if (ret != CL_CLEAN) {
- if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
- detection = CL_VIRUS;
- else
- return ret;
+ status = gpt_partition_intersection(ctx, phdr, sectorsize);
+ if (status != CL_SUCCESS) {
+ goto done;
}
- ret = gpt_partition_intersection(ctx, shdr, sectorsize);
- if (ret != CL_CLEAN) {
- if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
- detection = CL_VIRUS;
- else
- return ret;
+ status = gpt_partition_intersection(ctx, shdr, sectorsize);
+ if (status != CL_SUCCESS) {
+ goto done;
}
}
@@ -214,58 +211,51 @@ int cli_scangpt(cli_ctx *ctx, size_t sectorsize)
switch (state) {
case PRIMARY_ONLY:
cli_dbgmsg("cli_scangpt: Scanning primary GPT partitions only\n");
- ret = gpt_scan_partitions(ctx, phdr, sectorsize);
- if (ret != CL_CLEAN) {
- if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
- detection = CL_VIRUS;
- else
- return ret;
+ status = gpt_scan_partitions(ctx, phdr, sectorsize);
+ if (status != CL_SUCCESS) {
+ goto done;
}
break;
case SECONDARY_ONLY:
cli_dbgmsg("cli_scangpt: Scanning secondary GPT partitions only\n");
- ret = gpt_scan_partitions(ctx, shdr, sectorsize);
- if (ret != CL_CLEAN) {
- if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
- detection = CL_VIRUS;
- else
- return ret;
+ status = gpt_scan_partitions(ctx, shdr, sectorsize);
+ if (status != CL_SUCCESS) {
+ goto done;
}
break;
case BOTH:
cli_dbgmsg("cli_scangpt: Scanning primary GPT partitions\n");
- ret = gpt_scan_partitions(ctx, phdr, sectorsize);
- if (ret != CL_CLEAN) {
- if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
- detection = CL_VIRUS;
- else
- return ret;
+ status = gpt_scan_partitions(ctx, phdr, sectorsize);
+ if (status != CL_SUCCESS) {
+ goto done;
}
cli_dbgmsg("cli_scangpt: Scanning secondary GPT partitions\n");
- ret = gpt_scan_partitions(ctx, shdr, sectorsize);
- if (ret != CL_CLEAN) {
- if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
- detection = CL_VIRUS;
- else
- return ret;
+ status = gpt_scan_partitions(ctx, shdr, sectorsize);
+ if (status != CL_SUCCESS) {
+ goto done;
}
break;
default:
cli_dbgmsg("cli_scangpt: State is invalid\n");
}
- return detection;
+ status = CL_SUCCESS;
+
+done:
+ return status;
}
-static int gpt_scan_partitions(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize)
+static cl_error_t gpt_scan_partitions(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize)
{
+ cl_error_t status = CL_SUCCESS;
struct gpt_partition_entry gpe;
- int ret = CL_CLEAN, detection = CL_CLEAN;
- size_t maplen, part_size = 0;
+ size_t maplen, part_size = 0;
size_t pos = 0, part_off = 0;
unsigned i = 0, j = 0;
uint32_t max_prtns = 0;
+ char *namestr = NULL;
+
/* convert endian to host */
hdr.signature = be64_to_host(hdr.signature);
hdr.revision = be32_to_host(hdr.revision);
@@ -304,7 +294,8 @@ static int gpt_scan_partitions(cli_ctx *ctx, struct gpt_header hdr, size_t secto
/* read in partition entry */
if (fmap_readn(ctx->fmap, &gpe, pos, sizeof(gpe)) != sizeof(gpe)) {
cli_dbgmsg("cli_scangpt: Invalid GPT partition entry\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* convert the endian to host */
@@ -327,9 +318,8 @@ static int gpt_scan_partitions(cli_ctx *ctx, struct gpt_header hdr, size_t secto
} else if (((gpe.lastLBA + 1) * sectorsize) > maplen) {
/* partition exists outside bounds of the file map */
} else {
- char *namestr = NULL;
-
namestr = (char *)cli_utf16toascii((char *)gpe.name, 72);
+ // It's okay if namestr is NULL.
/* print partition entry data for debug */
cli_dbgmsg("GPT Partition Entry %u:\n", i);
@@ -344,15 +334,14 @@ static int gpt_scan_partitions(cli_ctx *ctx, struct gpt_header hdr, size_t secto
/* send the partition to cli_magic_scan_nested_fmap_type */
part_off = gpe.firstLBA * sectorsize;
part_size = (gpe.lastLBA - gpe.firstLBA + 1) * sectorsize;
- ret = cli_magic_scan_nested_fmap_type(ctx->fmap, part_off, part_size, ctx, CL_TYPE_PART_ANY, namestr);
+ status = cli_magic_scan_nested_fmap_type(ctx->fmap, part_off, part_size, ctx, CL_TYPE_PART_ANY, namestr);
+ if (status != CL_SUCCESS) {
+ goto done;
+ }
+
if (NULL != namestr) {
free(namestr);
- }
- if (ret != CL_CLEAN) {
- if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
- detection = CL_VIRUS;
- else
- return ret;
+ namestr = NULL;
}
}
@@ -364,11 +353,17 @@ static int gpt_scan_partitions(cli_ctx *ctx, struct gpt_header hdr, size_t secto
cli_dbgmsg("cli_scangpt: max partitions reached\n");
}
- return detection;
+done:
+
+ if (NULL != namestr) {
+ free(namestr);
+ }
+ return status;
}
-static int gpt_validate_header(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize)
+static cl_error_t gpt_validate_header(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize)
{
+ cl_error_t status = CL_SUCCESS;
uint32_t crc32_calc, crc32_ref;
uint64_t tableLastLBA, lastLBA;
size_t maplen, ptable_start, ptable_len;
@@ -383,7 +378,8 @@ static int gpt_validate_header(cli_ctx *ctx, struct gpt_header hdr, size_t secto
if (crc32_calc != crc32_ref) {
cli_dbgmsg("cli_scangpt: GPT header checksum mismatch\n");
gpt_parsemsg("%x != %x\n", crc32_calc, crc32_ref);
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* convert endian to host to check partition table */
@@ -413,56 +409,66 @@ static int gpt_validate_header(cli_ctx *ctx, struct gpt_header hdr, size_t secto
if (hdr.signature != GPT_SIGNATURE) {
cli_dbgmsg("cli_scangpt: Invalid GPT header signature %llx\n",
(long long unsigned)hdr.signature);
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* check header size */
if (hdr.headerSize != sizeof(hdr)) {
cli_dbgmsg("cli_scangpt: GPT header size does not match stated size\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* check reserved value == 0 */
if (hdr.reserved != GPT_HDR_RESERVED) {
cli_dbgmsg("cli_scangpt: GPT header reserved is not expected value\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* check that sectors are in a valid configuration */
if (!((hdr.currentLBA == GPT_PRIMARY_HDR_LBA && hdr.backupLBA == lastLBA) ||
(hdr.currentLBA == lastLBA && hdr.backupLBA == GPT_PRIMARY_HDR_LBA))) {
cli_dbgmsg("cli_scangpt: GPT secondary header is not last LBA\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
if (hdr.firstUsableLBA > hdr.lastUsableLBA) {
cli_dbgmsg("cli_scangpt: GPT first usable sectors is after last usable sector\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
if (hdr.firstUsableLBA <= GPT_PRIMARY_HDR_LBA || hdr.lastUsableLBA >= lastLBA) {
cli_dbgmsg("cli_scangpt: GPT usable sectors intersects header sector\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
if ((hdr.tableStartLBA <= hdr.firstUsableLBA && tableLastLBA >= hdr.firstUsableLBA) ||
(hdr.tableStartLBA >= hdr.firstUsableLBA && hdr.tableStartLBA <= hdr.lastUsableLBA)) {
cli_dbgmsg("cli_scangpt: GPT usable sectors intersects partition table\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
if (hdr.tableStartLBA <= GPT_PRIMARY_HDR_LBA || tableLastLBA >= lastLBA) {
cli_dbgmsg("cli_scangpt: GPT partition table intersects header sector\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* check that valid table entry size */
if (hdr.tableEntrySize != sizeof(struct gpt_partition_entry)) {
cli_dbgmsg("cli_scangpt: cannot parse gpt with partition entry sized %u\n",
hdr.tableEntrySize);
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* check valid table */
if ((ptable_start + ptable_len) > maplen) {
cli_dbgmsg("cli_scangpt: GPT partition table extends over fmap limit\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/** END HEADER CHECKS **/
@@ -473,17 +479,20 @@ static int gpt_validate_header(cli_ctx *ctx, struct gpt_header hdr, size_t secto
if (crc32_calc != hdr.tableCRC32) {
cli_dbgmsg("cli_scangpt: GPT partition table checksum mismatch\n");
gpt_parsemsg("%x != %x\n", crc32_calc, hdr.tableCRC32);
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
- return CL_SUCCESS;
+done:
+
+ return status;
}
-static int gpt_check_mbr(cli_ctx *ctx, size_t sectorsize)
+static cl_error_t gpt_check_mbr(cli_ctx *ctx, size_t sectorsize)
{
+ cl_error_t status = CL_SUCCESS;
struct mbr_boot_record pmbr;
size_t pos = 0, mbr_base = 0;
- int ret = CL_CLEAN;
unsigned i = 0;
/* read the mbr */
@@ -492,7 +501,8 @@ static int gpt_check_mbr(cli_ctx *ctx, size_t sectorsize)
if (fmap_readn(ctx->fmap, &pmbr, pos, sizeof(pmbr)) != sizeof(pmbr)) {
cli_dbgmsg("cli_scangpt: Invalid primary MBR header\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* convert mbr */
@@ -526,7 +536,9 @@ static int gpt_check_mbr(cli_ctx *ctx, size_t sectorsize)
/* check if the MBR and GPT partitions align - heuristic */
/* scan the MBR partitions - additional scans */
- return ret;
+done:
+
+ return status;
}
static void gpt_printSectors(cli_ctx *ctx, size_t sectorsize)
@@ -580,16 +592,16 @@ static void gpt_printGUID(uint8_t GUID[], const char *msg)
GUID[8], GUID[9], GUID[10], GUID[11], GUID[12], GUID[13], GUID[14], GUID[15]);
}
-static int gpt_partition_intersection(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize)
+static cl_error_t gpt_partition_intersection(cli_ctx *ctx, struct gpt_header hdr, size_t sectorsize)
{
+ cl_error_t status = CL_SUCCESS;
+ cl_error_t ret;
partition_intersection_list_t prtncheck;
struct gpt_partition_entry gpe;
unsigned i, pitxn;
- int ret = CL_CLEAN, tmp = CL_CLEAN;
size_t pos;
size_t maplen;
uint32_t max_prtns = 0;
- int virus_found = 0;
maplen = ctx->fmap->len;
@@ -611,8 +623,8 @@ static int gpt_partition_intersection(cli_ctx *ctx, struct gpt_header hdr, size_
/* read in partition entry */
if (fmap_readn(ctx->fmap, &gpe, pos, sizeof(gpe)) != sizeof(gpe)) {
cli_dbgmsg("cli_scangpt: Invalid GPT partition entry\n");
- partition_intersection_list_free(&prtncheck);
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* convert the endian to host */
@@ -627,22 +639,19 @@ static int gpt_partition_intersection(cli_ctx *ctx, struct gpt_header hdr, size_
} else if (((gpe.lastLBA + 1) * sectorsize) > maplen) {
/* partition exists outside bounds of the file map */
} else {
- tmp = partition_intersection_list_check(&prtncheck, &pitxn, gpe.firstLBA, gpe.lastLBA - gpe.firstLBA + 1);
- if (tmp != CL_CLEAN) {
- if (tmp == CL_VIRUS) {
+ ret = partition_intersection_list_check(&prtncheck, &pitxn, gpe.firstLBA, gpe.lastLBA - gpe.firstLBA + 1);
+ if (ret != CL_SUCCESS) {
+ if (ret == CL_VIRUS) {
cli_dbgmsg("cli_scangpt: detected intersection with partitions "
"[%u, %u]\n",
pitxn, i);
- ret = cli_append_virus(ctx, PRTN_INTXN_DETECTION);
- if (ret == CL_VIRUS)
- virus_found = 1;
- if (SCAN_ALLMATCHES || ret == CL_CLEAN)
- tmp = 0;
- else
- goto leave;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.GPTPartitionIntersection");
+ if (status != CL_SUCCESS) {
+ goto done;
+ }
} else {
- ret = tmp;
- goto leave;
+ status = ret;
+ goto done;
}
}
}
@@ -651,9 +660,8 @@ static int gpt_partition_intersection(cli_ctx *ctx, struct gpt_header hdr, size_
pos += hdr.tableEntrySize;
}
-leave:
+done:
+
partition_intersection_list_free(&prtncheck);
- if (virus_found)
- return CL_VIRUS;
- return ret;
+ return status;
}
diff --git a/libclamav/gpt.h b/libclamav/gpt.h
index 39ab124aff..bc79c26ae7 100644
--- a/libclamav/gpt.h
+++ b/libclamav/gpt.h
@@ -25,7 +25,7 @@
#include "clamav-config.h"
#endif
-#include "clamav-types.h"
+#include "clamav.h"
#include "others.h"
/* GPT sector size is normally 512 bytes be can be set to much larger
@@ -94,6 +94,6 @@ struct gpt_partition_entry {
#endif
size_t gpt_detect_size(fmap_t *map);
-int cli_scangpt(cli_ctx *ctx, size_t sectorsize);
+cl_error_t cli_scangpt(cli_ctx *ctx, size_t sectorsize);
#endif
diff --git a/libclamav/hashtab.c b/libclamav/hashtab.c
index a30f05c448..83558ed6a7 100644
--- a/libclamav/hashtab.c
+++ b/libclamav/hashtab.c
@@ -172,7 +172,7 @@ static inline void PROFILE_REPORT(const struct cli_hashtable *s)
#define PROFILE_REPORT(s)
#endif
-int cli_hashtab_init(struct cli_hashtable *s, size_t capacity)
+cl_error_t cli_hashtab_init(struct cli_hashtable *s, size_t capacity)
{
if (!s)
return CL_ENULLARG;
@@ -181,15 +181,16 @@ int cli_hashtab_init(struct cli_hashtable *s, size_t capacity)
capacity = nearest_power(capacity);
s->htable = cli_calloc(capacity, sizeof(*s->htable));
- if (!s->htable)
+ if (!s->htable) {
return CL_EMEM;
+ }
s->capacity = capacity;
s->used = 0;
s->maxfill = 8 * capacity / 10;
- return 0;
+ return CL_SUCCESS;
}
-int cli_htu32_init(struct cli_htu32 *s, size_t capacity, mpool_t *mempool)
+cl_error_t cli_htu32_init(struct cli_htu32 *s, size_t capacity, mpool_t *mempool)
{
if (!s)
return CL_ENULLARG;
@@ -198,12 +199,13 @@ int cli_htu32_init(struct cli_htu32 *s, size_t capacity, mpool_t *mempool)
capacity = nearest_power(capacity);
s->htable = MPOOL_CALLOC(mempool, capacity, sizeof(*s->htable));
- if (!s->htable)
+ if (!s->htable) {
return CL_EMEM;
+ }
s->capacity = capacity;
s->used = 0;
s->maxfill = 8 * capacity / 10;
- return 0;
+ return CL_SUCCESS;
}
static inline uint32_t hash32shift(uint32_t key)
@@ -296,7 +298,6 @@ const struct cli_htu32_element *cli_htu32_find(const struct cli_htu32 *s, uint32
return NULL; /* not found */
}
-/* linear enumeration - start with current = NULL, returns next item if present or NULL if not */
const struct cli_htu32_element *cli_htu32_next(const struct cli_htu32 *s, const struct cli_htu32_element *current)
{
size_t ncur;
@@ -320,7 +321,7 @@ const struct cli_htu32_element *cli_htu32_next(const struct cli_htu32 *s, const
return NULL;
}
-static int cli_hashtab_grow(struct cli_hashtable *s)
+static cl_error_t cli_hashtab_grow(struct cli_hashtable *s)
{
const size_t new_capacity = nearest_power(s->capacity + 1);
struct cli_element *htable;
@@ -377,7 +378,7 @@ static int cli_hashtab_grow(struct cli_hashtable *s)
#define cli_htu32_grow(A, B) cli_htu32_grow(A)
#endif
-static int cli_htu32_grow(struct cli_htu32 *s, mpool_t *mempool)
+static cl_error_t cli_htu32_grow(struct cli_htu32 *s, mpool_t *mempool)
{
const size_t new_capacity = nearest_power(s->capacity + 1);
struct cli_htu32_element *htable = MPOOL_CALLOC(mempool, new_capacity, sizeof(*s->htable));
@@ -482,13 +483,13 @@ const struct cli_element *cli_hashtab_insert(struct cli_hashtable *s, const char
return NULL;
}
-int cli_htu32_insert(struct cli_htu32 *s, const struct cli_htu32_element *item, mpool_t *mempool)
+cl_error_t cli_htu32_insert(struct cli_htu32 *s, const struct cli_htu32_element *item, mpool_t *mempool)
{
+ cl_error_t ret;
struct cli_htu32_element *element;
struct cli_htu32_element *deleted_element = NULL;
size_t tries = 1;
size_t idx;
- int ret;
if (!s)
return CL_ENULLARG;
@@ -513,14 +514,14 @@ int cli_htu32_insert(struct cli_htu32 *s, const struct cli_htu32_element *item,
}
*element = *item;
s->used++;
- return 0;
+ return CL_SUCCESS;
} else if (element->key == DELETED_HTU32_KEY) {
deleted_element = element;
element->key = 0;
} else if (item->key == element->key) {
PROFILE_DATA_UPDATE(s, tries);
element->data = item->data; /* key found, update */
- return 0;
+ return CL_SUCCESS;
} else {
idx = (idx + tries++) % s->capacity;
element = &s->htable[idx];
@@ -592,7 +593,7 @@ size_t cli_htu32_numitems(struct cli_htu32 *s)
return s->capacity;
}
-int cli_hashtab_store(const struct cli_hashtable *s, FILE *out)
+cl_error_t cli_hashtab_store(const struct cli_hashtable *s, FILE *out)
{
size_t i;
for (i = 0; i < s->capacity; i++) {
@@ -604,7 +605,7 @@ int cli_hashtab_store(const struct cli_hashtable *s, FILE *out)
return CL_SUCCESS;
}
-int cli_hashtab_generate_c(const struct cli_hashtable *s, const char *name)
+cl_error_t cli_hashtab_generate_c(const struct cli_hashtable *s, const char *name)
{
size_t i;
printf("/* TODO: include GPL headers */\n");
@@ -625,10 +626,10 @@ int cli_hashtab_generate_c(const struct cli_hashtable *s, const char *name)
printf("\n};\n");
PROFILE_REPORT(s);
- return 0;
+ return CL_SUCCESS;
}
-int cli_hashtab_load(FILE *in, struct cli_hashtable *s)
+cl_error_t cli_hashtab_load(FILE *in, struct cli_hashtable *s)
{
char line[1024];
while (fgets(line, sizeof(line), in)) {
@@ -640,9 +641,7 @@ int cli_hashtab_load(FILE *in, struct cli_hashtable *s)
return CL_SUCCESS;
}
-/* Initialize hashset. @initial_capacity is rounded to nearest power of 2.
- * Load factor is between 50 and 99. When capacity*load_factor/100 is reached, the hashset is growed */
-int cli_hashset_init(struct cli_hashset *hs, size_t initial_capacity, uint8_t load_factor)
+cl_error_t cli_hashset_init(struct cli_hashset *hs, size_t initial_capacity, uint8_t load_factor)
{
if (load_factor < 50 || load_factor > 99) {
cli_dbgmsg(MODULE_NAME "Invalid load factor: %u, using default of 80%%\n", load_factor);
@@ -665,10 +664,10 @@ int cli_hashset_init(struct cli_hashset *hs, size_t initial_capacity, uint8_t lo
cli_errmsg("hashtab.c: Unable to allocate memory for hs->bitmap\n");
return CL_EMEM;
}
- return 0;
+ return CL_SUCCESS;
}
-int cli_hashset_init_pool(struct cli_hashset *hs, size_t initial_capacity, uint8_t load_factor, mpool_t *mempool)
+cl_error_t cli_hashset_init_pool(struct cli_hashset *hs, size_t initial_capacity, uint8_t load_factor, mpool_t *mempool)
{
if (load_factor < 50 || load_factor > 99) {
cli_dbgmsg(MODULE_NAME "Invalid load factor: %u, using default of 80%%\n", load_factor);
@@ -691,7 +690,7 @@ int cli_hashset_init_pool(struct cli_hashset *hs, size_t initial_capacity, uint8
cli_errmsg("hashtab.c: Unable to allocate/initialize memory for hs->keys\n");
return CL_EMEM;
}
- return 0;
+ return CL_SUCCESS;
}
void cli_hashset_destroy(struct cli_hashset *hs)
@@ -746,23 +745,25 @@ static void cli_hashset_addkey_internal(struct cli_hashset *hs, const uint32_t k
}
}
-static int cli_hashset_grow(struct cli_hashset *hs)
+static cl_error_t cli_hashset_grow(struct cli_hashset *hs)
{
struct cli_hashset new_hs;
size_t i;
- int rc;
+ cl_error_t rc;
/* in-place growing is not possible, since the new keys
* will hash to different locations. */
cli_dbgmsg(MODULE_NAME "Growing hashset, used: %u, capacity: %u\n", hs->count, hs->capacity);
/* create a bigger hashset */
- if (hs->mempool)
+ if (hs->mempool) {
rc = cli_hashset_init_pool(&new_hs, hs->capacity << 1, hs->limit * 100 / hs->capacity, hs->mempool);
- else
+ } else {
rc = cli_hashset_init(&new_hs, hs->capacity << 1, hs->limit * 100 / hs->capacity);
- if (rc != 0)
+ }
+ if (rc != CL_SUCCESS) {
return rc;
+ }
/* and copy keys */
for (i = 0; i < hs->capacity; i++) {
if (BITMAP_CONTAINS(hs->bitmap, i)) {
@@ -773,39 +774,39 @@ static int cli_hashset_grow(struct cli_hashset *hs)
cli_hashset_destroy(hs);
/* replace old hashset with new one */
*hs = new_hs;
- return 0;
+ return CL_SUCCESS;
}
-int cli_hashset_addkey(struct cli_hashset *hs, const uint32_t key)
+cl_error_t cli_hashset_addkey(struct cli_hashset *hs, const uint32_t key)
{
/* check that we didn't reach the load factor.
* Even if we don't know yet whether we'd add this key */
if (hs->count + 1 > hs->limit) {
- int rc = cli_hashset_grow(hs);
- if (rc) {
+ cl_error_t rc = cli_hashset_grow(hs);
+ if (rc != CL_SUCCESS) {
return rc;
}
}
cli_hashset_addkey_internal(hs, key);
- return 0;
+ return CL_SUCCESS;
}
-int cli_hashset_removekey(struct cli_hashset *hs, const uint32_t key)
+cl_error_t cli_hashset_removekey(struct cli_hashset *hs, const uint32_t key)
{
const size_t idx = cli_hashset_search(hs, key);
if (BITMAP_CONTAINS(hs->bitmap, idx)) {
BITMAP_REMOVE(hs->bitmap, idx);
hs->keys[idx] = 0;
hs->count--;
- return 0;
+ return CL_SUCCESS;
}
- return -1;
+ return CL_ERROR;
}
-int cli_hashset_contains(const struct cli_hashset *hs, const uint32_t key)
+bool cli_hashset_contains(const struct cli_hashset *hs, const uint32_t key)
{
const size_t idx = cli_hashset_search(hs, key);
- return BITMAP_CONTAINS(hs->bitmap, idx);
+ return BITMAP_CONTAINS(hs->bitmap, idx) != 0;
}
ssize_t cli_hashset_toarray(const struct cli_hashset *hs, uint32_t **array)
@@ -814,12 +815,13 @@ ssize_t cli_hashset_toarray(const struct cli_hashset *hs, uint32_t **array)
uint32_t *arr;
if (!array) {
- return CL_ENULLARG;
+ return -1;
}
+
*array = arr = cli_malloc(hs->count * sizeof(*arr));
if (!arr) {
cli_errmsg("hashtab.c: Unable to allocate memory for array\n");
- return CL_EMEM;
+ return -1;
}
for (i = 0, j = 0; i < hs->capacity && j < hs->count; i++) {
@@ -835,71 +837,103 @@ void cli_hashset_init_noalloc(struct cli_hashset *hs)
memset(hs, 0, sizeof(*hs));
}
-int cli_hashset_contains_maybe_noalloc(const struct cli_hashset *hs, const uint32_t key)
+bool cli_hashset_contains_maybe_noalloc(const struct cli_hashset *hs, const uint32_t key)
{
- if (!hs->keys)
- return 0;
+ if (!hs->keys) {
+ return false;
+ }
+
return cli_hashset_contains(hs, key);
}
-int cli_map_init(struct cli_map *m, int32_t keysize, int32_t valuesize,
- int32_t capacity)
+cl_error_t cli_map_init(struct cli_map *m, int32_t keysize, int32_t valuesize,
+ int32_t capacity)
{
- if (keysize <= 0 || valuesize < 0 || capacity <= 0)
- return -CL_EARG;
+ cl_error_t ret;
+
+ if (keysize <= 0 || valuesize < 0 || capacity <= 0) {
+ return CL_EARG;
+ }
+
memset(m, 0, sizeof(*m));
- cli_hashtab_init(&m->htab, 16);
+
+ ret = cli_hashtab_init(&m->htab, 16);
+ if (CL_SUCCESS != ret) {
+ return ret;
+ }
+
m->keysize = keysize;
m->valuesize = valuesize;
m->last_insert = -1;
m->last_find = -1;
- return 0;
+
+ return CL_SUCCESS;
}
-int cli_map_addkey(struct cli_map *m, const void *key, int32_t keysize)
+cl_error_t cli_map_addkey(struct cli_map *m, const void *key, int32_t keysize)
{
uint32_t n;
struct cli_element *el;
- if (m->keysize != keysize)
- return -CL_EARG;
+
+ if (m->keysize != keysize) {
+ return CL_EARG;
+ }
+
el = cli_hashtab_find(&m->htab, key, keysize);
if (el) {
+ // already exists
m->last_insert = (int32_t)el->data;
- return 0;
+ return CL_ECREAT;
}
n = m->nvalues + 1;
if (m->valuesize) {
void *v;
+
v = cli_realloc(m->u.sized_values, n * m->valuesize);
- if (!v)
- return -CL_EMEM;
+ if (!v) {
+ return CL_EMEM;
+ }
+
m->u.sized_values = v;
memset((char *)m->u.sized_values + (n - 1) * m->valuesize, 0, m->valuesize);
} else {
struct cli_map_value *v;
+
v = cli_realloc(m->u.unsized_values, n * sizeof(*m->u.unsized_values));
- if (!v)
- return -CL_EMEM;
+ if (!v) {
+ return CL_EMEM;
+ }
+
m->u.unsized_values = v;
memset(&m->u.unsized_values[n - 1], 0, sizeof(*m->u.unsized_values));
}
m->nvalues = n;
- if (!cli_hashtab_insert(&m->htab, key, keysize, (const cli_element_data)(n - 1)))
- return -CL_EMEM;
+ if (!cli_hashtab_insert(&m->htab, key, keysize, (const cli_element_data)(n - 1))) {
+ return CL_EMEM;
+ }
+
m->last_insert = n - 1;
- return 1;
+ return CL_SUCCESS;
}
-int cli_map_removekey(struct cli_map *m, const void *key, int32_t keysize)
+cl_error_t cli_map_removekey(struct cli_map *m, const void *key, int32_t keysize)
{
struct cli_element *el;
- if (m->keysize != keysize)
- return -CL_EARG;
+
+ if (m->keysize != keysize) {
+ return CL_EARG;
+ }
+
el = cli_hashtab_find(&m->htab, key, keysize);
- if (!el)
- return 0;
- if ((int32_t)el->data >= (int32_t)m->nvalues || (int32_t)el->data < 0)
- return -CL_EARG;
+ if (!el) {
+ // not found, can't remove
+ return CL_EUNLINK;
+ }
+
+ if ((int32_t)el->data >= (int32_t)m->nvalues || (int32_t)el->data < 0) {
+ return CL_EARG;
+ }
+
if (!m->valuesize) {
struct cli_map_value *v = &m->u.unsized_values[(int32_t)el->data];
free(v->value);
@@ -909,72 +943,99 @@ int cli_map_removekey(struct cli_map *m, const void *key, int32_t keysize)
char *v = (char *)m->u.sized_values + (int32_t)el->data * m->valuesize;
memset(v, 0, m->valuesize);
}
+
cli_hashtab_delete(&m->htab, key, keysize);
- return 1;
+
+ return CL_SUCCESS;
}
-int cli_map_setvalue(struct cli_map *m, const void *value, int32_t valuesize)
+cl_error_t cli_map_setvalue(struct cli_map *m, const void *value, int32_t valuesize)
{
- if ((m->valuesize && m->valuesize != valuesize) || (uint32_t)(m->last_insert) >= m->nvalues || m->last_insert < 0)
- return -CL_EARG;
+ if ((m->valuesize && m->valuesize != valuesize) || (uint32_t)(m->last_insert) >= m->nvalues || m->last_insert < 0) {
+ return CL_EARG;
+ }
+
if (m->valuesize) {
memcpy((char *)m->u.sized_values + m->last_insert * m->valuesize,
value, valuesize);
} else {
struct cli_map_value *v = &m->u.unsized_values[m->last_insert];
- if (v->value)
+
+ if (v->value) {
free(v->value);
+ }
+
v->value = cli_malloc(valuesize);
if (!v->value) {
cli_errmsg("hashtab.c: Unable to allocate memory for v->value\n");
- return -CL_EMEM;
+ return CL_EMEM;
}
+
memcpy(v->value, value, valuesize);
v->valuesize = valuesize;
}
- return 0;
+ return CL_SUCCESS;
}
-int cli_map_find(struct cli_map *m, const void *key, int32_t keysize)
+cl_error_t cli_map_find(struct cli_map *m, const void *key, int32_t keysize)
{
struct cli_element *el;
- if (m->keysize != keysize)
- return -CL_EARG;
+ if (m->keysize != keysize) {
+ return CL_EARG;
+ }
+
el = cli_hashtab_find(&m->htab, key, keysize);
- if (!el)
- return 0;
+ if (!el) {
+ // not found
+ return CL_EACCES;
+ }
+
m->last_find = (int32_t)el->data;
- return 1;
+
+ return CL_SUCCESS;
}
int cli_map_getvalue_size(struct cli_map *m)
{
- if (m->valuesize)
+ if (m->valuesize) {
return m->valuesize;
- if (m->last_find < 0 || (uint32_t)(m->last_find) >= m->nvalues)
- return -CL_EARG;
+ }
+
+ if (m->last_find < 0 || (uint32_t)(m->last_find) >= m->nvalues) {
+ return -1;
+ }
+
return m->u.unsized_values[m->last_find].valuesize;
}
void *cli_map_getvalue(struct cli_map *m)
{
- if (m->last_find < 0 || (uint32_t)(m->last_find) >= m->nvalues)
+ if (m->last_find < 0 || (uint32_t)(m->last_find) >= m->nvalues) {
return NULL;
- if (m->valuesize)
+ }
+
+ if (m->valuesize) {
return (char *)m->u.sized_values + m->last_find * m->valuesize;
+ }
+
return m->u.unsized_values[m->last_find].value;
}
void cli_map_delete(struct cli_map *m)
{
cli_hashtab_free(&m->htab);
+
if (!m->valuesize) {
unsigned i;
- for (i = 0; i < m->nvalues; i++)
+
+ for (i = 0; i < m->nvalues; i++) {
free(m->u.unsized_values[i].value);
+ }
+
free(m->u.unsized_values);
} else {
free(m->u.sized_values);
}
+
memset(m, 0, sizeof(*m));
}
diff --git a/libclamav/hashtab.h b/libclamav/hashtab.h
index e17fd58d4a..4c8fec13f1 100644
--- a/libclamav/hashtab.h
+++ b/libclamav/hashtab.h
@@ -30,10 +30,26 @@
#include
#include
#include
+#include
-#include "clamav-types.h"
+#include "clamav.h"
#include "clamav-config.h"
#include "mpool.h"
+
+/******************************************************************************/
+/* A hash table.
+ *
+ * There are two types:
+ * 1. hashtable:
+ * The key is a const char* (string)
+ * The value (data) is a buffer, stored as a size_t (instead of a void *) and an offset.
+ *
+ * 2. htu32 (hashtable uint32_t)
+ * Th ekey is a uint32_t number
+ * The value (data) is a buffer, stored as either a size_t, or as a void *, and an offset.
+ */
+/******************************************************************************/
+
typedef size_t cli_element_data;
/* define this for debugging/profiling purposes only, NOT in production/release code */
@@ -81,15 +97,81 @@ struct cli_hashtable {
STRUCT_PROFILE
};
-int cli_hashtab_generate_c(const struct cli_hashtable *s, const char *name);
+/**
+ * @brief Generate C source code that represents the given hash table
+ *
+ * Comment: We don't really use this.
+ *
+ * @param s
+ * @param name Some string name for the elements of this generated table.
+ * @return cl_error_t
+ */
+cl_error_t cli_hashtab_generate_c(const struct cli_hashtable *s, const char *name);
+
struct cli_element *cli_hashtab_find(const struct cli_hashtable *s, const char *key, const size_t len);
-int cli_hashtab_init(struct cli_hashtable *s, size_t capacity);
+
+/**
+ * @brief Create a new hashtab with a given capacity.
+ *
+ * @param s
+ * @param capacity
+ * @return cl_error_t
+ */
+cl_error_t cli_hashtab_init(struct cli_hashtable *s, size_t capacity);
+
+/**
+ * @brief Insert a new key with data into the hashtable.
+ *
+ * @param s
+ * @param key
+ * @param len
+ * @param data
+ * @return const struct cli_element*
+ */
const struct cli_element *cli_hashtab_insert(struct cli_hashtable *s, const char *key, const size_t len, const cli_element_data data);
+
+/**
+ * @brief Delete a key from the hash table
+ *
+ * @param s
+ * @param key
+ * @param len
+ */
void cli_hashtab_delete(struct cli_hashtable *s, const char *key, const size_t len);
+
+/**
+ * @brief Remove all keys from the hashtable
+ *
+ * @param s
+ */
void cli_hashtab_clear(struct cli_hashtable *s);
+
+/**
+ * @brief Free the hash table
+ *
+ * This will clear the hash table first. You don't need to clear it manually first.
+ *
+ * @param s
+ */
void cli_hashtab_free(struct cli_hashtable *s);
-int cli_hashtab_load(FILE *in, struct cli_hashtable *s);
-int cli_hashtab_store(const struct cli_hashtable *s, FILE *out);
+
+/**
+ * @brief Load a hash table from a file. (unpickle!)
+ *
+ * @param in
+ * @param s
+ * @return cl_error_t
+ */
+cl_error_t cli_hashtab_load(FILE *in, struct cli_hashtable *s);
+
+/**
+ * @brief Write a hash table to a file. (pickle!)
+ *
+ * @param s
+ * @param out
+ * @return cl_error_t
+ */
+cl_error_t cli_hashtab_store(const struct cli_hashtable *s, FILE *out);
struct cli_htu32_element {
uint32_t key;
@@ -108,21 +190,121 @@ struct cli_htu32 {
STRUCT_PROFILE
};
-#ifndef USE_MPOOL
-#define cli_htu32_init(A, B, C) cli_htu32_init(A, B)
-#define cli_htu32_insert(A, B, C) cli_htu32_insert(A, B)
-#define cli_htu32_free(A, B) cli_htu32_free(A)
+#ifdef USE_MPOOL
+
+/**
+ * @brief A macro to wrap cli_htu32_init() where you can assume MEMPOOL is enabled,
+ * but will replace the last partment with NULL if MEMPOOL is not enabled.
+ */
+#define CLI_HTU32_INIT(A, B, C) cli_htu32_init(A, B, C)
+/**
+ * @brief A macro to wrap cli_htu32_insert() where you can assume MEMPOOL is enabled,
+ * but will replace the last partment with NULL if MEMPOOL is not enabled.
+ */
+#define CLI_HTU32_INSERT(A, B, C) cli_htu32_insert(A, B, C)
+/**
+ * @brief A macro to wrap cli_htu32_free() where you can assume MEMPOOL is enabled,
+ * but will replace the last partment with NULL if MEMPOOL is not enabled.
+ */
+#define CLI_HTU32_FREE(A, B) cli_htu32_free(A, B)
+
+#else
+
+/**
+ * @brief A macro to wrap cli_htu32_init() where you can assume MEMPOOL is enabled,
+ * but will replace the last partment with NULL if MEMPOOL is not enabled.
+ */
+#define CLI_HTU32_INIT(A, B, C) cli_htu32_init(A, B, NULL)
+/**
+ * @brief A macro to wrap cli_htu32_insert() where you can assume MEMPOOL is enabled,
+ * but will replace the last partment with NULL if MEMPOOL is not enabled.
+ */
+#define CLI_HTU32_INSERT(A, B, C) cli_htu32_insert(A, B, NULL)
+/**
+ * @brief A macro to wrap cli_htu32_free() where you can assume MEMPOOL is enabled,
+ * but will replace the last partment with NULL if MEMPOOL is not enabled.
+ */
+#define CLI_HTU32_FREE(A, B) cli_htu32_free(A, NULL)
+
#endif
-int cli_htu32_init(struct cli_htu32 *s, size_t capacity, mpool_t *mempool);
-int cli_htu32_insert(struct cli_htu32 *s, const struct cli_htu32_element *item, mpool_t *mempool);
+
+/**
+ * @brief Initialize a new u32 hashtable.
+ *
+ * @param s
+ * @param capacity
+ * @param mempool If MEMPOOL not enabled, this can be NULL.
+ * @return cl_error_t
+ */
+cl_error_t cli_htu32_init(struct cli_htu32 *s, size_t capacity, mpool_t *mempool);
+
+/**
+ * @brief Insert a new element into the u32 hashtable.
+ *
+ * @param s
+ * @param item
+ * @param mempool
+ * @return cl_error_t
+ */
+cl_error_t cli_htu32_insert(struct cli_htu32 *s, const struct cli_htu32_element *item, mpool_t *mempool);
+
+/**
+ * @brief Free the u32 hashtable.
+ *
+ * This will clear the hash table first. You don't need to clear it manually first.
+ *
+ * @param s
+ * @param mempool
+ */
+void cli_htu32_free(struct cli_htu32 *s, mpool_t *mempool);
+
+/**
+ * @brief Find a sepcific element by key in the u32 hashtable.
+ *
+ * @param s
+ * @param key
+ * @return const struct cli_htu32_element*
+ */
const struct cli_htu32_element *cli_htu32_find(const struct cli_htu32 *s, uint32_t key);
+
+/**
+ * @brief Remove a specific element from the u32 hashtable.
+ *
+ * @param s
+ * @param key
+ */
void cli_htu32_delete(struct cli_htu32 *s, uint32_t key);
+
+/**
+ * @brief Remove all elements from the u32 hashtable.
+ *
+ * @param s
+ */
void cli_htu32_clear(struct cli_htu32 *s);
-void cli_htu32_free(struct cli_htu32 *s, mpool_t *mempool);
+
+/**
+ * @brief Get the next element in the table, following the provided element
+ *
+ * Use this to enumerate the table linearly.
+ *
+ * @param s
+ * @param current If you feed it NULL, it will give you the first element.
+ * @return const struct cli_htu32_element* Will return the next element, or NULL if there are no further elements.
+ */
const struct cli_htu32_element *cli_htu32_next(const struct cli_htu32 *s, const struct cli_htu32_element *current);
+
+/**
+ * @brief Get the number of items in the u32 hashtable.
+ *
+ * @param s
+ * @return size_t
+ */
size_t cli_htu32_numitems(struct cli_htu32 *s);
+/******************************************************************************/
/* a hashtable that stores the values too */
+/******************************************************************************/
+
struct cli_map_value {
void *value;
int32_t valuesize;
@@ -140,17 +322,97 @@ struct cli_map {
int32_t last_insert;
int32_t last_find;
};
-int cli_map_init(struct cli_map *m, int32_t keysize, int32_t valuesize,
- int32_t capacity);
-int cli_map_addkey(struct cli_map *m, const void *key, int32_t keysize);
-int cli_map_removekey(struct cli_map *m, const void *key, int32_t keysize);
-int cli_map_setvalue(struct cli_map *m, const void *value, int32_t valuesize);
-int cli_map_find(struct cli_map *m, const void *key, int32_t keysize);
+
+/**
+ * @brief Initialize a new map
+ *
+ * @param m
+ * @param keysize
+ * @param valuesize
+ * @param capacity
+ * @return cl_error_t CL_SUCCESS on success
+ * @return cl_error_t CL_E* if some error occured
+ */
+cl_error_t cli_map_init(struct cli_map *m, int32_t keysize, int32_t valuesize,
+ int32_t capacity);
+
+/**
+ * @brief add key to the map
+ *
+ * @param m
+ * @param key
+ * @param keysize
+ * @return cl_error_t CL_SUCCESS if added.
+ * @return cl_error_t CL_ECREAT if already present.
+ * @return cl_error_t CL_E* if some error occured.
+ */
+cl_error_t cli_map_addkey(struct cli_map *m, const void *key, int32_t keysize);
+
+/**
+ * @brief remove key from the map
+ *
+ * @param m
+ * @param key
+ * @param keysize
+ * @return cl_error_t CL_SUCCESS if removed.
+ * @return cl_error_t CL_EUNLINK if not present, so didn't need to be removed.
+ * @return cl_error_t CL_E* if some error occured.
+ */
+cl_error_t cli_map_removekey(struct cli_map *m, const void *key, int32_t keysize);
+
+/**
+ * @brief set the value for the last inserted key with map_addkey
+ *
+ * @param m
+ * @param value
+ * @param valuesize
+ * @return cl_error_t CL_SUCCESS on success
+ * @return cl_error_t CL_E* if some error occured
+ */
+cl_error_t cli_map_setvalue(struct cli_map *m, const void *value, int32_t valuesize);
+
+/**
+ * @brief find key in the map
+ *
+ * @param m
+ * @param key
+ * @param keysize
+ * @return cl_error_t CL_SUCCESS if found
+ * @return cl_error_t CL_EACCES if NOT found
+ * @return cl_error_t CL_E* if some error occured.
+ */
+cl_error_t cli_map_find(struct cli_map *m, const void *key, int32_t keysize);
+
+/**
+ * @brief get the size of value obtained during the last map_find
+ *
+ * @param m
+ * @return int the value size on success
+ * @return int -1 on failure
+ */
int cli_map_getvalue_size(struct cli_map *m);
+
+/**
+ * @brief get the value obtained during the last map_find
+ *
+ * @param m
+ * @return void* the value on success
+ * @return void* NULL on failure
+ */
void *cli_map_getvalue(struct cli_map *m);
+
+/**
+ * @brief delete the map
+ *
+ * @param m
+ */
void cli_map_delete(struct cli_map *m);
-/* A set of unique keys. */
+/******************************************************************************/
+/* A set of unique keys (no values).
+ * The keys are just uint32_t numbers. */
+/******************************************************************************/
+
struct cli_hashset {
uint32_t *keys;
uint32_t *bitmap;
@@ -161,21 +423,102 @@ struct cli_hashset {
uint32_t limit;
};
-int cli_hashset_init(struct cli_hashset *hs, size_t initial_capacity, uint8_t load_factor);
-int cli_hashset_init_pool(struct cli_hashset *hs, size_t initial_capacity, uint8_t load_factor, mpool_t *mempool);
-int cli_hashset_addkey(struct cli_hashset *hs, const uint32_t key);
-int cli_hashset_removekey(struct cli_hashset *hs, const uint32_t key);
-int cli_hashset_contains(const struct cli_hashset *hs, const uint32_t key);
-int cli_hashset_clear(struct cli_hashset *hs);
+/**
+ * @brief Initialize hashset.
+ *
+ * When capacity * (load_factor/100) is reached, the hashset is growed.
+ *
+ * @param hs
+ * @param initial_capacity is rounded to nearest power of 2.
+ * @param load_factor is between 50 and 99.
+ * @return cl_error_t
+ */
+cl_error_t cli_hashset_init(struct cli_hashset *hs, size_t initial_capacity, uint8_t load_factor);
+
+/**
+ * @brief Initialize hashset using the clamav MEMPOOL instead of just malloc/realloc.
+ *
+ * Comment: not presently used in any parsers or signature loaders or anything.
+ *
+ * @param hs
+ * @param initial_capacity is rounded to nearest power of 2.
+ * @param load_factor is between 50 and 99.
+ * @param mempool the mempool
+ * @return cl_error_t
+ */
+cl_error_t cli_hashset_init_pool(struct cli_hashset *hs, size_t initial_capacity, uint8_t load_factor, mpool_t *mempool);
+
+/**
+ * @brief Add a key to the hashset.
+ *
+ * @param hs
+ * @param key
+ * @return cl_error_t
+ */
+cl_error_t cli_hashset_addkey(struct cli_hashset *hs, const uint32_t key);
+
+/**
+ * @brief Remove a key from the hashset
+ *
+ * @param hs
+ * @param key
+ * @return cl_error_t
+ */
+cl_error_t cli_hashset_removekey(struct cli_hashset *hs, const uint32_t key);
+
+/**
+ * @brief Find out if hashset contains akey
+ *
+ * @param hs
+ * @param key
+ * @return true If found
+ * @return false If not found
+ */
+bool cli_hashset_contains(const struct cli_hashset *hs, const uint32_t key);
+
+/**
+ * @brief Destroy/deallocate a hashset.
+ *
+ * @param hs
+ */
void cli_hashset_destroy(struct cli_hashset *hs);
+
+/**
+ * @brief Convert the hashset to an array of uint32_t's
+ *
+ * It will allocate a 0-length array! You are still responsible for freeing it if
+ * it returns 0!
+ *
+ * You don't need to free anything if it returns -1.
+ *
+ * @param hs
+ * @param [out] array Allocated array of the length returned. Caller must free it.
+ * @return ssize_t The length of the array if success, or else -1 if failed.
+ */
ssize_t cli_hashset_toarray(const struct cli_hashset *hs, uint32_t **array);
-int cli_hashset_removekey(struct cli_hashset *hs, const uint32_t key);
-/* Initializes the set without allocating memory, you can do lookups on it
+/**
+ * @brief Initializes the set without allocating memory
+ *
+ * Initializes the set without allocating memory, you can do lookups on it
* using _contains_maybe_noalloc. You need to initialize it using _init
- * before using _addkey or _removekey though */
+ * before using _addkey or _removekey though
+ *
+ * @param hs
+ */
void cli_hashset_init_noalloc(struct cli_hashset *hs);
-/* this works like its _contains counterpart above, except that the hashset may
- * have not been initialized by _init, only by _init_noalloc */
-int cli_hashset_contains_maybe_noalloc(const struct cli_hashset *hs, const uint32_t key);
+
+/**
+ * @brief
+ *
+ * this works like cli_hashset_contains (above), except that the hashset may
+ * have not been initialized by _init, only by _init_noalloc
+ *
+ * @param hs
+ * @param key
+ * @return true If found
+ * @return false If not found
+ */
+bool cli_hashset_contains_maybe_noalloc(const struct cli_hashset *hs, const uint32_t key);
+
#endif
diff --git a/libclamav/hfsplus.c b/libclamav/hfsplus.c
index 406789e089..a592d9b785 100644
--- a/libclamav/hfsplus.c
+++ b/libclamav/hfsplus.c
@@ -47,14 +47,14 @@ static void nodedescriptor_print(const char *, hfsNodeDescriptor *);
static void forkdata_to_host(hfsPlusForkData *);
static void forkdata_print(const char *, hfsPlusForkData *);
-static int hfsplus_volumeheader(cli_ctx *, hfsPlusVolumeHeader **);
-static int hfsplus_readheader(cli_ctx *, hfsPlusVolumeHeader *, hfsNodeDescriptor *,
- hfsHeaderRecord *, int, const char *);
+static cl_error_t hfsplus_volumeheader(cli_ctx *, hfsPlusVolumeHeader **);
+static cl_error_t hfsplus_readheader(cli_ctx *, hfsPlusVolumeHeader *, hfsNodeDescriptor *,
+ hfsHeaderRecord *, int, const char *);
static cl_error_t hfsplus_scanfile(cli_ctx *, hfsPlusVolumeHeader *, hfsHeaderRecord *,
hfsPlusForkData *, const char *, char **, char *);
-static int hfsplus_validate_catalog(cli_ctx *, hfsPlusVolumeHeader *, hfsHeaderRecord *);
-static int hfsplus_fetch_node(cli_ctx *, hfsPlusVolumeHeader *, hfsHeaderRecord *,
- hfsHeaderRecord *, hfsPlusForkData *, uint32_t, uint8_t *);
+static cl_error_t hfsplus_validate_catalog(cli_ctx *, hfsPlusVolumeHeader *, hfsHeaderRecord *);
+static cl_error_t hfsplus_fetch_node(cli_ctx *, hfsPlusVolumeHeader *, hfsHeaderRecord *,
+ hfsHeaderRecord *, hfsPlusForkData *, uint32_t, uint8_t *);
static cl_error_t hfsplus_walk_catalog(cli_ctx *, hfsPlusVolumeHeader *, hfsHeaderRecord *,
hfsHeaderRecord *, hfsHeaderRecord *, const char *);
@@ -128,7 +128,7 @@ static void forkdata_print(const char *pfx, hfsPlusForkData *fork)
}
/* Read and convert the HFS+ volume header */
-static int hfsplus_volumeheader(cli_ctx *ctx, hfsPlusVolumeHeader **header)
+static cl_error_t hfsplus_volumeheader(cli_ctx *ctx, hfsPlusVolumeHeader **header)
{
hfsPlusVolumeHeader *volHeader;
const uint8_t *mPtr;
@@ -209,8 +209,8 @@ static int hfsplus_volumeheader(cli_ctx *ctx, hfsPlusVolumeHeader **header)
}
/* Read and convert the header node */
-static int hfsplus_readheader(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsNodeDescriptor *nodeDesc,
- hfsHeaderRecord *headerRec, int headerType, const char *name)
+static cl_error_t hfsplus_readheader(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsNodeDescriptor *nodeDesc,
+ hfsHeaderRecord *headerRec, int headerType, const char *name)
{
const uint8_t *mPtr = NULL;
off_t offset;
@@ -317,11 +317,11 @@ static int hfsplus_readheader(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsN
static cl_error_t hfsplus_scanfile(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsHeaderRecord *extHeader,
hfsPlusForkData *fork, const char *dirname, char **filename, char *orig_filename)
{
+ cl_error_t status = CL_SUCCESS;
hfsPlusExtentDescriptor *currExt;
const uint8_t *mPtr = NULL;
char *tmpname = NULL;
int ofd;
- cl_error_t ret = CL_CLEAN;
uint64_t targetSize;
uint32_t outputBlocks = 0;
uint8_t ext;
@@ -331,7 +331,7 @@ static cl_error_t hfsplus_scanfile(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader,
/* bad record checks */
if (!fork || (fork->logicalSize == 0) || (fork->totalBlocks == 0)) {
cli_dbgmsg("hfsplus_scanfile: Empty file.\n");
- return CL_CLEAN;
+ goto done;
}
/* check limits */
@@ -339,19 +339,20 @@ static cl_error_t hfsplus_scanfile(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader,
#if SIZEOF_LONG < 8
if (targetSize > ULONG_MAX) {
cli_dbgmsg("hfsplus_scanfile: File too large for limit check.\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
#endif
- ret = cli_checklimits("hfsplus_scanfile", ctx, (unsigned long)targetSize, 0, 0);
- if (ret != CL_CLEAN) {
- return ret;
+ status = cli_checklimits("hfsplus_scanfile", ctx, (unsigned long)targetSize, 0, 0);
+ if (status != CL_SUCCESS) {
+ goto done;
}
/* open file */
- ret = cli_gentempfd(dirname, &tmpname, &ofd);
- if (ret != CL_CLEAN) {
+ status = cli_gentempfd(dirname, &tmpname, &ofd);
+ if (status != CL_SUCCESS) {
cli_dbgmsg("hfsplus_scanfile: Cannot generate temporary file.\n");
- return ret;
+ goto done;
}
cli_dbgmsg("hfsplus_scanfile: Extracting to %s\n", tmpname);
@@ -367,6 +368,7 @@ static cl_error_t hfsplus_scanfile(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader,
cli_dbgmsg("hfsplus_scanfile: output all blocks, remaining size " STDu64 "\n", targetSize);
break;
}
+
/* Prepare extent */
if (ext < 8) {
currExt = &(fork->extents[ext]);
@@ -374,85 +376,107 @@ static cl_error_t hfsplus_scanfile(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader,
} else {
cli_dbgmsg("hfsplus_scanfile: need next extent from ExtentOverflow\n");
/* Not implemented yet */
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
+
/* have extent, so validate and get block range */
if ((currExt->startBlock == 0) || (currExt->blockCount == 0)) {
cli_dbgmsg("hfsplus_scanfile: next extent empty, done\n");
break;
}
+
if ((currExt->startBlock & 0x10000000) && (currExt->blockCount & 0x10000000)) {
cli_dbgmsg("hfsplus_scanfile: next extent illegal!\n");
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
+
currBlock = currExt->startBlock;
endBlock = currExt->startBlock + currExt->blockCount - 1;
if ((currBlock > volHeader->totalBlocks) || (endBlock > volHeader->totalBlocks) || (currExt->blockCount > volHeader->totalBlocks)) {
cli_dbgmsg("hfsplus_scanfile: bad extent!\n");
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
+
/* Write the blocks, walking the map */
while (currBlock <= endBlock) {
size_t to_write = MIN(targetSize, volHeader->blockSize);
size_t written;
off_t offset = currBlock * volHeader->blockSize;
+
/* move map to next block */
mPtr = fmap_need_off_once(ctx->fmap, offset, volHeader->blockSize);
if (!mPtr) {
cli_errmsg("hfsplus_scanfile: map error\n");
- ret = CL_EMAP;
- break;
+ status = CL_EMAP;
+ goto done;
}
+
written = cli_writen(ofd, mPtr, to_write);
if (written != to_write) {
cli_errmsg("hfsplus_scanfile: write error\n");
- ret = CL_EWRITE;
- break;
+ status = CL_EWRITE;
+ goto done;
}
+
targetSize -= to_write;
outputSize += to_write;
currBlock++;
+
if (targetSize == 0) {
cli_dbgmsg("hfsplus_scanfile: all data written\n");
break;
}
+
if (outputBlocks >= fork->totalBlocks) {
cli_dbgmsg("hfsplus_scanfile: output all blocks, remaining size " STDu64 "\n", targetSize);
break;
}
}
+
/* Finished the extent, move to next */
ext++;
- } while (ret == CL_CLEAN);
+ } while (status == CL_SUCCESS);
- /* if successful so far, scan the output */
+ /* Now that we're done, ...
+ * A) if filename output param is provided, just pass back the filename.
+ * B) otherwise scan the file.
+ */
if (filename) {
*filename = tmpname;
+
} else {
- if (ret == CL_CLEAN) {
- ret = cli_magic_scan_desc(ofd, tmpname, ctx, orig_filename);
+ status = cli_magic_scan_desc(ofd, tmpname, ctx, orig_filename);
+ if (status != CL_SUCCESS) {
+ goto done;
}
- if (!ctx->engine->keeptmp) {
- if (cli_unlink(tmpname)) {
- ret = CL_EUNLINK;
- }
- }
- free(tmpname);
+ /* TODO: Scan overlay if outputBlocks >= fork->totalBlocks ? */
}
+done:
+
if (ofd >= 0) {
close(ofd);
}
+ if ((NULL == filename) || // output param not provided, which means we should clean up the temp file,
+ (status != CL_SUCCESS)) { // or we failed, so we should clean up the temp file.
+
+ if (tmpname) {
+ if (!ctx->engine->keeptmp) {
+ (void)cli_unlink(tmpname);
+ }
+ free(tmpname);
+ }
+ }
- return ret;
+ return status;
}
/* Calculate true node limit for catalogFile */
-static int hfsplus_validate_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsHeaderRecord *catHeader)
+static cl_error_t hfsplus_validate_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsHeaderRecord *catHeader)
{
hfsPlusForkData *catFork;
@@ -476,14 +500,14 @@ static int hfsplus_validate_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader
}
/* Check if an attribute is present in the attribute map */
-static cl_error_t hfsplus_check_attribute(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsHeaderRecord *attrHeader, uint32_t expectedCnid, const uint8_t name[], uint32_t nameLen, int *found, uint8_t record[], unsigned *recordSize)
+static cl_error_t hfsplus_check_attribute(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsHeaderRecord *attrHeader, uint32_t expectedCnid, const uint8_t name[], uint32_t nameLen, int *found, uint8_t record[], size_t *recordSize)
{
+ cl_error_t status = CL_SUCCESS;
uint16_t nodeSize, recordNum, topOfOffsets;
uint16_t recordStart, nextDist, nextStart;
uint8_t *nodeBuf = NULL;
uint32_t thisNode, nodeLimit, nodesScanned = 0;
- cl_error_t ret = CL_SUCCESS;
- int foundAttr = 0;
+ bool foundAttr = false;
if (found) {
*found = 0;
@@ -503,12 +527,13 @@ static cl_error_t hfsplus_check_attribute(cli_ctx *ctx, hfsPlusVolumeHeader *vol
cli_dbgmsg("hfsplus_check_attribute: failed to acquire node buffer, "
"size " STDu32 "\n",
nodeSize);
- return CL_EMEM;
+ status = CL_EMEM;
+ goto done;
}
/* Walk catalog leaf nodes, and scan contents of each */
/* Because we want to scan them all, the index nodes add no value */
- while (ret == CL_CLEAN && !foundAttr) {
+ while (status == CL_SUCCESS && !foundAttr) {
hfsNodeDescriptor nodeDesc;
if (thisNode == 0) {
@@ -521,10 +546,10 @@ static cl_error_t hfsplus_check_attribute(cli_ctx *ctx, hfsPlusVolumeHeader *vol
}
/* fetch node into buffer */
- ret = hfsplus_fetch_node(ctx, volHeader, attrHeader, NULL, &(volHeader->attributesFile), thisNode, nodeBuf);
- if (ret != CL_CLEAN) {
+ status = hfsplus_fetch_node(ctx, volHeader, attrHeader, NULL, &(volHeader->attributesFile), thisNode, nodeBuf);
+ if (status != CL_SUCCESS) {
cli_dbgmsg("hfsplus_check_attribute: node fetch failed.\n");
- break;
+ goto done;
}
memcpy(&nodeDesc, nodeBuf, 14);
@@ -533,13 +558,13 @@ static cl_error_t hfsplus_check_attribute(cli_ctx *ctx, hfsPlusVolumeHeader *vol
nodedescriptor_print("leaf attribute node", &nodeDesc);
if ((nodeDesc.kind != HFS_NODEKIND_LEAF) || (nodeDesc.height != 1)) {
cli_dbgmsg("hfsplus_check_attribute: invalid leaf node!\n");
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
if ((nodeSize / 4) < nodeDesc.numRecords) {
cli_dbgmsg("hfsplus_check_attribute: too many leaf records for one node!\n");
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
/* Walk this node's records and scan */
@@ -557,15 +582,15 @@ static cl_error_t hfsplus_check_attribute(cli_ctx *ctx, hfsPlusVolumeHeader *vol
/* Check record location */
if ((nextStart > topOfOffsets - 1) || (nextStart < recordStart)) {
cli_dbgmsg("hfsplus_check_attribute: bad record location %x for %u!\n", nextStart, recordNum);
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
recordStart = nextStart;
if (recordStart + sizeof(attrKey) >= topOfOffsets) {
cli_dbgmsg("hfsplus_check_attribute: Not enough data for an attribute key at location %x for %u!\n",
nextStart, recordNum);
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
memcpy(&attrKey, &nodeBuf[recordStart], sizeof(attrKey));
@@ -581,14 +606,14 @@ static cl_error_t hfsplus_check_attribute(cli_ctx *ctx, hfsPlusVolumeHeader *vol
if (recordStart + attrKey.keyLength + 4 >= topOfOffsets) {
cli_dbgmsg("hfsplus_check_attribute: key too long for location %x for %u!\n",
nextStart, recordNum);
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
if (recordStart + sizeof(hfsPlusAttributeKey) + attrKey.nameLength >= topOfOffsets) {
cli_dbgmsg("hfsplus_check_attribute: Attribute name is longer than expected: %u\n", attrKey.nameLength);
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
if (attrKey.cnid == expectedCnid && attrKey.nameLength * 2 == nameLen && memcmp(&nodeBuf[recordStart + 14], name, nameLen) == 0) {
@@ -601,37 +626,39 @@ static cl_error_t hfsplus_check_attribute(cli_ctx *ctx, hfsPlusVolumeHeader *vol
continue;
}
- if (found) {
- *found = 1;
- }
-
if (attrRec.attributeSize > *recordSize) {
- ret = CL_EMAXSIZE;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
memcpy(record, &(nodeBuf[recordStart + sizeof(hfsPlusAttributeKey) + attrKey.nameLength * 2 + sizeof(attrRec)]), attrRec.attributeSize);
*recordSize = attrRec.attributeSize;
- ret = CL_SUCCESS;
- foundAttr = 1;
+ if (found) {
+ *found = 1;
+ }
+
+ foundAttr = true;
break;
}
}
}
+done:
+
if (nodeBuf != NULL) {
free(nodeBuf);
nodeBuf = NULL;
}
- return ret;
+
+ return status;
}
/* Fetch a node's contents into the buffer */
-static int hfsplus_fetch_node(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsHeaderRecord *catHeader,
- hfsHeaderRecord *extHeader, hfsPlusForkData *catFork, uint32_t node, uint8_t *buff)
+static cl_error_t hfsplus_fetch_node(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsHeaderRecord *catHeader,
+ hfsHeaderRecord *extHeader, hfsPlusForkData *catFork, uint32_t node, uint8_t *buff)
{
- int foundBlock = 0;
+ bool foundBlock = false;
uint64_t catalogOffset;
uint32_t startBlock, startOffset;
uint32_t endBlock, endSize;
@@ -666,7 +693,7 @@ static int hfsplus_fetch_node(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsH
for (curBlock = startBlock; curBlock <= endBlock; ++curBlock) {
- foundBlock = 0;
+ foundBlock = false;
searchBlock = curBlock;
/* Find which extent has that block */
for (extentNum = 0; extentNum < 8; extentNum++) {
@@ -686,7 +713,7 @@ static int hfsplus_fetch_node(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsH
if (searchBlock < currExt->blockCount) {
cli_dbgmsg("hfsplus_fetch_node: found block in extent " STDu32 "\n", extentNum);
realFileBlock = currExt->startBlock + searchBlock;
- foundBlock = 1;
+ foundBlock = true;
break;
} else {
cli_dbgmsg("hfsplus_fetch_node: not in extent " STDu32 "\n", extentNum);
@@ -694,7 +721,7 @@ static int hfsplus_fetch_node(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsH
}
}
- if (foundBlock == 0) {
+ if (foundBlock == false) {
cli_dbgmsg("hfsplus_fetch_node: not in first 8 extents\n");
cli_dbgmsg("hfsplus_fetch_node: finding this node requires extent overflow support\n");
return CL_EFORMAT;
@@ -726,6 +753,7 @@ static int hfsplus_fetch_node(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsH
static cl_error_t hfsplus_seek_to_cmpf_resource(int fd, size_t *size)
{
+ cl_error_t status = CL_SUCCESS;
hfsPlusResourceHeader resourceHeader;
hfsPlusResourceMap resourceMap;
hfsPlusResourceType resourceType;
@@ -735,16 +763,15 @@ static cl_error_t hfsplus_seek_to_cmpf_resource(int fd, size_t *size)
int curInstanceIdx = 0;
size_t dataOffset;
uint32_t dataLength;
- cl_error_t ret = CL_SUCCESS;
if (!size) {
- ret = CL_ENULLARG;
+ status = CL_ENULLARG;
goto done;
}
if (cli_readn(fd, &resourceHeader, sizeof(resourceHeader)) != sizeof(resourceHeader)) {
cli_dbgmsg("hfsplus_seek_to_cmpf_resource: Failed to read resource header from temporary file\n");
- ret = CL_EREAD;
+ status = CL_EREAD;
goto done;
}
@@ -757,13 +784,13 @@ static cl_error_t hfsplus_seek_to_cmpf_resource(int fd, size_t *size)
if (lseek(fd, resourceHeader.mapOffset, SEEK_SET) != resourceHeader.mapOffset) {
cli_dbgmsg("hfsplus_seek_to_cmpf_resource: Failed to seek to map in temporary file\n");
- ret = CL_ESEEK;
+ status = CL_ESEEK;
goto done;
}
if (cli_readn(fd, &resourceMap, sizeof(resourceMap)) != sizeof(resourceMap)) {
cli_dbgmsg("hfsplus_seek_to_cmpf_resource: Failed to read resource map from temporary file\n");
- ret = CL_EREAD;
+ status = CL_EREAD;
goto done;
}
@@ -775,7 +802,7 @@ static cl_error_t hfsplus_seek_to_cmpf_resource(int fd, size_t *size)
for (i = 0; i < resourceMap.typeCount + 1; ++i) {
if (cli_readn(fd, &resourceType, sizeof(resourceType)) != sizeof(resourceType)) {
cli_dbgmsg("hfsplus_seek_to_cmpf_resource: Failed to read resource type from temporary file\n");
- ret = CL_EREAD;
+ status = CL_EREAD;
goto done;
}
resourceType.instanceCount = be16_to_host(resourceType.instanceCount);
@@ -784,7 +811,7 @@ static cl_error_t hfsplus_seek_to_cmpf_resource(int fd, size_t *size)
if (memcmp(resourceType.type, "cmpf", 4) == 0) {
if (cmpfInstanceIdx != -1) {
cli_dbgmsg("hfsplus_seek_to_cmpf_resource: There are several cmpf resource types in the file\n");
- ret = CL_EFORMAT;
+ status = CL_EFORMAT;
goto done;
}
@@ -797,19 +824,19 @@ static cl_error_t hfsplus_seek_to_cmpf_resource(int fd, size_t *size)
if (cmpfInstanceIdx < 0) {
cli_dbgmsg("hfsplus_seek_to_cmpf_resource: Didn't find cmpf resource type\n");
- ret = CL_EFORMAT;
+ status = CL_EFORMAT;
goto done;
}
if (lseek(fd, cmpfInstanceIdx * sizeof(hfsPlusReferenceEntry), SEEK_CUR) < 0) {
cli_dbgmsg("hfsplus_seek_to_cmpf_resource: Failed to seek to instance index\n");
- ret = CL_ESEEK;
+ status = CL_ESEEK;
goto done;
}
if (cli_readn(fd, &entry, sizeof(entry)) != sizeof(entry)) {
cli_dbgmsg("hfsplus_seek_to_cmpf_resource: Failed to read resource entry from temporary file\n");
- ret = CL_EREAD;
+ status = CL_EREAD;
goto done;
}
@@ -817,45 +844,60 @@ static cl_error_t hfsplus_seek_to_cmpf_resource(int fd, size_t *size)
if (lseek(fd, resourceHeader.dataOffset + dataOffset, SEEK_SET) < 0) {
cli_dbgmsg("hfsplus_seek_to_cmpf_resource: Failed to seek to data offset\n");
- ret = CL_ESEEK;
+ status = CL_ESEEK;
goto done;
}
if (cli_readn(fd, &dataLength, sizeof(dataLength)) != sizeof(dataLength)) {
cli_dbgmsg("hfsplus_seek_to_cmpf_resource: Failed to read data length from temporary file\n");
- ret = CL_EREAD;
+ status = CL_EREAD;
goto done;
}
*size = be32_to_host(dataLength);
+
done:
- return ret;
+ return status;
}
-static int hfsplus_read_block_table(int fd, uint32_t *numBlocks, hfsPlusResourceBlockTable **table)
+/**
+ * @brief Read the table from the provided file.
+ *
+ * The caller is responsible for freeing the table.
+ *
+ * @param fd File descriptor of the file to read from.
+ * @param [out] numBlocks Number of blocks in the table, as determined from reading the file.
+ * @param [out] table Will be allocated and populated with table data.
+ * @return cl_error_t CL_SUCCESS on success, CL_E* on failure.
+ */
+static cl_error_t hfsplus_read_block_table(int fd, uint32_t *numBlocks, hfsPlusResourceBlockTable **table)
{
+ cl_error_t status = CL_SUCCESS;
uint32_t i;
if (!table || !numBlocks) {
- return CL_ENULLARG;
+ status = CL_ENULLARG;
+ goto done;
}
if (cli_readn(fd, numBlocks, sizeof(*numBlocks)) != sizeof(*numBlocks)) {
cli_dbgmsg("hfsplus_read_block_table: Failed to read block count\n");
- return CL_EREAD;
+ status = CL_EREAD;
+ goto done;
}
*numBlocks = le32_to_host(*numBlocks); // Let's do a little little endian just for fun, shall we?
*table = cli_malloc(sizeof(hfsPlusResourceBlockTable) * *numBlocks);
if (!*table) {
cli_dbgmsg("hfsplus_read_block_table: Failed to allocate memory for block table\n");
- return CL_EMEM;
+ status = CL_EMEM;
+ goto done;
}
if (cli_readn(fd, *table, *numBlocks * sizeof(hfsPlusResourceBlockTable)) != *numBlocks * sizeof(hfsPlusResourceBlockTable)) {
cli_dbgmsg("hfsplus_read_block_table: Failed to read table\n");
- free(*table);
- return CL_EREAD;
+ status = CL_EREAD;
+ goto done;
}
for (i = 0; i < *numBlocks; ++i) {
@@ -863,15 +905,21 @@ static int hfsplus_read_block_table(int fd, uint32_t *numBlocks, hfsPlusResource
(*table)[i].length = le32_to_host((*table)[i].length);
}
- return CL_SUCCESS;
+done:
+ if (CL_SUCCESS != status) {
+ if (NULL != table) {
+ free(*table);
+ *table = NULL;
+ }
+ }
+ return status;
}
/* Given the catalog and other details, scan all the volume contents */
static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHeader, hfsHeaderRecord *catHeader,
hfsHeaderRecord *extHeader, hfsHeaderRecord *attrHeader, const char *dirname)
{
- cl_error_t ret = CL_SUCCESS;
- unsigned int has_alerts = 0;
+ cl_error_t status = CL_SUCCESS;
uint32_t thisNode, nodeLimit, nodesScanned = 0;
uint16_t nodeSize, recordNum, topOfOffsets;
uint16_t recordStart, nextDist, nextStart;
@@ -879,9 +927,14 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
const uint8_t COMPRESSED_ATTR[] = {0, 'c', 0, 'o', 0, 'm', 0, '.', 0, 'a', 0, 'p', 0, 'p', 0, 'l', 0, 'e', 0, '.', 0, 'd', 0, 'e', 0, 'c', 0, 'm', 0, 'p', 0, 'f', 0, 's'};
char *tmpname = NULL;
uint8_t *uncompressed = NULL;
+ char *resourceFile = NULL;
+ int ifd = -1;
int ofd = -1;
char *name_utf8 = NULL;
size_t name_utf8_size = 0;
+ bool extracted_file = false;
+
+ hfsPlusResourceBlockTable *table = NULL;
nodeLimit = MIN(catHeader->totalNodes, HFSPLUS_NODE_LIMIT);
thisNode = catHeader->firstLeafNode;
@@ -898,23 +951,23 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
/* Walk catalog leaf nodes, and scan contents of each */
/* Because we want to scan them all, the index nodes add no value */
- while (ret == CL_SUCCESS) {
+ while (status == CL_SUCCESS) {
hfsNodeDescriptor nodeDesc;
if (thisNode == 0) {
cli_dbgmsg("hfsplus_walk_catalog: reached end of leaf nodes.\n");
- break;
+ goto done;
}
if (nodesScanned++ > nodeLimit) {
cli_dbgmsg("hfsplus_walk_catalog: node scan limit reached.\n");
- break;
+ goto done;
}
/* fetch node into buffer */
- ret = hfsplus_fetch_node(ctx, volHeader, catHeader, extHeader, &(volHeader->catalogFile), thisNode, nodeBuf);
- if (ret != CL_SUCCESS) {
+ status = hfsplus_fetch_node(ctx, volHeader, catHeader, extHeader, &(volHeader->catalogFile), thisNode, nodeBuf);
+ if (status != CL_SUCCESS) {
cli_dbgmsg("hfsplus_walk_catalog: node fetch failed.\n");
- break;
+ goto done;
}
memcpy(&nodeDesc, nodeBuf, 14);
@@ -923,13 +976,13 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
nodedescriptor_print("leaf node", &nodeDesc);
if ((nodeDesc.kind != HFS_NODEKIND_LEAF) || (nodeDesc.height != 1)) {
cli_dbgmsg("hfsplus_walk_catalog: invalid leaf node!\n");
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
if ((nodeSize / 4) < nodeDesc.numRecords) {
cli_dbgmsg("hfsplus_walk_catalog: too many leaf records for one node!\n");
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
/* Walk this node's records and scan */
@@ -948,8 +1001,8 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
/* Check record location */
if ((nextStart > topOfOffsets - 1) || (nextStart < recordStart)) {
cli_dbgmsg("hfsplus_walk_catalog: bad record location %x for %u!\n", nextStart, recordNum);
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
recordStart = nextStart;
/* Get record key length */
@@ -959,8 +1012,8 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
if (recordStart + keylen + 4 >= topOfOffsets) {
cli_dbgmsg("hfsplus_walk_catalog: key too long for location %x for %u!\n",
nextStart, recordNum);
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
/* Collect filename */
if (keylen >= 6) {
@@ -993,8 +1046,8 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
/* Check file record location */
if (recordStart + keylen + 2 + sizeof(hfsPlusCatalogFile) >= topOfOffsets) {
cli_dbgmsg("hfsplus_walk_catalog: not enough bytes for file record!\n");
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
memcpy(&fileRec, &(nodeBuf[recordStart + keylen + 2]), sizeof(hfsPlusCatalogFile));
@@ -1004,7 +1057,7 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
if ((fileRec.permissions.fileMode & HFS_MODE_TYPEMASK) == HFS_MODE_FILE) {
int compressed = 0;
uint8_t attribute[8192];
- unsigned attributeSize = sizeof(attribute);
+ size_t attributeSize = sizeof(attribute);
/* Convert forks and scan */
forkdata_to_host(&(fileRec.dataFork));
@@ -1022,8 +1075,8 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
if (attributeSize < sizeof(header)) {
cli_warnmsg("hfsplus_walk_catalog: Error: Compression attribute size is less than the compression header\n");
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
memcpy(&header, attribute, sizeof(header));
@@ -1038,15 +1091,15 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
if (header.magic != DECMPFS_HEADER_MAGIC) {
cli_dbgmsg("hfsplus_walk_catalog: Unexpected magic value for compression header: 0x%08x\n", header.magic);
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
/* open file */
- ret = cli_gentempfd(dirname, &tmpname, &ofd);
- if (ret != CL_SUCCESS) {
+ status = cli_gentempfd(dirname, &tmpname, &ofd);
+ if (status != CL_SUCCESS) {
cli_dbgmsg("hfsplus_walk_catalog: Cannot generate temporary file.\n");
- break;
+ goto done;
}
cli_dbgmsg("Found compressed file type %u size %" PRIu64 "\n", header.compressionType, header.fileSize);
@@ -1055,16 +1108,15 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
size_t written;
if (attributeSize < sizeof(header) + 1) {
cli_dbgmsg("hfsplus_walk_catalog: Unexpected end of stream, no compression flag\n");
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
if ((attribute[sizeof(header)] & 0x0f) == 0x0f) { // Data is stored uncompressed
if (attributeSize - sizeof(header) - 1 != header.fileSize) {
cli_dbgmsg("hfsplus_walk_catalog: Expected file size different from size of data available\n");
- free(tmpname);
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
written = cli_writen(ofd, &attribute[sizeof(header) + 1], header.fileSize);
@@ -1074,15 +1126,15 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
if (header.fileSize > 65536) {
cli_dbgmsg("hfsplus_walk_catalog: Uncompressed file seems too big, something is probably wrong\n");
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
uncompressed = malloc(header.fileSize);
if (!uncompressed) {
cli_dbgmsg("hfsplus_walk_catalog: Failed to allocate memory for the uncompressed file contents\n");
- ret = CL_EMEM;
- break;
+ status = CL_EMEM;
+ goto done;
}
stream.zalloc = Z_NULL;
@@ -1106,37 +1158,37 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
cli_dbgmsg("hfsplus_walk_catalog: inflateinit2: zlib stream error!\n");
break;
default:
- cli_dbgmsg("hfsplus_walk_catalog: inflateInit2: unknown error %d\n", ret);
+ cli_dbgmsg("hfsplus_walk_catalog: inflateInit2: unknown error %d\n", z_ret);
break;
}
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
z_ret = inflate(&stream, Z_NO_FLUSH);
if (z_ret != Z_OK && z_ret != Z_STREAM_END) {
- cli_dbgmsg("hfsplus_walk_catalog: inflateSync failed to extract compressed stream (%d)\n", ret);
- ret = CL_EFORMAT;
- break;
+ cli_dbgmsg("hfsplus_walk_catalog: inflateSync failed to extract compressed stream (%d)\n", z_ret);
+ status = CL_EFORMAT;
+ goto done;
}
z_ret = inflateEnd(&stream);
if (z_ret == Z_STREAM_ERROR) {
- cli_dbgmsg("hfsplus_walk_catalog: inflateEnd failed (%d)\n", ret);
+ cli_dbgmsg("hfsplus_walk_catalog: inflateEnd failed (%d)\n", z_ret);
}
written = cli_writen(ofd, uncompressed, header.fileSize);
+
free(uncompressed);
uncompressed = NULL;
}
if (written != header.fileSize) {
cli_errmsg("hfsplus_walk_catalog: write error\n");
- ret = CL_EWRITE;
- break;
+ status = CL_EWRITE;
+ goto done;
}
- ret = CL_SUCCESS;
break;
}
case HFSPLUS_COMPRESSION_RESOURCE: {
@@ -1146,9 +1198,7 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
// Ideally we should check that there is only one
// resource, that its type is correct, and that its
// name is cmpf.
- char *resourceFile = NULL;
- int ifd = -1;
- size_t written = 0;
+ size_t written = 0;
// 4096 is an approximative value, there should be
// at least 16 (resource header) + 30 (map header) +
@@ -1156,44 +1206,42 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
// attribute)
if (fileRec.resourceFork.logicalSize < 4096) {
cli_dbgmsg("hfsplus_walk_catalog: Error: Expected more data in the compressed resource fork\n");
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
- if ((ret = hfsplus_scanfile(ctx, volHeader, extHeader, &(fileRec.resourceFork), dirname, &resourceFile, name_utf8)) != CL_SUCCESS) {
+ if ((status = hfsplus_scanfile(ctx, volHeader, extHeader, &(fileRec.resourceFork), dirname, &resourceFile, name_utf8)) != CL_SUCCESS) {
cli_dbgmsg("hfsplus_walk_catalog: Error while extracting the resource fork\n");
- if (resourceFile) {
- free(resourceFile);
- }
- break;
+ goto done;
}
if (NULL == resourceFile) {
cli_dbgmsg("hfsplus_walk_catalog: Error: hfsplus_scanfile returned no resource file\n");
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
}
- if ((ifd = safe_open(resourceFile, O_RDONLY | O_BINARY)) == -1) {
+ if (-1 == (ifd = safe_open(resourceFile, O_RDONLY | O_BINARY))) {
cli_dbgmsg("hfsplus_walk_catalog: Failed to open temporary file %s\n", resourceFile);
- ret = CL_EOPEN;
+ status = CL_EOPEN;
+ goto done;
} else {
size_t resourceLen;
- if ((ret = hfsplus_seek_to_cmpf_resource(ifd, &resourceLen)) != CL_SUCCESS) {
+ if (CL_SUCCESS != (status = hfsplus_seek_to_cmpf_resource(ifd, &resourceLen))) {
cli_dbgmsg("hfsplus_walk_catalog: Failed to find cmpf resource in resource fork\n");
} else {
- hfsPlusResourceBlockTable *table = NULL;
uint32_t numBlocks;
uint32_t dataOffset = lseek(ifd, 0, SEEK_CUR);
- if ((ret = hfsplus_read_block_table(ifd, &numBlocks, &table)) != CL_SUCCESS) {
+ if (CL_SUCCESS != (status = hfsplus_read_block_table(ifd, &numBlocks, &table))) {
cli_dbgmsg("hfsplus_walk_catalog: Failed to read block table\n");
} else {
uint8_t block[4096];
uint8_t uncompressed[4096];
unsigned curBlock;
- for (curBlock = 0; ret == CL_SUCCESS && curBlock < numBlocks; ++curBlock) {
+ for (curBlock = 0; status == CL_SUCCESS && curBlock < numBlocks; ++curBlock) {
+ int z_ret;
off_t blockOffset = dataOffset + table[curBlock].offset;
size_t curOffset;
size_t readLen;
@@ -1205,8 +1253,8 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
if (lseek(ifd, blockOffset, SEEK_SET) != blockOffset) {
cli_dbgmsg("hfsplus_walk_catalog: Failed to seek to beginning of block\n");
- ret = CL_ESEEK;
- break;
+ status = CL_ESEEK;
+ goto done;
}
for (curOffset = 0; curOffset < table[curBlock].length;) {
@@ -1217,8 +1265,8 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
if (cli_readn(ifd, block, readLen) != readLen) {
cli_dbgmsg("hfsplus_walk_catalog: Failed to read block from temporary file\n");
- ret = CL_EREAD;
- break;
+ status = CL_EREAD;
+ goto done;
}
if (streamBeginning) {
@@ -1234,10 +1282,10 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
stream.avail_out = sizeof(uncompressed);
stream.next_out = uncompressed;
- if ((ret = inflateInit2(&stream, 15)) != Z_OK) {
- cli_dbgmsg("hfsplus_walk_catalog: inflateInit2 failed (%d)\n", ret);
- ret = CL_EFORMAT;
- break;
+ if (Z_OK != (z_ret = inflateInit2(&stream, 15))) {
+ cli_dbgmsg("hfsplus_walk_catalog: inflateInit2 failed (%d)\n", z_ret);
+ status = CL_EFORMAT;
+ goto done;
}
}
}
@@ -1249,42 +1297,43 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
stream.next_out = uncompressed;
while (stream.avail_in > 0) {
- ret = inflate(&stream, Z_NO_FLUSH);
- if (ret != Z_OK && ret != Z_STREAM_END) {
- cli_dbgmsg("hfsplus_walk_catalog: Failed to extract (%d)\n", ret);
- ret = CL_EFORMAT;
- break;
+ z_ret = inflate(&stream, Z_NO_FLUSH);
+ if (z_ret != Z_OK && z_ret != Z_STREAM_END) {
+ cli_dbgmsg("hfsplus_walk_catalog: Failed to extract (%d)\n", z_ret);
+ status = CL_EFORMAT;
+ goto done;
}
- ret = CL_SUCCESS;
+
if (cli_writen(ofd, &uncompressed, sizeof(uncompressed) - stream.avail_out) != sizeof(uncompressed) - stream.avail_out) {
cli_dbgmsg("hfsplus_walk_catalog: Failed to write to temporary file\n");
- ret = CL_EWRITE;
- break;
+ status = CL_EWRITE;
+ goto done;
}
written += sizeof(uncompressed) - stream.avail_out;
stream.avail_out = sizeof(uncompressed);
stream.next_out = uncompressed;
+
+ extracted_file = true;
}
} else {
if (cli_writen(ofd, &block[streamBeginning ? 1 : 0], readLen - (streamBeginning ? 1 : 0)) != readLen - (streamBeginning ? 1 : 0)) {
cli_dbgmsg("hfsplus_walk_catalog: Failed to write to temporary file\n");
- ret = CL_EWRITE;
- break;
+ status = CL_EWRITE;
+ goto done;
}
written += readLen - (streamBeginning ? 1 : 0);
+
+ extracted_file = true;
}
curOffset += readLen;
streamBeginning = 0;
}
- if (ret == CL_SUCCESS) {
- if ((ret = inflateEnd(&stream)) != Z_OK) {
- cli_dbgmsg("hfsplus_walk_catalog: inflateEnd failed (%d)\n", ret);
- ret = CL_EFORMAT;
- } else {
- ret = CL_SUCCESS;
- }
+ if (Z_OK != (z_ret = inflateEnd(&stream))) {
+ cli_dbgmsg("hfsplus_walk_catalog: inflateEnd failed (%d)\n", z_ret);
+ status = CL_EFORMAT;
+ goto done;
}
}
@@ -1300,7 +1349,8 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
if (!ctx->engine->keeptmp) {
if (cli_unlink(resourceFile)) {
- ret = CL_EUNLINK;
+ status = CL_EUNLINK;
+ goto done;
}
}
free(resourceFile);
@@ -1315,74 +1365,49 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
}
if (tmpname) {
- if (ret == CL_SUCCESS) {
+ if (extracted_file) {
cli_dbgmsg("hfsplus_walk_catalog: Extracted to %s\n", tmpname);
- /* if successful so far, scan the output */
- ret = cli_magic_scan_desc(ofd, tmpname, ctx, name_utf8);
-
- if (ret == CL_VIRUS) {
- has_alerts = 1;
- if (SCAN_ALLMATCHES) {
- /* Continue scanning in SCAN_ALLMATCHES mode */
- cli_dbgmsg("hfsplus_walk_catalog: Compressed file alert, continuing");
- ret = CL_SUCCESS;
- }
+ /* Scan the extracted file */
+ status = cli_magic_scan_desc(ofd, tmpname, ctx, name_utf8);
+ if (status != CL_SUCCESS) {
+ goto done;
}
}
if (!ctx->engine->keeptmp) {
if (cli_unlink(tmpname)) {
- ret = CL_EUNLINK;
+ status = CL_EUNLINK;
+ goto done;
}
}
free(tmpname);
tmpname = NULL;
}
+
if (ofd >= 0) {
close(ofd);
ofd = -1;
}
-
- if (ret != CL_SUCCESS) {
- break;
- }
}
+ /* Scan data fork */
if (fileRec.dataFork.logicalSize) {
- ret = hfsplus_scanfile(ctx, volHeader, extHeader, &(fileRec.dataFork), dirname, NULL, name_utf8);
- }
- /* Check return code */
- if (ret == CL_VIRUS) {
- has_alerts = 1;
- if (SCAN_ALLMATCHES) {
- /* Continue scanning in SCAN_ALLMATCHES mode */
- cli_dbgmsg("hfsplus_walk_catalog: data fork alert, continuing");
- ret = CL_CLEAN;
+ status = hfsplus_scanfile(ctx, volHeader, extHeader, &(fileRec.dataFork), dirname, NULL, name_utf8);
+ if (status != CL_SUCCESS) {
+ cli_dbgmsg("hfsplus_walk_catalog: data fork retcode %d\n", status);
+ goto done;
}
}
- if (ret != CL_SUCCESS) {
- cli_dbgmsg("hfsplus_walk_catalog: data fork retcode %d\n", ret);
- break;
- }
/* Scan resource fork */
if (fileRec.resourceFork.logicalSize) {
- ret = hfsplus_scanfile(ctx, volHeader, extHeader, &(fileRec.resourceFork), dirname, NULL, name_utf8);
- }
- /* Check return code */
- if (ret == CL_VIRUS) {
- has_alerts = 1;
- if (SCAN_ALLMATCHES) {
- /* Continue scanning in SCAN_ALLMATCHES mode */
- cli_dbgmsg("hfsplus_walk_catalog: resource fork alert, continuing");
- ret = CL_CLEAN;
+ status = hfsplus_scanfile(ctx, volHeader, extHeader, &(fileRec.resourceFork), dirname, NULL, name_utf8);
+ if (status != CL_SUCCESS) {
+ cli_dbgmsg("hfsplus_walk_catalog: resource fork retcode %d", status);
+ goto done;
}
}
- if (ret != CL_SUCCESS) {
- cli_dbgmsg("hfsplus_walk_catalog: resource fork retcode %d", ret);
- break;
- }
} else {
cli_dbgmsg("hfsplus_walk_catalog: record mode %o is not File\n", fileRec.permissions.fileMode);
}
@@ -1392,38 +1417,65 @@ static cl_error_t hfsplus_walk_catalog(cli_ctx *ctx, hfsPlusVolumeHeader *volHea
name_utf8 = NULL;
}
}
- /* if return code, exit loop, message already logged */
- if (ret != CL_SUCCESS) {
- break;
- }
/* After that, proceed to next node */
if (thisNode == nodeDesc.fLink) {
- /* Future heuristic */
+ /* TODO: Add heuristic alert? */
cli_warnmsg("hfsplus_walk_catalog: simple cycle detected!\n");
- ret = CL_EFORMAT;
- break;
+ status = CL_EFORMAT;
+ goto done;
} else {
thisNode = nodeDesc.fLink;
}
}
- free(nodeBuf);
+done:
+ if (table) {
+ free(table);
+ table = NULL;
+ }
+ if (-1 != ifd) {
+ close(ifd);
+ }
+ if (-1 != ofd) {
+ close(ofd);
+ }
+ if (NULL != resourceFile) {
+ if (!ctx->engine->keeptmp) {
+ (void)cli_unlink(resourceFile);
+ }
+ free(resourceFile);
+ }
+ if (NULL != tmpname) {
+ if (!ctx->engine->keeptmp) {
+ if (cli_unlink(tmpname)) {
+ status = CL_EUNLINK;
+ goto done;
+ }
+ }
+
+ free(tmpname);
+ }
+ if (NULL != nodeBuf) {
+ free(nodeBuf);
+ }
if (NULL != name_utf8) {
free(name_utf8);
}
-
- if (has_alerts) {
- ret = CL_VIRUS;
+ if (NULL != name_utf8) {
+ free(name_utf8);
+ name_utf8 = NULL;
}
- return ret;
+
+ return status;
}
/* Base scan function for scanning HFS+ or HFSX partitions */
cl_error_t cli_scanhfsplus(cli_ctx *ctx)
{
+ cl_error_t status = CL_SUCCESS;
+ cl_error_t ret;
char *targetdir = NULL;
- cl_error_t ret = CL_SUCCESS;
hfsPlusVolumeHeader *volHeader = NULL;
hfsNodeDescriptor catFileDesc;
hfsHeaderRecord catFileHeader;
@@ -1435,14 +1487,15 @@ cl_error_t cli_scanhfsplus(cli_ctx *ctx)
if (!ctx || !ctx->fmap) {
cli_errmsg("cli_scanhfsplus: Invalid context\n");
- return CL_ENULLARG;
+ status = CL_ENULLARG;
+ goto done;
}
cli_dbgmsg("cli_scanhfsplus: scanning partition content\n");
/* first, read volume header contents */
- ret = hfsplus_volumeheader(ctx, &volHeader);
- if (ret != CL_SUCCESS) {
- goto freeHeader;
+ status = hfsplus_volumeheader(ctx, &volHeader);
+ if (status != CL_SUCCESS) {
+ goto done;
}
/*
@@ -1456,14 +1509,14 @@ cli_dbgmsg("sizeof(hfsNodeDescriptor) is %lu\n", sizeof(hfsNodeDescriptor));
*/
/* Get root node (header node) of extent overflow file */
- ret = hfsplus_readheader(ctx, volHeader, &extentFileDesc, &extentFileHeader, HFS_FILETREE_EXTENTS, "extentFile");
- if (ret != CL_SUCCESS) {
- goto freeHeader;
+ status = hfsplus_readheader(ctx, volHeader, &extentFileDesc, &extentFileHeader, HFS_FILETREE_EXTENTS, "extentFile");
+ if (status != CL_SUCCESS) {
+ goto done;
}
/* Get root node (header node) of catalog file */
- ret = hfsplus_readheader(ctx, volHeader, &catFileDesc, &catFileHeader, HFS_FILETREE_CATALOG, "catalogFile");
- if (ret != CL_SUCCESS) {
- goto freeHeader;
+ status = hfsplus_readheader(ctx, volHeader, &catFileDesc, &catFileHeader, HFS_FILETREE_CATALOG, "catalogFile");
+ if (status != CL_SUCCESS) {
+ goto done;
}
/* Get root node (header node) of attributes file */
@@ -1472,48 +1525,50 @@ cli_dbgmsg("sizeof(hfsNodeDescriptor) is %lu\n", sizeof(hfsNodeDescriptor));
hasAttributesFileHeader = 1;
} else {
hasAttributesFileHeader = 0;
- ret = CL_SUCCESS;
}
/* Create temp folder for contents */
if (!(targetdir = cli_gentemp_with_prefix(ctx->sub_tmpdir, "hfsplus-tmp"))) {
cli_errmsg("cli_scanhfsplus: cli_gentemp failed\n");
- ret = CL_ETMPDIR;
- goto freeHeader;
+ status = CL_ETMPDIR;
+ goto done;
}
if (mkdir(targetdir, 0700)) {
cli_errmsg("cli_scanhfsplus: Cannot create temporary directory %s\n", targetdir);
- ret = CL_ETMPDIR;
- goto freeDirname;
+ status = CL_ETMPDIR;
+ goto done;
}
cli_dbgmsg("cli_scanhfsplus: Extracting into %s\n", targetdir);
/* Can build and scan catalog file if we want ***
ret = hfsplus_scanfile(ctx, volHeader, &extentFileHeader, &(volHeader->catalogFile), targetdir);
*/
- if (ret == CL_SUCCESS) {
- ret = hfsplus_validate_catalog(ctx, volHeader, &catFileHeader);
- if (ret == CL_SUCCESS) {
- cli_dbgmsg("cli_scanhfsplus: validation successful\n");
- } else {
- cli_dbgmsg("cli_scanhfsplus: validation returned %d : %s\n", ret, cl_strerror(ret));
- }
+
+ status = hfsplus_validate_catalog(ctx, volHeader, &catFileHeader);
+ if (status == CL_SUCCESS) {
+ cli_dbgmsg("cli_scanhfsplus: validation successful\n");
+ } else {
+ cli_dbgmsg("cli_scanhfsplus: validation returned %d : %s\n", status, cl_strerror(status));
+ goto done;
}
/* Walk through catalog to identify files to scan */
- if (ret == CL_SUCCESS) {
- ret = hfsplus_walk_catalog(ctx, volHeader, &catFileHeader, &extentFileHeader, hasAttributesFileHeader ? &attributesFileHeader : NULL, targetdir);
- cli_dbgmsg("cli_scanhfsplus: walk catalog finished\n");
+ status = hfsplus_walk_catalog(ctx, volHeader, &catFileHeader, &extentFileHeader, hasAttributesFileHeader ? &attributesFileHeader : NULL, targetdir);
+ if (status != CL_SUCCESS) {
+ goto done;
}
- /* Clean up extracted content, if needed */
- if (!ctx->engine->keeptmp) {
- cli_rmdirs(targetdir);
+done:
+ if (NULL != targetdir) {
+ /* Clean up extracted content, if needed */
+ if (!ctx->engine->keeptmp) {
+ (void)cli_rmdirs(targetdir);
+ }
+ free(targetdir);
+ }
+ if (NULL != volHeader) {
+ free(volHeader);
}
-freeDirname:
- free(targetdir);
-freeHeader:
- free(volHeader);
- return ret;
+ return status;
}
diff --git a/libclamav/hwp.c b/libclamav/hwp.c
index c03726ed75..cab1e4d058 100644
--- a/libclamav/hwp.c
+++ b/libclamav/hwp.c
@@ -1856,15 +1856,10 @@ static cl_error_t hwp3_cb(void *cbdata, int fd, const char *filepath, cli_ctx *c
while (!last && ((ret = parsehwp3_infoblk_1(ctx, map, &offset, &last)) == CL_SUCCESS)) continue;
/* scan the uncompressed stream - both compressed and uncompressed cases [ALLMATCH] */
- if ((ret == CL_SUCCESS) || ((SCAN_ALLMATCHES) && (ret == CL_VIRUS))) {
- cl_error_t subret = ret;
- size_t dlen = offset - start;
+ if (ret == CL_SUCCESS) {
+ size_t dlen = offset - start;
ret = cli_magic_scan_nested_fmap_type(map, start, dlen, ctx, CL_TYPE_ANY, NULL);
- // ret = cli_magic_scan_nested_fmap_type(map, 0, 0, ctx, CL_TYPE_ANY);
-
- if (ret == CL_SUCCESS)
- ret = subret;
}
if (dmap)
diff --git a/libclamav/ishield.c b/libclamav/ishield.c
index dcdff6458a..806eddb77a 100644
--- a/libclamav/ishield.c
+++ b/libclamav/ishield.c
@@ -184,29 +184,33 @@ struct IS_FILEITEM {
#pragma pack
#endif
-static int is_dump_and_scan(cli_ctx *ctx, off_t off, size_t fsize);
+static cl_error_t is_dump_and_scan(cli_ctx *ctx, off_t off, size_t fsize);
static const uint8_t skey[] = {0xec, 0xca, 0x79, 0xf8}; /* ~0x13, ~0x35, ~0x86, ~0x07 */
/* Extracts the content of MSI based IS */
-int cli_scanishield_msi(cli_ctx *ctx, off_t off)
+cl_error_t cli_scanishield_msi(cli_ctx *ctx, off_t off)
{
+ cl_error_t ret;
const uint8_t *buf;
unsigned int fcount, scanned = 0;
- int ret;
fmap_t *map = ctx->fmap;
cli_dbgmsg("in ishield-msi\n");
if (!(buf = fmap_need_off_once(map, off, 0x20))) {
cli_dbgmsg("ishield-msi: short read for header\n");
- return CL_CLEAN;
+ return CL_SUCCESS;
}
+
off += 0x20;
- if (cli_readint32(buf + 8) | cli_readint32(buf + 0xc) | cli_readint32(buf + 0x10) | cli_readint32(buf + 0x14) | cli_readint32(buf + 0x18) | cli_readint32(buf + 0x1c))
- return CL_CLEAN;
+ if (cli_readint32(buf + 8) | cli_readint32(buf + 0xc) | cli_readint32(buf + 0x10) | cli_readint32(buf + 0x14) | cli_readint32(buf + 0x18) | cli_readint32(buf + 0x1c)) {
+ return CL_SUCCESS;
+ }
+
if (!(fcount = cli_readint32(buf))) {
cli_dbgmsg("ishield-msi: no files?\n");
- return CL_CLEAN;
+ return CL_SUCCESS;
}
+
while (fcount--) {
struct IS_FB fb;
uint8_t obuf[BUFSIZ], *key = (uint8_t *)&fb.fname;
@@ -219,15 +223,18 @@ int cli_scanishield_msi(cli_ctx *ctx, off_t off)
if (fmap_readn(map, &fb, off, sizeof(fb)) != sizeof(fb)) {
cli_dbgmsg("ishield-msi: short read for fileblock\n");
- return CL_CLEAN;
+ return CL_SUCCESS;
}
+
off += sizeof(fb);
fb.fname[sizeof(fb.fname) - 1] = '\0';
- csize = le64_to_host(fb.csize);
+
+ csize = le64_to_host(fb.csize);
if (!CLI_ISCONTAINED_0_TO(map->len, off, csize)) {
cli_dbgmsg("ishield-msi: next stream is out of file, giving up\n");
- return CL_CLEAN;
+ return CL_SUCCESS;
}
+
if (ctx->engine->maxfilesize && csize > ctx->engine->maxfilesize) {
cli_dbgmsg("ishield-msi: skipping stream due to size limits (%lu vs %lu)\n", (unsigned long int)csize, (unsigned long int)ctx->engine->maxfilesize);
off += csize;
@@ -235,7 +242,9 @@ int cli_scanishield_msi(cli_ctx *ctx, off_t off)
}
keylen = strlen((const char *)key);
- if (!keylen) return CL_CLEAN;
+ if (!keylen) {
+ return CL_SUCCESS;
+ }
filename = cli_strdup((const char *)key);
@@ -247,6 +256,7 @@ int cli_scanishield_msi(cli_ctx *ctx, off_t off)
}
return CL_EMEM;
}
+
if ((ofd = open(tempfile, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR)) < 0) {
cli_dbgmsg("ishield-msi: failed to create file %s\n", tempfile);
free(tempfile);
@@ -256,11 +266,15 @@ int cli_scanishield_msi(cli_ctx *ctx, off_t off)
return CL_ECREAT;
}
- for (i = 0; i < keylen; i++)
+ for (i = 0; i < keylen; i++) {
key[i] ^= skey[i & 3];
+ }
+
memset(&z, 0, sizeof(z));
inflateInit(&z);
+
ret = CL_SUCCESS;
+
while (csize) {
uint8_t buf2[BUFSIZ];
z.avail_in = MIN(csize, sizeof(buf2));
@@ -326,8 +340,9 @@ int cli_scanishield_msi(cli_ctx *ctx, off_t off)
free(filename);
}
- if (ret != CL_CLEAN)
+ if (ret != CL_SUCCESS) {
return ret;
+ }
scanned++;
if (ctx->engine->maxfiles && scanned >= ctx->engine->maxfiles) {
@@ -335,7 +350,7 @@ int cli_scanishield_msi(cli_ctx *ctx, off_t off)
return CL_EMAXFILES;
}
}
- return CL_CLEAN;
+ return CL_SUCCESS;
}
struct IS_CABSTUFF {
@@ -350,23 +365,22 @@ struct IS_CABSTUFF {
};
static void md5str(uint8_t *sum);
-static int is_parse_hdr(cli_ctx *ctx, struct IS_CABSTUFF *c);
-static int is_extract_cab(cli_ctx *ctx, uint64_t off, uint64_t size, uint64_t csize);
+static cl_error_t is_parse_hdr(cli_ctx *ctx, struct IS_CABSTUFF *c);
+static cl_error_t is_extract_cab(cli_ctx *ctx, uint64_t off, uint64_t size, uint64_t csize);
/* Extract the content of older (non-MSI) IS */
-int cli_scanishield(cli_ctx *ctx, off_t off, size_t sz)
+cl_error_t cli_scanishield(cli_ctx *ctx, off_t off, size_t sz)
{
+ cl_error_t ret = CL_SUCCESS;
const char *fname, *path, *version, *strsz, *data;
char *eostr;
- int ret = CL_CLEAN;
long fsize;
off_t coff = off;
struct IS_CABSTUFF c = {NULL, -1, 0, 0};
fmap_t *map = ctx->fmap;
unsigned fc = 0;
- int virus_found = 0;
- while (ret == CL_CLEAN) {
+ while (ret == CL_SUCCESS) {
fname = fmap_need_offstr(map, coff, 2048);
if (!fname) break;
coff += strlen(fname) + 1;
@@ -392,14 +406,11 @@ int cli_scanishield(cli_ctx *ctx, off_t off, size_t sz)
(size_t)(data - fname) >= sz - fsize) break;
cli_dbgmsg("ishield: @%lx found file %s (%s) - version %s - size %lu\n", (unsigned long int)coff, fname, path, version, (unsigned long int)fsize);
- if (cli_matchmeta(ctx, fname, fsize, fsize, 0, fc++, 0, NULL) == CL_VIRUS) {
- if (!SCAN_ALLMATCHES) {
- ret = CL_VIRUS;
- break;
- }
- ret = CL_CLEAN;
- virus_found = 1;
+ if (CL_SUCCESS != cli_matchmeta(ctx, fname, fsize, fsize, 0, fc++, 0, NULL)) {
+ ret = CL_VIRUS;
+ break;
}
+
sz -= (data - fname) + fsize;
if (!strncasecmp(fname, "data", 4)) {
@@ -441,47 +452,55 @@ int cli_scanishield(cli_ctx *ctx, off_t off, size_t sz)
coff += fsize;
}
- if (ret == CL_CLEAN && (c.cabcnt || c.hdr != -1)) {
- if ((ret = is_parse_hdr(ctx, &c)) == CL_CLEAN) {
+ if ((ret == CL_SUCCESS) &&
+ (c.cabcnt || c.hdr != -1)) {
+
+ if (CL_SUCCESS == (ret = is_parse_hdr(ctx, &c))) {
unsigned int i;
if (c.hdr != -1) {
cli_dbgmsg("ishield: scanning data1.hdr\n");
ret = is_dump_and_scan(ctx, c.hdr, c.hdrsz);
}
- for (i = 0; i < c.cabcnt && ret == CL_CLEAN; i++) {
+ for (i = 0; i < c.cabcnt && ret == CL_SUCCESS; i++) {
cli_dbgmsg("ishield: scanning data%u.cab\n", c.cabs[i].cabno);
ret = is_dump_and_scan(ctx, c.cabs[i].off, c.cabs[i].sz);
}
- } else if (ret == CL_BREAK)
- ret = CL_CLEAN;
+
+ } else if (ret == CL_BREAK) {
+ ret = CL_SUCCESS;
+ }
+ }
+
+ if (c.cabs) {
+ free(c.cabs);
}
- if (c.cabs) free(c.cabs);
- if (virus_found != 0)
- return CL_VIRUS;
return ret;
}
/* Utility func to scan a fd @ a given offset and size */
-static int is_dump_and_scan(cli_ctx *ctx, off_t off, size_t fsize)
+static cl_error_t is_dump_and_scan(cli_ctx *ctx, off_t off, size_t fsize)
{
char *fname;
const char *buf;
- int ofd, ret = CL_CLEAN;
+ cl_error_t ofd, ret = CL_SUCCESS;
fmap_t *map = ctx->fmap;
if (!fsize) {
cli_dbgmsg("ishield: skipping empty file\n");
- return CL_CLEAN;
+ return CL_SUCCESS;
}
- if (!(fname = cli_gentemp(ctx->sub_tmpdir)))
+
+ if (!(fname = cli_gentemp(ctx->sub_tmpdir))) {
return CL_EMEM;
+ }
if ((ofd = open(fname, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR)) < 0) {
cli_errmsg("ishield: failed to create file %s\n", fname);
free(fname);
return CL_ECREAT;
}
+
while (fsize) {
size_t rd = MIN(fsize, map->pgsz);
if (!(buf = fmap_need_off_once(map, off, rd))) {
@@ -496,6 +515,7 @@ static int is_dump_and_scan(cli_ctx *ctx, off_t off, size_t fsize)
fsize -= rd;
off += rd;
}
+
if (!fsize) {
cli_dbgmsg("ishield: extracted to %s\n", fname);
if (lseek(ofd, 0, SEEK_SET) == -1) {
@@ -504,15 +524,21 @@ static int is_dump_and_scan(cli_ctx *ctx, off_t off, size_t fsize)
}
ret = cli_magic_scan_desc(ofd, fname, ctx, NULL);
}
+
close(ofd);
- if (!ctx->engine->keeptmp)
- if (cli_unlink(fname)) ret = CL_EUNLINK;
+
+ if (!ctx->engine->keeptmp) {
+ if (cli_unlink(fname)) {
+ ret = CL_EUNLINK;
+ }
+ }
+
free(fname);
return ret;
}
/* Process data1.hdr and extracts all the available files from dataX.cab */
-static int is_parse_hdr(cli_ctx *ctx, struct IS_CABSTUFF *c)
+static cl_error_t is_parse_hdr(cli_ctx *ctx, struct IS_CABSTUFF *c)
{
uint32_t h1_data_off, objs_files_cnt, objs_dirs_off;
unsigned int off, i, scanned = 0;
@@ -526,26 +552,27 @@ static int is_parse_hdr(cli_ctx *ctx, struct IS_CABSTUFF *c)
if (!c->hdr || !c->hdrsz || !c->cabcnt) {
cli_dbgmsg("is_parse_hdr: inconsistent hdr, maybe a false match\n");
- return CL_CLEAN;
+ return CL_SUCCESS;
}
if (!(h1 = fmap_need_off(map, c->hdr, c->hdrsz))) {
cli_dbgmsg("is_parse_hdr: not enough room for H1\n");
- return CL_CLEAN;
+ return CL_SUCCESS;
}
+
hdr = (char *)h1;
h1_data_off = le32_to_host(h1->data_off);
objs = (struct IS_OBJECTS *)fmap_need_ptr(map, hdr + h1_data_off, sizeof(*objs));
if (!objs) {
cli_dbgmsg("is_parse_hdr: not enough room for OBJECTS\n");
- return CL_CLEAN;
+ return CL_SUCCESS;
}
cli_dbgmsg("is_parse_hdr: magic %x, unk1 %x, unk2 %x, data_off %x, data_sz %x\n",
h1->magic, h1->unk1, h1->unk2, h1_data_off, h1->data_sz);
if (le32_to_host(h1->magic) != 0x28635349) {
cli_dbgmsg("is_parse_hdr: bad magic. wrong version?\n");
- return CL_CLEAN;
+ return CL_SUCCESS;
}
fmap_unneed_ptr(map, h1, sizeof(*h1));
@@ -557,7 +584,7 @@ static int is_parse_hdr(cli_ctx *ctx, struct IS_CABSTUFF *c)
/* if(!CLI_ISCONTAINED(hdr, c->hdrsz, ((char *)cmp), sizeof(*cmp))) { */
/* cli_dbgmsg("is_extract: not enough room for COMPONENT\n"); */
/* free(hdr); */
- /* return CL_CLEAN; */
+ /* return CL_SUCCESS; */
/* } */
/* cli_errmsg("%06u\t%s\n", i, &hdr[le32_to_host(cmp->str_name_off) + h1_data_off]); */
/* spam_strarray(hdr, h1_data_off + cmp->sub_comp_offs_array, h1_data_off, cmp->sub_comp_cnt); */
@@ -630,7 +657,7 @@ static int is_parse_hdr(cli_ctx *ctx, struct IS_CABSTUFF *c)
else {
if (file_size) {
unsigned int j;
- int cabret = CL_CLEAN;
+ cl_error_t cabret = CL_SUCCESS;
if (ctx->engine->maxfilesize && file_csize > ctx->engine->maxfilesize) {
cli_dbgmsg("is_parse_hdr: skipping file due to size limits (%lu vs %lu)\n", (unsigned long int)file_csize, (unsigned long int)ctx->engine->maxfilesize);
@@ -652,18 +679,18 @@ static int is_parse_hdr(cli_ctx *ctx, struct IS_CABSTUFF *c)
}
cabret = is_extract_cab(ctx, file_stream_off + c->cabs[j].off, file_size, file_csize);
} else {
- ret = CL_CLEAN;
+ ret = CL_SUCCESS;
cli_dbgmsg("is_parse_hdr: stream out of file\n");
}
} else {
- ret = CL_CLEAN;
+ ret = CL_SUCCESS;
cli_dbgmsg("is_parse_hdr: data%u.cab not available\n", cabno);
}
if (cabret == CL_BREAK) {
- ret = CL_CLEAN;
- cabret = CL_CLEAN;
+ ret = CL_SUCCESS;
+ cabret = CL_SUCCESS;
}
- if (cabret != CL_CLEAN) {
+ if (cabret != CL_SUCCESS) {
if (file_name != emptyname)
fmap_unneed_ptr(map, (void *)file_name, strlen(file_name) + 1);
if (dir_name != emptyname)
@@ -684,7 +711,7 @@ static int is_parse_hdr(cli_ctx *ctx, struct IS_CABSTUFF *c)
fmap_unneed_ptr(map, (void *)dir_name, strlen(dir_name) + 1);
fmap_unneed_ptr(map, file, sizeof(*file));
} else {
- ret = CL_CLEAN;
+ ret = CL_SUCCESS;
cli_dbgmsg("is_parse_hdr: FILEITEM out of bounds\n");
}
off += sizeof(*file);
@@ -707,12 +734,13 @@ static void md5str(uint8_t *sum)
#define IS_CABBUFSZ 65536
-static int is_extract_cab(cli_ctx *ctx, uint64_t off, uint64_t size, uint64_t csize)
+static cl_error_t is_extract_cab(cli_ctx *ctx, uint64_t off, uint64_t size, uint64_t csize)
{
+ cl_error_t ret = CL_SUCCESS;
const uint8_t *inbuf;
uint8_t *outbuf;
char *tempfile;
- int ofd, ret = CL_CLEAN;
+ int ofd;
z_stream z;
uint64_t outsz = 0;
int success = 0;
diff --git a/libclamav/ishield.h b/libclamav/ishield.h
index 7bd11cfd55..e5c1e589e5 100644
--- a/libclamav/ishield.h
+++ b/libclamav/ishield.h
@@ -24,7 +24,7 @@
#include "others.h"
-int cli_scanishield_msi(cli_ctx *ctx, off_t off);
-int cli_scanishield(cli_ctx *ctx, off_t off, size_t sz);
+cl_error_t cli_scanishield_msi(cli_ctx *ctx, off_t off);
+cl_error_t cli_scanishield(cli_ctx *ctx, off_t off, size_t sz);
#endif
diff --git a/libclamav/iso9660.c b/libclamav/iso9660.c
index 9a74fbc70c..474e37b49b 100644
--- a/libclamav/iso9660.c
+++ b/libclamav/iso9660.c
@@ -54,13 +54,15 @@ static const void *needblock(const iso9660_t *iso, unsigned int block, int temp)
return fmap_need_off(ctx->fmap, iso->base_offset + loff, iso->blocksz);
}
-static int iso_scan_file(const iso9660_t *iso, unsigned int block, unsigned int len)
+static cl_error_t iso_scan_file(const iso9660_t *iso, unsigned int block, unsigned int len)
{
char *tmpf;
- int fd, ret = CL_SUCCESS;
+ int fd = -1;
+ cl_error_t ret = CL_SUCCESS;
- if (cli_gentempfd(iso->ctx->sub_tmpdir, &tmpf, &fd) != CL_SUCCESS)
+ if (cli_gentempfd(iso->ctx->sub_tmpdir, &tmpf, &fd) != CL_SUCCESS) {
return CL_ETMPFILE;
+ }
cli_dbgmsg("iso_scan_file: dumping to %s\n", tmpf);
while (len) {
@@ -81,8 +83,9 @@ static int iso_scan_file(const iso9660_t *iso, unsigned int block, unsigned int
block++;
}
- if (!len)
+ if (!len) {
ret = cli_magic_scan_desc(fd, tmpf, iso->ctx, iso->buf);
+ }
close(fd);
if (!iso->ctx->engine->keeptmp) {
@@ -117,18 +120,17 @@ static char *iso_string(iso9660_t *iso, const void *src, unsigned int len)
return iso->buf;
}
-static int iso_parse_dir(iso9660_t *iso, unsigned int block, unsigned int len)
+static cl_error_t iso_parse_dir(iso9660_t *iso, unsigned int block, unsigned int len)
{
- cli_ctx *ctx = iso->ctx;
- int ret = CL_CLEAN;
- int viruses_found = 0;
+ cli_ctx *ctx = iso->ctx;
+ cl_error_t ret = CL_SUCCESS;
if (len < 34) {
cli_dbgmsg("iso_parse_dir: Directory too small, skipping\n");
- return CL_CLEAN;
+ return CL_SUCCESS;
}
- for (; len && ret == CL_CLEAN; block++, len -= MIN(len, iso->blocksz)) {
+ for (; len && ret == CL_SUCCESS; block++, len -= MIN(len, iso->blocksz)) {
const uint8_t *dir, *dir_orig;
unsigned int dirsz;
@@ -137,15 +139,18 @@ static int iso_parse_dir(iso9660_t *iso, unsigned int block, unsigned int len)
return CL_BREAK;
}
- if (cli_hashset_contains(&iso->dir_blocks, block))
+ if (cli_hashset_contains(&iso->dir_blocks, block)) {
continue;
+ }
- if ((ret = cli_hashset_addkey(&iso->dir_blocks, block)) != CL_CLEAN)
+ if (CL_SUCCESS != (ret = cli_hashset_addkey(&iso->dir_blocks, block))) {
return ret;
+ }
dir = dir_orig = needblock(iso, block, 0);
- if (!dir)
- return CL_CLEAN;
+ if (!dir) {
+ return CL_SUCCESS;
+ }
for (dirsz = MIN(iso->blocksz, len);;) {
unsigned int entrysz = *dir, fileoff, filesz;
@@ -187,11 +192,8 @@ static int iso_parse_dir(iso9660_t *iso, unsigned int block, unsigned int len)
cli_dbgmsg("iso_parse_dir: %s '%s': off %x - size %x - flags %x - unit size %x - gap size %x - volume %u\n", (dir[25] & 2) ? "Directory" : "File", iso->buf, fileoff, filesz, dir[25], dir[26], dir[27], cli_readint32(&dir[28]) & 0xffff);
ret = cli_matchmeta(ctx, iso->buf, filesz, filesz, 0, 0, 0, NULL);
- if (ret == CL_VIRUS) {
- viruses_found = 1;
- if (!SCAN_ALLMATCHES)
- break;
- ret = CL_CLEAN;
+ if (ret != CL_SUCCESS) {
+ break;
}
if (dir[26] || dir[27])
@@ -201,16 +203,14 @@ static int iso_parse_dir(iso9660_t *iso, unsigned int block, unsigned int len)
if (dir[25] & 2) {
ret = iso_parse_dir(iso, fileoff, filesz);
} else {
- if (cli_checklimits("ISO9660", ctx, filesz, 0, 0) != CL_SUCCESS)
+ if (CL_SUCCESS != cli_checklimits("ISO9660", ctx, filesz, 0, 0)) {
cli_dbgmsg("iso_parse_dir: Skipping overlimit file\n");
- else
+ } else {
ret = iso_scan_file(iso, fileoff, filesz);
+ }
}
- if (ret == CL_VIRUS) {
- viruses_found = 1;
- if (!SCAN_ALLMATCHES)
- break;
- ret = CL_CLEAN;
+ if (ret != CL_SUCCESS) {
+ break;
}
}
dirsz -= entrysz;
@@ -219,35 +219,34 @@ static int iso_parse_dir(iso9660_t *iso, unsigned int block, unsigned int len)
fmap_unneed_ptr(ctx->fmap, dir_orig, iso->blocksz);
}
- if (viruses_found == 1)
- return CL_VIRUS;
+
return ret;
}
-int cli_scaniso(cli_ctx *ctx, size_t offset)
+cl_error_t cli_scaniso(cli_ctx *ctx, size_t offset)
{
const uint8_t *privol, *next;
iso9660_t iso;
int i;
if (offset < 32768)
- return CL_CLEAN; /* Need 16 sectors at least 2048 bytes long */
+ return CL_SUCCESS; /* Need 16 sectors at least 2048 bytes long */
privol = fmap_need_off(ctx->fmap, offset, 2448 + 6);
if (!privol)
- return CL_CLEAN;
+ return CL_SUCCESS;
next = (uint8_t *)cli_memstr((char *)privol + 2049, 2448 + 6 - 2049, "CD001", 5);
if (!next)
- return CL_CLEAN; /* Find next volume descriptor */
+ return CL_SUCCESS; /* Find next volume descriptor */
iso.sectsz = (next - privol) - 1;
if (iso.sectsz * 16 > offset)
- return CL_CLEAN; /* Need room for 16 system sectors */
+ return CL_SUCCESS; /* Need room for 16 system sectors */
iso.blocksz = cli_readint32(privol + 128) & 0xffff;
if (iso.blocksz != 512 && iso.blocksz != 1024 && iso.blocksz != 2048)
- return CL_CLEAN; /* Likely not a cdrom image */
+ return CL_SUCCESS; /* Likely not a cdrom image */
iso.base_offset = offset - iso.sectsz * 16;
iso.joliet = 0;
@@ -327,7 +326,7 @@ int cli_scaniso(cli_ctx *ctx, size_t offset)
if (privol[156 + 26] || privol[156 + 27]) {
cli_dbgmsg("cli_scaniso: Interleaved root directory is not supported\n");
- return CL_CLEAN;
+ return CL_SUCCESS;
}
iso.ctx = ctx;
@@ -337,6 +336,6 @@ int cli_scaniso(cli_ctx *ctx, size_t offset)
i = iso_parse_dir(&iso, cli_readint32(privol + 156 + 2) + privol[156 + 1], cli_readint32(privol + 156 + 10));
cli_hashset_destroy(&iso.dir_blocks);
if (i == CL_BREAK)
- return CL_CLEAN;
+ return CL_SUCCESS;
return i;
}
diff --git a/libclamav/iso9660.h b/libclamav/iso9660.h
index 4192a3c074..09ec9c3787 100644
--- a/libclamav/iso9660.h
+++ b/libclamav/iso9660.h
@@ -24,6 +24,6 @@
#include "others.h"
-int cli_scaniso(cli_ctx *ctx, size_t offset);
+cl_error_t cli_scaniso(cli_ctx *ctx, size_t offset);
#endif
diff --git a/libclamav/jpeg.c b/libclamav/jpeg.c
index e010e93ae1..ac3bfd6a6c 100644
--- a/libclamav/jpeg.c
+++ b/libclamav/jpeg.c
@@ -310,7 +310,7 @@ static cl_error_t jpeg_check_photoshop_8bim(cli_ctx *ctx, size_t *off)
cl_error_t cli_parsejpeg(cli_ctx *ctx)
{
- cl_error_t status = CL_CLEAN;
+ cl_error_t status = CL_SUCCESS;
fmap_t *map = NULL;
jpeg_marker_t marker, prev_marker, prev_segment = JPEG_MARKER_NOT_A_MARKER_0x00;
@@ -333,15 +333,17 @@ cl_error_t cli_parsejpeg(cli_ctx *ctx)
}
map = ctx->fmap;
- if (fmap_readn(map, buff, offset, 4) != 4)
+ if (fmap_readn(map, buff, offset, 4) != 4) {
goto done; /* Ignore */
+ }
- if (!memcmp(buff, "\xff\xd8\xff", 3))
+ if (!memcmp(buff, "\xff\xd8\xff", 3)) {
offset = 2;
- else if (!memcmp(buff, "\xff\xd9\xff\xd8", 4))
+ } else if (!memcmp(buff, "\xff\xd9\xff\xd8", 4)) {
offset = 4;
- else
+ } else {
goto done; /* Not a JPEG file */
+ }
while (1) {
segment++;
@@ -353,8 +355,7 @@ cl_error_t cli_parsejpeg(cli_ctx *ctx)
} else {
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
cli_errmsg("JPEG: Failed to read marker, file corrupted?\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.CantReadMarker");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.CantReadMarker");
} else {
cli_dbgmsg("Failed to read marker, file corrupted?\n");
}
@@ -369,8 +370,7 @@ cl_error_t cli_parsejpeg(cli_ctx *ctx)
if (i == 16) {
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
cli_warnmsg("JPEG: Spurious bytes before segment %u\n", segment);
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SpuriousBytesBeforeSegment");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SpuriousBytesBeforeSegment");
} else {
cli_dbgmsg("Spurious bytes before segment %u\n", segment);
}
@@ -387,7 +387,7 @@ cl_error_t cli_parsejpeg(cli_ctx *ctx)
if (buff[0] == 0x00) {
if ((buff[1] == 0x00) || (buff[1] == 0x01)) {
/* Found exploit */
- status = cli_append_virus(ctx, "Heuristics.Exploit.W32.MS04-028");
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Exploit.W32.MS04-028");
goto done;
}
}
@@ -397,8 +397,7 @@ cl_error_t cli_parsejpeg(cli_ctx *ctx)
if (fmap_readn(map, &len_u16, offset, sizeof(len_u16)) != sizeof(len_u16)) {
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
cli_errmsg("JPEG: Failed to read the segment size, file corrupted?\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.CantReadSegmentSize");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.CantReadSegmentSize");
} else {
cli_dbgmsg("Failed to read the segment size, file corrupted?\n");
}
@@ -410,8 +409,7 @@ cl_error_t cli_parsejpeg(cli_ctx *ctx)
if (len < 2) {
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
cli_warnmsg("JPEG: Invalid segment size\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.InvalidSegmentSize");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.InvalidSegmentSize");
} else {
cli_dbgmsg("Invalid segment size\n");
}
@@ -420,8 +418,7 @@ cl_error_t cli_parsejpeg(cli_ctx *ctx)
if (len >= map->len - offset + sizeof(len_u16)) {
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
cli_warnmsg("JPEG: Segment data out of file\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SegmentDataOutOfFile");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SegmentDataOutOfFile");
} else {
cli_dbgmsg("Segment data out of file\n");
}
@@ -443,8 +440,7 @@ cl_error_t cli_parsejpeg(cli_ctx *ctx)
if (found_app && num_JFIF > 0) {
cli_warnmsg("JPEG: Duplicate Application Marker found (JFIF)\n");
cli_warnmsg("JPEG: Already observed JFIF: %d, Exif: %d, SPIFF: %d\n", num_JFIF, num_Exif, num_SPIFF);
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.JFIFdupAppMarker");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.JFIFdupAppMarker");
goto done;
}
if (!(segment == 1 ||
@@ -456,14 +452,12 @@ cl_error_t cli_parsejpeg(cli_ctx *ctx)
* If segment 1 wasn't a comment or Exif, then the file structure is unusual. */
cli_warnmsg("JPEG: JFIF marker at wrong position, found in segment # %d\n", segment);
cli_warnmsg("JPEG: Already observed JFIF: %d, Exif: %d, SPIFF: %d\n", num_JFIF, num_Exif, num_SPIFF);
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.JFIFmarkerBadPosition");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.JFIFmarkerBadPosition");
goto done;
}
if (len < 16) {
cli_warnmsg("JPEG: JFIF header too short\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.JFIFheaderTooShort");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.JFIFheaderTooShort");
goto done;
}
}
@@ -489,21 +483,18 @@ cl_error_t cli_parsejpeg(cli_ctx *ctx)
if (found_app && (num_Exif > 0 || num_SPIFF > 0)) {
cli_warnmsg("JPEG: Duplicate Application Marker found (Exif)\n");
cli_warnmsg("JPEG: Already observed JFIF: %d, Exif: %d, SPIFF: %d\n", num_JFIF, num_Exif, num_SPIFF);
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.ExifDupAppMarker");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.ExifDupAppMarker");
goto done;
}
if (segment > 3 && !found_comment && num_JFIF > 0) {
/* If Exif was found after segment 3 and previous segments weren't a comment or JFIF, something is unusual. */
cli_warnmsg("JPEG: Exif marker at wrong position\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.ExifHeaderBadPosition");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.ExifHeaderBadPosition");
goto done;
}
if (len < 16) {
cli_warnmsg("JPEG: Exif header too short\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.ExifHeaderTooShort");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.ExifHeaderTooShort");
goto done;
}
}
@@ -545,20 +536,17 @@ cl_error_t cli_parsejpeg(cli_ctx *ctx)
if (found_app) {
cli_warnmsg("JPEG: Duplicate Application Marker found (SPIFF)\n");
cli_warnmsg("JPEG: Already observed JFIF: %d, Exif: %d, SPIFF: %d\n", num_JFIF, num_Exif, num_SPIFF);
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SPIFFdupAppMarker");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SPIFFdupAppMarker");
goto done;
}
if (segment != 1 && (segment != 2 || !found_comment)) {
cli_warnmsg("JPEG: SPIFF marker at wrong position\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SPIFFmarkerBadPosition");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SPIFFmarkerBadPosition");
goto done;
}
if (len < 16) {
cli_warnmsg("JPEG: SPIFF header too short\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SPIFFheaderTooShort");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.SPIFFheaderTooShort");
goto done;
}
}
@@ -656,8 +644,7 @@ cl_error_t cli_parsejpeg(cli_ctx *ctx)
if (found_app) {
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
cli_warnmsg("JPEG: Application Marker before JPG7\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.AppMarkerBeforeJPG7");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.AppMarkerBeforeJPG7");
goto done;
}
}
@@ -680,8 +667,7 @@ cl_error_t cli_parsejpeg(cli_ctx *ctx)
*/
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
cli_warnmsg("JPEG: No image in jpeg\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.NoImages");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.NoImages");
}
goto done;
@@ -699,8 +685,7 @@ cl_error_t cli_parsejpeg(cli_ctx *ctx)
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
if (prev_segment != JPEG_MARKER_SEGMENT_DTI) {
cli_warnmsg("JPEG: No DTI segment before DTT\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.DTTMissingDTISegment");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.JPEG.DTTMissingDTISegment");
goto done;
}
}
@@ -715,10 +700,5 @@ cl_error_t cli_parsejpeg(cli_ctx *ctx)
}
done:
- if (status == CL_EPARSE) {
- /* We added with cli_append_potentially_unwanted so it will alert at the end if nothing else matches. */
- status = CL_CLEAN;
- }
-
return status;
}
diff --git a/libclamav/libmspack.c b/libclamav/libmspack.c
index e26fd74e37..e7a1763582 100644
--- a/libclamav/libmspack.c
+++ b/libclamav/libmspack.c
@@ -341,26 +341,30 @@ static struct mspack_system mspack_sys_fmap_ops = {
.copy = mspack_fmap_copy,
};
-int cli_scanmscab(cli_ctx *ctx, off_t sfx_offset)
+cl_error_t cli_scanmscab(cli_ctx *ctx, off_t sfx_offset)
{
- struct mscab_decompressor *cab_d;
- struct mscabd_cabinet *cab_h;
- struct mscabd_file *cab_f;
- int ret = 0;
+ cl_error_t ret = CL_SUCCESS;
+ struct mscab_decompressor *cab_d = NULL;
+ struct mscabd_cabinet *cab_h = NULL;
+ struct mscabd_file *cab_f = NULL;
int files;
- int virus_num = 0;
struct mspack_name mspack_fmap = {
.fmap = ctx->fmap,
.org = sfx_offset,
};
struct mspack_system_ex ops_ex;
+
+ char *tmp_fname = NULL;
+ bool tempfile_exists = false;
+
memset(&ops_ex, 0, sizeof(struct mspack_system_ex));
ops_ex.ops = mspack_sys_fmap_ops;
cab_d = mspack_create_cab_decompressor(&ops_ex.ops);
if (!cab_d) {
cli_dbgmsg("%s() failed at %d\n", __func__, __LINE__);
- return CL_EUNPACK;
+ ret = CL_EUNPACK;
+ goto done;
}
cab_d->set_param(cab_d, MSCABD_PARAM_FIXMSZIP, 1);
@@ -369,31 +373,26 @@ int cli_scanmscab(cli_ctx *ctx, off_t sfx_offset)
#endif
cab_h = cab_d->open(cab_d, (char *)&mspack_fmap);
- if (!cab_h) {
- ret = CL_EFORMAT;
+ if (NULL == cab_h) {
cli_dbgmsg("%s() failed at %d\n", __func__, __LINE__);
- goto out_dest;
+ ret = CL_EFORMAT;
+ goto done;
}
+
files = 0;
for (cab_f = cab_h->files; cab_f; cab_f = cab_f->next) {
uint64_t max_size;
- char *tmp_fname = NULL;
ret = cli_matchmeta(ctx, cab_f->filename, 0, cab_f->length, 0,
files, 0, NULL);
- if (ret) {
- if (ret == CL_VIRUS) {
- virus_num++;
- if (!SCAN_ALLMATCHES)
- break;
- }
- goto out_close;
+ if (CL_SUCCESS != ret) {
+ goto done;
}
if (ctx->engine->maxscansize) {
if (ctx->scansize >= ctx->engine->maxscansize) {
ret = CL_CLEAN;
- break;
+ goto done;
}
}
@@ -422,94 +421,107 @@ int cli_scanmscab(cli_ctx *ctx, off_t sfx_offset)
tmp_fname = cli_gentemp(ctx->sub_tmpdir);
if (!tmp_fname) {
ret = CL_EMEM;
- break;
+ goto done;
}
ops_ex.max_size = max_size;
+
/* scan */
ret = cab_d->extract(cab_d, cab_f, tmp_fname);
- if (ret)
+ if (ret) {
/* Failed to extract. Try to scan what is there */
cli_dbgmsg("%s() failed to extract %d\n", __func__, ret);
+ }
+ tempfile_exists = true; // probably
ret = cli_magic_scan_file(tmp_fname, ctx, cab_f->filename);
if (CL_EOPEN == ret) {
- ret = CL_CLEAN;
- } else if (CL_VIRUS == ret) {
- virus_num++;
+ // okay so the file didn't actually get extracted. That's okay, we'll move on.
+ tempfile_exists = false;
+ ret = CL_SUCCESS;
+ } else if (CL_SUCCESS != ret) {
+ goto done;
}
- if (!ctx->engine->keeptmp) {
- if (!access(tmp_fname, R_OK) && cli_unlink(tmp_fname)) {
- free(tmp_fname);
+ if (!ctx->engine->keeptmp && tempfile_exists) {
+ if (cli_unlink(tmp_fname)) {
ret = CL_EUNLINK;
- break;
+ goto done;
}
}
+
free(tmp_fname);
+ tmp_fname = NULL;
+
files++;
- if (ret == CL_VIRUS && SCAN_ALLMATCHES)
- continue;
- if (ret)
- break;
}
-out_close:
- cab_d->close(cab_d, cab_h);
-out_dest:
- mspack_destroy_cab_decompressor(cab_d);
- if (virus_num)
- return CL_VIRUS;
+done:
+
+ if (NULL != tmp_fname) {
+ if (!ctx->engine->keeptmp && tempfile_exists) {
+ (void)cli_unlink(tmp_fname);
+ }
+
+ free(tmp_fname);
+ }
+
+ if (NULL != cab_d) {
+ if (NULL != cab_h) {
+ cab_d->close(cab_d, cab_h);
+ }
+ mspack_destroy_cab_decompressor(cab_d);
+ }
+
return ret;
}
-int cli_scanmschm(cli_ctx *ctx)
+cl_error_t cli_scanmschm(cli_ctx *ctx)
{
- struct mschm_decompressor *mschm_d;
- struct mschmd_header *mschm_h;
- struct mschmd_file *mschm_f;
- int ret = CL_CLEAN; // Default CLEAN in case CHM contains no files.
+ cl_error_t ret = CL_SUCCESS;
+ struct mschm_decompressor *mschm_d = NULL;
+ struct mschmd_header *mschm_h = NULL;
+ struct mschmd_file *mschm_f = NULL;
int files;
- int virus_num = 0;
struct mspack_name mspack_fmap = {
.fmap = ctx->fmap,
};
struct mspack_system_ex ops_ex;
+
+ char *tmp_fname = NULL;
+ bool tempfile_exists = false;
+
memset(&ops_ex, 0, sizeof(struct mspack_system_ex));
ops_ex.ops = mspack_sys_fmap_ops;
mschm_d = mspack_create_chm_decompressor(&ops_ex.ops);
if (!mschm_d) {
cli_dbgmsg("%s() failed at %d\n", __func__, __LINE__);
- return CL_EUNPACK;
+ ret = CL_EUNPACK;
+ goto done;
}
mschm_h = mschm_d->open(mschm_d, (char *)&mspack_fmap);
if (!mschm_h) {
- ret = CL_EFORMAT;
cli_dbgmsg("%s() failed at %d\n", __func__, __LINE__);
- goto out_dest;
+ ret = CL_EFORMAT;
+ goto done;
}
+
files = 0;
for (mschm_f = mschm_h->files; mschm_f; mschm_f = mschm_f->next) {
uint64_t max_size;
- char *tmp_fname;
ret = cli_matchmeta(ctx, mschm_f->filename, 0, mschm_f->length,
0, files, 0, NULL);
- if (ret) {
- if (ret == CL_VIRUS) {
- virus_num++;
- if (!SCAN_ALLMATCHES)
- break;
- }
- goto out_close;
+ if (CL_SUCCESS != ret) {
+ goto done;
}
if (ctx->engine->maxscansize) {
if (ctx->scansize >= ctx->engine->maxscansize) {
ret = CL_CLEAN;
- break;
+ goto done;
}
}
@@ -535,47 +547,60 @@ int cli_scanmschm(cli_ctx *ctx)
}
}
- ops_ex.max_size = max_size;
-
tmp_fname = cli_gentemp(ctx->sub_tmpdir);
if (!tmp_fname) {
ret = CL_EMEM;
break;
}
+ ops_ex.max_size = max_size;
+
/* scan */
ret = mschm_d->extract(mschm_d, mschm_f, tmp_fname);
- if (ret)
+ if (ret) {
/* Failed to extract. Try to scan what is there */
cli_dbgmsg("%s() failed to extract %d\n", __func__, ret);
+ }
+ tempfile_exists = true; // probably
ret = cli_magic_scan_file(tmp_fname, ctx, mschm_f->filename);
if (CL_EOPEN == ret) {
- ret = CL_CLEAN;
- } else if (CL_VIRUS == ret) {
- virus_num++;
+ // okay so the file didn't actually get extracted. That's okay, we'll move on.
+ tempfile_exists = false;
+ ret = CL_SUCCESS;
+ } else if (CL_SUCCESS != ret) {
+ goto done;
}
- if (!ctx->engine->keeptmp) {
- if (!access(tmp_fname, R_OK) && cli_unlink(tmp_fname)) {
- free(tmp_fname);
+ if (!ctx->engine->keeptmp && tempfile_exists) {
+ if (cli_unlink(tmp_fname)) {
ret = CL_EUNLINK;
- break;
+ goto done;
}
}
+
free(tmp_fname);
+ tmp_fname = NULL;
+
files++;
- if (ret == CL_VIRUS && SCAN_ALLMATCHES)
- continue;
- if (ret)
- break;
}
-out_close:
- mschm_d->close(mschm_d, mschm_h);
-out_dest:
- mspack_destroy_chm_decompressor(mschm_d);
- if (virus_num)
- return CL_VIRUS;
+done:
+
+ if (NULL != tmp_fname) {
+ if (!ctx->engine->keeptmp && tempfile_exists) {
+ (void)cli_unlink(tmp_fname);
+ }
+
+ free(tmp_fname);
+ }
+
+ if (NULL != mschm_d) {
+ if (NULL != mschm_h) {
+ mschm_d->close(mschm_d, mschm_h);
+ }
+ mspack_destroy_chm_decompressor(mschm_d);
+ }
+
return ret;
}
diff --git a/libclamav/macho.c b/libclamav/macho.c
index 2922cd4a63..fd8590587f 100644
--- a/libclamav/macho.c
+++ b/libclamav/macho.c
@@ -168,13 +168,11 @@ struct macho_fat_arch {
uint32_t align;
};
-#define RETURN_BROKEN \
- if (matcher) \
- return -1; \
- if (SCAN_HEURISTIC_BROKEN) { \
- if (CL_VIRUS == cli_append_virus(ctx, "Heuristics.Broken.Executable")) \
- return CL_VIRUS; \
- } \
+#define RETURN_BROKEN \
+ if (SCAN_HEURISTIC_BROKEN) { \
+ if (CL_VIRUS == cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Executable")) \
+ return CL_VIRUS; \
+ } \
return CL_EFORMAT
static uint32_t cli_rawaddr(uint32_t vaddr, struct cli_exe_section *sects, uint16_t nsects, unsigned int *err)
@@ -197,7 +195,7 @@ static uint32_t cli_rawaddr(uint32_t vaddr, struct cli_exe_section *sects, uint1
return vaddr - sects[i].rva + sects[i].raw;
}
-int cli_scanmacho(cli_ctx *ctx, struct cli_exe_info *fileinfo)
+cl_error_t cli_scanmacho(cli_ctx *ctx, struct cli_exe_info *fileinfo)
{
struct macho_hdr hdr;
struct macho_load_cmd load_cmd;
@@ -205,7 +203,8 @@ int cli_scanmacho(cli_ctx *ctx, struct cli_exe_info *fileinfo)
struct macho_segment_cmd64 segment_cmd64;
struct macho_section section;
struct macho_section64 section64;
- unsigned int i, j, sect = 0, conv, m64, nsects, matcher = 0;
+ unsigned int i, j, sect = 0, conv, m64, nsects;
+ bool get_fileinfo = false;
unsigned int arch = 0, ep = 0, err;
struct cli_exe_section *sections = NULL;
char name[16];
@@ -213,7 +212,7 @@ int cli_scanmacho(cli_ctx *ctx, struct cli_exe_info *fileinfo)
ssize_t at;
if (fileinfo) {
- matcher = 1;
+ get_fileinfo = true;
// TODO This code assumes fileinfo->offset == 0, which might not always
// be the case. For now just print this debug message and continue on
@@ -224,7 +223,7 @@ int cli_scanmacho(cli_ctx *ctx, struct cli_exe_info *fileinfo)
if (fmap_readn(map, &hdr, 0, sizeof(hdr)) != sizeof(hdr)) {
cli_dbgmsg("cli_scanmacho: Can't read header\n");
- return matcher ? -1 : CL_EFORMAT;
+ return CL_EFORMAT;
}
at = sizeof(hdr);
@@ -242,44 +241,44 @@ int cli_scanmacho(cli_ctx *ctx, struct cli_exe_info *fileinfo)
m64 = 1;
} else {
cli_dbgmsg("cli_scanmacho: Incorrect magic\n");
- return matcher ? -1 : CL_EFORMAT;
+ return CL_EFORMAT;
}
switch (EC32(hdr.cpu_type, conv)) {
case 7:
- if (!matcher)
+ if (!get_fileinfo)
cli_dbgmsg("MACHO: CPU Type: Intel 32-bit\n");
arch = 1;
break;
case 7 | 0x1000000:
- if (!matcher)
+ if (!get_fileinfo)
cli_dbgmsg("MACHO: CPU Type: Intel 64-bit\n");
break;
case 12:
- if (!matcher)
+ if (!get_fileinfo)
cli_dbgmsg("MACHO: CPU Type: ARM\n");
break;
case 14:
- if (!matcher)
+ if (!get_fileinfo)
cli_dbgmsg("MACHO: CPU Type: SPARC\n");
break;
case 18:
- if (!matcher)
+ if (!get_fileinfo)
cli_dbgmsg("MACHO: CPU Type: POWERPC 32-bit\n");
arch = 2;
break;
case 18 | 0x1000000:
- if (!matcher)
+ if (!get_fileinfo)
cli_dbgmsg("MACHO: CPU Type: POWERPC 64-bit\n");
arch = 3;
break;
default:
- if (!matcher)
+ if (!get_fileinfo)
cli_dbgmsg("MACHO: CPU Type: ** UNKNOWN ** (%u)\n", EC32(hdr.cpu_type, conv));
break;
}
- if (!matcher) switch (EC32(hdr.filetype, conv)) {
+ if (!get_fileinfo) switch (EC32(hdr.filetype, conv)) {
case 0x1: /* MH_OBJECT */
cli_dbgmsg("MACHO: Filetype: Relocatable object file\n");
break;
@@ -311,7 +310,7 @@ int cli_scanmacho(cli_ctx *ctx, struct cli_exe_info *fileinfo)
cli_dbgmsg("MACHO: Filetype: ** UNKNOWN ** (0x%x)\n", EC32(hdr.filetype, conv));
}
- if (!matcher) {
+ if (!get_fileinfo) {
cli_dbgmsg("MACHO: Number of load commands: %u\n", EC32(hdr.ncmds, conv));
cli_dbgmsg("MACHO: Size of load commands: %u\n", EC32(hdr.sizeofcmds, conv));
}
@@ -362,7 +361,7 @@ int cli_scanmacho(cli_ctx *ctx, struct cli_exe_info *fileinfo)
strncpy(name, segment_cmd.segname, sizeof(name));
name[sizeof(name) - 1] = '\0';
}
- if (!matcher) {
+ if (!get_fileinfo) {
cli_dbgmsg("MACHO: Segment name: %s\n", name);
cli_dbgmsg("MACHO: Number of sections: %u\n", nsects);
}
@@ -372,14 +371,14 @@ int cli_scanmacho(cli_ctx *ctx, struct cli_exe_info *fileinfo)
RETURN_BROKEN;
}
if (!nsects) {
- if (!matcher)
+ if (!get_fileinfo)
cli_dbgmsg("MACHO: ------------------\n");
continue;
}
sections = (struct cli_exe_section *)cli_realloc2(sections, (sect + nsects) * sizeof(struct cli_exe_section));
if (!sections) {
cli_errmsg("cli_scanmacho: Can't allocate memory for 'sections'\n");
- return matcher ? -1 : CL_EMEM;
+ return CL_EMEM;
}
for (j = 0; j < nsects; j++) {
@@ -417,7 +416,7 @@ int cli_scanmacho(cli_ctx *ctx, struct cli_exe_info *fileinfo)
strncpy(name, section.sectname, sizeof(name));
name[sizeof(name) - 1] = '\0';
}
- if (!matcher) {
+ if (!get_fileinfo) {
cli_dbgmsg("MACHO: --- Section %u ---\n", sect);
cli_dbgmsg("MACHO: Name: %s\n", name);
cli_dbgmsg("MACHO: Virtual address: 0x%x\n", (unsigned int)sections[sect].rva);
@@ -428,7 +427,7 @@ int cli_scanmacho(cli_ctx *ctx, struct cli_exe_info *fileinfo)
}
sect++;
}
- if (!matcher)
+ if (!get_fileinfo)
cli_dbgmsg("MACHO: ------------------\n");
} else if (arch && (load_cmd.cmd == 0x4 || load_cmd.cmd == 0x5)) { /* LC_(UNIX)THREAD */
@@ -477,7 +476,7 @@ int cli_scanmacho(cli_ctx *ctx, struct cli_exe_info *fileinfo)
default:
cli_errmsg("cli_scanmacho: Invalid arch setting!\n");
free(sections);
- return matcher ? -1 : CL_EARG;
+ return CL_EARG;
}
} else {
if (EC32(load_cmd.cmdsize, conv) > sizeof(load_cmd))
@@ -486,43 +485,43 @@ int cli_scanmacho(cli_ctx *ctx, struct cli_exe_info *fileinfo)
}
if (ep) {
- if (!matcher)
+ if (!get_fileinfo)
cli_dbgmsg("Entry Point: 0x%x\n", ep);
if (sections) {
ep = cli_rawaddr(ep, sections, sect, &err);
if (err) {
cli_dbgmsg("cli_scanmacho: Can't calculate EP offset\n");
free(sections);
- return matcher ? -1 : CL_EFORMAT;
+ return CL_EFORMAT;
}
- if (!matcher)
+ if (!get_fileinfo)
cli_dbgmsg("Entry Point file offset: %u\n", ep);
}
}
- if (matcher) {
+ if (get_fileinfo) {
fileinfo->ep = ep;
fileinfo->nsections = sect;
fileinfo->sections = sections;
- return 0;
} else {
free(sections);
- return CL_SUCCESS;
}
+
+ return CL_SUCCESS;
}
-int cli_machoheader(cli_ctx *ctx, struct cli_exe_info *fileinfo)
+cl_error_t cli_machoheader(cli_ctx *ctx, struct cli_exe_info *fileinfo)
{
return cli_scanmacho(ctx, fileinfo);
}
-int cli_scanmacho_unibin(cli_ctx *ctx)
+cl_error_t cli_scanmacho_unibin(cli_ctx *ctx)
{
struct macho_fat_header fat_header;
struct macho_fat_arch fat_arch;
- unsigned int conv, i, matcher = 0;
- int ret = CL_CLEAN;
- fmap_t *map = ctx->fmap;
+ unsigned int conv, i;
+ cl_error_t ret = CL_SUCCESS;
+ fmap_t *map = ctx->fmap;
ssize_t at;
if (fmap_readn(map, &fat_header, 0, sizeof(fat_header)) != sizeof(fat_header)) {
@@ -570,59 +569,64 @@ int cli_scanmacho_unibin(cli_ctx *ctx)
}
ret = cli_magic_scan_nested_fmap_type(map, fat_arch.offset, fat_arch.size, ctx, CL_TYPE_ANY, NULL);
- if (ret == CL_VIRUS)
+ if (ret != CL_SUCCESS) {
break;
+ }
}
return ret; /* result from the last binary */
}
-int cli_unpackmacho(cli_ctx *ctx)
+cl_error_t cli_unpackmacho(cli_ctx *ctx)
{
- char *tempfile;
- int ndesc;
+ cl_error_t ret = CL_SUCCESS;
+ char *tempfile = NULL;
+ int ndesc = -1;
struct cli_bc_ctx *bc_ctx;
- int ret;
+ bool bc_ctx_set = false;
/* Bytecode BC_MACHO_UNPACKER hook */
bc_ctx = cli_bytecode_context_alloc();
if (!bc_ctx) {
- cli_errmsg("cli_scanelf: can't allocate memory for bc_ctx\n");
- return CL_EMEM;
+ cli_errmsg("cli_unpackmacho: can't allocate memory for bc_ctx\n");
+ ret = CL_EMEM;
+ goto done;
}
cli_bytecode_context_setctx(bc_ctx, ctx);
+ bc_ctx_set = true;
+ cli_dbgmsg("Running bytecode hook\n");
ret = cli_bytecode_runhook(ctx, ctx->engine, bc_ctx, BC_MACHO_UNPACKER, ctx->fmap);
- switch (ret) {
- case CL_VIRUS:
- cli_bytecode_context_destroy(bc_ctx);
- return CL_VIRUS;
- case CL_SUCCESS:
- ndesc = cli_bytecode_context_getresult_file(bc_ctx, &tempfile);
- cli_bytecode_context_destroy(bc_ctx);
- if (ndesc != -1 && tempfile) {
- if (ctx->engine->keeptmp)
- cli_dbgmsg("cli_scanmacho: Unpacked and rebuilt executable saved in %s\n", tempfile);
- else
- cli_dbgmsg("cli_scanmacho: Unpacked and rebuilt executable\n");
- lseek(ndesc, 0, SEEK_SET);
- cli_dbgmsg("***** Scanning rebuilt Mach-O file *****\n");
- if (cli_magic_scan_desc(ndesc, tempfile, ctx, NULL) == CL_VIRUS) {
- close(ndesc);
- CLI_TMPUNLK();
- free(tempfile);
- return CL_VIRUS;
- }
- close(ndesc);
- CLI_TMPUNLK();
- free(tempfile);
- return CL_SUCCESS;
- }
- break;
- default:
- cli_bytecode_context_destroy(bc_ctx);
+ cli_dbgmsg("Finished running bytecode hook\n");
+ if (CL_SUCCESS == ret) {
+ // check for unpacked/rebuilt executable
+ ndesc = cli_bytecode_context_getresult_file(bc_ctx, &tempfile);
+ if (ndesc != -1 && tempfile) {
+ cli_dbgmsg("cli_unpackmacho: Unpacked and rebuilt Mach-O executable saved in %s\n", tempfile);
+
+ lseek(ndesc, 0, SEEK_SET);
+
+ cli_dbgmsg("***** Scanning rebuilt Mach-O file *****\n");
+ ret = cli_magic_scan_desc(ndesc, tempfile, ctx, NULL);
+ }
+ }
+
+done:
+ // cli_bytecode_context_getresult_file() gives up ownership of temp file, so we must clean it up.
+ if (-1 != ndesc) {
+ close(ndesc);
+ }
+ if (NULL != tempfile) {
+ if (!ctx->engine->keeptmp) {
+ (void)cli_unlink(tempfile);
+ }
+ free(tempfile);
+ }
+
+ if (bc_ctx_set) {
+ cli_bytecode_context_destroy(bc_ctx);
}
- return CL_CLEAN;
+ return ret;
}
diff --git a/libclamav/macho.h b/libclamav/macho.h
index b8423a6822..ffb99c8b10 100644
--- a/libclamav/macho.h
+++ b/libclamav/macho.h
@@ -26,9 +26,9 @@
#include "execs.h"
#include "fmap.h"
-int cli_scanmacho(cli_ctx *ctx, struct cli_exe_info *fileinfo);
-int cli_machoheader(cli_ctx *ctx, struct cli_exe_info *fileinfo);
-int cli_scanmacho_unibin(cli_ctx *ctx);
-int cli_unpackmacho(cli_ctx *ctx);
+cl_error_t cli_scanmacho(cli_ctx *ctx, struct cli_exe_info *fileinfo);
+cl_error_t cli_machoheader(cli_ctx *ctx, struct cli_exe_info *fileinfo);
+cl_error_t cli_scanmacho_unibin(cli_ctx *ctx);
+cl_error_t cli_unpackmacho(cli_ctx *ctx);
#endif
diff --git a/libclamav/matcher-ac.c b/libclamav/matcher-ac.c
index 2a46031e70..2543c3354c 100644
--- a/libclamav/matcher-ac.c
+++ b/libclamav/matcher-ac.c
@@ -1523,7 +1523,7 @@ cl_error_t cli_ac_initdata(struct cli_ac_data *data, uint32_t partsigs, uint32_t
cl_error_t cli_ac_caloff(const struct cli_matcher *root, struct cli_ac_data *data, const struct cli_target_info *info)
{
- int ret;
+ cl_error_t ret;
unsigned int i;
struct cli_ac_patt *patt;
@@ -1534,7 +1534,7 @@ cl_error_t cli_ac_caloff(const struct cli_matcher *root, struct cli_ac_data *dat
patt = root->ac_reloff[i];
if (!info) {
data->offset[patt->offset_min] = CLI_OFF_NONE;
- } else if ((ret = cli_caloff(NULL, info, root->type, patt->offdata, &data->offset[patt->offset_min], &data->offset[patt->offset_max]))) {
+ } else if (CL_SUCCESS != (ret = cli_caloff(NULL, info, root->type, patt->offdata, &data->offset[patt->offset_min], &data->offset[patt->offset_max]))) {
cli_errmsg("cli_ac_caloff: Can't calculate relative offset in signature for %s\n", patt->virname);
return ret;
} else if ((data->offset[patt->offset_min] != CLI_OFF_NONE) && (data->offset[patt->offset_min] + patt->length[1] > info->fsize)) {
@@ -1860,7 +1860,7 @@ cl_error_t cli_ac_scanbuff(
realoff = offset + matchstart;
if (pt->offdata[0] == CLI_OFF_VERSION) {
- if (!cli_hashset_contains_maybe_noalloc(mdata->vinfo, realoff)) {
+ if (false == cli_hashset_contains_maybe_noalloc(mdata->vinfo, realoff)) {
ptN = ptN->next_same;
continue;
}
diff --git a/libclamav/matcher-hash-types.h b/libclamav/matcher-hash-types.h
new file mode 100644
index 0000000000..57f192cd4d
--- /dev/null
+++ b/libclamav/matcher-hash-types.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2013-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ * Copyright (C) 2010-2013 Sourcefire, Inc.
+ *
+ * Authors: aCaB
+ *
+ * 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.
+ */
+
+#ifndef __MATCHER_HASH_TYPES_H
+#define __MATCHER_HASH_TYPES_H
+
+typedef enum cli_hash_type {
+ CLI_HASH_MD5 = 0,
+ CLI_HASH_SHA1,
+ CLI_HASH_SHA256,
+
+ /* new hash types go above this line */
+ CLI_HASH_AVAIL_TYPES
+} cli_hash_type_t;
+
+#define CLI_HASHLEN_MD5 16
+#define CLI_HASHLEN_SHA1 20
+#define CLI_HASHLEN_SHA256 32
+#define CLI_HASHLEN_MAX 32
+
+#endif
diff --git a/libclamav/matcher-hash.c b/libclamav/matcher-hash.c
index 7c9c842132..ea5e075383 100644
--- a/libclamav/matcher-hash.c
+++ b/libclamav/matcher-hash.c
@@ -28,7 +28,7 @@
int hm_addhash_str(struct cli_matcher *root, const char *strhash, uint32_t size, const char *virusname)
{
- enum CLI_HASH_TYPE type;
+ cli_hash_type_t type;
char binhash[CLI_HASHLEN_MAX];
int hlen;
@@ -71,7 +71,7 @@ const unsigned int hashlen[] = {
CLI_HASHLEN_SHA1,
CLI_HASHLEN_SHA256};
-int hm_addhash_bin(struct cli_matcher *root, const void *binhash, enum CLI_HASH_TYPE type, uint32_t size, const char *virusname)
+int hm_addhash_bin(struct cli_matcher *root, const void *binhash, cli_hash_type_t type, uint32_t size, const char *virusname)
{
const unsigned int hlen = hashlen[type];
const struct cli_htu32_element *item;
@@ -83,7 +83,7 @@ int hm_addhash_bin(struct cli_matcher *root, const void *binhash, enum CLI_HASH_
/* 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);
+ i = CLI_HTU32_INIT(ht, 64, root->mempool);
if (i) return i;
}
@@ -98,7 +98,7 @@ int hm_addhash_bin(struct cli_matcher *root, const void *binhash, enum CLI_HASH_
htitem.key = size;
htitem.data.as_ptr = szh;
- i = cli_htu32_insert(ht, &htitem, root->mempool);
+ i = CLI_HTU32_INSERT(ht, &htitem, root->mempool);
if (i) {
cli_errmsg("hm_addhash_bin: failed to add item to hashtab");
MPOOL_FREE(root->mempool, szh);
@@ -192,7 +192,7 @@ static void hm_sort(struct cli_sz_hash *szh, size_t l, size_t r, unsigned int ke
/* flush both size-specific and agnostic hash sets */
void hm_flush(struct cli_matcher *root)
{
- enum CLI_HASH_TYPE type;
+ cli_hash_type_t type;
unsigned int keylen;
struct cli_sz_hash *szh;
@@ -225,23 +225,22 @@ void hm_flush(struct cli_matcher *root)
}
}
-int cli_hm_have_size(const struct cli_matcher *root, enum CLI_HASH_TYPE type, uint32_t size)
+int cli_hm_have_size(const struct cli_matcher *root, cli_hash_type_t type, uint32_t size)
{
return (size && size != 0xffffffff && root && root->hm.sizehashes[type].capacity && cli_htu32_find(&root->hm.sizehashes[type], size));
}
-int cli_hm_have_wild(const struct cli_matcher *root, enum CLI_HASH_TYPE type)
+int cli_hm_have_wild(const struct cli_matcher *root, cli_hash_type_t type)
{
return (root && root->hwild.hashes[type].items);
}
-int cli_hm_have_any(const struct cli_matcher *root, enum CLI_HASH_TYPE type)
+int cli_hm_have_any(const struct cli_matcher *root, cli_hash_type_t type)
{
return (root && (root->hwild.hashes[type].items || root->hm.sizehashes[type].capacity));
}
-/* cli_hm_scan will scan only size-specific hashes, if any */
-static int hm_scan(const unsigned char *digest, const char **virname, const struct cli_sz_hash *szh, enum CLI_HASH_TYPE type)
+static int hm_scan(const unsigned char *digest, const char **virname, const struct cli_sz_hash *szh, cli_hash_type_t type)
{
unsigned int keylen;
size_t l, r;
@@ -273,7 +272,7 @@ static int hm_scan(const unsigned char *digest, const char **virname, const stru
}
/* cli_hm_scan will scan only size-specific hashes, if any */
-int cli_hm_scan(const unsigned char *digest, uint32_t size, const char **virname, const struct cli_matcher *root, enum CLI_HASH_TYPE type)
+int cli_hm_scan(const unsigned char *digest, uint32_t size, const char **virname, const struct cli_matcher *root, cli_hash_type_t type)
{
const struct cli_htu32_element *item;
struct cli_sz_hash *szh;
@@ -291,7 +290,7 @@ int cli_hm_scan(const unsigned char *digest, uint32_t size, const char **virname
}
/* cli_hm_scan_wild will scan only size-agnostic hashes, if any */
-int cli_hm_scan_wild(const unsigned char *digest, const char **virname, const struct cli_matcher *root, enum CLI_HASH_TYPE type)
+int cli_hm_scan_wild(const unsigned char *digest, const char **virname, const struct cli_matcher *root, cli_hash_type_t type)
{
if (!digest || !root || !root->hwild.hashes[type].items)
return CL_CLEAN;
@@ -302,7 +301,7 @@ int cli_hm_scan_wild(const unsigned char *digest, const char **virname, const st
/* free both size-specific and agnostic hash sets */
void hm_free(struct cli_matcher *root)
{
- enum CLI_HASH_TYPE type;
+ cli_hash_type_t type;
if (!root)
return;
@@ -323,7 +322,7 @@ void hm_free(struct cli_matcher *root)
MPOOL_FREE(root->mempool, szh->virusnames);
MPOOL_FREE(root->mempool, szh);
}
- cli_htu32_free(ht, root->mempool);
+ CLI_HTU32_FREE(ht, root->mempool);
}
for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++) {
diff --git a/libclamav/matcher-hash.h b/libclamav/matcher-hash.h
index 896d9ccc20..c41fba42fc 100644
--- a/libclamav/matcher-hash.h
+++ b/libclamav/matcher-hash.h
@@ -27,22 +27,9 @@
#endif
#include "clamav-types.h"
+#include "matcher-hash-types.h"
#include "hashtab.h"
-enum CLI_HASH_TYPE {
- CLI_HASH_MD5 = 0,
- CLI_HASH_SHA1,
- CLI_HASH_SHA256,
-
- /* new hash types go above this line */
- CLI_HASH_AVAIL_TYPES
-};
-
-#define CLI_HASHLEN_MD5 16
-#define CLI_HASHLEN_SHA1 20
-#define CLI_HASHLEN_SHA256 32
-#define CLI_HASHLEN_MAX 32
-
struct cli_sz_hash {
uint8_t *hash_array;
const char **virusnames;
@@ -58,13 +45,13 @@ struct cli_hash_wild {
};
int hm_addhash_str(struct cli_matcher *root, const char *strhash, uint32_t size, const char *virusname);
-int hm_addhash_bin(struct cli_matcher *root, const void *binhash, enum CLI_HASH_TYPE type, uint32_t size, const char *virusname);
+int hm_addhash_bin(struct cli_matcher *root, const void *binhash, cli_hash_type_t type, uint32_t size, const char *virusname);
void hm_flush(struct cli_matcher *root);
-int cli_hm_scan(const unsigned char *digest, uint32_t size, const char **virname, const struct cli_matcher *root, enum CLI_HASH_TYPE type);
-int cli_hm_scan_wild(const unsigned char *digest, const char **virname, const struct cli_matcher *root, enum CLI_HASH_TYPE type);
-int cli_hm_have_size(const struct cli_matcher *root, enum CLI_HASH_TYPE type, uint32_t size);
-int cli_hm_have_wild(const struct cli_matcher *root, enum CLI_HASH_TYPE type);
-int cli_hm_have_any(const struct cli_matcher *root, enum CLI_HASH_TYPE type);
+int cli_hm_scan(const unsigned char *digest, uint32_t size, const char **virname, const struct cli_matcher *root, cli_hash_type_t type);
+int cli_hm_scan_wild(const unsigned char *digest, const char **virname, const struct cli_matcher *root, cli_hash_type_t type);
+int cli_hm_have_size(const struct cli_matcher *root, cli_hash_type_t type, uint32_t size);
+int cli_hm_have_wild(const struct cli_matcher *root, cli_hash_type_t type);
+int cli_hm_have_any(const struct cli_matcher *root, cli_hash_type_t type);
void hm_free(struct cli_matcher *root);
#endif
diff --git a/libclamav/matcher-pcre.c b/libclamav/matcher-pcre.c
index e5678536c9..a0da207e7c 100644
--- a/libclamav/matcher-pcre.c
+++ b/libclamav/matcher-pcre.c
@@ -580,9 +580,9 @@ cl_error_t cli_pcre_scanbuf(const unsigned char *buffer, uint32_t length, const
unsigned int i, evalcnt = 0;
uint64_t evalids = 0;
uint32_t global, encompass, rolling;
- int rc = 0, options = 0;
- uint32_t offset = 0;
- uint8_t viruses_found = 0;
+ int rc = 0;
+ int options = 0;
+ uint32_t offset = 0;
if ((root->pcre_metas == 0) || (!root->pcre_metatable) || (ctx && ctx->dconf && !(ctx->dconf->pcre & PCRE_CONF_SUPPORT)))
return CL_SUCCESS;
@@ -731,15 +731,19 @@ cl_error_t cli_pcre_scanbuf(const unsigned char *buffer, uint32_t length, const
newres->offset = adjbuffer + p_res.match[0];
*res = newres;
} else {
- ret = CL_CLEAN;
- viruses_found = 1;
- if (ctx)
- ret = cli_append_virus(ctx, "test");
- if (virname)
+ ret = CL_VIRUS;
+
+ if (virname) {
*virname = "test";
- if (!ctx || !SCAN_ALLMATCHES)
- if (ret != CL_CLEAN)
+ }
+
+ // ctx is not provided in the unit tests.
+ if (ctx) {
+ ret = cli_append_virus(ctx, "test");
+ if (ret != CL_SUCCESS) {
break;
+ }
+ }
}
}
}
@@ -764,8 +768,6 @@ cl_error_t cli_pcre_scanbuf(const unsigned char *buffer, uint32_t length, const
/* free match results */
cli_pcre_results_free(&p_res);
- if (ret == CL_SUCCESS && viruses_found)
- return CL_VIRUS;
return ret;
}
diff --git a/libclamav/matcher.c b/libclamav/matcher.c
index 330ac795c3..3bee45f8e2 100644
--- a/libclamav/matcher.c
+++ b/libclamav/matcher.c
@@ -116,7 +116,6 @@ static inline cl_error_t matcher_run(const struct cli_matcher *root,
struct filter_match_info info;
uint32_t orig_length, orig_offset;
const unsigned char *orig_buffer;
- unsigned int viruses_found = 0;
if (root->filter) {
if (filter_search_ext(root->filter, buffer, length, &info) == -1) {
@@ -151,31 +150,23 @@ static inline cl_error_t matcher_run(const struct cli_matcher *root,
} else {
ret = cli_bm_scanbuff(buffer, length, virname, NULL, root, offset, tinfo, offdata, ctx);
}
- if (ret != CL_CLEAN) {
+ if (ret != CL_SUCCESS) {
if (ret != CL_VIRUS)
return ret;
-
/* else (ret == CL_VIRUS) */
- if (SCAN_ALLMATCHES)
- viruses_found = 1;
- else {
- ret = cli_append_virus(ctx, *virname);
- if (ret != CL_CLEAN)
- return ret;
- }
+
+ ret = cli_append_virus(ctx, *virname);
+ if (ret != CL_SUCCESS)
+ return ret;
}
}
perf_log_tries(acmode, 0, length);
ret = cli_ac_scanbuff(buffer, length, virname, NULL, acres, root, mdata, offset, ftype, ftoffset, acmode, ctx);
- if (ret != CL_CLEAN) {
+ if (ret != CL_SUCCESS) {
if (ret == CL_VIRUS) {
- if (SCAN_ALLMATCHES)
- viruses_found = 1;
- else {
- ret = cli_append_virus(ctx, *virname);
- if (ret != CL_CLEAN)
- return ret;
- }
+ ret = cli_append_virus(ctx, *virname);
+ if (ret != CL_SUCCESS)
+ return ret;
} else if (ret > CL_TYPENO && acmode & AC_SCAN_VIR) {
saved_ret = ret;
} else {
@@ -257,12 +248,12 @@ static inline cl_error_t matcher_run(const struct cli_matcher *root,
#endif /* HAVE_PCRE */
/* end experimental fragment */
- if (ctx && !SCAN_ALLMATCHES && ret == CL_VIRUS) {
- return cli_append_virus(ctx, *virname);
- }
- if (ctx && SCAN_ALLMATCHES && viruses_found) {
- return CL_VIRUS;
+ if (ctx && ret == CL_VIRUS) {
+ ret = cli_append_virus(ctx, *virname);
+ if (ret != CL_SUCCESS)
+ return ret;
}
+
if (saved_ret && ret == CL_CLEAN) {
return saved_ret;
}
@@ -273,9 +264,9 @@ static inline cl_error_t matcher_run(const struct cli_matcher *root,
cl_error_t cli_scan_buff(const unsigned char *buffer, uint32_t length, uint32_t offset, cli_ctx *ctx, cli_file_t ftype, struct cli_ac_data **acdata)
{
cl_error_t ret = CL_CLEAN;
- unsigned int i = 0, j = 0, viruses_found = 0;
- struct cli_ac_data mdata;
- struct cli_matcher *groot, *troot = NULL;
+ unsigned int i = 0, j = 0;
+ struct cli_ac_data matcher_data;
+ struct cli_matcher *generic_ac_root, *target_ac_root = NULL;
const char *virname = NULL;
const struct cl_engine *engine = ctx->engine;
@@ -284,52 +275,68 @@ cl_error_t cli_scan_buff(const unsigned char *buffer, uint32_t length, uint32_t
return CL_ENULLARG;
}
- groot = engine->root[0]; /* generic signatures */
+ generic_ac_root = engine->root[0]; /* generic signatures */
+
+ if (ftype != CL_TYPE_ANY) {
+ // Identify the target type, to find the matcher root for that target.
- if (ftype) {
for (i = 1; i < CLI_MTARGETS; i++) {
for (j = 0; j < cli_mtargets[i].target_count; ++j) {
if (cli_mtargets[i].target[j] == ftype) {
- troot = ctx->engine->root[i];
- break;
+ // Identified the target type, now get the matcher root for that target.
+ target_ac_root = ctx->engine->root[i];
+ break; // Break out of inner loop
}
}
- if (troot) break;
+ if (target_ac_root) break;
}
}
- if (troot) {
+ if (target_ac_root) {
+ /* If a target-specific specific signature root was found for the given file type, match with it. */
- if (!acdata && (ret = cli_ac_initdata(&mdata, troot->ac_partsigs, troot->ac_lsigs, troot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)))
- return ret;
+ if (!acdata) {
+ // no ac matcher data was provided, so we need to initialize our own.
+ ret = cli_ac_initdata(&matcher_data, target_ac_root->ac_partsigs, target_ac_root->ac_lsigs, target_ac_root->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN);
+ if (CL_SUCCESS != ret) {
+ return ret;
+ }
+ }
- ret = matcher_run(troot, buffer, length, &virname, acdata ? (acdata[0]) : (&mdata), offset, NULL, ftype, NULL, AC_SCAN_VIR, PCRE_SCAN_BUFF, NULL, ctx->fmap, NULL, NULL, ctx);
+ ret = matcher_run(target_ac_root, buffer, length, &virname,
+ acdata ? (acdata[0]) : (&matcher_data),
+ offset, NULL, ftype, NULL, AC_SCAN_VIR, PCRE_SCAN_BUFF, NULL, ctx->fmap, NULL, NULL, ctx);
- if (!acdata)
- cli_ac_freedata(&mdata);
+ if (!acdata) {
+ // no longer need our AC local matcher data (if using)
+ cli_ac_freedata(&matcher_data);
+ }
- if (ret == CL_EMEM)
+ if (ret == CL_EMEM || ret == CL_VIRUS) {
return ret;
- if (ret == CL_VIRUS) {
- viruses_found = 1;
- if (ctx && !SCAN_ALLMATCHES) {
- return ret;
- }
}
- }
- virname = NULL;
+ // reset virname back to NULL for matching with the generic AC root.
+ virname = NULL;
+ }
- if (!acdata && (ret = cli_ac_initdata(&mdata, groot->ac_partsigs, groot->ac_lsigs, groot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)))
- return ret;
+ if (!acdata) {
+ // no ac matcher data was provided, so we need to initialize our own.
+ ret = cli_ac_initdata(&matcher_data, generic_ac_root->ac_partsigs, generic_ac_root->ac_lsigs, generic_ac_root->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN);
+ if (CL_SUCCESS != ret) {
+ return ret;
+ }
+ }
- ret = matcher_run(groot, buffer, length, &virname, acdata ? (acdata[1]) : (&mdata), offset, NULL, ftype, NULL, AC_SCAN_VIR, PCRE_SCAN_BUFF, NULL, ctx->fmap, NULL, NULL, ctx);
+ ret = matcher_run(generic_ac_root, buffer, length, &virname,
+ acdata ? (acdata[1]) : (&matcher_data),
+ offset, NULL, ftype, NULL, AC_SCAN_VIR, PCRE_SCAN_BUFF, NULL, ctx->fmap, NULL, NULL, ctx);
- if (!acdata)
- cli_ac_freedata(&mdata);
+ if (!acdata) {
+ // no longer need our AC local matcher data (if using)
+ cli_ac_freedata(&matcher_data);
+ }
- if (viruses_found)
- return CL_VIRUS;
return ret;
}
@@ -339,7 +346,7 @@ cl_error_t cli_scan_buff(const unsigned char *buffer, uint32_t length, uint32_t
* offdata[2]: max shift
* offdata[3]: section number
*/
-cl_error_t cli_caloff(const char *offstr, const struct cli_target_info *info, unsigned int target, uint32_t *offdata, uint32_t *offset_min, uint32_t *offset_max)
+cl_error_t cli_caloff(const char *offstr, const struct cli_target_info *info, cli_target_t target, uint32_t *offdata, uint32_t *offset_min, uint32_t *offset_max)
{
char offcpy[65] = {0};
unsigned int n = 0, val = 0;
@@ -446,7 +453,7 @@ cl_error_t cli_caloff(const char *offstr, const struct cli_target_info *info, un
if (offdata[0] != CLI_OFF_ANY && offdata[0] != CLI_OFF_ABSOLUTE &&
offdata[0] != CLI_OFF_EOF_MINUS && offdata[0] != CLI_OFF_MACRO) {
- if (target != 1 && target != 6 && target != 9) {
+ if (target != TARGET_PE && target != TARGET_ELF && target != TARGET_MACHO) {
cli_errmsg("cli_caloff: Invalid offset type for target %u\n", target);
return CL_EMALFDB;
}
@@ -526,22 +533,27 @@ void cli_targetinfo_init(struct cli_target_info *info)
cli_exe_info_init(&(info->exeinfo), 0);
}
-void cli_targetinfo(struct cli_target_info *info, unsigned int target, cli_ctx *ctx)
+void cli_targetinfo(struct cli_target_info *info, cli_target_t target, cli_ctx *ctx)
{
- int (*einfo)(cli_ctx *, struct cli_exe_info *) = NULL;
+ cl_error_t (*einfo)(cli_ctx *, struct cli_exe_info *) = NULL;
info->fsize = ctx->fmap->len;
- if (target == 1)
- einfo = cli_pe_targetinfo;
- else if (target == 6)
- einfo = cli_elfheader;
- else if (target == 9)
- einfo = cli_machoheader;
- else
- return;
+ switch (target) {
+ case TARGET_PE:
+ einfo = cli_pe_targetinfo;
+ break;
+ case TARGET_ELF:
+ einfo = cli_elfheader;
+ break;
+ case TARGET_MACHO:
+ einfo = cli_machoheader;
+ break;
+ default:
+ return;
+ }
- if (einfo(ctx, &info->exeinfo))
+ if (CL_SUCCESS != einfo(ctx, &info->exeinfo))
info->status = -1;
else
info->status = 1;
@@ -578,7 +590,7 @@ cl_error_t cli_check_fp(cli_ctx *ctx, const char *vname)
while (stack_index >= 0) {
map = ctx->recursion_stack[stack_index].fmap;
- if (CL_SUCCESS != fmap_get_MD5(map, &digest)) {
+ if (CL_SUCCESS != fmap_get_hash(map, &digest, CLI_HASH_MD5)) {
cli_dbgmsg("cli_check_fp: Failed to get a hash for the map at stack index # %u\n", stack_index);
stack_index--;
continue;
@@ -731,7 +743,6 @@ int32_t cli_bcapi_matchicon(struct cli_bc_ctx *ctx, const uint8_t *grp1, int32_t
{
cl_error_t ret;
char group1[128], group2[128];
- const char **oldvirname;
struct cli_exe_info info;
// TODO This isn't a good check, since EP will be zero for DLLs and
@@ -768,7 +779,7 @@ int32_t cli_bcapi_matchicon(struct cli_bc_ctx *ctx, const uint8_t *grp1, int32_t
return (int32_t)ret;
}
-cl_error_t cli_scan_desc(int desc, cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode, struct cli_ac_result **acres, const char *name)
+cl_error_t cli_scan_desc(int desc, cli_ctx *ctx, cli_file_t ftype, bool filetype_only, struct cli_matched_type **ftoffset, unsigned int acmode, struct cli_ac_result **acres, const char *name)
{
cl_error_t status = CL_CLEAN;
int empty;
@@ -790,7 +801,7 @@ cl_error_t cli_scan_desc(int desc, cli_ctx *ctx, cli_file_t ftype, uint8_t ftonl
goto done;
}
- status = cli_scan_fmap(ctx, ftype, ftonly, ftoffset, acmode, acres, NULL);
+ status = cli_scan_fmap(ctx, ftype, filetype_only, ftoffset, acmode, acres, NULL);
map->dont_cache_flag = ctx->fmap->dont_cache_flag; /* Set the parent layer's "don't cache" flag to match the child.
TODO: This may not be needed since `emax_reached()` should've
@@ -886,11 +897,6 @@ static cl_error_t lsig_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_a
(void)cli_recursion_stack_pop(ctx); /* Restore the parent fmap */
- if (CL_VIRUS == status) {
- status = CL_VIRUS;
- goto done;
- }
-
goto done;
}
}
@@ -903,7 +909,7 @@ static cl_error_t lsig_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_a
if (CL_VIRUS == matchicon(ctx, &target_info->exeinfo, ac_lsig->tdb.icongrp1, ac_lsig->tdb.icongrp2)) {
if (!ac_lsig->bc_idx) {
status = cli_append_virus(ctx, ac_lsig->virname);
- if (status != CL_CLEAN) {
+ if (status != CL_SUCCESS) {
goto done;
}
} else if (CL_VIRUS == cli_bytecode_runlsig(ctx, target_info, &ctx->engine->bcs, ac_lsig->bc_idx, acdata->lsigcnt[lsid], acdata->lsigsuboff_first[lsid], ctx->fmap)) {
@@ -915,7 +921,7 @@ static cl_error_t lsig_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_a
}
if (!ac_lsig->bc_idx) {
status = cli_append_virus(ctx, ac_lsig->virname);
- if (status != CL_CLEAN) {
+ if (status != CL_SUCCESS) {
goto done;
}
}
@@ -965,7 +971,6 @@ static cl_error_t yara_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_a
cl_error_t cli_exp_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_ac_data *acdata, struct cli_target_info *target_info, const char *hash)
{
- uint8_t viruses_found = 0;
uint32_t i;
cl_error_t rc = CL_SUCCESS;
@@ -977,90 +982,118 @@ cl_error_t cli_exp_eval(cli_ctx *ctx, struct cli_matcher *root, struct cli_ac_da
rc = yara_eval(ctx, root, acdata, target_info, hash, i);
#endif
if (rc == CL_VIRUS) {
- viruses_found = 1;
- if (SCAN_ALLMATCHES)
- continue;
break;
}
}
- if (viruses_found)
+
+ if (rc == CL_VIRUS) {
return CL_VIRUS;
+ }
return CL_CLEAN;
}
-cl_error_t cli_scan_fmap(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode, struct cli_ac_result **acres, unsigned char *refhash)
+cl_error_t cli_scan_fmap(cli_ctx *ctx, cli_file_t ftype, bool filetype_only, struct cli_matched_type **ftoffset, unsigned int acmode, struct cli_ac_result **acres, unsigned char *refhash)
{
const unsigned char *buff;
cl_error_t ret = CL_CLEAN, type = CL_CLEAN;
- int compute_hash[CLI_HASH_AVAIL_TYPES];
- unsigned int i = 0, j = 0, bm_offmode = 0;
- uint32_t maxpatlen, bytes, offset = 0;
- struct cli_ac_data gdata, tdata;
- struct cli_bm_off toff;
- struct cli_pcre_off gpoff, tpoff;
- unsigned char digest[CLI_HASH_AVAIL_TYPES][32];
- struct cli_matcher *groot = NULL, *troot = NULL;
+ bool compute_hash[CLI_HASH_AVAIL_TYPES];
+ unsigned int i = 0, j = 0;
+ uint32_t maxpatlen, bytes, offset = 0;
+
+ struct cli_ac_data generic_ac_data;
+ bool gdata_initialized = false;
+
+ struct cli_ac_data target_ac_data;
+ bool tdata_initialized = false;
+
+ struct cli_bm_off bm_offsets_table;
+ bool bm_offsets_table_initialized = false;
+
+ struct cli_pcre_off generic_pcre_offsets_table;
+ bool generic_pcre_offsets_table_initialized = false;
+
+ struct cli_pcre_off target_pcre_offsets_table;
+ bool target_pcre_offsets_table_initialized = false;
+
+ unsigned char digest[CLI_HASH_AVAIL_TYPES][CLI_HASHLEN_MAX];
+
+ struct cli_matcher *generic_ac_root = NULL, *target_ac_root = NULL;
+
struct cli_target_info info;
+ bool info_initialized = false;
+
struct cli_matcher *hdb, *fp;
- const char *virname;
- uint32_t viruses_found = 0;
- void *md5ctx, *sha1ctx, *sha256ctx;
+
+ void *md5ctx = NULL;
+ void *sha1ctx = NULL;
+ void *sha256ctx = NULL;
if (!ctx->engine) {
cli_errmsg("cli_scan_fmap: engine == NULL\n");
- return CL_ENULLARG;
+ ret = CL_ENULLARG;
+ goto done;
}
md5ctx = cl_hash_init("md5");
- if (!(md5ctx))
- return CL_EMEM;
+ if (!(md5ctx)) {
+ ret = CL_EMEM;
+ goto done;
+ }
sha1ctx = cl_hash_init("sha1");
if (!(sha1ctx)) {
- cl_hash_destroy(md5ctx);
- return CL_EMEM;
+ ret = CL_EMEM;
+ goto done;
}
sha256ctx = cl_hash_init("sha256");
if (!(sha256ctx)) {
- cl_hash_destroy(md5ctx);
- cl_hash_destroy(sha1ctx);
- return CL_EMEM;
+ ret = CL_EMEM;
+ goto done;
+ }
+
+ if (!filetype_only) {
+ generic_ac_root = ctx->engine->root[0]; /* generic signatures */
}
- if (!ftonly)
- groot = ctx->engine->root[0]; /* generic signatures */
+ if (ftype != CL_TYPE_ANY) {
+ // Identify the target type, to find the matcher root for that target.
- if (ftype) {
for (i = 1; i < CLI_MTARGETS; i++) {
for (j = 0; j < cli_mtargets[i].target_count; ++j) {
if (cli_mtargets[i].target[j] == ftype) {
- troot = ctx->engine->root[i];
- break;
+ // Identified the target type, now get the matcher root for that target.
+ target_ac_root = ctx->engine->root[i];
+ break; // Break out of inner loop
}
}
- if (troot) break;
+ if (target_ac_root) break;
}
}
- if (ftonly) {
- if (!troot) {
- cl_hash_destroy(md5ctx);
- cl_hash_destroy(sha1ctx);
- cl_hash_destroy(sha256ctx);
- return CL_CLEAN;
+ if (!generic_ac_root) {
+ if (!target_ac_root) {
+ // Don't have a matcher root for either generic signatures or target-specific signatures.
+ // Nothing to do!
+ ret = CL_CLEAN;
+ goto done;
}
- maxpatlen = troot->maxpatlen;
+ // Only have a matcher root for target-specific signatures.
+ maxpatlen = target_ac_root->maxpatlen;
} else {
- if (troot)
- maxpatlen = MAX(troot->maxpatlen, groot->maxpatlen);
- else
- maxpatlen = groot->maxpatlen;
+ if (target_ac_root) {
+ // Have both generic and target-specific signatures.
+ maxpatlen = MAX(target_ac_root->maxpatlen, generic_ac_root->maxpatlen);
+ } else {
+ // Only have generic signatures.
+ maxpatlen = generic_ac_root->maxpatlen;
+ }
}
cli_targetinfo_init(&info);
cli_targetinfo(&info, i, ctx);
+ info_initialized = true;
if (-1 == info.status) {
cli_dbgmsg("cli_scan_fmap: Failed to successfully parse the executable header. "
@@ -1092,98 +1125,93 @@ cl_error_t cli_scan_fmap(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct
if (1 == info.status && i == 1) {
ret = cli_check_auth_header(ctx, &(info.exeinfo));
-
- if ((ret == CL_VIRUS || ret == CL_VERIFIED) && !SCAN_ALLMATCHES) {
- cli_targetinfo_destroy(&info);
- cl_hash_destroy(md5ctx);
- cl_hash_destroy(sha1ctx);
- cl_hash_destroy(sha256ctx);
- return ret;
+ if (ret == CL_VIRUS || ret == CL_VERIFIED) {
+ goto done;
}
ret = CL_CLEAN;
}
- if (!ftonly) {
- if ((ret = cli_ac_initdata(&gdata, groot->ac_partsigs, groot->ac_lsigs, groot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)) ||
- (ret = cli_ac_caloff(groot, &gdata, &info))) {
- cli_targetinfo_destroy(&info);
- cl_hash_destroy(md5ctx);
- cl_hash_destroy(sha1ctx);
- cl_hash_destroy(sha256ctx);
- return ret;
+ if (!filetype_only) {
+ /* If we're not doing a filetype-only scan, so we definitely need to include generic signatures.
+ So initialize the ac data for the generic signatures root. */
+
+ ret = cli_ac_initdata(&generic_ac_data, generic_ac_root->ac_partsigs, generic_ac_root->ac_lsigs, generic_ac_root->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN);
+ if (CL_SUCCESS != ret) {
+ goto done;
}
- if ((ret = cli_pcre_recaloff(groot, &gpoff, &info, ctx))) {
- cli_ac_freedata(&gdata);
- cli_targetinfo_destroy(&info);
- cl_hash_destroy(md5ctx);
- cl_hash_destroy(sha1ctx);
- cl_hash_destroy(sha256ctx);
- return ret;
+ gdata_initialized = true;
+
+ /* Recalculate the relative offsets in ac sigs (e.g. those that are based on pe/elf/macho section start/end). */
+ ret = cli_ac_caloff(generic_ac_root, &generic_ac_data, &info);
+ if (CL_SUCCESS != ret) {
+ goto done;
+ }
+
+ /* Recalculate the pcre offsets.
+ This does an allocation, that we will need to free later. */
+ ret = cli_pcre_recaloff(generic_ac_root, &generic_pcre_offsets_table, &info, ctx);
+ if (CL_SUCCESS != ret) {
+ goto done;
}
+ generic_pcre_offsets_table_initialized = true;
}
- if (troot) {
- if ((ret = cli_ac_initdata(&tdata, troot->ac_partsigs, troot->ac_lsigs, troot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN)) ||
- (ret = cli_ac_caloff(troot, &tdata, &info))) {
- if (!ftonly) {
- cli_ac_freedata(&gdata);
- cli_pcre_freeoff(&gpoff);
- }
- cli_targetinfo_destroy(&info);
- cl_hash_destroy(md5ctx);
- cl_hash_destroy(sha1ctx);
- cl_hash_destroy(sha256ctx);
- return ret;
+ if (target_ac_root) {
+ /* We have to match against target-specific signatures.
+ So initialize the ac data for the target-specific signatures root. */
+
+ ret = cli_ac_initdata(&target_ac_data, target_ac_root->ac_partsigs, target_ac_root->ac_lsigs, target_ac_root->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN);
+ if (CL_SUCCESS != ret) {
+ goto done;
}
- if (troot->bm_offmode) {
- if (ctx->fmap->len >= CLI_DEFAULT_BM_OFFMODE_FSIZE) {
- if ((ret = cli_bm_initoff(troot, &toff, &info))) {
- if (!ftonly) {
- cli_ac_freedata(&gdata);
- cli_pcre_freeoff(&gpoff);
- }
+ tdata_initialized = true;
- cli_ac_freedata(&tdata);
- cli_targetinfo_destroy(&info);
- cl_hash_destroy(md5ctx);
- cl_hash_destroy(sha1ctx);
- cl_hash_destroy(sha256ctx);
- return ret;
- }
+ /* Recalculate the relative offsets in ac sigs (e.g. those that are based on pe/elf/macho section start/end). */
+ ret = cli_ac_caloff(target_ac_root, &target_ac_data, &info);
+ if (CL_SUCCESS != ret) {
+ goto done;
+ }
- bm_offmode = 1;
+ if (target_ac_root->bm_offmode) {
+ if (ctx->fmap->len >= CLI_DEFAULT_BM_OFFMODE_FSIZE) {
+ /* Recalculate the relative offsets in boyer-moore signatures (e.g. those that are based on pe/elf/macho section start/end). */
+ ret = cli_bm_initoff(target_ac_root, &bm_offsets_table, &info);
+ if (CL_SUCCESS != ret) {
+ goto done;
+ }
+ bm_offsets_table_initialized = true;
}
}
- if ((ret = cli_pcre_recaloff(troot, &tpoff, &info, ctx))) {
- if (!ftonly) {
- cli_ac_freedata(&gdata);
- cli_pcre_freeoff(&gpoff);
- }
- cli_ac_freedata(&tdata);
- if (bm_offmode)
- cli_bm_freeoff(&toff);
- cli_targetinfo_destroy(&info);
- cl_hash_destroy(md5ctx);
- cl_hash_destroy(sha1ctx);
- cl_hash_destroy(sha256ctx);
- return ret;
+ /* Recalculate the pcre offsets.
+ This does an allocation, that we will need to free later. */
+ ret = cli_pcre_recaloff(target_ac_root, &target_pcre_offsets_table, &info, ctx);
+ if (CL_SUCCESS != ret) {
+ goto done;
}
+ target_pcre_offsets_table_initialized = true;
}
hdb = ctx->engine->hm_hdb;
fp = ctx->engine->hm_fp;
- if (!ftonly && hdb) {
+ if (!filetype_only && hdb) {
+ /* We're not just doing file typing, we're checking for viruses.
+ So we need to compute the hash sigs, if there are any.
+
+ Computing the hash in chunks the same size and time that we do for
+ matching with the AC & BM pattern matchers is an optimization so we
+ we can do both processes while the cache is still hot. */
+
if (!refhash) {
if (cli_hm_have_size(hdb, CLI_HASH_MD5, ctx->fmap->len) ||
cli_hm_have_size(fp, CLI_HASH_MD5, ctx->fmap->len) ||
cli_hm_have_wild(hdb, CLI_HASH_MD5) ||
cli_hm_have_wild(fp, CLI_HASH_MD5)) {
- compute_hash[CLI_HASH_MD5] = 1;
+ compute_hash[CLI_HASH_MD5] = true;
} else {
- compute_hash[CLI_HASH_MD5] = 0;
+ compute_hash[CLI_HASH_MD5] = false;
}
} else {
compute_hash[CLI_HASH_MD5] = 0;
@@ -1194,18 +1222,18 @@ cl_error_t cli_scan_fmap(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct
cli_hm_have_wild(hdb, CLI_HASH_SHA1) ||
cli_hm_have_size(fp, CLI_HASH_SHA1, ctx->fmap->len) ||
cli_hm_have_wild(fp, CLI_HASH_SHA1)) {
- compute_hash[CLI_HASH_SHA1] = 1;
+ compute_hash[CLI_HASH_SHA1] = true;
} else {
- compute_hash[CLI_HASH_SHA1] = 0;
+ compute_hash[CLI_HASH_SHA1] = false;
}
if (cli_hm_have_size(hdb, CLI_HASH_SHA256, ctx->fmap->len) ||
cli_hm_have_wild(hdb, CLI_HASH_SHA256) ||
cli_hm_have_size(fp, CLI_HASH_SHA256, ctx->fmap->len) ||
cli_hm_have_wild(fp, CLI_HASH_SHA256)) {
- compute_hash[CLI_HASH_SHA256] = 1;
+ compute_hash[CLI_HASH_SHA256] = true;
} else {
- compute_hash[CLI_HASH_SHA256] = 0;
+ compute_hash[CLI_HASH_SHA256] = false;
}
}
@@ -1216,56 +1244,27 @@ cl_error_t cli_scan_fmap(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct
if (ctx->scanned)
*ctx->scanned += bytes / CL_COUNT_PRECISION;
- if (troot) {
- virname = NULL;
- ret = matcher_run(troot, buff, bytes, &virname, &tdata, offset, &info, ftype, ftoffset, acmode, PCRE_SCAN_FMAP, acres, ctx->fmap, bm_offmode ? &toff : NULL, &tpoff, ctx);
-
- if (virname) {
- /* virname already appended by matcher_run */
- viruses_found = 1;
- }
- if ((ret == CL_VIRUS && !SCAN_ALLMATCHES) || ret == CL_EMEM) {
- if (!ftonly) {
- cli_ac_freedata(&gdata);
- cli_pcre_freeoff(&gpoff);
- }
-
- cli_ac_freedata(&tdata);
- if (bm_offmode)
- cli_bm_freeoff(&toff);
- cli_pcre_freeoff(&tpoff);
+ if (target_ac_root) {
+ const char *virname = NULL;
- cli_targetinfo_destroy(&info);
- cl_hash_destroy(md5ctx);
- cl_hash_destroy(sha1ctx);
- cl_hash_destroy(sha256ctx);
- return ret;
+ ret = matcher_run(target_ac_root, buff, bytes, &virname, &target_ac_data, offset,
+ &info, ftype, ftoffset, acmode, PCRE_SCAN_FMAP, acres, ctx->fmap,
+ bm_offsets_table_initialized ? &bm_offsets_table : NULL,
+ &target_pcre_offsets_table, ctx);
+ if (ret == CL_VIRUS || ret == CL_EMEM) {
+ goto done;
}
}
- if (!ftonly) {
- virname = NULL;
- ret = matcher_run(groot, buff, bytes, &virname, &gdata, offset, &info, ftype, ftoffset, acmode, PCRE_SCAN_FMAP, acres, ctx->fmap, NULL, &gpoff, ctx);
-
- if (virname) {
- /* virname already appended by matcher_run */
- viruses_found = 1;
- }
- if ((ret == CL_VIRUS && !SCAN_ALLMATCHES) || ret == CL_EMEM) {
- cli_ac_freedata(&gdata);
- cli_pcre_freeoff(&gpoff);
- if (troot) {
- cli_ac_freedata(&tdata);
- if (bm_offmode)
- cli_bm_freeoff(&toff);
- cli_pcre_freeoff(&tpoff);
- }
+ if (!filetype_only) {
+ const char *virname = NULL;
- cli_targetinfo_destroy(&info);
- cl_hash_destroy(md5ctx);
- cl_hash_destroy(sha1ctx);
- cl_hash_destroy(sha256ctx);
- return ret;
+ ret = matcher_run(generic_ac_root, buff, bytes, &virname, &generic_ac_data, offset,
+ &info, ftype, ftoffset, acmode, PCRE_SCAN_FMAP, acres, ctx->fmap,
+ NULL,
+ &generic_pcre_offsets_table, ctx);
+ if (ret == CL_VIRUS || ret == CL_EMEM) {
+ goto done;
} else if ((acmode & AC_SCAN_FT) && ((cli_file_t)ret >= CL_TYPENO)) {
if (ret > type)
type = ret;
@@ -1292,105 +1291,120 @@ cl_error_t cli_scan_fmap(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct
offset += bytes - maxpatlen;
}
- if (!ftonly && hdb) {
- enum CLI_HASH_TYPE hashtype, hashtype2;
+ if (!filetype_only && hdb) {
+ /* We're not just doing file typing, we're scanning for malware.
+ So we need to check the hash sigs, if there are any. */
+
+ cli_hash_type_t hashtype;
if (compute_hash[CLI_HASH_MD5]) {
cl_finish_hash(md5ctx, digest[CLI_HASH_MD5]);
md5ctx = NULL;
+
+ // Save the MD5 hash for later use (e.g. in FP checks).
+ fmap_set_hash(ctx->fmap, digest[CLI_HASH_MD5], CLI_HASH_MD5);
}
- if (refhash)
+ if (refhash) {
+ // Set "compute_hash" to 1 because we'll use this later to know if we have a hash to check.
compute_hash[CLI_HASH_MD5] = 1;
+ }
+
if (compute_hash[CLI_HASH_SHA1]) {
cl_finish_hash(sha1ctx, digest[CLI_HASH_SHA1]);
sha1ctx = NULL;
+
+ // Save the SHA1 hash for later use (e.g. in FP checks).
+ fmap_set_hash(ctx->fmap, digest[CLI_HASH_SHA1], CLI_HASH_SHA1);
}
if (compute_hash[CLI_HASH_SHA256]) {
cl_finish_hash(sha256ctx, digest[CLI_HASH_SHA256]);
sha256ctx = NULL;
+
+ // Save the SHA256 hash for later use (e.g. in FP checks).
+ fmap_set_hash(ctx->fmap, digest[CLI_HASH_SHA256], CLI_HASH_SHA256);
}
- virname = NULL;
for (hashtype = CLI_HASH_MD5; hashtype < CLI_HASH_AVAIL_TYPES; hashtype++) {
+ const char *virname = NULL;
const char *virname_w = NULL;
- int found = 0;
/* If no hash, skip to next type */
- if (!compute_hash[hashtype])
+ if (!compute_hash[hashtype]) {
continue;
-
- /* Do hash scan */
- if ((ret = cli_hm_scan(digest[hashtype], ctx->fmap->len, &virname, hdb, hashtype)) == CL_VIRUS) {
- found += 1;
- }
- if (!found || SCAN_ALLMATCHES) {
- if ((ret = cli_hm_scan_wild(digest[hashtype], &virname_w, hdb, hashtype)) == CL_VIRUS)
- found += 2;
}
- /* If found, do immediate hash-only FP check */
- if (found && fp) {
- for (hashtype2 = CLI_HASH_MD5; hashtype2 < CLI_HASH_AVAIL_TYPES; hashtype2++) {
- if (!compute_hash[hashtype2])
- continue;
- if (cli_hm_scan(digest[hashtype2], ctx->fmap->len, NULL, fp, hashtype2) == CL_VIRUS) {
- found = 0;
- ret = CL_CLEAN;
- break;
- } else if (cli_hm_scan_wild(digest[hashtype2], NULL, fp, hashtype2) == CL_VIRUS) {
- found = 0;
- ret = CL_CLEAN;
- break;
- }
+ /* Do hash scan checking hash sigs with specific size */
+ ret = cli_hm_scan(digest[hashtype], ctx->fmap->len, &virname, hdb, hashtype);
+ if (ret == CL_VIRUS) {
+ /* Matched with size-based hash ... */
+ ret = cli_append_virus(ctx, virname);
+ if (ret != CL_SUCCESS) {
+ goto done;
}
}
- /* If matched size-based hash ... */
- if (found % 2) {
- viruses_found = 1;
- ret = cli_append_virus(ctx, virname);
- if (ret != CL_CLEAN && !SCAN_ALLMATCHES)
- break;
- virname = NULL;
- }
- /* If matched size-agnostic hash ... */
- if (found > 1) {
- viruses_found = 1;
- ret = cli_append_virus(ctx, virname_w);
- if (ret != CL_CLEAN && !SCAN_ALLMATCHES)
- break;
+ /* Do hash scan checking hash sigs with wildcard size */
+ ret = cli_hm_scan_wild(digest[hashtype], &virname_w, hdb, hashtype);
+ if (ret == CL_VIRUS) {
+ /* Matched with size-agnostic hash ... */
+ ret = cli_append_virus(ctx, virname_w);
+ if (ret != CL_SUCCESS) {
+ goto done;
+ }
}
}
}
- cl_hash_destroy(md5ctx);
- cl_hash_destroy(sha1ctx);
- cl_hash_destroy(sha256ctx);
+ /*
+ * Evaluate the logical expressions for clamav logical signatures and YARA rules.
+ */
+ // Evalute for the target-specific signature AC matches.
+ if (NULL != target_ac_root) {
+ if (ret != CL_VIRUS) {
+ ret = cli_exp_eval(ctx, target_ac_root, &target_ac_data, &info, (const char *)refhash);
+ }
+ }
+
+ // Evalute for the generic signature AC matches.
+ if (NULL != generic_ac_root) {
+ if (ret != CL_VIRUS) {
+ ret = cli_exp_eval(ctx, generic_ac_root, &generic_ac_data, &info, (const char *)refhash);
+ }
+ }
- if (troot) {
- if (ret != CL_VIRUS || SCAN_ALLMATCHES)
- ret = cli_exp_eval(ctx, troot, &tdata, &info, (const char *)refhash);
- if (ret == CL_VIRUS)
- viruses_found++;
+done:
+ if (NULL != md5ctx) {
+ cl_hash_destroy(md5ctx);
+ }
+ if (NULL != sha1ctx) {
+ cl_hash_destroy(sha1ctx);
+ }
+ if (NULL != sha256ctx) {
+ cl_hash_destroy(sha256ctx);
+ }
- cli_ac_freedata(&tdata);
- if (bm_offmode)
- cli_bm_freeoff(&toff);
- cli_pcre_freeoff(&tpoff);
+ if (gdata_initialized) {
+ cli_ac_freedata(&generic_ac_data);
+ }
+ if (tdata_initialized) {
+ cli_ac_freedata(&target_ac_data);
}
- if (groot) {
- if (ret != CL_VIRUS || SCAN_ALLMATCHES)
- ret = cli_exp_eval(ctx, groot, &gdata, &info, (const char *)refhash);
- cli_ac_freedata(&gdata);
- cli_pcre_freeoff(&gpoff);
+ if (generic_pcre_offsets_table_initialized) {
+ cli_pcre_freeoff(&generic_pcre_offsets_table);
+ }
+ if (target_pcre_offsets_table_initialized) {
+ cli_pcre_freeoff(&target_pcre_offsets_table);
}
- cli_targetinfo_destroy(&info);
+ if (info_initialized) {
+ cli_targetinfo_destroy(&info);
+ }
- if (SCAN_ALLMATCHES && viruses_found) {
- return CL_VIRUS;
+ if (bm_offsets_table_initialized) {
+ cli_bm_freeoff(&bm_offsets_table);
}
+
if (ret == CL_VIRUS) {
return CL_VIRUS;
}
@@ -1410,25 +1424,26 @@ cl_error_t cli_scan_fmap(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct
cl_error_t cli_matchmeta(cli_ctx *ctx, const char *fname, size_t fsizec, size_t fsizer, int encrypted, unsigned int filepos, int res1, void *res2)
{
const struct cli_cdb *cdb;
- unsigned int viruses_found = 0;
- cl_error_t ret = CL_CLEAN;
+ cl_error_t ret = CL_SUCCESS;
cli_dbgmsg("CDBNAME:%s:%llu:%s:%llu:%llu:%d:%u:%u:%p\n",
cli_ftname(cli_recursion_stack_get_type(ctx, -1)), (long long unsigned)fsizec, fname, (long long unsigned)fsizec, (long long unsigned)fsizer,
encrypted, filepos, res1, res2);
- if (ctx->engine && ctx->engine->cb_meta)
+ if (ctx->engine && ctx->engine->cb_meta) {
if (ctx->engine->cb_meta(cli_ftname(cli_recursion_stack_get_type(ctx, -1)), fsizec, fname, fsizer, encrypted, filepos, ctx->cb_ctx) == CL_VIRUS) {
cli_dbgmsg("inner file blocked by callback: %s\n", fname);
ret = cli_append_virus(ctx, "Detected.By.Callback");
- viruses_found++;
- if (!SCAN_ALLMATCHES || ret != CL_CLEAN)
+ if (ret != CL_SUCCESS) {
return ret;
+ }
}
+ }
- if (!ctx->engine || !(cdb = ctx->engine->cdb))
+ if (NULL == ctx->engine || (NULL == (cdb = ctx->engine->cdb))) {
return CL_CLEAN;
+ }
do {
if (cdb->ctype != CL_TYPE_ANY && cdb->ctype != cli_recursion_stack_get_type(ctx, -1))
@@ -1449,13 +1464,11 @@ cl_error_t cli_matchmeta(cli_ctx *ctx, const char *fname, size_t fsizec, size_t
continue;
ret = cli_append_virus(ctx, cdb->virname);
- viruses_found++;
- if (!SCAN_ALLMATCHES || ret != CL_CLEAN)
+ if (ret != CL_SUCCESS) {
return ret;
+ }
} while ((cdb = cdb->next));
- if (SCAN_ALLMATCHES && viruses_found)
- return CL_VIRUS;
- return CL_CLEAN;
+ return ret;
}
diff --git a/libclamav/matcher.h b/libclamav/matcher.h
index 3a4d07971a..9b19a1626f 100644
--- a/libclamav/matcher.h
+++ b/libclamav/matcher.h
@@ -198,11 +198,29 @@ struct cli_cdb {
struct cli_cdb *next;
};
+typedef enum {
+ TARGET_GENERIC = 0,
+ TARGET_PE = 1,
+ TARGET_OLE2 = 2,
+ TARGET_HTML = 3,
+ TARGET_MAIL = 4,
+ TARGET_GRAPHICS = 5,
+ TARGET_ELF = 6,
+ TARGET_ASCII = 7,
+ TARGET_NOT_USED = 8,
+ TARGET_MACHO = 9,
+ TARGET_PDF = 10,
+ TARGET_FLASH = 11,
+ TARGET_JAVA = 12,
+ TARGET_INTERNAL = 13,
+ TARGET_OTHER = 14,
+} cli_target_t;
+
#define CLI_MAX_TARGETS 10 /* maximum filetypes for a specific target */
struct cli_mtarget {
cli_file_t target[CLI_MAX_TARGETS];
const char *name;
- uint8_t idx; /* idx of matcher */
+ cli_target_t idx; /* idx of matcher */
uint8_t ac_only;
uint8_t enable_prefiltering;
uint8_t target_count; /* must be synced with non-zero values in the target array */
@@ -211,21 +229,21 @@ struct cli_mtarget {
#define CLI_MTARGETS 15
static const struct cli_mtarget cli_mtargets[CLI_MTARGETS] = {
/* All types for target, name, idx, ac_only, pre-filtering?, # of types */
- {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "GENERIC", 0, 0, 1, 1},
- {{CL_TYPE_MSEXE, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "PE", 1, 0, 1, 1},
- {{CL_TYPE_MSOLE2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "OLE2", 2, 1, 0, 1},
- {{CL_TYPE_HTML, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "HTML", 3, 1, 0, 1},
- {{CL_TYPE_MAIL, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "MAIL", 4, 1, 1, 1},
- {{CL_TYPE_GRAPHICS, CL_TYPE_GIF, CL_TYPE_PNG, CL_TYPE_JPEG, CL_TYPE_TIFF, 0, 0, 0, 0, 0}, "GRAPHICS", 5, 1, 0, 5},
- {{CL_TYPE_ELF, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "ELF", 6, 1, 0, 1},
- {{CL_TYPE_TEXT_ASCII, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "ASCII", 7, 1, 1, 1},
- {{CL_TYPE_ERROR, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "NOT USED", 8, 1, 0, 1},
- {{CL_TYPE_MACHO, CL_TYPE_MACHO_UNIBIN, 0, 0, 0, 0, 0, 0, 0, 0}, "MACH-O", 9, 1, 0, 2},
- {{CL_TYPE_PDF, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "PDF", 10, 1, 0, 1},
- {{CL_TYPE_SWF, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "FLASH", 11, 1, 0, 1},
- {{CL_TYPE_JAVA, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "JAVA", 12, 1, 0, 1},
- {{CL_TYPE_INTERNAL, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "INTERNAL", 13, 1, 0, 1},
- {{CL_TYPE_OTHER, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "OTHER", 14, 1, 0, 1}};
+ {{CL_TYPE_ANY, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "GENERIC", TARGET_GENERIC, 0, 1, 1},
+ {{CL_TYPE_MSEXE, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "PE", TARGET_PE, 0, 1, 1},
+ {{CL_TYPE_MSOLE2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "OLE2", TARGET_OLE2, 1, 0, 1},
+ {{CL_TYPE_HTML, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "HTML", TARGET_HTML, 1, 0, 1},
+ {{CL_TYPE_MAIL, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "MAIL", TARGET_MAIL, 1, 1, 1},
+ {{CL_TYPE_GRAPHICS, CL_TYPE_GIF, CL_TYPE_PNG, CL_TYPE_JPEG, CL_TYPE_TIFF, 0, 0, 0, 0, 0}, "GRAPHICS", TARGET_GRAPHICS, 1, 0, 5},
+ {{CL_TYPE_ELF, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "ELF", TARGET_ELF, 1, 0, 1},
+ {{CL_TYPE_TEXT_ASCII, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "ASCII", TARGET_ASCII, 1, 1, 1},
+ {{CL_TYPE_ERROR, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "NOT USED", TARGET_NOT_USED, 1, 0, 1},
+ {{CL_TYPE_MACHO, CL_TYPE_MACHO_UNIBIN, 0, 0, 0, 0, 0, 0, 0, 0}, "MACH-O", TARGET_MACHO, 1, 0, 2},
+ {{CL_TYPE_PDF, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "PDF", TARGET_PDF, 1, 0, 1},
+ {{CL_TYPE_SWF, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "FLASH", TARGET_FLASH, 1, 0, 1},
+ {{CL_TYPE_JAVA, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "JAVA", TARGET_JAVA, 1, 0, 1},
+ {{CL_TYPE_INTERNAL, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "INTERNAL", TARGET_INTERNAL, 1, 0, 1},
+ {{CL_TYPE_OTHER, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "OTHER", TARGET_OTHER, 1, 0, 1}};
// clang-format off
@@ -246,18 +264,32 @@ static const struct cli_mtarget cli_mtargets[CLI_MTARGETS] = {
/**
* @brief Non-magic scan matching using a file buffer for input. Older API
*
- * This function is lower-level, requiring a call to `cli_exp_eval()` after the
- * match to evaluate logical signatures and yara rules.
- *
+ * This function is lower-level than the *magic_scan* functions from scanners.
* This function does not perform file type magic identification and does not use
* the file format scanners.
*
+ * Unlike the similar functions `cli_scan_desc()` and `cli_scan_fmap()` (below),
+ * this function:
+ *
+ * - REQUIRES a call to `cli_exp_eval()` after the match to evaluate logical
+ * signatures and yara rules.
+ *
+ * - Does NOT support filetype detection.
+ *
+ * - Does NOT perform hash-based matching.
+ *
+ * - Does NOT support AC, BM, or PCRE relative-offset signature matching.
+ *
+ * - DOES support passing in externally initialized AC matcher data
+ *
* @param buffer The buffer to be matched.
* @param length The length of the buffer or amount of bytets to match.
* @param offset Offset into the buffer from which to start matching.
* @param ctx The scanning context.
* @param ftype If specified, may limit signature matching trie by target type corresponding with the specified CL_TYPE
- * @param[in,out] acdata A list of pattern maching data structs to contain match results, one for each pattern matching trie.
+ * @param[in,out] acdata (optional) A list of pattern maching data structs to contain match results, one for generic signatures and one for target-specific signatures.
+ * If not provided, the matcher results are lost, outside of this function's return value.
+ * Required if you want to evaluate logical expressions afterwards.
* @return cl_error_t
*/
cl_error_t cli_scan_buff(const unsigned char *buffer, uint32_t length, uint32_t offset, cli_ctx *ctx, cli_file_t ftype, struct cli_ac_data **acdata);
@@ -265,38 +297,52 @@ cl_error_t cli_scan_buff(const unsigned char *buffer, uint32_t length, uint32_t
/**
* @brief Non-magic scan matching using a file descriptor for input.
*
+ * This function is lower-level than the *magic_scan* functions from scanners.
* This function does not perform file type magic identification and does not use
* the file format scanners.
*
- * This function uses the newer cli_scan_fmap() scanning API.
+ * This function does signature matching for generic signatures, target-specific
+ * signatures, and file type recognition signatures to detect embedded files or
+ * to correct the current file type.
+ *
+ * This function is just a wrapper for `cli_scan_fmap()` that converts the file
+ * to an fmap and scans it.
*
* @param desc File descriptor to be used for input
* @param ctx The scanning context.
* @param ftype If specified, may limit signature matching trie by target type corresponding with the specified CL_TYPE
- * @param ftonly Boolean indicating if the scan is for file-type detection only.
- * @param[out] ftoffset A list of file type signature matches with their corresponding offsets.
+ * @param filetype_only Boolean indicating if the scan is for file-type detection only.
+ * @param[out] ftoffset (optional) A list of file type signature matches with their corresponding offsets. If provided, will output the file type signature matches.
* @param acmode Use AC_SCAN_VIR and AC_SCAN_FT to set scanning modes.
* @param[out] acres A list of cli_ac_result AC pattern matching results.
* @param name (optional) Original name of the file (to set fmap name metadata)
* @return cl_error_t
*/
-cl_error_t cli_scan_desc(int desc, cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode, struct cli_ac_result **acres, const char *name);
+cl_error_t cli_scan_desc(int desc, cli_ctx *ctx, cli_file_t ftype, bool filetype_only, struct cli_matched_type **ftoffset, unsigned int acmode, struct cli_ac_result **acres, const char *name);
/**
* @brief Non-magic scan matching of the current fmap in the scan context. Newer API.
*
+ * This function is lower-level than the *magic_scan* functions from scanners.
+ * This function does not perform file type magic identification and does not use
+ * the file format scanners.
+ *
+ * This function does signature matching for generic signatures, target-specific
+ * signatures, and file type recognition signatures to detect embedded files or
+ * to correct the current file type.
+ *
* This API will invoke cli_exp_eval() for you.
*
* @param ctx The scanning context.
* @param ftype If specified, may limit signature matching trie by target type corresponding with the specified CL_TYPE
- * @param ftonly Boolean indicating if the scan is for file-type detection only.
- * @param[out] ftoffset A list of file type signature matches with their corresponding offsets.
+ * @param filetype_only Boolean indicating if the scan is for file-type detection only.
+ * @param[out] ftoffset (optional) A list of file type signature matches with their corresponding offsets. If provided, will output the file type signature matches.
* @param acmode Use AC_SCAN_VIR and AC_SCAN_FT to set scanning modes.
* @param[out] acres A list of cli_ac_result AC pattern matching results.
* @param refhash MD5 hash of the current file, used to save time creating hashes and to limit scan recursion for the HandlerType logical signature FTM feature.
* @return cl_error_t
*/
-cl_error_t cli_scan_fmap(cli_ctx *ctx, cli_file_t ftype, uint8_t ftonly, struct cli_matched_type **ftoffset, unsigned int acmode, struct cli_ac_result **acres, unsigned char *refhash);
+cl_error_t cli_scan_fmap(cli_ctx *ctx, cli_file_t ftype, bool filetype_only, struct cli_matched_type **ftoffset, unsigned int acmode, struct cli_ac_result **acres, unsigned char *refhash);
/**
* @brief Evaluate logical signatures and yara rules given the AC matching results
diff --git a/libclamav/mbox.c b/libclamav/mbox.c
index e1035a4335..927d9d55e0 100644
--- a/libclamav/mbox.c
+++ b/libclamav/mbox.c
@@ -568,15 +568,15 @@ cli_parse_mbox(const char *dir, cli_ctx *ctx)
break;
case MAXREC:
retcode = CL_EMAXREC;
- cli_append_virus_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxRecursion"); // Doing this now because it's actually tracking email recursion,-
- // not fmap recursion, but it still is aborting with stuff not scanned.
- // Also, we didn't have access to the ctx when this happened earlier.
+ cli_append_potentially_unwanted_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxRecursion"); // Doing this now because it's actually tracking email recursion,-
+ // not fmap recursion, but it still is aborting with stuff not scanned.
+ // Also, we didn't have access to the ctx when this happened earlier.
break;
case MAXFILES:
retcode = CL_EMAXFILES;
- cli_append_virus_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxFiles"); // Doing this now because it's actually tracking email parts,-
- // not actual files, but it still is aborting with stuff not scanned.
- // Also, we didn't have access to the ctx when this happened earlier.
+ cli_append_potentially_unwanted_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxFiles"); // Doing this now because it's actually tracking email parts,-
+ // not actual files, but it still is aborting with stuff not scanned.
+ // Also, we didn't have access to the ctx when this happened earlier.
break;
case VIRUS:
retcode = CL_VIRUS;
@@ -739,7 +739,7 @@ hitLineFoldCnt(const char *const line, size_t *lineFoldCnt, cli_ctx *ctx, bool *
if ((*lineFoldCnt) >= HEURISTIC_EMAIL_MAX_LINE_FOLDS_PER_HEADER) {
if (SCAN_HEURISTIC_EXCEEDS_MAX) {
- cli_append_virus(ctx, "Heuristics.Limits.Exceeded.EmailLineFoldCnt");
+ cli_append_potentially_unwanted(ctx, "Heuristics.Limits.Exceeded.EmailLineFoldCnt");
*heuristicFound = true;
}
@@ -755,7 +755,7 @@ haveTooManyHeaderBytes(size_t totalLen, cli_ctx *ctx, bool *heuristicFound)
if (totalLen > HEURISTIC_EMAIL_MAX_HEADER_BYTES) {
if (SCAN_HEURISTIC_EXCEEDS_MAX) {
- cli_append_virus(ctx, "Heuristics.Limits.Exceeded.EmailHeaderBytes");
+ cli_append_potentially_unwanted(ctx, "Heuristics.Limits.Exceeded.EmailHeaderBytes");
*heuristicFound = true;
}
@@ -770,7 +770,7 @@ haveTooManyEmailHeaders(size_t totalHeaderCnt, cli_ctx *ctx, bool *heuristicFoun
if (totalHeaderCnt > HEURISTIC_EMAIL_MAX_HEADERS) {
if (SCAN_HEURISTIC_EXCEEDS_MAX) {
- cli_append_virus(ctx, "Heuristics.Limits.Exceeded.EmailHeaders");
+ cli_append_potentially_unwanted(ctx, "Heuristics.Limits.Exceeded.EmailHeaders");
*heuristicFound = true;
}
@@ -785,7 +785,7 @@ haveTooManyMIMEPartsPerMessage(size_t mimePartCnt, cli_ctx *ctx, mbox_status *rc
if (mimePartCnt >= HEURISTIC_EMAIL_MAX_MIME_PARTS_PER_MESSAGE) {
if (SCAN_HEURISTIC_EXCEEDS_MAX) {
- cli_append_virus(ctx, "Heuristics.Limits.Exceeded.EmailMIMEPartsPerMessage");
+ cli_append_potentially_unwanted(ctx, "Heuristics.Limits.Exceeded.EmailMIMEPartsPerMessage");
*rc = VIRUS;
}
@@ -800,7 +800,7 @@ haveTooManyMIMEArguments(size_t argCnt, cli_ctx *ctx, bool *heuristicFound)
if (argCnt >= HEURISTIC_EMAIL_MAX_ARGUMENTS_PER_HEADER) {
if (SCAN_HEURISTIC_EXCEEDS_MAX) {
- cli_append_virus(ctx, "Heuristics.Limits.Exceeded.EmailMIMEArguments");
+ cli_append_potentially_unwanted(ctx, "Heuristics.Limits.Exceeded.EmailMIMEArguments");
*heuristicFound = true;
}
diff --git a/libclamav/mbr.c b/libclamav/mbr.c
index 26fa332361..c6102117d0 100644
--- a/libclamav/mbr.c
+++ b/libclamav/mbr.c
@@ -41,10 +41,6 @@
//#define DEBUG_MBR_PARSE
//#define DEBUG_EBR_PARSE
-#ifndef PRTN_INTXN_DETECTION
-#define PRTN_INTXN_DETECTION "heuristic.mbrprtnintersect"
-#endif
-
#ifdef DEBUG_MBR_PARSE
#define mbr_parsemsg(...) cli_dbgmsg(__VA_ARGS__)
#else
@@ -64,14 +60,14 @@ enum MBR_STATE {
SEEN_EMPTY
};
-static int mbr_scanextprtn(cli_ctx *ctx, unsigned *prtncount, size_t extlba,
- size_t extlbasize, size_t sectorsize);
-static int mbr_check_mbr(struct mbr_boot_record *record, size_t maplen, size_t sectorsize);
-static int mbr_check_ebr(struct mbr_boot_record *record);
-static int mbr_primary_partition_intersection(cli_ctx *ctx, struct mbr_boot_record mbr, size_t sectorsize);
-static int mbr_extended_partition_intersection(cli_ctx *ctx, unsigned *prtncount, size_t extlba, size_t sectorsize);
+static cl_error_t mbr_scanextprtn(cli_ctx *ctx, unsigned *prtncount, size_t extlba,
+ size_t extlbasize, size_t sectorsize);
+static cl_error_t mbr_check_mbr(struct mbr_boot_record *record, size_t maplen, size_t sectorsize);
+static cl_error_t mbr_check_ebr(struct mbr_boot_record *record);
+static cl_error_t mbr_primary_partition_intersection(cli_ctx *ctx, struct mbr_boot_record mbr, size_t sectorsize);
+static cl_error_t mbr_extended_partition_intersection(cli_ctx *ctx, unsigned *prtncount, size_t extlba, size_t sectorsize);
-int cli_mbr_check(const unsigned char *buff, size_t len, size_t maplen)
+cl_error_t cli_mbr_check(const unsigned char *buff, size_t len, size_t maplen)
{
struct mbr_boot_record mbr;
size_t mbr_base = 0;
@@ -91,7 +87,7 @@ int cli_mbr_check(const unsigned char *buff, size_t len, size_t maplen)
return mbr_check_mbr(&mbr, maplen, sectorsize);
}
-int cli_mbr_check2(cli_ctx *ctx, size_t sectorsize)
+cl_error_t cli_mbr_check2(cli_ctx *ctx, size_t sectorsize)
{
struct mbr_boot_record mbr;
size_t pos = 0, mbr_base = 0;
@@ -135,11 +131,11 @@ int cli_mbr_check2(cli_ctx *ctx, size_t sectorsize)
}
/* sets sectorsize to default value if specified to be 0 */
-int cli_scanmbr(cli_ctx *ctx, size_t sectorsize)
+cl_error_t cli_scanmbr(cli_ctx *ctx, size_t sectorsize)
{
+ cl_error_t status = CL_SUCCESS;
struct mbr_boot_record mbr;
enum MBR_STATE state = SEEN_NOTHING;
- int ret = CL_CLEAN, detection = CL_CLEAN;
size_t pos = 0, mbr_base = 0, partoff = 0;
unsigned i = 0, prtncount = 0;
size_t maplen, partsize;
@@ -148,7 +144,8 @@ int cli_scanmbr(cli_ctx *ctx, size_t sectorsize)
if (!ctx || !ctx->fmap) {
cli_errmsg("cli_scanmbr: Invalid context\n");
- return CL_ENULLARG;
+ status = CL_ENULLARG;
+ goto done;
}
/* sector size calculation, actual value is OS dependent */
@@ -162,7 +159,8 @@ int cli_scanmbr(cli_ctx *ctx, size_t sectorsize)
if ((maplen % sectorsize) != 0) {
cli_dbgmsg("cli_scanmbr: File sized %lu is not a multiple of sector size %lu\n",
(unsigned long)maplen, (unsigned long)sectorsize);
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* sector 0 (first sector) is the master boot record */
@@ -171,35 +169,33 @@ int cli_scanmbr(cli_ctx *ctx, size_t sectorsize)
/* read the master boot record */
if (fmap_readn(ctx->fmap, &mbr, pos, sizeof(mbr)) != sizeof(mbr)) {
cli_dbgmsg("cli_scanmbr: Invalid master boot record\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* convert the little endian to host, include the internal */
mbr_convert_to_host(&mbr);
/* MBR checks */
- ret = mbr_check_mbr(&mbr, maplen, sectorsize);
- if (ret != CL_CLEAN) {
- return ret;
+ status = mbr_check_mbr(&mbr, maplen, sectorsize);
+ if (status != CL_SUCCESS) {
+ status = status;
+ goto done;
}
/* MBR is valid, examine bootstrap code */
- ret = cli_magic_scan_nested_fmap_type(ctx->fmap, 0, sectorsize, ctx, CL_TYPE_ANY, NULL);
- if (ret != CL_CLEAN) {
- if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
- detection = CL_VIRUS;
- else
- return ret;
+ status = cli_magic_scan_nested_fmap_type(ctx->fmap, 0, sectorsize, ctx, CL_TYPE_ANY, NULL);
+ if (status != CL_SUCCESS) {
+ status = status;
+ goto done;
}
/* check that the partition table has no intersections - HEURISTICS */
if (SCAN_HEURISTIC_PARTITION_INTXN && (ctx->dconf->other & OTHER_CONF_PRTNINTXN)) {
- ret = mbr_primary_partition_intersection(ctx, mbr, sectorsize);
- if (ret != CL_CLEAN) {
- if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
- detection = CL_VIRUS;
- else
- return ret;
+ status = mbr_primary_partition_intersection(ctx, mbr, sectorsize);
+ if (status != CL_SUCCESS) {
+ status = status;
+ goto done;
}
}
@@ -226,13 +222,11 @@ int cli_scanmbr(cli_ctx *ctx, size_t sectorsize)
}
state = SEEN_EXTENDED; /* used only to detect multiple extended partitions */
- ret = mbr_scanextprtn(ctx, &prtncount, mbr.entries[i].firstLBA,
- mbr.entries[i].numLBA, sectorsize);
- if (ret != CL_CLEAN) {
- if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
- detection = CL_VIRUS;
- else
- return ret;
+ status = mbr_scanextprtn(ctx, &prtncount, mbr.entries[i].firstLBA,
+ mbr.entries[i].numLBA, sectorsize);
+ if (status != CL_SUCCESS) {
+ status = status;
+ goto done;
}
} else {
prtncount++;
@@ -240,12 +234,10 @@ int cli_scanmbr(cli_ctx *ctx, size_t sectorsize)
partoff = mbr.entries[i].firstLBA * sectorsize;
partsize = mbr.entries[i].numLBA * sectorsize;
mbr_parsemsg("cli_magic_scan_nested_fmap_type: [%u, +%u)\n", partoff, partsize);
- ret = cli_magic_scan_nested_fmap_type(ctx->fmap, partoff, partsize, ctx, CL_TYPE_PART_ANY, NULL);
- if (ret != CL_CLEAN) {
- if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
- detection = CL_VIRUS;
- else
- return ret;
+ status = cli_magic_scan_nested_fmap_type(ctx->fmap, partoff, partsize, ctx, CL_TYPE_PART_ANY, NULL);
+ if (status != CL_SUCCESS) {
+ status = status;
+ goto done;
}
}
}
@@ -254,14 +246,16 @@ int cli_scanmbr(cli_ctx *ctx, size_t sectorsize)
cli_dbgmsg("cli_scanmbr: maximum partitions reached\n");
}
- return detection;
+done:
+
+ return status;
}
-static int mbr_scanextprtn(cli_ctx *ctx, unsigned *prtncount, size_t extlba, size_t extlbasize, size_t sectorsize)
+static cl_error_t mbr_scanextprtn(cli_ctx *ctx, unsigned *prtncount, size_t extlba, size_t extlbasize, size_t sectorsize)
{
+ cl_error_t status = CL_CLEAN;
struct mbr_boot_record ebr;
enum MBR_STATE state = SEEN_NOTHING;
- int ret = CL_CLEAN, detection = CL_CLEAN;
size_t pos = 0, mbr_base = 0, logiclba = 0, extoff = 0, partoff = 0;
size_t partsize, extsize;
unsigned i = 0, j = 0;
@@ -280,16 +274,18 @@ static int mbr_scanextprtn(cli_ctx *ctx, unsigned *prtncount, size_t extlba, siz
pos += (logiclba * sectorsize) + mbr_base;
if (fmap_readn(ctx->fmap, &ebr, pos, sizeof(ebr)) != sizeof(ebr)) {
cli_dbgmsg("cli_scanebr: Invalid extended boot record\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* convert the little endian to host */
mbr_convert_to_host(&ebr);
/* EBR checks */
- ret = mbr_check_ebr(&ebr);
- if (ret != CL_CLEAN) {
- return ret;
+ status = mbr_check_ebr(&ebr);
+ if (status != CL_SUCCESS) {
+ status = status;
+ goto done;
}
/* update state */
@@ -327,7 +323,8 @@ static int mbr_scanextprtn(cli_ctx *ctx, unsigned *prtncount, size_t extlba, siz
break;
default:
cli_warnmsg("cli_scanebr: undefined state for EBR parsing\n");
- return CL_EPARSE;
+ status = CL_EPARSE;
+ goto done;
}
} else if (ebr.entries[j].type == MBR_EXTENDED) {
switch (state) {
@@ -343,10 +340,12 @@ static int mbr_scanextprtn(cli_ctx *ctx, unsigned *prtncount, size_t extlba, siz
case SEEN_EXTENDED:
cli_warnmsg("cli_scanebr: detected a logical boot record "
"with multiple extended partition records\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
default:
cli_dbgmsg("cli_scanebr: undefined state for EBR parsing\n");
- return CL_EPARSE;
+ status = CL_EPARSE;
+ goto done;
}
logiclba = ebr.entries[j].firstLBA;
@@ -371,22 +370,22 @@ static int mbr_scanextprtn(cli_ctx *ctx, unsigned *prtncount, size_t extlba, siz
break;
default:
cli_dbgmsg("cli_scanebr: undefined state for EBR parsing\n");
- return CL_EPARSE;
+ status = CL_EPARSE;
+ goto done;
}
partoff = (extlba + logiclba + ebr.entries[j].firstLBA) * sectorsize;
partsize = ebr.entries[j].numLBA * sectorsize;
if (partoff + partsize > extoff + extsize) {
cli_dbgmsg("cli_scanebr: Invalid extended partition entry\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
- ret = cli_magic_scan_nested_fmap_type(ctx->fmap, partoff, partsize, ctx, CL_TYPE_PART_ANY, NULL);
- if (ret != CL_CLEAN) {
- if (SCAN_ALLMATCHES && (ret == CL_VIRUS))
- detection = CL_VIRUS;
- else
- return ret;
+ status = cli_magic_scan_nested_fmap_type(ctx->fmap, partoff, partsize, ctx, CL_TYPE_PART_ANY, NULL);
+ if (status != CL_SUCCESS) {
+ status = status;
+ goto done;
}
}
} else {
@@ -396,7 +395,8 @@ static int mbr_scanextprtn(cli_ctx *ctx, unsigned *prtncount, size_t extlba, siz
"entry at index %u\n",
j);
/* should we attempt to use these entries? */
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
}
}
@@ -404,7 +404,9 @@ static int mbr_scanextprtn(cli_ctx *ctx, unsigned *prtncount, size_t extlba, siz
cli_dbgmsg("cli_scanmbr: examined %u logical partitions\n", i);
- return detection;
+done:
+
+ return status;
}
void mbr_convert_to_host(struct mbr_boot_record *record)
@@ -421,71 +423,84 @@ void mbr_convert_to_host(struct mbr_boot_record *record)
record->signature = be16_to_host(record->signature);
}
-static int mbr_check_mbr(struct mbr_boot_record *record, size_t maplen, size_t sectorsize)
+static cl_error_t mbr_check_mbr(struct mbr_boot_record *record, size_t maplen, size_t sectorsize)
{
- unsigned i = 0;
- size_t partoff = 0;
- size_t partsize = 0;
+ cl_error_t status = CL_SUCCESS;
+ unsigned i = 0;
+ size_t partoff = 0;
+ size_t partsize = 0;
for (i = 0; i < MBR_MAX_PARTITION_ENTRIES; ++i) {
/* check status */
if ((record->entries[i].status != MBR_STATUS_INACTIVE) &&
(record->entries[i].status != MBR_STATUS_ACTIVE)) {
cli_dbgmsg("cli_scanmbr: Invalid boot record status\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
partoff = record->entries[i].firstLBA * sectorsize;
partsize = record->entries[i].numLBA * sectorsize;
if (partoff + partsize > maplen) {
cli_dbgmsg("cli_scanmbr: Invalid partition entry\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
}
/* check the signature */
if (record->signature != MBR_SIGNATURE) {
cli_dbgmsg("cli_scanmbr: Invalid boot record signature\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* check the maplen */
if ((maplen / sectorsize) < 2) {
cli_dbgmsg("cli_scanmbr: bootstrap code or file is too small to hold disk image\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
- return CL_CLEAN;
+done:
+
+ return status;
}
-static int mbr_check_ebr(struct mbr_boot_record *record)
+static cl_error_t mbr_check_ebr(struct mbr_boot_record *record)
{
- unsigned i = 0;
+ cl_error_t status = CL_SUCCESS;
+ unsigned i = 0;
for (i = 0; i < MBR_MAX_PARTITION_ENTRIES - 2; ++i) {
/* check status */
if ((record->entries[i].status != MBR_STATUS_INACTIVE) &&
(record->entries[i].status != MBR_STATUS_ACTIVE)) {
cli_dbgmsg("cli_scanmbr: Invalid boot record status\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
}
/* check the signature */
if (record->signature != MBR_SIGNATURE) {
cli_dbgmsg("cli_scanmbr: Invalid boot record signature\n");
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
- return CL_CLEAN;
+done:
+
+ return status;
}
/* this includes the overall bounds of extended partitions */
-static int mbr_primary_partition_intersection(cli_ctx *ctx, struct mbr_boot_record mbr, size_t sectorsize)
+static cl_error_t mbr_primary_partition_intersection(cli_ctx *ctx, struct mbr_boot_record mbr, size_t sectorsize)
{
+ cl_error_t status = CL_CLEAN;
+ cl_error_t ret;
partition_intersection_list_t prtncheck;
unsigned i = 0, pitxn = 0, prtncount = 0;
- int ret = CL_CLEAN, tmp = CL_CLEAN;
partition_intersection_list_init(&prtncheck);
@@ -494,39 +509,30 @@ static int mbr_primary_partition_intersection(cli_ctx *ctx, struct mbr_boot_reco
/* empty partition entry */
prtncount++;
} else {
- tmp = partition_intersection_list_check(&prtncheck, &pitxn, mbr.entries[i].firstLBA,
+ ret = partition_intersection_list_check(&prtncheck, &pitxn, mbr.entries[i].firstLBA,
mbr.entries[i].numLBA);
- if (tmp != CL_CLEAN) {
- if (tmp == CL_VIRUS) {
+ if (ret != CL_CLEAN) {
+ if (ret == CL_VIRUS) {
cli_dbgmsg("cli_scanmbr: detected intersection with partitions "
"[%u, %u]\n",
pitxn, i);
- ret = cli_append_virus(ctx, PRTN_INTXN_DETECTION);
- if (SCAN_ALLMATCHES || ret == CL_CLEAN)
- tmp = 0;
- else
- goto leave;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.MBRPartitionnIntersect");
+ if (status != CL_SUCCESS) {
+ goto done;
+ }
} else {
- ret = tmp;
- goto leave;
+ status = ret;
+ goto done;
}
}
if (mbr.entries[i].type == MBR_EXTENDED) {
/* check the logical partitions */
- tmp = mbr_extended_partition_intersection(ctx, &prtncount,
+ ret = mbr_extended_partition_intersection(ctx, &prtncount,
mbr.entries[i].firstLBA, sectorsize);
- if (tmp != CL_CLEAN) {
- if (SCAN_ALLMATCHES && (tmp == CL_VIRUS)) {
- ret = tmp;
- tmp = 0;
- } else if (tmp == CL_VIRUS) {
- partition_intersection_list_free(&prtncheck);
- return CL_VIRUS;
- } else {
- partition_intersection_list_free(&prtncheck);
- return tmp;
- }
+ if (ret != CL_SUCCESS) {
+ status = ret;
+ goto done;
}
} else {
prtncount++;
@@ -534,20 +540,21 @@ static int mbr_primary_partition_intersection(cli_ctx *ctx, struct mbr_boot_reco
}
}
-leave:
+done:
partition_intersection_list_free(&prtncheck);
- return ret;
+ return status;
}
/* checks internal logical partitions */
-static int mbr_extended_partition_intersection(cli_ctx *ctx, unsigned *prtncount, size_t extlba, size_t sectorsize)
+static cl_error_t mbr_extended_partition_intersection(cli_ctx *ctx, unsigned *prtncount, size_t extlba, size_t sectorsize)
{
+ cl_error_t status = CL_CLEAN;
+ cl_error_t ret;
struct mbr_boot_record ebr;
partition_intersection_list_t prtncheck;
unsigned i, pitxn;
- int ret = CL_CLEAN, tmp = CL_CLEAN, mbr_base = 0;
+ int mbr_base = 0;
size_t pos = 0, logiclba = 0;
- int virus_found = 0;
mbr_base = sectorsize - sizeof(struct mbr_boot_record);
@@ -563,7 +570,8 @@ static int mbr_extended_partition_intersection(cli_ctx *ctx, unsigned *prtncount
if (fmap_readn(ctx->fmap, &ebr, pos, sizeof(ebr)) != sizeof(ebr)) {
cli_dbgmsg("cli_scanebr: Invalid extended boot record\n");
partition_intersection_list_free(&prtncheck);
- return CL_EFORMAT;
+ status = CL_EFORMAT;
+ goto done;
}
/* convert the little endian to host */
@@ -573,22 +581,19 @@ static int mbr_extended_partition_intersection(cli_ctx *ctx, unsigned *prtncount
(*prtncount)++;
/* assume that logical record is first and extended is second */
- tmp = partition_intersection_list_check(&prtncheck, &pitxn, logiclba, ebr.entries[0].numLBA);
- if (tmp != CL_CLEAN) {
- if (tmp == CL_VIRUS) {
+ ret = partition_intersection_list_check(&prtncheck, &pitxn, logiclba, ebr.entries[0].numLBA);
+ if (ret != CL_CLEAN) {
+ if (ret == CL_VIRUS) {
cli_dbgmsg("cli_scanebr: detected intersection with partitions "
"[%u, %u]\n",
pitxn, i);
- ret = cli_append_virus(ctx, PRTN_INTXN_DETECTION);
- if (ret == CL_VIRUS)
- virus_found = 1;
- if (SCAN_ALLMATCHES || ret == CL_CLEAN)
- tmp = 0;
- else
- goto leave;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.MBRPartitionnIntersect");
+ if (status == CL_VIRUS) {
+ goto done;
+ }
} else {
- ret = tmp;
- goto leave;
+ status = ret;
+ goto done;
}
}
@@ -603,9 +608,8 @@ static int mbr_extended_partition_intersection(cli_ctx *ctx, unsigned *prtncount
++i;
} while (logiclba != 0 && (*prtncount) < ctx->engine->maxpartitions);
-leave:
+done:
partition_intersection_list_free(&prtncheck);
- if (virus_found)
- return CL_VIRUS;
- return ret;
+
+ return status;
}
diff --git a/libclamav/mbr.h b/libclamav/mbr.h
index 46a3c8a647..4fd25117b8 100644
--- a/libclamav/mbr.h
+++ b/libclamav/mbr.h
@@ -84,9 +84,9 @@ struct mbr_boot_record {
#pragma pack
#endif
-int cli_mbr_check(const unsigned char *buff, size_t len, size_t maplen);
-int cli_mbr_check2(cli_ctx *ctx, size_t sectorsize);
-int cli_scanmbr(cli_ctx *ctx, size_t sectorsize);
+cl_error_t cli_mbr_check(const unsigned char *buff, size_t len, size_t maplen);
+cl_error_t cli_mbr_check2(cli_ctx *ctx, size_t sectorsize);
+cl_error_t cli_scanmbr(cli_ctx *ctx, size_t sectorsize);
void mbr_convert_to_host(struct mbr_boot_record *record);
#endif
diff --git a/libclamav/msxml_parser.c b/libclamav/msxml_parser.c
index 04ec738a57..bcd096837d 100644
--- a/libclamav/msxml_parser.c
+++ b/libclamav/msxml_parser.c
@@ -163,13 +163,14 @@ static int msxml_parse_value(json_object *wrkptr, const char *arrname, const xml
#endif /* HAVE_JSON */
#define MAX_ATTRIBS 20
-static int msxml_parse_element(struct msxml_ctx *mxctx, xmlTextReaderPtr reader, int rlvl, void *jptr)
+static cl_error_t msxml_parse_element(struct msxml_ctx *mxctx, xmlTextReaderPtr reader, int rlvl, void *jptr)
{
const xmlChar *element_name = NULL;
const xmlChar *node_name = NULL, *node_value = NULL;
const struct key_entry *keyinfo;
struct attrib_entry attribs[MAX_ATTRIBS];
- int ret, virus = 0, state, node_type, endtag = 0, num_attribs = 0;
+ cl_error_t ret;
+ int state, node_type, endtag = 0, num_attribs = 0;
cli_ctx *ctx = mxctx->ictx->ctx;
#if HAVE_JSON
json_object *root = mxctx->ictx->root;
@@ -188,7 +189,7 @@ static int msxml_parse_element(struct msxml_ctx *mxctx, xmlTextReaderPtr reader,
#if HAVE_JSON
if (track_json(mxctx)) {
- int tmp = cli_json_parse_error(root, "MSXML_RECURSIVE_LIMIT");
+ cl_error_t tmp = cli_json_parse_error(root, "MSXML_RECURSIVE_LIMIT");
if (tmp != CL_SUCCESS)
return tmp;
}
@@ -219,7 +220,7 @@ static int msxml_parse_element(struct msxml_ctx *mxctx, xmlTextReaderPtr reader,
cli_dbgmsg("msxml_parse_element: element tag node nameless\n");
#if HAVE_JSON
if (track_json(mxctx)) {
- int tmp = cli_json_parse_error(root, "MSXML_NAMELESS_ELEMENT");
+ cl_error_t tmp = cli_json_parse_error(root, "MSXML_NAMELESS_ELEMENT");
if (tmp != CL_SUCCESS)
return tmp;
}
@@ -369,10 +370,8 @@ static int msxml_parse_element(struct msxml_ctx *mxctx, xmlTextReaderPtr reader,
switch (node_type) {
case XML_READER_TYPE_ELEMENT:
ret = msxml_parse_element(mxctx, reader, rlvl + 1, thisjobj ? thisjobj : parent);
- if (ret != CL_SUCCESS || (!SCAN_ALLMATCHES && ret == CL_VIRUS)) {
+ if (ret != CL_SUCCESS) {
return ret;
- } else if (SCAN_ALLMATCHES && ret == CL_VIRUS) {
- virus = 1;
}
break;
@@ -417,13 +416,12 @@ static int msxml_parse_element(struct msxml_ctx *mxctx, xmlTextReaderPtr reader,
ret = mxctx->scan_cb(of, tempfile, ctx, num_attribs, attribs, mxctx->scan_data);
close(of);
- if (!(ctx->engine->keeptmp))
+ if (!(ctx->engine->keeptmp)) {
cli_unlink(tempfile);
+ }
free(tempfile);
- if (ret != CL_SUCCESS && (ret != CL_VIRUS || (!SCAN_ALLMATCHES && ret == CL_VIRUS))) {
+ if (ret != CL_SUCCESS) {
return ret;
- } else if (SCAN_ALLMATCHES && ret == CL_VIRUS) {
- virus = 1;
}
}
@@ -467,10 +465,8 @@ static int msxml_parse_element(struct msxml_ctx *mxctx, xmlTextReaderPtr reader,
if (!(ctx->engine->keeptmp))
cli_unlink(tempfile);
free(tempfile);
- if (ret != CL_SUCCESS && (ret != CL_VIRUS || (!SCAN_ALLMATCHES && ret == CL_VIRUS))) {
+ if (ret != CL_SUCCESS) {
return ret;
- } else if (SCAN_ALLMATCHES && ret == CL_VIRUS) {
- virus = 1;
}
}
@@ -491,10 +487,8 @@ static int msxml_parse_element(struct msxml_ctx *mxctx, xmlTextReaderPtr reader,
#else
ret = mxctx->comment_cb((const char *)node_value, ctx, NULL, mxctx->comment_data);
#endif
- if (ret != CL_SUCCESS && (ret != CL_VIRUS || (!SCAN_ALLMATCHES && ret == CL_VIRUS))) {
+ if (ret != CL_SUCCESS) {
return ret;
- } else if (SCAN_ALLMATCHES && ret == CL_VIRUS) {
- virus = 1;
}
}
@@ -549,23 +543,25 @@ static int msxml_parse_element(struct msxml_ctx *mxctx, xmlTextReaderPtr reader,
break;
case XML_READER_TYPE_END_ELEMENT:
cli_msxmlmsg("msxml_parse_element: END ELEMENT %s [%d]: %s\n", node_name, node_type, node_value);
- return (virus ? CL_VIRUS : CL_SUCCESS);
+ return CL_SUCCESS;
default:
cli_dbgmsg("msxml_parse_element: unhandled xml primary node %s [%d]: %s\n", node_name, node_type, node_value);
}
- return (virus ? CL_VIRUS : CL_SUCCESS);
+ return CL_SUCCESS;
}
/* reader initialization and closing handled by caller */
-int cli_msxml_parse_document(cli_ctx *ctx, xmlTextReaderPtr reader, const struct key_entry *keys, const size_t num_keys, uint32_t flags, struct msxml_ctx *mxctx)
+cl_error_t cli_msxml_parse_document(cli_ctx *ctx, xmlTextReaderPtr reader, const struct key_entry *keys, const size_t num_keys, uint32_t flags, struct msxml_ctx *mxctx)
{
struct msxml_ctx reserve;
struct msxml_ictx ictx;
- int state, virus = 0, ret = CL_SUCCESS;
+ int state;
+ cl_error_t ret = CL_SUCCESS;
- if (!ctx)
+ if (!ctx) {
return CL_ENULLARG;
+ }
if (!mxctx) {
memset(&reserve, 0, sizeof(reserve));
@@ -604,27 +600,25 @@ int cli_msxml_parse_document(cli_ctx *ctx, xmlTextReaderPtr reader, const struct
#else
ret = msxml_parse_element(mxctx, reader, 0, NULL);
#endif
- if (ret == CL_SUCCESS)
- ;
- else if (SCAN_ALLMATCHES && ret == CL_VIRUS) {
- /* non-allmatch simply propagates it down to return through ret */
- virus = 1;
- } else if (ret == CL_VIRUS || ret == CL_ETIMEOUT || ret == CL_BREAK) {
- cli_dbgmsg("cli_msxml_parse_document: encountered halt event in parsing xml document\n");
- break;
- } else {
- cli_warnmsg("cli_msxml_parse_document: encountered issue in parsing xml document\n");
- break;
+ if (ret != CL_SUCCESS) {
+ if (ret == CL_VIRUS || ret == CL_ETIMEOUT || ret == CL_BREAK) {
+ cli_dbgmsg("cli_msxml_parse_document: encountered halt event in parsing xml document\n");
+ break;
+ } else {
+ cli_warnmsg("cli_msxml_parse_document: encountered issue in parsing xml document\n");
+ break;
+ }
}
}
- if (state == -1)
+ if (state == -1) {
ret = CL_EPARSE;
+ }
#if HAVE_JSON
/* Parse General Error Handler */
if (ictx.flags & MSXML_FLAG_JSON) {
- int tmp = CL_SUCCESS;
+ cl_error_t tmp = CL_SUCCESS;
switch (ret) {
case CL_SUCCESS:
@@ -650,8 +644,9 @@ int cli_msxml_parse_document(cli_ctx *ctx, xmlTextReaderPtr reader, const struct
break;
}
- if (tmp)
+ if (tmp) {
return tmp;
+ }
}
#endif
@@ -665,7 +660,7 @@ int cli_msxml_parse_document(cli_ctx *ctx, xmlTextReaderPtr reader, const struct
ret = CL_SUCCESS;
}
- return (virus ? CL_VIRUS : ret);
+ return ret;
}
#endif /* HAVE_LIBXML2 */
diff --git a/libclamav/msxml_parser.h b/libclamav/msxml_parser.h
index 34b90338a7..ff3f6c2f6d 100644
--- a/libclamav/msxml_parser.h
+++ b/libclamav/msxml_parser.h
@@ -86,7 +86,7 @@ struct msxml_ctx {
struct msxml_ictx *ictx;
};
-int cli_msxml_parse_document(cli_ctx *ctx, xmlTextReaderPtr reader, const struct key_entry *keys, const size_t num_keys, uint32_t flags, struct msxml_ctx *mxctx);
+cl_error_t cli_msxml_parse_document(cli_ctx *ctx, xmlTextReaderPtr reader, const struct key_entry *keys, const size_t num_keys, uint32_t flags, struct msxml_ctx *mxctx);
#endif /* HAVE_LIBXML2 */
diff --git a/libclamav/nsis/nulsft.c b/libclamav/nsis/nulsft.c
index fab42e6bbc..522ac19690 100644
--- a/libclamav/nsis/nulsft.c
+++ b/libclamav/nsis/nulsft.c
@@ -545,13 +545,17 @@ int cli_scannulsft(cli_ctx *ctx, off_t offset)
free(nsist.dir);
return CL_ESEEK;
}
- if (nsist.fno == 1)
- ret = cli_scan_desc(nsist.ofd, ctx, 0, 0, NULL, AC_SCAN_VIR, NULL, NULL); /// TODO: Extract file names
- else
+ if (nsist.fno == 1) {
+ ret = cli_scan_desc(nsist.ofd, ctx, CL_TYPE_ANY, false, NULL, AC_SCAN_VIR, NULL, NULL); /// TODO: Extract file names
+ } else {
ret = cli_magic_scan_desc(nsist.ofd, nsist.ofn, ctx, NULL); /// TODO: Extract file names
+ }
close(nsist.ofd);
- if (!ctx->engine->keeptmp)
- if (cli_unlink(nsist.ofn)) ret = CL_EUNLINK;
+ if (!ctx->engine->keeptmp) {
+ if (cli_unlink(nsist.ofn)) {
+ ret = CL_EUNLINK;
+ }
+ }
} else if (ret == CL_EMAXSIZE) {
ret = nsist.solid ? CL_BREAK : CL_SUCCESS;
}
@@ -562,8 +566,9 @@ int cli_scannulsft(cli_ctx *ctx, off_t offset)
nsis_shutdown(&nsist);
- if (!ctx->engine->keeptmp)
+ if (!ctx->engine->keeptmp) {
cli_rmdirs(nsist.dir);
+ }
free(nsist.dir);
diff --git a/libclamav/ole2_extract.c b/libclamav/ole2_extract.c
index 3f3fa8795a..69999450c0 100644
--- a/libclamav/ole2_extract.c
+++ b/libclamav/ole2_extract.c
@@ -578,14 +578,13 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t
int32_t idx, current_block, i, curindex;
char *dirname;
ole2_list_t node_list;
- int ret, func_ret;
+ cl_error_t ret;
#if HAVE_JSON
char *name;
int toval = 0;
#endif
ole2_listmsg("ole2_walk_property_tree() called\n");
- func_ret = CL_SUCCESS;
ole2_list_init(&node_list);
ole2_listmsg("rec_level: %d\n", rec_level);
@@ -599,7 +598,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t
// Note: engine->max_recursion_level is re-purposed here out of convenience.
// ole2 recursion does not leverage the ctx->recursion_stack stack.
cli_dbgmsg("OLE2: Recursion limit reached (max: %d)\n", ctx->engine->max_recursion_level);
- cli_append_virus_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxRecursion");
+ cli_append_potentially_unwanted_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxRecursion");
return CL_EMAXREC;
}
@@ -687,12 +686,8 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t
if ((int)(prop_block[idx].child) != -1) {
ret = ole2_walk_property_tree(hdr, dir, prop_block[idx].child, handler, rec_level + 1, file_count, ctx, scansize);
if (ret != CL_SUCCESS) {
- if (SCAN_ALLMATCHES && (ret == CL_VIRUS)) {
- func_ret = ret;
- } else {
- ole2_list_delete(&node_list);
- return ret;
- }
+ ole2_list_delete(&node_list);
+ return ret;
}
}
if ((int)(prop_block[idx].prev) != -1) {
@@ -712,7 +707,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t
ole2_listmsg("file node\n");
if (ctx && ctx->engine->maxfiles && ((*file_count > ctx->engine->maxfiles) || (ctx->scannedfiles > ctx->engine->maxfiles - *file_count))) {
cli_dbgmsg("OLE2: files limit reached (max: %u)\n", ctx->engine->maxfiles);
- cli_append_virus_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxFiles");
+ cli_append_potentially_unwanted_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxFiles");
ole2_list_delete(&node_list);
return CL_EMAXFILES;
}
@@ -722,13 +717,9 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t
ole2_listmsg("running file handler\n");
ret = handler(hdr, &prop_block[idx], dir, ctx);
if (ret != CL_SUCCESS) {
- if (SCAN_ALLMATCHES && (ret == CL_VIRUS)) {
- func_ret = ret;
- } else {
- ole2_listmsg("file handler returned %d\n", ret);
- ole2_list_delete(&node_list);
- return ret;
- }
+ ole2_listmsg("file handler returned %d\n", ret);
+ ole2_list_delete(&node_list);
+ return ret;
}
} else {
cli_dbgmsg("OLE2: filesize exceeded\n");
@@ -736,12 +727,8 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t
if ((int)(prop_block[idx].child) != -1) {
ret = ole2_walk_property_tree(hdr, dir, prop_block[idx].child, handler, rec_level, file_count, ctx, scansize);
if (ret != CL_SUCCESS) {
- if (SCAN_ALLMATCHES && (ret == CL_VIRUS)) {
- func_ret = ret;
- } else {
- ole2_list_delete(&node_list);
- return ret;
- }
+ ole2_list_delete(&node_list);
+ return ret;
}
}
if ((int)(prop_block[idx].prev) != -1) {
@@ -792,14 +779,11 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t
if ((int)(prop_block[idx].child) != -1) {
ret = ole2_walk_property_tree(hdr, dirname, prop_block[idx].child, handler, rec_level + 1, file_count, ctx, scansize);
if (ret != CL_SUCCESS) {
- if (SCAN_ALLMATCHES && (ret == CL_VIRUS)) {
- func_ret = ret;
- } else {
- ole2_list_delete(&node_list);
- if (dirname)
- free(dirname);
- return ret;
+ ole2_list_delete(&node_list);
+ if (dirname) {
+ free(dirname);
}
+ return ret;
}
}
if (dirname) {
@@ -826,7 +810,7 @@ static int ole2_walk_property_tree(ole2_header_t *hdr, const char *dir, int32_t
ole2_listmsg("loop ended: %d %d\n", ole2_list_size(&node_list), ole2_list_is_empty(&node_list));
}
ole2_list_delete(&node_list);
- return func_ret;
+ return CL_SUCCESS;
}
/* Write file Handler - write the contents of the entry to a file */
diff --git a/libclamav/others.c b/libclamav/others.c
index c9d7db2359..a40fba6361 100644
--- a/libclamav/others.c
+++ b/libclamav/others.c
@@ -339,7 +339,7 @@ unsigned int cl_retflevel(void)
return CL_FLEVEL;
}
-const char *cl_strerror(int clerror)
+const char *cl_strerror(cl_error_t clerror)
{
switch (clerror) {
/* libclamav specific codes */
@@ -1089,7 +1089,7 @@ cl_error_t cl_engine_settings_free(struct cl_settings *settings)
return CL_SUCCESS;
}
-void cli_append_virus_if_heur_exceedsmax(cli_ctx *ctx, char *vname)
+void cli_append_potentially_unwanted_if_heur_exceedsmax(cli_ctx *ctx, char *vname)
{
if (!ctx->limit_exceeded) {
ctx->limit_exceeded = true; // guard against adding an alert (or metadata) a million times for non-fatal exceeds-max conditions
@@ -1114,46 +1114,56 @@ cl_error_t cli_checklimits(const char *who, cli_ctx *ctx, unsigned long need1, u
cl_error_t ret = CL_SUCCESS;
unsigned long needed;
- /* if called without limits, go on, unpack, scan */
- if (!ctx) return ret;
+ if (!ctx) {
+ /* if called without limits, go on, unpack, scan */
+ goto done;
+ }
needed = (need1 > need2) ? need1 : need2;
needed = (needed > need3) ? needed : need3;
- /* Enforce timelimit */
- if (CL_ETIMEOUT == (ret = cli_checktimelimit(ctx))) {
- /* Abort the scan ... */
- ret = CL_ETIMEOUT;
+ /* Enforce global time limit, if limit enabled */
+ ret = cli_checktimelimit(ctx);
+ if (CL_SUCCESS != ret) {
+ // Exceeding the time limit will abort the scan.
+ // The logic for this and the possible heuristic is done inside the cli_checktimelimit function.
+ goto done;
}
- /* Enforce global scan-size limit */
- if (needed && ctx->engine->maxscansize) {
- /* if the remaining scansize is too small... */
- if (ctx->engine->maxscansize - ctx->scansize < needed) {
- /* Skip this file */
- cli_dbgmsg("%s: scansize exceeded (initial: %lu, consumed: %lu, needed: %lu)\n", who, (unsigned long int)ctx->engine->maxscansize, (unsigned long int)ctx->scansize, needed);
- ret = CL_EMAXSIZE;
- cli_append_virus_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxScanSize");
- }
+ /* Enforce global scan-size limit, if limit enabled */
+ if (needed && (ctx->engine->maxscansize != 0) && (ctx->engine->maxscansize - ctx->scansize < needed)) {
+ /* The size needed is greater than the remaining scansize ... Skip this file. */
+ cli_dbgmsg("%s: scansize exceeded (initial: %lu, consumed: %lu, needed: %lu)\n", who, (unsigned long int)ctx->engine->maxscansize, (unsigned long int)ctx->scansize, needed);
+ ret = CL_EMAXSIZE;
+ cli_append_potentially_unwanted_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxScanSize");
+ goto done;
}
- /* Enforce per-file file-size limit */
- if (needed && ctx->engine->maxfilesize && ctx->engine->maxfilesize < needed) {
- /* Skip this file */
+ /* Enforce per-file file-size limit, if limit enabled */
+ if (needed && (ctx->engine->maxfilesize != 0) && (ctx->engine->maxfilesize < needed)) {
+ /* The size needed is greater than that limit ... Skip this file. */
cli_dbgmsg("%s: filesize exceeded (allowed: %lu, needed: %lu)\n", who, (unsigned long int)ctx->engine->maxfilesize, needed);
ret = CL_EMAXSIZE;
- cli_append_virus_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxFileSize");
+ cli_append_potentially_unwanted_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxFileSize");
+ goto done;
}
- /* Enforce limit on number of embedded files */
- if (ctx->engine->maxfiles && ctx->scannedfiles >= ctx->engine->maxfiles) {
- /* Abort the scan ... */
+ /* Enforce limit on number of embedded files, if limit enabled */
+ if ((ctx->engine->maxfiles != 0) && (ctx->scannedfiles >= ctx->engine->maxfiles)) {
+ /* This file would exceed the max # of files ... Skip this file. */
cli_dbgmsg("%s: files limit reached (max: %u)\n", who, ctx->engine->maxfiles);
ret = CL_EMAXFILES;
- cli_append_virus_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxFiles");
- ctx->abort_scan = true;
+ cli_append_potentially_unwanted_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxFiles");
+
+ // We don't need to set the `ctx->abort_scan` flag here.
+ // We want `cli_magic_scan()` to finish scanning the current file, but not any future files.
+ // We keep track of the # scanned files with `ctx->scannedfiles`, and that should be sufficient to prevent
+ // additional files from being scanned.
+ goto done;
}
+done:
+
return ret;
}
@@ -1191,10 +1201,8 @@ cl_error_t cli_checktimelimit(cli_ctx *ctx)
if (ctx->time_limit.tv_sec != 0) {
struct timeval now;
if (gettimeofday(&now, NULL) == 0) {
- if (now.tv_sec > ctx->time_limit.tv_sec) {
- ctx->abort_scan = true;
- ret = CL_ETIMEOUT;
- } else if (now.tv_sec == ctx->time_limit.tv_sec && now.tv_usec > ctx->time_limit.tv_usec) {
+ if ((now.tv_sec > ctx->time_limit.tv_sec) ||
+ (now.tv_sec == ctx->time_limit.tv_sec && now.tv_usec > ctx->time_limit.tv_usec)) {
ctx->abort_scan = true;
ret = CL_ETIMEOUT;
}
@@ -1202,7 +1210,10 @@ cl_error_t cli_checktimelimit(cli_ctx *ctx)
}
if (CL_ETIMEOUT == ret) {
- cli_append_virus_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxScanTime");
+ cli_append_potentially_unwanted_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxScanTime");
+
+ // abort_scan flag is set so that in cli_magic_scan() we *will* stop scanning, even if we lose the status code.
+ ctx->abort_scan = true;
}
done:
@@ -1279,7 +1290,7 @@ char *cli_hashfile(const char *filename, int type)
/* Function: unlink
unlink() with error checking
*/
-int cli_unlink(const char *pathname)
+cl_error_t cli_unlink(const char *pathname)
{
if (unlink(pathname) == -1) {
#ifdef _WIN32
@@ -1287,21 +1298,21 @@ int cli_unlink(const char *pathname)
* even if the user has permissions to delete the file. */
if (-1 == _chmod(pathname, _S_IWRITE)) {
char err[128];
- cli_warnmsg("cli_unlink: _chmod failure - %s\n", cli_strerror(errno, err, sizeof(err)));
+ cli_warnmsg("cli_unlink: _chmod failure for %s - %s\n", pathname, cli_strerror(errno, err, sizeof(err)));
return 1;
} else if (unlink(pathname) == -1) {
char err[128];
- cli_warnmsg("cli_unlink: unlink failure - %s\n", cli_strerror(errno, err, sizeof(err)));
+ cli_warnmsg("cli_unlink: unlink failure for %s - %s\n", pathname, cli_strerror(errno, err, sizeof(err)));
return 1;
}
return 0;
#else
char err[128];
- cli_warnmsg("cli_unlink: unlink failure - %s\n", cli_strerror(errno, err, sizeof(err)));
+ cli_warnmsg("cli_unlink: unlink failure - %s\n", pathname, cli_strerror(errno, err, sizeof(err)));
return 1;
#endif
}
- return 0;
+ return CL_SUCCESS;
}
void cli_virus_found_cb(cli_ctx *ctx, const char *virname)
@@ -1357,7 +1368,7 @@ static cl_error_t append_virus(cli_ctx *ctx, const char *virname, IndicatorType
goto done;
}
- if (type == IndicatorType_Strong || (type == IndicatorType_PotentiallyUnwanted && SCAN_HEURISTIC_PRECEDENCE)) {
+ if (type == IndicatorType_Strong) {
// Run that virus callback which in clamscan says " FOUND"
cli_virus_found_cb(ctx, virname);
}
@@ -1392,14 +1403,12 @@ static cl_error_t append_virus(cli_ctx *ctx, const char *virname, IndicatorType
switch (type) {
case IndicatorType_Strong: {
status = CL_VIRUS;
+ // abort_scan flag is set so that in cli_magic_scan() we *will* stop scanning, even if we lose the status code.
+ ctx->abort_scan = true;
break;
}
case IndicatorType_PotentiallyUnwanted: {
- if (SCAN_HEURISTIC_PRECEDENCE) {
- status = CL_VIRUS;
- } else {
- status = CL_SUCCESS;
- }
+ status = CL_SUCCESS;
break;
}
default: {
@@ -1418,12 +1427,22 @@ static cl_error_t append_virus(cli_ctx *ctx, const char *virname, IndicatorType
cl_error_t cli_append_potentially_unwanted(cli_ctx *ctx, const char *virname)
{
- return append_virus(ctx, virname, IndicatorType_PotentiallyUnwanted);
+ if (SCAN_HEURISTIC_PRECEDENCE) {
+ return append_virus(ctx, virname, IndicatorType_Strong);
+ } else {
+ return append_virus(ctx, virname, IndicatorType_PotentiallyUnwanted);
+ }
}
cl_error_t cli_append_virus(cli_ctx *ctx, const char *virname)
{
- return append_virus(ctx, virname, IndicatorType_Strong);
+ if ((strncmp(virname, "PUA.", 4) == 0) ||
+ (strncmp(virname, "Heuristics.", 11) == 0) ||
+ (strncmp(virname, "BC.Heuristics.", 14) == 0)) {
+ return cli_append_potentially_unwanted(ctx, virname);
+ } else {
+ return append_virus(ctx, virname, IndicatorType_Strong);
+ }
}
const char *cli_get_last_virus(const cli_ctx *ctx)
@@ -1454,7 +1473,7 @@ cl_error_t cli_recursion_stack_push(cli_ctx *ctx, cl_fmap_t *map, cli_file_t typ
recursion_level_t *new_container = NULL;
// Check the regular limits
- if (CL_SUCCESS != (status = cli_checklimits("cli_updatelimits", ctx, map->len, 0, 0))) {
+ if (CL_SUCCESS != (status = cli_checklimits("cli_recursion_stack_push", ctx, map->len, 0, 0))) {
cli_dbgmsg("cli_recursion_stack_push: Some content was skipped. The scan result will not be cached.\n");
emax_reached(ctx); // Disable caching for all recursion layers.
goto done;
@@ -1465,7 +1484,7 @@ cl_error_t cli_recursion_stack_push(cli_ctx *ctx, cl_fmap_t *map, cli_file_t typ
cli_dbgmsg("cli_recursion_stack_push: Archive recursion limit exceeded (%u, max: %u)\n", ctx->recursion_level, ctx->engine->max_recursion_level);
cli_dbgmsg("cli_recursion_stack_push: Some content was skipped. The scan result will not be cached.\n");
emax_reached(ctx); // Disable caching for all recursion layers.
- cli_append_virus_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxRecursion");
+ cli_append_potentially_unwanted_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxRecursion");
status = CL_EMAXREC;
goto done;
}
diff --git a/libclamav/others.h b/libclamav/others.h
index 5c7706eb47..57bce9dc12 100644
--- a/libclamav/others.h
+++ b/libclamav/others.h
@@ -204,7 +204,7 @@ typedef struct cli_ctx_tag {
uint64_t scansize;
struct cl_scan_options *options;
unsigned int scannedfiles;
- unsigned int corrupted_input;
+ unsigned int corrupted_input; /* Setting this flag will prevent the PE parser from reporting "broken executable" for unpacked/reconstructed files that may not be 100% to spec. */
recursion_level_t *recursion_stack; /* Array of recursion levels used as a stack. */
uint32_t recursion_stack_size; /* stack size must == engine->max_recursion_level */
uint32_t recursion_level; /* Index into recursion_stack; current fmap recursion level from start of scan. */
@@ -732,6 +732,18 @@ cl_error_t cli_append_virus(cli_ctx *ctx, const char *virname);
*/
cl_error_t cli_append_potentially_unwanted(cli_ctx *ctx, const char *virname);
+/**
+ * @brief If the SCAN_HEURISTIC_EXCEEDS_MAX option is enabled, append a "potentially unwanted" indicator.
+ *
+ * There is no return value because the caller should select the appropriate "CL_EMAX*" error code regardless
+ * of whether or not an FP sig is found, or allmatch is enabled, or whatever.
+ * That is, the scan must not continue because of an FP sig.
+ *
+ * @param ctx The scan context.
+ * @param virname The name of the potentially unwanted indicator.
+ */
+void cli_append_potentially_unwanted_if_heur_exceedsmax(cli_ctx *ctx, char *virname);
+
const char *cli_get_last_virus(const cli_ctx *ctx);
const char *cli_get_last_virus_str(const cli_ctx *ctx);
void cli_virus_found_cb(cli_ctx *ctx, const char *virname);
@@ -954,7 +966,15 @@ char *cli_strdup(const char *s);
int cli_rmdirs(const char *dirname);
char *cli_hashstream(FILE *fs, unsigned char *digcpy, int type);
char *cli_hashfile(const char *filename, int type);
-int cli_unlink(const char *pathname);
+
+/**
+ * @brief unlink() with error checking
+ *
+ * @param pathname the file path to unlink
+ * @return cl_error_t CL_SUCCESS if successful, CL_EUNLINK if unlink() failed
+ */
+cl_error_t cli_unlink(const char *pathname);
+
size_t cli_readn(int fd, void *buff, size_t count);
size_t cli_writen(int fd, const void *buff, size_t count);
const char *cli_gettmpdir(void);
@@ -1055,8 +1075,8 @@ void cli_bitset_free(bitset_t *bs);
int cli_bitset_set(bitset_t *bs, unsigned long bit_offset);
int cli_bitset_test(bitset_t *bs, unsigned long bit_offset);
const char *cli_ctime(const time_t *timep, char *buf, const size_t bufsize);
-void cli_append_virus_if_heur_exceedsmax(cli_ctx *, char *);
-cl_error_t cli_checklimits(const char *, cli_ctx *, unsigned long, unsigned long, unsigned long);
+
+cl_error_t cli_checklimits(const char *who, cli_ctx *ctx, unsigned long need1, unsigned long need2, unsigned long need3);
/**
* @brief Call before scanning a file to determine if we should scan it, skip it, or abort the entire scanning process.
diff --git a/libclamav/partition_intersection.c b/libclamav/partition_intersection.c
index 7c2aad6e5d..a003eb4699 100644
--- a/libclamav/partition_intersection.c
+++ b/libclamav/partition_intersection.c
@@ -26,22 +26,22 @@
#include "others.h"
#include "partition_intersection.h"
-static int partition_intersection_list_is_empty(partition_intersection_list_t* list)
+static bool partition_intersection_list_is_empty(partition_intersection_list_t* list)
{
return (list->Head == NULL);
}
-int partition_intersection_list_init(partition_intersection_list_t* list)
+cl_error_t partition_intersection_list_init(partition_intersection_list_t* list)
{
list->Head = NULL;
list->Size = 0;
return CL_SUCCESS;
}
-int partition_intersection_list_check(partition_intersection_list_t* list, unsigned* pitxn, off_t start, size_t size)
+cl_error_t partition_intersection_list_check(partition_intersection_list_t* list, unsigned* pitxn, off_t start, size_t size)
{
partition_intersection_node_t *new_node, *check_node;
- int ret = CL_CLEAN;
+ cl_error_t ret = CL_CLEAN;
*pitxn = list->Size;
@@ -84,7 +84,7 @@ int partition_intersection_list_check(partition_intersection_list_t* list, unsig
return ret;
}
-int partition_intersection_list_free(partition_intersection_list_t* list)
+cl_error_t partition_intersection_list_free(partition_intersection_list_t* list)
{
partition_intersection_node_t* next = NULL;
diff --git a/libclamav/partition_intersection.h b/libclamav/partition_intersection.h
index 4bfba8fbc3..fbda7430fe 100644
--- a/libclamav/partition_intersection.h
+++ b/libclamav/partition_intersection.h
@@ -28,8 +28,6 @@
#include "clamav-types.h"
#include "others.h"
-#define PRTN_INTXN_DETECTION "heuristic.partitionintersection"
-
struct partition_intersection_node;
typedef struct partition_intersection_node {
off_t Start;
@@ -42,8 +40,8 @@ typedef struct partition_intersection_list {
size_t Size; /* for debug */
} partition_intersection_list_t;
-int partition_intersection_list_init(partition_intersection_list_t *list);
-int partition_intersection_list_check(partition_intersection_list_t *list, unsigned *pitxn, off_t start, size_t size);
-int partition_intersection_list_free(partition_intersection_list_t *list);
+cl_error_t partition_intersection_list_init(partition_intersection_list_t *list);
+cl_error_t partition_intersection_list_check(partition_intersection_list_t *list, unsigned *pitxn, off_t start, size_t size);
+cl_error_t partition_intersection_list_free(partition_intersection_list_t *list);
#endif
diff --git a/libclamav/pdf.c b/libclamav/pdf.c
index 876a244d56..e4b6d3b368 100644
--- a/libclamav/pdf.c
+++ b/libclamav/pdf.c
@@ -1431,7 +1431,6 @@ static int pdf_scan_contents(int fd, struct pdf_struct *pdf)
cl_error_t pdf_extract_obj(struct pdf_struct *pdf, struct pdf_obj *obj, uint32_t flags)
{
- cli_ctx *ctx = pdf->ctx;
char fullname[PATH_MAX + 1];
int fout = -1;
size_t sum = 0;
@@ -1648,7 +1647,7 @@ cl_error_t pdf_extract_obj(struct pdf_struct *pdf, struct pdf_obj *obj, uint32_t
* make a best effort to keep parsing...
* Unless we were unable to allocate memory.*/
if (CL_EMEM == rc) {
- goto err;
+ goto really_done;
}
if (CL_EPARSE == rc) {
rc = CL_SUCCESS;
@@ -1694,7 +1693,7 @@ cl_error_t pdf_extract_obj(struct pdf_struct *pdf, struct pdf_obj *obj, uint32_t
if (dparams)
pdf_free_dict(dparams);
- if ((rc == CL_VIRUS) && !SCAN_ALLMATCHES) {
+ if (rc == CL_VIRUS) {
sum = 0; /* prevents post-filter scan */
goto done;
}
@@ -1815,33 +1814,37 @@ cl_error_t pdf_extract_obj(struct pdf_struct *pdf, struct pdf_obj *obj, uint32_t
/* TODO: invoke bytecode on this pdf obj with metainformation associated */
lseek(fout, 0, SEEK_SET);
rc2 = cli_magic_scan_desc(fout, fullname, pdf->ctx, NULL);
- if (rc2 == CL_VIRUS || rc == CL_SUCCESS)
+ if (rc2 != CL_SUCCESS) {
rc = rc2;
+ goto really_done;
+ }
- if ((rc == CL_CLEAN) || ((rc == CL_VIRUS) && SCAN_ALLMATCHES)) {
+ if ((rc == CL_CLEAN) || (rc == CL_VIRUS)) {
unsigned int dumpid = 0;
for (dumpid = 0; dumpid < pdf->nobjs; dumpid++) {
if (pdf->objs[dumpid] == obj)
break;
}
rc2 = run_pdf_hooks(pdf, PDF_PHASE_POSTDUMP, fout, dumpid);
- if (rc2 == CL_VIRUS)
+ if (rc2 == CL_VIRUS) {
rc = rc2;
+ goto really_done;
+ }
}
- if (((rc == CL_CLEAN) || ((rc == CL_VIRUS) && SCAN_ALLMATCHES)) && (obj->flags & (1 << OBJ_CONTENTS))) {
+ if (((rc == CL_CLEAN) || (rc == CL_VIRUS)) && (obj->flags & (1 << OBJ_CONTENTS))) {
lseek(fout, 0, SEEK_SET);
- cli_dbgmsg("pdf_extract_obj: dumping contents %u %u\n", obj->id >> 8, obj->id & 0xff);
+ cli_dbgmsg("pdf_extract_obj: dumping contents from obj %u %u\n", obj->id >> 8, obj->id & 0xff);
rc2 = pdf_scan_contents(fout, pdf);
- if (rc2 == CL_VIRUS)
+ if (rc2 != CL_SUCCESS) {
rc = rc2;
-
- noisy_msg(pdf, "pdf_extract_obj: extracted text from obj %u %u\n", obj->id >> 8, obj->id & 0xff);
+ goto really_done;
+ }
}
}
-err:
+really_done:
close(fout);
if (CL_EMEM != rc) {
@@ -3271,7 +3274,6 @@ cl_error_t pdf_find_and_parse_objs_in_objstm(struct pdf_struct *pdf, struct objs
{
cl_error_t status = CL_EFORMAT;
cl_error_t retval = CL_EPARSE;
- int32_t alerts = 0;
uint32_t badobjects = 0;
size_t i = 0;
@@ -3328,10 +3330,7 @@ cl_error_t pdf_find_and_parse_objs_in_objstm(struct pdf_struct *pdf, struct objs
pdf_parseobj(pdf, obj);
}
- if (alerts) {
- status = CL_VIRUS;
- goto done;
- } else if (badobjects) {
+ if (badobjects) {
status = CL_EFORMAT;
goto done;
}
@@ -3346,11 +3345,10 @@ cl_error_t pdf_find_and_parse_objs_in_objstm(struct pdf_struct *pdf, struct objs
* @brief Search pdf buffer for objects. Parse each and then extract each.
*
* @param pdf Pdf struct that keeps track of all information found in the PDF.
- * @param[in,out] alerts The number of alerts, relevant in ALLMATCH mode.
*
* @return cl_error_t Error code.
*/
-cl_error_t pdf_find_and_extract_objs(struct pdf_struct *pdf, uint32_t *alerts)
+static cl_error_t pdf_find_and_extract_objs(struct pdf_struct *pdf)
{
cl_error_t status = CL_SUCCESS;
int32_t rv = 0;
@@ -3358,7 +3356,7 @@ cl_error_t pdf_find_and_extract_objs(struct pdf_struct *pdf, uint32_t *alerts)
uint32_t badobjects = 0;
cli_ctx *ctx = NULL;
- if (NULL == pdf || NULL == alerts) {
+ if (NULL == pdf) {
cli_errmsg("pdf_find_and_extract_objs: Invalid arguments.\n");
status = CL_EARG;
goto done;
@@ -3400,53 +3398,40 @@ cl_error_t pdf_find_and_extract_objs(struct pdf_struct *pdf, uint32_t *alerts)
/* It is encrypted, and a password/key needs to be supplied to decrypt.
* This doesn't trigger for PDFs that are encrypted but don't need
* a password to decrypt */
- status = cli_append_virus(pdf->ctx, "Heuristics.Encrypted.PDF");
- if (status == CL_VIRUS) {
- *alerts += 1;
- if (SCAN_ALLMATCHES)
- status = CL_CLEAN;
- }
+ status = cli_append_potentially_unwanted(pdf->ctx, "Heuristics.Encrypted.PDF");
}
if (CL_SUCCESS == status) {
status = run_pdf_hooks(pdf, PDF_PHASE_PARSED, -1, -1);
cli_dbgmsg("pdf_find_and_extract_objs: (parsed hooks) returned %d\n", status);
- if (status == CL_VIRUS) {
- *alerts += 1;
- if (SCAN_ALLMATCHES) {
- status = CL_CLEAN;
- }
- }
}
- /* extract PDF objs */
- for (i = 0; !status && i < pdf->nobjs; i++) {
- struct pdf_obj *obj = pdf->objs[i];
+ if (CL_SUCCESS == status) {
+ /* extract PDF objs */
+ for (i = 0; !status && i < pdf->nobjs; i++) {
+ struct pdf_obj *obj = pdf->objs[i];
- if (cli_checktimelimit(pdf->ctx) != CL_SUCCESS) {
- cli_errmsg("pdf_find_and_extract_objs: Timeout reached in the PDF parser while extracting objects.\n");
+ if (cli_checktimelimit(pdf->ctx) != CL_SUCCESS) {
+ cli_errmsg("pdf_find_and_extract_objs: Timeout reached in the PDF parser while extracting objects.\n");
- status = CL_ETIMEOUT;
- goto done;
- }
+ status = CL_ETIMEOUT;
+ goto done;
+ }
- status = pdf_extract_obj(pdf, obj, PDF_EXTRACT_OBJ_SCAN);
- switch (status) {
- case CL_EFORMAT:
- /* Don't halt on one bad object */
- cli_dbgmsg("pdf_find_and_extract_objs: Format error when extracting object, skipping to the next object.\n");
- badobjects++;
- pdf->stats.ninvalidobjs++;
- status = CL_CLEAN;
- break;
- case CL_VIRUS:
- *alerts += 1;
- if (SCAN_ALLMATCHES) {
+ status = pdf_extract_obj(pdf, obj, PDF_EXTRACT_OBJ_SCAN);
+ switch (status) {
+ case CL_EFORMAT:
+ /* Don't halt on one bad object */
+ cli_dbgmsg("pdf_find_and_extract_objs: Format error when extracting object, skipping to the next object.\n");
+ badobjects++;
+ pdf->stats.ninvalidobjs++;
status = CL_CLEAN;
- }
- break;
- default:
- break;
+ break;
+ case CL_VIRUS:
+ break;
+ default:
+ break;
+ }
}
}
@@ -3478,7 +3463,7 @@ cl_error_t cli_pdf(const char *dir, cli_ctx *ctx, off_t offset)
unsigned long xref;
long temp_long;
const char *pdfver, *tmp, *start, *eofmap, *q, *eof;
- unsigned i, alerts = 0;
+ unsigned i;
unsigned int objs_found = 0;
#if HAVE_JSON
json_object *pdfobj = NULL;
@@ -3652,11 +3637,7 @@ cl_error_t cli_pdf(const char *dir, cli_ctx *ctx, off_t offset)
pdf.startoff = offset;
rc = run_pdf_hooks(&pdf, PDF_PHASE_PRE, -1, -1);
- if ((rc == CL_VIRUS) && SCAN_ALLMATCHES) {
- cli_dbgmsg("cli_pdf: (pre hooks) returned %d\n", rc);
- alerts++;
- rc = CL_CLEAN;
- } else if (rc) {
+ if (CL_SUCCESS != rc) {
cli_dbgmsg("cli_pdf: (pre hooks) returning %d\n", rc);
rc = rc == CL_BREAK ? CL_CLEAN : rc;
@@ -3668,7 +3649,7 @@ cl_error_t cli_pdf(const char *dir, cli_ctx *ctx, off_t offset)
* This methodology adds objects from object streams.
*/
objs_found = pdf.nobjs;
- rc = pdf_find_and_extract_objs(&pdf, &alerts);
+ rc = pdf_find_and_extract_objs(&pdf);
if (CL_EMEM == rc) {
cli_dbgmsg("cli_pdf: pdf_find_and_extract_objs had an allocation failure\n");
@@ -3682,20 +3663,14 @@ cl_error_t cli_pdf(const char *dir, cli_ctx *ctx, off_t offset)
if (pdf.flags & (1 << ENCRYPTED_PDF))
pdf.flags &= ~((1 << BAD_FLATESTART) | (1 << BAD_STREAMSTART) | (1 << BAD_ASCIIDECODE));
- if (pdf.flags && !rc) {
+ if (pdf.flags && CL_SUCCESS == rc) {
cli_dbgmsg("cli_pdf: flags 0x%02x\n", pdf.flags);
rc = run_pdf_hooks(&pdf, PDF_PHASE_END, -1, -1);
- if (rc == CL_VIRUS) {
- alerts++;
- if (SCAN_ALLMATCHES) {
- rc = CL_CLEAN;
- }
- }
- if (!rc && SCAN_HEURISTICS && (ctx->dconf->other & OTHER_CONF_PDFNAMEOBJ)) {
+ if (CL_SUCCESS == rc && SCAN_HEURISTICS && (ctx->dconf->other & OTHER_CONF_PDFNAMEOBJ)) {
if (pdf.flags & (1 << ESCAPED_COMMON_PDFNAME)) {
/* for example /Fl#61te#44#65#63#6f#64#65 instead of /FlateDecode */
- cli_append_potentially_unwanted(ctx, "Heuristics.PDF.ObfuscatedNameObject");
+ rc = cli_append_potentially_unwanted(ctx, "Heuristics.PDF.ObfuscatedNameObject");
}
}
#if 0
@@ -3704,7 +3679,7 @@ cl_error_t cli_pdf(const char *dir, cli_ctx *ctx, off_t offset)
pdf.flags &= ~ (1 << BAD_ASCIIDECODE);
if (pdf.flags & (1 << MANY_FILTERS))
pdf.flags &= ~ (1 << BAD_ASCIIDECODE);
- if (!rc && (pdf.flags &
+ if (CL_SUCCESS == rc && (pdf.flags &
((1 << BAD_PDF_TOOMANYOBJS) | (1 << BAD_STREAM_FILTERS) |
(1< 0) {
+ if (CL_SUCCESS == rc && pdf.stats.ninvalidobjs > 0) {
rc = CL_EFORMAT;
}
diff --git a/libclamav/pdf.h b/libclamav/pdf.h
index 3a03f19f13..f109c9d25b 100644
--- a/libclamav/pdf.h
+++ b/libclamav/pdf.h
@@ -197,7 +197,6 @@ void pdf_free_array(struct pdf_array *array);
void pdf_print_dict(struct pdf_dict *dict, unsigned long depth);
void pdf_print_array(struct pdf_array *array, unsigned long depth);
-cl_error_t pdf_find_and_extract_objs(struct pdf_struct *pdf, uint32_t *alerts);
cl_error_t pdf_find_and_parse_objs_in_objstm(struct pdf_struct *pdf, struct objstm_struct *objstm);
#endif
diff --git a/libclamav/pdfdecode.c b/libclamav/pdfdecode.c
index 473cfcd43a..382cabac60 100644
--- a/libclamav/pdfdecode.c
+++ b/libclamav/pdfdecode.c
@@ -111,7 +111,6 @@ size_t pdf_decodestream(
{
struct pdf_token *token = NULL;
size_t bytes_scanned = 0;
- cli_ctx *ctx = NULL;
if (!status) {
/* invalid args, and no way to pass back the status code */
@@ -124,8 +123,6 @@ size_t pdf_decodestream(
goto done;
}
- ctx = pdf->ctx;
-
if (!stream || !streamlen || fout < 0) {
cli_dbgmsg("pdf_decodestream: no filters or stream on obj %u %u\n", obj->id >> 8, obj->id & 0xff);
*status = CL_ENULLARG;
@@ -156,14 +153,14 @@ size_t pdf_decodestream(
*status = CL_EMEM;
goto done;
}
+
memcpy(token->content, stream, streamlen);
token->length = streamlen;
cli_dbgmsg("pdf_decodestream: detected %lu applied filters\n", (long unsigned)(obj->numfilters));
bytes_scanned = pdf_decodestream_internal(pdf, obj, params, token, fout, status, objstm);
-
- if ((CL_VIRUS == *status) && !SCAN_ALLMATCHES) {
+ if (CL_VIRUS == *status) {
goto done;
}
@@ -224,10 +221,8 @@ static size_t pdf_decodestream_internal(
struct pdf_struct *pdf, struct pdf_obj *obj, struct pdf_dict *params,
struct pdf_token *token, int fout, cl_error_t *status, struct objstm_struct *objstm)
{
- cl_error_t vir = CL_CLEAN;
cl_error_t retval = CL_SUCCESS;
size_t bytes_scanned = 0;
- cli_ctx *ctx = NULL;
const char *filter = NULL;
uint32_t i;
@@ -242,7 +237,6 @@ static size_t pdf_decodestream_internal(
goto done;
}
- ctx = pdf->ctx;
*status = CL_SUCCESS;
/*
@@ -323,29 +317,25 @@ static size_t pdf_decodestream_internal(
}
if (retval != CL_SUCCESS) {
- if (retval == CL_VIRUS && SCAN_ALLMATCHES) {
- vir = CL_VIRUS;
- } else {
- const char *reason;
-
- switch (retval) {
- case CL_VIRUS:
- *status = CL_VIRUS;
- reason = "detection";
- break;
- case CL_BREAK:
- *status = CL_SUCCESS;
- reason = "decoding break";
- break;
- default:
- *status = CL_EPARSE;
- reason = "decoding error";
- break;
- }
+ const char *reason;
- cli_dbgmsg("pdf_decodestream_internal: stopping after %d (of %u) filters (reason: %s)\n", i, obj->numfilters, reason);
- break;
+ switch (retval) {
+ case CL_VIRUS:
+ *status = CL_VIRUS;
+ reason = "detection";
+ break;
+ case CL_BREAK:
+ *status = CL_SUCCESS;
+ reason = "decoding break";
+ break;
+ default:
+ *status = CL_EPARSE;
+ reason = "decoding error";
+ break;
}
+
+ cli_dbgmsg("pdf_decodestream_internal: stopping after %d (of %u) filters (reason: %s)\n", i, obj->numfilters, reason);
+ break;
}
token->success++;
@@ -376,7 +366,7 @@ static size_t pdf_decodestream_internal(
}
if ((NULL != objstm) &&
- ((CL_SUCCESS == *status) || ((CL_VIRUS == *status) && SCAN_ALLMATCHES))) {
+ (CL_SUCCESS == *status)) {
unsigned int objs_found = pdf->nobjs;
/*
@@ -406,9 +396,6 @@ static size_t pdf_decodestream_internal(
done:
- if (vir == CL_VIRUS)
- *status = CL_VIRUS;
-
return bytes_scanned;
}
diff --git a/libclamav/pdfng.c b/libclamav/pdfng.c
index 2ab65f793d..032cfa726a 100644
--- a/libclamav/pdfng.c
+++ b/libclamav/pdfng.c
@@ -1008,6 +1008,7 @@ struct pdf_array *pdf_parse_array(struct pdf_struct *pdf, struct pdf_obj *obj, s
}
/* Not a dictionary. Intentionally fall through. */
+ /* fall-through */
case '(':
val = pdf_parse_string(pdf, obj, begin, end - objstart, NULL, &begin, NULL);
begin += 2;
diff --git a/libclamav/pe.c b/libclamav/pe.c
index 9a43562119..126348548c 100644
--- a/libclamav/pe.c
+++ b/libclamav/pe.c
@@ -193,47 +193,46 @@
free(tempfile); \
break;
-#define CLI_UNPRESULTS_(NAME, FSGSTUFF, EXPR, GOOD, FREEME) \
- switch (EXPR) { \
- case GOOD: /* Unpacked and rebuilt */ \
- if (ctx->engine->keeptmp) \
- cli_dbgmsg(NAME ": Unpacked and rebuilt executable saved in %s\n", tempfile); \
- else \
- cli_dbgmsg(NAME ": Unpacked and rebuilt executable\n"); \
- cli_multifree FREEME; \
- cli_exe_info_destroy(peinfo); \
- lseek(ndesc, 0, SEEK_SET); \
- cli_dbgmsg("***** Scanning rebuilt PE file *****\n"); \
- SHA_OFF; \
- if (cli_magic_scan_desc(ndesc, tempfile, ctx, NULL) == CL_VIRUS) { \
- close(ndesc); \
- SHA_RESET; \
- CLI_TMPUNLK(); \
- free(tempfile); \
- return CL_VIRUS; \
- } \
- SHA_RESET; \
- close(ndesc); \
- CLI_TMPUNLK(); \
- free(tempfile); \
- return CL_CLEAN; \
- \
- FSGSTUFF; \
- \
- default: \
- cli_dbgmsg(NAME ": Unpacking failed\n"); \
- close(ndesc); \
- if (cli_unlink(tempfile)) { \
- cli_exe_info_destroy(peinfo); \
- free(tempfile); \
- cli_multifree FREEME; \
- return CL_EUNLINK; \
- } \
- cli_multifree FREEME; \
- free(tempfile); \
- }
-
+#define CLI_UNPRESULTS_(NAME, FSGSTUFF, EXPR, GOOD, FREEME) \
+ switch (EXPR) { \
+ case GOOD: /* Unpacked and rebuilt */ \
+ cli_dbgmsg(NAME ": Unpacked and rebuilt executable saved in %s\n", tempfile); \
+ cli_multifree FREEME; \
+ cli_exe_info_destroy(peinfo); \
+ lseek(ndesc, 0, SEEK_SET); \
+ cli_dbgmsg("***** Scanning rebuilt PE file *****\n"); \
+ SHA_OFF; \
+ if (CL_SUCCESS != (ret = cli_magic_scan_desc(ndesc, tempfile, ctx, NULL))) { \
+ close(ndesc); \
+ SHA_RESET; \
+ CLI_TMPUNLK(); \
+ free(tempfile); \
+ return ret; \
+ } \
+ SHA_RESET; \
+ close(ndesc); \
+ CLI_TMPUNLK(); \
+ free(tempfile); \
+ return CL_CLEAN; \
+ \
+ FSGSTUFF; \
+ \
+ default: \
+ cli_dbgmsg(NAME ": Unpacking failed\n"); \
+ close(ndesc); \
+ if (cli_unlink(tempfile)) { \
+ cli_exe_info_destroy(peinfo); \
+ free(tempfile); \
+ cli_multifree FREEME; \
+ return CL_EUNLINK; \
+ } \
+ cli_multifree FREEME; \
+ free(tempfile); \
+ }
+
+// The GOOD parameter indicates what a successful unpacking should return.
#define CLI_UNPRESULTS(NAME, EXPR, GOOD, FREEME) CLI_UNPRESULTS_(NAME, (void)0, EXPR, GOOD, FREEME)
+
// TODO The second argument to FSGCASE below should match what gets freed as
// indicated by FREEME, otherwise a memory leak can occur (as currently used,
// it looks like dest can get leaked by these macros).
@@ -557,15 +556,15 @@ static unsigned int cli_hashsect(fmap_t *map, struct cli_exe_section *s, unsigne
}
/* check hash section sigs */
-static int scan_pe_mdb(cli_ctx *ctx, struct cli_exe_section *exe_section)
+static cl_error_t scan_pe_mdb(cli_ctx *ctx, struct cli_exe_section *exe_section)
{
struct cli_matcher *mdb_sect = ctx->engine->hm_mdb;
unsigned char *hashset[CLI_HASH_AVAIL_TYPES];
const char *virname = NULL;
int foundsize[CLI_HASH_AVAIL_TYPES];
int foundwild[CLI_HASH_AVAIL_TYPES];
- enum CLI_HASH_TYPE type;
- int ret = CL_CLEAN;
+ cli_hash_type_t type;
+ cl_error_t ret = CL_CLEAN;
unsigned char *md5 = NULL;
/* pick hashtypes to generate */
@@ -627,20 +626,14 @@ static int scan_pe_mdb(cli_ctx *ctx, struct cli_exe_section *exe_section)
for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++) {
if (foundsize[type] && cli_hm_scan(hashset[type], exe_section->rsz, &virname, mdb_sect, type) == CL_VIRUS) {
ret = cli_append_virus(ctx, virname);
- if (ret != CL_CLEAN) {
- if (ret != CL_VIRUS)
- break;
- else if (!SCAN_ALLMATCHES)
- break;
+ if (ret != CL_SUCCESS) {
+ break;
}
}
if (foundwild[type] && cli_hm_scan_wild(hashset[type], &virname, mdb_sect, type) == CL_VIRUS) {
ret = cli_append_virus(ctx, virname);
- if (ret != CL_CLEAN) {
- if (ret != CL_VIRUS)
- break;
- else if (!SCAN_ALLMATCHES)
- break;
+ if (ret != CL_SUCCESS) {
+ break;
}
}
}
@@ -2253,7 +2246,7 @@ static inline int hash_impfns(cli_ctx *ctx, void **hashctx, uint32_t *impsz, str
unsigned int err = 0;
int num_fns = 0, ret = CL_SUCCESS;
const char *buffer;
- enum CLI_HASH_TYPE type;
+ cli_hash_type_t type;
#if HAVE_JSON
json_object *imptbl = NULL;
#else
@@ -2421,7 +2414,7 @@ static cl_error_t hash_imptbl(cli_ctx *ctx, unsigned char **digest, uint32_t *im
uint32_t impoff, offset;
const char *buffer;
void *hashctx[CLI_HASH_AVAIL_TYPES] = {0};
- enum CLI_HASH_TYPE type;
+ cli_hash_type_t type;
int nimps = 0;
unsigned int err;
int first = 1;
@@ -2565,15 +2558,15 @@ static cl_error_t hash_imptbl(cli_ctx *ctx, unsigned char **digest, uint32_t *im
return status;
}
-static int scan_pe_imp(cli_ctx *ctx, struct cli_exe_info *peinfo)
+static cl_error_t scan_pe_imp(cli_ctx *ctx, struct cli_exe_info *peinfo)
{
struct cli_matcher *imp = ctx->engine->hm_imp;
unsigned char *hashset[CLI_HASH_AVAIL_TYPES];
const char *virname = NULL;
int genhash[CLI_HASH_AVAIL_TYPES];
uint32_t impsz = 0;
- enum CLI_HASH_TYPE type;
- int ret = CL_CLEAN;
+ cli_hash_type_t type;
+ cl_error_t ret = CL_CLEAN;
/* pick hashtypes to generate */
for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++) {
@@ -2639,20 +2632,14 @@ static int scan_pe_imp(cli_ctx *ctx, struct cli_exe_info *peinfo)
for (type = CLI_HASH_MD5; type < CLI_HASH_AVAIL_TYPES; type++) {
if (cli_hm_scan(hashset[type], impsz, &virname, imp, type) == CL_VIRUS) {
ret = cli_append_virus(ctx, virname);
- if (ret != CL_CLEAN) {
- if (ret != CL_VIRUS)
- break;
- else if (!SCAN_ALLMATCHES)
- break;
+ if (ret != CL_SUCCESS) {
+ break;
}
}
if (cli_hm_scan_wild(hashset[type], &virname, imp, type) == CL_VIRUS) {
cli_append_virus(ctx, virname);
- if (ret != CL_CLEAN) {
- if (ret != CL_VIRUS)
- break;
- else if (!SCAN_ALLMATCHES)
- break;
+ if (ret != CL_SUCCESS) {
+ break;
}
}
}
@@ -2786,7 +2773,10 @@ int cli_scanpe(cli_ctx *ctx)
int (*upxfn)(const char *, uint32_t, char *, uint32_t *, uint32_t, uint32_t, uint32_t) = NULL;
const char *src = NULL;
char *dest = NULL;
- int ndesc, ret = CL_CLEAN, upack = 0;
+ int ndesc;
+ cl_error_t ret = CL_SUCCESS;
+ cl_error_t peheader_ret;
+ int upack = 0;
size_t fsize;
struct cli_bc_ctx *bc_ctx;
fmap_t *map;
@@ -2794,7 +2784,6 @@ int cli_scanpe(cli_ctx *ctx)
#ifdef HAVE__INTERNAL__SHA_COLLECT
int sha_collect = ctx->sha_collect;
#endif
- uint32_t viruses_found = 0;
#if HAVE_JSON
int toval = 0;
struct json_object *pe_json = NULL;
@@ -2834,41 +2823,45 @@ int cli_scanpe(cli_ctx *ctx)
cli_exe_info_init(peinfo, 0);
- ret = cli_peheader(map, peinfo, opts, ctx);
+ peheader_ret = cli_peheader(map, peinfo, opts, ctx);
// Warn the user if PE header parsing failed - if it's a binary that runs
// successfully on Windows, we need to relax our PE parsing standards so
// that we make sure the executable gets scanned appropriately
-#define PE_HDR_PARSE_FAIL_CONSEQUENCE "won't attempt .mdb / .imp / PE-specific BC rule matching or exe unpacking\n"
+ switch (peheader_ret) {
+ case CL_EFORMAT:
+ ret = CL_SUCCESS;
+ if (DETECT_BROKEN_PE) {
+ ret = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Executable");
+ }
+ cli_dbgmsg("cli_scanpe: PE header appears broken - won't attempt .mdb / .imp / PE-specific BC rule matching or exe unpacking\n");
+ cli_exe_info_destroy(peinfo);
+ return ret;
- if (CLI_PEHEADER_RET_BROKEN_PE == ret) {
- if (DETECT_BROKEN_PE) {
- // TODO Handle allmatch
- ret = cli_append_virus(ctx, "Heuristics.Broken.Executable");
+ case CL_ERROR:
+ ret = CL_SUCCESS;
+ cli_dbgmsg("cli_scanpe: An error occurred when parsing the PE header - won't attempt .mdb / .imp / PE-specific BC rule matching or exe unpacking\n");
cli_exe_info_destroy(peinfo);
return ret;
- }
- cli_dbgmsg("cli_scanpe: PE header appears broken - " PE_HDR_PARSE_FAIL_CONSEQUENCE);
- cli_exe_info_destroy(peinfo);
- return CL_CLEAN;
- } else if (CLI_PEHEADER_RET_JSON_TIMEOUT == ret) {
- cli_dbgmsg("cli_scanpe: JSON creation timed out - " PE_HDR_PARSE_FAIL_CONSEQUENCE);
- cli_exe_info_destroy(peinfo);
- return CL_ETIMEOUT;
- } else if (CLI_PEHEADER_RET_GENERIC_ERROR == ret) {
- cli_dbgmsg("cli_scanpe: An error occurred when parsing the PE header - " PE_HDR_PARSE_FAIL_CONSEQUENCE);
- cli_exe_info_destroy(peinfo);
- return CL_CLEAN;
+ case CL_ETIMEOUT:
+ ret = CL_ETIMEOUT;
+ cli_dbgmsg("cli_scanpe: JSON creation timed out - won't attempt .mdb / .imp / PE-specific BC rule matching or exe unpacking\n");
+ cli_exe_info_destroy(peinfo);
+ return ret;
+
+ default:
+ break;
}
if (!peinfo->is_pe32plus) { /* PE */
- if (DCONF & PE_CONF_UPACK)
+ if (DCONF & PE_CONF_UPACK) {
upack = (EC16(peinfo->file_hdr.SizeOfOptionalHeader) == 0x148);
+ }
}
- for (i = 0; i < peinfo->nsections; i++) {
+ for (i = 0; i < peinfo->nsections; i++) {
if (peinfo->sections[i].rsz) { /* Don't bother with virtual only sections */
// TODO Regarding the commented out check below:
// This used to check that the section name was NULL, but now that
@@ -2883,18 +2876,13 @@ int cli_scanpe(cli_ctx *ctx)
/* check hash section sigs */
if ((DCONF & PE_CONF_MD5SECT) && ctx->engine->hm_mdb) {
ret = scan_pe_mdb(ctx, &(peinfo->sections[i]));
- if (ret != CL_CLEAN) {
- if (ret == CL_VIRUS && !SCAN_ALLMATCHES) {
- cli_dbgmsg("------------------------------------\n");
- cli_exe_info_destroy(peinfo);
- return ret;
- } else if (ret != CL_VIRUS) {
+ if (ret != CL_SUCCESS) {
+ if (ret != CL_VIRUS) {
cli_errmsg("cli_scanpe: scan_pe_mdb failed: %s!\n", cl_strerror(ret));
-
- cli_dbgmsg("------------------------------------\n");
- cli_exe_info_destroy(peinfo);
- return ret;
}
+ cli_dbgmsg("------------------------------------\n");
+ cli_exe_info_destroy(peinfo);
+ return ret;
}
}
}
@@ -2917,7 +2905,7 @@ int cli_scanpe(cli_ctx *ctx)
/* CLI_UNPTEMP("cli_scanpe: DISASM",(peinfo->sections,0)); */
/* if(disasmbuf((unsigned char*)epbuff, epsize, ndesc)) */
- /* ret = cli_scan_desc(ndesc, ctx, CL_TYPE_PE_DISASM, 1, NULL, AC_SCAN_VIR); */
+ /* ret = cli_scan_desc(ndesc, ctx, CL_TYPE_PE_DISASM, true, NULL, AC_SCAN_VIR); */
/* close(ndesc); */
/* if(ret == CL_VIRUS) { */
/* cli_exe_info_destroy(peinfo); */
@@ -2930,8 +2918,7 @@ int cli_scanpe(cli_ctx *ctx)
if (peinfo->overlay_start && peinfo->overlay_size > 0) {
ret = cli_scanishield(ctx, peinfo->overlay_start, peinfo->overlay_size);
- if (ret != CL_CLEAN) {
- // TODO Handle allmatch
+ if (ret != CL_SUCCESS) {
cli_exe_info_destroy(peinfo);
return ret;
}
@@ -2970,10 +2957,11 @@ int cli_scanpe(cli_ctx *ctx)
break;
case CL_VIRUS:
case CL_BREAK:
- // TODO Handle allmatch
cli_exe_info_destroy(peinfo);
cli_bytecode_context_destroy(bc_ctx);
return ret == CL_VIRUS ? CL_VIRUS : CL_CLEAN;
+ default:
+ break;
}
cli_bytecode_context_destroy(bc_ctx);
@@ -2992,9 +2980,6 @@ int cli_scanpe(cli_ctx *ctx)
cli_warnmsg("cli_scanpe: NULL argument supplied\n");
break;
case CL_VIRUS:
- if (SCAN_ALLMATCHES)
- break;
- /* intentional fall-through */
case CL_BREAK:
cli_exe_info_destroy(peinfo);
return ret == CL_VIRUS ? CL_VIRUS : CL_CLEAN;
@@ -3011,18 +2996,10 @@ int cli_scanpe(cli_ctx *ctx)
if (pt) {
pt += 15;
if ((((uint32_t)cli_readint32(pt) ^ (uint32_t)cli_readint32(pt + 4)) == 0x505a4f) && (((uint32_t)cli_readint32(pt + 8) ^ (uint32_t)cli_readint32(pt + 12)) == 0xffffb) && (((uint32_t)cli_readint32(pt + 16) ^ (uint32_t)cli_readint32(pt + 20)) == 0xb8)) {
- ret = cli_append_virus(ctx, "Heuristics.W32.Parite.B");
- if (ret != CL_CLEAN) {
- if (ret == CL_VIRUS) {
- if (!SCAN_ALLMATCHES) {
- cli_exe_info_destroy(peinfo);
- return ret;
- } else
- viruses_found++;
- } else {
- cli_exe_info_destroy(peinfo);
- return ret;
- }
+ ret = cli_append_potentially_unwanted(ctx, "Heuristics.W32.Parite.B");
+ if (ret != CL_SUCCESS) {
+ cli_exe_info_destroy(peinfo);
+ return ret;
}
}
}
@@ -3147,18 +3124,10 @@ int cli_scanpe(cli_ctx *ctx)
break;
case KZSLOOP:
if (op == kzdsize + 0x48 && *kzcode == 0x75 && kzlen - (int8_t)kzcode[1] - 3 <= kzinitlen && kzlen - (int8_t)kzcode[1] >= kzxorlen) {
- ret = cli_append_virus(ctx, "Heuristics.W32.Kriz");
- if (ret != CL_CLEAN) {
- if (ret == CL_VIRUS) {
- if (!SCAN_ALLMATCHES) {
- cli_exe_info_destroy(peinfo);
- return ret;
- } else
- viruses_found++;
- } else {
- cli_exe_info_destroy(peinfo);
- return ret;
- }
+ ret = cli_append_potentially_unwanted(ctx, "Heuristics.W32.Kriz");
+ if (ret != CL_SUCCESS) {
+ cli_exe_info_destroy(peinfo);
+ return ret;
}
}
cli_dbgmsg("cli_scanpe: kriz: loop out of bounds, corrupted sample?\n");
@@ -3184,18 +3153,10 @@ int cli_scanpe(cli_ctx *ctx)
if ((tbuff = fmap_need_off_once(map, peinfo->sections[peinfo->nsections - 1].raw + rsize - bw, 4096))) {
if (cli_memstr(tbuff, 4091, "\xe8\x2c\x61\x00\x00", 5)) {
- ret = cli_append_virus(ctx, dam ? "Heuristics.W32.Magistr.A.dam" : "Heuristics.W32.Magistr.A");
- if (ret != CL_CLEAN) {
- if (ret == CL_VIRUS) {
- if (!SCAN_ALLMATCHES) {
- cli_exe_info_destroy(peinfo);
- return ret;
- } else
- viruses_found++;
- } else {
- cli_exe_info_destroy(peinfo);
- return ret;
- }
+ ret = cli_append_potentially_unwanted(ctx, dam ? "Heuristics.W32.Magistr.A.dam" : "Heuristics.W32.Magistr.A");
+ if (ret != CL_SUCCESS) {
+ cli_exe_info_destroy(peinfo);
+ return ret;
}
}
}
@@ -3205,18 +3166,10 @@ int cli_scanpe(cli_ctx *ctx)
if ((tbuff = fmap_need_off_once(map, peinfo->sections[peinfo->nsections - 1].raw + rsize - bw, 4096))) {
if (cli_memstr(tbuff, 4091, "\xe8\x04\x72\x00\x00", 5)) {
- ret = cli_append_virus(ctx, dam ? "Heuristics.W32.Magistr.B.dam" : "Heuristics.W32.Magistr.B");
- if (ret != CL_CLEAN) {
- if (ret == CL_VIRUS) {
- if (!SCAN_ALLMATCHES) {
- cli_exe_info_destroy(peinfo);
- return ret;
- } else
- viruses_found++;
- } else {
- cli_exe_info_destroy(peinfo);
- return ret;
- }
+ ret = cli_append_potentially_unwanted(ctx, dam ? "Heuristics.W32.Magistr.B.dam" : "Heuristics.W32.Magistr.B");
+ if (ret != CL_SUCCESS) {
+ cli_exe_info_destroy(peinfo);
+ return ret;
}
}
}
@@ -3282,20 +3235,11 @@ int cli_scanpe(cli_ctx *ctx)
continue;
if ((jump = cli_readint32(code)) == 0x60ec8b55 || (code[4] == 0x0ec && ((jump == 0x83ec8b55 && code[6] == 0x60) || (jump == 0x81ec8b55 && !code[7] && !code[8])))) {
- ret = cli_append_virus(ctx, "Heuristics.W32.Polipos.A");
- if (ret != CL_CLEAN) {
- if (ret == CL_VIRUS) {
- if (!SCAN_ALLMATCHES) {
- free(jumps);
- cli_exe_info_destroy(peinfo);
- return ret;
- } else
- viruses_found++;
- } else {
- free(jumps);
- cli_exe_info_destroy(peinfo);
- return ret;
- }
+ ret = cli_append_potentially_unwanted(ctx, "Heuristics.W32.Polipos.A");
+ if (ret != CL_SUCCESS) {
+ free(jumps);
+ cli_exe_info_destroy(peinfo);
+ return ret;
}
}
}
@@ -3317,20 +3261,11 @@ int cli_scanpe(cli_ctx *ctx)
} else {
cli_parseres_special(EC32(peinfo->dirs[2].VirtualAddress), EC32(peinfo->dirs[2].VirtualAddress), map, peinfo, fsize, 0, 0, &m, stats);
if ((ret = cli_detect_swizz(stats)) == CL_VIRUS) {
- ret = cli_append_virus(ctx, "Heuristics.Trojan.Swizzor.Gen");
- if (ret != CL_CLEAN) {
- if (ret == CL_VIRUS) {
- if (!SCAN_ALLMATCHES) {
- free(stats);
- cli_exe_info_destroy(peinfo);
- return ret;
- } else
- viruses_found++;
- } else {
- free(stats);
- cli_exe_info_destroy(peinfo);
- return ret;
- }
+ ret = cli_append_potentially_unwanted(ctx, "Heuristics.Trojan.Swizzor.Gen");
+ if (ret != CL_SUCCESS) {
+ free(stats);
+ cli_exe_info_destroy(peinfo);
+ return ret;
}
}
}
@@ -4028,12 +3963,13 @@ int cli_scanpe(cli_ctx *ctx)
cli_dbgmsg("***** Scanning decompressed file *****\n");
SHA_OFF;
- if ((ret = cli_magic_scan_desc(ndesc, tempfile, ctx, NULL)) == CL_VIRUS) {
+ ret = cli_magic_scan_desc(ndesc, tempfile, ctx, NULL);
+ if (CL_SUCCESS != ret) {
close(ndesc);
SHA_RESET;
CLI_TMPUNLK();
free(tempfile);
- return CL_VIRUS;
+ return ret;
}
SHA_RESET;
@@ -4187,6 +4123,7 @@ int cli_scanpe(cli_ctx *ctx)
!memcmp(epbuff + 0x63 + offset, "\xaa\xe2\xcc", 3) &&
(fsize >= peinfo->sections[peinfo->nsections - 1].raw + 0xC6 + ecx + offset)) {
+ size_t num_alerts;
char *spinned;
if ((spinned = (char *)cli_malloc(fsize)) == NULL) {
@@ -4207,20 +4144,23 @@ int cli_scanpe(cli_ctx *ctx)
cli_jsonstr(pe_json, "Packer", "yC");
#endif
- do {
- size_t num_alerts = evidence_num_alerts(ctx->evidence);
+ // record number of alerts before unpacking and scanning
+ num_alerts = evidence_num_alerts(ctx->evidence);
- cli_dbgmsg("%d,%d,%d,%d\n", peinfo->nsections - 1, peinfo->e_lfanew, ecx, offset);
- CLI_UNPTEMP("cli_scanpe: yC", (spinned, 0));
- CLI_UNPRESULTS("cli_scanpe: yC", (yc_decrypt(ctx, spinned, fsize, peinfo->sections, peinfo->nsections - 1, peinfo->e_lfanew, ndesc, ecx, offset)), 0, (spinned, 0));
+ cli_dbgmsg("%d,%d,%d,%d\n", peinfo->nsections - 1, peinfo->e_lfanew, ecx, offset);
+ CLI_UNPTEMP("cli_scanpe: yC", (spinned, 0));
+ CLI_UNPRESULTS("cli_scanpe: yC", (yc_decrypt(ctx, spinned, fsize, peinfo->sections, peinfo->nsections - 1, peinfo->e_lfanew, ndesc, ecx, offset)), 0, (spinned, 0));
- if (SCAN_ALLMATCHES && num_alerts != evidence_num_alerts(ctx->evidence)) {
- // In ALLMATCH-mode, CLI_UNPRESULTS() will not return CL_VIRUS when something is found.
- // We apparently want to return CL_VIRUS here if CLI_UNPRESULTS() found something (preserving previous behavior).
- cli_exe_info_destroy(peinfo);
- return CL_VIRUS;
- }
- } while (0);
+ // Unpacking may have added new alerts if the bounds-check failed.
+ // Compare number of alerts now with number of alerts before unpacking/scanning.
+ // If the number of alerts has increased, then bail.
+ //
+ // This preserves the intention of https://github.com/Cisco-Talos/clamav/commit/771c23099893f02f1316960fbe84f62b115a3556
+ // although that commit had it bailing if a match occured even in allmatch-mode, which we do not want to do.
+ if (!SCAN_ALLMATCHES && num_alerts != evidence_num_alerts(ctx->evidence)) {
+ cli_exe_info_destroy(peinfo);
+ return CL_VIRUS;
+ }
}
}
@@ -4470,7 +4410,6 @@ int cli_scanpe(cli_ctx *ctx)
case CL_VIRUS:
cli_exe_info_destroy(peinfo);
cli_bytecode_context_destroy(bc_ctx);
- // TODO Handle allmatch
return CL_VIRUS;
case CL_SUCCESS:
ndesc = cli_bytecode_context_getresult_file(bc_ctx, &tempfile);
@@ -4491,13 +4430,10 @@ int cli_scanpe(cli_ctx *ctx)
return CL_ETIMEOUT;
#endif
- if (SCAN_ALLMATCHES && viruses_found)
- return CL_VIRUS;
-
- return CL_CLEAN;
+ return CL_SUCCESS;
}
-int cli_pe_targetinfo(cli_ctx *ctx, struct cli_exe_info *peinfo)
+cl_error_t cli_pe_targetinfo(cli_ctx *ctx, struct cli_exe_info *peinfo)
{
return cli_peheader(ctx->fmap, peinfo, CLI_PEHEADER_OPT_EXTRACT_VINFO, NULL);
}
@@ -4530,10 +4466,9 @@ int cli_pe_targetinfo(cli_ctx *ctx, struct cli_exe_info *peinfo)
* rsz is just set to 0 for it.
* @param ctx The overarching cli_ctx. This is required with certain opts, but
* optional otherwise.
- * @return If the PE header is parsed successfully, CLI_PEHEADER_RET_SUCCESS
- * is returned. If it seems like the PE is broken,
- * CLI_PEHEADER_RET_BROKEN_PE is returned. Otherwise, one of the
- * other error codes is returned.
+ * @return If the PE header is parsed successfully, CL_SUCCESS is returned.
+ * If it seems like the PE is broken, CL_EFORMAT is returned.
+ * Otherwise, one of the other error codes is returned.
* The caller MUST destroy peinfo, regardless of what this function
* returns.
*
@@ -4560,8 +4495,10 @@ int cli_pe_targetinfo(cli_ctx *ctx, struct cli_exe_info *peinfo)
*
* TODO Same as above but with JSON creation
*/
-int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ctx *ctx)
+cl_error_t cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ctx *ctx)
{
+ cl_error_t ret = CL_ERROR;
+
uint16_t e_magic; /* DOS signature ("MZ") */
const char *archtype = NULL, *subsystem = NULL;
time_t timestamp;
@@ -4583,7 +4520,6 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ct
int native = 0;
size_t read;
- int ret = CLI_PEHEADER_RET_GENERIC_ERROR;
#if HAVE_JSON
int toval = 0;
struct json_object *pe_json = NULL;
@@ -4617,7 +4553,7 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ct
if (fmap_readn(map, &(peinfo->e_lfanew), peinfo->offset + 58 + sizeof(e_magic), sizeof(peinfo->e_lfanew)) != sizeof(peinfo->e_lfanew)) {
/* truncated header? */
cli_dbgmsg("cli_peheader: Unable to read e_lfanew - truncated header?\n");
- ret = CLI_PEHEADER_RET_BROKEN_PE;
+ ret = CL_EFORMAT;
goto done;
}
@@ -4812,7 +4748,7 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ct
cli_dbgmsg("cli_peheader: Invalid NumberOfSections (0)\n");
}
}
- ret = CLI_PEHEADER_RET_BROKEN_PE;
+ ret = CL_EFORMAT;
goto done;
}
@@ -4845,14 +4781,14 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ct
pe_add_heuristic_property(ctx, "BadOptionalHeaderSize");
}
#endif
- ret = CLI_PEHEADER_RET_BROKEN_PE;
+ ret = CL_EFORMAT;
goto done;
}
at = peinfo->offset + peinfo->e_lfanew + sizeof(struct pe_image_file_hdr);
if (fmap_readn(map, &(peinfo->pe_opt.opt32), at, sizeof(struct pe_image_optional_hdr32)) != sizeof(struct pe_image_optional_hdr32)) {
cli_dbgmsg("cli_peheader: Can't read optional file header\n");
- ret = CLI_PEHEADER_RET_BROKEN_PE;
+ ret = CL_EFORMAT;
goto done;
}
stored_opt_hdr_size = sizeof(struct pe_image_optional_hdr32);
@@ -4871,13 +4807,13 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ct
pe_add_heuristic_property(ctx, "BadOptionalHeaderSizePE32Plus");
}
#endif
- ret = CLI_PEHEADER_RET_BROKEN_PE;
+ ret = CL_EFORMAT;
goto done;
}
if (fmap_readn(map, (void *)(((size_t) & (peinfo->pe_opt.opt64)) + sizeof(struct pe_image_optional_hdr32)), at, OPT_HDR_SIZE_DIFF) != OPT_HDR_SIZE_DIFF) {
cli_dbgmsg("cli_peheader: Can't read additional optional file header bytes\n");
- ret = CLI_PEHEADER_RET_BROKEN_PE;
+ ret = CL_EFORMAT;
goto done;
}
@@ -5060,7 +4996,7 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ct
if (!native && (!salign || (salign % 0x1000))) {
cli_dbgmsg("cli_peheader: Bad section alignment\n");
if (opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS) {
- ret = CLI_PEHEADER_RET_BROKEN_PE;
+ ret = CL_EFORMAT;
goto done;
}
}
@@ -5068,7 +5004,7 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ct
if (!native && (!falign || (falign % 0x200))) {
cli_dbgmsg("cli_peheader: Bad file alignment\n");
if (opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS) {
- ret = CLI_PEHEADER_RET_BROKEN_PE;
+ ret = CL_EFORMAT;
goto done;
}
}
@@ -5098,7 +5034,7 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ct
if (opt_hdr_size < (stored_opt_hdr_size + data_dirs_size)) {
cli_dbgmsg("cli_peheader: SizeOfOptionalHeader too small (doesn't include data dir size)\n");
- ret = CLI_PEHEADER_RET_BROKEN_PE;
+ ret = CL_EFORMAT;
goto done;
}
@@ -5149,7 +5085,7 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ct
read = fmap_readn(map, section_hdrs, at, peinfo->nsections * sizeof(struct pe_image_section_hdr));
if ((read == (size_t)-1) || (read != peinfo->nsections * sizeof(struct pe_image_section_hdr))) {
cli_dbgmsg("cli_peheader: Can't read section header - possibly broken PE file\n");
- ret = CLI_PEHEADER_RET_BROKEN_PE;
+ ret = CL_EFORMAT;
goto done;
}
at += sizeof(struct pe_image_section_hdr) * peinfo->nsections;
@@ -5195,7 +5131,7 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ct
if (opts & CLI_PEHEADER_OPT_REMOVE_MISSING_SECTIONS) {
if (peinfo->nsections == 1) {
- ret = CLI_PEHEADER_RET_BROKEN_PE;
+ ret = CL_EFORMAT;
goto done;
}
@@ -5238,7 +5174,7 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ct
add_section_info(ctx, &peinfo->sections[i]);
if (cli_json_timeout_cycle_check(ctx, &toval) != CL_SUCCESS) {
- ret = CLI_PEHEADER_RET_JSON_TIMEOUT;
+ ret = CL_ETIMEOUT;
goto done;
}
}
@@ -5282,7 +5218,7 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ct
if (!salign || (section->urva % salign)) { /* Bad section alignment */
cli_dbgmsg("cli_peheader: Broken PE - section's VirtualAddress is misaligned\n");
if (opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS) {
- ret = CLI_PEHEADER_RET_BROKEN_PE;
+ ret = CL_EFORMAT;
goto done;
}
}
@@ -5291,7 +5227,7 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ct
// section? Why the exception for uraw?
if (section->urva >> 31 || section->uvsz >> 31 || (section->rsz && section->uraw >> 31) || peinfo->sections[i].ursz >> 31) {
cli_dbgmsg("cli_peheader: Found PE values with sign bit set\n");
- ret = CLI_PEHEADER_RET_BROKEN_PE;
+ ret = CL_EFORMAT;
goto done;
}
@@ -5299,7 +5235,7 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ct
if (section->urva != peinfo->hdr_size) { /* Bad first section RVA */
cli_dbgmsg("cli_peheader: First section doesn't start immediately after the header\n");
if (opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS) {
- ret = CLI_PEHEADER_RET_BROKEN_PE;
+ ret = CL_EFORMAT;
goto done;
}
}
@@ -5310,7 +5246,7 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ct
if (section->urva - peinfo->sections[i - 1].urva != peinfo->sections[i - 1].vsz) { /* No holes, no overlapping, no virtual disorder */
cli_dbgmsg("cli_peheader: Virtually misplaced section (wrong order, overlapping, non contiguous)\n");
if (opts & CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS) {
- ret = CLI_PEHEADER_RET_BROKEN_PE;
+ ret = CL_EFORMAT;
goto done;
}
}
@@ -5337,7 +5273,7 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ct
// TODO Should this offset include peinfo->offset?
if (!(peinfo->ep = cli_rawaddr(peinfo->vep, peinfo->sections, peinfo->nsections, &err, fsize, peinfo->hdr_size)) && err) {
cli_dbgmsg("cli_peheader: Broken PE file - Can't map EntryPoint to a file offset\n");
- ret = CLI_PEHEADER_RET_BROKEN_PE;
+ ret = CL_EFORMAT;
goto done;
}
@@ -5346,7 +5282,7 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ct
cli_jsonint(pe_json, "EntryPointOffset", peinfo->ep);
if (cli_json_timeout_cycle_check(ctx, &toval) != CL_SUCCESS) {
- ret = CLI_PEHEADER_RET_JSON_TIMEOUT;
+ ret = CL_ETIMEOUT;
goto done;
}
}
@@ -5549,7 +5485,7 @@ int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ct
// Do final preperations for peinfo to be passed back
peinfo->is_dll = is_dll;
- ret = CLI_PEHEADER_RET_SUCCESS;
+ ret = CL_SUCCESS;
done:
/* In the fail case, peinfo will get destroyed by the caller */
@@ -5615,7 +5551,7 @@ cl_error_t cli_check_auth_header(cli_ctx *ctx, struct cli_exe_info *peinfo)
peinfo = &_peinfo;
cli_exe_info_init(peinfo, 0);
- if (cli_peheader(ctx->fmap, peinfo, CLI_PEHEADER_OPT_NONE, NULL) != CLI_PEHEADER_RET_SUCCESS) {
+ if (CL_SUCCESS != cli_peheader(ctx->fmap, peinfo, CLI_PEHEADER_OPT_NONE, NULL)) {
cli_exe_info_destroy(peinfo);
return CL_EFORMAT;
}
@@ -5758,7 +5694,7 @@ cl_error_t cli_check_auth_header(cli_ctx *ctx, struct cli_exe_info *peinfo)
// At this point we should compute the SHA1 authenticode hash to see
// whether we've had any hashes added from external catalog files
static const struct supported_hashes {
- const enum CLI_HASH_TYPE hashtype;
+ const cli_hash_type_t hashtype;
const char *hashctx_name;
} supported_hashes[] = {
{CLI_HASH_SHA1, "sha1"},
@@ -5766,8 +5702,8 @@ cl_error_t cli_check_auth_header(cli_ctx *ctx, struct cli_exe_info *peinfo)
};
for (i = 0; i < (sizeof(supported_hashes) / sizeof(supported_hashes[0])); i++) {
- const enum CLI_HASH_TYPE hashtype = supported_hashes[i].hashtype;
- const char *hashctx_name = supported_hashes[i].hashctx_name;
+ const cli_hash_type_t hashtype = supported_hashes[i].hashtype;
+ const char *hashctx_name = supported_hashes[i].hashctx_name;
if (!cli_hm_have_size(ctx->engine->hm_fp, hashtype, 2)) {
continue;
@@ -5869,7 +5805,7 @@ cl_error_t cli_genhash_pe(cli_ctx *ctx, unsigned int class, int type, stats_sect
// if so, use that to avoid having to re-parse the header
cli_exe_info_init(peinfo, 0);
- if (cli_peheader(ctx->fmap, peinfo, CLI_PEHEADER_OPT_NONE, NULL) != CLI_PEHEADER_RET_SUCCESS) {
+ if (cli_peheader(ctx->fmap, peinfo, CLI_PEHEADER_OPT_NONE, NULL) != CL_SUCCESS) {
cli_exe_info_destroy(peinfo);
return CL_EFORMAT;
}
diff --git a/libclamav/pe.h b/libclamav/pe.h
index 1b61e25479..b2f491a806 100644
--- a/libclamav/pe.h
+++ b/libclamav/pe.h
@@ -87,13 +87,8 @@ enum {
#define CLI_PEHEADER_OPT_STRICT_ON_PE_ERRORS 0x8
#define CLI_PEHEADER_OPT_REMOVE_MISSING_SECTIONS 0x10
-#define CLI_PEHEADER_RET_SUCCESS 0
-#define CLI_PEHEADER_RET_GENERIC_ERROR -1
-#define CLI_PEHEADER_RET_BROKEN_PE -2
-#define CLI_PEHEADER_RET_JSON_TIMEOUT -3
-
-int cli_pe_targetinfo(cli_ctx *ctx, struct cli_exe_info *peinfo);
-int cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ctx *ctx);
+cl_error_t cli_pe_targetinfo(cli_ctx *ctx, struct cli_exe_info *peinfo);
+cl_error_t cli_peheader(fmap_t *map, struct cli_exe_info *peinfo, uint32_t opts, cli_ctx *ctx);
cl_error_t cli_check_auth_header(cli_ctx *ctx, struct cli_exe_info *peinfo);
cl_error_t cli_genhash_pe(cli_ctx *ctx, unsigned int class, int type, stats_section_t *hashes);
diff --git a/libclamav/phishcheck.c b/libclamav/phishcheck.c
index 6d725a3bf1..c51ba33b6a 100644
--- a/libclamav/phishcheck.c
+++ b/libclamav/phishcheck.c
@@ -798,7 +798,7 @@ cl_error_t phishingScan(cli_ctx* ctx, tag_arguments_t* hrefs)
status = cli_append_potentially_unwanted(ctx, "Heuristics.Phishing.Email.SpoofedDomain");
break;
}
- if (CL_CLEAN != status && !SCAN_ALLMATCHES) {
+ if (CL_SUCCESS != status) {
goto done;
}
}
diff --git a/libclamav/png.c b/libclamav/png.c
index 4232ce20e5..2bd6a20762 100644
--- a/libclamav/png.c
+++ b/libclamav/png.c
@@ -77,7 +77,7 @@ typedef struct __attribute__((packed)) {
cl_error_t cli_parsepng(cli_ctx *ctx)
{
- cl_error_t status = CL_ERROR;
+ cl_error_t status = CL_SUCCESS;
uint64_t chunk_data_length = 0;
char chunk_type[5] = {'\0', '\0', '\0', '\0', '\0'};
@@ -114,8 +114,7 @@ cl_error_t cli_parsepng(cli_ctx *ctx)
if (chunk_data_length > (uint64_t)0x7fffffff) {
cli_dbgmsg("PNG: invalid chunk length (too large): 0x" STDx64 "\n", chunk_data_length);
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.PNG.InvalidChunkLength");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.PNG.InvalidChunkLength");
}
goto scan_overlay;
}
@@ -123,8 +122,7 @@ cl_error_t cli_parsepng(cli_ctx *ctx)
if (fmap_readn(map, chunk_type, offset, PNG_CHUNK_TYPE_SIZE) != PNG_CHUNK_TYPE_SIZE) {
cli_dbgmsg("PNG: EOF while reading chunk type\n");
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.PNG.EOFReadingChunkType");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.PNG.EOFReadingChunkType");
}
goto scan_overlay;
}
@@ -141,8 +139,7 @@ cl_error_t cli_parsepng(cli_ctx *ctx)
if (NULL == ptr) {
cli_warnmsg("PNG: Unexpected early end-of-file.\n");
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.PNG.EOFReadingChunk");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.PNG.EOFReadingChunk");
}
goto scan_overlay;
}
@@ -263,8 +260,8 @@ cl_error_t cli_parsepng(cli_ctx *ctx)
if (color_type == 3) {
if ((chunk_data_length > 256 || chunk_data_length > num_palette_entries) && !have_PLTE) {
- status = cli_append_virus(ctx, "Heuristics.PNG.CVE-2004-0597");
- goto done;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.PNG.CVE-2004-0597");
+ goto done; // always, even if allmatch mode means status comes back 'success' instead of 'virus'.
}
}
}
@@ -272,8 +269,7 @@ cl_error_t cli_parsepng(cli_ctx *ctx)
if (fmap_readn(map, &chunk_crc, offset, PNG_CHUNK_CRC_SIZE) != PNG_CHUNK_CRC_SIZE) {
cli_dbgmsg("PNG: EOF while reading chunk crc\n");
if (SCAN_HEURISTIC_BROKEN_MEDIA) {
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.PNG.EOFReadingChunkCRC");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.PNG.EOFReadingChunkCRC");
}
goto scan_overlay;
}
@@ -294,20 +290,16 @@ cl_error_t cli_parsepng(cli_ctx *ctx)
}
scan_overlay:
- if (status == CL_EPARSE) {
- /* We added with cli_append_potentially_unwanted so it will alert at the end if nothing else matches. */
- status = CL_CLEAN;
- }
- /* Check if there's an overlay, and scan it if one exists. */
- if (map->len > offset) {
- cli_dbgmsg("PNG: Found " STDu64 " additional data after end of PNG! Scanning as a nested file.\n", map->len - offset);
- status = cli_magic_scan_nested_fmap_type(map, (size_t)offset, map->len - offset, ctx, CL_TYPE_ANY, NULL);
- goto done;
+ if (CL_SUCCESS == status) {
+ /* Check if there's an overlay, and scan it if one exists. */
+ if (map->len > offset) {
+ cli_dbgmsg("PNG: Found " STDu64 " additional data after end of PNG! Scanning as a nested file.\n", map->len - offset);
+ status = cli_magic_scan_nested_fmap_type(map, (size_t)offset, map->len - offset, ctx, CL_TYPE_ANY, NULL);
+ goto done;
+ }
}
- status = CL_CLEAN;
-
done:
return status;
diff --git a/libclamav/readdb.c b/libclamav/readdb.c
index ad8b91da2a..a76f8832b6 100644
--- a/libclamav/readdb.c
+++ b/libclamav/readdb.c
@@ -127,7 +127,7 @@ char *cli_virname(const char *virname, unsigned int official)
cl_error_t cli_sigopts_handler(struct cli_matcher *root, const char *virname, const char *hexsig,
uint8_t sigopts, uint16_t rtype, uint16_t type,
- const char *offset, uint8_t target, const uint32_t *lsigid, unsigned int options)
+ const char *offset, const uint32_t *lsigid, unsigned int options)
{
char *hexcpy, *start, *end, *mid;
unsigned int i;
@@ -191,7 +191,7 @@ cl_error_t cli_sigopts_handler(struct cli_matcher *root, const char *virname, co
return CL_EMALFDB;
}
- ret = cli_add_content_match_pattern(root, virname, hexcpy, sigopts, rtype, type, offset, target, lsigid, options);
+ ret = cli_add_content_match_pattern(root, virname, hexcpy, sigopts, rtype, type, offset, lsigid, options);
free(hexcpy);
return ret;
}
@@ -203,7 +203,7 @@ cl_error_t cli_sigopts_handler(struct cli_matcher *root, const char *virname, co
if (start != end && mid && (*(++mid) == '#' || !strncmp(mid, ">>", 2) || !strncmp(mid, "<<", 2) || !strncmp(mid, "0#", 2))) {
/* TODO byte compare currently does not have support for sigopts, pass through */
- ret = cli_add_content_match_pattern(root, virname, hexcpy, sigopts, rtype, type, offset, target, lsigid, options);
+ ret = cli_add_content_match_pattern(root, virname, hexcpy, sigopts, rtype, type, offset, lsigid, options);
free(hexcpy);
return ret;
}
@@ -300,7 +300,7 @@ cl_error_t cli_sigopts_handler(struct cli_matcher *root, const char *virname, co
}
/* NOCASE sigopt is handled in cli_ac_addsig */
- ret = cli_add_content_match_pattern(root, virname, hexovr, sigopts, rtype, type, offset, target, lsigid, options);
+ ret = cli_add_content_match_pattern(root, virname, hexovr, sigopts, rtype, type, offset, lsigid, options);
free(hexovr);
if (ret != CL_SUCCESS || !(sigopts & ACPATT_OPTION_ASCII)) {
free(hexcpy);
@@ -312,7 +312,7 @@ cl_error_t cli_sigopts_handler(struct cli_matcher *root, const char *virname, co
}
/* ASCII sigopt; NOCASE sigopt is handled in cli_ac_addsig */
- ret = cli_add_content_match_pattern(root, virname, hexcpy, sigopts, rtype, type, offset, target, lsigid, options);
+ ret = cli_add_content_match_pattern(root, virname, hexcpy, sigopts, rtype, type, offset, lsigid, options);
free(hexcpy);
return ret;
}
@@ -412,7 +412,7 @@ static cl_error_t readdb_load_regex_subsignature(struct cli_matcher *root, const
}
cl_error_t readdb_parse_ldb_subsignature(struct cli_matcher *root, const char *virname, char *hexsig,
- const char *offset, uint8_t target, const uint32_t *lsigid, unsigned int options,
+ const char *offset, const uint32_t *lsigid, unsigned int options,
int current_subsig_index, int num_subsigs, struct cli_lsig_tdb *tdb)
{
cl_error_t status = CL_EPARSE;
@@ -479,7 +479,7 @@ cl_error_t readdb_parse_ldb_subsignature(struct cli_matcher *root, const char *v
goto done;
}
- if ((ret = cli_ac_addpatt(root, patt))) {
+ if (CL_SUCCESS != (ret = cli_ac_addpatt(root, patt))) {
MPOOL_FREE(root->mempool, patt->pattern);
free(patt);
status = ret;
@@ -601,9 +601,9 @@ cl_error_t readdb_parse_ldb_subsignature(struct cli_matcher *root, const char *v
sig = (subtokens_count % 2) ? subtokens[0] : subtokens[1];
if (subsig_opts) {
- ret = cli_sigopts_handler(root, virname, sig, subsig_opts, 0, 0, offset, target, lsigid, options);
+ ret = cli_sigopts_handler(root, virname, sig, subsig_opts, 0, 0, offset, lsigid, options);
} else {
- ret = cli_add_content_match_pattern(root, virname, sig, 0, 0, 0, offset, target, lsigid, options);
+ ret = cli_add_content_match_pattern(root, virname, sig, 0, 0, 0, offset, lsigid, options);
}
if (CL_SUCCESS != ret) {
@@ -633,13 +633,12 @@ cl_error_t readdb_parse_ldb_subsignature(struct cli_matcher *root, const char *v
* @param hexsig The string containing the regex
* @param subsig_opts Content match pattern options. See ACPATT_* macros in matcher-ac.h.
* @param offset The string offset where the pattern starts
- * @param target The clamav target type.
* @param lsigid An array of 2 uint32_t numbers: lsig_id and subsig_id. May be NULL for testing.
* @param options Database options. See CL_DB_* macros in clamav.h.
* @return cl_error_t
*/
static cl_error_t readdb_parse_yara_string(struct cli_matcher *root, const char *virname, char *hexsig, uint8_t subsig_opts,
- const char *offset, uint8_t target, const uint32_t *lsigid, unsigned int options)
+ const char *offset, const uint32_t *lsigid, unsigned int options)
{
cl_error_t status = CL_EPARSE;
cl_error_t ret;
@@ -655,9 +654,9 @@ static cl_error_t readdb_parse_yara_string(struct cli_matcher *root, const char
* Looks like an AC/BM content match subsignature.
*/
if (subsig_opts) {
- ret = cli_sigopts_handler(root, virname, hexsig, subsig_opts, 0, 0, offset, target, lsigid, options);
+ ret = cli_sigopts_handler(root, virname, hexsig, subsig_opts, 0, 0, offset, lsigid, options);
} else {
- ret = cli_add_content_match_pattern(root, virname, hexsig, 0, 0, 0, offset, target, lsigid, options);
+ ret = cli_add_content_match_pattern(root, virname, hexsig, 0, 0, 0, offset, lsigid, options);
}
}
@@ -686,14 +685,13 @@ static cl_error_t readdb_parse_yara_string(struct cli_matcher *root, const char
* @param rtype
* @param type
* @param offset The string offset where the pattern starts
- * @param target The clamav target type.
* @param lsigid An array of 2 uint32_t numbers: lsig_id and subsig_id. May be NULL for testing.
* @param options Database options. See CL_DB_* macros in clamav.h.
* @return cl_error_t
*/
cl_error_t cli_add_content_match_pattern(struct cli_matcher *root, const char *virname, const char *hexsig,
uint8_t sigopts, uint16_t rtype, uint16_t type,
- const char *offset, uint8_t target, const uint32_t *lsigid, unsigned int options)
+ const char *offset, const uint32_t *lsigid, unsigned int options)
{
struct cli_bm_patt *bm_new;
char *pt, *hexcpy, *n, l, r;
@@ -733,7 +731,7 @@ cl_error_t cli_add_content_match_pattern(struct cli_matcher *root, const char *v
}
strcat(hexcpy, ++wild);
- ret = cli_add_content_match_pattern(root, virname, hexcpy, sigopts, rtype, type, offset, target, lsigid, options);
+ ret = cli_add_content_match_pattern(root, virname, hexcpy, sigopts, rtype, type, offset, lsigid, options);
free(hexcpy);
return ret;
@@ -806,7 +804,7 @@ cl_error_t cli_add_content_match_pattern(struct cli_matcher *root, const char *v
*pt++ = 0;
}
- if ((ret = cli_ac_addsig(root, virname, start, sigopts, root->ac_partsigs, parts, i, rtype, type, mindist, maxdist, offset, lsigid, options))) {
+ if (CL_SUCCESS != (ret = cli_ac_addsig(root, virname, start, sigopts, root->ac_partsigs, parts, i, rtype, type, mindist, maxdist, offset, lsigid, options))) {
cli_errmsg("cli_add_content_match_pattern: Problem adding signature (1).\n");
error = 1;
break;
@@ -907,7 +905,7 @@ cl_error_t cli_add_content_match_pattern(struct cli_matcher *root, const char *v
return CL_EMALFDB;
}
- if ((ret = cli_ac_addsig(root, virname, pt, sigopts, root->ac_partsigs, parts, i, rtype, type, 0, 0, offset, lsigid, options))) {
+ if (CL_SUCCESS != (ret = cli_ac_addsig(root, virname, pt, sigopts, root->ac_partsigs, parts, i, rtype, type, 0, 0, offset, lsigid, options))) {
cli_errmsg("cli_add_content_match_pattern: Problem adding signature (2).\n");
free(pt);
return ret;
@@ -916,7 +914,9 @@ cl_error_t cli_add_content_match_pattern(struct cli_matcher *root, const char *v
free(pt);
}
- } else if (root->ac_only || type || lsigid || sigopts || strpbrk(hexsig, "?([") || (root->bm_offmode && (!strcmp(offset, "*") || strchr(offset, ','))) || strstr(offset, "VI") || strchr(offset, '$')) {
+ } else if (root->ac_only || type || lsigid || sigopts || strpbrk(hexsig, "?([") ||
+ // Relative offset features that are not fully supported by the Boyer-Moore matcher.
+ (root->bm_offmode && (!strcmp(offset, "*") || strchr(offset, ','))) || strstr(offset, "VI") || strstr(offset, "S") || strchr(offset, '$')) {
/*
* format seems like it must be handled with the Aho-Corasick (AC) pattern matcher.
*/
@@ -1261,7 +1261,7 @@ static cl_error_t cli_loaddb(FILE *fs, struct cl_engine *engine, unsigned int *s
if (*pt == '=') continue;
- if (CL_SUCCESS != (ret = cli_add_content_match_pattern(root, start, pt, 0, 0, 0, "*", 0, NULL, options))) {
+ if (CL_SUCCESS != (ret = cli_add_content_match_pattern(root, start, pt, 0, 0, 0, "*", NULL, options))) {
cli_dbgmsg("cli_loaddb: cli_add_content_match_pattern failed on line %d\n", line);
ret = CL_EMALFDB;
break;
@@ -1570,7 +1570,7 @@ static int cli_loadndb(FILE *fs, struct cl_engine *engine, unsigned int *signo,
const char *sig, *virname, *offset, *pt;
struct cli_matcher *root;
int line = 0, sigs = 0, ret = 0, tokens_count;
- unsigned short target;
+ cli_target_t target;
unsigned int phish = options & CL_DB_PHISHING;
UNUSEDPARAM(dbname);
@@ -1647,19 +1647,19 @@ static int cli_loadndb(FILE *fs, struct cl_engine *engine, unsigned int *signo,
ret = CL_EMALFDB;
break;
}
- target = (unsigned short)atoi(pt);
+ target = (cli_target_t)atoi(pt);
- if (target >= CLI_MTARGETS) {
- cli_dbgmsg("Not supported target type in signature for %s\n", virname);
+ if (target >= CLI_MTARGETS || target < 0) {
+ cli_dbgmsg("Not supported target type (%d) in signature for %s\n", (int)target, virname);
continue;
}
- root = engine->root[target];
+ root = engine->root[(size_t)target];
offset = tokens[2];
sig = tokens[3];
- if (CL_SUCCESS != (ret = cli_add_content_match_pattern(root, virname, sig, 0, 0, 0, offset, target, NULL, options))) {
+ if (CL_SUCCESS != (ret = cli_add_content_match_pattern(root, virname, sig, 0, 0, 0, offset, NULL, options))) {
ret = CL_EMALFDB;
break;
}
@@ -2004,13 +2004,13 @@ static inline int init_tdb(struct cli_lsig_tdb *tdb, struct cl_engine *engine, c
return CL_BREAK;
}
- if ((tdb->icongrp1 || tdb->icongrp2) && tdb->target[0] != 1) {
+ if ((tdb->icongrp1 || tdb->icongrp2) && tdb->target[0] != TARGET_PE) {
FREE_TDB_P(tdb);
cli_errmsg("init_tdb: IconGroup is only supported in PE (target 1) signatures\n");
return CL_EMALFDB;
}
- if ((tdb->ep || tdb->nos) && tdb->target[0] != 1 && tdb->target[0] != 6 && tdb->target[0] != 9) {
+ if ((tdb->ep || tdb->nos) && tdb->target[0] != TARGET_PE && tdb->target[0] != TARGET_ELF && tdb->target[0] != TARGET_MACHO) {
FREE_TDB_P(tdb);
cli_errmsg("init_tdb: EntryPoint/NumberOfSections is only supported in PE/ELF/Mach-O signatures\n");
return CL_EMALFDB;
@@ -2033,7 +2033,6 @@ static cl_error_t load_oneldb(char *buffer, int chkpua, struct cl_engine *engine
struct cli_ac_lsig *lsig = NULL;
char *tokens[LDB_TOKENS + 1];
int i, subsigs, tokens_count;
- unsigned short target = 0;
struct cli_matcher *root;
struct cli_lsig_tdb tdb;
uint32_t lsigid[2];
@@ -2172,7 +2171,7 @@ static cl_error_t load_oneldb(char *buffer, int chkpua, struct cl_engine *engine
lsigid[1] = i;
// handle each LDB subsig
- ret = readdb_parse_ldb_subsignature(root, virname, tokens[3 + i], "*", target, lsigid, options, i, subsigs, &tdb);
+ ret = readdb_parse_ldb_subsignature(root, virname, tokens[3 + i], "*", lsigid, options, i, subsigs, &tdb);
if (CL_SUCCESS != ret) {
cli_errmsg("cli_loadldb: failed to parse subsignature %d in %s\n", i, virname);
status = ret;
@@ -2482,7 +2481,7 @@ static int cli_loadftm(FILE *fs, struct cl_engine *engine, unsigned int options,
magictype = atoi(tokens[0]);
if (magictype == 1) { /* A-C */
- if (CL_SUCCESS != (ret = cli_add_content_match_pattern(engine->root[0], tokens[3], tokens[2], 0, rtype, type, tokens[1], 0, NULL, options)))
+ if (CL_SUCCESS != (ret = cli_add_content_match_pattern(engine->root[0], tokens[3], tokens[2], 0, rtype, type, tokens[1], NULL, options)))
break;
} else if ((magictype == 0) || (magictype == 4)) { /* memcmp() */
@@ -3788,7 +3787,7 @@ static int yara_hexstr_verify(YR_STRING *string, const char *hexstr, uint32_t *l
}
/* Long Check: Attempt to load hexstr */
- if (CL_SUCCESS != (ret = cli_sigopts_handler(engine->test_root, "test-hex", hexstr, 0, 0, 0, "*", 0, lsigid, options))) {
+ if (CL_SUCCESS != (ret = cli_sigopts_handler(engine->test_root, "test-hex", hexstr, 0, 0, 0, "*", lsigid, options))) {
if (ret == CL_EMALFDB) {
cli_warnmsg("load_oneyara[verify]: recovered from database loading error\n");
/* TODO: if necessary, reset testing matcher if error occurs */
@@ -3817,7 +3816,6 @@ static int load_oneyara(YR_RULE *rule, int chkpua, struct cl_engine *engine, uns
uint32_t lsigid[2];
struct cli_matcher *root;
struct cli_ac_lsig **newtable, *lsig, *tsig = NULL;
- unsigned short target = 0;
char *logic = NULL, *target_str = NULL;
char *newident = NULL;
/* size_t lsize; */ // only used in commented out code
@@ -4298,7 +4296,7 @@ static int load_oneyara(YR_RULE *rule, int chkpua, struct cl_engine *engine, uns
(ytable.table[i]->sigopts & ACPATT_OPTION_ASCII) ? "a" : "");
ret = readdb_parse_yara_string(root, newident, ytable.table[i]->hexstr, ytable.table[i]->sigopts,
- ytable.table[i]->offset, target, lsigid, options);
+ ytable.table[i]->offset, lsigid, options);
if (CL_SUCCESS != ret) {
root->ac_lsigs--;
FREE_TDB(tdb);
@@ -6048,7 +6046,7 @@ cl_error_t cl_engine_compile(struct cl_engine *engine)
MPOOL_FLUSH(engine->mempool);
/* Compile bytecode */
- if ((ret = cli_bytecode_prepare2(engine, &engine->bcs, engine->dconf->bytecode))) {
+ if (CL_SUCCESS != (ret = cli_bytecode_prepare2(engine, &engine->bcs, engine->dconf->bytecode))) {
cli_errmsg("Unable to compile/load bytecode: %s\n", cl_strerror(ret));
return ret;
}
diff --git a/libclamav/readdb.h b/libclamav/readdb.h
index f6f33efe25..d0b76877b6 100644
--- a/libclamav/readdb.h
+++ b/libclamav/readdb.h
@@ -140,14 +140,13 @@ char *cli_virname(const char *virname, unsigned int official);
* @param rtype
* @param type
* @param offset
- * @param target
* @param lsigid
* @param options
* @return cl_error_t
*/
cl_error_t cli_sigopts_handler(struct cli_matcher *root, const char *virname, const char *hexsig,
uint8_t sigopts, uint16_t rtype, uint16_t type,
- const char *offset, uint8_t target, const uint32_t *lsigid, unsigned int options);
+ const char *offset, const uint32_t *lsigid, unsigned int options);
/**
* @brief Parse body-based patterns that DO NOT have subsignature modifiers.
@@ -162,14 +161,13 @@ cl_error_t cli_sigopts_handler(struct cli_matcher *root, const char *virname, co
* @param rtype
* @param type
* @param offset
- * @param target
* @param lsigid
* @param options
* @return cl_error_t
*/
cl_error_t cli_add_content_match_pattern(struct cli_matcher *root, const char *virname, const char *hexsig,
uint8_t sigopts, uint16_t rtype, uint16_t type,
- const char *offset, uint8_t target, const uint32_t *lsigid, unsigned int options);
+ const char *offset, const uint32_t *lsigid, unsigned int options);
/**
* @brief Parse a subsignature from a logical signature.
@@ -188,7 +186,6 @@ cl_error_t cli_add_content_match_pattern(struct cli_matcher *root, const char *v
* @param virname
* @param hexsig
* @param offset
- * @param target
* @param lsigid An array of 2 uint32_t numbers: lsig_id and subsig_id. May be NULL for testing.
* @param options
* @param current_subsig_index
@@ -197,7 +194,7 @@ cl_error_t cli_add_content_match_pattern(struct cli_matcher *root, const char *v
* @return cl_error_t
*/
cl_error_t readdb_parse_ldb_subsignature(struct cli_matcher *root, const char *virname, char *hexsig,
- const char *offset, uint8_t target, const uint32_t *lsigid, unsigned int options,
+ const char *offset, const uint32_t *lsigid, unsigned int options,
int current_subsig_index, int num_subsigs, struct cli_lsig_tdb *tdb);
cl_error_t cli_load(const char *filename, struct cl_engine *engine, unsigned int *signo, unsigned int options, struct cli_dbio *dbio);
diff --git a/libclamav/regex_list.c b/libclamav/regex_list.c
index 6fae820b76..df689134e8 100644
--- a/libclamav/regex_list.c
+++ b/libclamav/regex_list.c
@@ -757,7 +757,6 @@ static cl_error_t add_pattern_suffix(void *cbdata, const char *suffix, size_t su
struct regex_matcher *matcher = cbdata;
struct regex_list *regex = NULL;
const struct cli_element *el = NULL;
- void *tmp_matcher = NULL; /* save original address if OOM occurs */
cl_error_t ret = CL_SUCCESS;
if (NULL == matcher) {
@@ -802,9 +801,8 @@ static cl_error_t add_pattern_suffix(void *cbdata, const char *suffix, size_t su
list_add_tail(&matcher->suffix_regexes[(size_t)el->data], regex);
} else {
/* new suffix */
- size_t n = matcher->suffix_cnt;
- el = cli_hashtab_insert(&matcher->suffix_hash, suffix, suffix_len, (cli_element_data)n);
- tmp_matcher = matcher->suffix_regexes; /* save the current value before cli_realloc() */
+ size_t n = matcher->suffix_cnt;
+ el = cli_hashtab_insert(&matcher->suffix_hash, suffix, suffix_len, (cli_element_data)n);
CLI_REALLOC(matcher->suffix_regexes,
(n + 1) * sizeof(*matcher->suffix_regexes),
cli_errmsg("add_pattern_suffix: Unable to reallocate memory for matcher->suffix_regexes\n");
diff --git a/libclamav/regex_pcre.h b/libclamav/regex_pcre.h
index d1f4127984..adb51e80f3 100644
--- a/libclamav/regex_pcre.h
+++ b/libclamav/regex_pcre.h
@@ -54,7 +54,7 @@ struct cli_pcre_data {
};
struct cli_pcre_results {
- int err;
+ cl_error_t err;
uint32_t match[2]; /* populated by cli_pcre_match to be start (0) and end (1) offset of match */
pcre2_match_data *match_data;
@@ -69,7 +69,7 @@ struct cli_pcre_data {
};
struct cli_pcre_results {
- int err;
+ cl_error_t err;
uint32_t match[2]; /* populated by cli_pcre_match to be start (0) and end (1) offset of match */
int ovector[OVECCOUNT];
@@ -79,6 +79,18 @@ struct cli_pcre_results {
cl_error_t cli_pcre_init_internal();
cl_error_t cli_pcre_addoptions(struct cli_pcre_data *pd, const char **opt, int errout);
cl_error_t cli_pcre_compile(struct cli_pcre_data *pd, long long unsigned match_limit, long long unsigned match_limit_recursion, unsigned int options, int opt_override);
+
+/**
+ * @brief perform a pcre match on a string
+ *
+ * @param pd
+ * @param buffer
+ * @param buflen
+ * @param override_offset
+ * @param options
+ * @param results
+ * @return int greater than zero if a match. 0 if no match. A PCRE2_ERROR_* error code if something went wrong.
+ */
int cli_pcre_match(struct cli_pcre_data *pd, const unsigned char *buffer, size_t buflen, size_t override_offset, int options, struct cli_pcre_results *results);
void cli_pcre_report(const struct cli_pcre_data *pd, const unsigned char *buffer, size_t buflen, int rc, struct cli_pcre_results *results);
diff --git a/libclamav/regex_suffix.c b/libclamav/regex_suffix.c
index 2eeb3cc919..b65d501e22 100644
--- a/libclamav/regex_suffix.c
+++ b/libclamav/regex_suffix.c
@@ -329,6 +329,7 @@ static struct node *parse_regex(const uint8_t *p, size_t *last)
/* next char is escaped, advance pointer
* and let fall-through handle it */
++*last;
+ /* fall-through */
default:
right = make_leaf(p[*last]);
v = make_node(concat, v, right);
@@ -479,7 +480,7 @@ cl_error_t cli_regex2suffix(const char *pattern, regex_t *preg, suffix_callback
cli_errmsg("cli_regex2suffix: unable to strdup regex.pattern");
rc = REG_ESPACE);
- n = parse_regex(pattern, &last);
+ n = parse_regex((const uint8_t *)pattern, &last);
if (!n) {
rc = REG_ESPACE;
goto done;
diff --git a/libclamav/rtf.c b/libclamav/rtf.c
index 0c74b5439d..f70cbcf0fb 100644
--- a/libclamav/rtf.c
+++ b/libclamav/rtf.c
@@ -237,19 +237,25 @@ static int rtf_object_begin(struct rtf_state* state, cli_ctx* ctx, const char* t
return 0;
}
-static int decode_and_scan(struct rtf_object_data* data, cli_ctx* ctx)
+static cl_error_t decode_and_scan(struct rtf_object_data* data, cli_ctx* ctx)
{
- int ret = CL_CLEAN;
+ cl_error_t ret = CL_CLEAN;
+
+ cli_dbgmsg("RTF:Scanning embedded object: %s\n", data->name);
+
+ if (data->fd > 0) {
+ if (data->bread == 1) {
+ cli_dbgmsg("Decoding ole object\n");
+
+ ret = cli_scan_ole10(data->fd, ctx);
+ } else {
+ ret = cli_magic_scan_desc(data->fd, data->name, ctx, NULL);
+ }
- cli_dbgmsg("RTF:Scanning embedded object:%s\n", data->name);
- if (data->bread == 1 && data->fd > 0) {
- cli_dbgmsg("Decoding ole object\n");
- ret = cli_scan_ole10(data->fd, ctx);
- } else if (data->fd > 0)
- ret = cli_magic_scan_desc(data->fd, data->name, ctx, NULL);
- if (data->fd > 0)
close(data->fd);
- data->fd = -1;
+ data->fd = -1;
+ }
+
if (data->name) {
if (!ctx->engine->keeptmp)
if (cli_unlink(data->name)) ret = CL_EUNLINK;
@@ -257,9 +263,7 @@ static int decode_and_scan(struct rtf_object_data* data, cli_ctx* ctx)
data->name = NULL;
}
- if (ret != CL_CLEAN)
- return ret;
- return 0;
+ return ret;
}
static int rtf_object_process(struct rtf_state* state, const unsigned char* input, const size_t len)
diff --git a/libclamav/scanners.c b/libclamav/scanners.c
index eeebd5ba97..527796e771 100644
--- a/libclamav/scanners.c
+++ b/libclamav/scanners.c
@@ -134,7 +134,6 @@ cl_error_t cli_magic_scan_dir(const char *dir, cli_ctx *ctx)
struct dirent *dent;
STATBUF statbuf;
char *fname = NULL;
- unsigned int viruses_found = 0;
bool processing_normalized_files = ctx->next_layer_is_normalized;
if ((dd = opendir(dir)) != NULL) {
@@ -154,25 +153,16 @@ cl_error_t cli_magic_scan_dir(const char *dir, cli_ctx *ctx)
/* stat the file */
if (LSTAT(fname, &statbuf) != -1) {
if (S_ISDIR(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)) {
- if (cli_magic_scan_dir(fname, ctx) == CL_VIRUS) {
- if (SCAN_ALLMATCHES) {
- viruses_found++;
- continue;
- }
-
- status = CL_VIRUS;
+ status = cli_magic_scan_dir(fname, ctx);
+ if (CL_SUCCESS != status) {
goto done;
}
} else {
if (S_ISREG(statbuf.st_mode)) {
ctx->next_layer_is_normalized = processing_normalized_files; // This flag ingested by cli_recursion_stack_push().
- if (cli_magic_scan_file(fname, ctx, dent->d_name) == CL_VIRUS) {
- if (SCAN_ALLMATCHES) {
- viruses_found++;
- continue;
- }
- status = CL_VIRUS;
+ status = cli_magic_scan_file(fname, ctx, dent->d_name);
+ if (CL_SUCCESS != status) {
goto done;
}
}
@@ -198,9 +188,6 @@ cl_error_t cli_magic_scan_dir(const char *dir, cli_ctx *ctx)
free(fname);
}
- if (SCAN_ALLMATCHES && viruses_found)
- status = CL_VIRUS;
-
return status;
}
@@ -236,8 +223,7 @@ static cl_error_t cli_scanrar_file(const char *filepath, int desc, cli_ctx *ctx)
cl_error_t status = CL_EPARSE;
cl_unrar_error_t unrar_ret = UNRAR_ERR;
- unsigned int file_count = 0;
- unsigned int viruses_found = 0;
+ unsigned int file_count = 0;
uint32_t nEncryptedFilesFound = 0;
uint32_t nTooLargeFilesFound = 0;
@@ -270,7 +256,8 @@ static cl_error_t cli_scanrar_file(const char *filepath, int desc, cli_ctx *ctx)
if (UNRAR_OK != (unrar_ret = cli_unrar_open(filepath, &hArchive, &comment, &comment_size, cli_debug_flag))) {
if (unrar_ret == UNRAR_ENCRYPTED) {
cli_dbgmsg("RAR: Encrypted main header\n");
- status = CL_EUNPACK;
+ status = CL_SUCCESS;
+ nEncryptedFilesFound += 1;
goto done;
}
if (unrar_ret == UNRAR_EMEM) {
@@ -309,12 +296,7 @@ static cl_error_t cli_scanrar_file(const char *filepath, int desc, cli_ctx *ctx)
/* Scan the comment */
status = cli_magic_scan_buff(comment, comment_size, ctx, NULL);
-
- if ((status == CL_VIRUS) && SCAN_ALLMATCHES) {
- status = CL_CLEAN;
- viruses_found++;
- }
- if ((status == CL_VIRUS) || (status == CL_BREAK)) {
+ if (status != CL_SUCCESS) {
goto done;
}
}
@@ -366,11 +348,9 @@ static cl_error_t cli_scanrar_file(const char *filepath, int desc, cli_ctx *ctx)
* Scan the metadata for the file in question since the content was clean, or we're running in all-match.
*/
status = cli_unrar_scanmetadata(&metadata, ctx, file_count);
- if ((status == CL_VIRUS) && SCAN_ALLMATCHES) {
- status = CL_CLEAN;
- viruses_found++;
- }
- if ((status == CL_VIRUS) || (status == CL_BREAK)) {
+ if (status == CL_EUNPACK) {
+ nEncryptedFilesFound += 1;
+ } else if (status != CL_SUCCESS) {
break;
}
@@ -467,17 +447,20 @@ static cl_error_t cli_scanrar_file(const char *filepath, int desc, cli_ctx *ctx)
status = cli_magic_scan_file(extract_fullpath, ctx, filename_base);
if (status == CL_EOPEN) {
cli_dbgmsg("RAR: File not found, Extraction failed!\n");
- status = CL_CLEAN;
+
+ // Don't abort the scan just because one file failed to extract.
+ status = CL_SUCCESS;
} else {
/* Delete the tempfile if not --leave-temps */
- if (!ctx->engine->keeptmp)
- if (cli_unlink(extract_fullpath))
+ if (!ctx->engine->keeptmp) {
+ if (cli_unlink(extract_fullpath)) {
cli_dbgmsg("RAR: Failed to unlink the extracted file: %s\n", extract_fullpath);
+ }
+ }
- if (status == CL_VIRUS) {
- cli_dbgmsg("RAR: infected with %s\n", cli_get_last_virus(ctx));
- status = CL_VIRUS;
- viruses_found++;
+ if (status != CL_SUCCESS) {
+ // Bail out if "virus" and also if exceeded scan maximums, etc.
+ goto done;
}
}
}
@@ -490,18 +473,6 @@ static cl_error_t cli_scanrar_file(const char *filepath, int desc, cli_ctx *ctx)
}
}
- if (status == CL_VIRUS) {
- if (SCAN_ALLMATCHES)
- status = CL_SUCCESS;
- else
- break;
- }
-
- if (ctx->engine->maxscansize && ctx->scansize >= ctx->engine->maxscansize) {
- status = CL_CLEAN;
- break;
- }
-
/*
* Free up any malloced metadata...
*/
@@ -514,10 +485,11 @@ static cl_error_t cli_scanrar_file(const char *filepath, int desc, cli_ctx *ctx)
filename_base = NULL;
}
- } while (status == CL_CLEAN);
+ } while (status == CL_SUCCESS);
- if (status == CL_BREAK)
- status = CL_CLEAN;
+ if (status == CL_BREAK) {
+ status = CL_SUCCESS;
+ }
done:
if (NULL != comment) {
@@ -553,23 +525,17 @@ static cl_error_t cli_scanrar_file(const char *filepath, int desc, cli_ctx *ctx)
extract_fullpath = NULL;
}
- if ((CL_VIRUS != status) && ((CL_EUNPACK == status) || (nEncryptedFilesFound > 0))) {
+ if ((CL_VIRUS != status) && (nEncryptedFilesFound > 0)) {
/* If user requests enabled the Heuristic for encrypted archives... */
if (SCAN_HEURISTIC_ENCRYPTED_ARCHIVE) {
- if (CL_VIRUS == cli_append_virus(ctx, "Heuristics.Encrypted.RAR")) {
+ if (CL_VIRUS == cli_append_potentially_unwanted(ctx, "Heuristics.Encrypted.RAR")) {
status = CL_VIRUS;
}
}
- if (status != CL_VIRUS) {
- status = CL_CLEAN;
- }
}
cli_dbgmsg("RAR: Exit code: %d\n", status);
- if (SCAN_ALLMATCHES && viruses_found)
- status = CL_VIRUS;
-
return status;
}
@@ -668,11 +634,10 @@ static cl_error_t cli_egg_scanmetadata(cl_egg_metadata *metadata, cli_ctx *ctx,
static cl_error_t cli_scanegg(cli_ctx *ctx)
{
- cl_error_t status = CL_EPARSE;
- cl_error_t egg_ret = CL_EPARSE;
+ cl_error_t status = CL_SUCCESS;
+ cl_error_t egg_ret;
- unsigned int file_count = 0;
- unsigned int viruses_found = 0;
+ unsigned int file_count = 0;
uint32_t nEncryptedFilesFound = 0;
uint32_t nTooLargeFilesFound = 0;
@@ -687,6 +652,10 @@ static cl_error_t cli_scanegg(cli_ctx *ctx)
char *extract_fullpath = NULL;
char *comment_fullpath = NULL;
+ char *extract_filename = NULL;
+ char *extract_buffer = NULL;
+ size_t extract_buffer_len = 0;
+
if (ctx == NULL) {
cli_dbgmsg("EGG: Invalid arguments!\n");
return CL_EARG;
@@ -703,7 +672,8 @@ static cl_error_t cli_scanegg(cli_ctx *ctx)
if (CL_SUCCESS != (egg_ret = cli_egg_open(ctx->fmap, &hArchive, &comments, &nComments))) {
if (egg_ret == CL_EUNPACK) {
cli_dbgmsg("EGG: Encrypted main header\n");
- status = CL_EUNPACK;
+ nEncryptedFilesFound += 1;
+ status = CL_SUCCESS;
goto done;
}
if (egg_ret == CL_EMEM) {
@@ -755,12 +725,7 @@ static cl_error_t cli_scanegg(cli_ctx *ctx)
* Scan the comment.
*/
status = cli_magic_scan_buff(comments[i], strlen(comments[i]), ctx, NULL);
-
- if ((status == CL_VIRUS) && SCAN_ALLMATCHES) {
- status = CL_CLEAN;
- viruses_found++;
- }
- if ((status == CL_VIRUS) || (status == CL_BREAK)) {
+ if (status != CL_SUCCESS) {
goto done;
}
}
@@ -813,13 +778,12 @@ static cl_error_t cli_scanegg(cli_ctx *ctx)
* Scan the metadata for the file in question since the content was clean, or we're running in all-match.
*/
status = cli_egg_scanmetadata(&metadata, ctx, file_count);
- if ((status == CL_VIRUS) && SCAN_ALLMATCHES) {
- status = CL_CLEAN;
- viruses_found++;
- }
- if ((status == CL_VIRUS) || (status == CL_BREAK)) {
+ if (status == CL_EUNPACK) {
+ nEncryptedFilesFound += 1;
+ } else if (status != CL_SUCCESS) {
break;
}
+
/* Check if we've already exceeded the scan limit */
if (cli_checklimits("EGG", ctx, 0, 0, 0))
break;
@@ -859,9 +823,6 @@ static cl_error_t cli_scanegg(cli_ctx *ctx)
/*
* Extract the file...
*/
- char *extract_filename = NULL;
- char *extract_buffer = NULL;
- size_t extract_buffer_len = 0;
cli_dbgmsg("EGG: Extracting file: %s\n", metadata.filename);
@@ -925,10 +886,8 @@ static cl_error_t cli_scanegg(cli_ctx *ctx)
*/
cli_dbgmsg("EGG: Extraction complete. Scanning now...\n");
status = cli_magic_scan_buff(extract_buffer, extract_buffer_len, ctx, filename_base);
- if (status == CL_VIRUS) {
- cli_dbgmsg("EGG: infected with %s\n", cli_get_last_virus(ctx));
- status = CL_VIRUS;
- viruses_found++;
+ if (status != CL_SUCCESS) {
+ goto done;
}
if (NULL != filename_base) {
@@ -953,13 +912,6 @@ static cl_error_t cli_scanegg(cli_ctx *ctx)
}
}
- if (status == CL_VIRUS) {
- if (SCAN_ALLMATCHES)
- status = CL_SUCCESS;
- else
- break;
- }
-
if (ctx->engine->maxscansize && ctx->scansize >= ctx->engine->maxscansize) {
status = CL_CLEAN;
break;
@@ -975,11 +927,22 @@ static cl_error_t cli_scanegg(cli_ctx *ctx)
} while (status == CL_CLEAN);
- if (status == CL_BREAK)
+ if (status == CL_BREAK) {
status = CL_CLEAN;
+ }
done:
+ if (NULL != extract_filename) {
+ free(extract_filename);
+ extract_filename = NULL;
+ }
+
+ if (NULL != extract_buffer) {
+ free(extract_buffer);
+ extract_buffer = NULL;
+ }
+
if (NULL != comment_fullpath) {
free(comment_fullpath);
comment_fullpath = NULL;
@@ -1005,34 +968,26 @@ static cl_error_t cli_scanegg(cli_ctx *ctx)
extract_fullpath = NULL;
}
- if ((CL_VIRUS != status) && ((CL_EUNPACK == status) || (nEncryptedFilesFound > 0))) {
+ if ((CL_VIRUS != status) && (nEncryptedFilesFound > 0)) {
/* If user requests enabled the Heuristic for encrypted archives... */
if (SCAN_HEURISTIC_ENCRYPTED_ARCHIVE) {
- if (CL_VIRUS == cli_append_virus(ctx, "Heuristics.Encrypted.EGG")) {
+ if (CL_VIRUS == cli_append_potentially_unwanted(ctx, "Heuristics.Encrypted.EGG")) {
status = CL_VIRUS;
}
}
- if (status != CL_VIRUS) {
- status = CL_CLEAN;
- }
}
cli_dbgmsg("EGG: Exit code: %d\n", status);
- if (SCAN_ALLMATCHES && viruses_found)
- status = CL_VIRUS;
-
return status;
}
static cl_error_t cli_scanarj(cli_ctx *ctx)
{
cl_error_t ret = CL_CLEAN;
- cl_error_t status;
- int file = 0;
+ int file = 0;
arj_metadata_t metadata;
- char *dir;
- int virus_found = 0;
+ char *dir = NULL;
cli_dbgmsg("in cli_scanarj()\n");
@@ -1058,22 +1013,20 @@ static cl_error_t cli_scanarj(cli_ctx *ctx)
}
do {
-
metadata.filename = NULL;
- ret = cli_unarj_prepare_file(dir, &metadata);
+
+ ret = cli_unarj_prepare_file(dir, &metadata);
if (ret != CL_SUCCESS) {
cli_dbgmsg("ARJ: cli_unarj_prepare_file Error: %s\n", cl_strerror(ret));
break;
}
+
file++;
- if (cli_matchmeta(ctx, metadata.filename, metadata.comp_size, metadata.orig_size, metadata.encrypted, file, 0, NULL) == CL_VIRUS) {
- if (!SCAN_ALLMATCHES) {
- cli_rmdirs(dir);
- free(dir);
- return CL_VIRUS;
- }
- virus_found = 1;
- ret = CL_SUCCESS;
+
+ if (CL_VIRUS == cli_matchmeta(ctx, metadata.filename, metadata.comp_size, metadata.orig_size, metadata.encrypted, file, 0, NULL)) {
+ cli_rmdirs(dir);
+ free(dir);
+ return CL_VIRUS;
}
if ((ret = cli_checklimits("ARJ", ctx, metadata.orig_size, metadata.comp_size, 0)) != CL_CLEAN) {
@@ -1082,30 +1035,24 @@ static cl_error_t cli_scanarj(cli_ctx *ctx)
free(metadata.filename);
continue;
}
+
ret = cli_unarj_extract_file(dir, &metadata);
if (ret != CL_SUCCESS) {
cli_dbgmsg("ARJ: cli_unarj_extract_file Error: %s\n", cl_strerror(ret));
}
+
if (metadata.ofd >= 0) {
if (lseek(metadata.ofd, 0, SEEK_SET) == -1) {
cli_dbgmsg("ARJ: call to lseek() failed\n");
}
- status = cli_magic_scan_desc(metadata.ofd, NULL, ctx, metadata.filename);
+
+ ret = cli_magic_scan_desc(metadata.ofd, NULL, ctx, metadata.filename);
close(metadata.ofd);
- if (status == CL_VIRUS) {
- cli_dbgmsg("ARJ: infected with %s\n", cli_get_last_virus(ctx));
- if (!SCAN_ALLMATCHES) {
- ret = CL_VIRUS;
- if (metadata.filename) {
- free(metadata.filename);
- metadata.filename = NULL;
- }
- break;
- }
- virus_found = 1;
- ret = CL_SUCCESS;
+ if (ret != CL_SUCCESS) {
+ break;
}
}
+
if (metadata.filename) {
free(metadata.filename);
metadata.filename = NULL;
@@ -1113,19 +1060,23 @@ static cl_error_t cli_scanarj(cli_ctx *ctx)
} while (ret == CL_SUCCESS);
- if (!ctx->engine->keeptmp)
+ if (!ctx->engine->keeptmp) {
cli_rmdirs(dir);
+ }
+
+ if (NULL != dir) {
+ free(dir);
+ }
- free(dir);
if (metadata.filename) {
free(metadata.filename);
}
- if (virus_found != 0)
- ret = CL_VIRUS;
cli_dbgmsg("ARJ: Exit code: %d\n", ret);
- if (ret == CL_BREAK)
- ret = CL_CLEAN;
+
+ if (ret == CL_BREAK) {
+ ret = CL_SUCCESS;
+ }
return ret;
}
@@ -1177,22 +1128,20 @@ static cl_error_t cli_scangzip_with_zib_from_the_80s(cli_ctx *ctx, unsigned char
gzclose(gz);
- if ((ret = cli_magic_scan_desc(fd, tmpname, ctx, NULL)) == CL_VIRUS) {
- cli_dbgmsg("GZip: Infected with %s\n", cli_get_last_virus(ctx));
+ if (CL_SUCCESS != (ret = cli_magic_scan_desc(fd, tmpname, ctx, NULL))) {
close(fd);
if (!ctx->engine->keeptmp) {
- if (cli_unlink(tmpname)) {
- free(tmpname);
- return CL_EUNLINK;
- }
+ (void)cli_unlink(tmpname);
}
free(tmpname);
- return CL_VIRUS;
+ return ret;
}
close(fd);
- if (!ctx->engine->keeptmp)
- if (cli_unlink(tmpname))
+ if (!ctx->engine->keeptmp) {
+ if (cli_unlink(tmpname)) {
ret = CL_EUNLINK;
+ }
+ }
free(tmpname);
return ret;
}
@@ -1279,8 +1228,7 @@ static cl_error_t cli_scangzip(cli_ctx *ctx)
inflateEnd(&z);
- if ((ret = cli_magic_scan_desc(fd, tmpname, ctx, NULL)) == CL_VIRUS) {
- cli_dbgmsg("GZip: Infected with %s\n", cli_get_last_virus(ctx));
+ if (CL_SUCCESS != (ret = cli_magic_scan_desc(fd, tmpname, ctx, NULL))) {
close(fd);
if (!ctx->engine->keeptmp) {
if (cli_unlink(tmpname)) {
@@ -1289,13 +1237,14 @@ static cl_error_t cli_scangzip(cli_ctx *ctx)
}
}
free(tmpname);
- return CL_VIRUS;
+ return ret;
}
close(fd);
if (!ctx->engine->keeptmp)
if (cli_unlink(tmpname))
ret = CL_EUNLINK;
free(tmpname);
+
return ret;
}
@@ -1385,18 +1334,16 @@ static cl_error_t cli_scanbzip(cli_ctx *ctx)
BZ2_bzDecompressEnd(&strm);
- if ((ret = cli_magic_scan_desc(fd, tmpname, ctx, NULL)) == CL_VIRUS) {
- cli_dbgmsg("Bzip: Infected with %s\n", cli_get_last_virus(ctx));
+ if (CL_SUCCESS != (ret = cli_magic_scan_desc(fd, tmpname, ctx, NULL))) {
close(fd);
if (!ctx->engine->keeptmp) {
if (cli_unlink(tmpname)) {
- ret = CL_EUNLINK;
free(tmpname);
- return ret;
+ return CL_EUNLINK;
}
}
free(tmpname);
- return CL_VIRUS;
+ return ret;
}
close(fd);
if (!ctx->engine->keeptmp)
@@ -1459,7 +1406,7 @@ static cl_error_t cli_scanxz(cli_ctx *ctx)
rc = cli_XzDecode(&strm);
if (XZ_RESULT_OK != rc && XZ_STREAM_END != rc) {
if (rc == XZ_DIC_HEURISTIC) {
- ret = cli_append_virus(ctx, "Heuristics.XZ.DicSizeLimit");
+ ret = cli_append_potentially_unwanted(ctx, "Heuristics.XZ.DicSizeLimit");
goto xz_exit;
}
cli_errmsg("cli_scanxz: decompress error: %d\n", rc);
@@ -1494,16 +1441,16 @@ static cl_error_t cli_scanxz(cli_ctx *ctx)
} while (XZ_STREAM_END != rc);
/* scan decompressed file */
- if ((ret = cli_magic_scan_desc(fd, tmpname, ctx, NULL)) == CL_VIRUS) {
- cli_dbgmsg("cli_scanxz: Infected with %s\n", cli_get_last_virus(ctx));
- }
+ ret = cli_magic_scan_desc(fd, tmpname, ctx, NULL);
xz_exit:
cli_XzShutdown(&strm);
close(fd);
- if (!ctx->engine->keeptmp)
- if (cli_unlink(tmpname) && ret == CL_CLEAN)
+ if (!ctx->engine->keeptmp) {
+ if (cli_unlink(tmpname) && ret == CL_CLEAN) {
ret = CL_EUNLINK;
+ }
+ }
free(tmpname);
free(buf);
return ret;
@@ -1546,23 +1493,23 @@ static cl_error_t cli_scanszdd(cli_ctx *ctx)
static cl_error_t vba_scandata(const unsigned char *data, size_t len, cli_ctx *ctx)
{
- cl_error_t ret = CL_SUCCESS;
- struct cli_matcher *groot = ctx->engine->root[0];
- struct cli_matcher *troot = ctx->engine->root[2];
+ cl_error_t ret = CL_SUCCESS;
+ struct cli_matcher *generic_ac_root = ctx->engine->root[0];
+ struct cli_matcher *target_ac_root = ctx->engine->root[2];
struct cli_ac_data gmdata, tmdata;
bool gmdata_initialized = false;
bool tmdata_initialized = false;
struct cli_ac_data *mdata[2];
- unsigned int viruses_found = 0;
+ bool must_pop_stack = false;
cl_fmap_t *new_map = NULL;
- if ((ret = cli_ac_initdata(&tmdata, troot->ac_partsigs, troot->ac_lsigs, troot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN))) {
+ if ((ret = cli_ac_initdata(&tmdata, target_ac_root->ac_partsigs, target_ac_root->ac_lsigs, target_ac_root->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN))) {
goto done;
}
tmdata_initialized = true;
- if ((ret = cli_ac_initdata(&gmdata, groot->ac_partsigs, groot->ac_lsigs, groot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN))) {
+ if ((ret = cli_ac_initdata(&gmdata, generic_ac_root->ac_partsigs, generic_ac_root->ac_lsigs, generic_ac_root->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN))) {
goto done;
}
gmdata_initialized = true;
@@ -1571,42 +1518,43 @@ static cl_error_t vba_scandata(const unsigned char *data, size_t len, cli_ctx *c
mdata[1] = &gmdata;
ret = cli_scan_buff(data, len, 0, ctx, CL_TYPE_MSOLE2, mdata);
- if (ret == CL_VIRUS) {
- viruses_found++;
+ if (CL_SUCCESS != ret) {
+ goto done;
}
- if (ret == CL_CLEAN || (ret == CL_VIRUS && SCAN_ALLMATCHES)) {
- /*
- * Evaluate logical & yara rules given the new matches to see if anything alerts.
- */
- new_map = fmap_open_memory(data, len, NULL);
- if (new_map == NULL) {
- cli_dbgmsg("Failed to create fmap for evaluating logical/yara rules after call to cli_scan_buff()\n");
- ret = CL_EMEM;
- goto done;
- }
+ /*
+ * Evaluate logical & yara rules given the new matches to see if anything alerts.
+ */
+ new_map = fmap_open_memory(data, len, NULL);
+ if (new_map == NULL) {
+ cli_dbgmsg("Failed to create fmap for evaluating logical/yara rules after call to cli_scan_buff()\n");
+ ret = CL_EMEM;
+ goto done;
+ }
- ctx->next_layer_is_normalized = true; // This flag ingested by cli_recursion_stack_push().
+ ctx->next_layer_is_normalized = true; // This flag ingested by cli_recursion_stack_push().
- ret = cli_recursion_stack_push(ctx, new_map, CL_TYPE_MSOLE2, true); /* Perform exp_eval with child fmap */
- if (CL_SUCCESS != ret) {
- cli_dbgmsg("Failed to scan fmap.\n");
- goto done;
- }
+ ret = cli_recursion_stack_push(ctx, new_map, CL_TYPE_MSOLE2, true); /* Perform exp_eval with child fmap */
+ if (CL_SUCCESS != ret) {
+ cli_dbgmsg("Failed to scan fmap.\n");
+ goto done;
+ }
- ret = cli_exp_eval(ctx, troot, &tmdata, NULL, NULL);
- if (ret == CL_VIRUS) {
- viruses_found++;
- }
+ must_pop_stack = true;
- if (ret == CL_CLEAN || (ret == CL_VIRUS && SCAN_ALLMATCHES)) {
- ret = cli_exp_eval(ctx, groot, &gmdata, NULL, NULL);
- }
+ ret = cli_exp_eval(ctx, target_ac_root, &tmdata, NULL, NULL);
+ if (ret == CL_VIRUS) {
+ goto done;
+ }
+
+ ret = cli_exp_eval(ctx, generic_ac_root, &gmdata, NULL, NULL);
+done:
+
+ if (must_pop_stack) {
(void)cli_recursion_stack_pop(ctx); /* Restore the parent fmap */
}
-done:
if (NULL != new_map) {
funmap(new_map);
}
@@ -1619,9 +1567,6 @@ static cl_error_t vba_scandata(const unsigned char *data, size_t len, cli_ctx *c
cli_ac_freedata(&gmdata);
}
- if (ret == CL_CLEAN && viruses_found) {
- ret = CL_VIRUS;
- }
return ret;
}
@@ -1693,8 +1638,7 @@ static cl_error_t cli_ole2_tempdir_scan_vba_new(const char *dir, cli_ctx *ctx, s
char *hash = NULL;
char path[PATH_MAX];
char filename[PATH_MAX];
- int tempfd = -1;
- int viruses_found = 0;
+ int tempfd = -1;
if (CL_SUCCESS != (ret = uniq_get(U, "dir", 3, &hash, &hashcnt))) {
cli_dbgmsg("cli_ole2_tempdir_scan_vba_new: uniq_get('dir') failed with ret code (%d)!\n", ret);
@@ -1733,12 +1677,9 @@ static cl_error_t cli_ole2_tempdir_scan_vba_new(const char *dir, cli_ctx *ctx, s
}
#endif
if (SCAN_HEURISTIC_MACROS && *has_macros) {
- ret = cli_append_virus(ctx, "Heuristics.OLE2.ContainsMacros.VBA");
+ ret = cli_append_potentially_unwanted(ctx, "Heuristics.OLE2.ContainsMacros.VBA");
if (ret == CL_VIRUS) {
- viruses_found++;
- if (!SCAN_ALLMATCHES) {
- goto done;
- }
+ goto done;
}
}
@@ -1751,17 +1692,13 @@ static cl_error_t cli_ole2_tempdir_scan_vba_new(const char *dir, cli_ctx *ctx, s
goto done;
}
- ret = cli_scan_desc(tempfd, ctx, CL_TYPE_SCRIPT, 0, NULL, AC_SCAN_VIR, NULL, NULL);
+ ret = cli_scan_desc(tempfd, ctx, CL_TYPE_SCRIPT, false, NULL, AC_SCAN_VIR, NULL, NULL);
+ if (CL_SUCCESS != ret) {
+ goto done;
+ }
close(tempfd);
tempfd = -1;
-
- if (CL_VIRUS == ret) {
- viruses_found++;
- if (!SCAN_ALLMATCHES) {
- goto done;
- }
- }
}
hashcnt--;
@@ -1773,8 +1710,6 @@ static cl_error_t cli_ole2_tempdir_scan_vba_new(const char *dir, cli_ctx *ctx, s
tempfd = -1;
}
- if (viruses_found > 0)
- ret = CL_VIRUS;
return ret;
}
@@ -1857,8 +1792,9 @@ static cl_error_t cli_ole2_tempdir_scan_embedded_ole10(const char *dir, cli_ctx
cl_error_t ret;
char ole10_filename[1024];
char *hash;
- uint32_t hashcnt = 0;
- unsigned int viruses_found = 0;
+ uint32_t hashcnt = 0;
+
+ int fd = -1;
/* Check directory for embedded OLE objects */
if (CL_SUCCESS != (ret = uniq_get(U, "_1_ole10native", 14, &hash, &hashcnt))) {
@@ -1867,29 +1803,31 @@ static cl_error_t cli_ole2_tempdir_scan_embedded_ole10(const char *dir, cli_ctx
goto done;
}
while (hashcnt) {
- int fd = -1;
-
snprintf(ole10_filename, sizeof(ole10_filename), "%s" PATHSEP "%s_%u", dir, hash, hashcnt);
ole10_filename[sizeof(ole10_filename) - 1] = '\0';
fd = open(ole10_filename, O_RDONLY | O_BINARY);
- if (fd >= 0) {
- ret = cli_scan_ole10(fd, ctx);
- close(fd);
- if (CL_VIRUS == ret) {
- viruses_found++;
- if (!SCAN_ALLMATCHES) {
- status = ret;
- goto done;
- }
- }
+ if (fd < 0) {
+ hashcnt--;
+ continue;
+ }
+
+ ret = cli_scan_ole10(fd, ctx);
+ if (CL_SUCCESS != ret) {
+ status = ret;
+ goto done;
}
+
+ close(fd);
+ fd = -1;
+
hashcnt--;
}
done:
- if (viruses_found > 0) {
- status = CL_VIRUS;
+
+ if (fd >= 0) {
+ close(fd);
}
return status;
@@ -1897,20 +1835,24 @@ static cl_error_t cli_ole2_tempdir_scan_embedded_ole10(const char *dir, cli_ctx
static cl_error_t cli_ole2_tempdir_scan_vba(const char *dir, cli_ctx *ctx, struct uniq *U, int *has_macros)
{
- cl_error_t status = CL_CLEAN;
+ cl_error_t status = CL_SUCCESS;
cl_error_t ret;
int i, j;
size_t data_len;
vba_project_t *vba_project;
- char *fullname, vbaname[1024];
- unsigned char *data;
+ char *fullname = NULL;
+ char vbaname[1024];
+ unsigned char *data = NULL;
char *hash;
- uint32_t hashcnt = 0;
- unsigned int viruses_found = 0;
+ uint32_t hashcnt = 0;
- if (CL_SUCCESS != (ret = uniq_get(U, "_vba_project", 12, NULL, &hashcnt))) {
- cli_dbgmsg("cli_ole2_tempdir_scan_vba: uniq_get('_vba_project') failed with ret code (%d)!\n", ret);
- status = ret;
+ int fd = -1;
+
+ int proj_contents_fd = -1;
+ char *proj_contents_fname = NULL;
+
+ if (CL_SUCCESS != (status = uniq_get(U, "_vba_project", 12, NULL, &hashcnt))) {
+ cli_dbgmsg("cli_ole2_tempdir_scan_vba: uniq_get('_vba_project') failed with ret code (%d)!\n", status);
goto done;
}
while (hashcnt) {
@@ -1921,8 +1863,6 @@ static cl_error_t cli_ole2_tempdir_scan_vba(const char *dir, cli_ctx *ctx, struc
for (i = 0; i < vba_project->count; i++) {
for (j = 1; (unsigned int)j <= vba_project->colls[i]; j++) {
- int fd = -1;
-
snprintf(vbaname, 1024, "%s" PATHSEP "%s_%u", vba_project->dir, vba_project->name[i], j);
vbaname[sizeof(vbaname) - 1] = '\0';
@@ -1930,188 +1870,197 @@ static cl_error_t cli_ole2_tempdir_scan_vba(const char *dir, cli_ctx *ctx, struc
if (fd == -1) {
continue;
}
+
cli_dbgmsg("cli_ole2_tempdir_scan_vba: Decompress VBA project '%s_%u'\n", vba_project->name[i], j);
+
data = (unsigned char *)cli_vba_inflate(fd, vba_project->offset[i], &data_len);
+
close(fd);
+ fd = -1;
+
*has_macros = *has_macros + 1;
- if (!data) {
- } else {
+
+ if (NULL != data) {
/* cli_dbgmsg("Project content:\n%s", data); */
if (ctx->scanned)
*ctx->scanned += data_len / CL_COUNT_PRECISION;
if (ctx->engine->keeptmp) {
- char *tempfile;
- int of;
-
- if ((ret = cli_gentempfd(ctx->sub_tmpdir, &tempfile, &of)) != CL_SUCCESS) {
+ if (CL_SUCCESS != (status = cli_gentempfd(ctx->sub_tmpdir, &proj_contents_fname, &proj_contents_fd))) {
cli_warnmsg("WARNING: VBA project '%s_%u' cannot be dumped to file\n", vba_project->name[i], j);
- status = ret;
goto done;
}
- if (cli_writen(of, data, data_len) != data_len) {
+
+ if (cli_writen(proj_contents_fd, data, data_len) != data_len) {
cli_warnmsg("WARNING: VBA project '%s_%u' failed to write to file\n", vba_project->name[i], j);
- close(of);
- free(tempfile);
status = CL_EWRITE;
goto done;
}
- cli_dbgmsg("cli_ole2_tempdir_scan_vba: VBA project '%s_%u' dumped to %s\n", vba_project->name[i], j, tempfile);
- free(tempfile);
+ close(proj_contents_fd);
+ proj_contents_fd = -1;
+
+ free(proj_contents_fname);
+ proj_contents_fname = NULL;
+
+ cli_dbgmsg("cli_ole2_tempdir_scan_vba: VBA project '%s_%u' dumped to %s\n", vba_project->name[i], j, proj_contents_fname);
}
- if (vba_scandata(data, data_len, ctx) == CL_VIRUS) {
- viruses_found++;
- if (!SCAN_ALLMATCHES) {
- free(data);
- status = CL_VIRUS;
- break;
- }
+ status = vba_scandata(data, data_len, ctx);
+ if (CL_SUCCESS != status) {
+ goto done;
}
+
free(data);
+ data = NULL;
}
}
-
- if (status == CL_VIRUS)
- break;
}
cli_free_vba_project(vba_project);
vba_project = NULL;
- if (status == CL_VIRUS)
- break;
-
hashcnt--;
}
- if (status == CL_CLEAN || (status == CL_VIRUS && SCAN_ALLMATCHES)) {
- if (CL_SUCCESS != (ret = uniq_get(U, "powerpoint document", 19, &hash, &hashcnt))) {
- cli_dbgmsg("cli_ole2_tempdir_scan_vba: uniq_get('powerpoint document') failed with ret code (%d)!\n", ret);
- status = ret;
- goto done;
- }
- while (hashcnt) {
- int fd = -1;
+ if (CL_SUCCESS != (status = uniq_get(U, "powerpoint document", 19, &hash, &hashcnt))) {
+ cli_dbgmsg("cli_ole2_tempdir_scan_vba: uniq_get('powerpoint document') failed with ret code (%d)!\n", status);
+ goto done;
+ }
+ while (hashcnt) {
+ snprintf(vbaname, 1024, "%s" PATHSEP "%s_%u", dir, hash, hashcnt);
+ vbaname[sizeof(vbaname) - 1] = '\0';
- snprintf(vbaname, 1024, "%s" PATHSEP "%s_%u", dir, hash, hashcnt);
- vbaname[sizeof(vbaname) - 1] = '\0';
+ fd = open(vbaname, O_RDONLY | O_BINARY);
+ if (fd == -1) {
+ hashcnt--;
+ continue;
+ }
- fd = open(vbaname, O_RDONLY | O_BINARY);
- if (fd == -1) {
- hashcnt--;
- continue;
+ fullname = cli_ppt_vba_read(fd, ctx);
+ if (NULL != fullname) {
+ status = cli_magic_scan_dir(fullname, ctx);
+ if (CL_SUCCESS != status) {
+ goto done;
}
- if ((fullname = cli_ppt_vba_read(fd, ctx))) {
- ret = cli_magic_scan_dir(fullname, ctx);
- if (!ctx->engine->keeptmp)
- cli_rmdirs(fullname);
- free(fullname);
-
- if (ret == CL_VIRUS) {
- status = CL_VIRUS;
- viruses_found++;
- if (!SCAN_ALLMATCHES) {
- close(fd);
- break;
- }
- }
+ if (!ctx->engine->keeptmp) {
+ cli_rmdirs(fullname);
}
- close(fd);
- hashcnt--;
+ free(fullname);
+ fullname = NULL;
}
+
+ close(fd);
+ fd = -1;
+
+ hashcnt--;
}
- if (status == CL_CLEAN || (status == CL_VIRUS && SCAN_ALLMATCHES)) {
- if (CL_SUCCESS != (ret = uniq_get(U, "worddocument", 12, &hash, &hashcnt))) {
- cli_dbgmsg("cli_ole2_tempdir_scan_vba: uniq_get('worddocument') failed with ret code (%d)!\n", ret);
- status = ret;
- goto done;
+ if (CL_SUCCESS != (status = uniq_get(U, "worddocument", 12, &hash, &hashcnt))) {
+ cli_dbgmsg("cli_ole2_tempdir_scan_vba: uniq_get('worddocument') failed with ret code (%d)!\n", status);
+ goto done;
+ }
+ while (hashcnt) {
+ snprintf(vbaname, sizeof(vbaname), "%s" PATHSEP "%s_%u", dir, hash, hashcnt);
+ vbaname[sizeof(vbaname) - 1] = '\0';
+
+ fd = open(vbaname, O_RDONLY | O_BINARY);
+ if (fd == -1) {
+ hashcnt--;
+ continue;
}
- while (hashcnt) {
- int fd = -1;
- snprintf(vbaname, sizeof(vbaname), "%s" PATHSEP "%s_%u", dir, hash, hashcnt);
- vbaname[sizeof(vbaname) - 1] = '\0';
+ if (!(vba_project = (vba_project_t *)cli_wm_readdir(fd))) {
+ close(fd);
+ fd = -1;
+ hashcnt--;
+ continue;
+ }
- fd = open(vbaname, O_RDONLY | O_BINARY);
- if (fd == -1) {
- hashcnt--;
- continue;
- }
+ for (i = 0; i < vba_project->count; i++) {
+ cli_dbgmsg("cli_ole2_tempdir_scan_vba: Decompress WM project macro:%d key:%d length:%d\n", i, vba_project->key[i], vba_project->length[i]);
- if (!(vba_project = (vba_project_t *)cli_wm_readdir(fd))) {
- close(fd);
- hashcnt--;
- continue;
- }
+ data = (unsigned char *)cli_wm_decrypt_macro(fd, vba_project->offset[i], vba_project->length[i], vba_project->key[i]);
+ if (!data) {
+ cli_dbgmsg("cli_ole2_tempdir_scan_vba: WARNING: WM project '%s' macro %d decrypted to NULL\n", vba_project->name[i], i);
+ } else {
+ cli_dbgmsg("cli_ole2_tempdir_scan_vba: Project content:\n%s", data);
- for (i = 0; i < vba_project->count; i++) {
- cli_dbgmsg("cli_ole2_tempdir_scan_vba: Decompress WM project macro:%d key:%d length:%d\n", i, vba_project->key[i], vba_project->length[i]);
- data = (unsigned char *)cli_wm_decrypt_macro(fd, vba_project->offset[i], vba_project->length[i], vba_project->key[i]);
- if (!data) {
- cli_dbgmsg("cli_ole2_tempdir_scan_vba: WARNING: WM project '%s' macro %d decrypted to NULL\n", vba_project->name[i], i);
- } else {
- cli_dbgmsg("cli_ole2_tempdir_scan_vba: Project content:\n%s", data);
- if (ctx->scanned)
- *ctx->scanned += vba_project->length[i] / CL_COUNT_PRECISION;
- if (vba_scandata(data, vba_project->length[i], ctx) == CL_VIRUS) {
- viruses_found++;
- if (!SCAN_ALLMATCHES) {
- free(data);
- status = CL_VIRUS;
- break;
- }
- }
- free(data);
+ if (ctx->scanned) {
+ *ctx->scanned += vba_project->length[i] / CL_COUNT_PRECISION;
}
- }
- close(fd);
- cli_free_vba_project(vba_project);
- vba_project = NULL;
+ status = vba_scandata(data, vba_project->length[i], ctx);
+ if (CL_SUCCESS != status) {
+ goto done;
+ }
- if (status == CL_VIRUS && !SCAN_ALLMATCHES) {
- break;
+ free(data);
+ data = NULL;
}
- hashcnt--;
}
+
+ close(fd);
+ fd = -1;
+
+ cli_free_vba_project(vba_project);
+ vba_project = NULL;
+
+ hashcnt--;
}
done:
+
+ if (*has_macros) {
#if HAVE_JSON
- if (*has_macros && SCAN_COLLECT_METADATA && (ctx->wrkproperty != NULL)) {
- cli_jsonbool(ctx->wrkproperty, "HasMacros", 1);
- json_object *macro_languages = cli_jsonarray(ctx->wrkproperty, "MacroLanguages");
- if (macro_languages) {
- cli_jsonstr(macro_languages, NULL, "VBA");
- } else {
- cli_dbgmsg("cli_ole2_tempdir_scan_vba: Failed to add \"VBA\" entry to MacroLanguages JSON array\n");
+ if (SCAN_COLLECT_METADATA && (ctx->wrkproperty != NULL)) {
+ cli_jsonbool(ctx->wrkproperty, "HasMacros", 1);
+ json_object *macro_languages = cli_jsonarray(ctx->wrkproperty, "MacroLanguages");
+ if (macro_languages) {
+ cli_jsonstr(macro_languages, NULL, "VBA");
+ } else {
+ cli_dbgmsg("cli_ole2_tempdir_scan_vba: Failed to add \"VBA\" entry to MacroLanguages JSON array\n");
+ }
}
- }
#endif
- if (SCAN_HEURISTIC_MACROS && *has_macros) {
- ret = cli_append_virus(ctx, "Heuristics.OLE2.ContainsMacros.VBA");
- if (ret == CL_VIRUS)
- viruses_found++;
+ if (SCAN_HEURISTIC_MACROS) {
+ ret = cli_append_potentially_unwanted(ctx, "Heuristics.OLE2.ContainsMacros.VBA");
+ if (ret == CL_VIRUS) {
+ status = ret;
+ }
+ }
}
- if (viruses_found > 0) {
- status = CL_VIRUS;
+ if (proj_contents_fd >= 0) {
+ close(proj_contents_fd);
+ }
+ if (NULL != proj_contents_fname) {
+ free(proj_contents_fname);
}
+
+ if (NULL != data) {
+ free(data);
+ }
+
+ if (NULL != fullname) {
+ if (!ctx->engine->keeptmp) {
+ (void)cli_rmdirs(fullname);
+ }
+
+ free(fullname);
+ }
+
return status;
}
static cl_error_t cli_ole2_tempdir_scan_for_xlm_and_images(const char *dir, cli_ctx *ctx, struct uniq *U)
{
- cl_error_t ret = CL_CLEAN;
- char *hash = NULL;
- uint32_t hashcnt = 0;
- unsigned int viruses_found = 0;
- char STR_WORKBOOK[] = "workbook";
- char STR_BOOK[] = "book";
+ cl_error_t ret = CL_CLEAN;
+ char *hash = NULL;
+ uint32_t hashcnt = 0;
+ char STR_WORKBOOK[] = "workbook";
+ char STR_BOOK[] = "book";
if (CL_SUCCESS != (ret = uniq_get(U, STR_WORKBOOK, sizeof(STR_WORKBOOK) - 1, &hash, &hashcnt))) {
if (CL_SUCCESS != (ret = uniq_get(U, STR_BOOK, sizeof(STR_BOOK) - 1, &hash, &hashcnt))) {
@@ -2121,7 +2070,7 @@ static cl_error_t cli_ole2_tempdir_scan_for_xlm_and_images(const char *dir, cli_
}
for (; hashcnt > 0; hashcnt--) {
- if ((ret = cli_extract_xlm_macros_and_images(dir, ctx, hash, hashcnt)) != CL_SUCCESS) {
+ if (CL_SUCCESS != (ret = cli_extract_xlm_macros_and_images(dir, ctx, hash, hashcnt))) {
switch (ret) {
case CL_VIRUS:
case CL_EMEM:
@@ -2133,133 +2082,160 @@ static cl_error_t cli_ole2_tempdir_scan_for_xlm_and_images(const char *dir, cli_
}
done:
- if (SCAN_ALLMATCHES && viruses_found)
- return CL_VIRUS;
return ret;
}
static cl_error_t cli_scanhtml(cli_ctx *ctx)
{
- char *tempname, fullname[1024];
- cl_error_t ret = CL_CLEAN;
- int fd;
- fmap_t *map = ctx->fmap;
- unsigned int viruses_found = 0;
- uint64_t curr_len = map->len;
+ cl_error_t status = CL_SUCCESS;
+ char *tempname = NULL;
+ char fullname[1024];
+ int fd = -1;
+ fmap_t *map = ctx->fmap;
+ uint64_t curr_len = map->len;
cli_dbgmsg("in cli_scanhtml()\n");
/* CL_ENGINE_MAX_HTMLNORMALIZE */
if (curr_len > ctx->engine->maxhtmlnormalize) {
cli_dbgmsg("cli_scanhtml: exiting (file larger than MaxHTMLNormalize)\n");
- return CL_CLEAN;
+ status = CL_SUCCESS;
+ goto done;
}
- if (!(tempname = cli_gentemp_with_prefix(ctx->sub_tmpdir, "html-tmp")))
- return CL_EMEM;
+ if (NULL == (tempname = cli_gentemp_with_prefix(ctx->sub_tmpdir, "html-tmp"))) {
+ status = CL_EMEM;
+ goto done;
+ }
if (mkdir(tempname, 0700)) {
cli_errmsg("cli_scanhtml: Can't create temporary directory %s\n", tempname);
- free(tempname);
- return CL_ETMPDIR;
+ status = CL_ETMPDIR;
+ goto done;
}
cli_dbgmsg("cli_scanhtml: using tempdir %s\n", tempname);
- html_normalise_map(map, tempname, NULL, ctx->dconf);
+ (void)html_normalise_map(map, tempname, NULL, ctx->dconf);
+
snprintf(fullname, 1024, "%s" PATHSEP "nocomment.html", tempname);
fd = open(fullname, O_RDONLY | O_BINARY);
if (fd >= 0) {
+ // nocomment.html file exists, so lets scan it.
+
ctx->next_layer_is_normalized = true; // This flag ingested by cli_recursion_stack_push().
- if ((ret = cli_scan_desc(fd, ctx, CL_TYPE_HTML, 0, NULL, AC_SCAN_VIR, NULL, NULL)) == CL_VIRUS)
- viruses_found++;
- close(fd);
- }
- if (ret == CL_CLEAN || (ret == CL_VIRUS && SCAN_ALLMATCHES)) {
- /* CL_ENGINE_MAX_HTMLNOTAGS */
- curr_len = map->len;
- if (curr_len > ctx->engine->maxhtmlnotags) {
- /* we're not interested in scanning large files in notags form */
- /* TODO: don't even create notags if file is over limit */
- cli_dbgmsg("cli_scanhtml: skipping notags (normalized size over MaxHTMLNoTags)\n");
- } else {
- snprintf(fullname, 1024, "%s" PATHSEP "notags.html", tempname);
- fd = open(fullname, O_RDONLY | O_BINARY);
- if (fd >= 0) {
- ctx->next_layer_is_normalized = true; // This flag ingested by cli_recursion_stack_push().
- if ((ret = cli_scan_desc(fd, ctx, CL_TYPE_HTML, 0, NULL, AC_SCAN_VIR, NULL, NULL)) == CL_VIRUS)
- viruses_found++;
- close(fd);
- }
+ status = cli_scan_desc(fd, ctx, CL_TYPE_HTML, false, NULL, AC_SCAN_VIR, NULL, NULL);
+ if (CL_SUCCESS != status) {
+ goto done;
}
+
+ close(fd);
+ fd = -1;
}
- if (ret == CL_CLEAN || (ret == CL_VIRUS && SCAN_ALLMATCHES)) {
- snprintf(fullname, 1024, "%s" PATHSEP "javascript", tempname);
+ /* CL_ENGINE_MAX_HTMLNOTAGS */
+ curr_len = map->len;
+ if (curr_len > ctx->engine->maxhtmlnotags) {
+ /* we're not interested in scanning large files in notags form */
+ /* TODO: don't even create notags if file is over limit */
+ cli_dbgmsg("cli_scanhtml: skipping notags (normalized size over MaxHTMLNoTags)\n");
+ } else {
+ snprintf(fullname, 1024, "%s" PATHSEP "notags.html", tempname);
+
fd = open(fullname, O_RDONLY | O_BINARY);
if (fd >= 0) {
+ // notags.html file exists, so lets scan it.
+
ctx->next_layer_is_normalized = true; // This flag ingested by cli_recursion_stack_push().
- if ((ret = cli_scan_desc(fd, ctx, CL_TYPE_HTML, 0, NULL, AC_SCAN_VIR, NULL, NULL)) == CL_VIRUS)
- viruses_found++;
- if (ret == CL_CLEAN || (ret == CL_VIRUS && SCAN_ALLMATCHES)) {
- ctx->next_layer_is_normalized = true; // This flag ingested by cli_recursion_stack_push().
- if ((ret = cli_scan_desc(fd, ctx, CL_TYPE_TEXT_ASCII, 0, NULL, AC_SCAN_VIR, NULL, NULL)) == CL_VIRUS)
- viruses_found++;
+
+ status = cli_scan_desc(fd, ctx, CL_TYPE_HTML, false, NULL, AC_SCAN_VIR, NULL, NULL);
+ if (CL_SUCCESS != status) {
+ goto done;
}
+
close(fd);
+ fd = -1;
}
}
- if (ret == CL_CLEAN || (ret == CL_VIRUS && SCAN_ALLMATCHES)) {
- ctx->next_layer_is_normalized = true; // This flag ingested by cli_recursion_stack_push() or cleared when cli_magic_scan_dir() is done.
- snprintf(fullname, 1024, "%s" PATHSEP "rfc2397", tempname);
- ret = cli_magic_scan_dir(fullname, ctx);
- if (CL_EOPEN == ret) {
- /* If the directory doesn't exist, that's fine */
- ret = CL_CLEAN;
+ snprintf(fullname, 1024, "%s" PATHSEP "javascript", tempname);
+ fd = open(fullname, O_RDONLY | O_BINARY);
+ if (fd >= 0) {
+ // javascript file exists, so lets scan it (twice, as different types).
+
+ ctx->next_layer_is_normalized = true; // This flag ingested by cli_recursion_stack_push().
+
+ status = cli_scan_desc(fd, ctx, CL_TYPE_HTML, false, NULL, AC_SCAN_VIR, NULL, NULL);
+ if (CL_SUCCESS != status) {
+ goto done;
}
+
+ ctx->next_layer_is_normalized = true; // This flag ingested by cli_recursion_stack_push().
+
+ status = cli_scan_desc(fd, ctx, CL_TYPE_TEXT_ASCII, false, NULL, AC_SCAN_VIR, NULL, NULL);
+ if (CL_SUCCESS != status) {
+ goto done;
+ }
+
+ close(fd);
+ fd = -1;
}
- if (!ctx->engine->keeptmp)
- cli_rmdirs(tempname);
+ ctx->next_layer_is_normalized = true; // This flag ingested by cli_recursion_stack_push() or cleared when cli_magic_scan_dir() is done.
+ snprintf(fullname, 1024, "%s" PATHSEP "rfc2397", tempname);
- free(tempname);
- if (SCAN_ALLMATCHES && viruses_found)
- return CL_VIRUS;
- return ret;
+ status = cli_magic_scan_dir(fullname, ctx);
+ if (CL_EOPEN == status) {
+ /* If the directory doesn't exist, that's fine */
+ status = CL_SUCCESS;
+ } else {
+ goto done;
+ }
+
+done:
+ if (fd >= 0) {
+ close(fd);
+ }
+ if (NULL != tempname) {
+ if (!ctx->engine->keeptmp) {
+ cli_rmdirs(tempname);
+ }
+ free(tempname);
+ }
+
+ return status;
}
static cl_error_t cli_scanscript(cli_ctx *ctx)
{
+ cl_error_t ret = CL_SUCCESS;
const unsigned char *buff;
unsigned char *normalized = NULL;
struct text_norm_state state;
char *tmpname = NULL;
int ofd = -1;
- cl_error_t ret;
- struct cli_matcher *troot;
+ struct cli_matcher *target_ac_root;
uint32_t maxpatlen, offset = 0;
- struct cli_matcher *groot;
+ struct cli_matcher *generic_ac_root;
struct cli_ac_data gmdata, tmdata;
int gmdata_initialized = 0;
int tmdata_initialized = 0;
struct cli_ac_data *mdata[2];
cl_fmap_t *new_map = NULL;
fmap_t *map;
- size_t at = 0;
- unsigned int viruses_found = 0;
+ size_t at = 0;
uint64_t curr_len;
struct cli_target_info info;
if (!ctx || !ctx->engine->root)
return CL_ENULLARG;
- map = ctx->fmap;
- curr_len = map->len;
- groot = ctx->engine->root[0];
- troot = ctx->engine->root[7];
- maxpatlen = troot ? troot->maxpatlen : 0;
+ map = ctx->fmap;
+ curr_len = map->len;
+ generic_ac_root = ctx->engine->root[0];
+ target_ac_root = ctx->engine->root[7];
+ maxpatlen = target_ac_root ? target_ac_root->maxpatlen : 0;
// Initialize info so it's safe to pass to destroy later
cli_targetinfo_init(&info);
@@ -2280,12 +2256,12 @@ static cl_error_t cli_scanscript(cli_ctx *ctx)
}
text_normalize_init(&state, normalized, SCANBUFF + maxpatlen);
- if ((ret = cli_ac_initdata(&tmdata, troot ? troot->ac_partsigs : 0, troot ? troot->ac_lsigs : 0, troot ? troot->ac_reloff_num : 0, CLI_DEFAULT_AC_TRACKLEN))) {
+ if ((ret = cli_ac_initdata(&tmdata, target_ac_root ? target_ac_root->ac_partsigs : 0, target_ac_root ? target_ac_root->ac_lsigs : 0, target_ac_root ? target_ac_root->ac_reloff_num : 0, CLI_DEFAULT_AC_TRACKLEN))) {
goto done;
}
tmdata_initialized = 1;
- if ((ret = cli_ac_initdata(&gmdata, groot->ac_partsigs, groot->ac_lsigs, groot->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN))) {
+ if ((ret = cli_ac_initdata(&gmdata, generic_ac_root->ac_partsigs, generic_ac_root->ac_lsigs, generic_ac_root->ac_reloff_num, CLI_DEFAULT_AC_TRACKLEN))) {
goto done;
}
gmdata_initialized = 1;
@@ -2293,7 +2269,7 @@ static cl_error_t cli_scanscript(cli_ctx *ctx)
/* dump to disk only if explicitly asked to
* or if necessary to check relative offsets,
* otherwise we can process just in-memory */
- if (ctx->engine->keeptmp || (troot && (troot->ac_reloff_num > 0 || troot->linked_bcs))) {
+ if (ctx->engine->keeptmp || (target_ac_root && (target_ac_root->ac_reloff_num > 0 || target_ac_root->linked_bcs))) {
if ((ret = cli_gentempfd(ctx->sub_tmpdir, &tmpname, &ofd))) {
cli_dbgmsg("cli_scanscript: Can't generate temporary file/descriptor\n");
goto done;
@@ -2305,8 +2281,8 @@ static cl_error_t cli_scanscript(cli_ctx *ctx)
mdata[0] = &tmdata;
mdata[1] = &gmdata;
- /* If there's a relative offset in troot or triggered bytecodes, normalize to file.*/
- if (troot && (troot->ac_reloff_num > 0 || troot->linked_bcs)) {
+ /* If there's a relative offset in target_ac_root or triggered bytecodes, normalize to file.*/
+ if (target_ac_root && (target_ac_root->ac_reloff_num > 0 || target_ac_root->linked_bcs)) {
size_t map_off = 0;
while (map_off < map->len) {
size_t written;
@@ -2338,19 +2314,21 @@ static cl_error_t cli_scanscript(cli_ctx *ctx)
}
/* scan map */
- ret = cli_scan_fmap(ctx, CL_TYPE_TEXT_ASCII, 0, NULL, AC_SCAN_VIR, NULL, NULL);
- if (ret == CL_VIRUS) {
- viruses_found++;
- }
+ ret = cli_scan_fmap(ctx, CL_TYPE_TEXT_ASCII, false, NULL, AC_SCAN_VIR, NULL, NULL);
(void)cli_recursion_stack_pop(ctx); /* Restore the parent fmap */
+
+ if (CL_SUCCESS != ret) {
+ goto done;
+ }
+
} else {
/* Since the above is moderately costly all in all,
* do the old stuff if there's no relative offsets. */
- if (troot) {
+ if (target_ac_root) {
cli_targetinfo(&info, 7, ctx);
- ret = cli_ac_caloff(troot, &tmdata, &info);
+ ret = cli_ac_caloff(target_ac_root, &tmdata, &info);
if (ret)
goto done;
}
@@ -2368,17 +2346,15 @@ static cl_error_t cli_scanscript(cli_ctx *ctx)
/* we can continue to scan in memory */
}
/* when we flush the buffer also scan */
- if (cli_scan_buff(state.out, state.out_pos, offset, ctx, CL_TYPE_TEXT_ASCII, mdata) == CL_VIRUS) {
- if (SCAN_ALLMATCHES)
- viruses_found++;
- else {
- ret = CL_VIRUS;
- break;
- }
+ ret = cli_scan_buff(state.out, state.out_pos, offset, ctx, CL_TYPE_TEXT_ASCII, mdata);
+ if (CL_SUCCESS != ret) {
+ goto done;
}
+
if (ctx->scanned)
*ctx->scanned += state.out_pos / CL_COUNT_PRECISION;
offset += state.out_pos;
+
/* carry over maxpatlen from previous buffer */
if (state.out_pos > maxpatlen)
memmove(state.out, state.out + state.out_pos - maxpatlen, maxpatlen);
@@ -2393,12 +2369,14 @@ static cl_error_t cli_scanscript(cli_ctx *ctx)
}
}
- if (ret != CL_VIRUS || SCAN_ALLMATCHES) {
- if ((ret = cli_exp_eval(ctx, troot, &tmdata, NULL, NULL)) == CL_VIRUS)
- viruses_found++;
- if (ret != CL_VIRUS || SCAN_ALLMATCHES)
- if ((ret = cli_exp_eval(ctx, groot, &gmdata, NULL, NULL)) == CL_VIRUS)
- viruses_found++;
+ ret = cli_exp_eval(ctx, target_ac_root, &tmdata, NULL, NULL);
+ if (CL_SUCCESS != ret) {
+ goto done;
+ }
+
+ ret = cli_exp_eval(ctx, generic_ac_root, &gmdata, NULL, NULL);
+ if (CL_SUCCESS != ret) {
+ goto done;
}
done:
@@ -2420,17 +2398,17 @@ static cl_error_t cli_scanscript(cli_ctx *ctx)
cli_ac_freedata(&gmdata);
}
- if (ofd != -1)
+ if (ofd != -1) {
close(ofd);
+ }
+
if (tmpname != NULL) {
- if (!ctx->engine->keeptmp)
- cli_unlink(tmpname);
+ if (!ctx->engine->keeptmp) {
+ (void)cli_unlink(tmpname);
+ }
free(tmpname);
}
- if (viruses_found)
- return CL_VIRUS;
-
return ret;
}
@@ -2498,7 +2476,9 @@ static cl_error_t cli_scanhtml_utf16(cli_ctx *ctx)
(void)cli_recursion_stack_pop(ctx); /* Restore the parent fmap */
- status = CL_SUCCESS;
+ if (CL_SUCCESS != status) {
+ goto done;
+ }
done:
if (NULL != new_map) {
@@ -2535,7 +2515,6 @@ static cl_error_t cli_ole2_scan_tempdir(
{
cl_error_t status = CL_CLEAN;
DIR *dd = NULL;
- int viruses_found = 0;
int has_macros = 0;
struct dirent *dent;
@@ -2550,51 +2529,27 @@ static cl_error_t cli_ole2_scan_tempdir(
}
status = cli_ole2_tempdir_scan_embedded_ole10(dir, ctx, files);
- if (CL_VIRUS == status) {
- viruses_found++;
- if (!SCAN_ALLMATCHES) {
- goto done;
- }
- } else if (CL_SUCCESS != status) {
- /* Some error occured */
- cli_dbgmsg("An error occured while scanning ole2 extracted VBA files: %s\n", cl_strerror(status));
+ if (CL_SUCCESS != status) {
goto done;
}
if (has_vba) {
status = cli_ole2_tempdir_scan_vba(dir, ctx, files, &has_macros);
- if (CL_VIRUS == status) {
- viruses_found++;
- if (!SCAN_ALLMATCHES) {
- goto done;
- }
- } else if (CL_SUCCESS != status) {
- /* Some error occured */
- cli_dbgmsg("An error occured while scanning ole2 extracted VBA files: %s\n", cl_strerror(status));
+ if (CL_SUCCESS != status) {
goto done;
}
status = cli_ole2_tempdir_scan_vba_new(dir, ctx, files, &has_macros);
- if (CL_VIRUS == status) {
- viruses_found++;
- if (!SCAN_ALLMATCHES) {
- goto done;
- }
- } else if (CL_SUCCESS != status) {
- /* Some error occured */
- cli_dbgmsg("An error occured while scanning ole2 extracted VBA files: %s\n", cl_strerror(status));
+ if (CL_SUCCESS != status) {
goto done;
}
}
if (has_xlm) {
if (SCAN_HEURISTIC_MACROS) {
- status = cli_append_virus(ctx, "Heuristics.OLE2.ContainsMacros.XLM");
- if (status == CL_VIRUS) {
- viruses_found++;
- if (!SCAN_ALLMATCHES) {
- goto done;
- }
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.OLE2.ContainsMacros.XLM");
+ if (CL_SUCCESS != status) {
+ goto done;
}
}
}
@@ -2603,28 +2558,14 @@ static cl_error_t cli_ole2_scan_tempdir(
/* TODO: Consider moving image extraction to handler_enum and
* removing the has_image and found_image stuff. */
status = cli_ole2_tempdir_scan_for_xlm_and_images(dir, ctx, files);
- if (CL_VIRUS == status) {
- viruses_found++;
- if (!SCAN_ALLMATCHES) {
- goto done;
- }
- } else if (CL_SUCCESS != status) {
- /* Some error occured */
- cli_dbgmsg("An error occured while scanning ole2 extracted VBA files: %s\n", cl_strerror(status));
+ if (CL_SUCCESS != status) {
goto done;
}
}
if (has_xlm || has_vba) {
status = cli_magic_scan_dir(dir, ctx);
- if (CL_VIRUS == status) {
- viruses_found++;
- if (!SCAN_ALLMATCHES) {
- goto done;
- }
- } else if (CL_SUCCESS != status) {
- /* Some error occured */
- cli_dbgmsg("An error occured while scanning ole2 extracted VBA files: %s\n", cl_strerror(status));
+ if (CL_SUCCESS != status) {
goto done;
}
}
@@ -2659,15 +2600,7 @@ static cl_error_t cli_ole2_scan_tempdir(
has_vba,
has_xlm,
has_image);
- if (CL_VIRUS == status) {
- viruses_found++;
- if (!SCAN_ALLMATCHES) {
- status = CL_VIRUS;
- goto done;
- }
- } else if (CL_SUCCESS != status) {
- /* Some error occured */
- cli_dbgmsg("An error occured while scanning ole2 extracted VBA files: %s\n", cl_strerror(status));
+ if (CL_SUCCESS != status) {
goto done;
}
}
@@ -2691,9 +2624,6 @@ static cl_error_t cli_ole2_scan_tempdir(
free(subdirectory);
}
- if (viruses_found > 0) {
- status = CL_VIRUS;
- }
return status;
}
@@ -2705,7 +2635,6 @@ static cl_error_t cli_scanole2(cli_ctx *ctx)
int has_vba = 0;
int has_xlm = 0;
int has_image = 0;
- int viruses_found = 0;
cli_dbgmsg("in cli_scanole2()\n");
@@ -2724,16 +2653,9 @@ static cl_error_t cli_scanole2(cli_ctx *ctx)
}
ret = cli_ole2_extract(dir, ctx, &files, &has_vba, &has_xlm, &has_image);
- if (ret != CL_CLEAN && ret != CL_VIRUS) {
- cli_dbgmsg("OLE2: %s\n", cl_strerror(ret));
+ if (CL_SUCCESS != ret) {
goto done;
}
- if (CL_VIRUS == ret) {
- viruses_found++;
- if (!SCAN_ALLMATCHES) {
- goto done;
- }
- }
if (files) {
/*
@@ -2767,10 +2689,6 @@ static cl_error_t cli_scanole2(cli_ctx *ctx)
free(dir);
}
- if (viruses_found > 0) {
- ret = CL_VIRUS;
- }
-
return ret;
}
@@ -2831,7 +2749,7 @@ static cl_error_t cli_scanriff(cli_ctx *ctx)
cl_error_t ret = CL_CLEAN;
if (cli_check_riff_exploit(ctx) == 2)
- ret = cli_append_virus(ctx, "Heuristics.Exploit.W32.MS05-002");
+ ret = cli_append_potentially_unwanted(ctx, "Heuristics.Exploit.W32.MS05-002");
return ret;
}
@@ -2882,15 +2800,17 @@ static cl_error_t cli_scancryptff(cli_ctx *ctx)
cli_dbgmsg("CryptFF: Scanning decrypted data\n");
- if ((ret = cli_magic_scan_desc(ndesc, tempfile, ctx, NULL)) == CL_VIRUS)
- cli_dbgmsg("CryptFF: Infected with %s\n", cli_get_last_virus(ctx));
+ ret = cli_magic_scan_desc(ndesc, tempfile, ctx, NULL);
close(ndesc);
- if (ctx->engine->keeptmp)
+ if (ctx->engine->keeptmp) {
cli_dbgmsg("CryptFF: Decompressed data saved in %s\n", tempfile);
- else if (cli_unlink(tempfile))
- ret = CL_EUNLINK;
+ } else {
+ if (CL_SUCCESS != cli_unlink(tempfile)) {
+ ret = CL_EUNLINK;
+ }
+ }
free(tempfile);
return ret;
@@ -2973,44 +2893,45 @@ static cl_error_t cli_scanuuencoded(cli_ctx *ctx)
static cl_error_t cli_scanmail(cli_ctx *ctx)
{
- char *dir;
+ char *dir = NULL;
cl_error_t ret;
- unsigned int viruses_found = 0;
cli_dbgmsg("Starting cli_scanmail()\n");
/* generate the temporary directory */
- if (!(dir = cli_gentemp_with_prefix(ctx->sub_tmpdir, "mail-tmp")))
- return CL_EMEM;
+ if (NULL == (dir = cli_gentemp_with_prefix(ctx->sub_tmpdir, "mail-tmp"))) {
+ ret = CL_EMEM;
+ goto done;
+ }
if (mkdir(dir, 0700)) {
cli_dbgmsg("Mail: Can't create temporary directory %s\n", dir);
- free(dir);
- return CL_ETMPDIR;
+ ret = CL_ETMPDIR;
+ goto done;
}
/*
* Extract the attachments into the temporary directory
*/
- if ((ret = cli_mbox(dir, ctx))) {
- if (ret == CL_VIRUS && SCAN_ALLMATCHES)
- viruses_found++;
- else {
- if (!ctx->engine->keeptmp)
- cli_rmdirs(dir);
- free(dir);
- return ret;
- }
+ ret = cli_mbox(dir, ctx);
+ if (CL_SUCCESS != ret) {
+ goto done;
}
ret = cli_magic_scan_dir(dir, ctx);
+ if (CL_SUCCESS != ret) {
+ goto done;
+ }
- if (!ctx->engine->keeptmp)
- cli_rmdirs(dir);
+done:
+ if (NULL != dir) {
+ if (!ctx->engine->keeptmp) {
+ cli_rmdirs(dir);
+ }
+
+ free(dir);
+ }
- free(dir);
- if (viruses_found)
- return CL_VIRUS;
return ret;
}
@@ -3020,12 +2941,11 @@ static cl_error_t cli_scan_structured(cli_ctx *ctx)
size_t result = 0;
unsigned int cc_count = 0;
unsigned int ssn_count = 0;
- int done = 0;
+ bool done = false;
fmap_t *map;
size_t pos = 0;
int (*ccfunc)(const unsigned char *buffer, size_t length, int cc_only);
int (*ssnfunc)(const unsigned char *buffer, size_t length);
- unsigned int viruses_found = 0;
if (ctx == NULL)
return CL_ENULLARG;
@@ -3067,38 +2987,28 @@ static cl_error_t cli_scan_structured(cli_ctx *ctx)
pos += result;
if ((cc_count += ccfunc((const unsigned char *)buf, result,
(ctx->options->heuristic & CL_SCAN_HEURISTIC_STRUCTURED_CC) ? 1 : 0)) >= ctx->engine->min_cc_count) {
- done = 1;
+ done = true;
}
if (ssnfunc && ((ssn_count += ssnfunc((const unsigned char *)buf, result)) >= ctx->engine->min_ssn_count)) {
- done = 1;
+ done = true;
}
}
if (cc_count != 0 && cc_count >= ctx->engine->min_cc_count) {
cli_dbgmsg("cli_scan_structured: %u credit card numbers detected\n", cc_count);
- if (CL_VIRUS == cli_append_virus(ctx, "Heuristics.Structured.CreditCardNumber")) {
- if (SCAN_ALLMATCHES) {
- viruses_found++;
- } else {
- return CL_VIRUS;
- }
+ if (CL_VIRUS == cli_append_potentially_unwanted(ctx, "Heuristics.Structured.CreditCardNumber")) {
+ return CL_VIRUS;
}
}
if (ssn_count != 0 && ssn_count >= ctx->engine->min_ssn_count) {
cli_dbgmsg("cli_scan_structured: %u social security numbers detected\n", ssn_count);
- if (CL_VIRUS == cli_append_virus(ctx, "Heuristics.Structured.SSN")) {
- if (SCAN_ALLMATCHES) {
- viruses_found++;
- } else {
- return CL_VIRUS;
- }
+ if (CL_VIRUS == cli_append_potentially_unwanted(ctx, "Heuristics.Structured.SSN")) {
+ return CL_VIRUS;
}
}
- if (viruses_found)
- return CL_VIRUS;
return CL_CLEAN;
}
@@ -3161,12 +3071,12 @@ static cl_error_t cli_scanembpe(cli_ctx *ctx, off_t offset)
}
}
+ // Setting ctx->corrupted_input will prevent the PE parser from reporting "broken executable" for unpacked/reconstructed files that may not be 100% to spec.
corrupted_input = ctx->corrupted_input;
ctx->corrupted_input = 1;
ret = cli_magic_scan_desc(fd, tmpname, ctx, NULL);
ctx->corrupted_input = corrupted_input;
- if (ret == CL_VIRUS) {
- cli_dbgmsg("cli_scanembpe: Infected with %s\n", cli_get_last_virus(ctx));
+ if (ret != CL_SUCCESS) {
close(fd);
if (!ctx->engine->keeptmp) {
if (cli_unlink(tmpname)) {
@@ -3175,7 +3085,7 @@ static cl_error_t cli_scanembpe(cli_ctx *ctx, off_t offset)
}
}
free(tmpname);
- return CL_VIRUS;
+ return ret;
}
close(fd);
@@ -3187,7 +3097,6 @@ static cl_error_t cli_scanembpe(cli_ctx *ctx, off_t offset)
}
free(tmpname);
- /* intentionally ignore possible errors from cli_magic_scan_desc */
return CL_CLEAN;
}
@@ -3375,6 +3284,8 @@ static cl_error_t scanraw(cli_ctx *ctx, cli_file_t type, uint8_t typercg, cli_fi
struct cli_exe_info peinfo;
unsigned int acmode = AC_SCAN_VIR, break_loop = 0;
+ cli_file_t found_type;
+
#if HAVE_JSON
struct json_object *parent_property = NULL;
#else
@@ -3401,16 +3312,17 @@ static cl_error_t scanraw(cli_ctx *ctx, cli_file_t type, uint8_t typercg, cli_fi
}
perf_start(ctx, PERFT_RAW);
- ret = cli_scan_fmap(ctx, type == CL_TYPE_TEXT_ASCII ? CL_TYPE_ANY : type, 0, &ftoffset, acmode, NULL, refhash);
+ ret = cli_scan_fmap(ctx, type == CL_TYPE_TEXT_ASCII ? CL_TYPE_ANY : type, false, &ftoffset, acmode, NULL, refhash);
perf_stop(ctx, PERFT_RAW);
- // I think this (CL_TYPENO business) causes embedded file extraction to stop when a
- // signature has matched in cli_scan_fmap, which wouldn't be what
- // we want if allmatch is specified.
- //
- // TODO: find a way to return type matches separately from malware matches
+ // In allmatch-mode, ret will never be CL_VIRUS, so ret may be used exlusively for file type detection and for terminal errors.
+ // When not in allmatch-mode, it's more important to return right away if ret is CL_VIRUS, so we don't care if file type matches were found.
if (ret >= CL_TYPENO) {
+ // Matched 1+ file type signatures. Handle them.
+ found_type = (cli_file_t)ret;
+
perf_nested_start(ctx, PERFT_RAWTYPENO, PERFT_SCAN);
+
fpt = ftoffset;
while (fpt) {
@@ -3418,6 +3330,9 @@ static cl_error_t scanraw(cli_ctx *ctx, cli_file_t type, uint8_t typercg, cli_fi
bool type_has_been_handled = true;
#if HAVE_JSON
+ /*
+ * Add embedded file to metadata JSON.
+ */
if (SCAN_COLLECT_METADATA && ctx->wrkproperty) {
json_object *arrobj;
@@ -3598,6 +3513,10 @@ static cl_error_t scanraw(cli_ctx *ctx, cli_file_t type, uint8_t typercg, cli_fi
type_has_been_handled = false;
}
+ if ((CL_EMEM == nret) || ctx->abort_scan) {
+ break;
+ }
+
/*
* Next, check for actual embedded files.
*/
@@ -3930,11 +3849,10 @@ static cl_error_t scanraw(cli_ctx *ctx, cli_file_t type, uint8_t typercg, cli_fi
if (NULL != new_map) {
free_duplicate_fmap(new_map);
}
- }
- }
+ } // end check for embedded files
+ } // end if (fpt->offset > 0)
- if ((nret == CL_VIRUS && !SCAN_ALLMATCHES) ||
- (nret == CL_EMEM) ||
+ if ((nret == CL_EMEM) ||
(ctx->abort_scan) ||
(break_loop)) {
break;
@@ -3948,17 +3866,17 @@ static cl_error_t scanraw(cli_ctx *ctx, cli_file_t type, uint8_t typercg, cli_fi
parent_property = NULL;
}
#endif
- }
+ } // end while (fpt) loop
- if (nret != CL_VIRUS) {
+ if (!((nret == CL_EMEM) || (ctx->abort_scan))) {
/*
* Now run the other file type parsers that may rely on file type
* recognition to determine the actual file type.
*/
- switch (ret) {
+ switch (found_type) {
case CL_TYPE_HTML:
- /* bb#11196 - autoit script file misclassified as HTML */
if (cli_recursion_stack_get_type(ctx, -2) == CL_TYPE_AUTOIT) {
+ /* bb#11196 - autoit script file misclassified as HTML */
ret = CL_TYPE_TEXT_ASCII;
} else if (SCAN_PARSE_HTML &&
(type == CL_TYPE_TEXT_ASCII ||
@@ -3985,7 +3903,7 @@ static cl_error_t scanraw(cli_ctx *ctx, cli_file_t type, uint8_t typercg, cli_fi
perf_nested_stop(ctx, PERFT_RAWTYPENO, PERFT_SCAN);
ret = nret;
- }
+ } // end if (ret >= CL_TYPENO)
#if HAVE_JSON
if (NULL != parent_property) {
@@ -3999,9 +3917,6 @@ static cl_error_t scanraw(cli_ctx *ctx, cli_file_t type, uint8_t typercg, cli_fi
free(fpt);
}
- if (ret == CL_VIRUS)
- cli_dbgmsg("%s found\n", cli_get_last_virus(ctx));
-
return ret;
}
@@ -4044,13 +3959,12 @@ static cl_error_t dispatch_prescan_callback(clcb_pre_scan cb, cli_ctx *ctx, cons
case CL_BREAK:
cli_dbgmsg("dispatch_prescan_callback: file allowed by callback\n");
perf_stop(ctx, PERFT_PRECB);
- status = CL_BREAK;
+ status = CL_VERIFIED;
break;
case CL_VIRUS:
cli_dbgmsg("dispatch_prescan_callback: file blocked by callback\n");
- cli_append_virus(ctx, "Detected.By.Callback");
+ status = cli_append_virus(ctx, "Detected.By.Callback");
perf_stop(ctx, PERFT_PRECB);
- status = CL_VIRUS;
break;
case CL_CLEAN:
break;
@@ -4116,18 +4030,118 @@ static cl_error_t calculate_fuzzy_image_hash(cli_ctx *ctx, cli_file_t type)
return status;
}
+/**
+ * @brief A unified list of reasons why a scan result inside the magic_scan function
+ * should goto done instead of continuing to parse/scan this layer.
+ *
+ * These are not reasons why the scan should abort entirely. For that, just check ctx->abort_scan.
+ *
+ * @param ctx The scan context.
+ * @param result_in The result to compare.
+ * @param result_out The result that magic_scan should return.
+ * @return true We found a reason to goto done.
+ * @return false The scan must go on.
+ */
+static inline bool result_should_goto_done(cli_ctx *ctx, cl_error_t result_in, cl_error_t *result_out)
+{
+ bool halt_scan = false;
+
+ if (NULL == ctx || NULL == result_out) {
+ cli_dbgmsg("Invalid arguments for file scan result check.\n");
+ halt_scan = true;
+ goto done;
+ }
+
+ if (NULL != ctx && ctx->abort_scan) {
+ // this covers CL_ETIMEOUT and CL_VIRUS at a minimum.
+ halt_scan = true;
+ *result_out = result_in;
+ goto done;
+ }
+
+ switch (result_in) {
+ /*
+ * Reasons to halt the scan and report the error up to the caller/user.
+ */
+
+ // A virus result means we should halt the scan.
+ // We do not return CL_VIRUS in allmatch-mode until the very end.
+ case CL_VIRUS:
+
+ // Each of these error codes considered terminal and will halt the scan.
+ case CL_EUNLINK:
+ case CL_ESTAT:
+ case CL_ESEEK:
+ case CL_EWRITE:
+ case CL_EDUP:
+ case CL_ETMPFILE:
+ case CL_ETMPDIR:
+ case CL_EMEM:
+ cli_dbgmsg("Descriptor[%d]: halting after file scan because: %s\n", fmap_fd(ctx->fmap), cl_strerror(result_in));
+ halt_scan = true;
+ *result_out = result_in;
+ break;
+
+ /*
+ * Reasons to halt the scan but report a successful scan.
+ */
+
+ // Exceeding the time limit should definitly halt the scan.
+ // But unless the user enabled alert-exceeds-max, we don't want to complain about it.
+ case CL_ETIMEOUT:
+
+ // If the file was determined to be trusted, then we can stop scanning this layer. (Ex: EXE with a valid Authenticode sig.)
+ // Convert CL_VERIFIED to CL_SUCCESS because we don't want to propagate the CL_VERIFIED return code up to the caller.
+ // If we didn't, a trusted file could cause a larger archive containing non-trustworthy files to be trusted.
+ case CL_VERIFIED:
+ cli_dbgmsg("Descriptor[%d]: halting after file scan because: %s\n", fmap_fd(ctx->fmap), cl_strerror(result_in));
+ halt_scan = true;
+ *result_out = CL_SUCCESS;
+ break;
+
+ /*
+ * All other results must not halt the scan.
+ */
+
+ // Nothing to do.
+ case CL_SUCCESS:
+
+ // Unless ctx->abort_scan was set, all these "MAX" conditions should finish scanning as much as is allowed.
+ // That is, the can may still be blocked from recursing into the next layer, or scanning new files or large files.
+ case CL_EMAXREC:
+ case CL_EMAXSIZE:
+ case CL_EMAXFILES:
+
+ // The following are explicitly listed here so you think twice before putting them in the scan-halt list, above.
+ // Malformed/truncated files could report as any of these three, and that's fine.
+ // See commit 087e7fc3fa923e5d6a6fd2efe8df852a36256b5b for additional details.
+ case CL_EFORMAT:
+ case CL_EPARSE:
+ case CL_EREAD:
+ case CL_EUNPACK:
+
+ default:
+ cli_dbgmsg("Descriptor[%d]: Continuing after file scan resulted with: %s\n",
+ fmap_fd(ctx->fmap), cl_strerror(result_in));
+ *result_out = CL_SUCCESS;
+ }
+
+done:
+ return halt_scan;
+}
+
cl_error_t cli_magic_scan(cli_ctx *ctx, cli_file_t type)
{
cl_error_t ret = CL_CLEAN;
- cl_error_t res;
- cl_error_t cb_retcode;
+ cl_error_t cache_check_result;
+ cl_error_t verdict_at_this_level;
cli_file_t dettype = 0;
uint8_t typercg = 1;
size_t hashed_size = 0;
unsigned char *hash = NULL;
bitset_t *old_hook_lsig_matches = NULL;
const char *filetype;
- int cache_clean = 0;
+
#if HAVE_JSON
struct json_object *parent_property = NULL;
#else
@@ -4150,7 +4164,7 @@ cl_error_t cli_magic_scan(cli_ctx *ctx, cli_file_t type)
}
if (ctx->fmap->len <= 5) {
- cli_dbgmsg("cli_magic_scandesc: File is too too small (%zu bytes), ignoring.\n", ctx->fmap->len);
+ cli_dbgmsg("cli_magic_scan: File is too too small (%zu bytes), ignoring.\n", ctx->fmap->len);
ret = CL_CLEAN;
goto early_ret;
}
@@ -4210,7 +4224,17 @@ cl_error_t cli_magic_scan(cli_ctx *ctx, cli_file_t type)
*/
perf_start(ctx, PERFT_FT);
if ((type == CL_TYPE_ANY) || type == CL_TYPE_PART_ANY) {
- type = cli_determine_fmap_type(ctx->fmap, ctx->engine, type);
+ cli_file_t detected_type;
+ detected_type = cli_determine_fmap_type(ctx->fmap, ctx->engine, type);
+ if ((detected_type != CL_TYPE_ZIPSFX) &&
+ (detected_type != CL_TYPE_ARJSFX) &&
+ (detected_type != CL_TYPE_RARSFX) &&
+ (detected_type != CL_TYPE_EGGSFX) &&
+ (detected_type != CL_TYPE_CABSFX) &&
+ (detected_type != CL_TYPE_7ZSFX)) {
+ /* Omit SFX archive types selected. We'll detect these in scanraw() */
+ type = detected_type;
+ }
}
perf_stop(ctx, PERFT_FT);
if (type == CL_TYPE_ERROR) {
@@ -4301,21 +4325,23 @@ cl_error_t cli_magic_scan(cli_ctx *ctx, cli_file_t type)
}
#endif
+ /*
+ * Run the pre_scan callback.
+ */
ret = dispatch_prescan_callback(ctx->engine->cb_pre_cache, ctx, filetype);
- if (CL_CLEAN != ret) {
- if (ret == CL_VIRUS) {
- ret = cli_check_fp(ctx, NULL);
- } else {
- ret = CL_CLEAN;
- }
+ if (CL_VERIFIED == ret || CL_VIRUS == ret) {
goto done;
}
/*
* Get the maphash
*/
- if (CL_SUCCESS != fmap_get_MD5(ctx->fmap, &hash)) {
+ if (CL_SUCCESS != fmap_get_hash(ctx->fmap, &hash, CLI_HASH_MD5)) {
cli_dbgmsg("cli_magic_scan: Failed to get a hash for the current fmap.\n");
+
+ // It may be that the file was truncated between the time we started the scan and the time we got the hash.
+ // Not a reason to print an error message.
+ ret = CL_SUCCESS;
goto done;
}
hashed_size = ctx->fmap->len;
@@ -4324,22 +4350,20 @@ cl_error_t cli_magic_scan(cli_ctx *ctx, cli_file_t type)
* Check if we've already scanned this file before.
*/
perf_start(ctx, PERFT_CACHE);
-
- if (!(SCAN_COLLECT_METADATA))
- res = clean_cache_check(hash, hashed_size, ctx);
- else
- res = CL_VIRUS;
+ cache_check_result = clean_cache_check(hash, hashed_size, ctx);
+ perf_stop(ctx, PERFT_CACHE);
#if HAVE_JSON
- if (SCAN_COLLECT_METADATA /* ctx.options->general & CL_SCAN_GENERAL_COLLECT_METADATA && ctx->wrkproperty != NULL */) {
- char hashstr[33];
- snprintf(hashstr, 33, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ if (SCAN_COLLECT_METADATA) {
+ char hashstr[CLI_HASHLEN_MD5 * 2 + 1];
+ snprintf(hashstr, CLI_HASHLEN_MD5 * 2 + 1, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7],
hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15]);
ret = cli_jsonstr(ctx->wrkproperty, "FileMD5", hashstr);
- if (ctx->engine->engine_options & ENGINE_OPTIONS_DISABLE_CACHE)
- memset(hash, 0, 16);
+ if (ctx->engine->engine_options & ENGINE_OPTIONS_DISABLE_CACHE) {
+ memset(hash, 0, CLI_HASHLEN_MD5);
+ }
if (ret != CL_SUCCESS) {
cli_dbgmsg("cli_magic_scan: returning %d %s (no post, no cache)\n", ret, __AT__);
goto early_ret;
@@ -4347,9 +4371,7 @@ cl_error_t cli_magic_scan(cli_ctx *ctx, cli_file_t type)
}
#endif
- perf_stop(ctx, PERFT_CACHE);
-
- if (res != CL_VIRUS) {
+ if (cache_check_result != CL_VIRUS) {
cli_dbgmsg("cli_magic_scan: returning %d %s (no post, no cache)\n", ret, __AT__);
goto early_ret;
}
@@ -4357,33 +4379,22 @@ cl_error_t cli_magic_scan(cli_ctx *ctx, cli_file_t type)
old_hook_lsig_matches = ctx->hook_lsig_matches;
ctx->hook_lsig_matches = NULL;
+ /*
+ * Run the pre_scan callback.
+ */
+ ret = dispatch_prescan_callback(ctx->engine->cb_pre_scan, ctx, filetype);
+ if (CL_VERIFIED == ret || CL_VIRUS == ret) {
+ goto done;
+ }
+
+ // If none of the scan options are enabled, then we can skip parsing and just do a raw pattern match.
+ // For this check, we don't care if the CL_SCAN_GENERAL_ALLMATCHES option is enabled, hence the `~`.
if (!((ctx->options->general & ~CL_SCAN_GENERAL_ALLMATCHES) || (ctx->options->parse) || (ctx->options->heuristic) || (ctx->options->mail) || (ctx->options->dev))) {
/*
* Scanning in raw mode (stdin, etc.)
*/
- ret = dispatch_prescan_callback(ctx->engine->cb_pre_scan, ctx, filetype);
- if (CL_CLEAN != ret) {
- if (ret == CL_VIRUS) {
- ret = cli_check_fp(ctx, NULL);
- } else if (ret == CL_BREAK) {
- ret = CL_CLEAN;
- }
- goto done;
- }
-
- if (CL_VIRUS == (ret = cli_scan_fmap(ctx, CL_TYPE_ANY, 0, NULL, AC_SCAN_VIR, NULL, hash)))
- cli_dbgmsg("cli_magic_scan: %s found in descriptor %d\n", cli_get_last_virus(ctx), fmap_fd(ctx->fmap));
-
- goto done;
- }
-
- ret = dispatch_prescan_callback(ctx->engine->cb_pre_scan, ctx, filetype);
- if (CL_CLEAN != ret) {
- if (ret == CL_VIRUS) {
- ret = cli_check_fp(ctx, NULL);
- } else if (ret == CL_BREAK) {
- ret = CL_CLEAN;
- }
+ ret = cli_scan_fmap(ctx, CL_TYPE_ANY, false, NULL, AC_SCAN_VIR, NULL, hash);
+ // It doesn't matter what was returned, always go to the end after this. Raw mode! No parsing files!
goto done;
}
@@ -4395,7 +4406,7 @@ cl_error_t cli_magic_scan(cli_ctx *ctx, cli_file_t type)
// We already saved the hook_lsig_matches (above)
// The ctx one is NULL at present.
ctx->hook_lsig_matches = cli_bitset_init();
- if (!ctx->hook_lsig_matches) {
+ if (NULL == ctx->hook_lsig_matches) {
ret = CL_EMEM;
goto done;
}
@@ -4406,8 +4417,10 @@ cl_error_t cli_magic_scan(cli_ctx *ctx, cli_file_t type)
* before extracting with a file type parser.
*/
ret = scanraw(ctx, type, 0, &dettype, (ctx->engine->engine_options & ENGINE_OPTIONS_DISABLE_CACHE) ? NULL : hash);
- if (ret == CL_EMEM || ret == CL_VIRUS) {
- ret = cli_check_fp(ctx, NULL);
+
+ // Evaluate the result from the scan to see if it end the scan of this layer early,
+ // and to decid if we should propagate an error or not.
+ if (result_should_goto_done(ctx, ret, &ret)) {
goto done;
}
}
@@ -4778,79 +4791,35 @@ cl_error_t cli_magic_scan(cli_ctx *ctx, cli_file_t type)
}
perf_nested_stop(ctx, PERFT_CONTAINER, PERFT_SCAN);
+ // Evaluate the result from the parsers to see if it end the scan of this layer early,
+ // and to decid if we should propagate an error or not.
+ if (result_should_goto_done(ctx, ret, &ret)) {
+ goto done;
+ }
+
/*
* Perform the raw scan, which may include file type recognition signatures.
*/
- if ((ret == CL_VIRUS && !SCAN_ALLMATCHES) ||
- (ctx->abort_scan)) {
- goto done;
- }
/* Disable type recognition for the raw scan for zip files larger than maxziptypercg */
if (type == CL_TYPE_ZIP && SCAN_PARSE_ARCHIVE && (DCONF_ARCH & ARCH_CONF_ZIP)) {
/* CL_ENGINE_MAX_ZIPTYPERCG */
uint64_t curr_len = ctx->fmap->len;
if (curr_len > ctx->engine->maxziptypercg) {
- cli_dbgmsg("cli_magic_scan_desc: Not checking for embedded PEs (zip file > MaxZipTypeRcg)\n");
+ cli_dbgmsg("cli_magic_scan: Not checking for embedded PEs (zip file > MaxZipTypeRcg)\n");
typercg = 0;
}
}
/* CL_TYPE_HTML: raw HTML files are not scanned, unless safety measure activated via DCONF */
if (type != CL_TYPE_IGNORED && (type != CL_TYPE_HTML || !(SCAN_PARSE_HTML) || !(DCONF_DOC & DOC_CONF_HTML_SKIPRAW)) && !ctx->engine->sdb) {
- res = scanraw(ctx, type, typercg, &dettype, (ctx->engine->engine_options & ENGINE_OPTIONS_DISABLE_CACHE) ? NULL : hash);
- if (res != CL_CLEAN) {
- switch (res) {
- /* List of scan halts, runtime errors only! */
- case CL_EUNLINK:
- case CL_ESTAT:
- case CL_ESEEK:
- case CL_EWRITE:
- case CL_EDUP:
- case CL_ETMPFILE:
- case CL_ETMPDIR:
- case CL_EMEM:
- cli_dbgmsg("Descriptor[%d]: scanraw error %s\n", fmap_fd(ctx->fmap), cl_strerror(res));
- ret = res;
- goto done;
- /* CL_VIRUS = malware found, check FP and report.
- * Likewise, if the file was determined to be trusted, then we
- * can also finish with the scan. (Ex: EXE with a valid
- * Authenticode sig.) */
- case CL_VERIFIED:
- // For now just conver CL_VERIFIED to CL_CLEAN, since
- // CL_VERIFIED isn't used elsewhere
- res = CL_CLEAN;
- // Fall through
- case CL_VIRUS:
- ret = res;
- if (SCAN_ALLMATCHES)
- break;
- goto done;
- /* All other "MAX" conditions should still fully scan the current file */
- case CL_ETIMEOUT:
- case CL_EMAXREC:
- case CL_EMAXSIZE:
- case CL_EMAXFILES:
- ret = res;
- cli_dbgmsg("Descriptor[%d]: Continuing after scanraw reached %s\n",
- fmap_fd(ctx->fmap), cl_strerror(res));
- break;
- /* Other errors must not block further scans below
- * This specifically includes CL_EFORMAT & CL_EREAD & CL_EUNPACK
- * Malformed/truncated files could report as any of these three.
- */
- default:
- ret = res;
- cli_dbgmsg("Descriptor[%d]: Continuing after scanraw error %s\n",
- fmap_fd(ctx->fmap), cl_strerror(res));
- }
- }
- }
+ ret = scanraw(ctx, type, typercg, &dettype, (ctx->engine->engine_options & ENGINE_OPTIONS_DISABLE_CACHE) ? NULL : hash);
- /* Make sure we bail out if required. */
- if (ctx->abort_scan) {
- goto done;
+ // Evaluate the result from the scan to see if it end the scan of this layer early,
+ // and to decid if we should propagate an error or not.
+ if (result_should_goto_done(ctx, ret, &ret)) {
+ goto done;
+ }
}
/*
@@ -4864,83 +4833,62 @@ cl_error_t cli_magic_scan(cli_ctx *ctx, cli_file_t type)
case CL_TYPE_TEXT_UTF16LE:
case CL_TYPE_TEXT_UTF8:
perf_nested_start(ctx, PERFT_SCRIPT, PERFT_SCAN);
- if ((DCONF_DOC & DOC_CONF_SCRIPT) && dettype != CL_TYPE_HTML && (ret != CL_VIRUS || SCAN_ALLMATCHES) && SCAN_PARSE_HTML)
+ if ((dettype != CL_TYPE_HTML) &&
+ SCAN_PARSE_HTML && (DCONF_DOC & DOC_CONF_SCRIPT) && (ret != CL_VIRUS)) {
ret = cli_scanscript(ctx);
- if (SCAN_PARSE_MAIL && (DCONF_MAIL & MAIL_CONF_MBOX) && ret != CL_VIRUS && (cli_recursion_stack_get_type(ctx, -1) == CL_TYPE_MAIL || dettype == CL_TYPE_MAIL)) {
- ret = cli_scan_fmap(ctx, CL_TYPE_MAIL, 0, NULL, AC_SCAN_VIR, NULL, NULL);
+ }
+ if (((dettype == CL_TYPE_MAIL) || (cli_recursion_stack_get_type(ctx, -1) == CL_TYPE_MAIL)) &&
+ SCAN_PARSE_MAIL && (DCONF_MAIL & MAIL_CONF_MBOX) && (ret != CL_VIRUS)) {
+ ret = cli_scan_fmap(ctx, CL_TYPE_MAIL, false, NULL, AC_SCAN_VIR, NULL, NULL);
}
perf_nested_stop(ctx, PERFT_SCRIPT, PERFT_SCAN);
break;
+
/* Due to performance reasons all executables were first scanned
* in raw mode. Now we will try to unpack them
*/
case CL_TYPE_MSEXE:
perf_nested_start(ctx, PERFT_PE, PERFT_SCAN);
if (SCAN_PARSE_PE && ctx->dconf->pe) {
+ // Setting ctx->corrupted_input will prevent the PE parser from reporting "broken executable" for unpacked/reconstructed files that may not be 100% to spec.
+ // In here we're just carrying the corrupted_input flag from parent to child, in case the parent's flag was set.
unsigned int corrupted_input = ctx->corrupted_input;
ret = cli_scanpe(ctx);
ctx->corrupted_input = corrupted_input;
}
perf_nested_stop(ctx, PERFT_PE, PERFT_SCAN);
break;
+
case CL_TYPE_ELF:
perf_nested_start(ctx, PERFT_ELF, PERFT_SCAN);
ret = cli_unpackelf(ctx);
perf_nested_stop(ctx, PERFT_ELF, PERFT_SCAN);
break;
+
case CL_TYPE_MACHO:
case CL_TYPE_MACHO_UNIBIN:
perf_nested_start(ctx, PERFT_MACHO, PERFT_SCAN);
ret = cli_unpackmacho(ctx);
perf_nested_stop(ctx, PERFT_MACHO, PERFT_SCAN);
break;
+
case CL_TYPE_BINARY_DATA:
- ret = cli_scan_fmap(ctx, CL_TYPE_OTHER, 0, NULL, AC_SCAN_VIR, NULL, NULL);
+ ret = cli_scan_fmap(ctx, CL_TYPE_OTHER, false, NULL, AC_SCAN_VIR, NULL, NULL);
break;
+
case CL_TYPE_PDF: /* FIXMELIMITS: pdf should be an archive! */
if (SCAN_PARSE_PDF && (DCONF_DOC & DOC_CONF_PDF))
ret = cli_scanpdf(ctx, 0);
break;
+
default:
break;
}
done:
- switch (ret) {
- /*
- * Limits exceeded
- */
- // Exceeding these maximums means we have to stop scanning:
- case CL_ETIMEOUT:
- case CL_EMAXFILES:
- ctx->abort_scan = true;
- cli_dbgmsg("Descriptor[%d]: %s\n", fmap_fd(ctx->fmap), cl_strerror(ret));
- ret = CL_CLEAN;
- break;
- // Exceeding these maximums means we had to skip an embedded file:
- case CL_EMAXREC:
- case CL_EMAXSIZE:
- cli_dbgmsg("Descriptor[%d]: %s\n", fmap_fd(ctx->fmap), cl_strerror(ret));
- ret = CL_CLEAN;
- break;
-
- /*
- * Malformed file cases
- */
- case CL_EFORMAT:
- case CL_EREAD:
- case CL_EUNPACK:
- cli_dbgmsg("Descriptor[%d]: %s\n", fmap_fd(ctx->fmap), cl_strerror(ret));
- ret = CL_CLEAN;
- break;
-
- case CL_CLEAN:
- cache_clean = 1;
- break;
-
- default:
- break;
- }
+ // Filter the result from the parsers so we don't propagate non-fatal errors.
+ // And to convert CL_VERIFIED -> CL_CLEAN
+ (void)result_should_goto_done(ctx, ret, &ret);
if (old_hook_lsig_matches) {
/* We need to restore the old hook_lsig_matches */
@@ -4952,54 +4900,70 @@ cl_error_t cli_magic_scan(cli_ctx *ctx, cli_file_t type)
ctx->wrkproperty = (struct json_object *)(parent_property);
#endif
- if ((ret == CL_SUCCESS) &&
- (evidence_num_alerts(ctx->evidence) > 0)) {
- cb_retcode = CL_VIRUS;
+ /*
+ * Determine if there was an alert for this layer (or its children).
+ */
+ if ((evidence_num_alerts(ctx->evidence) > 0)) {
+ // TODO: Bug here.
+ // If there was a PUA match in a previous file in a zip, all subsequent files will
+ // think they have a match.
+ // In allmatch mode, this affects strong sigs too, not just PUA sigs.
+ // The only way to solve this is to keep track of the # of alerts for each layer,
+ // including only children layers and propagating the evidence up to the parent layer
+ // only at the end, after the cache_add.
+ verdict_at_this_level = CL_VIRUS;
} else {
- cb_retcode = ret;
+ verdict_at_this_level = ret;
}
- cli_dbgmsg("cli_magic_scan_desc: returning %d %s\n", ret, __AT__);
+ /*
+ * Run the post-scan callback (if one exists) and provide the verdict for this layer.
+ */
+ cli_dbgmsg("cli_magic_scan: returning %d %s\n", ret, __AT__);
if (ctx->engine->cb_post_scan) {
- cl_error_t callbacK_ret;
+ cl_error_t callback_ret;
const char *virusname = NULL;
- if (cb_retcode == CL_VIRUS)
+ // Get the last signature that matched (if any).
+ if (verdict_at_this_level == CL_VIRUS) {
virusname = cli_get_last_virus(ctx);
+ }
perf_start(ctx, PERFT_POSTCB);
- callbacK_ret = ctx->engine->cb_post_scan(fmap_fd(ctx->fmap), cb_retcode, virusname, ctx->cb_ctx);
+ callback_ret = ctx->engine->cb_post_scan(fmap_fd(ctx->fmap), verdict_at_this_level, virusname, ctx->cb_ctx);
perf_stop(ctx, PERFT_POSTCB);
- switch (callbacK_ret) {
+ switch (callback_ret) {
case CL_BREAK:
- cli_dbgmsg("cli_magic_scan_desc: file allowed by post_scan callback\n");
+ cli_dbgmsg("cli_magic_scan: file allowed by post_scan callback\n");
ret = CL_CLEAN;
break;
case CL_VIRUS:
- cli_dbgmsg("cli_magic_scan_desc: file blocked by post_scan callback\n");
- cli_append_virus(ctx, "Detected.By.Callback");
- if (ret != CL_VIRUS) {
- ret = cli_check_fp(ctx, NULL);
+ cli_dbgmsg("cli_magic_scan: file blocked by post_scan callback\n");
+ callback_ret = cli_append_virus(ctx, "Detected.By.Callback");
+ if (callback_ret == CL_VIRUS) {
+ ret = CL_VIRUS;
}
break;
case CL_CLEAN:
break;
default:
- cli_warnmsg("cli_magic_scan_desc: ignoring bad return code from post_scan callback\n");
+ ret = CL_CLEAN;
+ cli_warnmsg("cli_magic_scan: ignoring bad return code from post_scan callback\n");
}
}
- if (cb_retcode == CL_CLEAN && cache_clean) {
+ /*
+ * If the verdict for this layer is "clean", we can cache it.
+ */
+ if (verdict_at_this_level == CL_CLEAN) {
+ // clean_cache_add() will check the fmap->dont_cache_flag,
+ // so this may not actually cache if we exceeded limits earlier.
perf_start(ctx, PERFT_CACHE);
clean_cache_add(hash, hashed_size, ctx);
perf_stop(ctx, PERFT_CACHE);
}
- if (ret == CL_VIRUS && SCAN_ALLMATCHES) {
- ret = CL_CLEAN;
- }
-
early_ret:
if ((ctx->engine->keeptmp) && (NULL != old_temp_path)) {
@@ -5040,7 +5004,7 @@ cl_error_t cli_magic_scan_desc_type(int desc, const char *filepath, cli_ctx *ctx
cli_dbgmsg("in cli_magic_scan_desc_type (recursion_level: %u/%u)\n", ctx->recursion_level, ctx->engine->max_recursion_level);
if (FSTAT(desc, &sb) == -1) {
- cli_errmsg("cli_magic_scan: Can't fstat descriptor %d\n", desc);
+ cli_errmsg("cli_magic_scan_desc_type: Can't fstat descriptor %d\n", desc);
status = CL_ESTAT;
cli_dbgmsg("cli_magic_scan_desc_type: returning %d %s (no post, no cache)\n", status, __AT__);
@@ -5272,11 +5236,14 @@ cl_error_t cli_magic_scan_buff(const void *buffer, size_t length, cli_ctx *ctx,
*/
static cl_error_t scan_common(cl_fmap_t *map, const char *filepath, const char **virname, unsigned long int *scanned, const struct cl_engine *engine, struct cl_scan_options *scanoptions, void *context)
{
- cl_error_t status;
+ cl_error_t status = CL_SUCCESS;
+ cl_error_t ret;
cl_error_t verdict = CL_CLEAN;
cli_ctx ctx = {0};
+ bool logg_initalized = false;
+
char *target_basename = NULL;
char *new_temp_prefix = NULL;
size_t new_temp_prefix_len;
@@ -5289,6 +5256,8 @@ static cl_error_t scan_common(cl_fmap_t *map, const char *filepath, const char *
return CL_ENULLARG;
}
+ size_t num_potentially_unwanted_indicators = 0;
+
*virname = NULL;
ctx.engine = engine;
@@ -5404,6 +5373,7 @@ static cl_error_t scan_common(cl_fmap_t *map, const char *filepath, const char *
}
cli_logg_setup(&ctx);
+ logg_initalized = true;
/* We have a limit of around 2GB (INT_MAX - 2). Enforce it here. */
/* TODO: Large file support is large-ly untested. Remove this restriction
@@ -5413,7 +5383,7 @@ static cl_error_t scan_common(cl_fmap_t *map, const char *filepath, const char *
* bound to behave badly with large files. */
if (map->len > INT_MAX - 2) {
if (scanoptions->heuristic & CL_SCAN_HEURISTIC_EXCEEDS_MAX) {
- status = cli_append_virus(&ctx, "Heuristics.Limits.Exceeded.MaxFileSize");
+ status = cli_append_potentially_unwanted(&ctx, "Heuristics.Limits.Exceeded.MaxFileSize");
} else {
status = CL_CLEAN;
}
@@ -5422,21 +5392,55 @@ static cl_error_t scan_common(cl_fmap_t *map, const char *filepath, const char *
status = cli_magic_scan(&ctx, CL_TYPE_ANY);
- // Set the output pointer to the "latest" alert signature name.
- *virname = cli_get_last_virus_str(&ctx);
-
+ // If any alerts occurred, set the output pointer to the "latest" alert signature name.
if (0 < evidence_num_alerts(ctx.evidence)) {
- verdict = CL_VIRUS;
+ *virname = cli_get_last_virus_str(&ctx);
+ verdict = CL_VIRUS;
}
- if (!(ctx.options->general & CL_SCAN_GENERAL_HEURISTIC_PRECEDENCE) &&
- (0 == evidence_num_indicators_type(ctx.evidence, IndicatorType_Strong)) &&
- (0 != evidence_num_indicators_type(ctx.evidence, IndicatorType_PotentiallyUnwanted))) {
- // "Heuristic precedence" mode not enabled, and the only alerts so far have been PUA.
- // But Heuristic-signatures / PUA sigs were recorded but never reported...
- // ... Now is the time to report them!
- // TODO: Report more than one if in ALLMATCH mode. For now, just reporting the "latest".
- cli_virus_found_cb(&ctx, cli_get_last_virus(&ctx));
+ /*
+ * Report PUA alerts here.
+ */
+ num_potentially_unwanted_indicators = evidence_num_indicators_type(
+ ctx.evidence,
+ IndicatorType_PotentiallyUnwanted);
+ if (0 != num_potentially_unwanted_indicators) {
+ // We have "potentially unwanted" indicators that would not have been reported yet.
+ // We may wish to report them now, ... depending ....
+
+ if (ctx.options->general & CL_SCAN_GENERAL_ALLMATCHES) {
+ // We're in allmatch mode, so report all "potentially unwanted" matches now.
+
+ size_t i;
+
+ for (i = 0; i < num_potentially_unwanted_indicators; i++) {
+ const char *pua_alert = evidence_get_indicator(
+ ctx.evidence,
+ IndicatorType_PotentiallyUnwanted,
+ i);
+
+ if (NULL != pua_alert) {
+ // We don't know exactly which layer the alert happened at.
+ // There's a decent chance it wasn't at this layer, and in that case we wouldn't
+ // even have access to that file anymore (it's gone!). So we'll pass back -1 for the
+ // file descriptor rather than using `cli_virus_found_cb() which would pass back
+ // The top level file descriptor.
+ if (ctx.engine->cb_virus_found) {
+ ctx.engine->cb_virus_found(
+ -1,
+ pua_alert,
+ ctx.cb_ctx);
+ }
+ }
+ }
+
+ } else {
+ // Not allmatch mode. Only want to report one thing...
+ if (0 == evidence_num_indicators_type(ctx.evidence, IndicatorType_Strong)) {
+ // And it looks like we haven't reported anything else, so report the last "potentially unwanted" one.
+ cli_virus_found_cb(&ctx, cli_get_last_virus(&ctx));
+ }
+ }
}
#if HAVE_JSON
@@ -5465,62 +5469,72 @@ static cl_error_t scan_common(cl_fmap_t *map, const char *filepath, const char *
if (NULL == jstring) {
cli_errmsg("scan_common: no memory for json serialization.\n");
status = CL_EMEM;
- } else {
- int ret = CL_SUCCESS;
+ goto done;
+ }
+
+ cli_dbgmsg("%s\n", jstring);
+
+ if (status != CL_VIRUS) {
+ /*
+ * Run bytecode preclass hook.
+ */
struct cli_matcher *iroot = ctx.engine->root[13];
- cli_dbgmsg("%s\n", jstring);
- if ((status != CL_VIRUS) || (ctx.options->general & CL_SCAN_GENERAL_ALLMATCHES)) {
- /* run bytecode preclass hook; generate fmap if needed for running hook */
- struct cli_bc_ctx *bc_ctx = cli_bytecode_context_alloc();
- if (!bc_ctx) {
- cli_errmsg("scan_common: can't allocate memory for bc_ctx\n");
- status = CL_EMEM;
- } else {
- cli_bytecode_context_setctx(bc_ctx, &ctx);
- status = cli_bytecode_runhook(&ctx, ctx.engine, bc_ctx, BC_PRECLASS, map);
- cli_bytecode_context_destroy(bc_ctx);
- }
+ struct cli_bc_ctx *bc_ctx = cli_bytecode_context_alloc();
+ if (!bc_ctx) {
+ cli_errmsg("scan_common: can't allocate memory for bc_ctx\n");
+ status = CL_EMEM;
+ } else {
+ cli_bytecode_context_setctx(bc_ctx, &ctx);
+ status = cli_bytecode_runhook(&ctx, ctx.engine, bc_ctx, BC_PRECLASS, map);
+ cli_bytecode_context_destroy(bc_ctx);
+ }
- /* backwards compatibility: scan the json string unless a virus was detected */
- if (status != CL_VIRUS && (iroot->ac_lsigs || iroot->ac_patterns
+ /* backwards compatibility: scan the json string unless a virus was detected */
+ if (status != CL_VIRUS && (iroot->ac_lsigs || iroot->ac_patterns
#ifdef HAVE_PCRE
- || iroot->pcre_metas
+ || iroot->pcre_metas
#endif // HAVE_PCRE
- )) {
- cli_dbgmsg("scan_common: running deprecated preclass bytecodes for target type 13\n");
- ctx.options->general &= ~CL_SCAN_GENERAL_COLLECT_METADATA;
- status = cli_magic_scan_buff(jstring, strlen(jstring), &ctx, NULL);
- }
+ )) {
+ cli_dbgmsg("scan_common: running deprecated preclass bytecodes for target type 13\n");
+ ctx.options->general &= ~CL_SCAN_GENERAL_COLLECT_METADATA;
+ status = cli_magic_scan_buff(jstring, strlen(jstring), &ctx, NULL);
}
+ }
- /* Invoke file props callback */
- if (ctx.engine->cb_file_props != NULL) {
- ret = ctx.engine->cb_file_props(jstring, status, ctx.cb_ctx);
- if (ret != CL_SUCCESS)
- status = ret;
+ /*
+ * Invoke file props callback.
+ */
+ if (ctx.engine->cb_file_props != NULL) {
+ ret = ctx.engine->cb_file_props(jstring, status, ctx.cb_ctx);
+ if (ret != CL_SUCCESS) {
+ status = ret;
}
+ }
- /* keeptmp file processing for file properties json string */
- if (ctx.engine->keeptmp) {
- int fd = -1;
- char *tmpname = NULL;
+ /*
+ * Write the file properties metadata JSON to metadata.json if keeptmp is enabled.
+ */
+ if (ctx.engine->keeptmp) {
+ int fd = -1;
+ char *tmpname = NULL;
- if ((ret = cli_newfilepathfd(ctx.sub_tmpdir, "metadata.json", &tmpname, &fd)) != CL_SUCCESS) {
- cli_dbgmsg("scan_common: Can't create json properties file, ret = %i.\n", ret);
+ if ((ret = cli_newfilepathfd(ctx.sub_tmpdir, "metadata.json", &tmpname, &fd)) != CL_SUCCESS) {
+ cli_dbgmsg("scan_common: Can't create json properties file, ret = %i.\n", ret);
+ } else {
+ if ((size_t)-1 == cli_writen(fd, jstring, strlen(jstring))) {
+ cli_dbgmsg("scan_common: cli_writen error writing json properties file.\n");
} else {
- if (cli_writen(fd, jstring, strlen(jstring)) == (size_t)-1)
- cli_dbgmsg("scan_common: cli_writen error writing json properties file.\n");
- else
- cli_dbgmsg("json written to: %s\n", tmpname);
+ cli_dbgmsg("json written to: %s\n", tmpname);
}
- if (fd != -1)
- close(fd);
- if (NULL != tmpname)
- free(tmpname);
+ }
+ if (fd != -1) {
+ close(fd);
+ }
+ if (NULL != tmpname) {
+ free(tmpname);
}
}
- cli_json_delobj(ctx.properties); /* frees all json memory */
}
#endif // HAVE_JSON
@@ -5530,9 +5544,15 @@ static cl_error_t scan_common(cl_fmap_t *map, const char *filepath, const char *
status = verdict;
}
- cli_logg_unsetup();
-
done:
+ if (logg_initalized) {
+ cli_logg_unsetup();
+ }
+
+ if (NULL != ctx.properties) {
+ cli_json_delobj(ctx.properties);
+ }
+
if (NULL != ctx.sub_tmpdir) {
if (!ctx.engine->keeptmp) {
(void)cli_rmdirs(ctx.sub_tmpdir);
diff --git a/libclamav/sis.c b/libclamav/sis.c
index 28e89cdd1a..08e24df1c0 100644
--- a/libclamav/sis.c
+++ b/libclamav/sis.c
@@ -517,8 +517,8 @@ static cl_error_t real_scansis(cli_ctx *ctx, const char *tmpd)
FREE(decomp);
- if (cli_magic_scan_desc(fd, ofn, ctx, original_filepath) == CL_VIRUS) {
- status = CL_VIRUS;
+ status = cli_magic_scan_desc(fd, ofn, ctx, original_filepath);
+ if (CL_SUCCESS != status) {
goto done;
}
@@ -713,6 +713,8 @@ static inline void seeknext(struct SISTREAM *s)
static cl_error_t real_scansis9x(cli_ctx *ctx, const char *tmpd)
{
+ cl_error_t ret;
+
struct SISTREAM stream;
struct SISTREAM *s = &stream;
uint32_t field, optst[] = {T_CONTROLLERCHECKSUM, T_DATACHECKSUM, T_COMPRESSED};
@@ -840,9 +842,10 @@ static cl_error_t real_scansis9x(cli_ctx *ctx, const char *tmpd)
break;
}
free(dst);
- if (cli_magic_scan_desc(fd, tempf, ctx, NULL) == CL_VIRUS) {
+ ret = cli_magic_scan_desc(fd, tempf, ctx, NULL);
+ if (CL_SUCCESS != ret) {
close(fd);
- return CL_VIRUS;
+ return ret;
}
close(fd);
break;
diff --git a/libclamav/special.c b/libclamav/special.c
index fe3ba3efbb..943a969499 100644
--- a/libclamav/special.c
+++ b/libclamav/special.c
@@ -89,7 +89,7 @@ int cli_check_mydoom_log(cli_ctx *ctx)
if ((~check) != key)
return CL_CLEAN;
- return cli_append_virus(ctx, "Heuristics.Worm.Mydoom.M.log");
+ return cli_append_potentially_unwanted(ctx, "Heuristics.Worm.Mydoom.M.log");
}
static uint32_t riff_endian_convert_32(uint32_t value, int big_endian)
diff --git a/libclamav/swf.c b/libclamav/swf.c
index 5f3966b8c4..49a9e2e5db 100644
--- a/libclamav/swf.c
+++ b/libclamav/swf.c
@@ -291,7 +291,7 @@ static cl_error_t scanzws(cli_ctx *ctx, struct swf_file_hdr *hdr)
ret = cli_magic_scan_desc(fd, tmpname, ctx, NULL);
close(fd);
- if (!(ctx->engine->keeptmp)) {
+ if (!ctx->engine->keeptmp) {
if (cli_unlink(tmpname)) {
free(tmpname);
return CL_EUNLINK;
diff --git a/libclamav/tiff.c b/libclamav/tiff.c
index 35bca40546..7b95d46ff7 100644
--- a/libclamav/tiff.c
+++ b/libclamav/tiff.c
@@ -78,8 +78,7 @@ cl_error_t cli_parsetiff(cli_ctx *ctx)
/* acquire offset of first IFD */
if (fmap_readn(map, &offset, offset, 4) != 4) {
cli_dbgmsg("cli_parsetiff: Failed to acquire offset of first IFD, file appears to be truncated.\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.TIFF.EOFReadingFirstIFDOffset");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.TIFF.EOFReadingFirstIFDOffset");
goto done;
}
/* offset of the first IFD */
@@ -89,8 +88,7 @@ cl_error_t cli_parsetiff(cli_ctx *ctx)
if (!offset) {
cli_errmsg("cli_parsetiff: Invalid offset for first IFD\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.TIFF.InvalidIFDOffset");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.TIFF.InvalidIFDOffset");
goto done;
}
@@ -99,8 +97,7 @@ cl_error_t cli_parsetiff(cli_ctx *ctx)
/* acquire number of directory entries in current IFD */
if (fmap_readn(map, &num_entries, offset, 2) != 2) {
cli_dbgmsg("cli_parsetiff: Failed to acquire number of directory entries in current IFD, file appears to be truncated.\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.TIFF.EOFReadingNumIFDDirectoryEntries");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.TIFF.EOFReadingNumIFDDirectoryEntries");
goto done;
}
offset += 2;
@@ -112,8 +109,7 @@ cl_error_t cli_parsetiff(cli_ctx *ctx)
for (i = 0; i < num_entries; i++) {
if (fmap_readn(map, &entry, offset, sizeof(entry)) != sizeof(entry)) {
cli_dbgmsg("cli_parsetiff: Failed to read next IFD entry, file appears to be truncated.\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.TIFF.EOFReadingIFDEntry");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.TIFF.EOFReadingIFDEntry");
goto done;
}
offset += sizeof(entry);
@@ -175,7 +171,7 @@ cl_error_t cli_parsetiff(cli_ctx *ctx)
if (entry.value + value_size > map->len) {
cli_warnmsg("cli_parsetiff: TFD entry field %u exceeds bounds of TIFF file [%llu > %llu]\n",
i, (long long unsigned)(entry.value + value_size), (long long unsigned)map->len);
- status = cli_append_virus(ctx, "Heuristics.Broken.Media.TIFF.OutOfBoundsAccess");
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.TIFF.OutOfBoundsAccess");
goto done;
}
}
@@ -188,8 +184,7 @@ cl_error_t cli_parsetiff(cli_ctx *ctx)
/* acquire next IFD location, gets 0 if last IFD */
if (fmap_readn(map, &offset, offset, sizeof(offset)) != sizeof(offset)) {
cli_dbgmsg("cli_parsetiff: Failed to aquire next IFD location, file appears to be truncated.\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.TIFF.EOFReadingChunkCRC");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.TIFF.EOFReadingChunkCRC");
goto done;
}
offset = tiff32_to_host(big_endian, offset);
@@ -198,8 +193,7 @@ cl_error_t cli_parsetiff(cli_ctx *ctx)
/*If the offsets are not in order, that is suspicious.*/
if (last_offset >= offset) {
cli_dbgmsg("cli_parsetiff: Next offset is before current offset, file appears to be malformed.\n");
- cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.TIFF.OutOfOrderIFDOffset");
- status = CL_EPARSE;
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Broken.Media.TIFF.OutOfOrderIFDOffset");
goto done;
}
}
@@ -210,10 +204,6 @@ cl_error_t cli_parsetiff(cli_ctx *ctx)
status = CL_CLEAN;
done:
- if (status == CL_EPARSE) {
- /* We added with cli_append_potentially_unwanted so it will alert at the end if nothing else matches. */
- status = CL_CLEAN;
- }
return status;
}
diff --git a/libclamav/untar.c b/libclamav/untar.c
index acf735bed2..949b42e4a9 100644
--- a/libclamav/untar.c
+++ b/libclamav/untar.c
@@ -138,7 +138,6 @@ cl_error_t cli_untar(const char *dir, unsigned int posix, cli_ctx *ctx)
size_t pos = 0;
size_t currsize = 0;
char zero[BLOCKSIZE];
- unsigned int num_viruses = 0;
cli_dbgmsg("In untar(%s)\n", dir);
memset(zero, 0, sizeof(zero));
@@ -175,13 +174,13 @@ cl_error_t cli_untar(const char *dir, unsigned int posix, cli_ctx *ctx)
lseek(fout, 0, SEEK_SET);
ret = cli_magic_scan_desc(fout, fullname, ctx, name);
close(fout);
- if (!ctx->engine->keeptmp)
- if (cli_unlink(fullname)) return CL_EUNLINK;
- if (ret == CL_VIRUS) {
- if (!SCAN_ALLMATCHES)
- return CL_VIRUS;
- else
- num_viruses++;
+ if (!ctx->engine->keeptmp) {
+ if (cli_unlink(fullname)) {
+ return CL_EUNLINK;
+ }
+ }
+ if (ret != CL_SUCCESS) {
+ return ret;
}
fout = -1;
}
@@ -304,10 +303,7 @@ cl_error_t cli_untar(const char *dir, unsigned int posix, cli_ctx *ctx)
strncpy(name, block, 100);
name[100] = '\0';
if (cli_matchmeta(ctx, name, size, size, 0, files, 0, NULL) == CL_VIRUS) {
- if (!SCAN_ALLMATCHES)
- return CL_VIRUS;
- else
- num_viruses++;
+ return CL_VIRUS;
}
snprintf(fullname, sizeof(fullname) - 1, "%s" PATHSEP "tar%02u", dir, files);
@@ -371,12 +367,15 @@ cl_error_t cli_untar(const char *dir, unsigned int posix, cli_ctx *ctx)
lseek(fout, 0, SEEK_SET);
ret = cli_magic_scan_desc(fout, fullname, ctx, name);
close(fout);
- if (!ctx->engine->keeptmp)
- if (cli_unlink(fullname)) return CL_EUNLINK;
- if (ret == CL_VIRUS)
- return CL_VIRUS;
+ if (!ctx->engine->keeptmp) {
+ if (cli_unlink(fullname)) {
+ return CL_EUNLINK;
+ }
+ }
+ if (ret != CL_SUCCESS) {
+ return ret;
+ }
}
- if (num_viruses)
- return CL_VIRUS;
+
return CL_CLEAN;
}
diff --git a/libclamav/unzip.c b/libclamav/unzip.c
index 1845d15831..f8dc7505e1 100644
--- a/libclamav/unzip.c
+++ b/libclamav/unzip.c
@@ -618,7 +618,6 @@ static unsigned int parse_local_file_header(
char name[256];
char *original_filename = NULL;
uint32_t csize, usize;
- int virus_found = 0;
unsigned int size_of_fileheader_and_data = 0;
if (!(local_header = fmap_need_off(map, loff, SIZEOF_LOCAL_HEADER))) {
@@ -666,9 +665,7 @@ static unsigned int parse_local_file_header(
/* Scan file header metadata. */
if (cli_matchmeta(ctx, name, LOCAL_HEADER_csize, LOCAL_HEADER_usize, (LOCAL_HEADER_flags & F_ENCR) != 0, file_count, LOCAL_HEADER_crc32, NULL) == CL_VIRUS) {
*ret = CL_VIRUS;
- if (!SCAN_ALLMATCHES)
- goto done;
- virus_found = 1;
+ goto done;
}
if (LOCAL_HEADER_flags & F_MSKED) {
@@ -681,13 +678,12 @@ static unsigned int parse_local_file_header(
if (detect_encrypted && (LOCAL_HEADER_flags & F_ENCR) && SCAN_HEURISTIC_ENCRYPTED_ARCHIVE) {
cl_error_t fp_check;
cli_dbgmsg("cli_unzip: Encrypted files found in archive.\n");
- fp_check = cli_append_virus(ctx, "Heuristics.Encrypted.Zip");
- if ((fp_check == CL_VIRUS && !SCAN_ALLMATCHES) || fp_check != CL_CLEAN) {
+ fp_check = cli_append_potentially_unwanted(ctx, "Heuristics.Encrypted.Zip");
+ if (fp_check != CL_SUCCESS) {
*ret = fp_check;
fmap_unneed_off(map, loff, SIZEOF_LOCAL_HEADER);
goto done;
}
- virus_found = 1;
}
if (LOCAL_HEADER_flags & F_USEDD) {
@@ -777,9 +773,6 @@ static unsigned int parse_local_file_header(
free(original_filename);
}
- if ((NULL != ret) && (0 != virus_found))
- *ret = CL_VIRUS;
-
return size_of_fileheader_and_data;
}
@@ -816,7 +809,6 @@ parse_central_directory_file_header(
char name[256];
int last = 0;
const uint8_t *central_header = NULL;
- int virus_found = 0;
*ret = CL_EPARSE;
@@ -854,12 +846,9 @@ parse_central_directory_file_header(
/* requests do not supply a ctx; also prevent multiple scans */
if (ctx && (CL_VIRUS == cli_matchmeta(ctx, name, CENTRAL_HEADER_csize, CENTRAL_HEADER_usize, (CENTRAL_HEADER_flags & F_ENCR) != 0, file_count, CENTRAL_HEADER_crc32, NULL))) {
- virus_found = 1;
-
- if (!SCAN_ALLMATCHES) {
- last = 1;
- goto done;
- }
+ last = 1;
+ *ret = CL_VIRUS;
+ goto done;
}
if (zsize - coff <= CENTRAL_HEADER_extra_len && !last) {
@@ -911,9 +900,6 @@ parse_central_directory_file_header(
}
done:
- if (virus_found == 1)
- *ret = CL_VIRUS;
-
if (NULL != central_header) {
fmap_unneed_ptr(map, central_header, SIZEOF_CENTRAL_HEADER);
}
@@ -986,7 +972,6 @@ cl_error_t index_the_central_directory(
struct zip_record *curr_record = NULL;
struct zip_record *prev_record = NULL;
uint32_t num_overlapping_files = 0;
- int virus_found = 0;
bool exceeded_max_files = false;
if (NULL == catalogue || NULL == num_records) {
@@ -1030,12 +1015,8 @@ cl_error_t index_the_central_directory(
}
if (ret == CL_VIRUS) {
- if (SCAN_ALLMATCHES)
- virus_found = 1;
- else {
- status = CL_VIRUS;
- goto done;
- }
+ status = CL_VIRUS;
+ goto done;
}
index++;
@@ -1049,7 +1030,7 @@ cl_error_t index_the_central_directory(
/* stop checking file entries if we'll exceed maxfiles */
if (ctx->engine->maxfiles && records_count >= ctx->engine->maxfiles) {
cli_dbgmsg("cli_unzip: Files limit reached (max: %u)\n", ctx->engine->maxfiles);
- cli_append_virus_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxFiles");
+ cli_append_potentially_unwanted_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxFiles");
exceeded_max_files = true; // Set a bool so we can return the correct status code later.
// We still need to scan the files we found while reviewing the file records up to this limit.
break;
@@ -1083,12 +1064,8 @@ cl_error_t index_the_central_directory(
} while (1);
if (ret == CL_VIRUS) {
- if (SCAN_ALLMATCHES)
- virus_found = 1;
- else {
- status = CL_VIRUS;
- goto done;
- }
+ status = CL_VIRUS;
+ goto done;
}
if (records_count > 1) {
@@ -1136,7 +1113,7 @@ cl_error_t index_the_central_directory(
if (ZIP_MAX_NUM_OVERLAPPING_FILES < num_overlapping_files) {
if (SCAN_HEURISTICS) {
- status = cli_append_virus(ctx, "Heuristics.Zip.OverlappingFiles");
+ status = cli_append_potentially_unwanted(ctx, "Heuristics.Zip.OverlappingFiles");
} else {
status = CL_EFORMAT;
}
@@ -1171,12 +1148,11 @@ cl_error_t index_the_central_directory(
free(zip_catalogue);
zip_catalogue = NULL;
}
- }
- if (virus_found)
- status = CL_VIRUS;
- else if (exceeded_max_files)
- status = CL_EMAXFILES;
+ if (exceeded_max_files) {
+ status = CL_EMAXFILES;
+ }
+ }
return status;
}
@@ -1189,7 +1165,6 @@ cl_error_t cli_unzip(cli_ctx *ctx)
fmap_t *map = ctx->fmap;
char *tmpd = NULL;
const char *ptr;
- int virus_found = 0;
#if HAVE_JSON
int toval = 0;
#endif
@@ -1235,11 +1210,7 @@ cl_error_t cli_unzip(cli_ctx *ctx)
&zip_catalogue,
&records_count);
if (CL_SUCCESS != ret) {
- if (CL_VIRUS == ret && SCAN_ALLMATCHES)
- virus_found = 1;
- else {
- goto done;
- }
+ goto done;
}
/*
@@ -1296,13 +1267,14 @@ cl_error_t cli_unzip(cli_ctx *ctx)
// so we will also check and update the limits for the actual number of scanned
// files inside cli_magic_scan()
cli_dbgmsg("cli_unzip: Files limit reached (max: %u)\n", ctx->engine->maxfiles);
- cli_append_virus_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxFiles");
+ cli_append_potentially_unwanted_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxFiles");
ret = CL_EMAXFILES;
}
if (cli_checktimelimit(ctx) != CL_SUCCESS) {
cli_dbgmsg("cli_unzip: Time limit reached (max: %u)\n", ctx->engine->maxscantime);
ret = CL_ETIMEOUT;
+ goto done;
}
#if HAVE_JSON
@@ -1310,22 +1282,21 @@ cl_error_t cli_unzip(cli_ctx *ctx)
ret = CL_ETIMEOUT;
}
#endif
- if (ret != CL_CLEAN) {
- if (ret == CL_VIRUS && SCAN_ALLMATCHES) {
- ret = CL_CLEAN;
- virus_found = 1;
- } else {
- break;
- }
+ if (ret != CL_SUCCESS) {
+ break;
}
}
} else {
cli_dbgmsg("cli_unzip: central not found, using localhdrs\n");
}
- if (virus_found == 1) {
- ret = CL_VIRUS;
+ if (CL_SUCCESS != ret) {
+ // goto done right away if there was a timeout, an alert, etc.
+ // This is slightly redundant since the while loop will only happen
+ // if ret == CL_SUCCESS but it's more explicit.
+ goto done;
}
+
if (0 < num_files_unzipped && num_files_unzipped <= (file_count / 4)) { /* FIXME: make up a sane ratio or remove the whole logic */
file_count = 0;
while ((ret == CL_CLEAN) &&
@@ -1344,10 +1315,7 @@ cl_error_t cli_unzip(cli_ctx *ctx)
NULL)))) {
file_count++;
lhoff += coff;
- if (SCAN_ALLMATCHES && ret == CL_VIRUS) {
- ret = CL_CLEAN;
- virus_found = 1;
- }
+
if (ctx->engine->maxfiles && num_files_unzipped >= ctx->engine->maxfiles) {
// Note: this check piggybacks on the MaxFiles setting, but is not actually
// scanning these files or incrementing the ctx->scannedfiles count
@@ -1355,7 +1323,7 @@ cl_error_t cli_unzip(cli_ctx *ctx)
// so we will also check and update the limits for the actual number of scanned
// files inside cli_magic_scan()
cli_dbgmsg("cli_unzip: Files limit reached (max: %u)\n", ctx->engine->maxfiles);
- cli_append_virus_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxFiles");
+ cli_append_potentially_unwanted_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxFiles");
ret = CL_EMAXFILES;
}
#if HAVE_JSON
@@ -1387,9 +1355,6 @@ cl_error_t cli_unzip(cli_ctx *ctx)
free(tmpd);
}
- if (ret == CL_CLEAN && virus_found)
- ret = CL_VIRUS;
-
return ret;
}
@@ -1516,7 +1481,7 @@ cl_error_t unzip_search(cli_ctx *ctx, fmap_t *map, struct zip_requests *requests
// Note: this check piggybacks on the MaxFiles setting, but is not actually
// scanning these files or incrementing the ctx->scannedfiles count
cli_dbgmsg("cli_unzip: Files limit reached (max: %u)\n", ctx->engine->maxfiles);
- cli_append_virus_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxFiles");
+ cli_append_potentially_unwanted_if_heur_exceedsmax(ctx, "Heuristics.Limits.Exceeded.MaxFiles");
ret = CL_EMAXFILES;
}
#if HAVE_JSON
diff --git a/libclamav/vba_extract.c b/libclamav/vba_extract.c
index d8280c68b3..b73835a528 100644
--- a/libclamav/vba_extract.c
+++ b/libclamav/vba_extract.c
@@ -1720,15 +1720,25 @@ int cli_scan_ole10(int fd, cli_ctx *ctx)
free(fullname);
return CL_ECREAT;
}
+
cli_dbgmsg("cli_decode_ole_object: decoding to %s\n", fullname);
+
ole_copy_file_data(fd, ofd, object_size);
+
lseek(ofd, 0, SEEK_SET);
+
ret = cli_magic_scan_desc(ofd, fullname, ctx, NULL);
+
close(ofd);
- if (ctx && !ctx->engine->keeptmp)
- if (cli_unlink(fullname))
- ret = CL_EUNLINK;
+
+ if (ctx && !ctx->engine->keeptmp) {
+ if (cli_unlink(fullname)) {
+ cli_dbgmsg("cli_decode_ole_object: Failed to remove temp file: %s\n", fullname);
+ }
+ }
+
free(fullname);
+
return ret;
}
diff --git a/libclamav/xar.c b/libclamav/xar.c
index 45e3d83f30..dfa94d5628 100644
--- a/libclamav/xar.c
+++ b/libclamav/xar.c
@@ -309,8 +309,6 @@ static int xar_scan_subdocuments(xmlTextReaderPtr reader, cli_ctx *ctx)
subdoc_len = xmlStrlen(subdoc);
cli_dbgmsg("cli_scanxar: in-memory scan of xml subdocument, len %i.\n", subdoc_len);
rc = cli_magic_scan_buff(subdoc, subdoc_len, ctx, NULL);
- if (rc == CL_VIRUS && SCAN_ALLMATCHES)
- rc = CL_SUCCESS;
/* make a file to leave if --leave-temps in effect */
if (ctx->engine->keeptmp) {
@@ -519,8 +517,7 @@ int cli_scanxar(cli_ctx *ctx)
cli_dbgmsg("cli_scanxar: scanning xar TOC xml in memory.\n");
rc = cli_magic_scan_buff(toc, hdr.toc_length_decompressed, ctx, NULL);
if (rc != CL_SUCCESS) {
- if (rc != CL_VIRUS || !SCAN_ALLMATCHES)
- goto exit_toc;
+ goto exit_toc;
}
/* make a file to leave if --leave-temps in effect */
@@ -848,14 +845,7 @@ int cli_scanxar(cli_ctx *ctx)
rc = cli_magic_scan_desc(fd, tmpname, ctx, NULL); /// TODO: collect file names in xar_get_toc_data_values()
if (rc != CL_SUCCESS) {
- if (rc == CL_VIRUS) {
- cli_dbgmsg("cli_scanxar: Infected with %s\n", cli_get_last_virus(ctx));
- if (!SCAN_ALLMATCHES)
- goto exit_tmpfile;
- } else if (rc != CL_BREAK) {
- cli_dbgmsg("cli_scanxar: cli_magic_scan_desc error %i\n", rc);
- goto exit_tmpfile;
- }
+ goto exit_tmpfile;
}
}
diff --git a/libclamav/xdp.c b/libclamav/xdp.c
index 8b829457a1..96eef4810e 100644
--- a/libclamav/xdp.c
+++ b/libclamav/xdp.c
@@ -161,7 +161,7 @@ cl_error_t cli_scanxdp(cli_ctx *ctx)
rc = cli_magic_scan_buff(decoded, decodedlen, ctx, NULL);
free(decoded);
- if (rc != CL_SUCCESS || rc == CL_BREAK) {
+ if (rc != CL_SUCCESS) {
xmlFree((void *)value);
break;
}
diff --git a/libclamav/xlm_extract.c b/libclamav/xlm_extract.c
index 02fa5a0874..fb7eae45b1 100644
--- a/libclamav/xlm_extract.c
+++ b/libclamav/xlm_extract.c
@@ -4215,7 +4215,6 @@ cl_error_t process_blip_record(struct OfficeArtRecordHeader_Unpacked *rh, const
{
cl_error_t status = CL_EARG;
cl_error_t ret;
- bool virus_found = false;
char *extracted_image_filepath = NULL;
int extracted_image_tempfd = -1;
@@ -4334,11 +4333,11 @@ cl_error_t process_blip_record(struct OfficeArtRecordHeader_Unpacked *rh, const
if (ctx->engine->keeptmp) {
/* Drop a temp file and scan that */
- if ((ret = cli_gentempfd_with_prefix(
- ctx->sub_tmpdir,
- extracted_image_type,
- &extracted_image_filepath,
- &extracted_image_tempfd)) != CL_SUCCESS) {
+ if (CL_SUCCESS != (ret = cli_gentempfd_with_prefix(
+ ctx->sub_tmpdir,
+ extracted_image_type,
+ &extracted_image_filepath,
+ &extracted_image_tempfd))) {
cli_warnmsg("Failed to create temp file for extracted %s file\n", extracted_image_type);
status = CL_EOPEN;
goto done;
@@ -4355,12 +4354,9 @@ cl_error_t process_blip_record(struct OfficeArtRecordHeader_Unpacked *rh, const
/* Scan the buffer */
ret = cli_magic_scan_buff(start_of_image, size_of_image, ctx, NULL);
}
- if (ret == CL_VIRUS) {
- if (!SCAN_ALLMATCHES) {
- status = CL_VIRUS;
- goto done;
- }
- virus_found = true;
+ if (CL_SUCCESS != ret) {
+ status = ret;
+ goto done;
}
}
@@ -4381,9 +4377,6 @@ cl_error_t process_blip_record(struct OfficeArtRecordHeader_Unpacked *rh, const
free(extracted_image_filepath);
}
- if (virus_found) {
- status = CL_VIRUS;
- }
return status;
}
@@ -4398,8 +4391,6 @@ cl_error_t process_blip_record(struct OfficeArtRecordHeader_Unpacked *rh, const
cl_error_t process_blip_store_container(const unsigned char *blip_store_container, size_t blip_store_container_len, cli_ctx *ctx)
{
cl_error_t status = CL_EARG;
- cl_error_t ret;
- bool virus_found = false;
struct OfficeArtRecordHeader_Unpacked rh;
const unsigned char *index = blip_store_container;
@@ -4478,13 +4469,9 @@ cl_error_t process_blip_store_container(const unsigned char *blip_store_containe
cli_dbgmsg("process_blip_store_container: Failed to get header\n");
goto done;
}
- ret = process_blip_record(&embeddedBlip_rh, embeddedBlip, embeddedBlip_size, ctx);
- if (ret == CL_VIRUS) {
- if (!SCAN_ALLMATCHES) {
- status = CL_VIRUS;
- goto done;
- }
- virus_found = true;
+ status = process_blip_record(&embeddedBlip_rh, embeddedBlip, embeddedBlip_size, ctx);
+ if (CL_SUCCESS != status) {
+ goto done;
}
}
}
@@ -4492,13 +4479,9 @@ cl_error_t process_blip_store_container(const unsigned char *blip_store_containe
} else if ((0xF018 <= rh.recType) && (0xF117 >= rh.recType)) {
/* it's an OfficeArtBlip record */
cli_dbgmsg("process_blip_store_container: Found a Blip record\n");
- ret = process_blip_record(&rh, index, remaining, ctx);
- if (ret == CL_VIRUS) {
- if (!SCAN_ALLMATCHES) {
- status = CL_VIRUS;
- goto done;
- }
- virus_found = true;
+ status = process_blip_record(&rh, index, remaining, ctx);
+ if (CL_SUCCESS != status) {
+ goto done;
}
} else {
@@ -4518,17 +4501,12 @@ cl_error_t process_blip_store_container(const unsigned char *blip_store_containe
done:
- if (virus_found) {
- status = CL_VIRUS;
- }
return status;
}
cl_error_t cli_extract_images_from_drawing_group(const unsigned char *drawinggroup, size_t drawinggroup_len, cli_ctx *ctx)
{
cl_error_t status = CL_EARG;
- cl_error_t ret;
- bool virus_found = false;
struct OfficeArtRecordHeader_Unpacked rh;
const unsigned char *index = drawinggroup;
@@ -4604,13 +4582,9 @@ cl_error_t cli_extract_images_from_drawing_group(const unsigned char *drawinggro
blip_store_container_len = rh.recLen;
}
- ret = process_blip_store_container(start_of_blip_store_container, blip_store_container_len, ctx);
- if (ret == CL_VIRUS) {
- if (!SCAN_ALLMATCHES) {
- status = CL_VIRUS;
- goto done;
- }
- virus_found = true;
+ status = process_blip_store_container(start_of_blip_store_container, blip_store_container_len, ctx);
+ if (CL_SUCCESS != status) {
+ goto done;
}
}
@@ -4625,9 +4599,7 @@ cl_error_t cli_extract_images_from_drawing_group(const unsigned char *drawinggro
status = CL_SUCCESS;
done:
- if (virus_found) {
- status = CL_VIRUS;
- }
+
return status;
}
@@ -4976,7 +4948,7 @@ cl_error_t cli_extract_xlm_macros_and_images(const char *dir, cli_ctx *ctx, char
goto done;
}
- if (cli_scan_desc(out_fd, ctx, CL_TYPE_SCRIPT, 0, NULL, AC_SCAN_VIR, NULL, NULL) == CL_VIRUS) {
+ if (cli_scan_desc(out_fd, ctx, CL_TYPE_SCRIPT, false, NULL, AC_SCAN_VIR, NULL, NULL) == CL_VIRUS) {
status = CL_VIRUS;
goto done;
}
@@ -4994,8 +4966,8 @@ cl_error_t cli_extract_xlm_macros_and_images(const char *dir, cli_ctx *ctx, char
* If we fail to extract images, that's fine.
*/
ret = cli_extract_images_from_drawing_group(drawinggroup, drawinggroup_len, ctx);
- if (ret == CL_VIRUS) {
- status = CL_VIRUS;
+ if (CL_SUCCESS != ret) {
+ status = ret;
goto done;
}
}
diff --git a/libclamav/yc.c b/libclamav/yc.c
index 5ade2b79ce..e95491edab 100644
--- a/libclamav/yc.c
+++ b/libclamav/yc.c
@@ -46,7 +46,7 @@ static int yc_bounds_check(cli_ctx *ctx, char *base, unsigned int filesize, char
if ((unsigned int)((offset + bound) - base) > filesize) {
cli_dbgmsg("yC: Bounds check assertion.\n");
#if DO_HEURISTIC
- cli_append_virus(ctx, "Heuristics.BoundsCheck");
+ cli_append_potentially_unwanted(ctx, "Heuristics.BoundsCheck");
#endif
return 1;
}
diff --git a/libclamav_rust/src/evidence.rs b/libclamav_rust/src/evidence.rs
index 326c6d05f1..6b76329f55 100644
--- a/libclamav_rust/src/evidence.rs
+++ b/libclamav_rust/src/evidence.rs
@@ -124,6 +124,43 @@ pub unsafe extern "C" fn _evidence_get_last_alert(evidence: sys::evidence_t) ->
}
}
+/// C interface to get a string name for one of the alerts.
+/// Will first check for one from the strong indicators, then pua.
+///
+/// # Safety
+///
+/// Returns a string that is either static, or allocated when reading the database.
+/// So the lifetime of the string is good at least until you reload or unload the databases.
+///
+/// No parameters may be NULL
+#[export_name = "evidence_get_indicator"]
+pub unsafe extern "C" fn _evidence_get_indicator(
+ evidence: sys::evidence_t,
+ indicator_type: IndicatorType,
+ index: usize,
+) -> *const c_char {
+ let evidence = ManuallyDrop::new(Box::from_raw(evidence as *mut Evidence));
+
+ match indicator_type {
+ IndicatorType::Strong => {
+ if let Some(meta) = evidence.strong.values().nth(index) {
+ return meta.last().unwrap().static_virname as *const c_char;
+ } else {
+ // no alert at that index. return NULL
+ return std::ptr::null();
+ }
+ }
+ IndicatorType::PotentiallyUnwanted => {
+ if let Some(meta) = evidence.pua.values().nth(index) {
+ return meta.last().unwrap().static_virname as *const c_char;
+ } else {
+ // no alert at that index. return NULL
+ return std::ptr::null();
+ }
+ }
+ }
+}
+
/// C interface to check number of alerting indicators in evidence.
///
/// # Safety
diff --git a/libclamav_rust/src/sys.rs b/libclamav_rust/src/sys.rs
index d05a59b4a9..804ac6cc45 100644
--- a/libclamav_rust/src/sys.rs
+++ b/libclamav_rust/src/sys.rs
@@ -441,8 +441,12 @@ pub struct cl_fmap {
>,
pub unneed_off:
::std::option::Option,
- pub have_maphash: bool,
- pub maphash: [::std::os::raw::c_uchar; 16usize],
+ pub have_md5: bool,
+ pub md5: [::std::os::raw::c_uchar; 16usize],
+ pub have_sha1: bool,
+ pub sha1: [::std::os::raw::c_uchar; 20usize],
+ pub have_sha256: bool,
+ pub sha256: [::std::os::raw::c_uchar; 32usize],
pub bitmap: *mut u64,
pub name: *mut ::std::os::raw::c_char,
}
@@ -743,6 +747,7 @@ pub struct recursion_level_tag {
pub recursion_level_buffer_fmap: u32,
pub is_normalized_layer: bool,
pub image_fuzzy_hash: image_fuzzy_hash_t,
+ pub calculated_image_fuzzy_hash: bool,
}
pub type recursion_level_t = recursion_level_tag;
pub type evidence_t = *mut ::std::os::raw::c_void;
diff --git a/libfreshclam/libfreshclam_internal.c b/libfreshclam/libfreshclam_internal.c
index faee5a1089..8538923a3f 100644
--- a/libfreshclam/libfreshclam_internal.c
+++ b/libfreshclam/libfreshclam_internal.c
@@ -2114,7 +2114,7 @@ static fc_error_t check_for_new_database_version(
&remotever,
&remotename);
switch (ret) {
- case FC_SUCCESS: {
+ case FC_SUCCESS:
if (0 == localver) {
logg(LOGG_INFO, "%s database available for download (remote version: %d)\n",
database, remotever);
@@ -2125,8 +2125,8 @@ static fc_error_t check_for_new_database_version(
break;
}
/* fall-through */
- }
- case FC_UPTODATE: {
+
+ case FC_UPTODATE:
if (NULL == local_database) {
logg(LOGG_ERROR, "check_for_new_database_version: server claims we're up-to-date, but we don't have a local database!\n");
status = FC_EFAILEDGET;
@@ -2143,18 +2143,17 @@ static fc_error_t check_for_new_database_version(
We know it will be the same as the local version though. */
remotever = localver;
break;
- }
- case FC_EFORBIDDEN: {
+
+ case FC_EFORBIDDEN:
/* We tried to look up the version using HTTP and were actively blocked. */
logg(LOGG_ERROR, "check_for_new_database_version: Blocked from using server %s.\n", server);
status = FC_EFORBIDDEN;
goto done;
- }
- default: {
+
+ default:
logg(LOGG_ERROR, "check_for_new_database_version: Failed to find %s database using server %s.\n", database, server);
status = FC_EFAILEDGET;
goto done;
- }
}
*remoteVersion = remotever;
diff --git a/sigtool/sigtool.c b/sigtool/sigtool.c
index d9458c8cdf..a7eab18e85 100644
--- a/sigtool/sigtool.c
+++ b/sigtool/sigtool.c
@@ -195,7 +195,7 @@ static int hashpe(const char *filename, unsigned int class, int type)
goto done;
}
- if (cli_add_content_match_pattern(engine->root[0], "test", "deadbeef", 0, 0, 0, "*", 0, NULL, 0) != CL_SUCCESS) {
+ if (cli_add_content_match_pattern(engine->root[0], "test", "deadbeef", 0, 0, 0, "*", NULL, 0) != CL_SUCCESS) {
mprintf(LOGG_ERROR, "hashpe: Can't parse signature\n");
goto done;
}
@@ -206,7 +206,7 @@ static int hashpe(const char *filename, unsigned int class, int type)
}
/* prepare context */
- ctx.engine = engine;
+ ctx.engine = engine;
ctx.evidence = evidence_new();
@@ -2096,7 +2096,7 @@ static void matchsig(char *sig, const char *offset, int fd)
goto done;
}
- if (readdb_parse_ldb_subsignature(engine->root[0], "test", sig, "*", 0, NULL, 0, 0, 1, &tdb) != CL_SUCCESS) {
+ if (readdb_parse_ldb_subsignature(engine->root[0], "test", sig, "*", NULL, 0, 0, 1, &tdb) != CL_SUCCESS) {
mprintf(LOGG_ERROR, "matchsig: Can't parse signature\n");
goto done;
}
@@ -2106,7 +2106,7 @@ static void matchsig(char *sig, const char *offset, int fd)
goto done;
}
- ctx.engine = engine;
+ ctx.engine = engine;
ctx.evidence = evidence_new();
@@ -2127,7 +2127,7 @@ static void matchsig(char *sig, const char *offset, int fd)
ctx.fmap = ctx.recursion_stack[ctx.recursion_level].fmap;
- (void)cli_scan_fmap(&ctx, 0, 0, NULL, AC_SCAN_VIR, &acres, NULL);
+ (void)cli_scan_fmap(&ctx, CL_TYPE_ANY, false, NULL, AC_SCAN_VIR, &acres, NULL);
res = acres;
while (res) {
@@ -3313,7 +3313,7 @@ static int dumpcerts(const struct optstruct *opts)
goto done;
}
- if (cli_add_content_match_pattern(engine->root[0], "test", "deadbeef", 0, 0, 0, "*", 0, NULL, 0) != CL_SUCCESS) {
+ if (cli_add_content_match_pattern(engine->root[0], "test", "deadbeef", 0, 0, 0, "*", NULL, 0) != CL_SUCCESS) {
mprintf(LOGG_ERROR, "dumpcerts: Can't parse signature\n");
goto done;
}
@@ -3327,7 +3327,7 @@ static int dumpcerts(const struct optstruct *opts)
cl_debug();
/* prepare context */
- ctx.engine = engine;
+ ctx.engine = engine;
ctx.evidence = evidence_new();
diff --git a/sigtool/vba.c b/sigtool/vba.c
index 6dc4167b76..e9852a7a5d 100644
--- a/sigtool/vba.c
+++ b/sigtool/vba.c
@@ -77,7 +77,7 @@ cli_ctx *convenience_ctx(int fd)
goto done;
}
- if (cli_add_content_match_pattern(engine->root[0], "test", "deadbeef", 0, 0, 0, "*", 0, NULL, 0) != CL_SUCCESS) {
+ if (cli_add_content_match_pattern(engine->root[0], "test", "deadbeef", 0, 0, 0, "*", NULL, 0) != CL_SUCCESS) {
printf("convenience_ctx: Can't parse signature\n");
goto done;
}
diff --git a/unit_tests/CMakeLists.txt b/unit_tests/CMakeLists.txt
index 1460357bad..b46c701ae8 100644
--- a/unit_tests/CMakeLists.txt
+++ b/unit_tests/CMakeLists.txt
@@ -353,11 +353,11 @@ add_rust_test(NAME libclamav_rust WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/libclam
set_property(TEST libclamav_rust PROPERTY ENVIRONMENT ${ENVIRONMENT})
if(ENABLE_APP)
- add_test(NAME clamscan COMMAND ${PythonTest_COMMAND};clamscan_test.py
+ add_test(NAME clamscan COMMAND ${PythonTest_COMMAND};clamscan
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
set_property(TEST clamscan PROPERTY ENVIRONMENT ${ENVIRONMENT})
if(Valgrind_FOUND)
- add_test(NAME clamscan_valgrind COMMAND ${PythonTest_COMMAND};clamscan_test.py
+ add_test(NAME clamscan_valgrind COMMAND ${PythonTest_COMMAND};clamscan
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
set_property(TEST clamscan_valgrind PROPERTY ENVIRONMENT ${ENVIRONMENT} VALGRIND=${Valgrind_EXECUTABLE})
endif()
diff --git a/unit_tests/check_clamav.c b/unit_tests/check_clamav.c
index e45f169e9e..8ae17d6ba0 100644
--- a/unit_tests/check_clamav.c
+++ b/unit_tests/check_clamav.c
@@ -169,14 +169,14 @@ END_TEST
static int get_test_file(int i, char *file, unsigned fsize, unsigned long *size);
static struct cl_engine *g_engine;
-/* int cl_scandesc(int desc, const char **virname, unsigned long int *scanned, const struct cl_engine *engine, const struct cl_limits *limits, struct cl_scan_options* options) */
+/* cl_error_t cl_scandesc(int desc, const char **virname, unsigned long int *scanned, const struct cl_engine *engine, const struct cl_limits *limits, struct cl_scan_options* options) */
START_TEST(test_cl_scandesc)
{
const char *virname = NULL;
char file[256];
unsigned long size;
unsigned long int scanned = 0;
- int ret;
+ cl_error_t ret;
struct cl_scan_options options;
memset(&options, 0, sizeof(struct cl_scan_options));
diff --git a/unit_tests/check_matchers.c b/unit_tests/check_matchers.c
index f208b4bd34..e0595f81a1 100644
--- a/unit_tests/check_matchers.c
+++ b/unit_tests/check_matchers.c
@@ -220,7 +220,7 @@ START_TEST(test_ac_scanbuff)
ck_assert_msg(ret == CL_SUCCESS, "cli_ac_init() failed");
for (i = 0; ac_testdata[i].data; i++) {
- ret = cli_add_content_match_pattern(root, ac_testdata[i].virname, ac_testdata[i].hexsig, 0, 0, 0, "*", 0, NULL, 0);
+ ret = cli_add_content_match_pattern(root, ac_testdata[i].virname, ac_testdata[i].hexsig, 0, 0, 0, "*", NULL, 0);
ck_assert_msg(ret == CL_SUCCESS, "cli_add_content_match_pattern failed");
}
@@ -263,7 +263,7 @@ START_TEST(test_ac_scanbuff_allscan)
ck_assert_msg(ret == CL_SUCCESS, "cli_ac_init() failed");
for (i = 0; ac_testdata[i].data; i++) {
- ret = cli_add_content_match_pattern(root, ac_testdata[i].virname, ac_testdata[i].hexsig, 0, 0, 0, "*", 0, NULL, 0);
+ ret = cli_add_content_match_pattern(root, ac_testdata[i].virname, ac_testdata[i].hexsig, 0, 0, 0, "*", NULL, 0);
ck_assert_msg(ret == CL_SUCCESS, "cli_add_content_match_pattern failed");
}
@@ -315,7 +315,7 @@ START_TEST(test_ac_scanbuff_ex)
ck_assert_msg(ret == CL_SUCCESS, "[ac_ex] cli_ac_init() failed");
for (i = 0; ac_sigopts_testdata[i].data; i++) {
- ret = cli_sigopts_handler(root, ac_sigopts_testdata[i].virname, ac_sigopts_testdata[i].hexsig, ac_sigopts_testdata[i].sigopts, 0, 0, ac_sigopts_testdata[i].offset, 0, NULL, 0);
+ ret = cli_sigopts_handler(root, ac_sigopts_testdata[i].virname, ac_sigopts_testdata[i].hexsig, ac_sigopts_testdata[i].sigopts, 0, 0, ac_sigopts_testdata[i].offset, NULL, 0);
ck_assert_msg(ret == CL_SUCCESS, "[ac_ex] cli_sigopts_handler() failed");
}
@@ -358,7 +358,7 @@ START_TEST(test_ac_scanbuff_allscan_ex)
ck_assert_msg(ret == CL_SUCCESS, "[ac_ex] cli_ac_init() failed");
for (i = 0; ac_sigopts_testdata[i].data; i++) {
- ret = cli_sigopts_handler(root, ac_sigopts_testdata[i].virname, ac_sigopts_testdata[i].hexsig, ac_sigopts_testdata[i].sigopts, 0, 0, ac_sigopts_testdata[i].offset, 0, NULL, 0);
+ ret = cli_sigopts_handler(root, ac_sigopts_testdata[i].virname, ac_sigopts_testdata[i].hexsig, ac_sigopts_testdata[i].sigopts, 0, 0, ac_sigopts_testdata[i].offset, NULL, 0);
ck_assert_msg(ret == CL_SUCCESS, "[ac_ex] cli_sigopts_handler() failed");
}
@@ -413,11 +413,11 @@ START_TEST(test_bm_scanbuff)
ret = cli_bm_init(root);
ck_assert_msg(ret == CL_SUCCESS, "cli_bm_init() failed");
- ret = cli_add_content_match_pattern(root, "Sig1", "deadbabe", 0, 0, 0, "*", 0, NULL, 0);
+ ret = cli_add_content_match_pattern(root, "Sig1", "deadbabe", 0, 0, 0, "*", NULL, 0);
ck_assert_msg(ret == CL_SUCCESS, "cli_add_content_match_pattern failed");
- ret = cli_add_content_match_pattern(root, "Sig2", "deadbeef", 0, 0, 0, "*", 0, NULL, 0);
+ ret = cli_add_content_match_pattern(root, "Sig2", "deadbeef", 0, 0, 0, "*", NULL, 0);
ck_assert_msg(ret == CL_SUCCESS, "cli_add_content_match_pattern failed");
- ret = cli_add_content_match_pattern(root, "Sig3", "babedead", 0, 0, 0, "*", 0, NULL, 0);
+ ret = cli_add_content_match_pattern(root, "Sig3", "babedead", 0, 0, 0, "*", NULL, 0);
ck_assert_msg(ret == CL_SUCCESS, "cli_add_content_match_pattern failed");
ctx.options->general &= ~CL_SCAN_GENERAL_ALLMATCHES; /* make sure all-match is disabled */
@@ -442,11 +442,11 @@ START_TEST(test_bm_scanbuff_allscan)
ret = cli_bm_init(root);
ck_assert_msg(ret == CL_SUCCESS, "cli_bm_init() failed");
- ret = cli_add_content_match_pattern(root, "Sig1", "deadbabe", 0, 0, 0, "*", 0, NULL, 0);
+ ret = cli_add_content_match_pattern(root, "Sig1", "deadbabe", 0, 0, 0, "*", NULL, 0);
ck_assert_msg(ret == CL_SUCCESS, "cli_add_content_match_pattern failed");
- ret = cli_add_content_match_pattern(root, "Sig2", "deadbeef", 0, 0, 0, "*", 0, NULL, 0);
+ ret = cli_add_content_match_pattern(root, "Sig2", "deadbeef", 0, 0, 0, "*", NULL, 0);
ck_assert_msg(ret == CL_SUCCESS, "cli_add_content_match_pattern failed");
- ret = cli_add_content_match_pattern(root, "Sig3", "babedead", 0, 0, 0, "*", 0, NULL, 0);
+ ret = cli_add_content_match_pattern(root, "Sig3", "babedead", 0, 0, 0, "*", NULL, 0);
ck_assert_msg(ret == CL_SUCCESS, "cli_add_content_match_pattern failed");
ctx.options->general |= CL_SCAN_GENERAL_ALLMATCHES; /* enable all-match */
@@ -484,7 +484,7 @@ START_TEST(test_pcre_scanbuff)
strncat(hexsig, PCRE_BYPASS, hexlen);
strncat(hexsig, pcre_testdata[i].hexsig, hexlen);
- ret = readdb_parse_ldb_subsignature(root, pcre_testdata[i].virname, hexsig, pcre_testdata[i].offset, 0, NULL, 0, 0, 0, NULL);
+ ret = readdb_parse_ldb_subsignature(root, pcre_testdata[i].virname, hexsig, pcre_testdata[i].offset, NULL, 0, 0, 0, NULL);
ck_assert_msg(ret == CL_SUCCESS, "[pcre] readdb_parse_ldb_subsignature failed");
free(hexsig);
}
@@ -538,7 +538,7 @@ START_TEST(test_pcre_scanbuff_allscan)
strncat(hexsig, PCRE_BYPASS, hexlen);
strncat(hexsig, pcre_testdata[i].hexsig, hexlen);
- ret = readdb_parse_ldb_subsignature(root, pcre_testdata[i].virname, hexsig, pcre_testdata[i].offset, 0, NULL, 0, 0, 1, NULL);
+ ret = readdb_parse_ldb_subsignature(root, pcre_testdata[i].virname, hexsig, pcre_testdata[i].offset, NULL, 0, 0, 1, NULL);
ck_assert_msg(ret == CL_SUCCESS, "[pcre] readdb_parse_ldb_subsignature failed");
free(hexsig);
}
diff --git a/unit_tests/check_str.c b/unit_tests/check_str.c
index 611b198206..501039c442 100644
--- a/unit_tests/check_str.c
+++ b/unit_tests/check_str.c
@@ -73,7 +73,7 @@ START_TEST(test_unescape_hex)
free(str);
str = cli_unescape("%00");
- ck_assert_msg(str && !strcmp(str, "\x1"), "cli_unescape %00");
+ ck_assert_msg(str && !strcmp(str, "\x1"), "cli_unescape 00");
free(str);
}
END_TEST
diff --git a/unit_tests/clamscan/_basic_test.py b/unit_tests/clamscan/_basic_test.py
new file mode 100644
index 0000000000..aeb0a31b7d
--- /dev/null
+++ b/unit_tests/clamscan/_basic_test.py
@@ -0,0 +1,97 @@
+# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+
+"""
+Run clamscan tests.
+"""
+
+import shutil
+import sys
+
+sys.path.append('../unit_tests')
+import testcase
+
+
+class TC(testcase.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TC, cls).setUpClass()
+
+ TC.testpaths = list(TC.path_build.glob('unit_tests/input/clamav_hdb_scanfiles/clam*')) # A list of Path()'s of each of our generated test files
+
+ # Prepare a directory to store our test databases
+ TC.path_db = TC.path_tmp / 'database'
+ TC.path_db.mkdir(parents=True)
+
+ shutil.copy(
+ str(TC.path_build / 'unit_tests' / 'input' / 'clamav.hdb'),
+ str(TC.path_db),
+ )
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TC, cls).tearDownClass()
+
+ def setUp(self):
+ super(TC, self).setUp()
+
+ def tearDown(self):
+ super(TC, self).tearDown()
+ self.verify_valgrind_log()
+
+ def test_00_version(self):
+ self.step_name('clamscan version test')
+
+ command = '{valgrind} {valgrind_args} {clamscan} -V'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 0 # success
+
+ expected_results = [
+ 'ClamAV {}'.format(TC.version),
+ ]
+ self.verify_output(output.out, expected=expected_results)
+
+ def test_01_all_testfiles(self):
+ self.step_name('Test that clamscan alerts on all test files')
+
+ testfiles = ' '.join([str(testpath) for testpath in TC.testpaths])
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles}'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args,
+ clamscan=TC.clamscan,
+ path_db=TC.path_db / 'clamav.hdb',
+ testfiles=testfiles,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus found
+
+ expected_results = ['{}: ClamAV-Test-File.UNOFFICIAL FOUND'.format(testpath.name) for testpath in TC.testpaths]
+ expected_results.append('Scanned files: {}'.format(len(TC.testpaths)))
+ expected_results.append('Infected files: {}'.format(len(TC.testpaths)))
+ self.verify_output(output.out, expected=expected_results)
+
+ def test_02_all_testfiles_ign2(self):
+ self.step_name('Test that clamscan ignores ClamAV-Test-File alerts')
+
+ # Drop an ignore db into the test database directory
+ # in our scan, we'll just use the whole directory, which should load the ignore db *first*.
+ (TC.path_db / 'clamav.ign2').write_text('ClamAV-Test-File\n')
+
+ testfiles = ' '.join([str(testpath) for testpath in TC.testpaths])
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles}'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args,
+ clamscan=TC.clamscan,
+ path_db=TC.path_db,
+ testfiles=testfiles,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 0 # virus found
+
+ expected_results = ['Scanned files: {}'.format(len(TC.testpaths))]
+ expected_results.append('Infected files: 0')
+ unexpected_results = ['{}: ClamAV-Test-File.UNOFFICIAL FOUND'.format(testpath.name) for testpath in TC.testpaths]
+ self.verify_output(output.out, expected=expected_results, unexpected=unexpected_results)
+
diff --git a/unit_tests/clamscan/allmatch_test.py b/unit_tests/clamscan/allmatch_test.py
new file mode 100644
index 0000000000..f1ce6516ff
--- /dev/null
+++ b/unit_tests/clamscan/allmatch_test.py
@@ -0,0 +1,309 @@
+# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+
+"""
+Run clamscan tests.
+"""
+
+import os
+from zipfile import ZIP_DEFLATED, ZipFile
+import sys
+
+sys.path.append('../unit_tests')
+import testcase
+
+
+class TC(testcase.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TC, cls).setUpClass()
+
+ # Prepare a directory to store our test databases
+ TC.path_db = TC.path_tmp / 'database'
+ TC.path_db.mkdir(parents=True)
+
+ (TC.path_db / 'clam.ndb').write_text(
+ "Test.NDB:0:*:4b45524e454c33322e444c4c00004578\n"
+ )
+ (TC.path_db / 'clam.ldb').write_text(
+ "Test.LDB;Engine:52-255,Target:1;0;4B45524E454C33322E444C4C00004578697450726F63657373005553455233322E444C4C00434C414D657373616765426F7841\n"
+ )
+ (TC.path_db / 'clam.hdb').write_text(
+ "aa15bcf478d165efd2065190eb473bcb:544:Test.MD5.Hash:73\n"
+ "aa15bcf478d165efd2065190eb473bcb:*:Test.MD5.Hash.NoSize:73\n"
+ )
+ (TC.path_db / 'clam.hsb').write_text(
+ "71e7b604d18aefd839e51a39c88df8383bb4c071dc31f87f00a2b5df580d4495:544:Test.Sha256.Hash:73\n"
+ "71e7b604d18aefd839e51a39c88df8383bb4c071dc31f87f00a2b5df580d4495:*:Test.Sha256.Hash.NoSize:73\n"
+ "62dd70f5e7530e0239901ac186f1f9ae39292561:544:Test.Sha1.Hash:73\n"
+ "62dd70f5e7530e0239901ac186f1f9ae39292561:*:Test.Sha1.NoSize:73\n"
+ )
+ (TC.path_db / 'clam.imp').write_text(
+ "98c88d882f01a3f6ac1e5f7dfd761624:39:Test.Import.Hash\n"
+ "98c88d882f01a3f6ac1e5f7dfd761624:*:Test.Import.Hash.NoSize\n"
+ )
+ (TC.path_db / 'clam.mdb').write_text(
+ "512:23db1dd3f77fae25610b6a32701313ae:Test.PESection.Hash:73\n"
+ "*:23db1dd3f77fae25610b6a32701313ae:Test.PESection.Hash.NoSize:73\n"
+ )
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TC, cls).tearDownClass()
+
+ def setUp(self):
+ super(TC, self).setUp()
+
+ def tearDown(self):
+ super(TC, self).tearDown()
+ self.verify_valgrind_log()
+
+ def test_many_sigs(self):
+ self.step_name('Test that each type of sig alerts in all-match mode')
+
+ testfiles = TC.path_build / 'unit_tests' / 'input' / 'clamav_hdb_scanfiles' / 'clam.exe'
+
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --allmatch'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ path_db=TC.path_db,
+ testfiles=testfiles,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus
+
+ expected_results = [
+ 'Test.LDB.UNOFFICIAL FOUND',
+ 'Test.NDB.UNOFFICIAL FOUND',
+ 'Test.MD5.Hash.UNOFFICIAL FOUND',
+ 'Test.MD5.Hash.NoSize.UNOFFICIAL FOUND',
+ 'Test.Sha1.Hash.UNOFFICIAL FOUND',
+ 'Test.Sha1.NoSize.UNOFFICIAL FOUND',
+ 'Test.Sha256.Hash.UNOFFICIAL FOUND',
+ 'Test.Sha256.Hash.NoSize.UNOFFICIAL FOUND',
+ 'Test.PESection.Hash.UNOFFICIAL FOUND',
+ 'Test.PESection.Hash.NoSize.UNOFFICIAL FOUND',
+ 'Test.Import.Hash.UNOFFICIAL FOUND',
+ 'Test.Import.Hash.NoSize.UNOFFICIAL FOUND',
+ ]
+ self.verify_output(output.out, expected=expected_results)
+
+ def test_many_sigs_no_allmatch(self):
+ self.step_name('Test that only one sig alerts when not using all-match mode')
+
+ testfiles = TC.path_build / 'unit_tests' / 'input' / 'clamav_hdb_scanfiles' / 'clam.exe'
+
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles}'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ path_db=TC.path_db,
+ testfiles=testfiles,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus
+
+ assert output.out.count('FOUND') == 1 # only finds one of these (order not guaranteeds afaik, so don't care which)
+
+ def test_regression_imphash_nosize(self):
+ self.step_name('Test an import hash with wildcard size when all-match mode is disabled.')
+
+ db_dir = TC.path_db / 'allmatch-regression-test-sigs'
+
+ os.mkdir(str(db_dir))
+
+ (db_dir / 'clam.imp').write_text(
+ "98c88d882f01a3f6ac1e5f7dfd761624:*:Test.Import.Hash.NoSize\n"
+ )
+
+ testfiles = TC.path_build / 'unit_tests' / 'input' / 'clamav_hdb_scanfiles' / 'clam.exe'
+
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles}'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ path_db=db_dir / 'clam.imp',
+ testfiles=testfiles,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus
+
+ expected_results = [
+ 'Test.Import.Hash.NoSize.UNOFFICIAL FOUND',
+ ]
+ self.verify_output(output.out, expected=expected_results)
+
+ def test_regression_cbc_and_ndb(self):
+ self.step_name('Test that bytecode rules will run after content match alerts in all-match mode.')
+
+ # Source for ClamAV-Unit-Test_Signature.cbc
+ # ```c
+ # VIRUSNAME_PREFIX("BC.Clamav-Unit-Test-Signature")
+ # VIRUSNAMES("")
+ # TARGET(0)
+
+ # FUNCTIONALITY_LEVEL_MIN(FUNC_LEVEL_096_4)
+
+ # SIGNATURES_DECL_BEGIN
+ # DECLARE_SIGNATURE(test_string)
+ # SIGNATURES_DECL_END
+
+ # SIGNATURES_DEF_BEGIN
+ # /* matches "CLAMAV-TEST-STRING-NOT-EICAR" */
+ # DEFINE_SIGNATURE(test_string, "0:434c414d41562d544553542d535452494e472d4e4f542d4549434152")
+ # SIGNATURES_DEF_END
+
+ # bool logical_trigger()
+ # {
+ # return matches(Signatures.test_string);
+ # }
+
+ # int entrypoint(void)
+ # {
+ # foundVirus("");
+ # return 0;
+ # }
+ # ```
+
+ testfile = TC.path_tmp / 'CLAMAV-TEST-STRING-NOT-EICAR'
+
+ (testfile).write_text(
+ "CLAMAV-TEST-STRING-NOT-EICAR"
+ )
+
+ command = '{valgrind} {valgrind_args} {clamscan} -d {cbc_db} -d {ndb_db} --bytecode-unsigned --allmatch {testfiles}'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ cbc_db=TC.path_source / 'unit_tests' / 'input' / 'bytecode_sigs' / 'Clamav-Unit-Test-Signature.cbc',
+ ndb_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb',
+ testfiles=testfile,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus
+
+ expected_results = [
+ 'BC.Clamav-Unit-Test-Signature FOUND', # <-- ".UNOFFICIAL" is not added for bytecode signatures
+ 'NDB.Clamav-Unit-Test-Signature.UNOFFICIAL FOUND',
+ ]
+ self.verify_output(output.out, expected=expected_results)
+
+ def test_txt_plus_clam_zipsfx(self):
+ self.step_name('Test that clam will detect a string in text file, plus identify, extract, and alert on concatenated clam.zip containing clam.exe with a hash sig.')
+
+ testfile = TC.path_tmp / 'test-string-cat-clam.exe.txt'
+
+ clamzip = TC.path_build / 'unit_tests' / 'input' / 'clamav_hdb_scanfiles' / 'clam.zip'
+
+ testfile.write_bytes(b"CLAMAV-TEST-STRING-NOT-EICAR" + clamzip.read_bytes())
+
+ command = '{valgrind} {valgrind_args} {clamscan} -d {clam_exe_db} -d {not_eicar_db} --allmatch {testfiles}'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ clam_exe_db=TC.path_source / 'unit_tests' / 'input' / 'clamav.hdb',
+ not_eicar_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb',
+ testfiles=testfile,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus
+
+ expected_results = [
+ 'ClamAV-Test-File.UNOFFICIAL FOUND',
+ 'NDB.Clamav-Unit-Test-Signature.UNOFFICIAL FOUND',
+ ]
+ self.verify_output(output.out, expected=expected_results)
+
+ def test_exe_imphash_plus_zipsfx(self):
+ self.step_name('Test that clam will detect a string in text file, plus identify, extract, and alert on concatenated clam.zip containing clam.exe with an imp-hash sig.')
+
+ # We can't use the hash sig for this clam.exe program because the hash goes out the window when we concatenate on the zip.
+ (TC.path_tmp / 'clam.imp').write_text(
+ "98c88d882f01a3f6ac1e5f7dfd761624:39:Test.Import.Hash\n"
+ )
+
+ # Build a file that is the clam.exe program with a zip concatinated on that contains the not_eicar test string file.
+ clam_exe = TC.path_build / 'unit_tests' / 'input' / 'clamav_hdb_scanfiles' / 'clam.exe'
+
+ not_eicar_zip = TC.path_tmp / 'not-eicar.zip'
+ with ZipFile(str(not_eicar_zip), 'w', ZIP_DEFLATED) as zf:
+ zf.writestr('not-eicar.txt', b"CLAMAV-TEST-STRING-NOT-EICAR")
+
+ testfile = TC.path_tmp / 'clam.exe.not_eicar.zipsfx'
+ testfile.write_bytes(clam_exe.read_bytes() + not_eicar_zip.read_bytes())
+
+ command = '{valgrind} {valgrind_args} {clamscan} -d {clam_exe_db} -d {not_eicar_db} --allmatch {testfiles}'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ clam_exe_db=TC.path_tmp / 'clam.imp',
+ not_eicar_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb',
+ testfiles=testfile,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus
+
+ expected_results = [
+ 'Test.Import.Hash.UNOFFICIAL FOUND',
+ 'NDB.Clamav-Unit-Test-Signature.UNOFFICIAL FOUND',
+ ]
+ self.verify_output(output.out, expected=expected_results)
+
+ def test_exe_pattern_plus_zipsfx(self):
+ self.step_name('Test that clam will detect a string in text file, plus identify, extract, and alert on concatenated clam.zip containing clam.exe with a pattern-match sig.')
+ # This tests a regression where clam will fail to extract the embedded zip file if the pattern-match sig matches before the embedded file type sig.
+
+ # Build a file that is the clam.exe program with a zip concatinated on that contains the not_eicar test string file.
+ clam_exe = TC.path_build / 'unit_tests' / 'input' / 'clamav_hdb_scanfiles' / 'clam.exe'
+
+ not_eicar_zip = TC.path_tmp / 'not-eicar.zip'
+ with ZipFile(str(not_eicar_zip), 'w', ZIP_DEFLATED) as zf:
+ zf.writestr('not-eicar.txt', b"CLAMAV-TEST-STRING-NOT-EICAR")
+
+ testfile = TC.path_tmp / 'clam.exe.not_eicar.zipsfx'
+ testfile.write_bytes(clam_exe.read_bytes() + not_eicar_zip.read_bytes())
+
+ command = '{valgrind} {valgrind_args} {clamscan} -d {clam_exe_db} -d {not_eicar_db} --allmatch {testfiles}'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ # We can't use the hash sig for this clam.exe program because the hash goes out the window when we concatenate on the zip.
+ clam_exe_db=TC.path_db / 'clam.ndb',
+ not_eicar_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb',
+ testfiles=testfile,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus
+
+ expected_results = [
+ 'Test.NDB.UNOFFICIAL FOUND',
+ 'NDB.Clamav-Unit-Test-Signature.UNOFFICIAL FOUND',
+ ]
+ self.verify_output(output.out, expected=expected_results)
+
+ def test_pe_allmatch(self):
+ self.step_name('Test that clam will detect a string in test.exe with a wide variety of signatures written or generated for the file.')
+
+ # The sig set and test.exe for test set was written by one of our threat researchers to test the allmatch option.
+ # Overall, it's much more thorough than previous tests, but some of the tests are duplicates of the previous tests.
+
+ # TODO: The section signatures are not working as written, hence the "broken_dbs" directory.
+ # There is a known issue with relative offset signatures when using the Boyer-Moore matcher. The sigs work if using the Aho-Corasick matcher.
+ # When we fix section signatures, we can move them to the alerting sigs directory and update this test.
+
+ test_path = TC.path_source / 'unit_tests' / 'input' / 'pe_allmatch'
+ test_exe = test_path / 'test.exe'
+
+ command = '{valgrind} {valgrind_args} {clamscan} \
+ -d {alerting_dbs} \
+ -d {weak_dbs} \
+ --allmatch --bytecode-unsigned {testfiles}'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ alerting_dbs=test_path / 'alert-sigs',
+ weak_dbs=test_path / 'weak-sigs',
+ testfiles=test_exe,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1
+
+ # The alert sig files are all given the signature name, so we can verify that the correct sigs were found.
+ # We need only to trim off the extension and say "FOUND" for the alerting sigs.
+ # Note: Some of these have ".UNOFFICIAL" in the name because not all of them have that ".UNOFFICIAL" suffix when reported.
+ # I think this is a minor bug. So if we change that, we'll need to update this test.
+ expected_results = ['{sig} FOUND'.format(sig=f.stem) for f in (test_path / 'alert-sigs').iterdir()]
+
+ self.verify_output(output.out, expected=expected_results)
diff --git a/unit_tests/clamscan/assorted_test.py b/unit_tests/clamscan/assorted_test.py
new file mode 100644
index 0000000000..51a6355531
--- /dev/null
+++ b/unit_tests/clamscan/assorted_test.py
@@ -0,0 +1,158 @@
+# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+
+"""
+Run clamscan tests.
+"""
+
+import unittest
+import sys
+
+sys.path.append('../unit_tests')
+import testcase
+
+
+class TC(testcase.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TC, cls).setUpClass()
+
+ TC.testpaths = list(TC.path_build.glob('unit_tests/input/clamav_hdb_scanfiles/clam*')) # A list of Path()'s of each of our generated test files
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TC, cls).tearDownClass()
+
+ def setUp(self):
+ super(TC, self).setUp()
+
+ def tearDown(self):
+ super(TC, self).tearDown()
+ self.verify_valgrind_log()
+
+ def test_00_version(self):
+ self.step_name('clamscan version test')
+
+ command = '{valgrind} {valgrind_args} {clamscan} -V'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 0 # success
+
+ expected_results = [
+ 'ClamAV {}'.format(TC.version),
+ ]
+ self.verify_output(output.out, expected=expected_results)
+
+ def test_weak_indicator_icon(self):
+ self.step_name('Test icon (.ldb + .idb) weak indicator matching signatures')
+
+ (TC.path_tmp / 'icon.idb').write_text(
+ "EA0X-32x32x8:ea0x-grp1:ea0x-grp2:2046f030a42a07153f4120a0031600007000005e1617ef0000d21100cb090674150f880313970b0e7716116d01136216022500002f0a173700081a004a0e\n"
+ "IScab-16x16x8:iscab-grp1:iscab-grp2:107b3000168306015c20a0105b07060be0a0b11c050bea0706cb0a0bbb060b6f00017c06018301068109086b03046705081b000a270a002a000039002b17\n"
+ )
+ (TC.path_tmp / 'icon.ldb').write_text(
+ "ClamAV-Test-Icon-EA0X;Engine:52-1000,Target:1,IconGroup1:ea0x-grp1,IconGroup2:*;(0);0:4d5a\n"
+ "ClamAV-Test-Icon-IScab;Engine:52-1000,Target:1,IconGroup2:iscab-grp2;(0);0:4d5a\n"
+ )
+
+ testfiles = ' '.join([str(testpath) for testpath in TC.testpaths])
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_ldb} -d {path_idb} {testfiles}'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args,
+ clamscan=TC.clamscan,
+ path_ldb=TC.path_tmp / 'icon.ldb',
+ path_idb=TC.path_tmp / 'icon.idb',
+ testfiles=testfiles,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus found
+
+ # Use check_fpu_endian to determine expected results
+ command = '{}'.format(TC.check_fpu_endian)
+ fpu_endian_output = self.execute_command(command)
+
+ expected_results = [
+ 'clam_IScab_ext.exe: ClamAV-Test-Icon-IScab.UNOFFICIAL FOUND',
+ 'clam_IScab_int.exe: ClamAV-Test-Icon-IScab.UNOFFICIAL FOUND',
+ ]
+ if fpu_endian_output.ec == 3:
+ expected_num_infected = 3
+ else:
+ expected_results.append('clam.ea06.exe: ClamAV-Test-Icon-EA0X.UNOFFICIAL FOUND')
+ expected_num_infected = 4
+ expected_results.append('Infected files: {}'.format(expected_num_infected))
+ self.verify_output(output.out, expected=expected_results)
+
+ def test_yara_regex(self):
+ self.step_name('Test yara signature - detect TAR file magic in a range')
+
+ db = TC.path_tmp / 'regex.yara'
+ db.write_text(
+ r'''
+rule regex
+{
+ meta:
+ author = "Micah"
+ date = "2022/03/12"
+ description = "Just a test"
+ strings:
+ $a = "/+eat/" /* <-- not a regex */
+ $b = /\$protein+=\([a-z]+\)/ /* <-- is a regex */
+ condition:
+ all of them
+}
+ '''
+ )
+ testfile = TC.path_tmp / 'regex.sample'
+ testfile.write_text('var $protein=(slugs); /+eat/ $protein')
+
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles}'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, path_db=db, testfiles=testfile,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus found
+
+ expected_results = [
+ 'regex.sample: YARA.regex.UNOFFICIAL FOUND',
+ 'Infected files: 1',
+ ]
+ self.verify_output(output.out, expected=expected_results)
+
+ @unittest.expectedFailure
+ def test_pe_cert_trust(self):
+ self.step_name('Test that clam can trust an EXE based on an authenticode certificate check.')
+
+ # TODO: This feature was added in 0.105, but was also broken during that release cycle when we upgraded TomsFastMath.
+ # So instead of trusting the certificate, prints this out and the certificate is not trusted so the matches may still happen:
+ # LibClamAV Warning: crtmgr_rsa_verify: verification failed: fp_exptmod failed with 1
+ # We need to fix this, and then update this test.
+
+ test_path = TC.path_source / 'unit_tests' / 'input' / 'pe_allmatch'
+ test_exe = test_path / 'test.exe'
+
+ command = '{valgrind} {valgrind_args} {clamscan} \
+ -d {alerting_dbs} \
+ -d {weak_dbs} \
+ -d {trust_dbs} \
+ --allmatch --bytecode-unsigned {testfiles}'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ alerting_dbs=test_path / 'alert-sigs',
+ weak_dbs=test_path / 'weak-sigs',
+ trust_dbs=test_path / 'trust-sigs',
+ testfiles=test_exe,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 0
+
+ expected_results = ['OK']
+
+ # The alert sig files are all given the signature name, so we can verify that the correct sigs were found.
+ # We need only to trim off the extension and say "FOUND" for the alerting sigs.
+ # Note: Some of these have ".UNOFFICIAL" in the name because not all of them have that ".UNOFFICIAL" suffix when reported.
+ # I think this is a minor bug. So if we change that, we'll need to update this test.
+ unexpected_results = ['{sig} FOUND'.format(sig=f.stem) for f in (test_path / 'alert-sigs').iterdir()]
+
+ self.verify_output(output.out, expected=expected_results, unexpected=unexpected_results)
diff --git a/unit_tests/clamscan/bytecode_test.py b/unit_tests/clamscan/bytecode_test.py
new file mode 100644
index 0000000000..7b65e097fa
--- /dev/null
+++ b/unit_tests/clamscan/bytecode_test.py
@@ -0,0 +1,45 @@
+# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+
+"""
+Run clamscan tests.
+"""
+
+import sys
+
+sys.path.append('../unit_tests')
+import testcase
+
+
+class TC(testcase.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TC, cls).setUpClass()
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TC, cls).tearDownClass()
+
+ def setUp(self):
+ super(TC, self).setUp()
+
+ def tearDown(self):
+ super(TC, self).tearDown()
+ self.verify_valgrind_log()
+
+ def test_pdf_hook(self):
+ self.step_name('Test that pdf bytecode hooks trigger')
+
+ testfiles = TC.path_build / 'unit_tests' / 'input' / 'clamav_hdb_scanfiles' / 'clam.pdf'
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --bytecode-unsigned'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ path_db=TC.path_source / 'unit_tests' / 'input' / 'bytecode_sigs' / 'pdf-hook.cbc',
+ testfiles=testfiles,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus
+
+ expected_results = [
+ 'Test.Case.BC.PDF.hook FOUND',
+ ]
+ self.verify_output(output.out, expected=expected_results)
diff --git a/unit_tests/clamscan/container_sigs_test.py b/unit_tests/clamscan/container_sigs_test.py
new file mode 100644
index 0000000000..b93b30388f
--- /dev/null
+++ b/unit_tests/clamscan/container_sigs_test.py
@@ -0,0 +1,81 @@
+# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+
+"""
+Run clamscan tests.
+"""
+
+import sys
+
+sys.path.append('../unit_tests')
+import testcase
+
+
+class TC(testcase.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TC, cls).setUpClass()
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TC, cls).tearDownClass()
+
+ def setUp(self):
+ super(TC, self).setUp()
+
+ def tearDown(self):
+ super(TC, self).tearDown()
+ self.verify_valgrind_log()
+
+ def test_container(self):
+ self.step_name('Test that clamav can successfully alert on jpeg image extracted from XLS documents')
+ # Note: we aren't testing PNG because the attached PNG is not properly fuzzy-hashed by clamav, yet.
+
+ (TC.path_tmp / '7z_zip_container.ldb').write_text(
+ "7z_zip_container_good;Engine:81-255,Container:CL_TYPE_7Z,Target:0;0;0:7631727573\n"
+ "7z_zip_container_bad;Engine:81-255,Container:CL_TYPE_ZIP,Target:0;0;0:7631727573\n"
+ )
+
+ testfiles = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'v1rusv1rus.7z.zip'
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --gen-json --debug --allmatch'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ path_db=TC.path_tmp / '7z_zip_container.ldb',
+ testfiles=testfiles,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # no virus, no failures
+
+ expected_stdout = [
+ 'v1rusv1rus.7z.zip: 7z_zip_container_good.UNOFFICIAL FOUND',
+ ]
+ unexpected_stdout = [
+ 'v1rusv1rus.7z.zip: 7z_zip_container_bad.UNOFFICIAL FOUND',
+ ]
+ self.verify_output(output.out, expected=expected_stdout, unexpected=unexpected_stdout)
+
+ def test_intermediates(self):
+ self.step_name('Test that clamav can successfully alert on jpeg image extracted from XLS documents')
+ # Note: we aren't testing PNG because the attached PNG is not properly fuzzy-hashed by clamav, yet.
+
+ (TC.path_tmp / '7z_zip_intermediates.ldb').write_text(
+ "7z_zip_intermediates_good;Engine:81-255,Intermediates:CL_TYPE_ZIP>CL_TYPE_7Z,Target:0;0;0:7631727573\n"
+ "7z_zip_intermediates;Engine:81-255,Intermediates:CL_TYPE_7Z>CL_TYPE_TEXT_ASCII,Target:0;0;0:7631727573\n"
+ )
+
+ testfiles = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'v1rusv1rus.7z.zip'
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --gen-json --debug --allmatch'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ path_db=TC.path_tmp / '7z_zip_intermediates.ldb',
+ testfiles=testfiles,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # no virus, no failures
+
+ expected_stdout = [
+ 'v1rusv1rus.7z.zip: 7z_zip_intermediates_good.UNOFFICIAL FOUND',
+ ]
+ unexpected_stdout = [
+ 'v1rusv1rus.7z.zip: 7z_zip_intermediates_bad.UNOFFICIAL FOUND',
+ ]
+ self.verify_output(output.out, expected=expected_stdout, unexpected=unexpected_stdout)
diff --git a/unit_tests/clamscan/fp_check_test.py b/unit_tests/clamscan/fp_check_test.py
new file mode 100644
index 0000000000..5838931c69
--- /dev/null
+++ b/unit_tests/clamscan/fp_check_test.py
@@ -0,0 +1,221 @@
+# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+
+"""
+Run {valgrind} {valgrind_args} {clamscan} tests.
+"""
+
+import unittest
+import hashlib
+from zipfile import ZIP_DEFLATED, ZipFile
+import sys
+
+sys.path.append('../unit_tests')
+import testcase
+
+
+class TC(testcase.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TC, cls).setUpClass()
+
+ TC.test_file = TC.path_tmp / "test_file"
+ with open(TC.test_file, "wb") as testfile:
+ testfile.write(
+ b"""sfasfasf
+?>
+""")
+
+ TC.normalized_match_sig = TC.path_tmp / "normalized.ndb"
+ TC.normalized_match_sig.write_text(r"Malicious.PHP.normalized:0:*:69676e6f72655f757365725f61626f7274286173646629")
+
+ TC.original_hash_fp = TC.path_tmp / "original_hash.fp"
+ TC.original_hash_fp.write_text(r"a4b3c39134fa424beb9f84ffe5f175a3:190:original_hash")
+
+ TC.original_hash_wild_fp = TC.path_tmp / "original_hash.wild.fp"
+ TC.original_hash_wild_fp.write_text(r"a4b3c39134fa424beb9f84ffe5f175a3:*:original_hash.wild:73")
+
+ # The normalized hash is this for now. Changes to clamav normalization logic may require
+ # changes to this hash.
+ TC.normalized_hash_fp = TC.path_tmp / "normalized_hash.fp"
+ TC.normalized_hash_fp.write_text(r"0e32a3ab501afb50daedc04764f8dc16:188:normalized_hash")
+
+ TC.normalized_hash_wild_fp = TC.path_tmp / "normalized_hash.wild.fp"
+ TC.normalized_hash_wild_fp.write_text(r"0e32a3ab501afb50daedc04764f8dc16:*:normalized_hash.wild:73")
+
+ TC.test_file_zipped = TC.path_tmp / 'test_file.zip'
+ with ZipFile(str(TC.test_file_zipped), 'w', ZIP_DEFLATED) as zf:
+ # Add truncted PNG file that will alert with --alert-broken-media
+ with open(TC.path_source / 'logo.png', 'br') as logo_png:
+ zf.writestr('test_file', b"""sfasfasf
+?>
+""")
+
+ # Generate hash of the zipped file.
+ # Since we generated the zip in python, we don't know the hash in advance.
+ hash_md5 = hashlib.md5()
+ with TC.test_file_zipped.open("rb") as f:
+ for chunk in iter(lambda: f.read(4096), b""):
+ hash_md5.update(chunk)
+ hash_md5 = hash_md5.hexdigest()
+
+ TC.test_file_zipped_hash_fp = TC.path_tmp / 'test_file.zip.hash.fp'
+ TC.test_file_zipped_hash_fp.write_text('{hash}:{size}:test_file.zip'.format(
+ hash=hash_md5,
+ size=TC.test_file_zipped.stat().st_size))
+
+ TC.test_file_zipped_hash_wild_fp = TC.path_tmp / 'test_file.zip.hash.wild.fp'
+ TC.test_file_zipped_hash_wild_fp.write_text('{hash}:*:test_file.zip.wild:73'.format(
+ hash=hash_md5))
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TC, cls).tearDownClass()
+
+ def setUp(self):
+ super(TC, self).setUp()
+
+ def tearDown(self):
+ super(TC, self).tearDown()
+ self.verify_valgrind_log()
+
+ def test_alerts_on_normalized(self):
+ """
+ This test expects that the normalized pattern match sig without the .fp sig will in fact alert.
+ """
+ self.step_name("Test file detection with pattern from normalized HTML")
+
+ output = self.execute_command(
+ "{valgrind} {valgrind_args} {clamscan} {testfiles} -d {db1}".format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ testfiles=TC.test_file,
+ db1=TC.normalized_match_sig,
+ )
+ )
+ self.verify_output(output.out, expected=["Malicious.PHP.normalized.UNOFFICIAL FOUND"], unexpected=[])
+
+ def test_alerts_on_zip(self):
+ """
+ This test expects that the OG sig without the .fp sig will in fact alert.
+ """
+ self.step_name("Test file detection with pattern from normalized HTML inside a ZIP file")
+
+ output = self.execute_command(
+ "{valgrind} {valgrind_args} {clamscan} {testfiles} -d {db1}".format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ testfiles=TC.test_file_zipped,
+ db1=TC.normalized_match_sig,
+ )
+ )
+ self.verify_output(output.out, expected=["Malicious.PHP.normalized.UNOFFICIAL FOUND"], unexpected=[])
+
+ def test_fp_for_normalized(self):
+ """
+ This test expects that FP sigs for normalized HTML hashes will work,
+ because hashes are now created when an fmap is created and all embedded
+ file content to be scanned now gets its own fmap.
+ """
+ self.step_name("Test file trusted with fixed-size hash of the normalized HTML")
+
+ output = self.execute_command(
+ "{valgrind} {valgrind_args} {clamscan} {testfiles} -d {db1} -d {db2} ".format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ testfiles=TC.test_file,
+ db1=TC.normalized_match_sig,
+ db2=TC.normalized_hash_fp,
+ )
+ )
+ self.verify_output(output.out, expected=["OK"], unexpected=[])
+
+ def test_fp_for_normalized_wild(self):
+ """
+ This test expects that wildcard FP sigs for normalized HTML hashes will work,
+ because hashes are now created when an fmap is created and all embedded
+ file content to be scanned now gets its own fmap.
+ """
+ self.step_name("Test file trusted with wild-card hash of the normalized HTML")
+
+ output = self.execute_command(
+ "{valgrind} {valgrind_args} {clamscan} {testfiles} -d {db1} -d {db2} ".format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ testfiles=TC.test_file,
+ db1=TC.normalized_match_sig,
+ db2=TC.normalized_hash_wild_fp,
+ )
+ )
+ self.verify_output(output.out, expected=["OK"], unexpected=[])
+
+ def test_fp_for_nonnormalized(self):
+ """
+ This test expects that FP sigs for non-normalized HTML hashes will work,
+ because we now check each hash in the fmap recursion list.
+ """
+ self.step_name("Test file trusted with the original non-normalized fixed-size hash")
+
+ output = self.execute_command(
+ "{valgrind} {valgrind_args} {clamscan} {testfiles} -d {db1} -d {db2}".format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ testfiles=TC.test_file,
+ db1=TC.normalized_match_sig,
+ db2=TC.original_hash_fp,
+ )
+ )
+ self.verify_output(output.out, expected=["OK"], unexpected=[])
+
+ def test_fp_for_nonnormalized_wild(self):
+ """
+ This test expects that FP sigs for non-normalized HTML hashes will work,
+ because we now check each hash in the fmap recursion list.
+ """
+ self.step_name("Test file trusted with the original non-normalized wild-card hash")
+
+ output = self.execute_command(
+ "{valgrind} {valgrind_args} {clamscan} {testfiles} -d {db1} -d {db2}".format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ testfiles=TC.test_file,
+ db1=TC.normalized_match_sig,
+ db2=TC.original_hash_wild_fp,
+ )
+ )
+ self.verify_output(output.out, expected=["OK"], unexpected=[])
+
+ def test_fp_for_zipped_file(self):
+ """
+ This test expects that FP sigs for a zip containing the test file will work.
+ """
+ self.step_name("Test file trusted with fixed-size hash of zip containing test file")
+
+ output = self.execute_command(
+ "{valgrind} {valgrind_args} {clamscan} {testfiles} -d {db1} -d {db2}".format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ testfiles=TC.test_file_zipped,
+ db1=TC.normalized_match_sig,
+ db2=TC.test_file_zipped_hash_fp,
+ )
+ )
+ self.verify_output(output.out, expected=["OK"], unexpected=[])
+
+ def test_fp_for_zipped_file_wild(self):
+ """
+ This test expects that FP sigs for a zip containing the test file will work.
+ """
+ self.step_name("Test file trusted with wildcard hash of zip containing test file")
+
+ output = self.execute_command(
+ "{valgrind} {valgrind_args} {clamscan} {testfiles} -d {db1} -d {db2}".format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ testfiles=TC.test_file_zipped,
+ db1=TC.normalized_match_sig,
+ db2=TC.test_file_zipped_hash_wild_fp,
+ )
+ )
+ self.verify_output(output.out, expected=["OK"], unexpected=[])
diff --git a/unit_tests/clamscan/fuzzy_img_hash_test.py b/unit_tests/clamscan/fuzzy_img_hash_test.py
new file mode 100644
index 0000000000..1e86f11716
--- /dev/null
+++ b/unit_tests/clamscan/fuzzy_img_hash_test.py
@@ -0,0 +1,142 @@
+# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+
+"""
+Run clamscan tests.
+"""
+
+import sys
+
+sys.path.append('../unit_tests')
+import testcase
+
+
+class TC(testcase.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TC, cls).setUpClass()
+
+ # Testing with our logo png file.
+ TC.testfiles = TC.path_source / 'logo.png'
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TC, cls).tearDownClass()
+
+ def setUp(self):
+ super(TC, self).setUp()
+
+ def tearDown(self):
+ super(TC, self).tearDown()
+ self.verify_valgrind_log()
+
+ #
+ # First check with the good database in all-match mode.
+ #
+
+ def test_sigs_good_allmatch(self):
+ self.step_name('Test with a good database in all-match mode.')
+
+ (TC.path_tmp / 'good.ldb').write_text(
+ "logo.png.good;Engine:150-255,Target:0;0;fuzzy_img#af2ad01ed42993c7#0\n"
+ "logo.png.bad.with.second.subsig;Engine:150-255,Target:0;0&1;deadbeef;fuzzy_img#af2ad01ed42993c7#0\n"
+ "logo.png.good.with.second.subsig;Engine:150-255,Target:0;0&1;49484452;fuzzy_img#af2ad01ed42993c7#0\n"
+ )
+
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --allmatch'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ path_db=TC.path_tmp / 'good.ldb',
+ testfiles=TC.testfiles,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus
+
+ expected_stdout = [
+ 'logo.png.good.UNOFFICIAL FOUND',
+ 'logo.png.good.with.second.subsig.UNOFFICIAL FOUND',
+ ]
+ unexpected_stdout = [
+ 'logo.png.bad.with.second.subsig.UNOFFICIAL FOUND',
+ ]
+ self.verify_output(output.out, expected=expected_stdout)
+
+ #
+ # Next check with the bad signatures
+ #
+
+ def test_sigs_bad_hash(self):
+ self.step_name('Test Invalid hash')
+
+ # Invalid hash
+ (TC.path_tmp / 'invalid-hash.ldb').write_text(
+ "logo.png.bad;Engine:150-255,Target:0;0;fuzzy_img#abcdef#0\n"
+ )
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --allmatch'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ path_db=TC.path_tmp / 'invalid-hash.ldb',
+ testfiles=TC.testfiles,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 2 # error
+
+ expected_stderr = [
+ 'LibClamAV Error: Failed to load',
+ 'Invalid hash: Image fuzzy hash must be 16 characters in length: abcdef',
+ ]
+ unexpected_stdout = [
+ 'logo.png.bad.UNOFFICIAL FOUND',
+ ]
+ self.verify_output(output.err, expected=expected_stderr)
+ self.verify_output(output.out, unexpected=unexpected_stdout)
+
+ def test_sigs_bad_hamming(self):
+ self.step_name('Test Unsupported hamming distancee')
+
+ # Unsupported hamming distance
+ (TC.path_tmp / 'invalid-ham.ldb').write_text(
+ "logo.png.bad;Engine:150-255,Target:0;0;fuzzy_img#af2ad01ed42993c7#1\n"
+ )
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --allmatch'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ path_db=TC.path_tmp / 'invalid-ham.ldb',
+ testfiles=TC.testfiles,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 2 # error
+
+ expected_stderr = [
+ 'LibClamAV Error: Failed to load',
+ 'Invalid hamming distance: 1',
+ ]
+ unexpected_stdout = [
+ 'logo.png.bad.UNOFFICIAL FOUND',
+ ]
+ self.verify_output(output.err, expected=expected_stderr)
+ self.verify_output(output.out, unexpected=unexpected_stdout)
+
+ def test_sigs_bad_algorithm(self):
+ self.step_name('Test invalid fuzzy image hash algorithm')
+
+ # invalid algorithm
+ (TC.path_tmp / 'invalid-alg.ldb').write_text(
+ "logo.png.bad;Engine:150-255,Target:0;0;fuzzy_imgy#af2ad01ed42993c7#0\n"
+ )
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --allmatch'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ path_db=TC.path_tmp / 'invalid-alg.ldb',
+ testfiles=TC.testfiles,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 2 # error
+
+ expected_stderr = [
+ 'cli_loadldb: failed to parse subsignature 0 in logo.png',
+ ]
+ unexpected_stdout = [
+ 'logo.png.bad.UNOFFICIAL FOUND',
+ ]
+ self.verify_output(output.err, expected=expected_stderr)
+ self.verify_output(output.out, unexpected=unexpected_stdout)
diff --git a/unit_tests/clamscan/heuristics_test.py b/unit_tests/clamscan/heuristics_test.py
new file mode 100644
index 0000000000..a7ef351073
--- /dev/null
+++ b/unit_tests/clamscan/heuristics_test.py
@@ -0,0 +1,196 @@
+# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+
+"""
+Run clamscan tests.
+"""
+
+from zipfile import ZIP_DEFLATED, ZipFile
+import sys
+
+sys.path.append('../unit_tests')
+import testcase
+from testcase import STRICT_ORDER
+
+
+class TC(testcase.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TC, cls).setUpClass()
+
+ (TC.path_tmp / 'clam.ndb').write_text(
+ "Test.NDB:0:*:4b45524e454c33322e444c4c00004578\n"
+ )
+
+ # Create a ZIP that has two things:
+ # 1. malformed file that will alert with --alert-broken-media
+ # 2. the clam.exe file that will alert normally.
+ # The idea is that since the malformed file is first, the heuristic alert will be encountered first.
+ # The heuristic alert must behave as intended, depending on whether we use --allmatch, --heuristic-scan-precedence, etc.
+ TC.heuristics_testfile = TC.path_tmp / 'heuristics-test.zip'
+ with ZipFile(str(TC.heuristics_testfile), 'w', ZIP_DEFLATED) as zf:
+ # Add truncted PNG file that will alert with --alert-broken-media
+ with open(TC.path_source / 'logo.png', 'br') as logo_png:
+ zf.writestr('logo.png.truncated', logo_png.read(6378))
+
+ # Add clam.exe which will alert normally
+ clam_exe = TC.path_build / 'unit_tests' / 'input' / 'clamav_hdb_scanfiles' / 'clam.exe'
+ zf.writestr('clam.exe', clam_exe.read_bytes())
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TC, cls).tearDownClass()
+
+ def setUp(self):
+ super(TC, self).setUp()
+
+ def tearDown(self):
+ super(TC, self).tearDown()
+ self.verify_valgrind_log()
+
+ def test_hidden_by_strong_indicator(self):
+ '''
+ This test uses a ZIP that has two things:
+ 1. malformed file that will alert with --alert-broken-media
+ 2. the clam.exe file that will alert normally.
+ The idea is that since the malformed file is first, the heuristic alert will be encountered first.
+
+ In this test the heuristic alert must not alert because neither allmatch is specified, nor --heuristic-scan-precedence
+ '''
+ self.step_name('Test that a clam heuristic not alert because regular sig alerts first.')
+
+ testfile = TC.heuristics_testfile
+
+ command = '{valgrind} {valgrind_args} {clamscan} -d {clam_exe_db} {testfiles} --alert-broken-media'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ clam_exe_db=TC.path_tmp / 'clam.ndb',
+ testfiles=testfile,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus
+
+ expected_results = ['Test.NDB.UNOFFICIAL FOUND']
+ unexpected_results = ['Heuristics.Broken.Media.PNG.EOFReadingChunk FOUND']
+ self.verify_output(output.out, expected=expected_results, unexpected=unexpected_results)
+
+ def test_only_heur(self):
+ '''
+ This test uses a ZIP that has two things:
+ 1. malformed file that will alert with --alert-broken-media
+ 2. the clam.exe file that will alert normally.
+ The idea is that since the malformed file is first, the heuristic alert will be encountered first.
+
+ In this test the heuristic alert must alert because we don't use the sig for the other file.
+ '''
+ self.step_name('Test that a clam heuristic will alert, because it is the only detection.')
+
+ testfile = TC.heuristics_testfile
+
+ # Add an empty NDB file, because we need to pass in some sort of database.
+ (TC.path_tmp / 'empty.ndb').write_text(
+ "# Just a comment\n"
+ )
+
+ command = '{valgrind} {valgrind_args} {clamscan} -d {clam_exe_db} {testfiles} --alert-broken-media'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ clam_exe_db=TC.path_tmp / 'empty.ndb',
+ testfiles=testfile,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus
+
+ expected_results = ['Heuristics.Broken.Media.PNG.EOFReadingChunk FOUND']
+ unexpected_results = ['Test.NDB.UNOFFICIAL FOUND']
+ self.verify_output(output.out, expected=expected_results, unexpected=unexpected_results)
+
+ def test_precedence(self):
+ '''
+ This test uses a ZIP that has two things:
+ 1. malformed file that will alert with --alert-broken-media
+ 2. the clam.exe file that will alert normally.
+ The idea is that since the malformed file is first, the heuristic alert will be encountered first.
+
+ In this test the heuristic alert must alert first because --heuristic-scan-precedence is enabled.
+ We won't see the other alert because it's not allmatch mode.
+ '''
+ self.step_name('Test that a heuristic-precendence will cause the heuristic alert to happen first, with no other alerts because not allmatch.')
+
+ testfile = TC.heuristics_testfile
+
+ command = '{valgrind} {valgrind_args} {clamscan} -d {clam_exe_db} {testfiles} --alert-broken-media \
+ --heuristic-scan-precedence'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ clam_exe_db=TC.path_tmp / 'clam.ndb',
+ testfiles=testfile,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus
+
+ expected_results = ['Heuristics.Broken.Media.PNG.EOFReadingChunk FOUND']
+ unexpected_results = ['Test.NDB.UNOFFICIAL FOUND']
+ self.verify_output(output.out, expected=expected_results, unexpected=unexpected_results)
+
+ def test_allmatch(self):
+ '''
+ This test uses a ZIP that has two things:
+ 1. malformed file that will alert with --alert-broken-media
+ 2. the clam.exe file that will alert normally.
+ The idea is that since the malformed file is first, the heuristic alert will be encountered first.
+
+ In this test we use --allmatch but we don't use --heuristic-scan-precedence.
+ That means the NDB sig should alert first, even though the heuristic is encountered first.
+ Note the verify_output() uses STRICT_ORDER.
+ '''
+ self.step_name('Test that a clam heuristic alert will alert LAST in allmatch mode without heuristic-precedence.')
+
+ testfile = TC.heuristics_testfile
+
+ command = '{valgrind} {valgrind_args} {clamscan} -d {clam_exe_db} {testfiles} --alert-broken-media \
+ --allmatch'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ clam_exe_db=TC.path_tmp / 'clam.ndb',
+ testfiles=testfile,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus
+
+ expected_results = [
+ 'Test.NDB.UNOFFICIAL FOUND',
+ 'Heuristics.Broken.Media.PNG.EOFReadingChunk FOUND',
+ ]
+ self.verify_output(output.out, expected=expected_results, order=STRICT_ORDER)
+
+ def test_allmatch_precedence(self):
+ '''
+ This test uses a ZIP that has two things:
+ 1. malformed file that will alert with --alert-broken-media
+ 2. the clam.exe file that will alert normally.
+ The idea is that since the malformed file is first, the heuristic alert will be encountered first.
+
+ In this test we use --allmatch AND we use --heuristic-scan-precedence.
+ That means the heuristic is encountered first and should be treated equally, so it should alert first.
+ Note the verify_output() uses STRICT_ORDER.
+ '''
+ self.step_name('Test that a clam heuristic alert will alert FIRST in allmatch mode with heuristic-precedence.')
+
+ testfile = TC.heuristics_testfile
+
+ command = '{valgrind} {valgrind_args} {clamscan} -d {clam_exe_db} {testfiles} --alert-broken-media \
+ --allmatch \
+ --heuristic-scan-precedence'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ clam_exe_db=TC.path_tmp / 'clam.ndb',
+ testfiles=testfile,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus
+
+ expected_results = [
+ 'Heuristics.Broken.Media.PNG.EOFReadingChunk FOUND',
+ 'Test.NDB.UNOFFICIAL FOUND',
+ ]
+ self.verify_output(output.out, expected=expected_results, order=STRICT_ORDER)
diff --git a/unit_tests/clamscan/image_extraction_test.py b/unit_tests/clamscan/image_extraction_test.py
new file mode 100644
index 0000000000..e800eca30d
--- /dev/null
+++ b/unit_tests/clamscan/image_extraction_test.py
@@ -0,0 +1,85 @@
+# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+
+"""
+Run clamscan tests.
+"""
+
+import os
+import sys
+
+sys.path.append('../unit_tests')
+import testcase
+
+
+class TC(testcase.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TC, cls).setUpClass()
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TC, cls).tearDownClass()
+
+ def setUp(self):
+ super(TC, self).setUp()
+
+ def tearDown(self):
+ super(TC, self).tearDown()
+ self.verify_valgrind_log()
+
+ def test_xls_jpeg_png(self):
+ self.step_name('Test that clamav can successfully extract jpeg and png images from XLS documents')
+ # Note: we aren't testing BMP, TIFF, or GIF because excel converts them to PNG when you try to insert them.
+
+ testfiles = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'has_png_and_jpeg.xls'
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --gen-json --debug'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ path_db=TC.path_build / 'unit_tests' / 'input' / 'clamav.hdb',
+ testfiles=testfiles,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 0 # no virus, no failures
+
+ expected_results = [
+ 'Recognized PNG file',
+ 'Recognized JPEG file',
+ '"FileMD5":"41e64a9ddb49690f0b6fbbd71362b1b3"',
+ '"FileMD5":"5341e0efde53a50c416b2352263e7693"',
+ ]
+ self.verify_output(output.err, expected=expected_results)
+
+ def test_xls_with_detection(self):
+ self.step_name('Test that clamav can successfully alert on PNG image extracted from XLS documents')
+ # This tests a regression wherein extracted images weren't properly scanned, or the scan result recorded.
+ # Note: we aren't testing the JPEG detection because the JPEG attached to the sample XLS is not properly fuzzy-hashed by clamav, yet.
+ # TODO: Once it is working, add the JPEG detection test.
+
+ os.mkdir(str(TC.path_tmp / 'xls-jpeg-detection-sigs'))
+
+ (TC.path_tmp / 'logo.png.ldb').write_text(
+ "logo.png.good;Engine:150-255,Target:0;0;fuzzy_img#ea0f85d0de719887#0\n"
+ )
+
+ testfiles = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'has_png_and_jpeg.xls'
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --gen-json --debug --allmatch'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
+ path_db=TC.path_tmp / 'logo.png.ldb',
+ testfiles=testfiles,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # no virus, no failures
+
+ expected_stderr = [
+ 'Recognized PNG file',
+ 'Recognized JPEG file',
+ '"FileMD5":"41e64a9ddb49690f0b6fbbd71362b1b3"',
+ '"FileMD5":"5341e0efde53a50c416b2352263e7693"',
+ ]
+ self.verify_output(output.err, expected=expected_stderr)
+
+ expected_stdout = [
+ 'has_png_and_jpeg.xls: logo.png.good.UNOFFICIAL FOUND',
+ ]
+ self.verify_output(output.out, expected=expected_stdout)
diff --git a/unit_tests/clamscan/match_offsets_test.py b/unit_tests/clamscan/match_offsets_test.py
new file mode 100644
index 0000000000..6300006764
--- /dev/null
+++ b/unit_tests/clamscan/match_offsets_test.py
@@ -0,0 +1,94 @@
+# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+
+"""
+Run clamscan tests.
+"""
+
+import shutil
+import sys
+
+sys.path.append('../unit_tests')
+import testcase
+
+
+class TC(testcase.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TC, cls).setUpClass()
+
+ TC.testpaths = list(TC.path_build.glob('unit_tests/input/clamav_hdb_scanfiles/clam*')) # A list of Path()'s of each of our generated test files
+
+ (TC.path_tmp / 'Clam-VI.ldb').write_text(
+ "Clam-VI-Test:Target;Engine:52-255,Target:1;(0&1);VI:43006f006d00700061006e0079004e0061006d0065000000000063006f006d00700061006e007900;VI:500072006f0064007500630074004e0061006d0065000000000063006c0061006d00\n"
+ )
+ (TC.path_tmp / 'yara-at-offset.yara').write_text(
+ "rule yara_at_offset {strings: $tar_magic = { 75 73 74 61 72 } condition: $tar_magic at 257}\n"
+ )
+ (TC.path_tmp / 'yara-in-range.yara').write_text(
+ "rule yara_in_range {strings: $tar_magic = { 75 73 74 61 72 } condition: $tar_magic in (200..300)}\n"
+ )
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TC, cls).tearDownClass()
+
+ def setUp(self):
+ super(TC, self).setUp()
+
+ def tearDown(self):
+ super(TC, self).tearDown()
+ self.verify_valgrind_log()
+
+ def test_LDB_VI(self):
+ self.step_name('Test LDB VI feature')
+
+ testfiles = ' '.join([str(testpath) for testpath in TC.testpaths])
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles}'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, path_db=TC.path_tmp / 'Clam-VI.ldb', testfiles=testfiles,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus found
+
+ expected_results = [
+ 'clam_ISmsi_ext.exe: Clam-VI-Test:Target.UNOFFICIAL FOUND',
+ 'clam_ISmsi_int.exe: Clam-VI-Test:Target.UNOFFICIAL FOUND',
+ 'Infected files: 2',
+ ]
+ self.verify_output(output.out, expected=expected_results)
+
+ def test_yara_at_offset(self):
+ self.step_name('Test yara signature - detect TAR file magic at an offset')
+
+ testfiles = ' '.join([str(testpath) for testpath in TC.testpaths])
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles}'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, path_db=TC.path_tmp / 'yara-at-offset.yara', testfiles=testfiles,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus found
+
+ expected_results = [
+ 'clam.tar.gz: YARA.yara_at_offset.UNOFFICIAL FOUND',
+ 'clam_cache_emax.tgz: YARA.yara_at_offset.UNOFFICIAL FOUND',
+ 'Infected files: 3',
+ ]
+ self.verify_output(output.out, expected=expected_results)
+
+ def test_yara_in_range(self):
+ self.step_name('Test yara signature - detect TAR file magic in a range')
+
+ testfiles = ' '.join([str(testpath) for testpath in TC.testpaths])
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles}'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, path_db=TC.path_tmp / 'yara-in-range.yara', testfiles=testfiles,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus found
+
+ expected_results = [
+ 'clam.tar.gz: YARA.yara_in_range.UNOFFICIAL FOUND',
+ 'clam_cache_emax.tgz: YARA.yara_in_range.UNOFFICIAL FOUND',
+ 'Infected files: 3',
+ ]
+ self.verify_output(output.out, expected=expected_results)
diff --git a/unit_tests/clamscan/phishing_test.py b/unit_tests/clamscan/phishing_test.py
new file mode 100644
index 0000000000..a07dd9a844
--- /dev/null
+++ b/unit_tests/clamscan/phishing_test.py
@@ -0,0 +1,75 @@
+# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+
+"""
+Run clamscan tests.
+"""
+
+import sys
+
+sys.path.append('../unit_tests')
+import testcase
+
+
+class TC(testcase.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ super(TC, cls).setUpClass()
+
+ (TC.path_tmp / 'monitor-example-com.pdb').write_text('H:example.com\n')
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TC, cls).tearDownClass()
+
+ def setUp(self):
+ super(TC, self).setUp()
+
+ def tearDown(self):
+ super(TC, self).tearDown()
+ self.verify_valgrind_log()
+
+ def test_not_enabled(self):
+ self.step_name('Test that clamscan will load the phishing sigs w/out issue')
+
+ testpaths = list(TC.path_source.glob('unit_tests/input/other_scanfiles/phish-test-*'))
+
+ testfiles = ' '.join([str(testpath) for testpath in testpaths])
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles}'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args,
+ clamscan=TC.clamscan,
+ path_db=TC.path_tmp / 'monitor-example-com.pdb',
+ testfiles=testfiles,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 0 # virus NOT found
+
+ expected_results = [
+ 'Scanned files: 3',
+ 'Infected files: 0',
+ ]
+ self.verify_output(output.out, expected=expected_results)
+
+ def test_ssl_and_cloak(self):
+ self.step_name('Test clamscan --alert-phishing-ssl --alert-phishing-cloak')
+
+ testpaths = list(TC.path_source.glob('unit_tests/input/other_scanfiles/phish-test-*'))
+
+ testfiles = ' '.join([str(testpath) for testpath in testpaths])
+ command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --alert-phishing-ssl --alert-phishing-cloak {testfiles}'.format(
+ valgrind=TC.valgrind, valgrind_args=TC.valgrind_args,
+ clamscan=TC.clamscan,
+ path_db=TC.path_tmp / 'monitor-example-com.pdb',
+ testfiles=testfiles,
+ )
+ output = self.execute_command(command)
+
+ assert output.ec == 1 # virus found
+
+ expected_results = [
+ 'phish-test-ssl: Heuristics.Phishing.Email.SSL-Spoof FOUND',
+ 'phish-test-cloak: Heuristics.Phishing.Email.Cloaked.Null FOUND',
+ 'Scanned files: 3',
+ 'Infected files: 2', # there's a clean one
+ ]
+ self.verify_output(output.out, expected=expected_results)
diff --git a/unit_tests/clamscan_test.py b/unit_tests/clamscan_test.py
deleted file mode 100644
index d007f2749c..0000000000
--- a/unit_tests/clamscan_test.py
+++ /dev/null
@@ -1,736 +0,0 @@
-# Copyright (C) 2020-2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
-
-"""
-Run clamscan tests.
-"""
-
-import os
-from pathlib import Path
-import platform
-import shutil
-import subprocess
-import sys
-import time
-import unittest
-from zipfile import ZIP_DEFLATED, ZipFile
-
-import testcase
-
-
-os_platform = platform.platform()
-operating_system = os_platform.split('-')[0].lower()
-
-
-class TC(testcase.TestCase):
- @classmethod
- def setUpClass(cls):
- super(TC, cls).setUpClass()
-
- TC.testpaths = list(TC.path_build.glob('unit_tests/input/clamav_hdb_scanfiles/clam*')) # A list of Path()'s of each of our generated test files
-
- # Prepare a directory to store our test databases
- TC.path_db = TC.path_tmp / 'database'
- TC.path_db.mkdir(parents=True)
-
- shutil.copy(
- str(TC.path_build / 'unit_tests' / 'input' / 'clamav.hdb'),
- str(TC.path_db),
- )
-
- (TC.path_db / 'clamav.ign2').write_text('ClamAV-Test-File\n')
-
- (TC.path_db / 'phish.pdb').write_text('H:example.com\n')
-
- (TC.path_db / 'icon.idb').write_text(
- "EA0X-32x32x8:ea0x-grp1:ea0x-grp2:2046f030a42a07153f4120a0031600007000005e1617ef0000d21100cb090674150f880313970b0e7716116d01136216022500002f0a173700081a004a0e\n"
- "IScab-16x16x8:iscab-grp1:iscab-grp2:107b3000168306015c20a0105b07060be0a0b11c050bea0706cb0a0bbb060b6f00017c06018301068109086b03046705081b000a270a002a000039002b17\n"
- )
- (TC.path_db / 'icon.ldb').write_text(
- "ClamAV-Test-Icon-EA0X;Engine:52-1000,Target:1,IconGroup1:ea0x-grp1,IconGroup2:*;(0);0:4d5a\n"
- "ClamAV-Test-Icon-IScab;Engine:52-1000,Target:1,IconGroup2:iscab-grp2;(0);0:4d5a\n"
- )
- (TC.path_db / 'Clam-VI.ldb').write_text(
- "Clam-VI-Test:Target;Engine:52-255,Target:1;(0&1);VI:43006f006d00700061006e0079004e0061006d0065000000000063006f006d00700061006e007900;VI:500072006f0064007500630074004e0061006d0065000000000063006c0061006d00\n"
- )
- (TC.path_db / 'yara-at-offset.yara').write_text(
- "rule yara_at_offset {strings: $tar_magic = { 75 73 74 61 72 } condition: $tar_magic at 257}\n"
- )
- (TC.path_db / 'yara-in-range.yara').write_text(
- "rule yara_in_range {strings: $tar_magic = { 75 73 74 61 72 } condition: $tar_magic in (200..300)}\n"
- )
-
- @classmethod
- def tearDownClass(cls):
- super(TC, cls).tearDownClass()
-
- def setUp(self):
- super(TC, self).setUp()
-
- def tearDown(self):
- super(TC, self).tearDown()
- self.verify_valgrind_log()
-
- def test_clamscan_00_version(self):
- self.step_name('clamscan version test')
-
- command = '{valgrind} {valgrind_args} {clamscan} -V'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan
- )
- output = self.execute_command(command)
-
- assert output.ec == 0 # success
-
- expected_results = [
- 'ClamAV {}'.format(TC.version),
- ]
- self.verify_output(output.out, expected=expected_results)
-
- def test_clamscan_01_all_testfiles(self):
- self.step_name('Test that clamscan alerts on all test files')
-
- testfiles = ' '.join([str(testpath) for testpath in TC.testpaths])
- command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles}'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, path_db=TC.path_db / "clamav.hdb", testfiles=testfiles,
- )
- output = self.execute_command(command)
-
- assert output.ec == 1 # virus found
-
- expected_results = ['{}: ClamAV-Test-File.UNOFFICIAL FOUND'.format(testpath.name) for testpath in TC.testpaths]
- expected_results.append('Scanned files: {}'.format(len(TC.testpaths)))
- expected_results.append('Infected files: {}'.format(len(TC.testpaths)))
- self.verify_output(output.out, expected=expected_results)
-
- def test_clamscan_02_all_testfiles_ign2(self):
- self.step_name('Test that clamscan ignores ClamAV-Test-File alerts')
-
- testfiles = ' '.join([str(testpath) for testpath in TC.testpaths])
- command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} -d {path_ign_db} {testfiles}'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, path_db=TC.path_db / "clamav.hdb", path_ign_db=TC.path_db / "clamav.ign2", testfiles=testfiles,
- )
- output = self.execute_command(command)
-
- assert output.ec == 1 # virus found
-
- expected_results = ['{}: ClamAV-Test-File.UNOFFICIAL FOUND'.format(testpath.name) for testpath in TC.testpaths]
- expected_results.append('Scanned files: {}'.format(len(TC.testpaths)))
- expected_results.append('Infected files: {}'.format(len(TC.testpaths)))
- self.verify_output(output.out, expected=expected_results)
-
- def test_clamscan_03_phish_test_not_enabled(self):
- self.step_name('Test that clamscan will load the phishing sigs w/out issue')
-
- testpaths = list(TC.path_source.glob('unit_tests/input/other_scanfiles/phish-test-*'))
-
- testfiles = ' '.join([str(testpath) for testpath in testpaths])
- command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles}'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, path_db=TC.path_db / "phish.pdb", path_ign_db=TC.path_db / "clamav.ign2", testfiles=testfiles,
- )
- output = self.execute_command(command)
-
- assert output.ec == 0 # virus NOT found
-
- expected_results = [
- 'Scanned files: 3',
- 'Infected files: 0',
- ]
- self.verify_output(output.out, expected=expected_results)
-
- def test_clamscan_04_phish_test_alert_phishing_ssl_alert_phishing_cloak(self):
- self.step_name('Test clamscan --alert-phishing-ssl --alert-phishing-cloak')
-
- testpaths = list(TC.path_source.glob('unit_tests/input/other_scanfiles/phish-test-*'))
-
- testfiles = ' '.join([str(testpath) for testpath in testpaths])
- command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} --alert-phishing-ssl --alert-phishing-cloak {testfiles}'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, path_db=TC.path_db / "phish.pdb", testfiles=testfiles,
- )
- output = self.execute_command(command)
-
- assert output.ec == 1 # virus found
-
- expected_results = [
- 'phish-test-ssl: Heuristics.Phishing.Email.SSL-Spoof FOUND',
- 'phish-test-cloak: Heuristics.Phishing.Email.Cloaked.Null FOUND',
- 'Scanned files: 3',
- 'Infected files: 2', # there's a clean one
- ]
- self.verify_output(output.out, expected=expected_results)
-
- def test_clamscan_05_icon(self):
- self.step_name('Test icon (.ldb + .idb) signatures')
-
- testfiles = ' '.join([str(testpath) for testpath in TC.testpaths])
- command = '{valgrind} {valgrind_args} {clamscan} -d {path_ldb} -d {path_idb} {testfiles}'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, path_ldb=TC.path_db / "icon.ldb", path_idb=TC.path_db / "icon.idb", testfiles=testfiles,
- )
- output = self.execute_command(command)
-
- assert output.ec == 1 # virus found
-
- # Use check_fpu_endian to determine expected results
- command = '{}'.format(TC.check_fpu_endian)
- fpu_endian_output = self.execute_command(command)
-
- expected_results = [
- 'clam_IScab_ext.exe: ClamAV-Test-Icon-IScab.UNOFFICIAL FOUND',
- 'clam_IScab_int.exe: ClamAV-Test-Icon-IScab.UNOFFICIAL FOUND',
- ]
- if fpu_endian_output.ec == 3:
- expected_num_infected = 3
- else:
- expected_results.append('clam.ea06.exe: ClamAV-Test-Icon-EA0X.UNOFFICIAL FOUND')
- expected_num_infected = 4
- expected_results.append('Infected files: {}'.format(expected_num_infected))
- self.verify_output(output.out, expected=expected_results)
-
- def test_clamscan_06_LDB_VI(self):
- self.step_name('Test LDB VI feature')
-
- testfiles = ' '.join([str(testpath) for testpath in TC.testpaths])
- command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles}'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, path_db=TC.path_db / "Clam-VI.ldb", testfiles=testfiles,
- )
- output = self.execute_command(command)
-
- assert output.ec == 1 # virus found
-
- expected_results = [
- 'clam_ISmsi_ext.exe: Clam-VI-Test:Target.UNOFFICIAL FOUND',
- 'clam_ISmsi_int.exe: Clam-VI-Test:Target.UNOFFICIAL FOUND',
- 'Infected files: 2',
- ]
- self.verify_output(output.out, expected=expected_results)
-
- def test_clamscan_07_yara_at_offset(self):
- self.step_name('Test yara signature - detect TAR file magic at an offset')
-
- testfiles = ' '.join([str(testpath) for testpath in TC.testpaths])
- command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles}'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, path_db=TC.path_db / "yara-at-offset.yara", testfiles=testfiles,
- )
- output = self.execute_command(command)
-
- assert output.ec == 1 # virus found
-
- expected_results = [
- 'clam.tar.gz: YARA.yara_at_offset.UNOFFICIAL FOUND',
- 'clam_cache_emax.tgz: YARA.yara_at_offset.UNOFFICIAL FOUND',
- 'Infected files: 3',
- ]
- self.verify_output(output.out, expected=expected_results)
-
- def test_clamscan_08_yara_in_range(self):
- self.step_name('Test yara signature - detect TAR file magic in a range')
-
- testfiles = ' '.join([str(testpath) for testpath in TC.testpaths])
- command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles}'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, path_db=TC.path_db / "yara-in-range.yara", testfiles=testfiles,
- )
- output = self.execute_command(command)
-
- assert output.ec == 1 # virus found
-
- expected_results = [
- 'clam.tar.gz: YARA.yara_in_range.UNOFFICIAL FOUND',
- 'clam_cache_emax.tgz: YARA.yara_in_range.UNOFFICIAL FOUND',
- 'Infected files: 3',
- ]
- self.verify_output(output.out, expected=expected_results)
-
- def test_clamscan_09_xls_jpeg_png_extraction(self):
- self.step_name('Test that clamav can successfully extract jpeg and png images from XLS documents')
- # Note: we aren't testing BMP, TIFF, or GIF because excel converts them to PNG when you try to insert them.
-
- testfiles = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'has_png_and_jpeg.xls'
- command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --gen-json --debug'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
- path_db=TC.path_build / 'unit_tests' / 'input' / 'clamav.hdb',
- testfiles=testfiles,
- )
- output = self.execute_command(command)
-
- assert output.ec == 0 # no virus, no failures
-
- expected_results = [
- 'Recognized PNG file',
- 'Recognized JPEG file',
- '"FileMD5":"41e64a9ddb49690f0b6fbbd71362b1b3"',
- '"FileMD5":"5341e0efde53a50c416b2352263e7693"',
- ]
- self.verify_output(output.err, expected=expected_results)
-
- def test_clamscan_10_bytecode_pdf_hook(self):
- self.step_name('Test that pdf bytecode hooks trigger')
-
- testfiles = TC.path_build / 'unit_tests' / 'input' / 'clamav_hdb_scanfiles' / 'clam.pdf'
- command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --bytecode-unsigned'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
- path_db=TC.path_source / 'unit_tests' / 'input' / 'bytecode_sigs' / 'pdf-hook.cbc',
- testfiles=testfiles,
- )
- output = self.execute_command(command)
-
- assert output.ec == 1 # virus
-
- expected_results = [
- 'Test.Case.BC.PDF.hook FOUND',
- ]
- self.verify_output(output.out, expected=expected_results)
-
- def test_clamscan_11_allmatch_hash_sigs(self):
- self.step_name('Test that each type of hash sig is detected in all-match mode')
-
- os.mkdir(str(TC.path_db / 'allmatch-test-sigs'))
-
- (TC.path_db / 'allmatch-test-sigs' / 'clam.hdb').write_text(
- "aa15bcf478d165efd2065190eb473bcb:544:Test.MD5.Hash:73\n"
- "aa15bcf478d165efd2065190eb473bcb:*:Test.MD5.Hash.NoSize:73\n"
- )
- (TC.path_db / 'allmatch-test-sigs' / 'clam.hsb').write_text(
- "71e7b604d18aefd839e51a39c88df8383bb4c071dc31f87f00a2b5df580d4495:544:Test.Sha256.Hash:73\n"
- "71e7b604d18aefd839e51a39c88df8383bb4c071dc31f87f00a2b5df580d4495:*:Test.Sha256.Hash.NoSize:73\n"
- "62dd70f5e7530e0239901ac186f1f9ae39292561:544:Test.Sha1.Hash:73\n"
- "62dd70f5e7530e0239901ac186f1f9ae39292561:*:Test.Sha1.NoSize:73\n"
- )
- (TC.path_db / 'allmatch-test-sigs' / 'clam.imp').write_text(
- "98c88d882f01a3f6ac1e5f7dfd761624:39:Test.Import.Hash\n"
- "98c88d882f01a3f6ac1e5f7dfd761624:*:Test.Import.Hash.NoSize\n"
- )
- (TC.path_db / 'allmatch-test-sigs' / 'clam.mdb').write_text(
- "512:23db1dd3f77fae25610b6a32701313ae:Test.PESection.Hash:73\n"
- "*:23db1dd3f77fae25610b6a32701313ae:Test.PESection.Hash.NoSize:73\n"
- )
-
- testfiles = TC.path_build / 'unit_tests' / 'input' / 'clamav_hdb_scanfiles' / 'clam.exe'
-
- command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --allmatch'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
- path_db=TC.path_db / 'allmatch-test-sigs',
- testfiles=testfiles,
- )
- output = self.execute_command(command)
-
- assert output.ec == 1 # virus
-
- expected_results = [
- 'Test.MD5.Hash.UNOFFICIAL FOUND',
- 'Test.MD5.Hash.NoSize.UNOFFICIAL FOUND',
- 'Test.Sha1.Hash.UNOFFICIAL FOUND',
- 'Test.Sha1.NoSize.UNOFFICIAL FOUND',
- 'Test.Sha256.Hash.UNOFFICIAL FOUND',
- 'Test.Sha256.Hash.NoSize.UNOFFICIAL FOUND',
- 'Test.PESection.Hash.UNOFFICIAL FOUND',
- 'Test.PESection.Hash.NoSize.UNOFFICIAL FOUND',
- 'Test.Import.Hash.UNOFFICIAL FOUND',
- 'Test.Import.Hash.NoSize.UNOFFICIAL FOUND',
- ]
- self.verify_output(output.out, expected=expected_results)
-
- def test_clamscan_11_allmatch_regression_imphash_nosize(self):
- self.step_name('Test an import hash with wildcard size when all-match mode is disabled.')
-
- db_dir = TC.path_db / 'allmatch-regression-test-sigs'
-
- os.mkdir(str(db_dir))
-
- (db_dir / 'clam.imp').write_text(
- "98c88d882f01a3f6ac1e5f7dfd761624:*:Test.Import.Hash.NoSize\n"
- )
-
- testfiles = TC.path_build / 'unit_tests' / 'input' / 'clamav_hdb_scanfiles' / 'clam.exe'
-
- command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles}'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
- path_db=db_dir / 'clam.imp',
- testfiles=testfiles,
- )
- output = self.execute_command(command)
-
- assert output.ec == 1 # virus
-
- expected_results = [
- 'Test.Import.Hash.NoSize.UNOFFICIAL FOUND',
- ]
- self.verify_output(output.out, expected=expected_results)
-
- def test_clamscan_11_allmatch_regression_cbc_and_ndb(self):
- self.step_name('Test that bytecode rules will run after content match alerts in all-match mode.')
-
- # Source for ClamAV-Unit-Test_Signature.cbc
- # ```c
- # VIRUSNAME_PREFIX("BC.Clamav-Unit-Test-Signature")
- # VIRUSNAMES("")
- # TARGET(0)
-
- # FUNCTIONALITY_LEVEL_MIN(FUNC_LEVEL_096_4)
-
- # SIGNATURES_DECL_BEGIN
- # DECLARE_SIGNATURE(test_string)
- # SIGNATURES_DECL_END
-
- # SIGNATURES_DEF_BEGIN
- # /* matches "CLAMAV-TEST-STRING-NOT-EICAR" */
- # DEFINE_SIGNATURE(test_string, "0:434c414d41562d544553542d535452494e472d4e4f542d4549434152")
- # SIGNATURES_DEF_END
-
- # bool logical_trigger()
- # {
- # return matches(Signatures.test_string);
- # }
-
- # int entrypoint(void)
- # {
- # foundVirus("");
- # return 0;
- # }
- # ```
-
- testfile = TC.path_tmp / 'CLAMAV-TEST-STRING-NOT-EICAR'
-
- (testfile).write_text(
- "CLAMAV-TEST-STRING-NOT-EICAR"
- )
-
- command = '{valgrind} {valgrind_args} {clamscan} -d {cbc_db} -d {ndb_db} --bytecode-unsigned --allmatch {testfiles}'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
- cbc_db=TC.path_source / 'unit_tests' / 'input' / 'bytecode_sigs' / 'Clamav-Unit-Test-Signature.cbc',
- ndb_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb',
- testfiles=testfile,
- )
- output = self.execute_command(command)
-
- assert output.ec == 1 # virus
-
- expected_results = [
- 'BC.Clamav-Unit-Test-Signature FOUND', # <-- ".UNOFFICIAL" is not added for bytecode signatures
- 'NDB.Clamav-Unit-Test-Signature.UNOFFICIAL FOUND',
- ]
- self.verify_output(output.out, expected=expected_results)
-
- def test_clamscan_11_allmatch_txt_plus_clam_zipsfx(self):
- self.step_name('Test that clam will detect a string in text file, plus identify, extract, and alert on concatenated clam.zip containing clam.exe with a hash sig.')
-
- testfile = TC.path_tmp / 'test-string-cat-clam.exe.txt'
-
- clamzip = TC.path_build / 'unit_tests' / 'input' / 'clamav_hdb_scanfiles' / 'clam.zip'
-
- testfile.write_bytes(b"CLAMAV-TEST-STRING-NOT-EICAR" + clamzip.read_bytes())
-
- command = '{valgrind} {valgrind_args} {clamscan} -d {clam_exe_db} -d {not_eicar_db} --allmatch {testfiles}'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
- clam_exe_db=TC.path_source / 'unit_tests' / 'input' / 'clamav.hdb',
- not_eicar_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb',
- testfiles=testfile,
- )
- output = self.execute_command(command)
-
- assert output.ec == 1 # virus
-
- expected_results = [
- 'ClamAV-Test-File.UNOFFICIAL FOUND',
- 'NDB.Clamav-Unit-Test-Signature.UNOFFICIAL FOUND',
- ]
- self.verify_output(output.out, expected=expected_results)
-
- def test_clamscan_11_allmatch_clam_exe_imphash_plus_not_eicar_zipsfx(self):
- self.step_name('Test that clam will detect a string in text file, plus identify, extract, and alert on concatenated clam.zip containing clam.exe with an imp-hash sig.')
-
- # We can't use the hash sig for this clam.exe program because the hash goes out the window when we concatenate on the zip.
- (TC.path_tmp / 'clam.imp').write_text(
- "98c88d882f01a3f6ac1e5f7dfd761624:39:Test.Import.Hash\n"
- )
-
- # Build a file that is the clam.exe program with a zip concatinated on that contains the not_eicar test string file.
- clam_exe = TC.path_build / 'unit_tests' / 'input' / 'clamav_hdb_scanfiles' / 'clam.exe'
-
- not_eicar_zip = TC.path_tmp / 'not-eicar.zip'
- with ZipFile(str(not_eicar_zip), 'w', ZIP_DEFLATED) as zf:
- zf.writestr('not-eicar.txt', b"CLAMAV-TEST-STRING-NOT-EICAR")
-
- testfile = TC.path_tmp / 'clam.exe.not_eicar.zipsfx'
- testfile.write_bytes(clam_exe.read_bytes() + not_eicar_zip.read_bytes())
-
- command = '{valgrind} {valgrind_args} {clamscan} -d {clam_exe_db} -d {not_eicar_db} --allmatch {testfiles}'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
- clam_exe_db=TC.path_tmp / 'clam.imp',
- not_eicar_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb',
- testfiles=testfile,
- )
- output = self.execute_command(command)
-
- assert output.ec == 1 # virus
-
- expected_results = [
- 'Test.Import.Hash.UNOFFICIAL FOUND',
- 'NDB.Clamav-Unit-Test-Signature.UNOFFICIAL FOUND',
- ]
- self.verify_output(output.out, expected=expected_results)
-
- def test_clamscan_11_allmatch_clam_exe_pattern_plus_not_eicar_zipsfx(self):
- self.step_name('Test that clam will detect a string in text file, plus identify, extract, and alert on concatenated clam.zip containing clam.exe with a pattern-match sig.')
- # This tests a regression where clam will fail to extract the embedded zip file if the pattern-match sig matches before the embedded file type sig.
-
- # We can't use the hash sig for this clam.exe program because the hash goes out the window when we concatenate on the zip.
- (TC.path_tmp / 'clam.ndb').write_text(
- "Test.Pattern.Match:0:*:4b45524e454c33322e444c4c00004578\n"
- )
-
- # Build a file that is the clam.exe program with a zip concatinated on that contains the not_eicar test string file.
- clam_exe = TC.path_build / 'unit_tests' / 'input' / 'clamav_hdb_scanfiles' / 'clam.exe'
-
- not_eicar_zip = TC.path_tmp / 'not-eicar.zip'
- with ZipFile(str(not_eicar_zip), 'w', ZIP_DEFLATED) as zf:
- zf.writestr('not-eicar.txt', b"CLAMAV-TEST-STRING-NOT-EICAR")
-
- testfile = TC.path_tmp / 'clam.exe.not_eicar.zipsfx'
- testfile.write_bytes(clam_exe.read_bytes() + not_eicar_zip.read_bytes())
-
- command = '{valgrind} {valgrind_args} {clamscan} -d {clam_exe_db} -d {not_eicar_db} --allmatch {testfiles}'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
- clam_exe_db=TC.path_tmp / 'clam.ndb',
- not_eicar_db=TC.path_source / 'unit_tests' / 'input' / 'other_sigs' / 'Clamav-Unit-Test-Signature.ndb',
- testfiles=testfile,
- )
- output = self.execute_command(command)
-
- assert output.ec == 1 # virus
-
- expected_results = [
- 'Test.Pattern.Match.UNOFFICIAL FOUND',
- 'NDB.Clamav-Unit-Test-Signature.UNOFFICIAL FOUND',
- ]
- self.verify_output(output.out, expected=expected_results)
-
- def test_clamscan_12_image_fuzzy_hash_sigs(self):
- self.step_name('Test that each type of hash sig is detected in all-match mode')
-
- os.mkdir(str(TC.path_db / 'image-fuzzy-hash-test-sigs'))
-
- (TC.path_db / 'image-fuzzy-hash-test-sigs' / 'good.ldb').write_text(
- "logo.png.good;Engine:150-255,Target:0;0;fuzzy_img#af2ad01ed42993c7#0\n"
- "logo.png.bad.with.second.subsig;Engine:150-255,Target:0;0&1;deadbeef;fuzzy_img#af2ad01ed42993c7#0\n"
- "logo.png.good.with.second.subsig;Engine:150-255,Target:0;0&1;49484452;fuzzy_img#af2ad01ed42993c7#0\n"
- )
-
- testfiles = TC.path_source / 'logo.png'
-
- #
- # First check with the good database in all-match mode.
- #
- command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --allmatch'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
- path_db=TC.path_db / 'image-fuzzy-hash-test-sigs' / 'good.ldb',
- testfiles=testfiles,
- )
- output = self.execute_command(command)
-
- assert output.ec == 1 # virus
-
- expected_stdout = [
- 'logo.png.good.UNOFFICIAL FOUND',
- 'logo.png.good.with.second.subsig.UNOFFICIAL FOUND',
- ]
- unexpected_stdout = [
- 'logo.png.bad.with.second.subsig.UNOFFICIAL FOUND',
- ]
- self.verify_output(output.out, expected=expected_stdout)
-
- #
- # Next check with the bad signatures
- #
-
- # Invalid hash
- (TC.path_db / 'image-fuzzy-hash-test-sigs' / 'invalid-hash.ldb').write_text(
- "logo.png.bad;Engine:150-255,Target:0;0;fuzzy_img#abcdef#0\n"
- )
- command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --allmatch'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
- path_db=TC.path_db / 'image-fuzzy-hash-test-sigs' / 'invalid-hash.ldb',
- testfiles=testfiles,
- )
- output = self.execute_command(command)
-
- assert output.ec == 2 # error
-
- expected_stderr = [
- 'LibClamAV Error: Failed to load',
- 'Invalid hash: Image fuzzy hash must be 16 characters in length: abcdef',
- ]
- unexpected_stdout = [
- 'logo.png.bad.UNOFFICIAL FOUND',
- ]
- self.verify_output(output.err, expected=expected_stderr)
- self.verify_output(output.out, unexpected=unexpected_stdout)
-
- # Unsupported hamming distance
- (TC.path_db / 'image-fuzzy-hash-test-sigs' / 'invalid-ham.ldb').write_text(
- "logo.png.bad;Engine:150-255,Target:0;0;fuzzy_img#af2ad01ed42993c7#1\n"
- )
- command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --allmatch'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
- path_db=TC.path_db / 'image-fuzzy-hash-test-sigs' / 'invalid-ham.ldb',
- testfiles=testfiles,
- )
- output = self.execute_command(command)
-
- assert output.ec == 2 # error
-
- expected_stderr = [
- 'LibClamAV Error: Failed to load',
- 'Invalid hamming distance: 1',
- ]
- unexpected_stdout = [
- 'logo.png.bad.UNOFFICIAL FOUND',
- ]
- self.verify_output(output.err, expected=expected_stderr)
- self.verify_output(output.out, unexpected=unexpected_stdout)
-
- # invalid algorithm
- (TC.path_db / 'image-fuzzy-hash-test-sigs' / 'invalid-alg.ldb').write_text(
- "logo.png.bad;Engine:150-255,Target:0;0;fuzzy_imgy#af2ad01ed42993c7#0\n"
- )
- command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --allmatch'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
- path_db=TC.path_db / 'image-fuzzy-hash-test-sigs' / 'invalid-alg.ldb',
- testfiles=testfiles,
- )
- output = self.execute_command(command)
-
- assert output.ec == 2 # error
-
- expected_stderr = [
- 'cli_loadldb: failed to parse subsignature 0 in logo.png',
- ]
- unexpected_stdout = [
- 'logo.png.bad.UNOFFICIAL FOUND',
- ]
- self.verify_output(output.err, expected=expected_stderr)
- self.verify_output(output.out, unexpected=unexpected_stdout)
-
- def test_clamscan_13_yara_regex(self):
- self.step_name('Test yara signature - detect TAR file magic in a range')
-
- db = TC.path_tmp / 'regex.yara'
- db.write_text(
- r'''
-rule regex
-{
- meta:
- author = "Micah"
- date = "2022/03/12"
- description = "Just a test"
- strings:
- $a = "/+eat/" /* <-- not a regex */
- $b = /\$protein+=\([a-z]+\)/ /* <-- is a regex */
- condition:
- all of them
-}
- '''
- )
- testfile = TC.path_tmp / 'regex.sample'
- testfile.write_text('var $protein=(slugs); /+eat/ $protein')
-
- command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles}'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan, path_db=db, testfiles=testfile,
- )
- output = self.execute_command(command)
-
- assert output.ec == 1 # virus found
-
- expected_results = [
- 'regex.sample: YARA.regex.UNOFFICIAL FOUND',
- 'Infected files: 1',
- ]
- self.verify_output(output.out, expected=expected_results)
-
- def test_clamscan_14_xls_jpeg_detection(self):
- self.step_name('Test that clamav can successfully alert on jpeg image extracted from XLS documents')
- # Note: we aren't testing PNG because the attached PNG is not properly fuzzy-hashed by clamav, yet.
-
- os.mkdir(str(TC.path_db / 'xls-jpeg-detection-sigs'))
-
- (TC.path_db / 'image-fuzzy-hash-test-sigs' / 'good.ldb').write_text(
- "logo.png.good;Engine:150-255,Target:0;0;fuzzy_img#ea0f85d0de719887#0\n"
- )
-
- testfiles = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'has_png_and_jpeg.xls'
- command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --gen-json --debug --allmatch'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
- path_db=TC.path_db / 'image-fuzzy-hash-test-sigs' / 'good.ldb',
- testfiles=testfiles,
- )
- output = self.execute_command(command)
-
- assert output.ec == 1 # no virus, no failures
-
- expected_stderr = [
- 'Recognized PNG file',
- 'Recognized JPEG file',
- '"FileMD5":"41e64a9ddb49690f0b6fbbd71362b1b3"',
- '"FileMD5":"5341e0efde53a50c416b2352263e7693"',
- ]
- self.verify_output(output.err, expected=expected_stderr)
-
- expected_stdout = [
- 'has_png_and_jpeg.xls: logo.png.good.UNOFFICIAL FOUND',
- ]
- self.verify_output(output.out, expected=expected_stdout)
-
- def test_clamscan_15_container(self):
- self.step_name('Test that clamav can successfully alert on jpeg image extracted from XLS documents')
- # Note: we aren't testing PNG because the attached PNG is not properly fuzzy-hashed by clamav, yet.
-
- os.mkdir(str(TC.path_db / '7z_zip_container'))
-
- (TC.path_db / '7z_zip_container' / 'test.ldb').write_text(
- "7z_zip_container_good;Engine:81-255,Container:CL_TYPE_7Z,Target:0;0;0:7631727573\n"
- "7z_zip_container_bad;Engine:81-255,Container:CL_TYPE_ZIP,Target:0;0;0:7631727573\n"
- )
-
- testfiles = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'v1rusv1rus.7z.zip'
- command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --gen-json --debug --allmatch'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
- path_db=TC.path_db / '7z_zip_container' / 'test.ldb',
- testfiles=testfiles,
- )
- output = self.execute_command(command)
-
- assert output.ec == 1 # no virus, no failures
-
- expected_stdout = [
- 'v1rusv1rus.7z.zip: 7z_zip_container_good.UNOFFICIAL FOUND',
- ]
- unexpected_stdout = [
- 'v1rusv1rus.7z.zip: 7z_zip_container_bad.UNOFFICIAL FOUND',
- ]
- self.verify_output(output.out, expected=expected_stdout, unexpected=unexpected_stdout)
-
- def test_clamscan_16_intermediates(self):
- self.step_name('Test that clamav can successfully alert on jpeg image extracted from XLS documents')
- # Note: we aren't testing PNG because the attached PNG is not properly fuzzy-hashed by clamav, yet.
-
- os.mkdir(str(TC.path_db / '7z_zip_intermediates'))
-
- (TC.path_db / '7z_zip_intermediates' / 'test.ldb').write_text(
- "7z_zip_intermediates_good;Engine:81-255,Intermediates:CL_TYPE_ZIP>CL_TYPE_7Z,Target:0;0;0:7631727573\n"
- "7z_zip_intermediates;Engine:81-255,Intermediates:CL_TYPE_7Z>CL_TYPE_TEXT_ASCII,Target:0;0;0:7631727573\n"
- )
-
- testfiles = TC.path_source / 'unit_tests' / 'input' / 'other_scanfiles' / 'v1rusv1rus.7z.zip'
- command = '{valgrind} {valgrind_args} {clamscan} -d {path_db} {testfiles} --gen-json --debug --allmatch'.format(
- valgrind=TC.valgrind, valgrind_args=TC.valgrind_args, clamscan=TC.clamscan,
- path_db=TC.path_db / '7z_zip_intermediates' / 'test.ldb',
- testfiles=testfiles,
- )
- output = self.execute_command(command)
-
- assert output.ec == 1 # no virus, no failures
-
- expected_stdout = [
- 'v1rusv1rus.7z.zip: 7z_zip_intermediates_good.UNOFFICIAL FOUND',
- ]
- unexpected_stdout = [
- 'v1rusv1rus.7z.zip: 7z_zip_intermediates_bad.UNOFFICIAL FOUND',
- ]
- self.verify_output(output.out, expected=expected_stdout, unexpected=unexpected_stdout)
diff --git a/unit_tests/input/pe_allmatch/README.md b/unit_tests/input/pe_allmatch/README.md
new file mode 100644
index 0000000000..beab848325
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/README.md
@@ -0,0 +1,95 @@
+## Overview
+
+This aims to provide many different types of ClamAV rules for the given `test.exe` x86 Windows executable binary in order to test ClamAV's ability to report multiple signature matches (`-z` or `--allmatch` in `clamscan`).
+
+- `src` - contains the code necessary to rebuild the `test.exe` program.
+- `alert-sigs` are a combination of signatures generated by this tool, and those that were manually created.
+ - As the name implies, these signatures are expected to alert on the `text.exe` program.
+ - Some of these signatures depend on signatures in `weak-sigs`.
+- `weak-sigs` are a combination of signatures generated by this tool, and those that were manually created.
+ - As the name implies, these signatures support the `alert-sigs`, but do not alert on their own.
+
+The tools necessary to (re)generate those sigs that were generated are not included here.
+
+## Requirements
+
+These are the requirements to build the `test.exe` program.
+The requirements, features needed to generate those of the sig test set that weren't hand-written are not provided here.
+
+- `python3`
+- `mingw-w64`
+ - Just `sudo apt install mingw-w64` on ubuntu 20.04
+- `osslsigncode`
+ - The ubuntu package is dated, but it's pretty easy to install from https://github.com/mtrojnar/osslsigncode
+ - Follow the directions for those prereqs.
+ - Note: you can use these `cmake` commands:
+ ```bash
+ mkdir build && cd build
+ cmake .. && cmake --build .
+ sudo cmake --install . --prefix /usr/local/bin
+ ```
+
+## Steps to regenerate the `test.exe` program
+
+1. Build the binary with: `./build.py`
+
+2. Generate the signatures. First run:
+ ```sh
+ mkdir gen
+ ```
+ Then run:
+ ```sh
+ python generate.py build/test.exe
+ ```
+
+## Testing:
+ - Example invocation:
+```
+$ clamscan -z -d gen/ -d manual/ build/test.exe --bytecode-unsigned --no-summary | sort -n
+test.exe: Test.GenSig.HSB_01of06_MD5_FIXED.UNOFFICIAL FOUND
+test.exe: Test.GenSig.MSB_01of90_MD5_FIXED_dottext.UNOFFICIAL FOUND
+test.exe: Test.GenSig.MSB_02of90_SHA1_FIXED_dottext.UNOFFICIAL FOUND
+test.exe: Test.GenSig.MSB_03of90_SHA256_FIXED_dottext.UNOFFICIAL FOUND
+test.exe: Test.GenSig.MSB_04of90_MD5_STAR_dottext.UNOFFICIAL FOUND
+test.exe: Test.GenSig.MSB_05of90_SHA1_STAR_dottext.UNOFFICIAL FOUND
+test.exe: Test.GenSig.MSB_06of90_SHA256_STAR_dottext.UNOFFICIAL FOUND
+test.exe: Test.GenSig.NDB_01of08_legalcopyright.UNOFFICIAL FOUND
+test.exe: Test.GenSig.NDB_02of08_internalname.UNOFFICIAL FOUND
+test.exe: Test.GenSig.NDB_03of08_fileversion.UNOFFICIAL FOUND
+test.exe: Test.GenSig.NDB_04of08_companyname.UNOFFICIAL FOUND
+test.exe: Test.GenSig.NDB_05of08_productname.UNOFFICIAL FOUND
+test.exe: Test.GenSig.NDB_06of08_productversion.UNOFFICIAL FOUND
+test.exe: Test.GenSig.NDB_07of08_filedescription.UNOFFICIAL FOUND
+test.exe: Test.GenSig.NDB_08of08_originalfilename.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_01of16_PE_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_02of16_PE_2.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_03of16_ANY_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_04of16_ANY_2.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_05of16_PE_EP_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_06of16_PE_EP_2.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_07of16_PE_SE1_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_09of16_PE_S1_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_10of16_PE_S1_2.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_11of16_PE_PCRE_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_12of16_PE_PCRE_2.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_13of16_ANY_PCRE_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_14of16_ANY_PCRE_2.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_15of16_PE_ICON_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_16of16_PE_ICON_2.UNOFFICIAL FOUND
+test.exe: Test.Sig.NDB_01of10_PE_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.NDB_02of10_PE_2.UNOFFICIAL FOUND
+test.exe: Test.Sig.NDB_03of10_ANY_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.NDB_04of10_ANY_2.UNOFFICIAL FOUND
+test.exe: Test.Sig.NDB_05of10_PE_EP_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.NDB_06of10_PE_EP_2.UNOFFICIAL FOUND
+test.exe: Test.Sig.NDB_07of10_PE_SE2_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.NDB_09of10_PE_S1_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.NDB_10of10_PE_S1_2.UNOFFICIAL FOUND
+test.exe: YARA.Test_Sig_YARA_1of1_strings.UNOFFICIAL FOUND
+```
+
+## Steps to generate the .ico file:
+```
+head -c 245760 /dev/urandom | convert -depth 8 -size 320x256 RGB:- test.png
+convert -background transparent test.png -define icon:auto-resize=16,32,48,64,256 test.ico
+```
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.BC_1of6_ANY_testexe_0.matched.cbc b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.BC_1of6_ANY_testexe_0.matched.cbc
new file mode 100644
index 0000000000..1c364c4ed5
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.BC_1of6_ANY_testexe_0.matched.cbc
@@ -0,0 +1,12 @@
+ClamBCafhijhbcnbf|aimfifcfafcgnfigdf```c``a```|ah`cnbac`cccnbac``b`aaap`clamcoincidencejb:4096
+Test.GenSig.BC_1of6_ANY_testexe_0.{matched};Engine:56-255,Target:0;0;434c414d41565f544553545f5052494e54465f535452494e475f666661385f333939345f313738385f65386236
+Teddaaahdabahdacahdadahdaeahdafahdagahebodebadaacb`bbadb`bdbjbah
+Eaeaaaebnd|amcgefdgfgifbgegcgnfafmfef``
+G`ac`@`bodBdeBefBcgBdgBnbBgdBefBnfBceBifBgfBnbBbdBcdBoeBacBofBffBfcBoeBadBndBieBoeBdgBefBcgBdgBefBhgBefBoeB`cBnbBmfBafBdgBcfBhfBefBdf@`bad@Aa`
+A`b`bLaab`b`Fabaa
+Bb`b`abbaeAb`BibdTcab`b@dE
+SSfeidbeeecendadmdedoe`ebeedfdidhehbbbdeefcgdgnbgdefnfceifgfnbbdcdoeacoffffcoeadndieoedgefcgdgefhgefoe`cbbibSfeidbeeecendadmdedcehbbbmfafdgcfhfefdfbbibSdeadbegdeddehb`cib
+SceidgdndaddeeebeedceoeddedcdldoebdedgdidndSobob`bldofofkfcg`bffofbgjc`bcdldadmdadfeoedeedcedeoe`ebeidnddefdoecedebeidndgdoeffffafhcoeccicicdcoeacgchchcoeefhcbffc
+ddedcdldadbeedoeceidgdndaddeeebeedhbdgefcgdgibSceidgdndaddeeebeedceoeddedcdldoeedndddSSceidgdndaddeeebeedceoeddedfdoebdedgdidndSddedfdidndedoeceidgdndaddeeebeedhbdgefcgdglb`bbbdcccdccfdcacdcdfdcacecfcecffecdcdcececccecdcecffec`cecbcdcicdcefecdcdcfcecffecccecdcecbcdcicdcefdcgcecfffcfcfcfcfcaccchcecffccccccicccicccdcecffccacccgccchccchcecfffceccchcfcbcccfcbbib
+ceidgdndaddeeebeedceoeddedfdoeedndddSSbfofoflf`blfofgfifcfaflfoedgbgifgfgfefbghbibSkgSbgefdgegbgnf`bmfafdgcfhfefcghbceifgfnfafdgegbgefcgnbdgefcgdgibkcSmgSSSSifnfdg`befnfdgbgig`gofifnfdghbfgofifdfib
+kgSffofegnfdffeifbgegcghbbbmfafdgcfhfefdfbbibkcSbgefdgegbgnf`b`ckcSmgS
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.BC_2of6_ANY_testexe_1.matched.cbc b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.BC_2of6_ANY_testexe_1.matched.cbc
new file mode 100644
index 0000000000..b2f05f4d89
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.BC_2of6_ANY_testexe_1.matched.cbc
@@ -0,0 +1,12 @@
+ClamBCafhjjhbcnbf|aimfifcfafcgnfigdf```c``a```|ah`cnbac`cccnbac``b`aaap`clamcoincidencejb:4096
+Test.GenSig.BC_2of6_ANY_testexe_1.{matched};Engine:56-255,Target:0;0;434c414d41565f544553545f5052494e54465f535452494e475f653464375f376234335f333135355f30343061
+Teddaaahdabahdacahdadahdaeahdafahdagahebodebadaacb`bbadb`bdbjbah
+Eaeaaaebnd|amcgefdgfgifbgegcgnfafmfef``
+G`ac`@`bodBdeBefBcgBdgBnbBgdBefBnfBceBifBgfBnbBbdBcdBoeBbcBofBffBfcBoeBadBndBieBoeBdgBefBcgBdgBefBhgBefBoeBacBnbBmfBafBdgBcfBhfBefBdf@`bad@Aa`
+A`b`bLaab`b`Fabaa
+Bb`b`abbaeAb`BibdTcab`b@dE
+SSfeidbeeecendadmdedoe`ebeedfdidhehbbbdeefcgdgnbgdefnfceifgfnbbdcdoebcoffffcoeadndieoedgefcgdgefhgefoeacbbibSfeidbeeecendadmdedcehbbbmfafdgcfhfefdfbbibSdeadbegdeddehb`cib
+SceidgdndaddeeebeedceoeddedcdldoebdedgdidndSobob`bldofofkfcg`bffofbgjc`bcdldadmdadfeoedeedcedeoe`ebeidnddefdoecedebeidndgdoeefdcdfgcoegcbfdcccoeccacececoe`cdc`caf
+ddedcdldadbeedoeceidgdndaddeeebeedhbdgefcgdgibSceidgdndaddeeebeedceoeddedcdldoeedndddSSceidgdndaddeeebeedceoeddedfdoebdedgdidndSddedfdidndedoeceidgdndaddeeebeedhbdgefcgdglb`bbbdcccdccfdcacdcdfdcacecfcecffecdcdcececccecdcecffec`cecbcdcicdcefecdcdcfcecffecccecdcecbcdcicdcefdcgcecfffcecccdcfcdcccgcecffccgcfcbcccdcccccecffccccccacccecccececffcc`cccdccc`cfcacbbib
+ceidgdndaddeeebeedceoeddedfdoeedndddSSbfofoflf`blfofgfifcfaflfoedgbgifgfgfefbghbibSkgSbgefdgegbgnf`bmfafdgcfhfefcghbceifgfnfafdgegbgefcgnbdgefcgdgibkcSmgSSSSifnfdg`befnfdgbgig`gofifnfdghbfgofifdfib
+kgSffofegnfdffeifbgegcghbbbmfafdgcfhfefdfbbibkcSbgefdgegbgnf`b`ckcSmgS
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.BC_3of6_PE_REG_testexe_0.matched.cbc b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.BC_3of6_PE_REG_testexe_0.matched.cbc
new file mode 100644
index 0000000000..75620a9191
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.BC_3of6_PE_REG_testexe_0.matched.cbc
@@ -0,0 +1,12 @@
+ClamBCafhjjhbcnbf|aimfifcfafcgnfigdf```c``a```|ah`cnbac`cccnbac``b`aaap`clamcoincidencejb:4096
+Test.GenSig.BC_3of6_PE_REG_testexe_0.{matched};Engine:56-255,Target:1;0;434c414d41565f544553545f5052494e54465f535452494e475f666661385f333939345f313738385f65386236
+Teddaaahdabahdacahdadahdaeahdafahdagahebodebadaacb`bbadb`bdbmbah
+Eaeaaaebnd|amcgefdgfgifbgegcgnfafmfef``
+G`ac`@`bodBdeBefBcgBdgBnbBgdBefBnfBceBifBgfBnbBbdBcdBoeBccBofBffBfcBoeB`eBedBoeBbeBedBgdBoeBdgBefBcgBdgBefBhgBefBoeB`cBnbBmfBafBdgBcfBhfBefBdf@`bad@Aa`
+A`b`bLaab`b`Fabaa
+Bb`b`abbaeAb`BlbdTcab`b@dE
+SSfeidbeeecendadmdedoe`ebeedfdidhehbbbdeefcgdgnbgdefnfceifgfnbbdcdoeccoffffcoe`eedoebeedgdoedgefcgdgefhgefoe`cbbibSfeidbeeecendadmdedcehbbbmfafdgcfhfefdfbbibSdeadbegdeddehbacib
+SceidgdndaddeeebeedceoeddedcdldoebdedgdidndSobob`bldofofkfcg`bffofbgjc`bcdldadmdadfeoedeedcedeoe`ebeidnddefdoecedebeidndgdoeffffafhcoeccicicdcoeacgchchcoeefhcbffc
+ddedcdldadbeedoeceidgdndaddeeebeedhbdgefcgdgibSceidgdndaddeeebeedceoeddedcdldoeedndddSSceidgdndaddeeebeedceoeddedfdoebdedgdidndSddedfdidndedoeceidgdndaddeeebeedhbdgefcgdglb`bbbdcccdccfdcacdcdfdcacecfcecffecdcdcececccecdcecffec`cecbcdcicdcefecdcdcfcecffecccecdcecbcdcicdcefdcgcecfffcfcfcfcfcaccchcecffccccccicccicccdcecffccacccgccchccchcecfffceccchcfcbcccfcbbib
+ceidgdndaddeeebeedceoeddedfdoeedndddSSbfofoflf`blfofgfifcfaflfoedgbgifgfgfefbghbibSkgSbgefdgegbgnf`bmfafdgcfhfefcghbceifgfnfafdgegbgefcgnbdgefcgdgibkcSmgSSSSifnfdg`befnfdgbgig`gofifnfdghbfgofifdfib
+kgSffofegnfdffeifbgegcghbbbmfafdgcfhfefdfbbibkcSbgefdgegbgnf`b`ckcSmgS
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.BC_4of6_PE_REG_testexe_1.matched.cbc b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.BC_4of6_PE_REG_testexe_1.matched.cbc
new file mode 100644
index 0000000000..28e502904e
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.BC_4of6_PE_REG_testexe_1.matched.cbc
@@ -0,0 +1,12 @@
+ClamBCafhjjhbcnbf|aimfifcfafcgnfigdf```c``a```|ah`cnbac`cccnbac``b`aaap`clamcoincidencejb:4096
+Test.GenSig.BC_4of6_PE_REG_testexe_1.{matched};Engine:56-255,Target:1;0;434c414d41565f544553545f5052494e54465f535452494e475f653464375f376234335f333135355f30343061
+Teddaaahdabahdacahdadahdaeahdafahdagahebodebadaacb`bbadb`bdbmbah
+Eaeaaaebnd|amcgefdgfgifbgegcgnfafmfef``
+G`ac`@`bodBdeBefBcgBdgBnbBgdBefBnfBceBifBgfBnbBbdBcdBoeBdcBofBffBfcBoeB`eBedBoeBbeBedBgdBoeBdgBefBcgBdgBefBhgBefBoeBacBnbBmfBafBdgBcfBhfBefBdf@`bad@Aa`
+A`b`bLaab`b`Fabaa
+Bb`b`abbaeAb`BlbdTcab`b@dE
+SSfeidbeeecendadmdedoe`ebeedfdidhehbbbdeefcgdgnbgdefnfceifgfnbbdcdoedcoffffcoe`eedoebeedgdoedgefcgdgefhgefoeacbbibSfeidbeeecendadmdedcehbbbmfafdgcfhfefdfbbibSdeadbegdeddehbacib
+SceidgdndaddeeebeedceoeddedcdldoebdedgdidndSobob`bldofofkfcg`bffofbgjc`bcdldadmdadfeoedeedcedeoe`ebeidnddefdoecedebeidndgdoeefdcdfgcoegcbfdcccoeccacececoe`cdc`caf
+ddedcdldadbeedoeceidgdndaddeeebeedhbdgefcgdgibSceidgdndaddeeebeedceoeddedcdldoeedndddSSceidgdndaddeeebeedceoeddedfdoebdedgdidndSddedfdidndedoeceidgdndaddeeebeedhbdgefcgdglb`bbbdcccdccfdcacdcdfdcacecfcecffecdcdcececccecdcecffec`cecbcdcicdcefecdcdcfcecffecccecdcecbcdcicdcefdcgcecfffcecccdcfcdcccgcecffccgcfcbcccdcccccecffccccccacccecccececffcc`cccdccc`cfcacbbib
+ceidgdndaddeeebeedceoeddedfdoeedndddSSbfofoflf`blfofgfifcfaflfoedgbgifgfgfefbghbibSkgSbgefdgegbgnf`bmfafdgcfhfefcghbceifgfnfafdgegbgefcgnbdgefcgdgibkcSmgSSSSifnfdg`befnfdgbgig`gofifnfdghbfgofifdfib
+kgSffofegnfdffeifbgegcghbbbmfafdgcfhfefdfbbibkcSbgefdgegbgnf`b`ckcSmgS
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.BC_5of6_PE_UNPCKR_testexe_0.matched.cbc b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.BC_5of6_PE_UNPCKR_testexe_0.matched.cbc
new file mode 100644
index 0000000000..2714eadb36
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.BC_5of6_PE_UNPCKR_testexe_0.matched.cbc
@@ -0,0 +1,12 @@
+ClamBCafhjjhbcnbf|aimfifcfafcgnfigdf```ca`a```|ah`cnbac`cccnbac``b`aaap`clamcoincidencejb:4096
+Test.GenSig.BC_5of6_PE_UNPCKR_testexe_0.{matched};Engine:56-255,Target:1;0;434c414d41565f544553545f5052494e54465f535452494e475f666661385f333939345f313738385f65386236
+Teddaaahdabahdacahdadahdaeahdafahdagahebodebadaacb`bbadb`bdb`cah
+Eaeaaaebnd|amcgefdgfgifbgegcgnfafmfef``
+G`ac`@`bodBdeBefBcgBdgBnbBgdBefBnfBceBifBgfBnbBbdBcdBoeBecBofBffBfcBoeB`eBedBoeBeeBndB`eBcdBkdBbeBoeBdgBefBcgBdgBefBhgBefBoeB`cBnbBmfBafBdgBcfBhfBefBdf@`bad@Aa`
+A`b`bLaab`b`Fabaa
+Bb`b`abbaeAb`BobdTcab`b@dE
+SSfeidbeeecendadmdedoe`ebeedfdidhehbbbdeefcgdgnbgdefnfceifgfnbbdcdoeecoffffcoe`eedoeeend`ecdkdbeoedgefcgdgefhgefoe`cbbibSfeidbeeecendadmdedcehbbbmfafdgcfhfefdfbbib
+deadbegdeddehbacibSSceidgdndaddeeebeedceoeddedcdldoebdedgdidndSobob`bldofofkfcg`bffofbgjc`bcdldadmdadfeoedeedcedeoe`ebeidnddefdoecedebeidndgdoeffffafhcoeccicicdcoeacgchchcoeefhcbffc
+ddedcdldadbeedoeceidgdndaddeeebeedhbdgefcgdgibSceidgdndaddeeebeedceoeddedcdldoeedndddSSceidgdndaddeeebeedceoeddedfdoebdedgdidndSddedfdidndedoeceidgdndaddeeebeedhbdgefcgdglb`bbbdcccdccfdcacdcdfdcacecfcecffecdcdcececccecdcecffec`cecbcdcicdcefecdcdcfcecffecccecdcecbcdcicdcefdcgcecfffcfcfcfcfcaccchcecffccccccicccicccdcecffccacccgccchccchcecfffceccchcfcbcccfcbbib
+ceidgdndaddeeebeedceoeddedfdoeedndddSSbfofoflf`blfofgfifcfaflfoedgbgifgfgfefbghbibSkgSbgefdgegbgnf`bmfafdgcfhfefcghbceifgfnfafdgegbgefcgnbdgefcgdgibkcSmgSS`eedoeeend`eadcdkdedbeoeddedcdldadbeed
+Sifnfdg`befnfdgbgig`gofifnfdghbfgofifdfibSkgSffofegnfdffeifbgegcghbbbmfafdgcfhfefdfbbibkcSbgefdgegbgnf`b`ckcSmgS
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.BC_6of6_PE_UNPCKR_testexe_1.matched.cbc b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.BC_6of6_PE_UNPCKR_testexe_1.matched.cbc
new file mode 100644
index 0000000000..1c19ec4424
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.BC_6of6_PE_UNPCKR_testexe_1.matched.cbc
@@ -0,0 +1,12 @@
+ClamBCafhjjhbcnbf|aimfifcfafcgnfigdf```ca`a```|ah`cnbac`cccnbac``b`aaap`clamcoincidencejb:4096
+Test.GenSig.BC_6of6_PE_UNPCKR_testexe_1.{matched};Engine:56-255,Target:1;0;434c414d41565f544553545f5052494e54465f535452494e475f653464375f376234335f333135355f30343061
+Teddaaahdabahdacahdadahdaeahdafahdagahebodebadaacb`bbadb`bdb`cah
+Eaeaaaebnd|amcgefdgfgifbgegcgnfafmfef``
+G`ac`@`bodBdeBefBcgBdgBnbBgdBefBnfBceBifBgfBnbBbdBcdBoeBfcBofBffBfcBoeB`eBedBoeBeeBndB`eBcdBkdBbeBoeBdgBefBcgBdgBefBhgBefBoeBacBnbBmfBafBdgBcfBhfBefBdf@`bad@Aa`
+A`b`bLaab`b`Fabaa
+Bb`b`abbaeAb`BobdTcab`b@dE
+SSfeidbeeecendadmdedoe`ebeedfdidhehbbbdeefcgdgnbgdefnfceifgfnbbdcdoefcoffffcoe`eedoeeend`ecdkdbeoedgefcgdgefhgefoeacbbibSfeidbeeecendadmdedcehbbbmfafdgcfhfefdfbbib
+deadbegdeddehbacibSSceidgdndaddeeebeedceoeddedcdldoebdedgdidndSobob`bldofofkfcg`bffofbgjc`bcdldadmdadfeoedeedcedeoe`ebeidnddefdoecedebeidndgdoeefdcdfgcoegcbfdcccoeccacececoe`cdc`caf
+ddedcdldadbeedoeceidgdndaddeeebeedhbdgefcgdgibSceidgdndaddeeebeedceoeddedcdldoeedndddSSceidgdndaddeeebeedceoeddedfdoebdedgdidndSddedfdidndedoeceidgdndaddeeebeedhbdgefcgdglb`bbbdcccdccfdcacdcdfdcacecfcecffecdcdcececccecdcecffec`cecbcdcicdcefecdcdcfcecffecccecdcecbcdcicdcefdcgcecfffcecccdcfcdcccgcecffccgcfcbcccdcccccecffccccccacccecccececffcc`cccdccc`cfcacbbib
+ceidgdndaddeeebeedceoeddedfdoeedndddSSbfofoflf`blfofgfifcfaflfoedgbgifgfgfefbghbibSkgSbgefdgegbgnf`bmfafdgcfhfefcghbceifgfnfafdgegbgefcgnbdgefcgdgibkcSmgSS`eedoeeend`eadcdkdedbeoeddedcdldadbeed
+Sifnfdg`befnfdgbgig`gofifnfdghbfgofifdfibSkgSffofegnfdffeifbgegcghbbbmfafdgcfhfefdfbbibkcSbgefdgegbgnf`b`ckcSmgS
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.HDB_1of2_MD5_FIXED_testexe.UNOFFICIAL.hdb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.HDB_1of2_MD5_FIXED_testexe.UNOFFICIAL.hdb
new file mode 100644
index 0000000000..94fe32aec2
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.HDB_1of2_MD5_FIXED_testexe.UNOFFICIAL.hdb
@@ -0,0 +1 @@
+05fcb14bd4dbad8617251d4e22708367:1447976:Test.GenSig.HDB_1of2_MD5_FIXED_testexe
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.HDB_2of2_MD5_STAR_testexe.UNOFFICIAL.hdb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.HDB_2of2_MD5_STAR_testexe.UNOFFICIAL.hdb
new file mode 100644
index 0000000000..261053d1d8
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.HDB_2of2_MD5_STAR_testexe.UNOFFICIAL.hdb
@@ -0,0 +1 @@
+05fcb14bd4dbad8617251d4e22708367:*:Test.GenSig.HDB_2of2_MD5_STAR_testexe:73
\ No newline at end of file
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.HSB_1of4_SHA1_FIXED_testexe.UNOFFICIAL.hsb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.HSB_1of4_SHA1_FIXED_testexe.UNOFFICIAL.hsb
new file mode 100644
index 0000000000..7a3d422c7a
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.HSB_1of4_SHA1_FIXED_testexe.UNOFFICIAL.hsb
@@ -0,0 +1 @@
+2ba31b0352bae4f57c1c9144f64ac7a57c010876:1447976:Test.GenSig.HSB_1of4_SHA1_FIXED_testexe
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.HSB_2of4_SHA1_STAR_testexe.UNOFFICIAL.hsb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.HSB_2of4_SHA1_STAR_testexe.UNOFFICIAL.hsb
new file mode 100644
index 0000000000..9d05bc2da2
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.HSB_2of4_SHA1_STAR_testexe.UNOFFICIAL.hsb
@@ -0,0 +1 @@
+2ba31b0352bae4f57c1c9144f64ac7a57c010876:*:Test.GenSig.HSB_2of4_SHA1_STAR_testexe:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.HSB_3of4_SHA256_FIXED_testexe.UNOFFICIAL.hsb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.HSB_3of4_SHA256_FIXED_testexe.UNOFFICIAL.hsb
new file mode 100644
index 0000000000..60300a3689
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.HSB_3of4_SHA256_FIXED_testexe.UNOFFICIAL.hsb
@@ -0,0 +1 @@
+4f713f2f0d3269d5ea24bf58c8acff9ad67d53044c07f028ae825cacffb6e82e:1447976:Test.GenSig.HSB_3of4_SHA256_FIXED_testexe
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.HSB_4of4_SHA256_STAR_testexe.UNOFFICIAL.hsb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.HSB_4of4_SHA256_STAR_testexe.UNOFFICIAL.hsb
new file mode 100644
index 0000000000..1558769a64
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.HSB_4of4_SHA256_STAR_testexe.UNOFFICIAL.hsb
@@ -0,0 +1 @@
+4f713f2f0d3269d5ea24bf58c8acff9ad67d53044c07f028ae825cacffb6e82e:*:Test.GenSig.HSB_4of4_SHA256_STAR_testexe:73
\ No newline at end of file
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.IMP_1of6_MD5_FIXED.UNOFFICIAL.imp b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.IMP_1of6_MD5_FIXED.UNOFFICIAL.imp
new file mode 100644
index 0000000000..5b77fead8e
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.IMP_1of6_MD5_FIXED.UNOFFICIAL.imp
@@ -0,0 +1 @@
+ddeba1ab394d6c43225d83208b2d8ee3:1351:Test.GenSig.IMP_1of6_MD5_FIXED
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.IMP_2of6_MD5_STAR.UNOFFICIAL.imp b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.IMP_2of6_MD5_STAR.UNOFFICIAL.imp
new file mode 100644
index 0000000000..c8e8bb66ad
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.IMP_2of6_MD5_STAR.UNOFFICIAL.imp
@@ -0,0 +1 @@
+ddeba1ab394d6c43225d83208b2d8ee3:*:Test.GenSig.IMP_2of6_MD5_STAR:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.IMP_3of6_SHA1_FIXED.UNOFFICIAL.imp b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.IMP_3of6_SHA1_FIXED.UNOFFICIAL.imp
new file mode 100644
index 0000000000..1ff7ad2190
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.IMP_3of6_SHA1_FIXED.UNOFFICIAL.imp
@@ -0,0 +1 @@
+0fd72fd48bcf571bdfb7399fe285618aaec7b089:1351:Test.GenSig.IMP_3of6_SHA1_FIXED
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.IMP_4of6_SHA1_STAR.UNOFFICIAL.imp b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.IMP_4of6_SHA1_STAR.UNOFFICIAL.imp
new file mode 100644
index 0000000000..692ee67bbd
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.IMP_4of6_SHA1_STAR.UNOFFICIAL.imp
@@ -0,0 +1 @@
+0fd72fd48bcf571bdfb7399fe285618aaec7b089:*:Test.GenSig.IMP_4of6_SHA1_STAR:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.IMP_5of6_SHA256_FIXED.UNOFFICIAL.imp b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.IMP_5of6_SHA256_FIXED.UNOFFICIAL.imp
new file mode 100644
index 0000000000..9847e67cb7
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.IMP_5of6_SHA256_FIXED.UNOFFICIAL.imp
@@ -0,0 +1 @@
+f4b75231012ecd2164685ce91557c4c68c7473efeddc521bb7279ad13c362205:1351:Test.GenSig.IMP_5of6_SHA256_FIXED
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.IMP_6of6_SHA256_STAR.UNOFFICIAL.imp b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.IMP_6of6_SHA256_STAR.UNOFFICIAL.imp
new file mode 100644
index 0000000000..ca5d53cbde
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.IMP_6of6_SHA256_STAR.UNOFFICIAL.imp
@@ -0,0 +1 @@
+f4b75231012ecd2164685ce91557c4c68c7473efeddc521bb7279ad13c362205:*:Test.GenSig.IMP_6of6_SHA256_STAR:73
\ No newline at end of file
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_01of16_MD5_FIXED_text.UNOFFICIAL.mdb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_01of16_MD5_FIXED_text.UNOFFICIAL.mdb
new file mode 100644
index 0000000000..60ea617381
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_01of16_MD5_FIXED_text.UNOFFICIAL.mdb
@@ -0,0 +1 @@
+34304:c2cf3afc85a94f96246ebc2d10427b99:Test.GenSig.MDB_01of16_MD5_FIXED_text
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_02of16_MD5_STAR_text.UNOFFICIAL.mdb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_02of16_MD5_STAR_text.UNOFFICIAL.mdb
new file mode 100644
index 0000000000..a5fa019c22
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_02of16_MD5_STAR_text.UNOFFICIAL.mdb
@@ -0,0 +1 @@
+*:c2cf3afc85a94f96246ebc2d10427b99:Test.GenSig.MDB_02of16_MD5_STAR_text:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_03of16_MD5_FIXED_data.UNOFFICIAL.mdb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_03of16_MD5_FIXED_data.UNOFFICIAL.mdb
new file mode 100644
index 0000000000..da1edc59fc
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_03of16_MD5_FIXED_data.UNOFFICIAL.mdb
@@ -0,0 +1 @@
+1168896:6623c7640384c88d74cc4d7701a02627:Test.GenSig.MDB_03of16_MD5_FIXED_data
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_04of16_MD5_STAR_data.UNOFFICIAL.mdb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_04of16_MD5_STAR_data.UNOFFICIAL.mdb
new file mode 100644
index 0000000000..7fbd3d852b
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_04of16_MD5_STAR_data.UNOFFICIAL.mdb
@@ -0,0 +1 @@
+*:6623c7640384c88d74cc4d7701a02627:Test.GenSig.MDB_04of16_MD5_STAR_data:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_05of16_MD5_FIXED_rdata.UNOFFICIAL.mdb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_05of16_MD5_FIXED_rdata.UNOFFICIAL.mdb
new file mode 100644
index 0000000000..e269ed8501
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_05of16_MD5_FIXED_rdata.UNOFFICIAL.mdb
@@ -0,0 +1 @@
+3584:4ddc472eca4b9be7039758dda4737e8c:Test.GenSig.MDB_05of16_MD5_FIXED_rdata
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_06of16_MD5_STAR_rdata.UNOFFICIAL.mdb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_06of16_MD5_STAR_rdata.UNOFFICIAL.mdb
new file mode 100644
index 0000000000..fa20ff93a7
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_06of16_MD5_STAR_rdata.UNOFFICIAL.mdb
@@ -0,0 +1 @@
+*:4ddc472eca4b9be7039758dda4737e8c:Test.GenSig.MDB_06of16_MD5_STAR_rdata:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_09of16_MD5_FIXED_idata.UNOFFICIAL.mdb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_09of16_MD5_FIXED_idata.UNOFFICIAL.mdb
new file mode 100644
index 0000000000..3e76f56dd2
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_09of16_MD5_FIXED_idata.UNOFFICIAL.mdb
@@ -0,0 +1 @@
+2048:e5ec1d4f047f50cec79e2a0503509f27:Test.GenSig.MDB_09of16_MD5_FIXED_idata
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_10of16_MD5_STAR_idata.UNOFFICIAL.mdb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_10of16_MD5_STAR_idata.UNOFFICIAL.mdb
new file mode 100644
index 0000000000..efad9d2fe6
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_10of16_MD5_STAR_idata.UNOFFICIAL.mdb
@@ -0,0 +1 @@
+*:e5ec1d4f047f50cec79e2a0503509f27:Test.GenSig.MDB_10of16_MD5_STAR_idata:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_11of16_MD5_FIXED_CRT.UNOFFICIAL.mdb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_11of16_MD5_FIXED_CRT.UNOFFICIAL.mdb
new file mode 100644
index 0000000000..0b08dfbd82
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_11of16_MD5_FIXED_CRT.UNOFFICIAL.mdb
@@ -0,0 +1 @@
+512:1e10c60d92bb19eefe6f3e1e4d3d78ec:Test.GenSig.MDB_11of16_MD5_FIXED_CRT
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_12of16_MD5_STAR_CRT.UNOFFICIAL.mdb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_12of16_MD5_STAR_CRT.UNOFFICIAL.mdb
new file mode 100644
index 0000000000..0b6d0193a0
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_12of16_MD5_STAR_CRT.UNOFFICIAL.mdb
@@ -0,0 +1 @@
+*:1e10c60d92bb19eefe6f3e1e4d3d78ec:Test.GenSig.MDB_12of16_MD5_STAR_CRT:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_13of16_MD5_FIXED_tls.UNOFFICIAL.mdb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_13of16_MD5_FIXED_tls.UNOFFICIAL.mdb
new file mode 100644
index 0000000000..4d7e5f3247
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_13of16_MD5_FIXED_tls.UNOFFICIAL.mdb
@@ -0,0 +1 @@
+512:bf619eac0cdf3f68d496ea9344137e8b:Test.GenSig.MDB_13of16_MD5_FIXED_tls
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_14of16_MD5_STAR_tls.UNOFFICIAL.mdb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_14of16_MD5_STAR_tls.UNOFFICIAL.mdb
new file mode 100644
index 0000000000..7bb00262fb
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_14of16_MD5_STAR_tls.UNOFFICIAL.mdb
@@ -0,0 +1 @@
+*:bf619eac0cdf3f68d496ea9344137e8b:Test.GenSig.MDB_14of16_MD5_STAR_tls:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_15of16_MD5_FIXED_rsrc.UNOFFICIAL.mdb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_15of16_MD5_FIXED_rsrc.UNOFFICIAL.mdb
new file mode 100644
index 0000000000..3dd2da66d8
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_15of16_MD5_FIXED_rsrc.UNOFFICIAL.mdb
@@ -0,0 +1 @@
+230912:2eed75bfc8ec3ff1e5ed3161b6fd11df:Test.GenSig.MDB_15of16_MD5_FIXED_rsrc
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_16of16_MD5_STAR_rsrc.UNOFFICIAL.mdb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_16of16_MD5_STAR_rsrc.UNOFFICIAL.mdb
new file mode 100644
index 0000000000..d895ab8e83
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MDB_16of16_MD5_STAR_rsrc.UNOFFICIAL.mdb
@@ -0,0 +1 @@
+*:2eed75bfc8ec3ff1e5ed3161b6fd11df:Test.GenSig.MDB_16of16_MD5_STAR_rsrc:73
\ No newline at end of file
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_01of32_SHA1_FIXED_text.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_01of32_SHA1_FIXED_text.UNOFFICIAL.msb
new file mode 100644
index 0000000000..26fb2ba711
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_01of32_SHA1_FIXED_text.UNOFFICIAL.msb
@@ -0,0 +1 @@
+34304:7bcc8fbbab4b38c28cb9a571fa7004d8ff47b09d:Test.GenSig.MSB_01of32_SHA1_FIXED_text
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_02of32_SHA1_STAR_text.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_02of32_SHA1_STAR_text.UNOFFICIAL.msb
new file mode 100644
index 0000000000..b614ef8fa0
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_02of32_SHA1_STAR_text.UNOFFICIAL.msb
@@ -0,0 +1 @@
+*:7bcc8fbbab4b38c28cb9a571fa7004d8ff47b09d:Test.GenSig.MSB_02of32_SHA1_STAR_text:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_03of32_SHA1_FIXED_data.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_03of32_SHA1_FIXED_data.UNOFFICIAL.msb
new file mode 100644
index 0000000000..d7bca8539a
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_03of32_SHA1_FIXED_data.UNOFFICIAL.msb
@@ -0,0 +1 @@
+1168896:dae420693dde3530da0ad06f593148c9647a66b3:Test.GenSig.MSB_03of32_SHA1_FIXED_data
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_04of32_SHA1_STAR_data.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_04of32_SHA1_STAR_data.UNOFFICIAL.msb
new file mode 100644
index 0000000000..8762992c3e
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_04of32_SHA1_STAR_data.UNOFFICIAL.msb
@@ -0,0 +1 @@
+*:dae420693dde3530da0ad06f593148c9647a66b3:Test.GenSig.MSB_04of32_SHA1_STAR_data:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_05of32_SHA1_FIXED_rdata.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_05of32_SHA1_FIXED_rdata.UNOFFICIAL.msb
new file mode 100644
index 0000000000..7bf702d895
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_05of32_SHA1_FIXED_rdata.UNOFFICIAL.msb
@@ -0,0 +1 @@
+3584:ff14bc18e10021c1e30f9f8cdead7a6a8d017da1:Test.GenSig.MSB_05of32_SHA1_FIXED_rdata
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_06of32_SHA1_STAR_rdata.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_06of32_SHA1_STAR_rdata.UNOFFICIAL.msb
new file mode 100644
index 0000000000..7a91124a8c
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_06of32_SHA1_STAR_rdata.UNOFFICIAL.msb
@@ -0,0 +1 @@
+*:ff14bc18e10021c1e30f9f8cdead7a6a8d017da1:Test.GenSig.MSB_06of32_SHA1_STAR_rdata:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_09of32_SHA1_FIXED_idata.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_09of32_SHA1_FIXED_idata.UNOFFICIAL.msb
new file mode 100644
index 0000000000..71fdb37bc7
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_09of32_SHA1_FIXED_idata.UNOFFICIAL.msb
@@ -0,0 +1 @@
+2048:4f9ec5efe49dd45cc3e39f8e553c5b22b9d6d820:Test.GenSig.MSB_09of32_SHA1_FIXED_idata
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_10of32_SHA1_STAR_idata.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_10of32_SHA1_STAR_idata.UNOFFICIAL.msb
new file mode 100644
index 0000000000..a5eda1eed8
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_10of32_SHA1_STAR_idata.UNOFFICIAL.msb
@@ -0,0 +1 @@
+*:4f9ec5efe49dd45cc3e39f8e553c5b22b9d6d820:Test.GenSig.MSB_10of32_SHA1_STAR_idata:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_11of32_SHA1_FIXED_CRT.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_11of32_SHA1_FIXED_CRT.UNOFFICIAL.msb
new file mode 100644
index 0000000000..071c049fe1
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_11of32_SHA1_FIXED_CRT.UNOFFICIAL.msb
@@ -0,0 +1 @@
+512:8c0957f3d183375d014b1c920cfb3111cdb045e1:Test.GenSig.MSB_11of32_SHA1_FIXED_CRT
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_12of32_SHA1_STAR_CRT.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_12of32_SHA1_STAR_CRT.UNOFFICIAL.msb
new file mode 100644
index 0000000000..3cae6f96cd
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_12of32_SHA1_STAR_CRT.UNOFFICIAL.msb
@@ -0,0 +1 @@
+*:8c0957f3d183375d014b1c920cfb3111cdb045e1:Test.GenSig.MSB_12of32_SHA1_STAR_CRT:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_13of32_SHA1_FIXED_tls.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_13of32_SHA1_FIXED_tls.UNOFFICIAL.msb
new file mode 100644
index 0000000000..67a7ffe824
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_13of32_SHA1_FIXED_tls.UNOFFICIAL.msb
@@ -0,0 +1 @@
+512:5c3eb80066420002bc3dcc7ca4ab6efad7ed4ae5:Test.GenSig.MSB_13of32_SHA1_FIXED_tls
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_14of32_SHA1_STAR_tls.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_14of32_SHA1_STAR_tls.UNOFFICIAL.msb
new file mode 100644
index 0000000000..f1ba4dc539
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_14of32_SHA1_STAR_tls.UNOFFICIAL.msb
@@ -0,0 +1 @@
+*:5c3eb80066420002bc3dcc7ca4ab6efad7ed4ae5:Test.GenSig.MSB_14of32_SHA1_STAR_tls:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_15of32_SHA1_FIXED_rsrc.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_15of32_SHA1_FIXED_rsrc.UNOFFICIAL.msb
new file mode 100644
index 0000000000..2c75a10158
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_15of32_SHA1_FIXED_rsrc.UNOFFICIAL.msb
@@ -0,0 +1 @@
+230912:c86503c04dc7dc934c4bda3630667c29d0df0b02:Test.GenSig.MSB_15of32_SHA1_FIXED_rsrc
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_16of32_SHA1_STAR_rsrc.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_16of32_SHA1_STAR_rsrc.UNOFFICIAL.msb
new file mode 100644
index 0000000000..cfce1cc6b9
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_16of32_SHA1_STAR_rsrc.UNOFFICIAL.msb
@@ -0,0 +1 @@
+*:c86503c04dc7dc934c4bda3630667c29d0df0b02:Test.GenSig.MSB_16of32_SHA1_STAR_rsrc:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_17of32_SHA256_FIXED_text.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_17of32_SHA256_FIXED_text.UNOFFICIAL.msb
new file mode 100644
index 0000000000..c23e1d313a
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_17of32_SHA256_FIXED_text.UNOFFICIAL.msb
@@ -0,0 +1 @@
+34304:a0174c8dfab8cd480495fede811c9fcd16ec40db6d9dbe69e9e5f32907be3a1a:Test.GenSig.MSB_17of32_SHA256_FIXED_text
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_18of32_SHA256_STAR_text.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_18of32_SHA256_STAR_text.UNOFFICIAL.msb
new file mode 100644
index 0000000000..920ef139e6
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_18of32_SHA256_STAR_text.UNOFFICIAL.msb
@@ -0,0 +1 @@
+*:a0174c8dfab8cd480495fede811c9fcd16ec40db6d9dbe69e9e5f32907be3a1a:Test.GenSig.MSB_18of32_SHA256_STAR_text:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_19of32_SHA256_FIXED_data.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_19of32_SHA256_FIXED_data.UNOFFICIAL.msb
new file mode 100644
index 0000000000..aefc9430e9
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_19of32_SHA256_FIXED_data.UNOFFICIAL.msb
@@ -0,0 +1 @@
+1168896:96559752f87084cc488e3163b615d13eac1816580375facd2f872a3e4d808789:Test.GenSig.MSB_19of32_SHA256_FIXED_data
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_20of32_SHA256_STAR_data.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_20of32_SHA256_STAR_data.UNOFFICIAL.msb
new file mode 100644
index 0000000000..236bec5bbc
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_20of32_SHA256_STAR_data.UNOFFICIAL.msb
@@ -0,0 +1 @@
+*:96559752f87084cc488e3163b615d13eac1816580375facd2f872a3e4d808789:Test.GenSig.MSB_20of32_SHA256_STAR_data:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_21of32_SHA256_FIXED_rdata.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_21of32_SHA256_FIXED_rdata.UNOFFICIAL.msb
new file mode 100644
index 0000000000..a493114145
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_21of32_SHA256_FIXED_rdata.UNOFFICIAL.msb
@@ -0,0 +1 @@
+3584:758066b48be8aa110b1169906eee6556ee1eaccde3cbc2d2815ec0c9514b5199:Test.GenSig.MSB_21of32_SHA256_FIXED_rdata
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_22of32_SHA256_STAR_rdata.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_22of32_SHA256_STAR_rdata.UNOFFICIAL.msb
new file mode 100644
index 0000000000..7aac76afe5
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_22of32_SHA256_STAR_rdata.UNOFFICIAL.msb
@@ -0,0 +1 @@
+*:758066b48be8aa110b1169906eee6556ee1eaccde3cbc2d2815ec0c9514b5199:Test.GenSig.MSB_22of32_SHA256_STAR_rdata:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_25of32_SHA256_FIXED_idata.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_25of32_SHA256_FIXED_idata.UNOFFICIAL.msb
new file mode 100644
index 0000000000..768364b5a6
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_25of32_SHA256_FIXED_idata.UNOFFICIAL.msb
@@ -0,0 +1 @@
+2048:52a7e01eefcc019ac8a1fe696fe9fd4b68a2a637c86971711b8d827ff56f6ea1:Test.GenSig.MSB_25of32_SHA256_FIXED_idata
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_26of32_SHA256_STAR_idata.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_26of32_SHA256_STAR_idata.UNOFFICIAL.msb
new file mode 100644
index 0000000000..a6a40b66e7
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_26of32_SHA256_STAR_idata.UNOFFICIAL.msb
@@ -0,0 +1 @@
+*:52a7e01eefcc019ac8a1fe696fe9fd4b68a2a637c86971711b8d827ff56f6ea1:Test.GenSig.MSB_26of32_SHA256_STAR_idata:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_27of32_SHA256_FIXED_CRT.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_27of32_SHA256_FIXED_CRT.UNOFFICIAL.msb
new file mode 100644
index 0000000000..baf4d3a65c
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_27of32_SHA256_FIXED_CRT.UNOFFICIAL.msb
@@ -0,0 +1 @@
+512:629b57a90ca0c6338e5eb18ecfe55ea8695236b94833dc34ea0f9f302a0c3cad:Test.GenSig.MSB_27of32_SHA256_FIXED_CRT
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_28of32_SHA256_STAR_CRT.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_28of32_SHA256_STAR_CRT.UNOFFICIAL.msb
new file mode 100644
index 0000000000..ff6d3c589b
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_28of32_SHA256_STAR_CRT.UNOFFICIAL.msb
@@ -0,0 +1 @@
+*:629b57a90ca0c6338e5eb18ecfe55ea8695236b94833dc34ea0f9f302a0c3cad:Test.GenSig.MSB_28of32_SHA256_STAR_CRT:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_29of32_SHA256_FIXED_tls.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_29of32_SHA256_FIXED_tls.UNOFFICIAL.msb
new file mode 100644
index 0000000000..49f7d29ec4
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_29of32_SHA256_FIXED_tls.UNOFFICIAL.msb
@@ -0,0 +1 @@
+512:076a27c79e5ace2a3d47f9dd2e83e4ff6ea8872b3c2218f66c92b89b55f36560:Test.GenSig.MSB_29of32_SHA256_FIXED_tls
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_30of32_SHA256_STAR_tls.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_30of32_SHA256_STAR_tls.UNOFFICIAL.msb
new file mode 100644
index 0000000000..4ce81c820e
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_30of32_SHA256_STAR_tls.UNOFFICIAL.msb
@@ -0,0 +1 @@
+*:076a27c79e5ace2a3d47f9dd2e83e4ff6ea8872b3c2218f66c92b89b55f36560:Test.GenSig.MSB_30of32_SHA256_STAR_tls:73
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_31of32_SHA256_FIXED_rsrc.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_31of32_SHA256_FIXED_rsrc.UNOFFICIAL.msb
new file mode 100644
index 0000000000..c10a90ca80
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_31of32_SHA256_FIXED_rsrc.UNOFFICIAL.msb
@@ -0,0 +1 @@
+230912:d5d3481e5ca17ad4b5e940d9af88a5950d5a363080a73e04b2f0692cdde0eb44:Test.GenSig.MSB_31of32_SHA256_FIXED_rsrc
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_32of32_SHA256_STAR_rsrc.UNOFFICIAL.msb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_32of32_SHA256_STAR_rsrc.UNOFFICIAL.msb
new file mode 100644
index 0000000000..508998cd4f
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.MSB_32of32_SHA256_STAR_rsrc.UNOFFICIAL.msb
@@ -0,0 +1 @@
+*:d5d3481e5ca17ad4b5e940d9af88a5950d5a363080a73e04b2f0692cdde0eb44:Test.GenSig.MSB_32of32_SHA256_STAR_rsrc:73
\ No newline at end of file
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_01of22_companyname.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_01of22_companyname.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..ec60a9668b
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_01of22_companyname.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_01of22_companyname:1:VI:43006f006d00700061006e0079004e0061006d006500000000005400650073007400200043006f006d00700061006e0079004e0061006d00650000000000
\ No newline at end of file
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_02of22_filedescription.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_02of22_filedescription.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..63190c9f3c
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_02of22_filedescription.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_02of22_filedescription:1:VI:460069006c0065004400650073006300720069007000740069006f006e000000000054006500730074002000460069006c0065004400650073006300720069007000740069006f006e0000000000
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_03of22_fileversion.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_03of22_fileversion.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..c6127c9341
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_03of22_fileversion.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_03of22_fileversion:1:VI:460069006c006500560065007200730069006f006e000000000054006500730074002000460069006c006500560065007200730069006f006e0000000000
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_04of22_internalname.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_04of22_internalname.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..353950f2da
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_04of22_internalname.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_04of22_internalname:1:VI:49006e007400650072006e0061006c004e0061006d00650000005400650073007400200049006e007400650072006e0061006c004e0061006d0065000000
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_05of22_legalcopyright.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_05of22_legalcopyright.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..01898ec166
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_05of22_legalcopyright.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_05of22_legalcopyright:1:VI:4c006500670061006c0043006f0070007900720069006700680074000000540065007300740020004c006500670061006c0043006f0070007900720069006700680074000000
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_06of22_originalfilename.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_06of22_originalfilename.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..31443055a3
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_06of22_originalfilename.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_06of22_originalfilename:1:VI:4f0072006900670069006e0061006c00460069006c0065006e0061006d0065000000540065007300740020004f0072006900670069006e0061006c00460069006c0065006e0061006d0065000000
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_07of22_productname.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_07of22_productname.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..66a9b88c7d
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_07of22_productname.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_07of22_productname:1:VI:500072006f0064007500630074004e0061006d0065000000000054006500730074002000500072006f0064007500630074004e0061006d00650000000000
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_08of22_productversion.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_08of22_productversion.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..eff3e221a3
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_08of22_productversion.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_08of22_productversion:1:VI:500072006f006400750063007400560065007200730069006f006e00000054006500730074002000500072006f006400750063007400560065007200730069006f006e000000
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_09of22_ANY_testexe_0.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_09of22_ANY_testexe_0.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..b2059de9be
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_09of22_ANY_testexe_0.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_09of22_ANY_testexe_0:0:*:434c414d41565f544553545f5052494e54465f535452494e475f666661385f333939345f313738385f65386236
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_10of22_PE_testexe_0.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_10of22_PE_testexe_0.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..85567a1757
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_10of22_PE_testexe_0.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_10of22_PE_testexe_0:1:*:434c414d41565f544553545f5052494e54465f535452494e475f666661385f333939345f313738385f65386236
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_11of22_PE_EP_testexe_0.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_11of22_PE_EP_testexe_0.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..9d69e5a06e
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_11of22_PE_EP_testexe_0.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_11of22_PE_EP_testexe_0:1:EP+69320,69365:434c414d41565f544553545f5052494e54465f535452494e475f666661385f333939345f313738385f65386236
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_12of22_PE_EP_testexe_0.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_12of22_PE_EP_testexe_0.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..e807938fb4
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_12of22_PE_EP_testexe_0.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_12of22_PE_EP_testexe_0:1:EP+0,134217728:434c414d41565f544553545f5052494e54465f535452494e475f666661385f333939345f313738385f65386236
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_13of22_PE_S1_EXACT_testexe_0.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_13of22_PE_S1_EXACT_testexe_0.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..03f454eff0
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_13of22_PE_S1_EXACT_testexe_0.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_13of22_PE_S1_EXACT_testexe_0:1:S1+36232,36277:434c414d41565f544553545f5052494e54465f535452494e475f666661385f333939345f313738385f65386236
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_14of22_PE_S1_BROAD_testexe_0.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_14of22_PE_S1_BROAD_testexe_0.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..ac620d2f64
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_14of22_PE_S1_BROAD_testexe_0.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_14of22_PE_S1_BROAD_testexe_0:1:S1+0,134217728:434c414d41565f544553545f5052494e54465f535452494e475f666661385f333939345f313738385f65386236
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_15of22_PE_SE1_testexe_0.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_15of22_PE_SE1_testexe_0.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..33b033e402
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_15of22_PE_SE1_testexe_0.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_15of22_PE_SE1_testexe_0:1:SE1:434c414d41565f544553545f5052494e54465f535452494e475f666661385f333939345f313738385f65386236
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_16of22_ANY_testexe_1.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_16of22_ANY_testexe_1.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..f546a80ea4
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_16of22_ANY_testexe_1.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_16of22_ANY_testexe_1:0:*:434c414d41565f544553545f5052494e54465f535452494e475f653464375f376234335f333135355f30343061
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_17of22_PE_testexe_1.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_17of22_PE_testexe_1.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..350859e2d5
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_17of22_PE_testexe_1.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_17of22_PE_testexe_1:1:*:434c414d41565f544553545f5052494e54465f535452494e475f653464375f376234335f333135355f30343061
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_18of22_PE_EP_testexe_1.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_18of22_PE_EP_testexe_1.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..99acd8881c
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_18of22_PE_EP_testexe_1.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_18of22_PE_EP_testexe_1:1:EP+69370,69415:434c414d41565f544553545f5052494e54465f535452494e475f653464375f376234335f333135355f30343061
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_19of22_PE_EP_testexe_1.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_19of22_PE_EP_testexe_1.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..a27854fe44
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_19of22_PE_EP_testexe_1.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_19of22_PE_EP_testexe_1:1:EP+0,134217728:434c414d41565f544553545f5052494e54465f535452494e475f653464375f376234335f333135355f30343061
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_20of22_PE_S1_EXACT_testexe_1.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_20of22_PE_S1_EXACT_testexe_1.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..8c9c9afb42
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_20of22_PE_S1_EXACT_testexe_1.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_20of22_PE_S1_EXACT_testexe_1:1:S1+36282,36327:434c414d41565f544553545f5052494e54465f535452494e475f653464375f376234335f333135355f30343061
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_21of22_PE_S1_BROAD_testexe_1.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_21of22_PE_S1_BROAD_testexe_1.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..100d9bdbac
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_21of22_PE_S1_BROAD_testexe_1.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_21of22_PE_S1_BROAD_testexe_1:1:S1+0,134217728:434c414d41565f544553545f5052494e54465f535452494e475f653464375f376234335f333135355f30343061
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_22of22_PE_SE1_testexe_1.UNOFFICIAL.ndb b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_22of22_PE_SE1_testexe_1.UNOFFICIAL.ndb
new file mode 100644
index 0000000000..5c7f2349ef
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.GenSig.NDB_22of22_PE_SE1_testexe_1.UNOFFICIAL.ndb
@@ -0,0 +1 @@
+Test.GenSig.NDB_22of22_PE_SE1_testexe_1:1:SE1:434c414d41565f544553545f5052494e54465f535452494e475f653464375f376234335f333135355f30343061
\ No newline at end of file
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.Sig.CRB.BlockCert.crb b/unit_tests/input/pe_allmatch/alert-sigs/Test.Sig.CRB.BlockCert.crb
new file mode 100644
index 0000000000..08083f0d69
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.Sig.CRB.BlockCert.crb
@@ -0,0 +1 @@
+Test.Sig.CRB.BlockCert;0;8b166a274bfaa700a912edd57e8e41365beea576;d2a8ea878c4bba243788488f59354835a005baae;E709F7C042C0DFE60CDEF79BFEC8723468DE92B027E4BC31C2D2B8224DE04B6A23C49C8FEBFAD35274651AA5DAA91FD392E0336B31140F90C125E43E4DEBD3276659BBC639425595F4713C4CAC1892D5D136F76263EDE02DF4EBB849A508B492C7BD3FE295617FC5FF1C482543C938F389D521D8E758D59183C7986A5729E16B5BC3081CF3A749447E23106D170E5835BA137821202B100124EDAD00F7508C19F8103B774E9FA19989058EC52776934690E2CAD67B99E93A9AD50C470E0DF4C48F9F78DBFCEF812730A3A458A310A913CCA7E0B10699A4A441C8900A59193FFDC7376162DA6DB805E4BD9AD9463717B6EEDACAD53AEA9E7FDBB2826588FBF8E45F390B4A44A6A01787DC8110581DD1DC00407C3868F3534241BB340AED7CC9CAB56D27F7E6B645F7CC7BA7B0D1BFB27036F09B9FB25B396575C16B0BF3177FE052F7B5C8BB97F72E69DA7971EAFA643C68E36B5F156BAD46F3E3A580A7CE56BA92ACB972143DDA4B20867A45081262DF2E7A1F80A9D3588C60D48010F10461A8AE675CF8EE47E66425A5A6A0D95F1076AFEC6246C3AA635C13AC1E9CEA760316FD89AE2C19FDF696A106BC20B5A2F8E14613A4633726ADC92FD67B6E219CE1A419FE7F397F153C2591547EE08B19C54C8F04B0F6824EABD2572AF9115486479567A77853DDA31CB84609154BA3A043AD0548204875FF365047BC4B7382FD03AF;010001;0;1;0;;Generated with details from sigtool --print-certs
\ No newline at end of file
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.Sig.LDB_1of2_PE_ICON_1.UNOFFICIAL.ldb b/unit_tests/input/pe_allmatch/alert-sigs/Test.Sig.LDB_1of2_PE_ICON_1.UNOFFICIAL.ldb
new file mode 100644
index 0000000000..64b0262f31
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.Sig.LDB_1of2_PE_ICON_1.UNOFFICIAL.ldb
@@ -0,0 +1 @@
+Test.Sig.LDB_1of2_PE_ICON_1;Engine:51-255,Target:1,IconGroup1:TEST_ICON_GROUP_1;0;434c414d41565f544553545f5052494e54465f535452494e475f
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/Test.Sig.LDB_2of2_PE_ICON_2.UNOFFICIAL.ldb b/unit_tests/input/pe_allmatch/alert-sigs/Test.Sig.LDB_2of2_PE_ICON_2.UNOFFICIAL.ldb
new file mode 100644
index 0000000000..ea8dc3cfa0
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/Test.Sig.LDB_2of2_PE_ICON_2.UNOFFICIAL.ldb
@@ -0,0 +1 @@
+Test.Sig.LDB_2of2_PE_ICON_2;Engine:51-255,Target:1,IconGroup2:TEST_ICON_GROUP_2;0;434c414d41565f544553545f5052494e54465f535452494e47
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/YARA.Test_GenSig_YARA_1of2_BASIC_0.UNOFFICIAL.yar b/unit_tests/input/pe_allmatch/alert-sigs/YARA.Test_GenSig_YARA_1of2_BASIC_0.UNOFFICIAL.yar
new file mode 100644
index 0000000000..69eebec561
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/YARA.Test_GenSig_YARA_1of2_BASIC_0.UNOFFICIAL.yar
@@ -0,0 +1,8 @@
+
+rule Test_GenSig_YARA_1of2_BASIC_0
+{
+ strings:
+ $s1 = "CLAMAV_TEST_PRINTF_STRING_ffa8_3994_1788_e8b6"
+ condition:
+ $s1
+}
diff --git a/unit_tests/input/pe_allmatch/alert-sigs/YARA.Test_GenSig_YARA_2of2_BASIC_1.UNOFFICIAL.yar b/unit_tests/input/pe_allmatch/alert-sigs/YARA.Test_GenSig_YARA_2of2_BASIC_1.UNOFFICIAL.yar
new file mode 100644
index 0000000000..595de8ddd7
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/alert-sigs/YARA.Test_GenSig_YARA_2of2_BASIC_1.UNOFFICIAL.yar
@@ -0,0 +1,8 @@
+
+rule Test_GenSig_YARA_2of2_BASIC_1
+{
+ strings:
+ $s1 = "CLAMAV_TEST_PRINTF_STRING_e4d7_7b43_3155_040a"
+ condition:
+ $s1
+}
diff --git a/unit_tests/input/pe_allmatch/test-exe-src/README.md b/unit_tests/input/pe_allmatch/test-exe-src/README.md
new file mode 100644
index 0000000000..24a4817c0c
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/test-exe-src/README.md
@@ -0,0 +1,92 @@
+## Overview
+
+This aims to provide many different types of ClamAV rules for the given `test.exe` x86 Windows executable binary in order to test ClamAV's ability to report multiple signature matches (`-z` or `--allmatch` in `clamscan`).
+
+- `src` - contains the code necessary to rebuild the `test.exe` program.
+- `alert-sigs` are a combination of signatures generated by this tool, and those that were manually created.
+ - As the name implies, these signatures are expected to alert on the `text.exe` program.
+ - Some of these signatures depend on signatures in `weak-sigs`.
+- `weak-sigs` are a combination of signatures generated by this tool, and those that were manually created.
+ - As the name implies, these signatures support the `alert-sigs`, but do not alert on their own.
+
+The tools necessary to (re)generate those sigs that were generated are not included here.
+
+## Requirements
+
+- `python3`
+- `mingw-w64`
+ - Just `sudo apt install mingw-w64` on ubuntu 20.04
+- `osslsigncode`
+ - The ubuntu package is dated, but it's pretty easy to install from https://github.com/mtrojnar/osslsigncode
+ - Follow the directions for those prereqs.
+ - Note: you can use these `cmake` commands:
+ ```bash
+ mkdir build && cd build
+ cmake .. && cmake --build .
+ sudo cmake --install . --prefix /usr/local/bin
+ ```
+
+## Steps to regenerate the `test.exe` program
+
+1. Build the binary with: `./build.py`
+
+2. Generate the signatures. First run:
+ ```sh
+ mkdir gen
+ ```
+ Then run:
+ ```sh
+ python generate.py build/test.exe
+ ```
+
+## Testing:
+ - Example invocation:
+```
+$ clamscan -z -d gen/ -d manual/ build/test.exe --bytecode-unsigned --no-summary | sort -n
+test.exe: Test.GenSig.HSB_01of06_MD5_FIXED.UNOFFICIAL FOUND
+test.exe: Test.GenSig.MSB_01of90_MD5_FIXED_dottext.UNOFFICIAL FOUND
+test.exe: Test.GenSig.MSB_02of90_SHA1_FIXED_dottext.UNOFFICIAL FOUND
+test.exe: Test.GenSig.MSB_03of90_SHA256_FIXED_dottext.UNOFFICIAL FOUND
+test.exe: Test.GenSig.MSB_04of90_MD5_STAR_dottext.UNOFFICIAL FOUND
+test.exe: Test.GenSig.MSB_05of90_SHA1_STAR_dottext.UNOFFICIAL FOUND
+test.exe: Test.GenSig.MSB_06of90_SHA256_STAR_dottext.UNOFFICIAL FOUND
+test.exe: Test.GenSig.NDB_01of08_legalcopyright.UNOFFICIAL FOUND
+test.exe: Test.GenSig.NDB_02of08_internalname.UNOFFICIAL FOUND
+test.exe: Test.GenSig.NDB_03of08_fileversion.UNOFFICIAL FOUND
+test.exe: Test.GenSig.NDB_04of08_companyname.UNOFFICIAL FOUND
+test.exe: Test.GenSig.NDB_05of08_productname.UNOFFICIAL FOUND
+test.exe: Test.GenSig.NDB_06of08_productversion.UNOFFICIAL FOUND
+test.exe: Test.GenSig.NDB_07of08_filedescription.UNOFFICIAL FOUND
+test.exe: Test.GenSig.NDB_08of08_originalfilename.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_01of16_PE_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_02of16_PE_2.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_03of16_ANY_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_04of16_ANY_2.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_05of16_PE_EP_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_06of16_PE_EP_2.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_07of16_PE_SE1_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_09of16_PE_S1_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_10of16_PE_S1_2.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_11of16_PE_PCRE_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_12of16_PE_PCRE_2.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_13of16_ANY_PCRE_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_14of16_ANY_PCRE_2.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_15of16_PE_ICON_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.LDB_16of16_PE_ICON_2.UNOFFICIAL FOUND
+test.exe: Test.Sig.NDB_01of10_PE_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.NDB_02of10_PE_2.UNOFFICIAL FOUND
+test.exe: Test.Sig.NDB_03of10_ANY_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.NDB_04of10_ANY_2.UNOFFICIAL FOUND
+test.exe: Test.Sig.NDB_05of10_PE_EP_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.NDB_06of10_PE_EP_2.UNOFFICIAL FOUND
+test.exe: Test.Sig.NDB_07of10_PE_SE2_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.NDB_09of10_PE_S1_1.UNOFFICIAL FOUND
+test.exe: Test.Sig.NDB_10of10_PE_S1_2.UNOFFICIAL FOUND
+test.exe: YARA.Test_Sig_YARA_1of1_strings.UNOFFICIAL FOUND
+```
+
+## Steps to generate the .ico file:
+```
+head -c 245760 /dev/urandom | convert -depth 8 -size 320x256 RGB:- test.png
+convert -background transparent test.png -define icon:auto-resize=16,32,48,64,256 test.ico
+```
diff --git a/unit_tests/input/pe_allmatch/test-exe-src/build.py b/unit_tests/input/pe_allmatch/test-exe-src/build.py
new file mode 100755
index 0000000000..45e63abf3f
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/test-exe-src/build.py
@@ -0,0 +1,224 @@
+#!/bin/env python3
+
+# Copyright (C) 2022 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+#
+# Authors: Andrew Williams
+#
+# 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.
+
+'''
+Build a `test.exe` program with distinctive features that may be easily detected by clamscan.
+This program is also signed with an authenticode certificate to test authenticode certificate trust features.
+'''
+
+import errno
+import os
+import argparse
+import random
+
+parser = argparse.ArgumentParser()
+parser.add_argument("os", choices=['windows', 'linux', 'win32', 'win64'], nargs='?', default='windows')
+parser.add_argument("--no-sign", action='store_true', default=False)
+parser.add_argument("--no-strip", action='store_true', default=False)
+parser.add_argument("--no-cleanup", action='store_true', default=False)
+args = parser.parse_args()
+
+do_cleanup = not args.no_cleanup
+
+if args.os == 'windows' or args.os == 'win32':
+ prefix = 'i686-w64-mingw32-'
+ do_res = True
+ do_sign = not args.no_sign
+ do_strip = not args.no_strip
+ # __USE_MINGW_ANSI_STDIO lets %xd be used correctly when doing printf
+ cc_flags = '-D__USE_MINGW_ANSI_STDIO=1'
+ ext = ".exe"
+
+elif args.os == 'win64':
+ prefix = 'x86_64-w64-mingw32-'
+ do_res = True
+ do_sign = not args.no_sign
+ do_strip = not args.no_strip
+ cc_flags = '-D__USE_MINGW_ANSI_STDIO=1'
+ ext = ".exe"
+
+else: # linux
+ prefix = ''
+ do_res = False
+ do_sign = False
+ do_strip = False
+ cc_flags = ''
+ ext = ''
+
+windres = '%swindres' % (prefix)
+gcc = '%sgcc' % (prefix)
+ld = '%sld' % (prefix)
+strip = '%sstrip' % (prefix)
+
+indicators = [
+ # Indicators for the outer binary
+ ('22df_62ba_15c8_f482', '1a0d_301b_228d_56c8'),
+
+ # Indicators for up to 24 inner binaries
+ ('ffa8_3994_1788_e8b6', 'e4d7_7b43_3155_040a'),
+ ('3a28_3628_9dd8_9c32', '3da0_c7bc_1948_39bd'),
+ ('d251_d598_8e5c_6f9d', 'ee01_7f34_e9c9_1ec1'),
+ ('6761_4a90_365b_b4c0', 'b8b0_d765_3df3_e550'),
+ ('7939_5658_544a_f991', '768b_f5e5_f5e4_1a3b'),
+ ('d765_7e8b_90b9_4a2f', '6cb8_1966_8bc3_9874'),
+ ('cef1_e295_6e46_f429', '0ef3_77f1_e811_2753'),
+ ('8f8a_f7f2_cdd1_9d0b', '99aa_e67d_afb6_3735'),
+ ('d183_4b76_bde9_f5fb', 'f5e2_acf2_ff78_88f7'),
+ ('3bce_e84b_be85_77bc', 'b155_ac2c_22f6_cb9e'),
+ ('6efd_bf13_3679_9d30', '00de_ce35_6606_114c'),
+ ('6ef7_8f1c_19f8_9746', '38d9_f9c7_45de_9931'),
+ ('54b5_a0a4_b852_d3ba', 'cc78_0d79_8e9d_6f8a'),
+ ('7468_e227_c130_b48d', '0c57_4d6a_e113_e03a'),
+ ('8272_e8e2_a133_2971', 'e113_d12a_266c_3253'),
+ ('b552_7c09_8b0f_01c7', '01c3_a69a_899f_0764'),
+ ('04a3_b125_45ed_2a8b', '58f8_6bf7_b1bb_c7b6'),
+ ('5749_2c8c_9df1_4c7e', 'c0a0_3f27_99c5_51f4'),
+ ('89df_0268_6c60_29fb', 'db58_4b5e_f2df_9b65'),
+ ('c2cd_3a28_5180_dc7a', '2c70_5359_0a46_06fa'),
+ ('6764_76f5_0e92_faa6', 'd07a_b1af_e36f_8894'),
+ ('4e45_18c0_40da_e74d', '6a1d_d382_068a_7e71'),
+ ('01c4_23f0_0e32_fad1', 'a2f0_7a46_5cf1_2e0d'),
+ ('1cec_e7c0_daac_517f', '448a_2336_facf_e5e7'),
+]
+
+if len(indicators)-1 > 32:
+ raise Exception("More embedded binaries than extract.h can handle")
+
+try:
+ os.mkdir('build')
+except OSError as exc:
+ if exc.errno != errno.EEXIST:
+ raise
+ pass
+
+def run_cmd(cmd):
+ print(cmd)
+ if os.system(cmd):
+ raise Exception("Command Failed: %s" % cmd)
+
+def gen_ca_cert():
+ run_cmd('openssl genrsa -out build/ca.key 4096')
+
+ # TODO Explore making this cert have attributes that look more like
+ # a real CA cert (ex: restrict its uses)
+ subj = "/C=US/ST=Maryland/L=Fulton/O=Cisco Talos/OU=ClamAV Test CA %016x/emailAddress=rfc2606@example.net" % (random.randint(1,0xFFFFFFFFFFFFFFFF))
+ cmd = 'openssl req -new -x509 -days 3650 -key build/ca.key -out build/ca.crt -subj "%s"' % (subj)
+ run_cmd(cmd)
+
+# https://blog.didierstevens.com/2008/12/30/howto-make-your-own-cert-with-openssl/
+def gen_cs_cert(name, ext):
+ key_name = 'build/%s%s.key' % (name, ext)
+ csr_name = 'build/%s%s.csr' % (name, ext)
+ crt_name = 'build/%s%s.crt' % (name, ext)
+
+ run_cmd('openssl genrsa -out %s 4096' % (key_name))
+
+ # TODO Explore making this cert have attributes that look more like
+ # a real CS cert (ex: restrict its uses)
+ subj = "/C=US/ST=Maryland/L=Fulton/O=Cisco Talos/OU=ClamAV Test %016x/emailAddress=rfc2606@example.net" % (random.randint(1,0xFFFFFFFFFFFFFFFF))
+ cmd = 'openssl req -new -key %s -out %s -subj "%s"' % (key_name, csr_name, subj)
+ run_cmd(cmd)
+
+ cmd = 'openssl x509 -req -days 730 -in %s -CA build/ca.crt -CAkey build/ca.key -out %s -set_serial %012d -extfile ./cs.extfile.cfg' % (csr_name, crt_name, random.randint(100000000000,999999999999))
+ run_cmd(cmd)
+
+ return (key_name, crt_name)
+
+# https://blog.didierstevens.com/2018/09/24/quickpost-signing-windows-executables-on-kali/
+def sign_exe(name, ext, cert_info):
+ key_name = cert_info[0]
+ crt_name = cert_info[1]
+ orig_path = "build/%s%s" % (name, ext)
+ signed_path = "build/%s-signed%s" % (name, ext)
+ cmd = 'osslsigncode sign -certs %s -key %s -ts http://sha256timestamp.ws.symantec.com/sha256/timestamp -in %s -out %s' % (crt_name, key_name, orig_path, signed_path)
+ run_cmd(cmd)
+ os.unlink(orig_path)
+ os.rename(signed_path, orig_path)
+
+def build_exe(name, ext, index, cc_flags, do_res, do_strip, sources=['./test.c']):
+
+ if do_res:
+ # TODO Generate a new icon and new version information per exe. The
+ # icon can be generated with:
+ #
+ # head -c 245760 /dev/urandom | convert -depth 8 -size 320x256 RGB:- test.png
+ # convert -background transparent test.png -define icon:auto-resize=16,32,48,64,256 test.ico
+ #
+ # NOTE ^^^ 245760 is 320x256x3
+ res_path = 'build/%s%s.res' % (name, ext)
+ cmd = '%s ./test.rc -O coff -o %s' % (windres, res_path)
+ run_cmd(cmd)
+ sources.append(res_path)
+
+ cmd = '%s -Os %s %s -DINDICATOR1=\\"%s\\" -DINDICATOR2=\\"%s\\" -DINDEX=%d -o build/%s%s' % (gcc, cc_flags, ' '.join([x for x in sources]), indicators[index][0], indicators[index][1], index, name, ext)
+ run_cmd(cmd)
+
+ if do_strip:
+ cmd = '%s --strip-all build/%s%s' %(strip, name, ext)
+ run_cmd(cmd)
+
+def package_exe(name, ext):
+ # The name of the export symbols depends on the path, so we have to be in
+ # the same directory as the exe we are packaging for the symbol name the
+ # code expects to be generated.
+ os.chdir('build')
+ cmd = '%s -r -b binary %s%s -o %s%s.o' %(ld, name, ext, name, ext)
+ run_cmd(cmd)
+ os.chdir('..')
+
+# Generate a new CA cert for code-signing
+if do_sign:
+ gen_ca_cert()
+
+outer_exe_sources = ['./test.c']
+# Build the inner exes. They MUST be named 'exe#', where # is the index. They
+# can't have a file extension or else it breaks linking.
+for i in range(1, len(indicators)):
+ name = 'exe%d' % (i)
+ inner_exe_ext = ".exe"
+ build_exe(name, inner_exe_ext, i, cc_flags, False, do_strip);
+ if do_sign:
+ cert_info = gen_cs_cert(name, inner_exe_ext)
+ sign_exe(name, inner_exe_ext, cert_info)
+ package_exe(name, inner_exe_ext)
+ outer_exe_sources.append('build/%s%s.o' % (name, inner_exe_ext))
+
+# Build the outer exe
+name = 'test'
+build_exe(name, ext, 0, cc_flags, do_res, do_strip, sources=outer_exe_sources)
+if do_sign:
+ cert_info = gen_cs_cert(name, ext)
+ sign_exe(name, ext, cert_info)
+
+# Delete unneeded artifacts
+if do_cleanup:
+ cleanups = ['build/*.o']
+ if do_sign:
+ cleanups += ['build/*.csr', 'build/*.key']
+
+ if do_res:
+ cleanups += ['build/test.exe.res']
+
+ for cleanup in cleanups:
+ cmd = 'rm %s' % (cleanup)
+ try:
+ run_cmd(cmd)
+ except:
+ pass
diff --git a/unit_tests/input/pe_allmatch/test-exe-src/cs.extfile.cfg b/unit_tests/input/pe_allmatch/test-exe-src/cs.extfile.cfg
new file mode 100644
index 0000000000..be14e3795d
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/test-exe-src/cs.extfile.cfg
@@ -0,0 +1,3 @@
+basicConstraints=CA:FALSE
+keyUsage=digitalSignature
+extendedKeyUsage=codeSigning
diff --git a/unit_tests/input/pe_allmatch/test-exe-src/extract.h b/unit_tests/input/pe_allmatch/test-exe-src/extract.h
new file mode 100644
index 0000000000..cc6f0c7776
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/test-exe-src/extract.h
@@ -0,0 +1,128 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#ifdef _WIN32
+#include
+#else
+#define O_BINARY 0
+#include
+#endif
+
+#define DO1(X) X(1)
+#define DO2(X) DO1(X) X(2)
+#define DO3(X) DO2(X) X(3)
+#define DO4(X) DO3(X) X(4)
+#define DO5(X) DO4(X) X(5)
+#define DO6(X) DO5(X) X(6)
+#define DO7(X) DO6(X) X(7)
+#define DO8(X) DO7(X) X(8)
+#define DO9(X) DO8(X) X(9)
+#define DO10(X) DO9(X) X(10)
+#define DO11(X) DO10(X) X(11)
+#define DO12(X) DO11(X) X(12)
+#define DO13(X) DO12(X) X(13)
+#define DO14(X) DO13(X) X(14)
+#define DO15(X) DO14(X) X(15)
+#define DO16(X) DO15(X) X(16)
+#define DO17(X) DO16(X) X(17)
+#define DO18(X) DO17(X) X(18)
+#define DO19(X) DO18(X) X(19)
+#define DO20(X) DO19(X) X(20)
+#define DO21(X) DO20(X) X(21)
+#define DO22(X) DO21(X) X(22)
+#define DO23(X) DO22(X) X(23)
+#define DO24(X) DO23(X) X(24)
+#define DO25(X) DO24(X) X(25)
+#define DO26(X) DO25(X) X(26)
+#define DO27(X) DO26(X) X(27)
+#define DO28(X) DO27(X) X(28)
+#define DO29(X) DO28(X) X(29)
+#define DO30(X) DO29(X) X(30)
+#define DO31(X) DO30(X) X(31)
+#define DO32(X) DO31(X) X(32)
+
+#define DO(NUMBER, X) \
+ DO##NUMBER(X)
+
+// The following works for GCC producing ELFs, but not with mingw-w64
+// producing Windows PEs:
+// extern const char __attribute__((weak)) _binary_exe ## index ## _start[0];
+// Luckily using weakref instead works for both, albeit with the difference in
+// whether an underscore is prepended.
+//
+// These symbols will correspond with embedded EXE object files that we create
+// with `ld -r -b binary` and then link with.
+
+#ifdef __MINGW32__
+#define DEFINE(index) \
+ static const char __attribute__((weakref, alias("binary_exe" #index "_start"))) exe##index##_start[0]; \
+ static const char __attribute__((weakref, alias("binary_exe" #index "_end"))) exe##index##_end[0];
+#else
+#define DEFINE(index) \
+ static const char __attribute__((weakref, alias("_binary_exe" #index "_start"))) exe##index##_start[0]; \
+ static const char __attribute__((weakref, alias("_binary_exe" #index "_end"))) exe##index##_end[0];
+#endif
+
+DO(32, DEFINE)
+
+void exec_in_new_process(const char *filename)
+{
+
+#ifdef _WIN32
+ PROCESS_INFORMATION pi = {0};
+ STARTUPINFO si = {0};
+ si.cb = sizeof(si);
+ BOOL result = CreateProcess(NULL, (char *)filename, NULL, NULL, TRUE,
+ NORMAL_PRIORITY_CLASS, NULL, NULL,
+ &si, &pi);
+ if (result) {
+ WaitForSingleObject(pi.hProcess, INFINITE);
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ }
+#else
+ pid_t pid = fork();
+ if (0 == pid) {
+ char *const args[2] = {(char *)filename, NULL};
+ execv(filename, args);
+ exit(1);
+ } else {
+ waitpid(pid, NULL, 0);
+ }
+#endif
+}
+
+#define EXTRACT_EXECUTE_DELETE(index) \
+ do { \
+ size_t size = exe##index##_end - exe##index##_start; \
+ if (!size) break; \
+ printf("Extracting file with size %zd\n", size); \
+ /* Linux doesn't care if there is a file extension, but Windows does */ \
+ const char *filename = "exe" #index ".exe"; \
+ int fd = open(filename, O_WRONLY | O_CREAT | O_BINARY, S_IRWXU | S_IRWXG | S_IRWXO); \
+ if (-1 == fd) { \
+ perror("open"); \
+ break; \
+ } \
+ int pos = 0; \
+ while (pos < size) { \
+ int written = write(fd, exe##index##_start + pos, size - pos); \
+ if (-1 == written) { \
+ perror("write"); \
+ break; \
+ } \
+ pos += written; \
+ } \
+ close(fd); \
+ if (pos == size) { \
+ exec_in_new_process(filename); \
+ } \
+ unlink(filename); \
+ printf("Finished extracting and executing\n"); \
+ } while (0);
+
+#define DROP_AND_EXECUTE() \
+ DO(32, EXTRACT_EXECUTE_DELETE)
diff --git a/unit_tests/input/pe_allmatch/test-exe-src/test.c b/unit_tests/input/pe_allmatch/test-exe-src/test.c
new file mode 100644
index 0000000000..c1de1814ab
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/test-exe-src/test.c
@@ -0,0 +1,73 @@
+#include
+#include "extract.h"
+#ifdef _WIN32
+#include
+#include
+#else
+#include
+#endif
+
+int main(int argc, char **argv)
+{
+ DROP_AND_EXECUTE()
+
+ printf("%s\n", "CLAMAV_TEST_PRINTF_STRING_" INDICATOR1);
+ printf("%s\n", "CLAMAV_TEST_PRINTF_STRING_" INDICATOR2);
+
+ // Do some random stuff to change the .imp hashes and .text MDB/MSB hashes
+ // To change the .imp hash (Windows-specific) we actually need to make
+ // the exe import new functions, so we need to call interesting APIs.
+ //
+ // On Linux, we just need to make sure the assembly in the .text section
+ // is different
+#if INDEX == 1
+#ifdef _WIN32
+
+ printf("Enumerating Modules via CreateToolhelp32Snapshot\n");
+ HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
+ if (INVALID_HANDLE_VALUE == h) {
+ return 1;
+ }
+ MODULEENTRY32 m;
+ if (!Module32First(h, &m)) {
+ CloseHandle(h);
+ return 1;
+ }
+ do {
+ printf(" - %s\n", m.szModule);
+ } while (Module32Next(h, &m));
+ CloseHandle(h);
+
+#else
+
+ printf("Listing program name\n - %s\n", argv[0]);
+
+#endif
+
+#elif INDEX == 2
+#ifdef _WIN32
+
+ printf("Enumerating Keys in HKEY_CURRENT_USER\n");
+ char key[256];
+ int index = 0;
+ if (ERROR_SUCCESS != RegEnumKeyA(HKEY_CURRENT_USER, index++, (char *)&key, sizeof(key))) {
+ return 1;
+ }
+ do {
+ printf(" - %s\n", key);
+ } while (ERROR_SUCCESS == RegEnumKeyA(HKEY_CURRENT_USER, index++, (char *)&key, sizeof(key)));
+
+#else
+
+ printf("Listing current time\n - %d\n", (int)time(NULL));
+
+#endif
+
+#else
+
+ printf("Nothing to do!\n");
+
+#endif
+
+ return 0;
+}
diff --git a/unit_tests/input/pe_allmatch/test-exe-src/test.ico b/unit_tests/input/pe_allmatch/test-exe-src/test.ico
new file mode 100644
index 0000000000..e906fb6f62
Binary files /dev/null and b/unit_tests/input/pe_allmatch/test-exe-src/test.ico differ
diff --git a/unit_tests/input/pe_allmatch/test-exe-src/test.png b/unit_tests/input/pe_allmatch/test-exe-src/test.png
new file mode 100644
index 0000000000..a3a2078470
Binary files /dev/null and b/unit_tests/input/pe_allmatch/test-exe-src/test.png differ
diff --git a/unit_tests/input/pe_allmatch/test-exe-src/test.rc b/unit_tests/input/pe_allmatch/test-exe-src/test.rc
new file mode 100644
index 0000000000..98bfca8144
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/test-exe-src/test.rc
@@ -0,0 +1,24 @@
+id ICON "test.ico"
+1 VERSIONINFO
+FILEVERSION 1,0,0,0
+PRODUCTVERSION 1,0,0,0
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904E4"
+ BEGIN
+ VALUE "CompanyName", "Test CompanyName"
+ VALUE "FileDescription", "Test FileDescription"
+ VALUE "FileVersion", "Test FileVersion"
+ VALUE "InternalName", "Test InternalName"
+ VALUE "LegalCopyright", "Test LegalCopyright"
+ VALUE "OriginalFilename", "Test OriginalFilename"
+ VALUE "ProductName", "Test ProductName"
+ VALUE "ProductVersion", "Test ProductVersion"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
diff --git a/unit_tests/input/pe_allmatch/test-exe-src/test.res b/unit_tests/input/pe_allmatch/test-exe-src/test.res
new file mode 100644
index 0000000000..14cc62703c
Binary files /dev/null and b/unit_tests/input/pe_allmatch/test-exe-src/test.res differ
diff --git a/unit_tests/input/pe_allmatch/test.exe b/unit_tests/input/pe_allmatch/test.exe
new file mode 100644
index 0000000000..3f0272eb0d
Binary files /dev/null and b/unit_tests/input/pe_allmatch/test.exe differ
diff --git a/unit_tests/input/pe_allmatch/trust-sigs/Test.Sig.CRB.TrustCert.crb b/unit_tests/input/pe_allmatch/trust-sigs/Test.Sig.CRB.TrustCert.crb
new file mode 100644
index 0000000000..dbb59b5185
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/trust-sigs/Test.Sig.CRB.TrustCert.crb
@@ -0,0 +1 @@
+Test.Sig.CRB.TrustCert;1;8b166a274bfaa700a912edd57e8e41365beea576;d2a8ea878c4bba243788488f59354835a005baae;E709F7C042C0DFE60CDEF79BFEC8723468DE92B027E4BC31C2D2B8224DE04B6A23C49C8FEBFAD35274651AA5DAA91FD392E0336B31140F90C125E43E4DEBD3276659BBC639425595F4713C4CAC1892D5D136F76263EDE02DF4EBB849A508B492C7BD3FE295617FC5FF1C482543C938F389D521D8E758D59183C7986A5729E16B5BC3081CF3A749447E23106D170E5835BA137821202B100124EDAD00F7508C19F8103B774E9FA19989058EC52776934690E2CAD67B99E93A9AD50C470E0DF4C48F9F78DBFCEF812730A3A458A310A913CCA7E0B10699A4A441C8900A59193FFDC7376162DA6DB805E4BD9AD9463717B6EEDACAD53AEA9E7FDBB2826588FBF8E45F390B4A44A6A01787DC8110581DD1DC00407C3868F3534241BB340AED7CC9CAB56D27F7E6B645F7CC7BA7B0D1BFB27036F09B9FB25B396575C16B0BF3177FE052F7B5C8BB97F72E69DA7971EAFA643C68E36B5F156BAD46F3E3A580A7CE56BA92ACB972143DDA4B20867A45081262DF2E7A1F80A9D3588C60D48010F10461A8AE675CF8EE47E66425A5A6A0D95F1076AFEC6246C3AA635C13AC1E9CEA760316FD89AE2C19FDF696A106BC20B5A2F8E14613A4633726ADC92FD67B6E219CE1A419FE7F397F153C2591547EE08B19C54C8F04B0F6824EABD2572AF9115486479567A77853DDA31CB84609154BA3A043AD0548204875FF365047BC4B7382FD03AF;010001;1;0;0;;Generated with details from sigtool --print-certs
\ No newline at end of file
diff --git a/unit_tests/input/pe_allmatch/weak-sigs/sig00.idb b/unit_tests/input/pe_allmatch/weak-sigs/sig00.idb
new file mode 100644
index 0000000000..b20f2700bd
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/weak-sigs/sig00.idb
@@ -0,0 +1 @@
+Test.Sig.IDB_16x16x32:TEST_ICON_GROUP_1:TEST_ICON_GROUP_2:100620a0b05b0307055060b0350a0b0480307051060b8407008402058308058000008104098109096d020b6b0a046306031800001f040035080000000000
diff --git a/unit_tests/input/pe_allmatch/weak-sigs/sig01.idb b/unit_tests/input/pe_allmatch/weak-sigs/sig01.idb
new file mode 100644
index 0000000000..de76424bb1
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/weak-sigs/sig01.idb
@@ -0,0 +1 @@
+Test.Sig.IDB_32x32x32:TEST_ICON_GROUP_1:TEST_ICON_GROUP_2:20050161704f041604e0d13040161704104160410d13870f0087141787050a82000084070f85111365150d640808561503340000390a003c001700000000
diff --git a/unit_tests/input/pe_allmatch/weak-sigs/sig02.idb b/unit_tests/input/pe_allmatch/weak-sigs/sig02.idb
new file mode 100644
index 0000000000..6bcf15608d
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/weak-sigs/sig02.idb
@@ -0,0 +1 @@
+Test.Sig.IDB_48x48x32:TEST_ICON_GROUP_1:TEST_ICON_GROUP_2:180510e100370f00000000003c0e100410f000410000860b02850211850b1181000083090a83051060050c5e10065605012e0000320a0037100000000000
diff --git a/unit_tests/input/pe_allmatch/weak-sigs/sig03.idb b/unit_tests/input/pe_allmatch/weak-sigs/sig03.idb
new file mode 100644
index 0000000000..d9c710292d
--- /dev/null
+++ b/unit_tests/input/pe_allmatch/weak-sigs/sig03.idb
@@ -0,0 +1 @@
+Test.Sig.IDB_64x64x32:TEST_ICON_GROUP_1:TEST_ICON_GROUP_2:20056150304f161704f020104015030411617043020187050a870f0087011783000084070e85111364150c6308095b0417380000390a003f160000000000