clamav/libclamav/blob.c

694 lines
16 KiB
C
Raw Normal View History

2003-07-29 15:48:06 +00:00
/*
2025-02-14 10:24:30 -05:00
* Copyright (C) 2013-2025 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2007-2013 Sourcefire, Inc.
*
* Authors: Nigel Horne
2003-07-29 15:48:06 +00:00
*
* 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.
2003-07-29 15:48:06 +00:00
*
* 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.
2003-07-29 15:48:06 +00:00
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
2003-07-29 15:48:06 +00:00
2004-08-22 10:37:32 +00:00
#include <stdio.h>
2003-07-29 15:48:06 +00:00
#include <stdlib.h>
#include <string.h>
2004-08-22 10:37:32 +00:00
#include <errno.h>
#include <fcntl.h>
2010-01-15 16:24:16 +01:00
#include <sys/types.h>
#include <sys/stat.h>
2004-08-22 10:37:32 +00:00
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h> /* for NAME_MAX */
#endif
2004-08-22 10:37:32 +00:00
#ifdef C_DARWIN
2003-07-29 15:48:06 +00:00
#include <sys/types.h>
#endif
2004-08-22 10:37:32 +00:00
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "clamav.h"
#include "others.h"
2003-07-29 15:48:06 +00:00
#include "mbox.h"
#include "matcher.h"
#include "scanners.h"
#include "filetypes.h"
2003-07-29 15:48:06 +00:00
#include <assert.h>
Spelling Adjustments (#30) * spelling: accessed * spelling: alignment * spelling: amalgamated * spelling: answers * spelling: another * spelling: acquisition * spelling: apitid * spelling: ascii * spelling: appending * spelling: appropriate * spelling: arbitrary * spelling: architecture * spelling: asynchronous * spelling: attachments * spelling: argument * spelling: authenticode * spelling: because * spelling: boundary * spelling: brackets * spelling: bytecode * spelling: calculation * spelling: cannot * spelling: changes * spelling: check * spelling: children * spelling: codegen * spelling: commands * spelling: container * spelling: concatenated * spelling: conditions * spelling: continuous * spelling: conversions * spelling: corresponding * spelling: corrupted * spelling: coverity * spelling: crafting * spelling: daemon * spelling: definition * spelling: delivered * spelling: delivery * spelling: delimit * spelling: dependencies * spelling: dependency * spelling: detection * spelling: determine * spelling: disconnects * spelling: distributed * spelling: documentation * spelling: downgraded * spelling: downloading * spelling: endianness * spelling: entities * spelling: especially * spelling: empty * spelling: expected * spelling: explicitly * spelling: existent * spelling: finished * spelling: flexibility * spelling: flexible * spelling: freshclam * spelling: functions * spelling: guarantee * spelling: hardened * spelling: headaches * spelling: heighten * spelling: improper * spelling: increment * spelling: indefinitely * spelling: independent * spelling: inaccessible * spelling: infrastructure Conflicts: docs/html/node68.html * spelling: initializing * spelling: inited * spelling: instream * spelling: installed * spelling: initialization * spelling: initialize * spelling: interface * spelling: intrinsics * spelling: interpreter * spelling: introduced * spelling: invalid * spelling: latency * spelling: lawyers * spelling: libclamav * spelling: likelihood * spelling: loop * spelling: maximum * spelling: million * spelling: milliseconds * spelling: minimum * spelling: minzhuan * spelling: multipart * spelling: misled * spelling: modifiers * spelling: notifying * spelling: objects * spelling: occurred * spelling: occurs * spelling: occurrences * spelling: optimization * spelling: original * spelling: originated * spelling: output * spelling: overridden * spelling: parenthesis * spelling: partition * spelling: performance * spelling: permission * spelling: phishing * spelling: portions * spelling: positives * spelling: preceded * spelling: properties * spelling: protocol * spelling: protos * spelling: quarantine * spelling: recursive * spelling: referring * spelling: reorder * spelling: reset * spelling: resources * spelling: resume * spelling: retrieval * spelling: rewrite * spelling: sanity * spelling: scheduled * spelling: search * spelling: section * spelling: separator * spelling: separated * spelling: specify * spelling: special * spelling: statement * spelling: streams * spelling: succession * spelling: suggests * spelling: superfluous * spelling: suspicious * spelling: synonym * spelling: temporarily * spelling: testfiles * spelling: transverse * spelling: turkish * spelling: typos * spelling: unable * spelling: unexpected * spelling: unexpectedly * spelling: unfinished * spelling: unfortunately * spelling: uninitialized * spelling: unlocking * spelling: unnecessary * spelling: unpack * spelling: unrecognized * spelling: unsupported * spelling: usable * spelling: wherever * spelling: wishlist * spelling: white * spelling: infrastructure * spelling: directories * spelling: overridden * spelling: permission * spelling: yesterday * spelling: initialization * spelling: intrinsics * space adjustment for spelling changes * minor modifications by klin
2018-02-21 15:00:59 -05:00
/* Scheduled for rewrite in 0.94 (bb#804). Disabling for now */
2008-02-11 20:19:20 +00:00
/* #define MAX_SCAN_SIZE 20*1024 /\* */
/* * The performance benefit of scanning */
/* * early disappears on medium and */
/* * large sized files */
/* *\/ */
static const char *blobGetFilename(const blob *b);
2003-07-29 15:48:06 +00:00
blob *
blobCreate(void)
{
#ifdef CL_DEBUG
blob *b = (blob *)calloc(1, sizeof(blob));
if (b)
b->magic = BLOBCLASS;
cli_dbgmsg("blobCreate\n");
return b;
2003-07-29 15:48:06 +00:00
#else
return (blob *)calloc(1, sizeof(blob));
2003-07-29 15:48:06 +00:00
#endif
}
void blobDestroy(blob *b)
2003-07-29 15:48:06 +00:00
{
#ifdef CL_DEBUG
cli_dbgmsg("blobDestroy %d\n", b->magic);
#else
cli_dbgmsg("blobDestroy\n");
#endif
assert(b != NULL);
2019-03-26 15:09:52 -04:00
#ifdef CL_DEBUG
assert(b->magic == BLOBCLASS);
2019-03-26 15:09:52 -04:00
#endif
2003-07-29 15:48:06 +00:00
if (b->name)
free(b->name);
if (b->data)
free(b->data);
#ifdef CL_DEBUG
b->magic = INVALIDCLASS;
2003-07-29 15:48:06 +00:00
#endif
free(b);
2003-07-29 15:48:06 +00:00
}
void blobArrayDestroy(blob *blobList[], int n)
2003-07-29 15:48:06 +00:00
{
assert(blobList != NULL);
while (--n >= 0) {
cli_dbgmsg("blobArrayDestroy: %d\n", n);
if (blobList[n]) {
blobDestroy(blobList[n]);
blobList[n] = NULL;
}
}
2003-07-29 15:48:06 +00:00
}
/*
* No longer needed to be growable, so turn into a normal memory area which
* the caller must free. The passed blob is destroyed
*/
void *
blobToMem(blob *b)
{
void *ret;
assert(b != NULL);
2019-03-26 15:09:52 -04:00
#ifdef CL_DEBUG
assert(b->magic == BLOBCLASS);
2019-03-26 15:09:52 -04:00
#endif
if (!b->isClosed)
blobClose(b);
if (b->name)
free(b->name);
#ifdef CL_DEBUG
b->magic = INVALIDCLASS;
#endif
ret = (void *)b->data;
free(b);
return ret;
}
2004-08-22 15:10:32 +00:00
/*ARGSUSED*/
void blobSetFilename(blob *b, const char *dir, const char *filename)
2003-07-29 15:48:06 +00:00
{
assert(b != NULL);
2019-03-26 15:09:52 -04:00
#ifdef CL_DEBUG
assert(b->magic == BLOBCLASS);
2019-03-26 15:09:52 -04:00
#endif
assert(filename != NULL);
2003-07-29 15:48:06 +00:00
UNUSEDPARAM(dir);
cli_dbgmsg("blobSetFilename: %s\n", filename);
if (b->name)
free(b->name);
2003-07-29 15:48:06 +00:00
b->name = cli_safer_strdup(filename);
2003-07-29 15:48:06 +00:00
if (b->name)
sanitiseName(b->name);
2003-07-29 15:48:06 +00:00
}
static const char *
2003-07-29 15:48:06 +00:00
blobGetFilename(const blob *b)
{
assert(b != NULL);
2019-03-26 15:09:52 -04:00
#ifdef CL_DEBUG
assert(b->magic == BLOBCLASS);
2019-03-26 15:09:52 -04:00
#endif
2003-07-29 15:48:06 +00:00
return b->name;
2003-07-29 15:48:06 +00:00
}
/*
* Returns <0 for failure
*/
int blobAddData(blob *b, const unsigned char *data, size_t len)
2003-07-29 15:48:06 +00:00
{
#if HAVE_CLI_GETPAGESIZE
static int pagesize = 0;
int growth;
2004-09-18 15:03:15 +00:00
#endif
assert(b != NULL);
2019-03-26 15:09:52 -04:00
#ifdef CL_DEBUG
assert(b->magic == BLOBCLASS);
2019-03-26 15:09:52 -04:00
#endif
assert(data != NULL);
2003-07-29 15:48:06 +00:00
if (len == 0)
return 0;
2003-07-29 15:48:06 +00:00
if (b->isClosed) {
/*
* Should be cli_dbgmsg, but I want to see them for now,
* and cli_dbgmsg doesn't support debug levels
*/
cli_warnmsg("Reopening closed blob\n");
b->isClosed = 0;
}
/*
* The payoff here is between reducing the number of calls to
* malloc/realloc and not overallocating memory. A lot of machines
* are more tight with memory than one may imagine which is why
* we don't just allocate a *huge* amount and be done with it. Closing
* the blob helps because that reclaims memory. If you know the maximum
* size of a blob before you start adding data, use blobGrow() that's
* the most optimum
*/
#if HAVE_CLI_GETPAGESIZE
if (pagesize == 0) {
pagesize = cli_getpagesize();
if (pagesize <= 0)
pagesize = 4096;
}
growth = pagesize;
if (len >= (size_t)pagesize)
growth = ((len / pagesize) + 1) * pagesize;
/*cli_dbgmsg("blobGrow: b->size %lu, b->len %lu, len %lu, growth = %u\n",
b->size, b->len, len, growth);*/
2004-12-16 15:29:51 +00:00
if (b->data == NULL) {
assert(b->len == 0);
assert(b->size == 0);
2004-09-18 15:03:15 +00:00
b->size = growth;
b->data = cli_max_malloc(growth);
2020-07-24 08:32:47 -07:00
if (NULL == b->data) {
b->size = 0;
return -1;
}
} else if (b->size < b->len + (off_t)len) {
unsigned char *p = cli_max_realloc(b->data, b->size + growth);
2004-09-18 15:03:15 +00:00
if (p == NULL)
return -1;
2004-09-18 15:03:15 +00:00
b->size += growth;
b->data = p;
}
2004-09-18 15:03:15 +00:00
#else
if (b->data == NULL) {
assert(b->len == 0);
assert(b->size == 0);
b->size = (off_t)len * 4;
b->data = cli_max_malloc(b->size);
2020-07-24 08:32:47 -07:00
if (NULL == b->data) {
b->size = 0;
return -1;
}
} else if (b->size < b->len + (off_t)len) {
unsigned char *p = cli_max_realloc(b->data, b->size + (len * 4));
if (p == NULL)
return -1;
b->size += (off_t)len * 4;
b->data = p;
}
2004-09-18 15:03:15 +00:00
#endif
2003-07-29 15:48:06 +00:00
if (b->data) {
memcpy(&b->data[b->len], data, len);
b->len += (off_t)len;
} else {
b->size = 0;
return -1;
}
return 0;
2003-07-29 15:48:06 +00:00
}
unsigned char *
2003-07-29 15:48:06 +00:00
blobGetData(const blob *b)
{
assert(b != NULL);
2019-03-26 15:09:52 -04:00
#ifdef CL_DEBUG
assert(b->magic == BLOBCLASS);
2019-03-26 15:09:52 -04:00
#endif
2003-07-29 15:48:06 +00:00
if (b->len == 0)
return NULL;
return b->data;
2003-07-29 15:48:06 +00:00
}
size_t
2003-07-29 15:48:06 +00:00
blobGetDataSize(const blob *b)
{
assert(b != NULL);
2019-03-26 15:09:52 -04:00
#ifdef CL_DEBUG
assert(b->magic == BLOBCLASS);
2019-03-26 15:09:52 -04:00
#endif
2003-07-29 15:48:06 +00:00
return b->len;
2003-07-29 15:48:06 +00:00
}
void blobClose(blob *b)
{
assert(b != NULL);
2019-03-26 15:09:52 -04:00
#ifdef CL_DEBUG
assert(b->magic == BLOBCLASS);
2019-03-26 15:09:52 -04:00
#endif
if (b->isClosed) {
cli_warnmsg("Attempt to close a previously closed blob\n");
return;
}
2004-09-18 15:03:15 +00:00
/*
* Nothing more is going to be added to this blob. If it'll save more
* than a trivial amount (say 64 bytes) of memory, shrink the allocation
*/
if ((b->size - b->len) >= 64) {
if (b->len == 0) { /* Not likely */
free(b->data);
b->data = NULL;
cli_dbgmsg("blobClose: recovered all %lu bytes\n",
(unsigned long)b->size);
b->size = 0;
} else {
unsigned char *ptr = cli_max_realloc(b->data, b->len);
if (ptr == NULL) {
return;
}
cli_dbgmsg("blobClose: recovered %lu bytes from %lu\n",
(unsigned long)(b->size - b->len),
(unsigned long)b->size);
b->size = b->len;
b->data = ptr;
}
}
b->isClosed = 1;
}
/*
* Returns 0 if the blobs are the same
*/
int blobcmp(const blob *b1, const blob *b2)
{
size_t s1, s2;
assert(b1 != NULL);
assert(b2 != NULL);
if (b1 == b2)
return 0;
s1 = blobGetDataSize(b1);
s2 = blobGetDataSize(b2);
if (s1 != s2)
return 1;
if ((s1 == 0) && (s2 == 0))
return 0;
2004-09-18 15:03:15 +00:00
return memcmp(blobGetData(b1), blobGetData(b2), s1);
}
/*
* Return clamav return code
*/
int blobGrow(blob *b, size_t len)
{
assert(b != NULL);
2019-03-26 15:09:52 -04:00
#ifdef CL_DEBUG
assert(b->magic == BLOBCLASS);
2019-03-26 15:09:52 -04:00
#endif
if (len == 0)
return CL_SUCCESS;
if (b->isClosed) {
/*
* Should be cli_dbgmsg, but I want to see them for now,
* and cli_dbgmsg doesn't support debug levels
*/
cli_warnmsg("Growing closed blob\n");
b->isClosed = 0;
}
if (b->data == NULL) {
assert(b->len == 0);
assert(b->size == 0);
b->data = cli_max_malloc(len);
if (b->data)
b->size = (off_t)len;
} else {
unsigned char *ptr = cli_max_realloc(b->data, b->size + len);
if (ptr) {
b->size += (off_t)len;
b->data = ptr;
}
}
return (b->data) ? CL_SUCCESS : CL_EMEM;
}
2004-08-22 10:37:32 +00:00
fileblob *
fileblobCreate(void)
{
#ifdef CL_DEBUG
fileblob *fb = (fileblob *)calloc(1, sizeof(fileblob));
if (fb)
fb->b.magic = BLOBCLASS;
cli_dbgmsg("blobCreate\n");
return fb;
2004-08-22 10:37:32 +00:00
#else
return (fileblob *)calloc(1, sizeof(fileblob));
2004-08-22 10:37:32 +00:00
#endif
}
/*
* Returns CL_CLEAN or CL_VIRUS. Destroys the fileblob and removes the file
* if possible
*/
int fileblobScanAndDestroy(fileblob *fb)
{
switch (fileblobScan(fb)) {
case CL_VIRUS:
fileblobDestructiveDestroy(fb);
return CL_VIRUS;
case CL_BREAK:
fileblobDestructiveDestroy(fb);
return CL_CLEAN;
default:
fileblobDestroy(fb);
return CL_CLEAN;
}
}
/*
* Destroy the fileblob, and remove the file associated with it
*/
void fileblobDestructiveDestroy(fileblob *fb)
{
if (fb->fp && fb->fullname) {
fclose(fb->fp);
cli_dbgmsg("fileblobDestructiveDestroy: %s\n", fb->fullname);
if (!fb->ctx || !fb->ctx->engine->keeptmp)
cli_unlink(fb->fullname);
free(fb->fullname);
fb->fp = NULL;
fb->fullname = NULL;
}
if (fb->b.name) {
free(fb->b.name);
fb->b.name = NULL;
}
fileblobDestroy(fb);
}
/*
* Destroy the fileblob, and remove the file associated with it if that file is
* empty
*/
void fileblobDestroy(fileblob *fb)
2004-08-22 10:37:32 +00:00
{
assert(fb != NULL);
2019-03-26 15:09:52 -04:00
#ifdef CL_DEBUG
assert(fb->b.magic == BLOBCLASS);
2019-03-26 15:09:52 -04:00
#endif
if (fb->b.name && fb->fp) {
fclose(fb->fp);
if (fb->fullname) {
cli_dbgmsg("fileblobDestroy: %s\n", fb->fullname);
if (!fb->isNotEmpty) {
cli_dbgmsg("fileblobDestroy: not saving empty file\n");
cli_unlink(fb->fullname);
}
}
free(fb->b.name);
assert(fb->b.data == NULL);
} else if (fb->b.data) {
free(fb->b.data);
if (fb->b.name) {
cli_errmsg("fileblobDestroy: %s not saved: report to https://github.com/Cisco-Talos/clamav/issues\n",
(fb->fullname) ? fb->fullname : fb->b.name);
free(fb->b.name);
} else
cli_errmsg("fileblobDestroy: file not saved (%lu bytes): report to https://github.com/Cisco-Talos/clamav/issues\n",
(unsigned long)fb->b.len);
}
if (fb->fullname)
free(fb->fullname);
#ifdef CL_DEBUG
fb->b.magic = INVALIDCLASS;
#endif
free(fb);
2004-08-22 10:37:32 +00:00
}
void fileblobPartialSet(fileblob *fb, const char *fullname, const char *arg)
{
UNUSEDPARAM(arg);
if (fb->b.name)
return;
assert(fullname != NULL);
cli_dbgmsg("fileblobPartialSet: saving to %s\n", fullname);
fb->fd = open(fullname, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, 0600);
if (fb->fd < 0) {
cli_errmsg("fileblobPartialSet: unable to create file: %s\n", fullname);
return;
}
fb->fp = fdopen(fb->fd, "wb");
if (fb->fp == NULL) {
cli_errmsg("fileblobSetFilename: fdopen failed\n");
close(fb->fd);
return;
}
libclamav: Add engine option to toggle temp directory recursion Temp directory recursion in ClamAV is when each layer of a scan gets its own temp directory in the parent layer's temp directory. In addition to temp directory recursion, ClamAV has been creating a new subdirectory for each file scan as a risk-adverse method to ensure no temporary file leaks fill up the disk. Creating a directory is relatively slow on Windows in particular if scanning a lot of very small files. This commit: 1. Separates the temp directory recursion feature from the leave-temps feature so that libclamav can leave temp files without making subdirectories for each file scanned. 2. Makes it so that when temp directory recursion is off, libclamav will just use the configure temp directory for all files. The new option to enable temp directory recursion is for libclamav-only at this time. It is off by default, and you can enable it like this: ```c cl_engine_set_num(engine, CL_ENGINE_TMPDIR_RECURSION, 1); ``` For the `clamscan` and `clamd` programs, temp directory recursion will be enabled when `--leave-temps` / `LeaveTemporaryFiles` is enabled. The difference is that when disabled, it will return to using the configured temp directory without making a subdirectory for each file scanned, so as to improve scan performance for small files, mostly on Windows. Under the hood, this commit also: 1. Cleans up how we keep track of tmpdirs for each layer. The goal here is to align how we keep track of layer-specific stuff using the scan_layer structure. 2. Cleans up how we record metadata JSON for embedded files. Note: Embedded files being different from Contained files, as they are extracted not with a parser, but by finding them with file type magic signatures. CLAM-1583
2025-06-09 20:42:31 -04:00
blobSetFilename(&fb->b, fb->ctx ? fb->ctx->this_layer_tmpdir : NULL, fullname);
if (fb->b.data)
if (fileblobAddData(fb, fb->b.data, fb->b.len) == 0) {
free(fb->b.data);
fb->b.data = NULL;
fb->b.len = fb->b.size = 0;
fb->isNotEmpty = 1;
}
fb->fullname = cli_safer_strdup(fullname);
}
void fileblobSetFilename(fileblob *fb, const char *dir, const char *filename)
2004-08-22 10:37:32 +00:00
{
char *fullname;
2004-08-22 10:37:32 +00:00
if (fb->b.name)
return;
2004-08-22 10:37:32 +00:00
assert(filename != NULL);
assert(dir != NULL);
2005-03-03 09:28:19 +00:00
blobSetFilename(&fb->b, dir, filename);
2004-08-22 10:37:32 +00:00
/*
* Reload the filename, it may be different from the one we've
* asked for, e.g. '/'s taken out
*/
filename = blobGetFilename(&fb->b);
assert(filename != NULL);
if (cli_gentempfd(dir, &fullname, &fb->fd) != CL_SUCCESS) return;
cli_dbgmsg("fileblobSetFilename: file %s saved to %s\n", filename, fullname);
fb->fp = fdopen(fb->fd, "wb");
if (fb->fp == NULL) {
cli_errmsg("fileblobSetFilename: fdopen failed\n");
close(fb->fd);
free(fullname);
return;
}
if (fb->b.data)
if (fileblobAddData(fb, fb->b.data, fb->b.len) == 0) {
free(fb->b.data);
fb->b.data = NULL;
fb->b.len = fb->b.size = 0;
fb->isNotEmpty = 1;
}
fb->fullname = fullname;
2004-08-22 10:37:32 +00:00
}
int fileblobAddData(fileblob *fb, const unsigned char *data, size_t len)
2004-08-22 10:37:32 +00:00
{
if (len == 0)
return 0;
assert(data != NULL);
if (fb->fp) {
#if defined(MAX_SCAN_SIZE) && (MAX_SCAN_SIZE > 0)
const cli_ctx *ctx = fb->ctx;
if (fb->isInfected) /* pretend all was written */
return 0;
if (ctx) {
int do_scan = 1;
if (cli_checklimits("fileblobAddData", ctx, fb->bytes_scanned, 0, 0) != CL_CLEAN)
do_scan = 0;
if (fb->bytes_scanned > MAX_SCAN_SIZE)
do_scan = 0;
if (do_scan) {
if (ctx->scanned)
*ctx->scanned += len;
fb->bytes_scanned += 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)) {
fb->isInfected = 1;
}
}
}
#endif
if (fwrite(data, len, 1, fb->fp) != 1) {
cli_errmsg("fileblobAddData: Can't write %lu bytes to temporary file %s\n",
(unsigned long)len, fb->b.name);
return -1;
}
fb->isNotEmpty = 1;
return 0;
}
return blobAddData(&(fb->b), data, len);
2004-08-22 10:37:32 +00:00
}
const char *
fileblobGetFilename(const fileblob *fb)
{
return blobGetFilename(&(fb->b));
2004-08-22 10:37:32 +00:00
}
void fileblobSetCTX(fileblob *fb, cli_ctx *ctx)
{
fb->ctx = ctx;
}
/*
* Performs a full scan on the fileblob, returning ClamAV status:
* CL_BREAK means clean
* CL_CLEAN means unknown
* CL_VIRUS means infected
*/
cl_error_t fileblobScan(const fileblob *fb)
{
cl_error_t rc;
STATBUF sb;
if (fb->isInfected)
return CL_VIRUS;
if (fb->fp == NULL || fb->fullname == NULL) {
/* shouldn't happen, scan called before fileblobSetFilename */
cli_warnmsg("fileblobScan, fullname == NULL\n");
return CL_ENULLARG; /* there is no CL_UNKNOWN */
}
if (fb->ctx == NULL) {
/* fileblobSetCTX hasn't been called */
cli_dbgmsg("fileblobScan, ctx == NULL\n");
return CL_CLEAN; /* there is no CL_UNKNOWN */
}
fflush(fb->fp);
lseek(fb->fd, 0, SEEK_SET);
FSTAT(fb->fd, &sb);
rc = cli_matchmeta(fb->ctx, fb->b.name, sb.st_size, sb.st_size, 0, 0, 0);
if (rc != CL_SUCCESS) {
return rc;
}
rc = cli_magic_scan_desc(fb->fd, fb->fullname, fb->ctx, fb->b.name, LAYER_ATTRIBUTES_NONE);
if (rc != CL_SUCCESS) {
return rc;
}
return CL_BREAK;
}
/*
* Doesn't perform a full scan just lets the caller know if something suspicious has
* been seen yet
*/
int fileblobInfected(const fileblob *fb)
{
return fb->isInfected;
}
/*
* Different operating systems allow different characters in their filenames
* FIXME: What does QNX want? There is no #ifdef C_QNX, but if there were
* it may be best to treat it like MSDOS
*/
void sanitiseName(char *name)
{
char c;
while ((c = *name)) {
if (c != '.' && c != '_' && (c > 'z' || c < '0' || (c > '9' && c < 'A') || (c > 'Z' && c < 'a'))) {
*name = '_';
}
name++;
}
}