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.
|
2019-01-25 10:15:50 -05:00
|
|
|
* Copyright (C) 2007-2013 Sourcefire, Inc.
|
2008-04-02 15:24:51 +00:00
|
|
|
*
|
|
|
|
* Authors: Nigel Horne
|
2003-07-29 15:48:06 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
2008-04-02 15:24:51 +00:00
|
|
|
* 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
|
2006-04-09 19:59:28 +00:00
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
|
|
* MA 02110-1301, USA.
|
2003-07-29 15:48:06 +00:00
|
|
|
*/
|
2008-04-02 15:24:51 +00:00
|
|
|
|
2004-02-06 13:46:08 +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>
|
2004-08-30 11:37:47 +00:00
|
|
|
#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
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
#ifdef HAVE_SYS_PARAM_H
|
|
|
|
#include <sys/param.h> /* for NAME_MAX */
|
2006-07-25 15:09:45 +00:00
|
|
|
#endif
|
2004-08-22 10:37:32 +00:00
|
|
|
|
2018-12-03 12:40:13 -05: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
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
#ifdef HAVE_UNISTD_H
|
2006-10-15 14:42:39 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
2022-08-10 09:41:30 -07:00
|
|
|
#include "clamav.h"
|
2006-05-03 09:36:40 +00:00
|
|
|
#include "others.h"
|
2003-07-29 15:48:06 +00:00
|
|
|
#include "mbox.h"
|
2006-05-03 09:36:40 +00:00
|
|
|
#include "matcher.h"
|
2007-08-29 18:21:39 +00:00
|
|
|
#include "scanners.h"
|
2008-02-11 18:34:28 +00:00
|
|
|
#include "filetypes.h"
|
2003-07-29 15:48:06 +00:00
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
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 */
|
|
|
|
/* *\/ */
|
2006-12-13 13:52:39 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
static const char *blobGetFilename(const blob *b);
|
2007-04-21 22:59:28 +00:00
|
|
|
|
2003-07-29 15:48:06 +00:00
|
|
|
blob *
|
|
|
|
blobCreate(void)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
#ifdef CL_DEBUG
|
2022-05-08 14:59:09 -07:00
|
|
|
blob *b = (blob *)calloc(1, sizeof(blob));
|
2018-12-03 12:40:13 -05:00
|
|
|
if (b)
|
|
|
|
b->magic = BLOBCLASS;
|
|
|
|
cli_dbgmsg("blobCreate\n");
|
|
|
|
return b;
|
2003-07-29 15:48:06 +00:00
|
|
|
#else
|
2022-05-08 14:59:09 -07:00
|
|
|
return (blob *)calloc(1, sizeof(blob));
|
2003-07-29 15:48:06 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
void blobDestroy(blob *b)
|
2003-07-29 15:48:06 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
#ifdef CL_DEBUG
|
|
|
|
cli_dbgmsg("blobDestroy %d\n", b->magic);
|
2003-08-30 19:20:04 +00:00
|
|
|
#else
|
2018-12-03 12:40:13 -05:00
|
|
|
cli_dbgmsg("blobDestroy\n");
|
2003-08-30 19:20:04 +00:00
|
|
|
#endif
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(b != NULL);
|
2019-03-26 15:09:52 -04:00
|
|
|
#ifdef CL_DEBUG
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(b->magic == BLOBCLASS);
|
2019-03-26 15:09:52 -04:00
|
|
|
#endif
|
2003-07-29 15:48:06 +00:00
|
|
|
|
2018-12-03 12:40:13 -05: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
|
2018-12-03 12:40:13 -05:00
|
|
|
free(b);
|
2003-07-29 15:48:06 +00:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
void blobArrayDestroy(blob *blobList[], int n)
|
2003-07-29 15:48:06 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05: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
|
|
|
}
|
|
|
|
|
2007-12-13 16:18:18 +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)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
void *ret;
|
2007-12-13 16:18:18 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(b != NULL);
|
2019-03-26 15:09:52 -04:00
|
|
|
#ifdef CL_DEBUG
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(b->magic == BLOBCLASS);
|
2019-03-26 15:09:52 -04:00
|
|
|
#endif
|
2007-12-13 16:18:18 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (!b->isClosed)
|
|
|
|
blobClose(b);
|
|
|
|
if (b->name)
|
|
|
|
free(b->name);
|
|
|
|
#ifdef CL_DEBUG
|
|
|
|
b->magic = INVALIDCLASS;
|
2007-12-13 16:18:18 +00:00
|
|
|
#endif
|
2018-12-03 12:40:13 -05:00
|
|
|
ret = (void *)b->data;
|
|
|
|
free(b);
|
2007-12-13 16:18:18 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
return ret;
|
2007-12-13 16:18:18 +00:00
|
|
|
}
|
|
|
|
|
2004-08-22 15:10:32 +00:00
|
|
|
/*ARGSUSED*/
|
2018-12-03 12:40:13 -05:00
|
|
|
void blobSetFilename(blob *b, const char *dir, const char *filename)
|
2003-07-29 15:48:06 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(b != NULL);
|
2019-03-26 15:09:52 -04:00
|
|
|
#ifdef CL_DEBUG
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(b->magic == BLOBCLASS);
|
2019-03-26 15:09:52 -04:00
|
|
|
#endif
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(filename != NULL);
|
2003-07-29 15:48:06 +00:00
|
|
|
|
2014-07-10 18:11:49 -04:00
|
|
|
UNUSEDPARAM(dir);
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
cli_dbgmsg("blobSetFilename: %s\n", filename);
|
2004-09-06 08:34:47 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (b->name)
|
|
|
|
free(b->name);
|
2003-07-29 15:48:06 +00:00
|
|
|
|
2024-01-09 17:44:33 -05:00
|
|
|
b->name = cli_safer_strdup(filename);
|
2003-07-29 15:48:06 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (b->name)
|
|
|
|
sanitiseName(b->name);
|
2003-07-29 15:48:06 +00:00
|
|
|
}
|
|
|
|
|
2007-04-21 22:59:28 +00:00
|
|
|
static const char *
|
2003-07-29 15:48:06 +00:00
|
|
|
blobGetFilename(const blob *b)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(b != NULL);
|
2019-03-26 15:09:52 -04:00
|
|
|
#ifdef CL_DEBUG
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(b->magic == BLOBCLASS);
|
2019-03-26 15:09:52 -04:00
|
|
|
#endif
|
2003-07-29 15:48:06 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
return b->name;
|
2003-07-29 15:48:06 +00:00
|
|
|
}
|
|
|
|
|
2007-11-07 14:05:23 +00:00
|
|
|
/*
|
|
|
|
* Returns <0 for failure
|
|
|
|
*/
|
2018-12-03 12:40:13 -05:00
|
|
|
int blobAddData(blob *b, const unsigned char *data, size_t len)
|
2003-07-29 15:48:06 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
#if HAVE_CLI_GETPAGESIZE
|
2020-07-13 19:31:27 -07:00
|
|
|
static int pagesize = 0;
|
2018-12-03 12:40:13 -05:00
|
|
|
int growth;
|
2004-09-18 15:03:15 +00:00
|
|
|
#endif
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(b != NULL);
|
2019-03-26 15:09:52 -04:00
|
|
|
#ifdef CL_DEBUG
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(b->magic == BLOBCLASS);
|
2019-03-26 15:09:52 -04:00
|
|
|
#endif
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(data != NULL);
|
2003-07-29 15:48:06 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (len == 0)
|
|
|
|
return 0;
|
2003-07-29 15:48:06 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (b->isClosed) {
|
|
|
|
/*
|
2022-02-16 00:13:55 +01:00
|
|
|
* Should be cli_dbgmsg, but I want to see them for now,
|
|
|
|
* and cli_dbgmsg doesn't support debug levels
|
|
|
|
*/
|
2018-12-03 12:40:13 -05:00
|
|
|
cli_warnmsg("Reopening closed blob\n");
|
|
|
|
b->isClosed = 0;
|
|
|
|
}
|
|
|
|
/*
|
2022-02-16 00:13:55 +01:00
|
|
|
* 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
|
|
|
|
*/
|
2018-12-03 12:40:13 -05:00
|
|
|
#if HAVE_CLI_GETPAGESIZE
|
|
|
|
if (pagesize == 0) {
|
|
|
|
pagesize = cli_getpagesize();
|
2023-08-07 16:14:27 -07:00
|
|
|
if (pagesize <= 0)
|
2018-12-03 12:40:13 -05:00
|
|
|
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",
|
2022-02-16 00:13:55 +01:00
|
|
|
b->size, b->len, len, growth);*/
|
2004-12-16 15:29:51 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (b->data == NULL) {
|
|
|
|
assert(b->len == 0);
|
|
|
|
assert(b->size == 0);
|
2004-09-18 15:03:15 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
b->size = growth;
|
2022-05-09 14:28:34 -07:00
|
|
|
b->data = cli_max_malloc(growth);
|
2020-07-24 08:32:47 -07:00
|
|
|
if (NULL == b->data) {
|
2020-07-13 19:31:27 -07:00
|
|
|
b->size = 0;
|
|
|
|
return -1;
|
|
|
|
}
|
2018-12-03 12:40:13 -05:00
|
|
|
} else if (b->size < b->len + (off_t)len) {
|
2022-05-09 14:28:34 -07:00
|
|
|
unsigned char *p = cli_max_realloc(b->data, b->size + growth);
|
2004-09-18 15:03:15 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (p == NULL)
|
|
|
|
return -1;
|
2004-09-18 15:03:15 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
b->size += growth;
|
|
|
|
b->data = p;
|
|
|
|
}
|
2004-09-18 15:03:15 +00:00
|
|
|
#else
|
2018-12-03 12:40:13 -05:00
|
|
|
if (b->data == NULL) {
|
|
|
|
assert(b->len == 0);
|
|
|
|
assert(b->size == 0);
|
2004-03-25 22:42:00 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
b->size = (off_t)len * 4;
|
2022-05-09 14:28:34 -07:00
|
|
|
b->data = cli_max_malloc(b->size);
|
2020-07-24 08:32:47 -07:00
|
|
|
if (NULL == b->data) {
|
2020-07-13 19:31:27 -07:00
|
|
|
b->size = 0;
|
|
|
|
return -1;
|
|
|
|
}
|
2018-12-03 12:40:13 -05:00
|
|
|
} else if (b->size < b->len + (off_t)len) {
|
2022-05-09 14:28:34 -07:00
|
|
|
unsigned char *p = cli_max_realloc(b->data, b->size + (len * 4));
|
2004-08-01 08:22:58 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (p == NULL)
|
|
|
|
return -1;
|
2004-08-01 08:22:58 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
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
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (b->data) {
|
|
|
|
memcpy(&b->data[b->len], data, len);
|
|
|
|
b->len += (off_t)len;
|
2020-07-13 19:31:27 -07:00
|
|
|
} else {
|
|
|
|
b->size = 0;
|
|
|
|
return -1;
|
2018-12-03 12:40:13 -05:00
|
|
|
}
|
|
|
|
return 0;
|
2003-07-29 15:48:06 +00:00
|
|
|
}
|
|
|
|
|
2004-01-14 10:09:59 +00:00
|
|
|
unsigned char *
|
2003-07-29 15:48:06 +00:00
|
|
|
blobGetData(const blob *b)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(b != NULL);
|
2019-03-26 15:09:52 -04:00
|
|
|
#ifdef CL_DEBUG
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(b->magic == BLOBCLASS);
|
2019-03-26 15:09:52 -04:00
|
|
|
#endif
|
2003-07-29 15:48:06 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (b->len == 0)
|
|
|
|
return NULL;
|
|
|
|
return b->data;
|
2003-07-29 15:48:06 +00:00
|
|
|
}
|
|
|
|
|
2006-07-25 15:09:45 +00:00
|
|
|
size_t
|
2003-07-29 15:48:06 +00:00
|
|
|
blobGetDataSize(const blob *b)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(b != NULL);
|
2019-03-26 15:09:52 -04:00
|
|
|
#ifdef CL_DEBUG
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(b->magic == BLOBCLASS);
|
2019-03-26 15:09:52 -04:00
|
|
|
#endif
|
2003-07-29 15:48:06 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
return b->len;
|
2003-07-29 15:48:06 +00:00
|
|
|
}
|
2004-02-15 08:49:05 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
void blobClose(blob *b)
|
2004-02-15 08:49:05 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(b != NULL);
|
2019-03-26 15:09:52 -04:00
|
|
|
#ifdef CL_DEBUG
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(b->magic == BLOBCLASS);
|
2019-03-26 15:09:52 -04:00
|
|
|
#endif
|
2004-09-21 09:28:28 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (b->isClosed) {
|
|
|
|
cli_warnmsg("Attempt to close a previously closed blob\n");
|
|
|
|
return;
|
|
|
|
}
|
2004-09-18 15:03:15 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
/*
|
2022-02-16 00:13:55 +01: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
|
|
|
|
*/
|
2018-12-03 12:40:13 -05:00
|
|
|
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 {
|
2022-05-09 14:28:34 -07:00
|
|
|
unsigned char *ptr = cli_max_realloc(b->data, b->len);
|
2018-12-03 12:40:13 -05:00
|
|
|
|
2020-07-13 19:31:27 -07:00
|
|
|
if (ptr == NULL) {
|
2018-12-03 12:40:13 -05:00
|
|
|
return;
|
2020-07-13 19:31:27 -07:00
|
|
|
}
|
2018-12-03 12:40:13 -05:00
|
|
|
|
|
|
|
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;
|
2004-02-15 08:49:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns 0 if the blobs are the same
|
|
|
|
*/
|
2018-12-03 12:40:13 -05:00
|
|
|
int blobcmp(const blob *b1, const blob *b2)
|
2004-02-15 08:49:05 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
size_t s1, s2;
|
2004-02-15 08:49:05 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(b1 != NULL);
|
|
|
|
assert(b2 != NULL);
|
2004-02-15 08:49:05 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (b1 == b2)
|
|
|
|
return 0;
|
2004-02-15 08:49:05 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
s1 = blobGetDataSize(b1);
|
|
|
|
s2 = blobGetDataSize(b2);
|
2004-02-15 08:49:05 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (s1 != s2)
|
|
|
|
return 1;
|
2004-02-15 08:49:05 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((s1 == 0) && (s2 == 0))
|
|
|
|
return 0;
|
2004-09-18 15:03:15 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
return memcmp(blobGetData(b1), blobGetData(b2), s1);
|
2004-02-15 08:49:05 +00:00
|
|
|
}
|
2004-03-25 22:42:00 +00:00
|
|
|
|
2006-07-01 03:51:03 +00:00
|
|
|
/*
|
|
|
|
* Return clamav return code
|
|
|
|
*/
|
2018-12-03 12:40:13 -05:00
|
|
|
int blobGrow(blob *b, size_t len)
|
2004-03-25 22:42:00 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(b != NULL);
|
2019-03-26 15:09:52 -04:00
|
|
|
#ifdef CL_DEBUG
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(b->magic == BLOBCLASS);
|
2019-03-26 15:09:52 -04:00
|
|
|
#endif
|
2004-03-25 22:42:00 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (len == 0)
|
|
|
|
return CL_SUCCESS;
|
2004-03-25 22:42:00 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (b->isClosed) {
|
|
|
|
/*
|
2022-02-16 00:13:55 +01:00
|
|
|
* Should be cli_dbgmsg, but I want to see them for now,
|
|
|
|
* and cli_dbgmsg doesn't support debug levels
|
|
|
|
*/
|
2018-12-03 12:40:13 -05:00
|
|
|
cli_warnmsg("Growing closed blob\n");
|
|
|
|
b->isClosed = 0;
|
|
|
|
}
|
|
|
|
if (b->data == NULL) {
|
|
|
|
assert(b->len == 0);
|
|
|
|
assert(b->size == 0);
|
|
|
|
|
2022-05-09 14:28:34 -07:00
|
|
|
b->data = cli_max_malloc(len);
|
2018-12-03 12:40:13 -05:00
|
|
|
if (b->data)
|
|
|
|
b->size = (off_t)len;
|
|
|
|
} else {
|
2022-05-09 14:28:34 -07:00
|
|
|
unsigned char *ptr = cli_max_realloc(b->data, b->size + len);
|
2018-12-03 12:40:13 -05:00
|
|
|
|
|
|
|
if (ptr) {
|
|
|
|
b->size += (off_t)len;
|
|
|
|
b->data = ptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (b->data) ? CL_SUCCESS : CL_EMEM;
|
2004-03-25 22:42:00 +00:00
|
|
|
}
|
2004-08-22 10:37:32 +00:00
|
|
|
|
|
|
|
fileblob *
|
|
|
|
fileblobCreate(void)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
#ifdef CL_DEBUG
|
2022-05-08 14:59:09 -07:00
|
|
|
fileblob *fb = (fileblob *)calloc(1, sizeof(fileblob));
|
2018-12-03 12:40:13 -05:00
|
|
|
if (fb)
|
|
|
|
fb->b.magic = BLOBCLASS;
|
|
|
|
cli_dbgmsg("blobCreate\n");
|
|
|
|
return fb;
|
2004-08-22 10:37:32 +00:00
|
|
|
#else
|
2022-05-08 14:59:09 -07:00
|
|
|
return (fileblob *)calloc(1, sizeof(fileblob));
|
2004-08-22 10:37:32 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2007-08-29 18:21:39 +00:00
|
|
|
/*
|
|
|
|
* Returns CL_CLEAN or CL_VIRUS. Destroys the fileblob and removes the file
|
|
|
|
* if possible
|
|
|
|
*/
|
2018-12-03 12:40:13 -05:00
|
|
|
int fileblobScanAndDestroy(fileblob *fb)
|
2007-08-29 18:21:39 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
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;
|
|
|
|
}
|
2007-08-29 18:21:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Destroy the fileblob, and remove the file associated with it
|
|
|
|
*/
|
2018-12-03 12:40:13 -05:00
|
|
|
void fileblobDestructiveDestroy(fileblob *fb)
|
2007-08-29 18:21:39 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
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);
|
2007-08-29 18:21:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Destroy the fileblob, and remove the file associated with it if that file is
|
|
|
|
* empty
|
|
|
|
*/
|
2018-12-03 12:40:13 -05:00
|
|
|
void fileblobDestroy(fileblob *fb)
|
2004-08-22 10:37:32 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(fb != NULL);
|
2019-03-26 15:09:52 -04:00
|
|
|
#ifdef CL_DEBUG
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(fb->b.magic == BLOBCLASS);
|
2019-03-26 15:09:52 -04:00
|
|
|
#endif
|
2018-12-03 12:40:13 -05:00
|
|
|
|
|
|
|
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) {
|
2021-07-17 13:33:13 -07:00
|
|
|
cli_errmsg("fileblobDestroy: %s not saved: report to https://github.com/Cisco-Talos/clamav/issues\n",
|
2018-12-03 12:40:13 -05:00
|
|
|
(fb->fullname) ? fb->fullname : fb->b.name);
|
|
|
|
free(fb->b.name);
|
|
|
|
} else
|
2021-07-17 13:33:13 -07:00
|
|
|
cli_errmsg("fileblobDestroy: file not saved (%lu bytes): report to https://github.com/Cisco-Talos/clamav/issues\n",
|
2018-12-03 12:40:13 -05:00
|
|
|
(unsigned long)fb->b.len);
|
|
|
|
}
|
|
|
|
if (fb->fullname)
|
|
|
|
free(fb->fullname);
|
|
|
|
#ifdef CL_DEBUG
|
|
|
|
fb->b.magic = INVALIDCLASS;
|
2005-03-16 14:47:26 +00:00
|
|
|
#endif
|
2018-12-03 12:40:13 -05:00
|
|
|
free(fb);
|
2004-08-22 10:37:32 +00:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
void fileblobPartialSet(fileblob *fb, const char *fullname, const char *arg)
|
2008-07-30 13:54:34 +00:00
|
|
|
{
|
2014-07-10 18:11:49 -04:00
|
|
|
UNUSEDPARAM(arg);
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
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);
|
2018-12-03 12:40:13 -05:00
|
|
|
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;
|
|
|
|
}
|
2024-01-09 17:44:33 -05:00
|
|
|
fb->fullname = cli_safer_strdup(fullname);
|
2008-07-30 13:54:34 +00:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
void fileblobSetFilename(fileblob *fb, const char *dir, const char *filename)
|
2004-08-22 10:37:32 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
char *fullname;
|
2004-08-22 10:37:32 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (fb->b.name)
|
|
|
|
return;
|
2004-08-22 10:37:32 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
assert(filename != NULL);
|
|
|
|
assert(dir != NULL);
|
2005-03-03 09:28:19 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
blobSetFilename(&fb->b, dir, filename);
|
2004-08-22 10:37:32 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
/*
|
2022-02-16 00:13:55 +01:00
|
|
|
* Reload the filename, it may be different from the one we've
|
|
|
|
* asked for, e.g. '/'s taken out
|
|
|
|
*/
|
2018-12-03 12:40:13 -05:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
int fileblobAddData(fileblob *fb, const unsigned char *data, size_t len)
|
2004-08-22 10:37:32 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05: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)
|
ClamScan & libclamav: improve precision of bytes-scanned, bytes-read
The ClamScan scan summary prints bytes scanned and bytes read in
multiples of 4096 (aka `CL_COUNT_PRECISION`), as is provided by the
`cl_scanfile()`, `cl_scandesc()`, `cl_scanfile_callback()`, and
`cl_scandesc_callback()` functions.
I believe this imprecision was the result of using an `unsigned long int`
which may be 64bit or 32bit, depending on platform. I believe the
intention was to be able to support scanning more than 4 GiB of data.
Since the new `cl_scan*_ex()` functions use a `uint64_t`, which
guarantees a 64bit integer and supports ~16,777,216 terabytes, I find no
reason not to report an accurate count.
For the legacy scan functions (above) I've kept the `CL_COUNT_PRECISION`
behavior to maintain backwards compatibility.
I have also improved the bytes scanned/read output to report GiB, MiB,
KiB, or B as appropriate. Previously, it always report "MB".
CLAM-1433
2025-06-25 14:39:11 -04:00
|
|
|
*ctx->scanned += len;
|
|
|
|
fb->bytes_scanned += len;
|
2018-12-03 12:40:13 -05:00
|
|
|
|
2020-03-21 14:15:28 -04:00
|
|
|
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)) {
|
2018-12-03 12:40:13 -05:00
|
|
|
fb->isInfected = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-12-13 13:52:39 +00:00
|
|
|
#endif
|
2006-05-03 09:36:40 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
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)
|
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
return blobGetFilename(&(fb->b));
|
2004-08-22 10:37:32 +00:00
|
|
|
}
|
2004-09-06 08:34:47 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
void fileblobSetCTX(fileblob *fb, cli_ctx *ctx)
|
2006-05-03 15:41:44 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
fb->ctx = ctx;
|
2006-05-03 15:41:44 +00:00
|
|
|
}
|
|
|
|
|
2007-08-29 18:21:39 +00:00
|
|
|
/*
|
|
|
|
* Performs a full scan on the fileblob, returning ClamAV status:
|
|
|
|
* CL_BREAK means clean
|
|
|
|
* CL_CLEAN means unknown
|
|
|
|
* CL_VIRUS means infected
|
|
|
|
*/
|
2022-08-10 09:41:30 -07:00
|
|
|
cl_error_t fileblobScan(const fileblob *fb)
|
2007-08-29 18:21:39 +00:00
|
|
|
{
|
2022-08-10 09:41:30 -07:00
|
|
|
cl_error_t rc;
|
2018-12-03 12:40:13 -05:00
|
|
|
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);
|
2022-08-10 09:41:30 -07:00
|
|
|
|
2023-11-07 23:00:30 -05:00
|
|
|
rc = cli_matchmeta(fb->ctx, fb->b.name, sb.st_size, sb.st_size, 0, 0, 0);
|
2022-08-10 09:41:30 -07:00
|
|
|
if (rc != CL_SUCCESS) {
|
|
|
|
return rc;
|
2018-12-03 12:40:13 -05:00
|
|
|
}
|
|
|
|
|
2022-03-09 22:26:40 -08:00
|
|
|
rc = cli_magic_scan_desc(fb->fd, fb->fullname, fb->ctx, fb->b.name, LAYER_ATTRIBUTES_NONE);
|
2022-08-10 09:41:30 -07:00
|
|
|
if (rc != CL_SUCCESS) {
|
|
|
|
return rc;
|
2018-12-03 12:40:13 -05:00
|
|
|
}
|
2022-08-10 09:41:30 -07:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
return CL_BREAK;
|
2007-08-29 18:21:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Doesn't perform a full scan just lets the caller know if something suspicious has
|
|
|
|
* been seen yet
|
|
|
|
*/
|
2018-12-03 12:40:13 -05:00
|
|
|
int fileblobInfected(const fileblob *fb)
|
2006-05-03 15:41:44 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
return fb->isInfected;
|
2006-05-03 15:41:44 +00:00
|
|
|
}
|
2007-04-02 17:49:01 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
2018-12-03 12:40:13 -05:00
|
|
|
void sanitiseName(char *name)
|
2007-04-02 17:49:01 +00:00
|
|
|
{
|
2018-12-03 12:40:13 -05:00
|
|
|
char c;
|
|
|
|
while ((c = *name)) {
|
2022-08-10 09:41:30 -07:00
|
|
|
if (c != '.' && c != '_' && (c > 'z' || c < '0' || (c > '9' && c < 'A') || (c > 'Z' && c < 'a'))) {
|
2018-12-03 12:40:13 -05:00
|
|
|
*name = '_';
|
2022-08-10 09:41:30 -07:00
|
|
|
}
|
2018-12-03 12:40:13 -05:00
|
|
|
name++;
|
|
|
|
}
|
2007-04-02 17:49:01 +00:00
|
|
|
}
|