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.
|
2013-10-14 17:07:40 -04:00
|
|
|
* Copyright (C) 2007-2013 Sourcefire, Inc.
|
2009-02-13 10:55:45 +00:00
|
|
|
*
|
|
|
|
* Authors: Tomasz Kojm
|
2003-07-29 15:48:06 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
2007-03-31 20:31:04 +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
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2004-02-13 21:36:32 +00:00
|
|
|
#if HAVE_CONFIG_H
|
|
|
|
#include "clamav-config.h"
|
|
|
|
#endif
|
|
|
|
|
2003-07-29 15:48:06 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
2010-12-28 18:24:51 +01:00
|
|
|
#ifdef HAVE_PWD_H
|
|
|
|
#include <pwd.h>
|
|
|
|
#endif
|
2009-09-24 19:23:21 +02:00
|
|
|
#include <dirent.h>
|
|
|
|
#ifndef _WIN32
|
2003-07-29 15:48:06 +00:00
|
|
|
#include <sys/wait.h>
|
2004-11-23 22:27:24 +00:00
|
|
|
#include <utime.h>
|
2008-07-30 15:20:30 +00:00
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/resource.h>
|
2007-02-25 02:54:38 +00:00
|
|
|
#endif
|
2003-07-29 15:48:06 +00:00
|
|
|
#include <fcntl.h>
|
2018-12-03 12:40:13 -05:00
|
|
|
#ifdef HAVE_UNISTD_H
|
2003-07-29 15:48:06 +00:00
|
|
|
#include <unistd.h>
|
2007-02-25 02:54:38 +00:00
|
|
|
#endif
|
2003-07-29 15:48:06 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <errno.h>
|
2021-07-16 11:47:23 -07:00
|
|
|
#include <math.h>
|
2003-07-29 15:48:06 +00:00
|
|
|
|
Add CMake build tooling
This patch adds experimental-quality CMake build tooling.
The libmspack build required a modification to use "" instead of <> for
header #includes. This will hopefully be included in the libmspack
upstream project when adding CMake build tooling to libmspack.
Removed use of libltdl when using CMake.
Flex & Bison are now required to build.
If -DMAINTAINER_MODE, then GPERF is also required, though it currently
doesn't actually do anything. TODO!
I found that the autotools build system was generating the lexer output
but not actually compiling it, instead using previously generated (and
manually renamed) lexer c source. As a consequence, changes to the .l
and .y files weren't making it into the build. To resolve this, I
removed generated flex/bison files and fixed the tooling to use the
freshly generated files. Flex and bison are now required build tools.
On Windows, this adds a dependency on the winflexbison package,
which can be obtained using Chocolatey or may be manually installed.
CMake tooling only has partial support for building with external LLVM
library, and no support for the internal LLVM (to be removed in the
future). I.e. The CMake build currently only supports the bytecode
interpreter.
Many files used include paths relative to the top source directory or
relative to the current project, rather than relative to each build
target. Modern CMake support requires including internal dependency
headers the same way you would external dependency headers (albeit
with "" instead of <>). This meant correcting all header includes to
be relative to the build targets and not relative to the workspace.
For example, ...
```c
include "../libclamav/clamav.h"
include "clamd/clamd_others.h"
```
... becomes:
```c
// libclamav
include "clamav.h"
// clamd
include "clamd_others.h"
```
Fixes header name conflicts by renaming a few of the files.
Converted the "shared" code into a static library, which depends on
libclamav. The ironically named "shared" static library provides
features common to the ClamAV apps which are not required in
libclamav itself and are not intended for use by downstream projects.
This change was required for correct modern CMake practices but was
also required to use the automake "subdir-objects" option.
This eliminates warnings when running autoreconf which, in the next
version of autoconf & automake are likely to break the build.
libclamav used to build in multiple stages where an earlier stage is
a static library containing utils required by the "shared" code.
Linking clamdscan and clamdtop with this libclamav utils static lib
allowed these two apps to function without libclamav. While this is
nice in theory, the practical gains are minimal and it complicates
the build system. As such, the autotools and CMake tooling was
simplified for improved maintainability and this feature was thrown
out. clamdtop and clamdscan now require libclamav to function.
Removed the nopthreads version of the autotools
libclamav_internal_utils static library and added pthread linking to
a couple apps that may have issues building on some platforms without
it, with the intention of removing needless complexity from the
source. Kept the regular version of libclamav_internal_utils.la
though it is no longer used anywhere but in libclamav.
Added an experimental doxygen build option which attempts to build
clamav.h and libfreshclam doxygen html docs.
The CMake build tooling also may build the example program(s), which
isn't a feature in the Autotools build system.
Changed C standard to C90+ due to inline linking issues with socket.h
when linking libfreshclam.so on Linux.
Generate common.rc for win32.
Fix tabs/spaces in shared Makefile.am, and remove vestigial ifndef
from misc.c.
Add CMake files to the automake dist, so users can try the new
CMake tooling w/out having to build from a git clone.
clamonacc changes:
- Renamed FANOTIFY macro to HAVE_SYS_FANOTIFY_H to better match other
similar macros.
- Added a new clamav-clamonacc.service systemd unit file, based on
the work of ChadDevOps & Aaron Brighton.
- Added missing clamonacc man page.
Updates to clamdscan man page, add missing options.
Remove vestigial CL_NOLIBCLAMAV definitions (all apps now use
libclamav).
Rename Windows mspack.dll to libmspack.dll so all ClamAV-built
libraries have the lib-prefix with Visual Studio as with CMake.
2020-08-13 00:25:34 -07:00
|
|
|
// libclamav
|
|
|
|
#include "clamav.h"
|
|
|
|
#include "others.h"
|
|
|
|
#include "matcher-ac.h"
|
|
|
|
#include "matcher-pcre.h"
|
|
|
|
#include "str.h"
|
|
|
|
#include "readdb.h"
|
2024-05-07 09:07:56 -07:00
|
|
|
#include "default.h"
|
Add CMake build tooling
This patch adds experimental-quality CMake build tooling.
The libmspack build required a modification to use "" instead of <> for
header #includes. This will hopefully be included in the libmspack
upstream project when adding CMake build tooling to libmspack.
Removed use of libltdl when using CMake.
Flex & Bison are now required to build.
If -DMAINTAINER_MODE, then GPERF is also required, though it currently
doesn't actually do anything. TODO!
I found that the autotools build system was generating the lexer output
but not actually compiling it, instead using previously generated (and
manually renamed) lexer c source. As a consequence, changes to the .l
and .y files weren't making it into the build. To resolve this, I
removed generated flex/bison files and fixed the tooling to use the
freshly generated files. Flex and bison are now required build tools.
On Windows, this adds a dependency on the winflexbison package,
which can be obtained using Chocolatey or may be manually installed.
CMake tooling only has partial support for building with external LLVM
library, and no support for the internal LLVM (to be removed in the
future). I.e. The CMake build currently only supports the bytecode
interpreter.
Many files used include paths relative to the top source directory or
relative to the current project, rather than relative to each build
target. Modern CMake support requires including internal dependency
headers the same way you would external dependency headers (albeit
with "" instead of <>). This meant correcting all header includes to
be relative to the build targets and not relative to the workspace.
For example, ...
```c
include "../libclamav/clamav.h"
include "clamd/clamd_others.h"
```
... becomes:
```c
// libclamav
include "clamav.h"
// clamd
include "clamd_others.h"
```
Fixes header name conflicts by renaming a few of the files.
Converted the "shared" code into a static library, which depends on
libclamav. The ironically named "shared" static library provides
features common to the ClamAV apps which are not required in
libclamav itself and are not intended for use by downstream projects.
This change was required for correct modern CMake practices but was
also required to use the automake "subdir-objects" option.
This eliminates warnings when running autoreconf which, in the next
version of autoconf & automake are likely to break the build.
libclamav used to build in multiple stages where an earlier stage is
a static library containing utils required by the "shared" code.
Linking clamdscan and clamdtop with this libclamav utils static lib
allowed these two apps to function without libclamav. While this is
nice in theory, the practical gains are minimal and it complicates
the build system. As such, the autotools and CMake tooling was
simplified for improved maintainability and this feature was thrown
out. clamdtop and clamdscan now require libclamav to function.
Removed the nopthreads version of the autotools
libclamav_internal_utils static library and added pthread linking to
a couple apps that may have issues building on some platforms without
it, with the intention of removing needless complexity from the
source. Kept the regular version of libclamav_internal_utils.la
though it is no longer used anywhere but in libclamav.
Added an experimental doxygen build option which attempts to build
clamav.h and libfreshclam doxygen html docs.
The CMake build tooling also may build the example program(s), which
isn't a feature in the Autotools build system.
Changed C standard to C90+ due to inline linking issues with socket.h
when linking libfreshclam.so on Linux.
Generate common.rc for win32.
Fix tabs/spaces in shared Makefile.am, and remove vestigial ifndef
from misc.c.
Add CMake files to the automake dist, so users can try the new
CMake tooling w/out having to build from a git clone.
clamonacc changes:
- Renamed FANOTIFY macro to HAVE_SYS_FANOTIFY_H to better match other
similar macros.
- Added a new clamav-clamonacc.service systemd unit file, based on
the work of ChadDevOps & Aaron Brighton.
- Added missing clamonacc man page.
Updates to clamdscan man page, add missing options.
Remove vestigial CL_NOLIBCLAMAV definitions (all apps now use
libclamav).
Rename Windows mspack.dll to libmspack.dll so all ClamAV-built
libraries have the lib-prefix with Visual Studio as with CMake.
2020-08-13 00:25:34 -07:00
|
|
|
|
2021-03-04 19:39:50 -08:00
|
|
|
// common
|
Add CMake build tooling
This patch adds experimental-quality CMake build tooling.
The libmspack build required a modification to use "" instead of <> for
header #includes. This will hopefully be included in the libmspack
upstream project when adding CMake build tooling to libmspack.
Removed use of libltdl when using CMake.
Flex & Bison are now required to build.
If -DMAINTAINER_MODE, then GPERF is also required, though it currently
doesn't actually do anything. TODO!
I found that the autotools build system was generating the lexer output
but not actually compiling it, instead using previously generated (and
manually renamed) lexer c source. As a consequence, changes to the .l
and .y files weren't making it into the build. To resolve this, I
removed generated flex/bison files and fixed the tooling to use the
freshly generated files. Flex and bison are now required build tools.
On Windows, this adds a dependency on the winflexbison package,
which can be obtained using Chocolatey or may be manually installed.
CMake tooling only has partial support for building with external LLVM
library, and no support for the internal LLVM (to be removed in the
future). I.e. The CMake build currently only supports the bytecode
interpreter.
Many files used include paths relative to the top source directory or
relative to the current project, rather than relative to each build
target. Modern CMake support requires including internal dependency
headers the same way you would external dependency headers (albeit
with "" instead of <>). This meant correcting all header includes to
be relative to the build targets and not relative to the workspace.
For example, ...
```c
include "../libclamav/clamav.h"
include "clamd/clamd_others.h"
```
... becomes:
```c
// libclamav
include "clamav.h"
// clamd
include "clamd_others.h"
```
Fixes header name conflicts by renaming a few of the files.
Converted the "shared" code into a static library, which depends on
libclamav. The ironically named "shared" static library provides
features common to the ClamAV apps which are not required in
libclamav itself and are not intended for use by downstream projects.
This change was required for correct modern CMake practices but was
also required to use the automake "subdir-objects" option.
This eliminates warnings when running autoreconf which, in the next
version of autoconf & automake are likely to break the build.
libclamav used to build in multiple stages where an earlier stage is
a static library containing utils required by the "shared" code.
Linking clamdscan and clamdtop with this libclamav utils static lib
allowed these two apps to function without libclamav. While this is
nice in theory, the practical gains are minimal and it complicates
the build system. As such, the autotools and CMake tooling was
simplified for improved maintainability and this feature was thrown
out. clamdtop and clamdscan now require libclamav to function.
Removed the nopthreads version of the autotools
libclamav_internal_utils static library and added pthread linking to
a couple apps that may have issues building on some platforms without
it, with the intention of removing needless complexity from the
source. Kept the regular version of libclamav_internal_utils.la
though it is no longer used anywhere but in libclamav.
Added an experimental doxygen build option which attempts to build
clamav.h and libfreshclam doxygen html docs.
The CMake build tooling also may build the example program(s), which
isn't a feature in the Autotools build system.
Changed C standard to C90+ due to inline linking issues with socket.h
when linking libfreshclam.so on Linux.
Generate common.rc for win32.
Fix tabs/spaces in shared Makefile.am, and remove vestigial ifndef
from misc.c.
Add CMake files to the automake dist, so users can try the new
CMake tooling w/out having to build from a git clone.
clamonacc changes:
- Renamed FANOTIFY macro to HAVE_SYS_FANOTIFY_H to better match other
similar macros.
- Added a new clamav-clamonacc.service systemd unit file, based on
the work of ChadDevOps & Aaron Brighton.
- Added missing clamonacc man page.
Updates to clamdscan man page, add missing options.
Remove vestigial CL_NOLIBCLAMAV definitions (all apps now use
libclamav).
Rename Windows mspack.dll to libmspack.dll so all ClamAV-built
libraries have the lib-prefix with Visual Studio as with CMake.
2020-08-13 00:25:34 -07:00
|
|
|
#include "optparser.h"
|
|
|
|
#include "actions.h"
|
|
|
|
#include "output.h"
|
|
|
|
#include "misc.h"
|
|
|
|
|
2003-07-29 15:48:06 +00:00
|
|
|
#include "manager.h"
|
2007-01-30 21:11:32 +00:00
|
|
|
#include "global.h"
|
|
|
|
|
2021-08-27 09:14:45 -07:00
|
|
|
#ifdef _WIN32 /* scan memory */
|
|
|
|
#include "scanmem.h"
|
|
|
|
#endif
|
|
|
|
|
2003-07-29 15:48:06 +00:00
|
|
|
#ifdef C_LINUX
|
|
|
|
dev_t procdev;
|
|
|
|
#endif
|
|
|
|
|
2010-12-28 18:24:51 +01:00
|
|
|
#ifdef _WIN32
|
|
|
|
/* FIXME: If possible, handle users correctly */
|
|
|
|
static int checkaccess(const char *path, const char *username, int mode)
|
|
|
|
{
|
2011-04-17 13:08:11 +02:00
|
|
|
return !access(path, mode);
|
2010-12-28 18:24:51 +01:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
static int checkaccess(const char *path, const char *username, int mode)
|
|
|
|
{
|
2014-09-11 14:15:36 -04:00
|
|
|
struct passwd *user;
|
|
|
|
int ret = 0, status;
|
2010-12-28 18:24:51 +01:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (!geteuid()) {
|
|
|
|
if ((user = getpwnam(username)) == NULL) {
|
2014-09-11 14:15:36 -04:00
|
|
|
return -1;
|
|
|
|
}
|
2010-12-28 18:24:51 +01:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
switch (fork()) {
|
|
|
|
case -1:
|
|
|
|
return -2;
|
|
|
|
case 0:
|
|
|
|
if (setgid(user->pw_gid)) {
|
|
|
|
fprintf(stderr, "ERROR: setgid(%d) failed.\n", (int)user->pw_gid);
|
|
|
|
exit(0);
|
|
|
|
}
|
2010-12-28 18:24:51 +01:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (setuid(user->pw_uid)) {
|
|
|
|
fprintf(stderr, "ERROR: setuid(%d) failed.\n", (int)user->pw_uid);
|
|
|
|
exit(0);
|
|
|
|
}
|
2010-12-28 18:24:51 +01:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (access(path, mode))
|
|
|
|
exit(0);
|
|
|
|
else
|
|
|
|
exit(1);
|
|
|
|
default:
|
|
|
|
wait(&status);
|
|
|
|
if (WIFEXITED(status) && WEXITSTATUS(status) == 1)
|
|
|
|
ret = 1;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
2010-12-28 18:24:51 +01:00
|
|
|
} else {
|
2018-12-03 12:40:13 -05:00
|
|
|
if (!access(path, mode))
|
2014-09-11 14:15:36 -04:00
|
|
|
ret = 1;
|
2010-12-28 18:24:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-08-22 16:58:48 +03:00
|
|
|
struct metachain {
|
|
|
|
char **chains;
|
2014-11-06 14:51:26 -05:00
|
|
|
size_t lastadd;
|
|
|
|
size_t lastvir;
|
|
|
|
size_t level;
|
|
|
|
size_t nchains;
|
2011-08-22 16:58:48 +03:00
|
|
|
};
|
|
|
|
|
2015-10-01 17:47:37 -04:00
|
|
|
struct clamscan_cb_data {
|
2018-12-03 12:40:13 -05:00
|
|
|
struct metachain *chain;
|
|
|
|
const char *filename;
|
2015-10-01 17:47:37 -04:00
|
|
|
};
|
|
|
|
|
2011-08-22 16:58:48 +03:00
|
|
|
static cl_error_t pre(int fd, const char *type, void *context)
|
|
|
|
{
|
2014-11-06 14:51:26 -05:00
|
|
|
struct metachain *c;
|
2015-10-01 17:47:37 -04:00
|
|
|
struct clamscan_cb_data *d;
|
2014-11-06 14:51:26 -05:00
|
|
|
|
2014-07-11 09:30:58 -04:00
|
|
|
UNUSEDPARAM(fd);
|
2014-07-11 09:42:42 -04:00
|
|
|
UNUSEDPARAM(type);
|
2014-07-11 09:30:58 -04:00
|
|
|
|
2014-11-06 14:51:26 -05:00
|
|
|
if (!(context))
|
|
|
|
return CL_CLEAN;
|
2015-10-01 17:47:37 -04:00
|
|
|
d = (struct clamscan_cb_data *)context;
|
|
|
|
c = d->chain;
|
|
|
|
if (c == NULL)
|
|
|
|
return CL_CLEAN;
|
2014-11-06 14:51:26 -05:00
|
|
|
|
|
|
|
c->level++;
|
|
|
|
|
2011-08-22 16:58:48 +03:00
|
|
|
return CL_CLEAN;
|
|
|
|
}
|
|
|
|
|
2014-11-06 14:51:26 -05:00
|
|
|
static int print_chain(struct metachain *c, char *str, size_t len)
|
2011-08-22 16:58:48 +03:00
|
|
|
{
|
2014-11-06 14:51:26 -05:00
|
|
|
size_t i;
|
|
|
|
size_t na = 0;
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
for (i = 0; i < c->nchains - 1; i++) {
|
2014-11-06 14:51:26 -05:00
|
|
|
size_t n = strlen(c->chains[i]);
|
2014-09-11 14:15:36 -04:00
|
|
|
|
|
|
|
if (na)
|
|
|
|
str[na++] = '!';
|
|
|
|
|
|
|
|
if (n + na + 2 > len)
|
|
|
|
break;
|
|
|
|
|
|
|
|
memcpy(str + na, c->chains[i], n);
|
|
|
|
na += n;
|
2011-08-22 16:58:48 +03:00
|
|
|
}
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
str[na] = '\0';
|
|
|
|
str[len - 1] = '\0';
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
return i == c->nchains - 1 ? 0 : 1;
|
2011-08-22 16:58:48 +03:00
|
|
|
}
|
|
|
|
|
2025-07-27 22:47:29 -04:00
|
|
|
static cl_error_t post(int fd, int result, const char *alert_name, void *context)
|
2011-08-22 16:58:48 +03:00
|
|
|
{
|
2015-10-01 17:47:37 -04:00
|
|
|
struct clamscan_cb_data *d = context;
|
2018-12-03 12:40:13 -05:00
|
|
|
struct metachain *c = NULL;
|
2014-11-06 14:51:26 -05:00
|
|
|
char str[128];
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2014-07-11 09:30:58 -04:00
|
|
|
UNUSEDPARAM(fd);
|
|
|
|
UNUSEDPARAM(result);
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2015-10-01 17:47:37 -04:00
|
|
|
if (d != NULL)
|
|
|
|
c = d->chain;
|
|
|
|
|
2014-11-06 14:51:26 -05:00
|
|
|
if (c && c->nchains) {
|
2014-09-11 14:15:36 -04:00
|
|
|
print_chain(c, str, sizeof(str));
|
2014-11-06 14:51:26 -05:00
|
|
|
|
2025-07-27 22:47:29 -04:00
|
|
|
if (c->level == c->lastadd && !alert_name)
|
2014-11-06 14:51:26 -05:00
|
|
|
free(c->chains[--c->nchains]);
|
|
|
|
|
2025-07-27 22:47:29 -04:00
|
|
|
if (alert_name && !c->lastvir)
|
2014-09-11 14:15:36 -04:00
|
|
|
c->lastvir = c->level;
|
2011-08-22 16:58:48 +03:00
|
|
|
}
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2011-08-22 16:58:48 +03:00
|
|
|
if (c)
|
2014-09-11 14:15:36 -04:00
|
|
|
c->level--;
|
|
|
|
|
2011-08-22 16:58:48 +03:00
|
|
|
return CL_CLEAN;
|
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
static cl_error_t meta(const char *container_type, unsigned long fsize_container, const char *filename,
|
|
|
|
unsigned long fsize_real, int is_encrypted, unsigned int filepos_container, void *context)
|
2011-08-22 16:58:48 +03:00
|
|
|
{
|
|
|
|
char prev[128];
|
2014-11-06 14:51:26 -05:00
|
|
|
struct metachain *c;
|
2015-10-01 17:47:37 -04:00
|
|
|
struct clamscan_cb_data *d;
|
2014-11-06 14:51:26 -05:00
|
|
|
const char *type;
|
|
|
|
size_t n;
|
2011-08-22 16:58:48 +03:00
|
|
|
char *chain;
|
|
|
|
char **chains;
|
|
|
|
int toolong;
|
|
|
|
|
2014-07-11 09:30:58 -04:00
|
|
|
UNUSEDPARAM(fsize_container);
|
|
|
|
UNUSEDPARAM(fsize_real);
|
|
|
|
UNUSEDPARAM(is_encrypted);
|
|
|
|
UNUSEDPARAM(filepos_container);
|
|
|
|
|
2015-10-01 17:47:37 -04:00
|
|
|
if (!(context))
|
|
|
|
return CL_CLEAN;
|
|
|
|
d = (struct clamscan_cb_data *)context;
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
c = d->chain;
|
2014-11-06 14:51:26 -05:00
|
|
|
type = (strncmp(container_type, "CL_TYPE_", 8) == 0 ? container_type + 8 : container_type);
|
2018-12-03 12:40:13 -05:00
|
|
|
n = strlen(type) + strlen(filename) + 2;
|
2014-11-06 14:51:26 -05:00
|
|
|
|
2011-08-22 16:58:48 +03:00
|
|
|
if (!c)
|
2014-09-11 14:15:36 -04:00
|
|
|
return CL_CLEAN;
|
|
|
|
|
2011-08-22 16:58:48 +03:00
|
|
|
chain = malloc(n);
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2011-08-22 16:58:48 +03:00
|
|
|
if (!chain)
|
2014-09-11 14:15:36 -04:00
|
|
|
return CL_CLEAN;
|
2014-11-06 14:51:26 -05:00
|
|
|
|
2011-08-22 16:58:48 +03:00
|
|
|
if (!strcmp(type, "ANY"))
|
2018-12-03 12:40:13 -05:00
|
|
|
snprintf(chain, n, "%s", filename);
|
2011-08-22 16:58:48 +03:00
|
|
|
else
|
2018-12-03 12:40:13 -05:00
|
|
|
snprintf(chain, n, "%s:%s", type, filename);
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2011-08-22 16:58:48 +03:00
|
|
|
if (c->lastadd != c->level) {
|
2014-11-06 14:51:26 -05:00
|
|
|
n = c->nchains + 1;
|
2014-09-11 14:15:36 -04:00
|
|
|
|
|
|
|
chains = realloc(c->chains, n * sizeof(*chains));
|
|
|
|
if (!chains) {
|
|
|
|
free(chain);
|
|
|
|
return CL_CLEAN;
|
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
c->chains = chains;
|
2014-11-06 14:51:26 -05:00
|
|
|
c->nchains = n;
|
2014-09-11 14:15:36 -04:00
|
|
|
c->lastadd = c->level;
|
2011-08-22 16:58:48 +03:00
|
|
|
} else {
|
2014-11-06 14:51:26 -05:00
|
|
|
if (c->nchains > 0)
|
2018-12-03 12:40:13 -05:00
|
|
|
free(c->chains[c->nchains - 1]);
|
2011-08-22 16:58:48 +03:00
|
|
|
}
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2014-11-06 14:51:26 -05:00
|
|
|
if (c->nchains > 0) {
|
2018-12-03 12:40:13 -05:00
|
|
|
c->chains[c->nchains - 1] = chain;
|
|
|
|
toolong = print_chain(c, prev, sizeof(prev));
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_DEBUG, "Scanning %s%s!%s\n", prev, toolong ? "..." : "", chain);
|
2014-11-06 14:51:26 -05:00
|
|
|
} else {
|
|
|
|
free(chain);
|
|
|
|
}
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2011-08-22 16:58:48 +03:00
|
|
|
return CL_CLEAN;
|
|
|
|
}
|
|
|
|
|
2025-07-27 22:47:29 -04:00
|
|
|
static void clamscan_virus_found_cb(int fd, const char *alert_name, void *context)
|
2015-10-01 17:47:37 -04:00
|
|
|
{
|
|
|
|
struct clamscan_cb_data *data = (struct clamscan_cb_data *)context;
|
2018-12-03 12:40:13 -05:00
|
|
|
const char *filename;
|
2015-10-01 17:47:37 -04:00
|
|
|
|
2019-02-27 00:47:38 -05:00
|
|
|
UNUSEDPARAM(fd);
|
|
|
|
|
2015-10-01 17:47:37 -04:00
|
|
|
if (data == NULL)
|
|
|
|
return;
|
|
|
|
if (data->filename != NULL)
|
|
|
|
filename = data->filename;
|
|
|
|
else
|
|
|
|
filename = "(filename not set)";
|
2025-07-27 22:47:29 -04:00
|
|
|
logg(LOGG_INFO, "%s: %s FOUND\n", filename, alert_name);
|
2015-10-01 17:47:37 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-20 22:28:48 -04:00
|
|
|
static void scanfile(const char *filename, struct cl_engine *engine, const struct optstruct *opts, struct cl_scan_options *options)
|
2007-01-30 21:11:32 +00:00
|
|
|
{
|
clamd clients: Mitigate move/remove symlink attack
A malicious user could replace a scan target's directory with a symlink
to another path to trick clamscan, clamdscan, or clamonacc into removing
or moving a different file (eg. a critical system file). The issue would
affect users that use the `--move` or `--remove` options for clamscan,
clamdscan, and clamonacc.
This patch gets the real path for the scan target before the scan,
and if the file alerts and the --move or --remove quarantine features
are used, it mitigates the symlink attack by traversing the path one
directory at a time until reaching the leaf directory where the scan
target file resides before unlinking (or renaming) the file directly.
This commit applies a similar tactic used in the previous commit for
Windows builds, using the Win32 Native API to traverse a path and delete
or move files by handle rather than by file path.
I had some trouble using SetFileInformationByHandle to rename a file by
handle, so for Windows instead it will copy the file to the new location
and then use the safe unlink technique to remove the old file. If the
symlink attack occurs, the unlink will fail, and the system will not be
damaged.
For more information about AV quarantine attacks using links, see the
[RACK911 Lab's report](https://www.rack911labs.com/research/exploiting-almost-every-antivirus-software)
2020-07-01 22:21:40 -07:00
|
|
|
cl_error_t ret = CL_SUCCESS;
|
2025-06-16 14:23:45 -04:00
|
|
|
int fd = -1;
|
|
|
|
int included = 0;
|
2014-09-11 14:15:36 -04:00
|
|
|
unsigned i;
|
|
|
|
const struct optstruct *opt;
|
2025-07-27 22:47:29 -04:00
|
|
|
cl_verdict_t verdict = CL_VERDICT_NOTHING_FOUND;
|
|
|
|
const char *alert_name = NULL;
|
2014-09-11 14:15:36 -04:00
|
|
|
STATBUF sb;
|
2025-06-16 14:23:45 -04:00
|
|
|
struct metachain chain = {0};
|
|
|
|
struct clamscan_cb_data data = {0};
|
|
|
|
const char *hash_hint = NULL;
|
|
|
|
char **hash_out = NULL;
|
|
|
|
char *hash = NULL;
|
|
|
|
const char *hash_alg = NULL;
|
|
|
|
const char *file_type_hint = NULL;
|
|
|
|
char **file_type_out = NULL;
|
|
|
|
char *file_type = NULL;
|
2008-06-10 16:59:19 +00:00
|
|
|
|
clamd clients: Mitigate move/remove symlink attack
A malicious user could replace a scan target's directory with a symlink
to another path to trick clamscan, clamdscan, or clamonacc into removing
or moving a different file (eg. a critical system file). The issue would
affect users that use the `--move` or `--remove` options for clamscan,
clamdscan, and clamonacc.
This patch gets the real path for the scan target before the scan,
and if the file alerts and the --move or --remove quarantine features
are used, it mitigates the symlink attack by traversing the path one
directory at a time until reaching the leaf directory where the scan
target file resides before unlinking (or renaming) the file directly.
This commit applies a similar tactic used in the previous commit for
Windows builds, using the Win32 Native API to traverse a path and delete
or move files by handle rather than by file path.
I had some trouble using SetFileInformationByHandle to rename a file by
handle, so for Windows instead it will copy the file to the new location
and then use the safe unlink technique to remove the old file. If the
symlink attack occurs, the unlink will fail, and the system will not be
damaged.
For more information about AV quarantine attacks using links, see the
[RACK911 Lab's report](https://www.rack911labs.com/research/exploiting-almost-every-antivirus-software)
2020-07-01 22:21:40 -07:00
|
|
|
char *real_filename = NULL;
|
|
|
|
|
|
|
|
if (NULL == filename || NULL == engine || NULL == opts || NULL == options) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_INFO, "scanfile: Invalid args.\n");
|
clamd clients: Mitigate move/remove symlink attack
A malicious user could replace a scan target's directory with a symlink
to another path to trick clamscan, clamdscan, or clamonacc into removing
or moving a different file (eg. a critical system file). The issue would
affect users that use the `--move` or `--remove` options for clamscan,
clamdscan, and clamonacc.
This patch gets the real path for the scan target before the scan,
and if the file alerts and the --move or --remove quarantine features
are used, it mitigates the symlink attack by traversing the path one
directory at a time until reaching the leaf directory where the scan
target file resides before unlinking (or renaming) the file directly.
This commit applies a similar tactic used in the previous commit for
Windows builds, using the Win32 Native API to traverse a path and delete
or move files by handle rather than by file path.
I had some trouble using SetFileInformationByHandle to rename a file by
handle, so for Windows instead it will copy the file to the new location
and then use the safe unlink technique to remove the old file. If the
symlink attack occurs, the unlink will fail, and the system will not be
damaged.
For more information about AV quarantine attacks using links, see the
[RACK911 Lab's report](https://www.rack911labs.com/research/exploiting-almost-every-antivirus-software)
2020-07-01 22:21:40 -07:00
|
|
|
ret = CL_EARG;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2020-07-24 08:32:47 -07:00
|
|
|
ret = cli_realpath((const char *)filename, &real_filename);
|
clamd clients: Mitigate move/remove symlink attack
A malicious user could replace a scan target's directory with a symlink
to another path to trick clamscan, clamdscan, or clamonacc into removing
or moving a different file (eg. a critical system file). The issue would
affect users that use the `--move` or `--remove` options for clamscan,
clamdscan, and clamonacc.
This patch gets the real path for the scan target before the scan,
and if the file alerts and the --move or --remove quarantine features
are used, it mitigates the symlink attack by traversing the path one
directory at a time until reaching the leaf directory where the scan
target file resides before unlinking (or renaming) the file directly.
This commit applies a similar tactic used in the previous commit for
Windows builds, using the Win32 Native API to traverse a path and delete
or move files by handle rather than by file path.
I had some trouble using SetFileInformationByHandle to rename a file by
handle, so for Windows instead it will copy the file to the new location
and then use the safe unlink technique to remove the old file. If the
symlink attack occurs, the unlink will fail, and the system will not be
damaged.
For more information about AV quarantine attacks using links, see the
[RACK911 Lab's report](https://www.rack911labs.com/research/exploiting-almost-every-antivirus-software)
2020-07-01 22:21:40 -07:00
|
|
|
if (CL_SUCCESS != ret) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_DEBUG, "Failed to determine real filename of %s.\n", filename);
|
|
|
|
logg(LOGG_DEBUG, "Quarantine of the file may fail if file path contains symlinks.\n");
|
2021-01-12 12:01:27 -08:00
|
|
|
} else {
|
|
|
|
filename = real_filename;
|
clamd clients: Mitigate move/remove symlink attack
A malicious user could replace a scan target's directory with a symlink
to another path to trick clamscan, clamdscan, or clamonacc into removing
or moving a different file (eg. a critical system file). The issue would
affect users that use the `--move` or `--remove` options for clamscan,
clamdscan, and clamonacc.
This patch gets the real path for the scan target before the scan,
and if the file alerts and the --move or --remove quarantine features
are used, it mitigates the symlink attack by traversing the path one
directory at a time until reaching the leaf directory where the scan
target file resides before unlinking (or renaming) the file directly.
This commit applies a similar tactic used in the previous commit for
Windows builds, using the Win32 Native API to traverse a path and delete
or move files by handle rather than by file path.
I had some trouble using SetFileInformationByHandle to rename a file by
handle, so for Windows instead it will copy the file to the new location
and then use the safe unlink technique to remove the old file. If the
symlink attack occurs, the unlink will fail, and the system will not be
damaged.
For more information about AV quarantine attacks using links, see the
[RACK911 Lab's report](https://www.rack911labs.com/research/exploiting-almost-every-antivirus-software)
2020-07-01 22:21:40 -07:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "exclude"))->enabled) {
|
|
|
|
while (opt) {
|
|
|
|
if (match_regex(filename, opt->strarg) == 1) {
|
|
|
|
if (!printinfected)
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_INFO, "%s: Excluded\n", filename);
|
2014-09-11 14:15:36 -04:00
|
|
|
|
clamd clients: Mitigate move/remove symlink attack
A malicious user could replace a scan target's directory with a symlink
to another path to trick clamscan, clamdscan, or clamonacc into removing
or moving a different file (eg. a critical system file). The issue would
affect users that use the `--move` or `--remove` options for clamscan,
clamdscan, and clamonacc.
This patch gets the real path for the scan target before the scan,
and if the file alerts and the --move or --remove quarantine features
are used, it mitigates the symlink attack by traversing the path one
directory at a time until reaching the leaf directory where the scan
target file resides before unlinking (or renaming) the file directly.
This commit applies a similar tactic used in the previous commit for
Windows builds, using the Win32 Native API to traverse a path and delete
or move files by handle rather than by file path.
I had some trouble using SetFileInformationByHandle to rename a file by
handle, so for Windows instead it will copy the file to the new location
and then use the safe unlink technique to remove the old file. If the
symlink attack occurs, the unlink will fail, and the system will not be
damaged.
For more information about AV quarantine attacks using links, see the
[RACK911 Lab's report](https://www.rack911labs.com/research/exploiting-almost-every-antivirus-software)
2020-07-01 22:21:40 -07:00
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
opt = opt->nextarg;
|
|
|
|
}
|
2008-06-10 16:59:19 +00:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "include"))->enabled) {
|
2014-09-11 14:15:36 -04:00
|
|
|
included = 0;
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
while (opt) {
|
|
|
|
if (match_regex(filename, opt->strarg) == 1) {
|
2014-09-11 14:15:36 -04:00
|
|
|
included = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
opt = opt->nextarg;
|
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (!included) {
|
|
|
|
if (!printinfected)
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_INFO, "%s: Excluded\n", filename);
|
2014-09-11 14:15:36 -04:00
|
|
|
|
clamd clients: Mitigate move/remove symlink attack
A malicious user could replace a scan target's directory with a symlink
to another path to trick clamscan, clamdscan, or clamonacc into removing
or moving a different file (eg. a critical system file). The issue would
affect users that use the `--move` or `--remove` options for clamscan,
clamdscan, and clamonacc.
This patch gets the real path for the scan target before the scan,
and if the file alerts and the --move or --remove quarantine features
are used, it mitigates the symlink attack by traversing the path one
directory at a time until reaching the leaf directory where the scan
target file resides before unlinking (or renaming) the file directly.
This commit applies a similar tactic used in the previous commit for
Windows builds, using the Win32 Native API to traverse a path and delete
or move files by handle rather than by file path.
I had some trouble using SetFileInformationByHandle to rename a file by
handle, so for Windows instead it will copy the file to the new location
and then use the safe unlink technique to remove the old file. If the
symlink attack occurs, the unlink will fail, and the system will not be
damaged.
For more information about AV quarantine attacks using links, see the
[RACK911 Lab's report](https://www.rack911labs.com/research/exploiting-almost-every-antivirus-software)
2020-07-01 22:21:40 -07:00
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
2008-06-10 16:59:19 +00:00
|
|
|
}
|
|
|
|
|
2010-12-28 18:24:51 +01:00
|
|
|
/* argh, don't scan /proc files */
|
2018-12-03 12:40:13 -05:00
|
|
|
if (CLAMSTAT(filename, &sb) != -1) {
|
2010-12-28 18:24:51 +01:00
|
|
|
#ifdef C_LINUX
|
2018-12-03 12:40:13 -05:00
|
|
|
if (procdev && sb.st_dev == procdev) {
|
|
|
|
if (!printinfected)
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_INFO, "%s: Excluded (/proc)\n", filename);
|
2014-09-11 14:15:36 -04:00
|
|
|
|
clamd clients: Mitigate move/remove symlink attack
A malicious user could replace a scan target's directory with a symlink
to another path to trick clamscan, clamdscan, or clamonacc into removing
or moving a different file (eg. a critical system file). The issue would
affect users that use the `--move` or `--remove` options for clamscan,
clamdscan, and clamonacc.
This patch gets the real path for the scan target before the scan,
and if the file alerts and the --move or --remove quarantine features
are used, it mitigates the symlink attack by traversing the path one
directory at a time until reaching the leaf directory where the scan
target file resides before unlinking (or renaming) the file directly.
This commit applies a similar tactic used in the previous commit for
Windows builds, using the Win32 Native API to traverse a path and delete
or move files by handle rather than by file path.
I had some trouble using SetFileInformationByHandle to rename a file by
handle, so for Windows instead it will copy the file to the new location
and then use the safe unlink technique to remove the old file. If the
symlink attack occurs, the unlink will fail, and the system will not be
damaged.
For more information about AV quarantine attacks using links, see the
[RACK911 Lab's report](https://www.rack911labs.com/research/exploiting-almost-every-antivirus-software)
2020-07-01 22:21:40 -07:00
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
2018-12-03 12:40:13 -05:00
|
|
|
#endif
|
|
|
|
if (!sb.st_size) {
|
|
|
|
if (!printinfected)
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_INFO, "%s: Empty file\n", filename);
|
2014-09-11 14:15:36 -04:00
|
|
|
|
clamd clients: Mitigate move/remove symlink attack
A malicious user could replace a scan target's directory with a symlink
to another path to trick clamscan, clamdscan, or clamonacc into removing
or moving a different file (eg. a critical system file). The issue would
affect users that use the `--move` or `--remove` options for clamscan,
clamdscan, and clamonacc.
This patch gets the real path for the scan target before the scan,
and if the file alerts and the --move or --remove quarantine features
are used, it mitigates the symlink attack by traversing the path one
directory at a time until reaching the leaf directory where the scan
target file resides before unlinking (or renaming) the file directly.
This commit applies a similar tactic used in the previous commit for
Windows builds, using the Win32 Native API to traverse a path and delete
or move files by handle rather than by file path.
I had some trouble using SetFileInformationByHandle to rename a file by
handle, so for Windows instead it will copy the file to the new location
and then use the safe unlink technique to remove the old file. If the
symlink attack occurs, the unlink will fail, and the system will not be
damaged.
For more information about AV quarantine attacks using links, see the
[RACK911 Lab's report](https://www.rack911labs.com/research/exploiting-almost-every-antivirus-software)
2020-07-01 22:21:40 -07:00
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
|
|
|
|
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
|
|
|
info.bytes_read += sb.st_size;
|
2008-06-10 16:59:19 +00:00
|
|
|
}
|
2010-12-28 18:24:51 +01:00
|
|
|
|
2009-09-24 16:08:52 +02:00
|
|
|
#ifndef _WIN32
|
2018-12-03 12:40:13 -05:00
|
|
|
if (geteuid()) {
|
|
|
|
if (checkaccess(filename, NULL, R_OK) != 1) {
|
|
|
|
if (!printinfected)
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_INFO, "%s: Access denied\n", filename);
|
2014-09-11 14:15:36 -04:00
|
|
|
|
|
|
|
info.errors++;
|
clamd clients: Mitigate move/remove symlink attack
A malicious user could replace a scan target's directory with a symlink
to another path to trick clamscan, clamdscan, or clamonacc into removing
or moving a different file (eg. a critical system file). The issue would
affect users that use the `--move` or `--remove` options for clamscan,
clamdscan, and clamonacc.
This patch gets the real path for the scan target before the scan,
and if the file alerts and the --move or --remove quarantine features
are used, it mitigates the symlink attack by traversing the path one
directory at a time until reaching the leaf directory where the scan
target file resides before unlinking (or renaming) the file directly.
This commit applies a similar tactic used in the previous commit for
Windows builds, using the Win32 Native API to traverse a path and delete
or move files by handle rather than by file path.
I had some trouble using SetFileInformationByHandle to rename a file by
handle, so for Windows instead it will copy the file to the new location
and then use the safe unlink technique to remove the old file. If the
symlink attack occurs, the unlink will fail, and the system will not be
damaged.
For more information about AV quarantine attacks using links, see the
[RACK911 Lab's report](https://www.rack911labs.com/research/exploiting-almost-every-antivirus-software)
2020-07-01 22:21:40 -07:00
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
|
|
|
}
|
2008-06-10 16:59:19 +00:00
|
|
|
#endif
|
|
|
|
|
2011-08-22 16:58:48 +03:00
|
|
|
memset(&chain, 0, sizeof(chain));
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "archive-verbose")->enabled) {
|
2014-11-06 14:51:26 -05:00
|
|
|
chain.chains = malloc(sizeof(char **));
|
2014-09-11 14:15:36 -04:00
|
|
|
if (chain.chains) {
|
|
|
|
chain.chains[0] = strdup(filename);
|
2016-03-11 16:02:22 -05:00
|
|
|
if (!chain.chains[0]) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_INFO, "Unable to allocate memory in scanfile()\n");
|
2016-03-11 16:02:22 -05:00
|
|
|
info.errors++;
|
clamd clients: Mitigate move/remove symlink attack
A malicious user could replace a scan target's directory with a symlink
to another path to trick clamscan, clamdscan, or clamonacc into removing
or moving a different file (eg. a critical system file). The issue would
affect users that use the `--move` or `--remove` options for clamscan,
clamdscan, and clamonacc.
This patch gets the real path for the scan target before the scan,
and if the file alerts and the --move or --remove quarantine features
are used, it mitigates the symlink attack by traversing the path one
directory at a time until reaching the leaf directory where the scan
target file resides before unlinking (or renaming) the file directly.
This commit applies a similar tactic used in the previous commit for
Windows builds, using the Win32 Native API to traverse a path and delete
or move files by handle rather than by file path.
I had some trouble using SetFileInformationByHandle to rename a file by
handle, so for Windows instead it will copy the file to the new location
and then use the safe unlink technique to remove the old file. If the
symlink attack occurs, the unlink will fail, and the system will not be
damaged.
For more information about AV quarantine attacks using links, see the
[RACK911 Lab's report](https://www.rack911labs.com/research/exploiting-almost-every-antivirus-software)
2020-07-01 22:21:40 -07:00
|
|
|
goto done;
|
2016-03-11 16:02:22 -05:00
|
|
|
}
|
2014-11-06 14:51:26 -05:00
|
|
|
chain.nchains = 1;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
2011-08-22 16:58:48 +03:00
|
|
|
}
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2025-06-16 14:23:45 -04:00
|
|
|
if ((opt = optget(opts, "hash-alg"))->enabled) {
|
|
|
|
hash_alg = opt->strarg;
|
|
|
|
}
|
|
|
|
if ((opt = optget(opts, "hash-hint"))->enabled) {
|
|
|
|
hash_hint = opt->strarg;
|
|
|
|
}
|
|
|
|
if ((opt = optget(opts, "log-hash"))->enabled) {
|
|
|
|
hash_out = &hash;
|
|
|
|
}
|
|
|
|
if ((opt = optget(opts, "file-type-hint"))->enabled) {
|
|
|
|
file_type_hint = opt->strarg;
|
|
|
|
}
|
|
|
|
if ((opt = optget(opts, "log-file-type"))->enabled) {
|
|
|
|
file_type_out = &file_type;
|
|
|
|
}
|
|
|
|
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_DEBUG, "Scanning %s\n", filename);
|
2008-06-10 16:59:19 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((fd = safe_open(filename, O_RDONLY | O_BINARY)) == -1) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_WARNING, "Can't open file %s: %s\n", filename, strerror(errno));
|
2014-09-11 14:15:36 -04:00
|
|
|
info.errors++;
|
clamd clients: Mitigate move/remove symlink attack
A malicious user could replace a scan target's directory with a symlink
to another path to trick clamscan, clamdscan, or clamonacc into removing
or moving a different file (eg. a critical system file). The issue would
affect users that use the `--move` or `--remove` options for clamscan,
clamdscan, and clamonacc.
This patch gets the real path for the scan target before the scan,
and if the file alerts and the --move or --remove quarantine features
are used, it mitigates the symlink attack by traversing the path one
directory at a time until reaching the leaf directory where the scan
target file resides before unlinking (or renaming) the file directly.
This commit applies a similar tactic used in the previous commit for
Windows builds, using the Win32 Native API to traverse a path and delete
or move files by handle rather than by file path.
I had some trouble using SetFileInformationByHandle to rename a file by
handle, so for Windows instead it will copy the file to the new location
and then use the safe unlink technique to remove the old file. If the
symlink attack occurs, the unlink will fail, and the system will not be
damaged.
For more information about AV quarantine attacks using links, see the
[RACK911 Lab's report](https://www.rack911labs.com/research/exploiting-almost-every-antivirus-software)
2020-07-01 22:21:40 -07:00
|
|
|
goto done;
|
2008-06-10 16:59:19 +00:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
data.chain = &chain;
|
2015-10-01 17:47:37 -04:00
|
|
|
data.filename = filename;
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2025-07-27 22:47:29 -04:00
|
|
|
ret = cl_scandesc_ex(
|
|
|
|
fd,
|
|
|
|
filename,
|
|
|
|
&verdict,
|
|
|
|
&alert_name,
|
|
|
|
&info.bytes_scanned,
|
|
|
|
engine, options,
|
|
|
|
&data,
|
|
|
|
hash_hint,
|
|
|
|
hash_out,
|
|
|
|
hash_alg,
|
|
|
|
file_type_hint,
|
|
|
|
file_type_out);
|
|
|
|
|
|
|
|
switch (verdict) {
|
|
|
|
case CL_VERDICT_NOTHING_FOUND: {
|
|
|
|
if (CL_SUCCESS == ret) {
|
|
|
|
if (!printinfected && printclean) {
|
|
|
|
mprintf(LOGG_INFO, "%s: OK\n", filename);
|
|
|
|
}
|
|
|
|
info.files++;
|
|
|
|
} else {
|
|
|
|
if (!printinfected)
|
|
|
|
logg(LOGG_INFO, "%s: %s ERROR\n", filename, cl_strerror(ret));
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2025-07-27 22:47:29 -04:00
|
|
|
info.errors++;
|
|
|
|
}
|
|
|
|
} break;
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2025-07-27 22:47:29 -04:00
|
|
|
case CL_VERDICT_TRUSTED: {
|
|
|
|
// TODO: Option to print "TRUSTED" verdict instead of "OK"?
|
|
|
|
if (!printinfected && printclean) {
|
|
|
|
mprintf(LOGG_INFO, "%s: OK\n", filename);
|
|
|
|
}
|
|
|
|
info.files++;
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case CL_VERDICT_STRONG_INDICATOR:
|
|
|
|
case CL_VERDICT_POTENTIALLY_UNWANTED: {
|
|
|
|
if (optget(opts, "archive-verbose")->enabled) {
|
|
|
|
if (chain.nchains > 1) {
|
|
|
|
char str[128];
|
|
|
|
int toolong = print_chain(&chain, str, sizeof(str));
|
|
|
|
|
|
|
|
logg(LOGG_INFO, "%s%s!(%llu)%s: %s FOUND\n", str, toolong ? "..." : "", (long long unsigned)(chain.lastvir - 1), chain.chains[chain.nchains - 1], alert_name);
|
|
|
|
} else if (chain.lastvir) {
|
|
|
|
logg(LOGG_INFO, "%s!(%llu): %s FOUND\n", filename, (long long unsigned)(chain.lastvir - 1), alert_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
info.files++;
|
|
|
|
info.ifiles++;
|
|
|
|
|
|
|
|
if (bell) {
|
|
|
|
fprintf(stderr, "\007");
|
|
|
|
}
|
|
|
|
} break;
|
2010-02-04 21:33:03 +01:00
|
|
|
}
|
2008-06-10 16:59:19 +00:00
|
|
|
|
2025-06-16 14:23:45 -04:00
|
|
|
if (NULL != hash) {
|
|
|
|
if (hash_alg == NULL) {
|
|
|
|
// libclamav defaults to sha2-256
|
|
|
|
hash_alg = "sha2-256";
|
|
|
|
}
|
|
|
|
logg(LOGG_INFO, "%s FileHash: %s (%s)\n", filename, hash, hash_alg);
|
|
|
|
}
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2025-06-16 14:23:45 -04:00
|
|
|
if (NULL != file_type) {
|
|
|
|
logg(LOGG_INFO, "%s FileType: %s\n", filename, file_type);
|
|
|
|
}
|
2008-06-10 16:59:19 +00:00
|
|
|
|
2025-06-16 14:23:45 -04:00
|
|
|
done:
|
|
|
|
/*
|
|
|
|
* Run the action callback if the file was infected.
|
|
|
|
*/
|
2025-07-27 22:47:29 -04:00
|
|
|
if (((verdict == CL_VERDICT_STRONG_INDICATOR) || (verdict == CL_VERDICT_POTENTIALLY_UNWANTED)) && action) {
|
2014-09-11 14:15:36 -04:00
|
|
|
action(filename);
|
2025-06-16 14:23:45 -04:00
|
|
|
}
|
clamd clients: Mitigate move/remove symlink attack
A malicious user could replace a scan target's directory with a symlink
to another path to trick clamscan, clamdscan, or clamonacc into removing
or moving a different file (eg. a critical system file). The issue would
affect users that use the `--move` or `--remove` options for clamscan,
clamdscan, and clamonacc.
This patch gets the real path for the scan target before the scan,
and if the file alerts and the --move or --remove quarantine features
are used, it mitigates the symlink attack by traversing the path one
directory at a time until reaching the leaf directory where the scan
target file resides before unlinking (or renaming) the file directly.
This commit applies a similar tactic used in the previous commit for
Windows builds, using the Win32 Native API to traverse a path and delete
or move files by handle rather than by file path.
I had some trouble using SetFileInformationByHandle to rename a file by
handle, so for Windows instead it will copy the file to the new location
and then use the safe unlink technique to remove the old file. If the
symlink attack occurs, the unlink will fail, and the system will not be
damaged.
For more information about AV quarantine attacks using links, see the
[RACK911 Lab's report](https://www.rack911labs.com/research/exploiting-almost-every-antivirus-software)
2020-07-01 22:21:40 -07:00
|
|
|
|
2025-06-16 14:23:45 -04:00
|
|
|
if (NULL != hash) {
|
|
|
|
free(hash);
|
|
|
|
}
|
|
|
|
if (NULL != file_type) {
|
|
|
|
free(file_type);
|
|
|
|
}
|
|
|
|
if (NULL != chain.chains) {
|
|
|
|
for (i = 0; i < chain.nchains; i++) {
|
|
|
|
free(chain.chains[i]);
|
|
|
|
}
|
|
|
|
free(chain.chains);
|
|
|
|
}
|
|
|
|
if (fd != -1) {
|
|
|
|
close(fd);
|
|
|
|
}
|
clamd clients: Mitigate move/remove symlink attack
A malicious user could replace a scan target's directory with a symlink
to another path to trick clamscan, clamdscan, or clamonacc into removing
or moving a different file (eg. a critical system file). The issue would
affect users that use the `--move` or `--remove` options for clamscan,
clamdscan, and clamonacc.
This patch gets the real path for the scan target before the scan,
and if the file alerts and the --move or --remove quarantine features
are used, it mitigates the symlink attack by traversing the path one
directory at a time until reaching the leaf directory where the scan
target file resides before unlinking (or renaming) the file directly.
This commit applies a similar tactic used in the previous commit for
Windows builds, using the Win32 Native API to traverse a path and delete
or move files by handle rather than by file path.
I had some trouble using SetFileInformationByHandle to rename a file by
handle, so for Windows instead it will copy the file to the new location
and then use the safe unlink technique to remove the old file. If the
symlink attack occurs, the unlink will fail, and the system will not be
damaged.
For more information about AV quarantine attacks using links, see the
[RACK911 Lab's report](https://www.rack911labs.com/research/exploiting-almost-every-antivirus-software)
2020-07-01 22:21:40 -07:00
|
|
|
if (NULL != real_filename) {
|
|
|
|
free(real_filename);
|
|
|
|
}
|
2025-06-16 14:23:45 -04:00
|
|
|
|
clamd clients: Mitigate move/remove symlink attack
A malicious user could replace a scan target's directory with a symlink
to another path to trick clamscan, clamdscan, or clamonacc into removing
or moving a different file (eg. a critical system file). The issue would
affect users that use the `--move` or `--remove` options for clamscan,
clamdscan, and clamonacc.
This patch gets the real path for the scan target before the scan,
and if the file alerts and the --move or --remove quarantine features
are used, it mitigates the symlink attack by traversing the path one
directory at a time until reaching the leaf directory where the scan
target file resides before unlinking (or renaming) the file directly.
This commit applies a similar tactic used in the previous commit for
Windows builds, using the Win32 Native API to traverse a path and delete
or move files by handle rather than by file path.
I had some trouble using SetFileInformationByHandle to rename a file by
handle, so for Windows instead it will copy the file to the new location
and then use the safe unlink technique to remove the old file. If the
symlink attack occurs, the unlink will fail, and the system will not be
damaged.
For more information about AV quarantine attacks using links, see the
[RACK911 Lab's report](https://www.rack911labs.com/research/exploiting-almost-every-antivirus-software)
2020-07-01 22:21:40 -07:00
|
|
|
return;
|
2008-06-10 16:59:19 +00:00
|
|
|
}
|
|
|
|
|
2018-07-20 22:28:48 -04:00
|
|
|
static void scandirs(const char *dirname, struct cl_engine *engine, const struct optstruct *opts, struct cl_scan_options *options, unsigned int depth, dev_t dev)
|
2008-06-10 16:59:19 +00:00
|
|
|
{
|
2014-09-11 14:15:36 -04:00
|
|
|
DIR *dd;
|
|
|
|
struct dirent *dent;
|
|
|
|
STATBUF sb;
|
|
|
|
char *fname;
|
|
|
|
int included;
|
|
|
|
const struct optstruct *opt;
|
|
|
|
unsigned int dirlnk, filelnk;
|
2008-06-10 16:59:19 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "exclude-dir"))->enabled) {
|
|
|
|
while (opt) {
|
|
|
|
if (match_regex(dirname, opt->strarg) == 1) {
|
|
|
|
if (!printinfected)
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_INFO, "%s: Excluded\n", dirname);
|
2014-09-11 14:15:36 -04:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
opt = opt->nextarg;
|
|
|
|
}
|
2008-06-10 16:59:19 +00:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "include-dir"))->enabled) {
|
2014-09-11 14:15:36 -04:00
|
|
|
included = 0;
|
2018-12-03 12:40:13 -05:00
|
|
|
while (opt) {
|
|
|
|
if (match_regex(dirname, opt->strarg) == 1) {
|
2014-09-11 14:15:36 -04:00
|
|
|
included = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
opt = opt->nextarg;
|
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (!included) {
|
|
|
|
if (!printinfected)
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_INFO, "%s: Excluded\n", dirname);
|
2014-09-11 14:15:36 -04:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2008-06-10 16:59:19 +00:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (depth > (unsigned int)optget(opts, "max-dir-recursion")->numarg)
|
2014-09-11 14:15:36 -04:00
|
|
|
return;
|
2008-06-10 16:59:19 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
dirlnk = optget(opts, "follow-dir-symlinks")->numarg;
|
2010-12-28 18:24:51 +01:00
|
|
|
filelnk = optget(opts, "follow-file-symlinks")->numarg;
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((dd = opendir(dirname)) != NULL) {
|
2014-09-11 14:15:36 -04:00
|
|
|
info.dirs++;
|
|
|
|
depth++;
|
2018-12-03 12:40:13 -05:00
|
|
|
while ((dent = readdir(dd))) {
|
|
|
|
if (dent->d_ino) {
|
|
|
|
if (strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")) {
|
2014-09-11 14:15:36 -04:00
|
|
|
/* build the full name */
|
|
|
|
fname = malloc(strlen(dirname) + strlen(dent->d_name) + 2);
|
|
|
|
if (fname == NULL) { /* oops, malloc() failed, print warning and return */
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "scandirs: Memory allocation failed for fname\n");
|
2014-09-11 14:15:36 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (!strcmp(dirname, PATHSEP))
|
|
|
|
sprintf(fname, PATHSEP "%s", dent->d_name);
|
2014-09-11 14:15:36 -04:00
|
|
|
else
|
2018-12-03 12:40:13 -05:00
|
|
|
sprintf(fname, "%s" PATHSEP "%s", dirname, dent->d_name);
|
2014-09-11 14:15:36 -04:00
|
|
|
|
|
|
|
/* stat the file */
|
2018-12-03 12:40:13 -05:00
|
|
|
if (LSTAT(fname, &sb) != -1) {
|
|
|
|
if (!optget(opts, "cross-fs")->enabled) {
|
|
|
|
if (sb.st_dev != dev) {
|
|
|
|
if (!printinfected)
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_INFO, "%s: Excluded\n", fname);
|
2014-09-11 14:15:36 -04:00
|
|
|
|
|
|
|
free(fname);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2018-12-03 12:40:13 -05:00
|
|
|
if (S_ISLNK(sb.st_mode)) {
|
|
|
|
if (dirlnk != 2 && filelnk != 2) {
|
|
|
|
if (!printinfected)
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_INFO, "%s: Symbolic link\n", fname);
|
2018-12-03 12:40:13 -05:00
|
|
|
} else if (CLAMSTAT(fname, &sb) != -1) {
|
|
|
|
if (S_ISREG(sb.st_mode) && filelnk == 2) {
|
2014-09-11 14:15:36 -04:00
|
|
|
scanfile(fname, engine, opts, options);
|
2018-12-03 12:40:13 -05:00
|
|
|
} else if (S_ISDIR(sb.st_mode) && dirlnk == 2) {
|
|
|
|
if (recursion)
|
2014-09-11 14:15:36 -04:00
|
|
|
scandirs(fname, engine, opts, options, depth, dev);
|
|
|
|
} else {
|
2018-12-03 12:40:13 -05:00
|
|
|
if (!printinfected)
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_INFO, "%s: Symbolic link\n", fname);
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
|
|
|
}
|
2018-12-03 12:40:13 -05:00
|
|
|
} else if (S_ISREG(sb.st_mode)) {
|
2014-09-11 14:15:36 -04:00
|
|
|
scanfile(fname, engine, opts, options);
|
2018-12-03 12:40:13 -05:00
|
|
|
} else if (S_ISDIR(sb.st_mode) && recursion) {
|
2014-09-11 14:15:36 -04:00
|
|
|
scandirs(fname, engine, opts, options, depth, dev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(fname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
closedir(dd);
|
2008-06-10 16:59:19 +00:00
|
|
|
} else {
|
2018-12-03 12:40:13 -05:00
|
|
|
if (!printinfected)
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_INFO, "%s: Can't open directory.\n", dirname);
|
2014-09-11 14:15:36 -04:00
|
|
|
|
|
|
|
info.errors++;
|
2008-06-10 16:59:19 +00:00
|
|
|
}
|
2007-01-30 21:11:32 +00:00
|
|
|
}
|
|
|
|
|
2025-06-16 14:23:45 -04:00
|
|
|
static int scanstdin(const struct cl_engine *engine, const struct optstruct *opts, struct cl_scan_options *options)
|
2007-01-30 21:11:32 +00:00
|
|
|
{
|
2025-06-16 14:23:45 -04:00
|
|
|
cl_error_t ret;
|
|
|
|
|
2025-07-27 22:47:29 -04:00
|
|
|
size_t fsize = 0;
|
|
|
|
cl_verdict_t verdict = CL_VERDICT_NOTHING_FOUND;
|
|
|
|
const char *alert_name = NULL;
|
|
|
|
const char *tmpdir = NULL;
|
2025-06-16 14:23:45 -04:00
|
|
|
char *filename, buff[FILEBUFF];
|
2014-09-11 14:15:36 -04:00
|
|
|
size_t bread;
|
|
|
|
FILE *fs;
|
2015-10-01 17:47:37 -04:00
|
|
|
struct clamscan_cb_data data;
|
2025-06-16 14:23:45 -04:00
|
|
|
const struct optstruct *opt;
|
|
|
|
const char *hash_hint = NULL;
|
|
|
|
char **hash_out = NULL;
|
|
|
|
char *hash = NULL;
|
|
|
|
const char *hash_alg = NULL;
|
|
|
|
const char *file_type_hint = NULL;
|
|
|
|
char **file_type_out = NULL;
|
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
|
|
|
char *file_type = NULL;
|
2007-01-30 21:11:32 +00:00
|
|
|
|
2019-05-23 22:50:04 -04:00
|
|
|
tmpdir = cl_engine_get_str(engine, CL_ENGINE_TMPDIR, NULL);
|
|
|
|
if (NULL == tmpdir) {
|
2014-09-11 14:15:36 -04:00
|
|
|
tmpdir = cli_gettmpdir();
|
|
|
|
}
|
2007-01-30 21:11:32 +00:00
|
|
|
|
2019-06-26 14:51:40 -07:00
|
|
|
if (access(tmpdir, R_OK | W_OK) == -1) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "Can't write to temporary directory\n");
|
2014-09-11 14:15:36 -04:00
|
|
|
return 2;
|
2007-01-30 21:11:32 +00:00
|
|
|
}
|
|
|
|
|
2025-06-16 14:23:45 -04:00
|
|
|
if (!(filename = cli_gentemp(tmpdir))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "Can't generate tempfile name\n");
|
2014-09-11 14:15:36 -04:00
|
|
|
return 2;
|
2012-07-05 12:45:07 -04:00
|
|
|
}
|
2007-01-30 21:11:32 +00:00
|
|
|
|
2025-06-16 14:23:45 -04:00
|
|
|
if (!(fs = fopen(filename, "wb"))) {
|
|
|
|
logg(LOGG_ERROR, "Can't open %s for writing\n", filename);
|
|
|
|
free(filename);
|
2014-09-11 14:15:36 -04:00
|
|
|
return 2;
|
2007-01-30 21:11:32 +00:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
while ((bread = fread(buff, 1, FILEBUFF, stdin))) {
|
2014-09-11 14:15:36 -04:00
|
|
|
fsize += bread;
|
2018-12-03 12:40:13 -05:00
|
|
|
if (fwrite(buff, 1, bread, fs) < bread) {
|
2025-06-16 14:23:45 -04:00
|
|
|
logg(LOGG_ERROR, "Can't write to %s\n", filename);
|
|
|
|
free(filename);
|
2014-09-11 14:15:36 -04:00
|
|
|
fclose(fs);
|
|
|
|
return 2;
|
|
|
|
}
|
2009-03-07 18:16:20 +00:00
|
|
|
}
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2007-01-30 21:11:32 +00:00
|
|
|
fclose(fs);
|
|
|
|
|
2025-06-16 14:23:45 -04:00
|
|
|
if ((opt = optget(opts, "hash-alg"))->enabled) {
|
|
|
|
hash_alg = opt->strarg;
|
|
|
|
}
|
|
|
|
if ((opt = optget(opts, "hash-hint"))->enabled) {
|
|
|
|
hash_hint = opt->strarg;
|
|
|
|
}
|
|
|
|
if ((opt = optget(opts, "log-hash"))->enabled) {
|
|
|
|
hash_out = &hash;
|
|
|
|
}
|
|
|
|
if ((opt = optget(opts, "file-type-hint"))->enabled) {
|
|
|
|
file_type_hint = opt->strarg;
|
|
|
|
}
|
|
|
|
if ((opt = optget(opts, "log-file-type"))->enabled) {
|
|
|
|
file_type_out = &file_type;
|
|
|
|
}
|
|
|
|
|
|
|
|
logg(LOGG_DEBUG, "Scanning %s\n", filename);
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2007-01-30 21:11:32 +00:00
|
|
|
info.files++;
|
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
|
|
|
info.bytes_read += fsize;
|
2007-01-30 21:11:32 +00:00
|
|
|
|
2015-10-01 17:47:37 -04:00
|
|
|
data.filename = "stdin";
|
2018-12-03 12:40:13 -05:00
|
|
|
data.chain = NULL;
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2025-07-27 22:47:29 -04:00
|
|
|
ret = cl_scanfile_ex(
|
|
|
|
filename,
|
|
|
|
&verdict,
|
|
|
|
&alert_name,
|
|
|
|
&info.bytes_scanned,
|
|
|
|
engine,
|
|
|
|
options,
|
|
|
|
&data,
|
|
|
|
hash_hint,
|
|
|
|
hash_out,
|
|
|
|
hash_alg,
|
|
|
|
file_type_hint,
|
|
|
|
file_type_out);
|
|
|
|
|
|
|
|
switch (verdict) {
|
|
|
|
case CL_VERDICT_NOTHING_FOUND: {
|
|
|
|
if (CL_SUCCESS == ret) {
|
|
|
|
if (!printinfected) {
|
|
|
|
mprintf(LOGG_INFO, "stdin: OK\n");
|
|
|
|
}
|
|
|
|
info.files++;
|
|
|
|
} else {
|
|
|
|
if (!printinfected) {
|
|
|
|
logg(LOGG_INFO, "stdin: %s ERROR\n", cl_strerror(ret));
|
|
|
|
}
|
|
|
|
info.errors++;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case CL_VERDICT_TRUSTED: {
|
|
|
|
// TODO: Option to print "TRUSTED" verdict instead of "OK"?
|
|
|
|
if (!printinfected) {
|
|
|
|
mprintf(LOGG_INFO, "stdin: OK\n");
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
|
|
|
|
case CL_VERDICT_STRONG_INDICATOR:
|
|
|
|
case CL_VERDICT_POTENTIALLY_UNWANTED: {
|
|
|
|
info.ifiles++;
|
|
|
|
|
|
|
|
if (bell) {
|
|
|
|
fprintf(stderr, "\007");
|
|
|
|
}
|
|
|
|
} break;
|
2010-10-08 15:39:55 +02:00
|
|
|
}
|
2007-01-30 21:11:32 +00:00
|
|
|
|
2025-06-16 14:23:45 -04:00
|
|
|
if (NULL != hash) {
|
|
|
|
if (hash_alg == NULL) {
|
|
|
|
// libclamav defaults to sha2-256
|
|
|
|
hash_alg = "sha2-256";
|
|
|
|
}
|
|
|
|
logg(LOGG_INFO, "%s FileHash: %s (%s)\n", filename, hash, hash_alg);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NULL != file_type) {
|
|
|
|
logg(LOGG_INFO, "%s FileType: %s\n", filename, file_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NULL != hash) {
|
|
|
|
free(hash);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NULL != file_type) {
|
|
|
|
free(file_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
unlink(filename);
|
|
|
|
free(filename);
|
2007-01-30 21:11:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2004-07-19 17:54:40 +00:00
|
|
|
|
2021-07-16 11:47:23 -07:00
|
|
|
struct sigload_progress {
|
|
|
|
time_t startTime;
|
|
|
|
time_t lastRunTime;
|
|
|
|
uint8_t bComplete;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct engine_compile_progress {
|
|
|
|
time_t startTime;
|
|
|
|
time_t lastRunTime;
|
|
|
|
uint8_t bComplete;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct engine_free_progress {
|
|
|
|
time_t startTime;
|
|
|
|
time_t lastRunTime;
|
|
|
|
uint8_t bComplete;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void print_time(time_t seconds)
|
|
|
|
{
|
|
|
|
if (seconds >= 3600) {
|
|
|
|
fprintf(stdout, "%2lldh %02lldm", (long long)seconds / 3600, ((long long)seconds % 3600) / 60);
|
|
|
|
} else if (seconds >= 60) {
|
|
|
|
fprintf(stdout, "%2lldm %02llds", (long long)seconds / 60, (long long)seconds % 60);
|
|
|
|
} else {
|
|
|
|
fprintf(stdout, "%3llds", (long long)seconds);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print_num_sigs(size_t sigs, int bPad)
|
|
|
|
{
|
|
|
|
if (sigs >= (1000 * 1000)) {
|
|
|
|
const char *format = bPad ? "%7.02fM" : "%.02fM";
|
|
|
|
double megasigs = sigs / (double)(1000 * 1000);
|
|
|
|
fprintf(stdout, format, megasigs);
|
|
|
|
} else if (sigs >= 1000) {
|
|
|
|
const char *format = bPad ? "%7.02fK" : "%.02fK";
|
|
|
|
double kilosigs = sigs / (double)(1000);
|
|
|
|
fprintf(stdout, format, kilosigs);
|
|
|
|
} else {
|
|
|
|
const char *format = bPad ? "%8zu" : "%zu";
|
|
|
|
fprintf(stdout, format, sigs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Progress callback for sig-load
|
|
|
|
*
|
|
|
|
* @param total_items Total number of items
|
|
|
|
* @param now_completed Number of items completed
|
|
|
|
* @param context Opaque application provided data; This maps to sigload_progress
|
|
|
|
*/
|
|
|
|
static cl_error_t sigload_callback(size_t total_items, size_t now_completed, void *context)
|
|
|
|
{
|
|
|
|
time_t curtime = 0;
|
|
|
|
time_t remtime = 0;
|
|
|
|
|
|
|
|
struct sigload_progress *sigloadProgress = (struct sigload_progress *)context;
|
|
|
|
|
|
|
|
uint32_t i = 0;
|
|
|
|
uint32_t totalNumDots = 25;
|
|
|
|
uint32_t numDots = 0;
|
|
|
|
double fraction_loaded = 0.0;
|
|
|
|
|
|
|
|
if ((total_items <= 0) || (sigloadProgress->bComplete)) {
|
2021-07-16 18:53:14 -07:00
|
|
|
return CL_SUCCESS;
|
2021-07-16 11:47:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fraction_loaded = (double)now_completed / (double)total_items;
|
|
|
|
numDots = round(fraction_loaded * totalNumDots);
|
|
|
|
|
|
|
|
if (0 == sigloadProgress->startTime) {
|
|
|
|
sigloadProgress->startTime = time(0);
|
|
|
|
}
|
|
|
|
curtime = time(0) - sigloadProgress->startTime;
|
|
|
|
|
|
|
|
sigloadProgress->lastRunTime = curtime;
|
|
|
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
fprintf(stdout, "\e[?7l");
|
|
|
|
#endif
|
|
|
|
if (fraction_loaded <= 0.0) {
|
|
|
|
fprintf(stdout, "Loading: ");
|
|
|
|
print_time(curtime);
|
|
|
|
fprintf(stdout, " ");
|
|
|
|
} else {
|
|
|
|
remtime = (curtime / fraction_loaded) - curtime;
|
|
|
|
fprintf(stdout, "Loading: ");
|
|
|
|
print_time(curtime);
|
|
|
|
fprintf(stdout, ", ETA: ");
|
|
|
|
print_time(remtime);
|
|
|
|
fprintf(stdout, " ");
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stdout, "[");
|
|
|
|
if (numDots > 0) {
|
|
|
|
if (numDots > 1) {
|
|
|
|
for (i = 0; i < numDots - 1; i++) {
|
|
|
|
fprintf(stdout, "=");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fprintf(stdout, ">");
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
for (; i < totalNumDots; i++) {
|
|
|
|
fprintf(stdout, " ");
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stdout, "] ");
|
|
|
|
|
|
|
|
print_num_sigs(now_completed, 1);
|
|
|
|
fprintf(stdout, "/");
|
|
|
|
print_num_sigs(total_items, 0);
|
|
|
|
fprintf(stdout, " sigs ");
|
|
|
|
|
|
|
|
if (now_completed < total_items) {
|
|
|
|
fprintf(stdout, "\r");
|
|
|
|
} else {
|
|
|
|
fprintf(stdout, "\n");
|
|
|
|
sigloadProgress->bComplete = 1;
|
|
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
|
|
fprintf(stdout, "\e[?7h");
|
|
|
|
#endif
|
|
|
|
fflush(stdout);
|
|
|
|
|
|
|
|
return CL_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Progress callback for sig-load
|
|
|
|
*
|
|
|
|
* @param total_items Total number of items
|
|
|
|
* @param now_completed Number of items completed
|
|
|
|
* @param context Opaque application provided data; This maps to engine_compile_progress
|
|
|
|
*/
|
|
|
|
static cl_error_t engine_compile_callback(size_t total_items, size_t now_completed, void *context)
|
|
|
|
{
|
|
|
|
time_t curtime = 0;
|
|
|
|
time_t remtime = 0;
|
|
|
|
|
|
|
|
struct engine_compile_progress *engineCompileProgress = (struct engine_compile_progress *)context;
|
|
|
|
|
|
|
|
uint32_t i = 0;
|
|
|
|
uint32_t totalNumDots = 25;
|
|
|
|
uint32_t numDots = 0;
|
|
|
|
double fraction_compiled = 0.0;
|
|
|
|
|
|
|
|
if ((total_items <= 0) || (engineCompileProgress->bComplete)) {
|
2021-07-16 18:53:14 -07:00
|
|
|
return CL_SUCCESS;
|
2021-07-16 11:47:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fraction_compiled = (double)now_completed / (double)total_items;
|
|
|
|
numDots = round(fraction_compiled * totalNumDots);
|
|
|
|
|
|
|
|
if (0 == engineCompileProgress->startTime) {
|
|
|
|
engineCompileProgress->startTime = time(0);
|
|
|
|
}
|
|
|
|
curtime = time(0) - engineCompileProgress->startTime;
|
|
|
|
|
|
|
|
engineCompileProgress->lastRunTime = curtime;
|
|
|
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
fprintf(stdout, "\e[?7l");
|
|
|
|
#endif
|
|
|
|
if (fraction_compiled <= 0.0) {
|
|
|
|
fprintf(stdout, "Compiling: ");
|
|
|
|
print_time(curtime);
|
|
|
|
fprintf(stdout, " ");
|
|
|
|
} else {
|
|
|
|
remtime = (curtime / fraction_compiled) - curtime;
|
|
|
|
fprintf(stdout, "Compiling: ");
|
|
|
|
print_time(curtime);
|
|
|
|
fprintf(stdout, ", ETA: ");
|
|
|
|
print_time(remtime);
|
|
|
|
fprintf(stdout, " ");
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stdout, "[");
|
|
|
|
if (numDots > 0) {
|
|
|
|
if (numDots > 1) {
|
|
|
|
for (i = 0; i < numDots - 1; i++) {
|
|
|
|
fprintf(stdout, "=");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fprintf(stdout, ">");
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
for (; i < totalNumDots; i++) {
|
|
|
|
fprintf(stdout, " ");
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stdout, "] ");
|
|
|
|
|
|
|
|
print_num_sigs(now_completed, 1);
|
|
|
|
fprintf(stdout, "/");
|
|
|
|
print_num_sigs(total_items, 0);
|
|
|
|
fprintf(stdout, " tasks ");
|
|
|
|
|
|
|
|
if (now_completed < total_items) {
|
|
|
|
fprintf(stdout, "\r");
|
|
|
|
} else {
|
|
|
|
fprintf(stdout, "\n");
|
|
|
|
engineCompileProgress->bComplete = 1;
|
|
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
|
|
fprintf(stdout, "\e[?7h");
|
|
|
|
#endif
|
|
|
|
fflush(stdout);
|
|
|
|
|
|
|
|
return CL_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef ENABLE_ENGINE_FREE_PROGRESSBAR
|
|
|
|
/**
|
|
|
|
* @brief Progress callback for sig-load
|
|
|
|
*
|
|
|
|
* @param total_items Total number of items
|
|
|
|
* @param now_completed Number of items completed
|
|
|
|
* @param context Opaque application provided data; This maps to engine_free_progress
|
|
|
|
*/
|
|
|
|
static cl_error_t engine_free_callback(size_t total_items, size_t now_completed, void *context)
|
|
|
|
{
|
|
|
|
time_t curtime = 0;
|
|
|
|
|
|
|
|
struct engine_free_progress *engineFreeProgress = (struct engine_free_progress *)context;
|
|
|
|
|
|
|
|
uint32_t i = 0;
|
|
|
|
uint32_t totalNumDots = 10;
|
|
|
|
uint32_t numDots = 0;
|
|
|
|
double fraction_freed = 0.0;
|
|
|
|
|
|
|
|
if ((total_items <= 0) || (engineFreeProgress->bComplete)) {
|
2021-07-16 18:53:14 -07:00
|
|
|
return CL_SUCCESS;
|
2021-07-16 11:47:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fraction_freed = (double)now_completed / (double)total_items;
|
|
|
|
numDots = round(fraction_freed * totalNumDots);
|
|
|
|
|
|
|
|
if (0 == engineFreeProgress->startTime) {
|
|
|
|
engineFreeProgress->startTime = time(0);
|
|
|
|
}
|
|
|
|
curtime = time(0) - engineFreeProgress->startTime;
|
|
|
|
|
|
|
|
engineFreeProgress->lastRunTime = curtime;
|
|
|
|
|
|
|
|
#ifndef _WIN32
|
|
|
|
fprintf(stdout, "\e[?7l");
|
|
|
|
#endif
|
|
|
|
fprintf(stdout, "Unloading");
|
|
|
|
|
|
|
|
if (numDots > 0) {
|
|
|
|
if (numDots > 1) {
|
|
|
|
for (i = 0; i < numDots - 1; i++) {
|
|
|
|
fprintf(stdout, ".");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
for (; i < totalNumDots; i++) {
|
|
|
|
fprintf(stdout, " ");
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stdout, " ");
|
|
|
|
|
|
|
|
print_num_sigs(now_completed, 1);
|
|
|
|
fprintf(stdout, "/");
|
|
|
|
print_num_sigs(total_items, 0);
|
|
|
|
fprintf(stdout, " tasks ");
|
|
|
|
|
|
|
|
if (now_completed < total_items) {
|
|
|
|
fprintf(stdout, "\r");
|
|
|
|
} else {
|
|
|
|
fprintf(stdout, "\n");
|
|
|
|
engineFreeProgress->bComplete = 1;
|
|
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
|
|
fprintf(stdout, "\e[?7h");
|
|
|
|
#endif
|
|
|
|
fflush(stdout);
|
|
|
|
|
|
|
|
return CL_SUCCESS;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
Code cleanup: Refactor to clean up formatting issues
Refactored the clamscan code that determines 'what to scan' in order
to clean up some very messy logic and also to get around a difference in
how vscode and clang-format handle formatting #ifdef blocks in the
middle of an else/if.
In addition to refactoring, there is a slight behavior improvement. With
this change, doing `clamscan blah -` will now scan `blah` and then also
scan `stdin`. You can even do `clamscan - blah` to now scan `stdin` and
then scan `blah`. Before, The `-` had to be the only "filename" argument
in order to scan from stdin.
In addition, added a bunch of extra empty lines or changing multi-line
function calls to single-line function calls in order to get around a
bug in clang-format with these two options do not playing nice together:
- AlignConsecutiveAssignments: true
- AlignAfterOpenBracket: true
AlignAfterOpenBracket is not taking account the spaces inserted by
AlignConsecutiveAssignments, so you end up with stuff like this:
```c
bleeblah = 1;
blah = function(arg1,
arg2,
arg3);
// ^--- these args 4-left from where they should be.
```
VSCode, meanwhile, somehow fixes this whitespace issue so code that is
correctly formatted by VSCode doesn't have this bug, meaning that:
1. The clang-format check in GH Actions fails.
2. We'd all have to stop using format-on-save in VSCode and accept the
bug if we wanted those GH Actions tests to pass.
Adding an empty line before variable assignments from multi-line
function calls evades the buggy behavior.
This commit should resolve the clang-format github action test failures,
for now.
2022-03-10 20:55:13 -08:00
|
|
|
#ifdef _WIN32
|
|
|
|
static int scan_memory(struct cl_engine *engine, const struct optstruct *opts, struct cl_scan_options *options)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
struct mem_info minfo;
|
|
|
|
|
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
|
|
|
minfo.d = 0;
|
|
|
|
minfo.files = info.files;
|
|
|
|
minfo.ifiles = info.ifiles;
|
|
|
|
minfo.bytes_scanned = info.bytes_scanned;
|
|
|
|
minfo.engine = engine;
|
|
|
|
minfo.opts = opts;
|
|
|
|
minfo.options = options;
|
|
|
|
ret = scanmem(&minfo);
|
|
|
|
|
|
|
|
info.files = minfo.files;
|
|
|
|
info.ifiles = minfo.ifiles;
|
|
|
|
info.bytes_scanned = minfo.bytes_scanned;
|
Code cleanup: Refactor to clean up formatting issues
Refactored the clamscan code that determines 'what to scan' in order
to clean up some very messy logic and also to get around a difference in
how vscode and clang-format handle formatting #ifdef blocks in the
middle of an else/if.
In addition to refactoring, there is a slight behavior improvement. With
this change, doing `clamscan blah -` will now scan `blah` and then also
scan `stdin`. You can even do `clamscan - blah` to now scan `stdin` and
then scan `blah`. Before, The `-` had to be the only "filename" argument
in order to scan from stdin.
In addition, added a bunch of extra empty lines or changing multi-line
function calls to single-line function calls in order to get around a
bug in clang-format with these two options do not playing nice together:
- AlignConsecutiveAssignments: true
- AlignAfterOpenBracket: true
AlignAfterOpenBracket is not taking account the spaces inserted by
AlignConsecutiveAssignments, so you end up with stuff like this:
```c
bleeblah = 1;
blah = function(arg1,
arg2,
arg3);
// ^--- these args 4-left from where they should be.
```
VSCode, meanwhile, somehow fixes this whitespace issue so code that is
correctly formatted by VSCode doesn't have this bug, meaning that:
1. The clang-format check in GH Actions fails.
2. We'd all have to stop using format-on-save in VSCode and accept the
bug if we wanted those GH Actions tests to pass.
Adding an empty line before variable assignments from multi-line
function calls evades the buggy behavior.
This commit should resolve the clang-format github action test failures,
for now.
2022-03-10 20:55:13 -08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Scan the files from the --file-list option, or scan the files listed as individual arguments.
|
|
|
|
*
|
|
|
|
* If the user uses both --file-list <LISTFILE> AND one or more files, then clam will only
|
|
|
|
* scan the files listed in the LISTFILE and emit a warning about not scanning the other file parameters.
|
|
|
|
*
|
|
|
|
* @param opts
|
|
|
|
* @param options
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
static int scan_files(struct cl_engine *engine, const struct optstruct *opts, struct cl_scan_options *options,
|
|
|
|
unsigned int dirlnk, unsigned int filelnk)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
const char *filename;
|
|
|
|
char *file;
|
|
|
|
STATBUF sb;
|
|
|
|
|
|
|
|
if (optget(opts, "file-list")->enabled && opts->filename) {
|
|
|
|
logg(LOGG_WARNING, "Only scanning files from --file-list (files passed at cmdline are ignored)\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
/* scan first memory if requested */
|
|
|
|
if (optget(opts, "memory")->enabled) {
|
|
|
|
ret = scan_memory(engine, opts, options);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
while ((filename = filelist(opts, &ret)) && (file = strdup(filename))) {
|
|
|
|
if (!strcmp(file, "-")) {
|
|
|
|
/* scan data from stdin */
|
2025-06-16 14:23:45 -04:00
|
|
|
ret = scanstdin(engine, opts, options);
|
Code cleanup: Refactor to clean up formatting issues
Refactored the clamscan code that determines 'what to scan' in order
to clean up some very messy logic and also to get around a difference in
how vscode and clang-format handle formatting #ifdef blocks in the
middle of an else/if.
In addition to refactoring, there is a slight behavior improvement. With
this change, doing `clamscan blah -` will now scan `blah` and then also
scan `stdin`. You can even do `clamscan - blah` to now scan `stdin` and
then scan `blah`. Before, The `-` had to be the only "filename" argument
in order to scan from stdin.
In addition, added a bunch of extra empty lines or changing multi-line
function calls to single-line function calls in order to get around a
bug in clang-format with these two options do not playing nice together:
- AlignConsecutiveAssignments: true
- AlignAfterOpenBracket: true
AlignAfterOpenBracket is not taking account the spaces inserted by
AlignConsecutiveAssignments, so you end up with stuff like this:
```c
bleeblah = 1;
blah = function(arg1,
arg2,
arg3);
// ^--- these args 4-left from where they should be.
```
VSCode, meanwhile, somehow fixes this whitespace issue so code that is
correctly formatted by VSCode doesn't have this bug, meaning that:
1. The clang-format check in GH Actions fails.
2. We'd all have to stop using format-on-save in VSCode and accept the
bug if we wanted those GH Actions tests to pass.
Adding an empty line before variable assignments from multi-line
function calls evades the buggy behavior.
This commit should resolve the clang-format github action test failures,
for now.
2022-03-10 20:55:13 -08:00
|
|
|
} else if (LSTAT(file, &sb) == -1) {
|
|
|
|
/* Can't access the file */
|
|
|
|
perror(file);
|
|
|
|
logg(LOGG_WARNING, "%s: Can't access file\n", file);
|
|
|
|
ret = 2;
|
|
|
|
} else {
|
|
|
|
/* Can access the file. Now have to identify what type of file it is */
|
|
|
|
int i;
|
|
|
|
for (i = strlen(file) - 1; i > 0; i--) {
|
|
|
|
if (file[i] == *PATHSEP) {
|
|
|
|
file[i] = 0;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (S_ISLNK(sb.st_mode)) {
|
|
|
|
/* found a link */
|
|
|
|
if (dirlnk == 0 && filelnk == 0) {
|
|
|
|
/* don't follow links */
|
|
|
|
if (!printinfected) {
|
|
|
|
logg(LOGG_INFO, "%s: Symbolic link\n", file);
|
|
|
|
}
|
|
|
|
} else if (CLAMSTAT(file, &sb) != -1) {
|
|
|
|
/* maybe follow links */
|
|
|
|
if (S_ISREG(sb.st_mode) && filelnk) {
|
|
|
|
/* follow file links */
|
|
|
|
scanfile(file, engine, opts, options);
|
|
|
|
} else if (S_ISDIR(sb.st_mode) && dirlnk) {
|
|
|
|
/* follow directory links */
|
|
|
|
scandirs(file, engine, opts, options, 1, sb.st_dev);
|
|
|
|
} else {
|
|
|
|
if (!printinfected) {
|
|
|
|
logg(LOGG_INFO, "%s: Symbolic link\n", file);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (S_ISREG(sb.st_mode)) {
|
|
|
|
/* Found a file, scan it. */
|
|
|
|
scanfile(file, engine, opts, options);
|
|
|
|
} else if (S_ISDIR(sb.st_mode)) {
|
|
|
|
/* Found a directory, scan it. */
|
|
|
|
scandirs(file, engine, opts, options, 1, sb.st_dev);
|
|
|
|
} else {
|
|
|
|
logg(LOGG_WARNING, "%s: Not supported file type\n", file);
|
|
|
|
ret = 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-12-30 10:33:43 +00:00
|
|
|
int scanmanager(const struct optstruct *opts)
|
2003-07-29 15:48:06 +00:00
|
|
|
{
|
Code cleanup: Refactor to clean up formatting issues
Refactored the clamscan code that determines 'what to scan' in order
to clean up some very messy logic and also to get around a difference in
how vscode and clang-format handle formatting #ifdef blocks in the
middle of an else/if.
In addition to refactoring, there is a slight behavior improvement. With
this change, doing `clamscan blah -` will now scan `blah` and then also
scan `stdin`. You can even do `clamscan - blah` to now scan `stdin` and
then scan `blah`. Before, The `-` had to be the only "filename" argument
in order to scan from stdin.
In addition, added a bunch of extra empty lines or changing multi-line
function calls to single-line function calls in order to get around a
bug in clang-format with these two options do not playing nice together:
- AlignConsecutiveAssignments: true
- AlignAfterOpenBracket: true
AlignAfterOpenBracket is not taking account the spaces inserted by
AlignConsecutiveAssignments, so you end up with stuff like this:
```c
bleeblah = 1;
blah = function(arg1,
arg2,
arg3);
// ^--- these args 4-left from where they should be.
```
VSCode, meanwhile, somehow fixes this whitespace issue so code that is
correctly formatted by VSCode doesn't have this bug, meaning that:
1. The clang-format check in GH Actions fails.
2. We'd all have to stop using format-on-save in VSCode and accept the
bug if we wanted those GH Actions tests to pass.
Adding an empty line before variable assignments from multi-line
function calls evades the buggy behavior.
This commit should resolve the clang-format github action test failures,
for now.
2022-03-10 20:55:13 -08:00
|
|
|
int ret = 0;
|
|
|
|
int i;
|
2018-07-20 22:28:48 -04:00
|
|
|
struct cl_scan_options options;
|
|
|
|
unsigned int dboptions = 0, dirlnk = 1, filelnk = 1;
|
2021-07-16 11:47:23 -07:00
|
|
|
struct cl_engine *engine = NULL;
|
2014-09-11 14:15:36 -04:00
|
|
|
STATBUF sb;
|
Code cleanup: Refactor to clean up formatting issues
Refactored the clamscan code that determines 'what to scan' in order
to clean up some very messy logic and also to get around a difference in
how vscode and clang-format handle formatting #ifdef blocks in the
middle of an else/if.
In addition to refactoring, there is a slight behavior improvement. With
this change, doing `clamscan blah -` will now scan `blah` and then also
scan `stdin`. You can even do `clamscan - blah` to now scan `stdin` and
then scan `blah`. Before, The `-` had to be the only "filename" argument
in order to scan from stdin.
In addition, added a bunch of extra empty lines or changing multi-line
function calls to single-line function calls in order to get around a
bug in clang-format with these two options do not playing nice together:
- AlignConsecutiveAssignments: true
- AlignAfterOpenBracket: true
AlignAfterOpenBracket is not taking account the spaces inserted by
AlignConsecutiveAssignments, so you end up with stuff like this:
```c
bleeblah = 1;
blah = function(arg1,
arg2,
arg3);
// ^--- these args 4-left from where they should be.
```
VSCode, meanwhile, somehow fixes this whitespace issue so code that is
correctly formatted by VSCode doesn't have this bug, meaning that:
1. The clang-format check in GH Actions fails.
2. We'd all have to stop using format-on-save in VSCode and accept the
bug if we wanted those GH Actions tests to pass.
Adding an empty line before variable assignments from multi-line
function calls evades the buggy behavior.
This commit should resolve the clang-format github action test failures,
for now.
2022-03-10 20:55:13 -08:00
|
|
|
char *pua_cats = NULL;
|
2014-09-11 14:15:36 -04:00
|
|
|
const struct optstruct *opt;
|
Code cleanup: Refactor to clean up formatting issues
Refactored the clamscan code that determines 'what to scan' in order
to clean up some very messy logic and also to get around a difference in
how vscode and clang-format handle formatting #ifdef blocks in the
middle of an else/if.
In addition to refactoring, there is a slight behavior improvement. With
this change, doing `clamscan blah -` will now scan `blah` and then also
scan `stdin`. You can even do `clamscan - blah` to now scan `stdin` and
then scan `blah`. Before, The `-` had to be the only "filename" argument
in order to scan from stdin.
In addition, added a bunch of extra empty lines or changing multi-line
function calls to single-line function calls in order to get around a
bug in clang-format with these two options do not playing nice together:
- AlignConsecutiveAssignments: true
- AlignAfterOpenBracket: true
AlignAfterOpenBracket is not taking account the spaces inserted by
AlignConsecutiveAssignments, so you end up with stuff like this:
```c
bleeblah = 1;
blah = function(arg1,
arg2,
arg3);
// ^--- these args 4-left from where they should be.
```
VSCode, meanwhile, somehow fixes this whitespace issue so code that is
correctly formatted by VSCode doesn't have this bug, meaning that:
1. The clang-format check in GH Actions fails.
2. We'd all have to stop using format-on-save in VSCode and accept the
bug if we wanted those GH Actions tests to pass.
Adding an empty line before variable assignments from multi-line
function calls evades the buggy behavior.
This commit should resolve the clang-format github action test failures,
for now.
2022-03-10 20:55:13 -08:00
|
|
|
#ifndef _WIN32
|
2014-09-11 14:15:36 -04:00
|
|
|
struct rlimit rlim;
|
2008-07-30 15:20:30 +00:00
|
|
|
#endif
|
2021-07-16 11:47:23 -07:00
|
|
|
struct sigload_progress sigload_progress_ctx = {0};
|
|
|
|
struct engine_compile_progress engine_compile_progress_ctx = {0};
|
|
|
|
#ifdef ENABLE_ENGINE_FREE_PROGRESSBAR
|
|
|
|
struct engine_free_progress engine_free_progress_ctx = {0};
|
|
|
|
#endif
|
2003-07-29 15:48:06 +00:00
|
|
|
|
FIPS-compliant CVD signing and verification
Add X509 certificate chain based signing with PKCS7-PEM external
signatures distributed alongside CVD's in a custom .cvd.sign format.
This new signing and verification mechanism is primarily in support
of FIPS compliance.
Fixes: https://github.com/Cisco-Talos/clamav/issues/564
Add a Rust implementation for parsing, verifying, and unpacking CVD
files.
Now installs a 'certs' directory in the app config directory
(e.g. <prefix>/etc/certs). The install location is configurable.
The CMake option to configure the CVD certs directory is:
`-D CVD_CERTS_DIRECTORY=PATH`
New options to set an alternative CVD certs directory:
- Commandline for freshclam, clamd, clamscan, and sigtool is:
`--cvdcertsdir PATH`
- Env variable for freshclam, clamd, clamscan, and sigtool is:
`CVD_CERTS_DIR`
- Config option for freshclam and clamd is:
`CVDCertsDirectory PATH`
Sigtool:
- Add sign/verify commands.
- Also verify CDIFF external digital signatures when applying CDIFFs.
- Place commonly used commands at the top of --help string.
- Fix up manpage.
Freshclam:
- Will try to download .sign files to verify CVDs and CDIFFs.
- Fix an issue where making a CLD would only include the CFG file for
daily and not if patching any other database.
libclamav.so:
- Bump version to 13:0:1 (aka 12.1.0).
- Also remove libclamav.map versioning.
Resolves: https://github.com/Cisco-Talos/clamav/issues/1304
- Add two new API's to the public clamav.h header:
```c
extern cl_error_t cl_cvdverify_ex(const char *file,
const char *certs_directory);
extern cl_error_t cl_cvdunpack_ex(const char *file,
const char *dir,
bool dont_verify,
const char *certs_directory);
```
The original `cl_cvdverify` and `cl_cvdunpack` are deprecated.
- Add `cl_engine_field` enum option `CL_ENGINE_CVDCERTSDIR`.
You may set this option with `cl_engine_set_str` and get it
with `cl_engine_get_str`, to override the compiled in default
CVD certs directory.
libfreshclam.so: Bump version to 4:0:0 (aka 4.0.0).
Add sigtool sign/verify tests and test certs.
Make it so downloadFile doesn't throw a warning if the server
doesn't have the .sign file.
Replace use of md5-based FP signatures in the unit tests with
sha256-based FP signatures because the md5 implementation used
by Python may be disabled in FIPS mode.
Fixes: https://github.com/Cisco-Talos/clamav/issues/1411
CMake: Add logic to enable the Rust openssl-sys / openssl-rs crates
to build against the same OpenSSL library as is used for the C build.
The Rust unit test application must also link directly with libcrypto
and libssl.
Fix some log messages with missing new lines.
Fix missing environment variable notes in --help messages and manpages.
Deconflict CONFDIR/DATADIR/CERTSDIR variable names that are defined in
clamav-config.h.in for libclamav from variable that had the same name
for use in clamav applications that use the optparser.
The 'clamav-test' certs for the unit tests will live for 10 years.
The 'clamav-beta.crt' public cert will only live for 120 days and will
be replaced before the stable release with a production 'clamav.crt'.
2024-11-21 14:01:09 -05:00
|
|
|
char *cvdcertsdir = NULL;
|
|
|
|
STATBUF statbuf;
|
|
|
|
|
2023-11-26 15:01:19 -08:00
|
|
|
/* Initialize scan options struct */
|
2018-07-20 22:28:48 -04:00
|
|
|
memset(&options, 0, sizeof(struct cl_scan_options));
|
|
|
|
|
2010-12-28 18:24:51 +01:00
|
|
|
dirlnk = optget(opts, "follow-dir-symlinks")->numarg;
|
2018-12-03 12:40:13 -05:00
|
|
|
if (dirlnk > 2) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "--follow-dir-symlinks: Invalid argument\n");
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2010-12-28 18:24:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
filelnk = optget(opts, "follow-file-symlinks")->numarg;
|
2018-12-03 12:40:13 -05:00
|
|
|
if (filelnk > 2) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "--follow-file-symlinks: Invalid argument\n");
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2010-12-28 18:24:51 +01:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "yara-rules")->enabled) {
|
|
|
|
char *p = optget(opts, "yara-rules")->strarg;
|
|
|
|
if (strcmp(p, "yes")) {
|
|
|
|
if (!strcmp(p, "only"))
|
|
|
|
dboptions |= CL_DB_YARA_ONLY;
|
|
|
|
else if (!strcmp(p, "no"))
|
|
|
|
dboptions |= CL_DB_YARA_EXCLUDE;
|
|
|
|
}
|
2015-07-23 15:36:16 -04:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "phishing-sigs")->enabled)
|
2014-09-11 14:15:36 -04:00
|
|
|
dboptions |= CL_DB_PHISHING;
|
2003-07-29 15:48:06 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "official-db-only")->enabled)
|
2014-09-11 14:15:36 -04:00
|
|
|
dboptions |= CL_DB_OFFICIAL_ONLY;
|
2009-11-10 19:30:33 +01:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "phishing-scan-urls")->enabled)
|
2014-09-11 14:15:36 -04:00
|
|
|
dboptions |= CL_DB_PHISHING_URLS;
|
2006-09-14 17:40:20 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "bytecode")->enabled)
|
2014-09-11 14:15:36 -04:00
|
|
|
dboptions |= CL_DB_BYTECODE;
|
2009-09-21 19:24:16 +03:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((ret = cl_init(CL_INIT_DEFAULT))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "Can't initialize libclamav: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2008-11-12 16:19:43 +00:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (!(engine = cl_engine_new())) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "Can't initialize antivirus engine\n");
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2008-11-12 16:19:43 +00:00
|
|
|
}
|
|
|
|
|
2016-08-24 17:39:20 -04:00
|
|
|
cl_engine_set_clcb_virus_found(engine, clamscan_virus_found_cb);
|
2018-12-03 12:40:13 -05:00
|
|
|
|
2021-07-16 11:47:23 -07:00
|
|
|
if (isatty(fileno(stdout)) &&
|
2021-07-16 18:53:14 -07:00
|
|
|
!optget(opts, "debug")->enabled &&
|
2021-07-16 11:47:23 -07:00
|
|
|
!optget(opts, "quiet")->enabled &&
|
|
|
|
!optget(opts, "infected")->enabled &&
|
|
|
|
!optget(opts, "no-summary")->enabled) {
|
|
|
|
/* set progress callbacks */
|
|
|
|
cl_engine_set_clcb_sigload_progress(engine, sigload_callback, &sigload_progress_ctx);
|
|
|
|
cl_engine_set_clcb_engine_compile_progress(engine, engine_compile_callback, &engine_compile_progress_ctx);
|
|
|
|
#ifdef ENABLE_ENGINE_FREE_PROGRESSBAR
|
|
|
|
cl_engine_set_clcb_engine_free_progress(engine, engine_free_callback, &engine_free_progress_ctx);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2023-03-31 17:20:54 -04:00
|
|
|
if ((opt = optget(opts, "cache-size"))->enabled)
|
|
|
|
cl_engine_set_num(engine, CL_ENGINE_CACHE_SIZE, opt->numarg);
|
2013-11-15 19:15:20 +00:00
|
|
|
if (optget(opts, "disable-cache")->enabled)
|
|
|
|
cl_engine_set_num(engine, CL_ENGINE_DISABLE_CACHE, 1);
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "detect-pua")->enabled) {
|
2014-09-11 14:15:36 -04:00
|
|
|
dboptions |= CL_DB_PUA;
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "exclude-pua"))->enabled) {
|
2014-09-11 14:15:36 -04:00
|
|
|
dboptions |= CL_DB_PUA_EXCLUDE;
|
|
|
|
i = 0;
|
2018-12-03 12:40:13 -05:00
|
|
|
while (opt) {
|
|
|
|
if (!(pua_cats = realloc(pua_cats, i + strlen(opt->strarg) + 3))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "Can't allocate memory for pua_cats\n");
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
sprintf(pua_cats + i, ".%s", opt->strarg);
|
|
|
|
i += strlen(opt->strarg) + 1;
|
|
|
|
pua_cats[i] = 0;
|
|
|
|
|
|
|
|
opt = opt->nextarg;
|
|
|
|
}
|
2018-12-03 12:40:13 -05:00
|
|
|
pua_cats[i] = '.';
|
2014-09-11 14:15:36 -04:00
|
|
|
pua_cats[i + 1] = 0;
|
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "include-pua"))->enabled) {
|
|
|
|
if (pua_cats) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "--exclude-pua and --include-pua cannot be used at the same time\n");
|
2014-09-11 14:15:36 -04:00
|
|
|
|
|
|
|
free(pua_cats);
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
dboptions |= CL_DB_PUA_INCLUDE;
|
|
|
|
i = 0;
|
2018-12-03 12:40:13 -05:00
|
|
|
while (opt) {
|
|
|
|
if (!(pua_cats = realloc(pua_cats, i + strlen(opt->strarg) + 3))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "Can't allocate memory for pua_cats\n");
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
sprintf(pua_cats + i, ".%s", opt->strarg);
|
|
|
|
i += strlen(opt->strarg) + 1;
|
|
|
|
pua_cats[i] = 0;
|
|
|
|
|
|
|
|
opt = opt->nextarg;
|
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
pua_cats[i] = '.';
|
2014-09-11 14:15:36 -04:00
|
|
|
pua_cats[i + 1] = 0;
|
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (pua_cats) {
|
|
|
|
if ((ret = cl_engine_set_str(engine, CL_ENGINE_PUA_CATEGORIES, pua_cats))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_str(CL_ENGINE_PUA_CATEGORIES) failed: %s\n", cl_strerror(ret));
|
2014-09-11 14:15:36 -04:00
|
|
|
|
|
|
|
free(pua_cats);
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
free(pua_cats);
|
|
|
|
}
|
2008-07-31 16:26:50 +00:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "dev-ac-only")->enabled)
|
2014-09-11 14:15:36 -04:00
|
|
|
cl_engine_set_num(engine, CL_ENGINE_AC_ONLY, 1);
|
2008-11-13 19:06:42 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "dev-ac-depth")->enabled)
|
2014-09-11 14:15:36 -04:00
|
|
|
cl_engine_set_num(engine, CL_ENGINE_AC_MAXDEPTH, optget(opts, "dev-ac-depth")->numarg);
|
2008-11-13 19:06:42 +00:00
|
|
|
|
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
|
|
|
if (optget(opts, "leave-temps")->enabled) {
|
|
|
|
/* Set the engine to keep temporary files */
|
2014-09-11 14:15:36 -04:00
|
|
|
cl_engine_set_num(engine, CL_ENGINE_KEEPTMP, 1);
|
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
|
|
|
/* Also set the engine to create temporary directory structure */
|
|
|
|
cl_engine_set_num(engine, CL_ENGINE_TMPDIR_RECURSION, 1);
|
|
|
|
}
|
2008-11-14 22:23:39 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "force-to-disk")->enabled)
|
2014-09-11 14:15:36 -04:00
|
|
|
cl_engine_set_num(engine, CL_ENGINE_FORCETODISK, 1);
|
2013-11-08 17:10:43 -05:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "bytecode-unsigned")->enabled)
|
2014-09-11 14:15:36 -04:00
|
|
|
dboptions |= CL_DB_BYTECODE_UNSIGNED;
|
2011-02-17 19:17:35 +01:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "bytecode-timeout"))->enabled)
|
2014-09-11 14:15:36 -04:00
|
|
|
cl_engine_set_num(engine, CL_ENGINE_BYTECODE_TIMEOUT, opt->numarg);
|
|
|
|
|
2016-03-16 15:42:35 -04:00
|
|
|
if (optget(opts, "nocerts")->enabled)
|
|
|
|
cl_engine_set_num(engine, CL_ENGINE_DISABLE_PE_CERTS, 1);
|
|
|
|
|
|
|
|
if (optget(opts, "dumpcerts")->enabled)
|
|
|
|
cl_engine_set_num(engine, CL_ENGINE_PE_DUMPCERTS, 1);
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "bytecode-mode"))->enabled) {
|
2014-09-11 14:15:36 -04:00
|
|
|
enum bytecode_mode mode;
|
|
|
|
|
|
|
|
if (!strcmp(opt->strarg, "ForceJIT"))
|
|
|
|
mode = CL_BYTECODE_MODE_JIT;
|
2018-12-03 12:40:13 -05:00
|
|
|
else if (!strcmp(opt->strarg, "ForceInterpreter"))
|
2014-09-11 14:15:36 -04:00
|
|
|
mode = CL_BYTECODE_MODE_INTERPRETER;
|
2018-12-03 12:40:13 -05:00
|
|
|
else if (!strcmp(opt->strarg, "Test"))
|
2014-09-11 14:15:36 -04:00
|
|
|
mode = CL_BYTECODE_MODE_TEST;
|
|
|
|
else
|
|
|
|
mode = CL_BYTECODE_MODE_AUTO;
|
|
|
|
|
|
|
|
cl_engine_set_num(engine, CL_ENGINE_BYTECODE_MODE, mode);
|
2010-07-29 13:46:16 +03:00
|
|
|
}
|
2010-03-12 13:13:08 +02:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "statistics"))->enabled) {
|
|
|
|
while (opt) {
|
|
|
|
if (!strcasecmp(opt->strarg, "bytecode")) {
|
|
|
|
dboptions |= CL_DB_BYTECODE_STATS;
|
|
|
|
} else if (!strcasecmp(opt->strarg, "pcre")) {
|
|
|
|
dboptions |= CL_DB_PCRE_STATS;
|
|
|
|
}
|
|
|
|
opt = opt->nextarg;
|
2014-09-16 14:44:50 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
FIPS & FIPS-like limits on hash algs for cryptographic uses
ClamAV will not function when using a FIPS-enabled OpenSSL 3.x.
This is because ClamAV uses MD5 and SHA1 algorithms for a variety of
purposes including matching for malware detection, matching to prevent
false positives on known-clean files, and for verification of MD5-based
RSA digital signatures for determining CVD (signature database archive)
authenticity.
Interestingly, FIPS had been intentionally bypassed when creating hashes
based whole buffers and whole files (by descriptor or `FILE`-pointer):
https://github.com/Cisco-Talos/clamav/commit/78d4a9985a06a418dd1338c94ee5db461035d75b
Note: this bypassed FIPS the 1.x way with:
`EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);`
It was NOT disabled when using `cl_hash_init()` / `cl_update_hash()` /
`cl_finish_hash()`. That likely worked by coincidence in that the hash
was already calculated most of the time. It certainly would have made
use of those functions if the hash had not been calculated prior:
https://github.com/Cisco-Talos/clamav/blob/78d4a9985a06a418dd1338c94ee5db461035d75b/libclamav/matcher.c#L743
Regardless, bypassing FIPS entirely is not the correct solution.
The FIPS restrictions against using MD5 and SHA1 are valid, particularly
when verifying CVD digital siganatures, but also I think when using a
hash to determine if the file is known-clean (i.e. the "clean cache" and
also MD5-based and SHA1-based FP signatures).
This commit extends the work to bypass FIPS using the newer 3.x method:
`md = EVP_MD_fetch(NULL, alg, "-fips");`
It does this for the legacy `cl_hash*()` functions including
`cl_hash_init()` / `cl_update_hash()` / `cl_finish_hash()`.
It also introduces extended versions that allow the caller to choose if
they want to bypass FIPS:
- `cl_hash_data_ex()`
- `cl_hash_init_ex()`
- `cl_update_hash_ex()`
- `cl_finish_hash_ex()`
- `cl_hash_destroy_ex()`
- `cl_hash_file_fd_ex()`
See the `flags` parameter for each.
Ironically, this commit does NOT use the new functions at this time.
The rational is that ClamAV may need MD5, SHA1, and SHA-256 hashes of
the same files both for determining if the file is malware, and for
determining if the file is clean.
So instead, this commit will do a checks when:
1. Creating a new ClamAV scanning engine. If FIPS-mode enabled, it will
automatically toggle the "FIPS limits" engine option.
When loading signatures, if the engine "FIPS limits" option is enabled,
then MD5 and SHA1 FP signatures will be skipped.
2. Before verifying a CVD (e.g. also for loading, unpacking when
verification enabled).
If "FIPS limits" or FIPS-mode are enabled, then the legacy MD5-based RSA
method is disabled.
Note: This commit also refactors the interface for `cl_cvdverify_ex()`
and `cl_cvdunpack_ex()` so they take a `flags` parameters, rather than a
single `bool`. As these functions are new in this version, it does not
break the ABI.
The cache was already switched to use SHA2-256, so that's not a concern
for checking FIPS-mode / FIPS limits options.
This adds an option for `freshclam.conf` and `clamd.conf`:
FIPSCryptoHashLimits yes
And an equivalent command-line option for `clamscan` and `sigtool`:
--fips-limits
You may programmatically enable FIPS-limits for a ClamAV engine like this:
```C
cl_engine_set_num(engine, CL_ENGINE_FIPS_LIMITS, 1);
```
CLAM-2792
2025-07-01 20:41:47 -04:00
|
|
|
if (optget(opts, "fips-limits")->enabled) {
|
|
|
|
dboptions |= CL_DB_FIPS_LIMITS;
|
|
|
|
cl_engine_set_num(engine, CL_ENGINE_FIPS_LIMITS, 1);
|
|
|
|
}
|
|
|
|
|
2025-06-04 10:13:47 -04:00
|
|
|
if (optget(opts, "gen-json")->enabled) {
|
2018-07-20 22:28:48 -04:00
|
|
|
options.general |= CL_SCAN_GENERAL_COLLECT_METADATA;
|
2025-06-04 10:13:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (optget(opts, "json-store-html-uris")->enabled) {
|
|
|
|
options.general |= CL_SCAN_GENERAL_STORE_HTML_URIS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (optget(opts, "json-store-pdf-uris")->enabled) {
|
|
|
|
options.general |= CL_SCAN_GENERAL_STORE_PDF_URIS;
|
|
|
|
}
|
2018-02-26 13:41:32 -05:00
|
|
|
|
2025-06-03 19:03:20 -04:00
|
|
|
if (optget(opts, "json-store-extra-hashes")->enabled) {
|
|
|
|
options.general |= CL_SCAN_GENERAL_STORE_EXTRA_HASHES;
|
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "tempdir"))->enabled) {
|
|
|
|
if ((ret = cl_engine_set_str(engine, CL_ENGINE_TMPDIR, opt->strarg))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_str(CL_ENGINE_TMPDIR) failed: %s\n", cl_strerror(ret));
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
2008-11-14 22:23:39 +00:00
|
|
|
}
|
|
|
|
|
FIPS-compliant CVD signing and verification
Add X509 certificate chain based signing with PKCS7-PEM external
signatures distributed alongside CVD's in a custom .cvd.sign format.
This new signing and verification mechanism is primarily in support
of FIPS compliance.
Fixes: https://github.com/Cisco-Talos/clamav/issues/564
Add a Rust implementation for parsing, verifying, and unpacking CVD
files.
Now installs a 'certs' directory in the app config directory
(e.g. <prefix>/etc/certs). The install location is configurable.
The CMake option to configure the CVD certs directory is:
`-D CVD_CERTS_DIRECTORY=PATH`
New options to set an alternative CVD certs directory:
- Commandline for freshclam, clamd, clamscan, and sigtool is:
`--cvdcertsdir PATH`
- Env variable for freshclam, clamd, clamscan, and sigtool is:
`CVD_CERTS_DIR`
- Config option for freshclam and clamd is:
`CVDCertsDirectory PATH`
Sigtool:
- Add sign/verify commands.
- Also verify CDIFF external digital signatures when applying CDIFFs.
- Place commonly used commands at the top of --help string.
- Fix up manpage.
Freshclam:
- Will try to download .sign files to verify CVDs and CDIFFs.
- Fix an issue where making a CLD would only include the CFG file for
daily and not if patching any other database.
libclamav.so:
- Bump version to 13:0:1 (aka 12.1.0).
- Also remove libclamav.map versioning.
Resolves: https://github.com/Cisco-Talos/clamav/issues/1304
- Add two new API's to the public clamav.h header:
```c
extern cl_error_t cl_cvdverify_ex(const char *file,
const char *certs_directory);
extern cl_error_t cl_cvdunpack_ex(const char *file,
const char *dir,
bool dont_verify,
const char *certs_directory);
```
The original `cl_cvdverify` and `cl_cvdunpack` are deprecated.
- Add `cl_engine_field` enum option `CL_ENGINE_CVDCERTSDIR`.
You may set this option with `cl_engine_set_str` and get it
with `cl_engine_get_str`, to override the compiled in default
CVD certs directory.
libfreshclam.so: Bump version to 4:0:0 (aka 4.0.0).
Add sigtool sign/verify tests and test certs.
Make it so downloadFile doesn't throw a warning if the server
doesn't have the .sign file.
Replace use of md5-based FP signatures in the unit tests with
sha256-based FP signatures because the md5 implementation used
by Python may be disabled in FIPS mode.
Fixes: https://github.com/Cisco-Talos/clamav/issues/1411
CMake: Add logic to enable the Rust openssl-sys / openssl-rs crates
to build against the same OpenSSL library as is used for the C build.
The Rust unit test application must also link directly with libcrypto
and libssl.
Fix some log messages with missing new lines.
Fix missing environment variable notes in --help messages and manpages.
Deconflict CONFDIR/DATADIR/CERTSDIR variable names that are defined in
clamav-config.h.in for libclamav from variable that had the same name
for use in clamav applications that use the optparser.
The 'clamav-test' certs for the unit tests will live for 10 years.
The 'clamav-beta.crt' public cert will only live for 120 days and will
be replaced before the stable release with a production 'clamav.crt'.
2024-11-21 14:01:09 -05:00
|
|
|
cvdcertsdir = optget(opts, "cvdcertsdir")->strarg;
|
|
|
|
if (NULL != cvdcertsdir) {
|
|
|
|
// Command line option must override the engine defaults
|
|
|
|
// (which would've used the env var or hardcoded path)
|
|
|
|
if (LSTAT(cvdcertsdir, &statbuf) == -1) {
|
|
|
|
logg(LOGG_ERROR,
|
|
|
|
"ClamAV CA certificates directory is missing: %s"
|
|
|
|
" - It should have been provided as a part of installation.\n",
|
|
|
|
cvdcertsdir);
|
|
|
|
ret = 2;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret = cl_engine_set_str(engine, CL_ENGINE_CVDCERTSDIR, cvdcertsdir))) {
|
|
|
|
logg(LOGG_ERROR, "cli_engine_set_str(CL_ENGINE_CVDCERTSDIR) failed: %s\n", cl_strerror(ret));
|
|
|
|
ret = 2;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "database"))->active) {
|
|
|
|
while (opt) {
|
2023-03-29 00:22:48 +03:00
|
|
|
if (optget(opts, "fail-if-cvd-older-than")->enabled) {
|
2024-07-12 16:57:09 +03:00
|
|
|
if (LSTAT(opt->strarg, &sb) == -1) {
|
|
|
|
logg(LOGG_ERROR, "Can't access database directory/file: %s\n", opt->strarg);
|
|
|
|
ret = 2;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
if (!S_ISDIR(sb.st_mode) && !CLI_DBEXT_SIGNATURE(opt->strarg)) {
|
|
|
|
opt = opt->nextarg;
|
|
|
|
continue;
|
|
|
|
}
|
2023-03-29 00:22:48 +03:00
|
|
|
if (check_if_cvd_outdated(opt->strarg, optget(opts, "fail-if-cvd-older-than")->numarg) != CL_SUCCESS) {
|
|
|
|
ret = 2;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((ret = cl_load(opt->strarg, engine, &info.sigs, dboptions))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "%s\n", cl_strerror(ret));
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
opt = opt->nextarg;
|
|
|
|
}
|
2003-07-29 15:48:06 +00:00
|
|
|
} else {
|
2014-09-11 14:15:36 -04:00
|
|
|
char *dbdir = freshdbdir();
|
|
|
|
|
2023-03-29 00:22:48 +03:00
|
|
|
if (optget(opts, "fail-if-cvd-older-than")->enabled) {
|
|
|
|
if (check_if_cvd_outdated(dbdir, optget(opts, "fail-if-cvd-older-than")->numarg) != CL_SUCCESS) {
|
|
|
|
ret = 2;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((ret = cl_load(dbdir, engine, &info.sigs, dboptions))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "%s\n", cl_strerror(ret));
|
2014-09-11 14:15:36 -04:00
|
|
|
|
|
|
|
free(dbdir);
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
free(dbdir);
|
2003-07-29 15:48:06 +00:00
|
|
|
}
|
|
|
|
|
2016-02-08 15:24:30 -05:00
|
|
|
/* pcre engine limits - required for cl_engine_compile */
|
|
|
|
if ((opt = optget(opts, "pcre-match-limit"))->active) {
|
|
|
|
if ((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_MATCH_LIMIT, opt->numarg))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_num(CL_ENGINE_PCRE_MATCH_LIMIT) failed: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2016-02-08 15:24:30 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((opt = optget(opts, "pcre-recmatch-limit"))->active) {
|
|
|
|
if ((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_RECMATCH_LIMIT, opt->numarg))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_num(CL_ENGINE_PCRE_RECMATCH_LIMIT) failed: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2016-02-08 15:24:30 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((ret = cl_engine_compile(engine)) != 0) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "Database initialization error: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
|
|
|
}
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2021-07-16 11:47:23 -07:00
|
|
|
if (isatty(fileno(stdout)) &&
|
2021-07-16 18:53:14 -07:00
|
|
|
!optget(opts, "debug")->enabled &&
|
2021-07-16 11:47:23 -07:00
|
|
|
!optget(opts, "quiet")->enabled &&
|
|
|
|
!optget(opts, "infected")->enabled &&
|
|
|
|
!optget(opts, "no-summary")->enabled) {
|
|
|
|
/* For a space after the progress bars */
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_INFO, "\n");
|
2003-12-02 22:48:56 +00:00
|
|
|
}
|
2003-07-29 15:48:06 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "archive-verbose")->enabled) {
|
2014-09-11 14:15:36 -04:00
|
|
|
cl_engine_set_clcb_meta(engine, meta);
|
|
|
|
cl_engine_set_clcb_pre_cache(engine, pre);
|
|
|
|
cl_engine_set_clcb_post_scan(engine, post);
|
2011-08-22 16:58:48 +03:00
|
|
|
}
|
|
|
|
|
2007-01-30 21:11:32 +00:00
|
|
|
/* set limits */
|
2003-07-29 15:48:06 +00:00
|
|
|
|
2019-08-16 17:18:59 -07:00
|
|
|
/* TODO: Remove deprecated option in a future feature release */
|
|
|
|
if ((opt = optget(opts, "timelimit"))->active) {
|
|
|
|
if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANTIME, opt->numarg))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_num(CL_ENGINE_MAX_SCANTIME) failed: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2019-08-16 17:18:59 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((opt = optget(opts, "max-scantime"))->active) {
|
|
|
|
if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANTIME, opt->numarg))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_num(CL_ENGINE_MAX_SCANTIME) failed: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2019-08-16 17:18:59 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "max-scansize"))->active) {
|
|
|
|
if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCANSIZE, opt->numarg))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_num(CL_ENGINE_MAX_SCANSIZE) failed: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
2008-11-12 16:19:43 +00:00
|
|
|
}
|
2008-02-07 02:00:21 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "max-filesize"))->active) {
|
|
|
|
if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_FILESIZE, opt->numarg))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_num(CL_ENGINE_MAX_FILESIZE) failed: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
2008-11-12 16:19:43 +00:00
|
|
|
}
|
2003-07-29 15:48:06 +00:00
|
|
|
|
2009-09-24 19:23:21 +02:00
|
|
|
#ifndef _WIN32
|
2018-12-03 12:40:13 -05:00
|
|
|
if (getrlimit(RLIMIT_FSIZE, &rlim) == 0) {
|
|
|
|
if (rlim.rlim_cur < (rlim_t)cl_engine_get_num(engine, CL_ENGINE_MAX_FILESIZE, NULL))
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_WARNING, "System limit for file size is lower than engine->maxfilesize\n");
|
2018-12-03 12:40:13 -05:00
|
|
|
if (rlim.rlim_cur < (rlim_t)cl_engine_get_num(engine, CL_ENGINE_MAX_SCANSIZE, NULL))
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_WARNING, "System limit for file size is lower than engine->maxscansize\n");
|
2008-07-30 15:20:30 +00:00
|
|
|
} else {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_WARNING, "Cannot obtain resource limits for file size\n");
|
2008-07-30 15:20:30 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "max-files"))->active) {
|
|
|
|
if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_FILES, opt->numarg))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_num(CL_ENGINE_MAX_FILES) failed: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
2008-11-12 16:19:43 +00:00
|
|
|
}
|
2003-07-29 15:48:06 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "max-recursion"))->active) {
|
2024-05-07 09:07:56 -07:00
|
|
|
uint32_t opt_value = opt->numarg;
|
|
|
|
if ((0 == opt_value) || (opt_value > CLI_MAX_MAXRECLEVEL)) {
|
|
|
|
logg(LOGG_ERROR, "max-recursion set to %u, but cannot be larger than %u, and cannot be 0.\n",
|
2024-09-09 12:46:33 -04:00
|
|
|
opt_value, CLI_MAX_MAXRECLEVEL);
|
2024-05-07 09:07:56 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
|
|
|
}
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_RECURSION, opt->numarg))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_num(CL_ENGINE_MAX_RECURSION) failed: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
2008-11-12 16:19:43 +00:00
|
|
|
}
|
2003-07-29 15:48:06 +00:00
|
|
|
|
2012-11-27 17:15:02 -05:00
|
|
|
/* Engine max sizes */
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "max-embeddedpe"))->active) {
|
|
|
|
if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_EMBEDDEDPE, opt->numarg))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_num(CL_ENGINE_MAX_EMBEDDEDPE) failed: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
2012-11-27 17:15:02 -05:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "max-htmlnormalize"))->active) {
|
|
|
|
if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_HTMLNORMALIZE, opt->numarg))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_num(CL_ENGINE_MAX_HTMLNORMALIZE) failed: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
2012-11-27 17:15:02 -05:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "max-htmlnotags"))->active) {
|
|
|
|
if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_HTMLNOTAGS, opt->numarg))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_num(CL_ENGINE_MAX_HTMLNOTAGS) failed: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
2012-11-27 17:15:02 -05:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "max-scriptnormalize"))->active) {
|
|
|
|
if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_SCRIPTNORMALIZE, opt->numarg))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_num(CL_ENGINE_MAX_SCRIPTNORMALIZE) failed: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
2012-11-27 17:15:02 -05:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "max-ziptypercg"))->active) {
|
|
|
|
if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_ZIPTYPERCG, opt->numarg))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_num(CL_ENGINE_MAX_ZIPTYPERCG) failed: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
2012-11-27 17:15:02 -05:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "max-partitions"))->active) {
|
|
|
|
if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_PARTITIONS, opt->numarg))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_num(CL_ENGINE_MAX_PARTITIONS) failed: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
2014-02-06 18:55:40 -05:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "max-iconspe"))->active) {
|
|
|
|
if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_ICONSPE, opt->numarg))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_num(CL_ENGINE_MAX_ICONSPE) failed: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
2014-03-06 18:19:11 -05:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "max-rechwp3"))->active) {
|
|
|
|
if ((ret = cl_engine_set_num(engine, CL_ENGINE_MAX_RECHWP3, opt->numarg))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_num(CL_ENGINE_MAX_RECHWP3) failed: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2016-01-19 14:25:55 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-19 02:39:52 -04:00
|
|
|
if ((opt = optget(opts, "pcre-max-filesize"))->active) {
|
|
|
|
if ((ret = cl_engine_set_num(engine, CL_ENGINE_PCRE_MAX_FILESIZE, opt->numarg))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_num(CL_ENGINE_PCRE_MAX_FILESIZE) failed: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-19 02:39:52 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-30 10:33:43 +00:00
|
|
|
/* set scan options */
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "allmatch")->enabled) {
|
2018-07-20 22:28:48 -04:00
|
|
|
options.general |= CL_SCAN_GENERAL_ALLMATCHES;
|
2015-10-01 17:47:37 -04:00
|
|
|
}
|
2012-10-18 14:12:58 -07:00
|
|
|
|
2018-10-10 06:02:28 -07:00
|
|
|
/* TODO: Remove deprecated option in a future feature release */
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((optget(opts, "phishing-ssl")->enabled) ||
|
|
|
|
(optget(opts, "alert-phishing-ssl")->enabled))
|
2018-07-20 22:28:48 -04:00
|
|
|
options.heuristic |= CL_SCAN_HEURISTIC_PHISHING_SSL_MISMATCH;
|
2004-09-04 21:09:20 +00:00
|
|
|
|
2018-10-10 06:02:28 -07:00
|
|
|
/* TODO: Remove deprecated option in a future feature release */
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((optget(opts, "phishing-cloak")->enabled) ||
|
|
|
|
(optget(opts, "alert-phishing-cloak")->enabled))
|
2018-07-20 22:28:48 -04:00
|
|
|
options.heuristic |= CL_SCAN_HEURISTIC_PHISHING_CLOAK;
|
2008-12-30 10:33:43 +00:00
|
|
|
|
2018-10-10 06:02:28 -07:00
|
|
|
/* TODO: Remove deprecated option in a future feature release */
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((optget(opts, "partition-intersection")->enabled) ||
|
|
|
|
(optget(opts, "alert-partition-intersection")->enabled))
|
2018-07-20 22:28:48 -04:00
|
|
|
options.heuristic |= CL_SCAN_HEURISTIC_PARTITION_INTXN;
|
2014-02-06 18:55:40 -05:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "heuristic-scan-precedence")->enabled)
|
2018-07-20 22:28:48 -04:00
|
|
|
options.general |= CL_SCAN_GENERAL_HEURISTIC_PRECEDENCE;
|
2008-12-30 10:33:43 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "scan-archive")->enabled)
|
2018-07-20 22:28:48 -04:00
|
|
|
options.parse |= CL_SCAN_PARSE_ARCHIVE;
|
2004-09-04 21:09:20 +00:00
|
|
|
|
2018-10-10 06:02:28 -07:00
|
|
|
/* TODO: Remove deprecated option in a future feature release */
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((optget(opts, "detect-broken")->enabled) ||
|
2018-10-10 06:02:28 -07:00
|
|
|
(optget(opts, "alert-broken")->enabled)) {
|
2018-07-20 22:28:48 -04:00
|
|
|
options.heuristic |= CL_SCAN_HEURISTIC_BROKEN;
|
2018-10-10 06:02:28 -07:00
|
|
|
}
|
|
|
|
|
GIF, PNG bugfixes; Add AlertBrokenMedia option
Added a new scan option to alert on broken media (graphics) file
formats. This feature mitigates the risk of malformed media files
intended to exploit vulnerabilities in other software. At present
media validation exists for JPEG, TIFF, PNG, and GIF files.
To enable this feature, set `AlertBrokenMedia yes` in clamd.conf, or
use the `--alert-broken-media` option when using `clamscan`.
These options are disabled by default for now.
Application developers may enable this scan option by enabling
`CL_SCAN_HEURISTIC_BROKEN_MEDIA` for the `heuristic` scan option bit
field.
Fixed PNG parser logic bugs that caused an excess of parsing errors
and fixed a stack exhaustion issue affecting some systems when
scanning PNG files. PNG file type detection was disabled via
signature database update for 0.103.0 to mitigate effects from these
bugs.
Fixed an issue where PNG and GIF files no longer work with Target:5
(graphics) signatures if detected as CL_TYPE_PNG/GIF rather than as
CL_TYPE_GRAPHICS. Target types now support up to 10 possible file
types to make way for additional graphics types in future releases.
Scanning JPEG, TIFF, PNG, and GIF files will no longer return "parse"
errors when file format validation fails. Instead, the scan will alert
with the "Heuristics.Broken.Media" signature prefix and a descriptive
suffix to indicate the issue, provided that the "alert broken media"
feature is enabled.
GIF format validation will no longer fail if the GIF image is missing
the trailer byte, as this appears to be a relatively common issue in
otherwise functional GIF files.
Added a TIFF dynamic configuration (DCONF) option, which was missing.
This will allow us to disable TIFF format validation via signature
database update in the event that it proves to be problematic.
This feature already exists for many other file types.
Added CL_TYPE_JPEG and CL_TYPE_TIFF types.
2020-11-04 15:49:43 -08:00
|
|
|
if (optget(opts, "alert-broken-media")->enabled) {
|
|
|
|
options.heuristic |= CL_SCAN_HEURISTIC_BROKEN_MEDIA;
|
|
|
|
}
|
|
|
|
|
2018-10-10 06:02:28 -07:00
|
|
|
/* TODO: Remove deprecated option in a future feature release */
|
|
|
|
if ((optget(opts, "block-encrypted")->enabled) ||
|
|
|
|
(optget(opts, "alert-encrypted")->enabled)) {
|
|
|
|
options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_ARCHIVE;
|
|
|
|
options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_DOC;
|
|
|
|
}
|
2004-09-04 21:09:20 +00:00
|
|
|
|
2018-10-10 06:02:28 -07:00
|
|
|
if (optget(opts, "alert-encrypted-archive")->enabled)
|
|
|
|
options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_ARCHIVE;
|
2004-09-04 21:09:20 +00:00
|
|
|
|
2018-10-10 06:02:28 -07:00
|
|
|
if (optget(opts, "alert-encrypted-doc")->enabled)
|
|
|
|
options.heuristic |= CL_SCAN_HEURISTIC_ENCRYPTED_DOC;
|
|
|
|
|
|
|
|
/* TODO: Remove deprecated option in a future feature release */
|
|
|
|
if ((optget(opts, "block-macros")->enabled) ||
|
|
|
|
(optget(opts, "alert-macros")->enabled)) {
|
2018-07-20 22:28:48 -04:00
|
|
|
options.heuristic |= CL_SCAN_HEURISTIC_MACROS;
|
2018-10-10 06:02:28 -07:00
|
|
|
}
|
2016-03-10 18:26:33 -05:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "scan-pe")->enabled)
|
2018-07-20 22:28:48 -04:00
|
|
|
options.parse |= CL_SCAN_PARSE_PE;
|
2004-09-04 21:09:20 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "scan-elf")->enabled)
|
2018-07-20 22:28:48 -04:00
|
|
|
options.parse |= CL_SCAN_PARSE_ELF;
|
2006-10-28 22:01:51 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "scan-ole2")->enabled)
|
2018-07-20 22:28:48 -04:00
|
|
|
options.parse |= CL_SCAN_PARSE_OLE2;
|
2004-09-04 21:09:20 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "scan-pdf")->enabled)
|
2018-07-20 22:28:48 -04:00
|
|
|
options.parse |= CL_SCAN_PARSE_PDF;
|
2007-02-22 17:49:57 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "scan-swf")->enabled)
|
2018-07-20 22:28:48 -04:00
|
|
|
options.parse |= CL_SCAN_PARSE_SWF;
|
2013-02-05 19:46:56 -05:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "scan-html")->enabled && optget(opts, "normalize")->enabled)
|
2018-07-20 22:28:48 -04:00
|
|
|
options.parse |= CL_SCAN_PARSE_HTML;
|
2004-09-04 21:09:20 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "scan-mail")->enabled)
|
2018-07-20 22:28:48 -04:00
|
|
|
options.parse |= CL_SCAN_PARSE_MAIL;
|
2004-09-04 21:09:20 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "scan-xmldocs")->enabled)
|
2018-07-20 22:28:48 -04:00
|
|
|
options.parse |= CL_SCAN_PARSE_XMLDOCS;
|
2016-02-02 14:23:13 -05:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "scan-hwp3")->enabled)
|
2018-07-20 22:28:48 -04:00
|
|
|
options.parse |= CL_SCAN_PARSE_HWP3;
|
2016-02-02 14:23:13 -05:00
|
|
|
|
2023-09-29 12:49:29 -07:00
|
|
|
if (optget(opts, "scan-onenote")->enabled)
|
|
|
|
options.parse |= CL_SCAN_PARSE_ONENOTE;
|
|
|
|
|
2024-02-25 12:43:56 -05:00
|
|
|
if (optget(opts, "scan-image")->enabled)
|
|
|
|
options.parse |= CL_SCAN_PARSE_IMAGE;
|
|
|
|
|
|
|
|
if (optget(opts, "scan-image-fuzzy-hash")->enabled)
|
|
|
|
options.parse |= CL_SCAN_PARSE_IMAGE_FUZZY_HASH;
|
|
|
|
|
2018-10-23 13:20:12 -07:00
|
|
|
/* TODO: Remove deprecated option in a future feature release */
|
|
|
|
if ((optget(opts, "algorithmic-detection")->enabled) && /* && used due to default-yes for both options */
|
|
|
|
(optget(opts, "heuristic-alerts")->enabled)) {
|
2018-07-20 22:28:48 -04:00
|
|
|
options.general |= CL_SCAN_GENERAL_HEURISTICS;
|
2018-10-23 13:20:12 -07:00
|
|
|
}
|
2005-12-12 18:44:37 +00:00
|
|
|
|
2018-10-10 06:02:28 -07:00
|
|
|
/* TODO: Remove deprecated option in a future feature release */
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((optget(opts, "block-max")->enabled) ||
|
2018-10-10 06:02:28 -07:00
|
|
|
(optget(opts, "alert-exceeds-max")->enabled)) {
|
2018-07-20 22:28:48 -04:00
|
|
|
options.heuristic |= CL_SCAN_HEURISTIC_EXCEEDS_MAX;
|
2016-08-24 17:39:20 -04:00
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "dev-performance")->enabled)
|
2018-10-19 20:43:19 -07:00
|
|
|
options.dev |= CL_SCAN_DEV_COLLECT_PERFORMANCE_INFO;
|
2011-02-14 19:19:20 +02:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if (optget(opts, "detect-structured")->enabled) {
|
2018-07-20 22:28:48 -04:00
|
|
|
options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED;
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "structured-ssn-format"))->enabled) {
|
|
|
|
switch (opt->numarg) {
|
|
|
|
case 0:
|
|
|
|
options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED_SSN_STRIPPED;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
options.heuristic |= (CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL | CL_SCAN_HEURISTIC_STRUCTURED_SSN_STRIPPED);
|
|
|
|
break;
|
|
|
|
default:
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "Invalid argument for --structured-ssn-format\n");
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
|
|
|
} else {
|
2018-07-20 22:28:48 -04:00
|
|
|
options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED_SSN_NORMAL;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
2008-05-07 10:51:23 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "structured-ssn-count"))->active) {
|
|
|
|
if ((ret = cl_engine_set_num(engine, CL_ENGINE_MIN_SSN_COUNT, opt->numarg))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_num(CL_ENGINE_MIN_SSN_COUNT) failed: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "structured-cc-count"))->active) {
|
|
|
|
if ((ret = cl_engine_set_num(engine, CL_ENGINE_MIN_CC_COUNT, opt->numarg))) {
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "cli_engine_set_num(CL_ENGINE_MIN_CC_COUNT) failed: %s\n", cl_strerror(ret));
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
|
|
|
}
|
2016-04-21 12:13:57 -04:00
|
|
|
|
2019-08-27 17:33:22 -04:00
|
|
|
if ((opt = optget(opts, "structured-cc-mode"))->active) {
|
|
|
|
switch (opt->numarg) {
|
2016-04-21 12:13:57 -04:00
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
options.heuristic |= CL_SCAN_HEURISTIC_STRUCTURED_CC;
|
|
|
|
break;
|
|
|
|
default:
|
2022-02-16 00:13:55 +01:00
|
|
|
logg(LOGG_ERROR, "Invalid argument for --structured-cc-mode\n");
|
2021-07-16 11:47:23 -07:00
|
|
|
ret = 2;
|
|
|
|
goto done;
|
2016-04-21 12:13:57 -04:00
|
|
|
}
|
|
|
|
}
|
2008-12-30 10:33:43 +00:00
|
|
|
} else {
|
2018-07-20 22:28:48 -04:00
|
|
|
options.heuristic &= ~CL_SCAN_HEURISTIC_STRUCTURED;
|
2008-12-30 10:33:43 +00:00
|
|
|
}
|
2008-04-16 18:47:42 +00:00
|
|
|
|
2003-07-29 15:48:06 +00:00
|
|
|
#ifdef C_LINUX
|
2018-12-03 12:40:13 -05:00
|
|
|
procdev = (dev_t)0;
|
Code cleanup: Refactor to clean up formatting issues
Refactored the clamscan code that determines 'what to scan' in order
to clean up some very messy logic and also to get around a difference in
how vscode and clang-format handle formatting #ifdef blocks in the
middle of an else/if.
In addition to refactoring, there is a slight behavior improvement. With
this change, doing `clamscan blah -` will now scan `blah` and then also
scan `stdin`. You can even do `clamscan - blah` to now scan `stdin` and
then scan `blah`. Before, The `-` had to be the only "filename" argument
in order to scan from stdin.
In addition, added a bunch of extra empty lines or changing multi-line
function calls to single-line function calls in order to get around a
bug in clang-format with these two options do not playing nice together:
- AlignConsecutiveAssignments: true
- AlignAfterOpenBracket: true
AlignAfterOpenBracket is not taking account the spaces inserted by
AlignConsecutiveAssignments, so you end up with stuff like this:
```c
bleeblah = 1;
blah = function(arg1,
arg2,
arg3);
// ^--- these args 4-left from where they should be.
```
VSCode, meanwhile, somehow fixes this whitespace issue so code that is
correctly formatted by VSCode doesn't have this bug, meaning that:
1. The clang-format check in GH Actions fails.
2. We'd all have to stop using format-on-save in VSCode and accept the
bug if we wanted those GH Actions tests to pass.
Adding an empty line before variable assignments from multi-line
function calls evades the buggy behavior.
This commit should resolve the clang-format github action test failures,
for now.
2022-03-10 20:55:13 -08:00
|
|
|
if (CLAMSTAT("/proc", &sb) != -1 && !sb.st_size) {
|
2014-09-11 14:15:36 -04:00
|
|
|
procdev = sb.st_dev;
|
Code cleanup: Refactor to clean up formatting issues
Refactored the clamscan code that determines 'what to scan' in order
to clean up some very messy logic and also to get around a difference in
how vscode and clang-format handle formatting #ifdef blocks in the
middle of an else/if.
In addition to refactoring, there is a slight behavior improvement. With
this change, doing `clamscan blah -` will now scan `blah` and then also
scan `stdin`. You can even do `clamscan - blah` to now scan `stdin` and
then scan `blah`. Before, The `-` had to be the only "filename" argument
in order to scan from stdin.
In addition, added a bunch of extra empty lines or changing multi-line
function calls to single-line function calls in order to get around a
bug in clang-format with these two options do not playing nice together:
- AlignConsecutiveAssignments: true
- AlignAfterOpenBracket: true
AlignAfterOpenBracket is not taking account the spaces inserted by
AlignConsecutiveAssignments, so you end up with stuff like this:
```c
bleeblah = 1;
blah = function(arg1,
arg2,
arg3);
// ^--- these args 4-left from where they should be.
```
VSCode, meanwhile, somehow fixes this whitespace issue so code that is
correctly formatted by VSCode doesn't have this bug, meaning that:
1. The clang-format check in GH Actions fails.
2. We'd all have to stop using format-on-save in VSCode and accept the
bug if we wanted those GH Actions tests to pass.
Adding an empty line before variable assignments from multi-line
function calls evades the buggy behavior.
This commit should resolve the clang-format github action test failures,
for now.
2022-03-10 20:55:13 -08:00
|
|
|
}
|
2003-07-29 15:48:06 +00:00
|
|
|
#endif
|
|
|
|
|
Code cleanup: Refactor to clean up formatting issues
Refactored the clamscan code that determines 'what to scan' in order
to clean up some very messy logic and also to get around a difference in
how vscode and clang-format handle formatting #ifdef blocks in the
middle of an else/if.
In addition to refactoring, there is a slight behavior improvement. With
this change, doing `clamscan blah -` will now scan `blah` and then also
scan `stdin`. You can even do `clamscan - blah` to now scan `stdin` and
then scan `blah`. Before, The `-` had to be the only "filename" argument
in order to scan from stdin.
In addition, added a bunch of extra empty lines or changing multi-line
function calls to single-line function calls in order to get around a
bug in clang-format with these two options do not playing nice together:
- AlignConsecutiveAssignments: true
- AlignAfterOpenBracket: true
AlignAfterOpenBracket is not taking account the spaces inserted by
AlignConsecutiveAssignments, so you end up with stuff like this:
```c
bleeblah = 1;
blah = function(arg1,
arg2,
arg3);
// ^--- these args 4-left from where they should be.
```
VSCode, meanwhile, somehow fixes this whitespace issue so code that is
correctly formatted by VSCode doesn't have this bug, meaning that:
1. The clang-format check in GH Actions fails.
2. We'd all have to stop using format-on-save in VSCode and accept the
bug if we wanted those GH Actions tests to pass.
Adding an empty line before variable assignments from multi-line
function calls evades the buggy behavior.
This commit should resolve the clang-format github action test failures,
for now.
2022-03-10 20:55:13 -08:00
|
|
|
if (optget(opts, "file-list")->enabled || opts->filename) {
|
|
|
|
/* scan the files listed in the --file-list, or it that's not specified, then
|
|
|
|
* scan the list of file arguments (including data from stdin, if `-` specified) */
|
|
|
|
ret = scan_files(engine, opts, &options, dirlnk, filelnk);
|
2014-09-11 14:15:36 -04:00
|
|
|
|
2021-08-27 09:14:45 -07:00
|
|
|
#ifdef _WIN32
|
Code cleanup: Refactor to clean up formatting issues
Refactored the clamscan code that determines 'what to scan' in order
to clean up some very messy logic and also to get around a difference in
how vscode and clang-format handle formatting #ifdef blocks in the
middle of an else/if.
In addition to refactoring, there is a slight behavior improvement. With
this change, doing `clamscan blah -` will now scan `blah` and then also
scan `stdin`. You can even do `clamscan - blah` to now scan `stdin` and
then scan `blah`. Before, The `-` had to be the only "filename" argument
in order to scan from stdin.
In addition, added a bunch of extra empty lines or changing multi-line
function calls to single-line function calls in order to get around a
bug in clang-format with these two options do not playing nice together:
- AlignConsecutiveAssignments: true
- AlignAfterOpenBracket: true
AlignAfterOpenBracket is not taking account the spaces inserted by
AlignConsecutiveAssignments, so you end up with stuff like this:
```c
bleeblah = 1;
blah = function(arg1,
arg2,
arg3);
// ^--- these args 4-left from where they should be.
```
VSCode, meanwhile, somehow fixes this whitespace issue so code that is
correctly formatted by VSCode doesn't have this bug, meaning that:
1. The clang-format check in GH Actions fails.
2. We'd all have to stop using format-on-save in VSCode and accept the
bug if we wanted those GH Actions tests to pass.
Adding an empty line before variable assignments from multi-line
function calls evades the buggy behavior.
This commit should resolve the clang-format github action test failures,
for now.
2022-03-10 20:55:13 -08:00
|
|
|
} else if (optget(opts, "memory")->enabled) {
|
|
|
|
/* scan only memory */
|
|
|
|
ret = scan_memory(engine, opts, &options);
|
2014-09-11 14:15:36 -04:00
|
|
|
|
Code cleanup: Refactor to clean up formatting issues
Refactored the clamscan code that determines 'what to scan' in order
to clean up some very messy logic and also to get around a difference in
how vscode and clang-format handle formatting #ifdef blocks in the
middle of an else/if.
In addition to refactoring, there is a slight behavior improvement. With
this change, doing `clamscan blah -` will now scan `blah` and then also
scan `stdin`. You can even do `clamscan - blah` to now scan `stdin` and
then scan `blah`. Before, The `-` had to be the only "filename" argument
in order to scan from stdin.
In addition, added a bunch of extra empty lines or changing multi-line
function calls to single-line function calls in order to get around a
bug in clang-format with these two options do not playing nice together:
- AlignConsecutiveAssignments: true
- AlignAfterOpenBracket: true
AlignAfterOpenBracket is not taking account the spaces inserted by
AlignConsecutiveAssignments, so you end up with stuff like this:
```c
bleeblah = 1;
blah = function(arg1,
arg2,
arg3);
// ^--- these args 4-left from where they should be.
```
VSCode, meanwhile, somehow fixes this whitespace issue so code that is
correctly formatted by VSCode doesn't have this bug, meaning that:
1. The clang-format check in GH Actions fails.
2. We'd all have to stop using format-on-save in VSCode and accept the
bug if we wanted those GH Actions tests to pass.
Adding an empty line before variable assignments from multi-line
function calls evades the buggy behavior.
This commit should resolve the clang-format github action test failures,
for now.
2022-03-10 20:55:13 -08:00
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
/* No list of files provided to scan, and no request to scan memory,
|
|
|
|
* so just scan the current directory. */
|
|
|
|
char cwd[1024];
|
|
|
|
|
2023-11-26 15:01:19 -08:00
|
|
|
/* Get the current working directory.
|
Code cleanup: Refactor to clean up formatting issues
Refactored the clamscan code that determines 'what to scan' in order
to clean up some very messy logic and also to get around a difference in
how vscode and clang-format handle formatting #ifdef blocks in the
middle of an else/if.
In addition to refactoring, there is a slight behavior improvement. With
this change, doing `clamscan blah -` will now scan `blah` and then also
scan `stdin`. You can even do `clamscan - blah` to now scan `stdin` and
then scan `blah`. Before, The `-` had to be the only "filename" argument
in order to scan from stdin.
In addition, added a bunch of extra empty lines or changing multi-line
function calls to single-line function calls in order to get around a
bug in clang-format with these two options do not playing nice together:
- AlignConsecutiveAssignments: true
- AlignAfterOpenBracket: true
AlignAfterOpenBracket is not taking account the spaces inserted by
AlignConsecutiveAssignments, so you end up with stuff like this:
```c
bleeblah = 1;
blah = function(arg1,
arg2,
arg3);
// ^--- these args 4-left from where they should be.
```
VSCode, meanwhile, somehow fixes this whitespace issue so code that is
correctly formatted by VSCode doesn't have this bug, meaning that:
1. The clang-format check in GH Actions fails.
2. We'd all have to stop using format-on-save in VSCode and accept the
bug if we wanted those GH Actions tests to pass.
Adding an empty line before variable assignments from multi-line
function calls evades the buggy behavior.
This commit should resolve the clang-format github action test failures,
for now.
2022-03-10 20:55:13 -08:00
|
|
|
* we need full path for some reasons (eg. archive handling) */
|
|
|
|
if (!getcwd(cwd, sizeof(cwd))) {
|
|
|
|
logg(LOGG_ERROR, "Can't get absolute pathname of current working directory\n");
|
|
|
|
ret = 2;
|
|
|
|
} else {
|
|
|
|
CLAMSTAT(cwd, &sb);
|
|
|
|
scandirs(cwd, engine, opts, &options, 1, sb.st_dev);
|
2014-09-11 14:15:36 -04:00
|
|
|
}
|
Code cleanup: Refactor to clean up formatting issues
Refactored the clamscan code that determines 'what to scan' in order
to clean up some very messy logic and also to get around a difference in
how vscode and clang-format handle formatting #ifdef blocks in the
middle of an else/if.
In addition to refactoring, there is a slight behavior improvement. With
this change, doing `clamscan blah -` will now scan `blah` and then also
scan `stdin`. You can even do `clamscan - blah` to now scan `stdin` and
then scan `blah`. Before, The `-` had to be the only "filename" argument
in order to scan from stdin.
In addition, added a bunch of extra empty lines or changing multi-line
function calls to single-line function calls in order to get around a
bug in clang-format with these two options do not playing nice together:
- AlignConsecutiveAssignments: true
- AlignAfterOpenBracket: true
AlignAfterOpenBracket is not taking account the spaces inserted by
AlignConsecutiveAssignments, so you end up with stuff like this:
```c
bleeblah = 1;
blah = function(arg1,
arg2,
arg3);
// ^--- these args 4-left from where they should be.
```
VSCode, meanwhile, somehow fixes this whitespace issue so code that is
correctly formatted by VSCode doesn't have this bug, meaning that:
1. The clang-format check in GH Actions fails.
2. We'd all have to stop using format-on-save in VSCode and accept the
bug if we wanted those GH Actions tests to pass.
Adding an empty line before variable assignments from multi-line
function calls evades the buggy behavior.
This commit should resolve the clang-format github action test failures,
for now.
2022-03-10 20:55:13 -08:00
|
|
|
}
|
2003-07-29 15:48:06 +00:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
if ((opt = optget(opts, "statistics"))->enabled) {
|
|
|
|
while (opt) {
|
|
|
|
if (!strcasecmp(opt->strarg, "bytecode")) {
|
|
|
|
cli_sigperf_print();
|
|
|
|
cli_sigperf_events_destroy();
|
2024-03-25 10:32:38 -04:00
|
|
|
} else if (!strcasecmp(opt->strarg, "pcre")) {
|
2018-12-03 12:40:13 -05:00
|
|
|
cli_pcre_perf_print();
|
|
|
|
cli_pcre_perf_events_destroy();
|
|
|
|
}
|
2024-03-25 10:32:38 -04:00
|
|
|
|
2018-12-03 12:40:13 -05:00
|
|
|
opt = opt->nextarg;
|
2014-09-16 14:44:50 -04:00
|
|
|
}
|
2012-12-05 15:48:52 -08:00
|
|
|
}
|
|
|
|
|
2021-07-16 11:47:23 -07:00
|
|
|
done:
|
2007-01-30 21:11:32 +00:00
|
|
|
/* free the engine */
|
2008-11-12 16:19:43 +00:00
|
|
|
cl_engine_free(engine);
|
2003-07-29 15:48:06 +00:00
|
|
|
|
2010-02-04 21:33:03 +01:00
|
|
|
/* overwrite return code - infection takes priority */
|
2018-12-03 12:40:13 -05:00
|
|
|
if (info.ifiles)
|
2014-09-11 14:15:36 -04:00
|
|
|
ret = 1;
|
2018-12-03 12:40:13 -05:00
|
|
|
else if (info.errors)
|
2014-09-11 14:15:36 -04:00
|
|
|
ret = 2;
|
2003-07-29 15:48:06 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|