clamav/libclamav/blob.c

694 lines
16 KiB
C
Raw Normal View History

2003-07-29 15:48:06 +00:00
/*
2024-01-12 17:03:59 -05:00
* Copyright (C) 2013-2024 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;
}
Record names of extracted files A way is needed to record scanned file names for two purposes: 1. File names (and extensions) must be stored in the json metadata properties recorded when using the --gen-json clamscan option. Future work may use this to compare file extensions with detected file types. 2. File names are useful when interpretting tmp directory output when using the --leave-temps option. This commit enables file name retention for later use by storing file names in the fmap header structure, if a file name exists. To store the names in fmaps, an optional name argument has been added to any internal scan API's that create fmaps and every call to these APIs has been modified to pass a file name or NULL if a file name is not required. The zip and gpt parsers required some modification to record file names. The NSIS and XAR parsers fail to collect file names at all and will require future work to support file name extraction. Also: - Added recursive extraction to the tmp directory when the --leave-temps option is enabled. When not enabled, the tmp directory structure remains flat so as to prevent the likelihood of exceeding MAX_PATH. The current tmp directory is stored in the scan context. - Made the cli_scanfile() internal API non-static and added it to scanners.h so it would be accessible outside of scanners.c in order to remove code duplication within libmspack.c. - Added function comments to scanners.h and matcher.h - Converted a TDB-type macros and LSIG-type macros to enums for improved type safey. - Converted more return status variables from `int` to `cl_error_t` for improved type safety, and corrected ooxml file typing functions so they use `cli_file_t` exclusively rather than mixing types with `cl_error_t`. - Restructured the magic_scandesc() function to use goto's for error handling and removed the early_ret_from_magicscan() macro and magic_scandesc_cleanup() function. This makes the code easier to read and made it easier to add the recursive tmp directory cleanup to magic_scandesc(). - Corrected zip, egg, rar filename extraction issues. - Removed use of extra sub-directory layer for zip, egg, and rar file extraction. For Zip, this also involved changing the extracted filenames to be randomly generated rather than using the "zip.###" file name scheme.
2020-03-19 21:23:54 -04:00
blobSetFilename(&fb->b, fb->ctx ? fb->ctx->sub_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 += (unsigned long)len / CL_COUNT_PRECISION;
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)) {
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++;
}
}