clamav/clamdscan/client.c

499 lines
15 KiB
C
Raw Permalink Normal View History

2003-07-29 15:48:06 +00:00
/*
2025-02-14 10:24:30 -05:00
* Copyright (C) 2013-2025 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2009-2013 Sourcefire, Inc.
*
* Authors: Tomasz Kojm, aCaB
2003-07-29 15:48:06 +00:00
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
2003-07-29 15:48:06 +00:00
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
2003-07-29 15:48:06 +00:00
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
2003-07-29 15:48:06 +00:00
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
2009-02-12 17:31:09 +00:00
#endif
#include <string.h>
2003-07-29 15:48:06 +00:00
#include <sys/types.h>
2004-03-29 00:00:58 +00:00
#include <sys/stat.h>
#ifdef HAVE_SYS_LIMITS_H
#include <sys/limits.h>
#endif
2009-09-24 16:08:52 +02:00
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
2009-09-24 16:08:52 +02:00
#endif
2009-09-24 19:23:21 +02:00
#ifndef _WIN32
#include <sys/socket.h>
2003-07-29 15:48:06 +00:00
#include <sys/un.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <utime.h>
2009-09-24 19:23:21 +02:00
#endif
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
2003-07-29 15:48:06 +00:00
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
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 "str.h"
#include "others.h"
// 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 "output.h"
#include "misc.h"
#include "actions.h"
#include "clamdcom.h"
#ifdef _WIN32
#include "scanmem.h"
#endif
#include "client.h"
#include "proto.h"
2003-07-29 15:48:06 +00:00
unsigned long int maxstream;
extern struct optstruct *clamdopts;
/* Inits the communication layer
* Returns 0 if clamd is local, non zero if clamd is remote */
static int isremote(const struct optstruct *opts)
{
int s, ret;
const struct optstruct *opt;
2014-01-26 14:58:21 -05:00
char *ipaddr, port[10];
struct addrinfo hints, *info, *p;
int res;
UNUSEDPARAM(opts);
2009-09-24 16:08:52 +02:00
#ifndef _WIN32
if ((opt = optget(clamdopts, "LocalSocket"))->enabled) {
2014-01-25 21:50:33 -05:00
memset((void *)&nixsock, 0, sizeof(nixsock));
nixsock.sun_family = AF_UNIX;
strncpy(nixsock.sun_path, opt->strarg, sizeof(nixsock.sun_path));
nixsock.sun_path[sizeof(nixsock.sun_path) - 1] = '\0';
2014-01-25 21:50:33 -05:00
return 0;
}
2009-09-24 16:08:52 +02:00
#endif
if (!(opt = optget(clamdopts, "TCPSocket"))->enabled)
2014-01-25 21:50:33 -05:00
return 0;
2014-01-26 14:58:21 -05:00
snprintf(port, sizeof(port), "%lld", optget(clamdopts, "TCPSocket")->numarg);
2014-01-25 21:50:33 -05:00
opt = optget(clamdopts, "TCPAddr");
while (opt) {
ipaddr = NULL;
if (opt->strarg)
2014-01-26 14:58:21 -05:00
ipaddr = (!strcmp(opt->strarg, "any") ? NULL : opt->strarg);
memset(&hints, 0x00, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
2014-01-26 14:58:21 -05:00
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
2014-01-26 14:58:21 -05:00
if ((res = getaddrinfo(ipaddr, port, &hints, &info))) {
logg(LOGG_ERROR, "Can't lookup clamd hostname: %s\n", gai_strerror(res));
opt = opt->nextarg;
2014-01-26 14:58:21 -05:00
continue;
2014-01-25 21:50:33 -05:00
}
2014-01-26 14:58:21 -05:00
for (p = info; p != NULL; p = p->ai_next) {
if ((s = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) {
logg(LOGG_INFO, "isremote: socket() returning: %s.\n", strerror(errno));
2014-01-26 14:58:21 -05:00
continue;
}
switch (p->ai_family) {
case AF_INET:
((struct sockaddr_in *)(p->ai_addr))->sin_port = htons(INADDR_ANY);
break;
case AF_INET6:
((struct sockaddr_in6 *)(p->ai_addr))->sin6_port = htons(INADDR_ANY);
break;
default:
break;
}
ret = bind(s, p->ai_addr, p->ai_addrlen);
2014-01-26 14:58:21 -05:00
if (ret) {
if (errno == EADDRINUSE) {
2020-01-03 15:44:07 -05:00
/*
* If we can't bind, then either we're attempting to listen on an IP that isn't
* ours or that clamd is already listening on.
*/
closesocket(s);
freeaddrinfo(info);
return 0;
}
closesocket(s);
2014-01-26 14:58:21 -05:00
freeaddrinfo(info);
return 1;
}
closesocket(s);
2014-01-25 21:50:33 -05:00
}
2014-01-26 14:58:21 -05:00
freeaddrinfo(info);
2014-01-25 21:50:33 -05:00
opt = opt->nextarg;
}
2014-01-26 14:58:21 -05:00
return 0;
}
/* pings clamd at the specified interval the number of time specified
2023-11-26 15:01:19 -08:00
* return 0 on a successful connection, 1 upon timeout, -1 on error */
int16_t ping_clamd(const struct optstruct *opts)
{
uint64_t attempts = 0;
uint64_t interval = 0;
char *attempt_str = NULL;
char *interval_str = NULL;
char *errchk = NULL;
uint64_t i = 0;
const struct optstruct *opt = NULL;
int64_t sockd = -1;
struct RCVLN rcv;
uint16_t ret = 0;
if (opts == NULL) {
logg(LOGG_ERROR, "null parameter was passed\n");
ret = -1;
goto done;
}
/* ping command takes the form --ping [attempts[:interval]] */
if (NULL != (opt = optget(opts, "ping"))) {
if (NULL != opt->strarg) {
if (NULL == (attempt_str = cli_safer_strdup(opt->strarg))) {
logg(LOGG_ERROR, "could not allocate memory for string\n");
ret = -1;
goto done;
}
interval_str = strchr(attempt_str, ':');
if ((NULL != interval_str) && (interval_str[0] != '\0')) {
interval_str[0] = '\0';
interval_str++;
interval = cli_strntoul(interval_str, strlen(interval_str), &errchk, 10);
if (interval_str + strlen(interval_str) > errchk) {
logg(LOGG_WARNING, "interval_str would go past end of buffer\n");
ret = -1;
goto done;
}
} else {
interval = CLAMDSCAN_DEFAULT_PING_INTERVAL;
}
attempts = cli_strntoul(attempt_str, strlen(attempt_str), &errchk, 10);
if (attempt_str + strlen(attempt_str) > errchk) {
2024-01-19 09:08:36 -08:00
logg(LOGG_WARNING, "attempt_str would go past end of buffer\n");
ret = -1;
goto done;
}
} else {
attempts = CLAMDSCAN_DEFAULT_PING_ATTEMPTS;
interval = CLAMDSCAN_DEFAULT_PING_INTERVAL;
}
}
isremote(opts);
do {
if ((sockd = dconnect(clamdopts)) >= 0) {
const char zPING[] = "zPING";
recvlninit(&rcv, sockd);
if (sendln(sockd, zPING, sizeof(zPING))) {
logg(LOGG_DEBUG, "PING failed...\n");
closesocket(sockd);
sockd = -1;
} else {
if (!optget(opts, "wait")->enabled) {
logg(LOGG_INFO, "PONG\n");
}
ret = 0;
goto done;
}
}
if (i + 1 < attempts) {
clamonacc: Reduce warning log verbosity Users have complained about two specific log events that are extremely verbose in non-critical error conditions: - clamonacc reports "ERROR: Can't send to clamd: Bad address" This may occur when small files are created/destroyed before they can be sent to be scanned. The log message probably should only be reported in verbose mode. - clamonacc reports "ClamMisc: $/proc/XXX vanished before UIDs could be excluded; scanning anyway" This may occur when a process that accessed a file exits before clamonacc find out who accessed the file. This is a fairly frequent occurence. It can still be problematic if `clamd` was the process which accessed the file (like a clamd temp file if watching /tmp), generally it's not an issue and we want to silently scan it anyways. Also addressed copypaste issue in onas_send_stream() wherein fd is set to 0 (aka STDIN) if the provided fd == 0 (should've been -1 for invalid FD) and if filename == NULL. In fact clamonacc never scans STDIN so the scan should fail if filename == NULL and the provided FD is invalid (-1). I also found that "Access denied. ERROR" is easily provoked when using --fdpass or --stream using this simple script: for i in {1..5000}; do echo "blah $i" > tmp-$i && rm tmp-$i; done Clamdscan does not allow for scans to fail quietly because the file does not exist, but for clamonacc it's a common thing and we don't want to output an error. To solve this, I changed it so a return length of -1 will still result in an "internal error" message but return len 0 failures will be silently ignored. I've added a static variable to onas_client_scan() that keeps state in case clamd is stopped and started - that way it won't print an error message for every event when offline. Instead it will log an error for the first connection failure, and log again when the connection is re-established for a future scan. Calls to onas_client_scan() are already wrapped with the onas_scan_lock mutex so the static variable should be safe. Finally, there were a couple of error responses from clamd that can occur if the file isn't found which we want to silently ignore, so I've tweaked the code which checks for specific error messages to account for these.
2021-01-03 18:40:48 -08:00
if (optget(opts, "wait")->enabled) {
if (interval == 1)
logg(LOGG_DEBUG, "Could not connect, will try again in %lu second\n", interval);
clamonacc: Reduce warning log verbosity Users have complained about two specific log events that are extremely verbose in non-critical error conditions: - clamonacc reports "ERROR: Can't send to clamd: Bad address" This may occur when small files are created/destroyed before they can be sent to be scanned. The log message probably should only be reported in verbose mode. - clamonacc reports "ClamMisc: $/proc/XXX vanished before UIDs could be excluded; scanning anyway" This may occur when a process that accessed a file exits before clamonacc find out who accessed the file. This is a fairly frequent occurence. It can still be problematic if `clamd` was the process which accessed the file (like a clamd temp file if watching /tmp), generally it's not an issue and we want to silently scan it anyways. Also addressed copypaste issue in onas_send_stream() wherein fd is set to 0 (aka STDIN) if the provided fd == 0 (should've been -1 for invalid FD) and if filename == NULL. In fact clamonacc never scans STDIN so the scan should fail if filename == NULL and the provided FD is invalid (-1). I also found that "Access denied. ERROR" is easily provoked when using --fdpass or --stream using this simple script: for i in {1..5000}; do echo "blah $i" > tmp-$i && rm tmp-$i; done Clamdscan does not allow for scans to fail quietly because the file does not exist, but for clamonacc it's a common thing and we don't want to output an error. To solve this, I changed it so a return length of -1 will still result in an "internal error" message but return len 0 failures will be silently ignored. I've added a static variable to onas_client_scan() that keeps state in case clamd is stopped and started - that way it won't print an error message for every event when offline. Instead it will log an error for the first connection failure, and log again when the connection is re-established for a future scan. Calls to onas_client_scan() are already wrapped with the onas_scan_lock mutex so the static variable should be safe. Finally, there were a couple of error responses from clamd that can occur if the file isn't found which we want to silently ignore, so I've tweaked the code which checks for specific error messages to account for these.
2021-01-03 18:40:48 -08:00
else
logg(LOGG_DEBUG, "Could not connect, will try again in %lu seconds\n", interval);
clamonacc: Reduce warning log verbosity Users have complained about two specific log events that are extremely verbose in non-critical error conditions: - clamonacc reports "ERROR: Can't send to clamd: Bad address" This may occur when small files are created/destroyed before they can be sent to be scanned. The log message probably should only be reported in verbose mode. - clamonacc reports "ClamMisc: $/proc/XXX vanished before UIDs could be excluded; scanning anyway" This may occur when a process that accessed a file exits before clamonacc find out who accessed the file. This is a fairly frequent occurence. It can still be problematic if `clamd` was the process which accessed the file (like a clamd temp file if watching /tmp), generally it's not an issue and we want to silently scan it anyways. Also addressed copypaste issue in onas_send_stream() wherein fd is set to 0 (aka STDIN) if the provided fd == 0 (should've been -1 for invalid FD) and if filename == NULL. In fact clamonacc never scans STDIN so the scan should fail if filename == NULL and the provided FD is invalid (-1). I also found that "Access denied. ERROR" is easily provoked when using --fdpass or --stream using this simple script: for i in {1..5000}; do echo "blah $i" > tmp-$i && rm tmp-$i; done Clamdscan does not allow for scans to fail quietly because the file does not exist, but for clamonacc it's a common thing and we don't want to output an error. To solve this, I changed it so a return length of -1 will still result in an "internal error" message but return len 0 failures will be silently ignored. I've added a static variable to onas_client_scan() that keeps state in case clamd is stopped and started - that way it won't print an error message for every event when offline. Instead it will log an error for the first connection failure, and log again when the connection is re-established for a future scan. Calls to onas_client_scan() are already wrapped with the onas_scan_lock mutex so the static variable should be safe. Finally, there were a couple of error responses from clamd that can occur if the file isn't found which we want to silently ignore, so I've tweaked the code which checks for specific error messages to account for these.
2021-01-03 18:40:48 -08:00
} else {
if (interval == 1)
logg(LOGG_INFO, "Could not connect, will PING again in %lu second\n", interval);
clamonacc: Reduce warning log verbosity Users have complained about two specific log events that are extremely verbose in non-critical error conditions: - clamonacc reports "ERROR: Can't send to clamd: Bad address" This may occur when small files are created/destroyed before they can be sent to be scanned. The log message probably should only be reported in verbose mode. - clamonacc reports "ClamMisc: $/proc/XXX vanished before UIDs could be excluded; scanning anyway" This may occur when a process that accessed a file exits before clamonacc find out who accessed the file. This is a fairly frequent occurence. It can still be problematic if `clamd` was the process which accessed the file (like a clamd temp file if watching /tmp), generally it's not an issue and we want to silently scan it anyways. Also addressed copypaste issue in onas_send_stream() wherein fd is set to 0 (aka STDIN) if the provided fd == 0 (should've been -1 for invalid FD) and if filename == NULL. In fact clamonacc never scans STDIN so the scan should fail if filename == NULL and the provided FD is invalid (-1). I also found that "Access denied. ERROR" is easily provoked when using --fdpass or --stream using this simple script: for i in {1..5000}; do echo "blah $i" > tmp-$i && rm tmp-$i; done Clamdscan does not allow for scans to fail quietly because the file does not exist, but for clamonacc it's a common thing and we don't want to output an error. To solve this, I changed it so a return length of -1 will still result in an "internal error" message but return len 0 failures will be silently ignored. I've added a static variable to onas_client_scan() that keeps state in case clamd is stopped and started - that way it won't print an error message for every event when offline. Instead it will log an error for the first connection failure, and log again when the connection is re-established for a future scan. Calls to onas_client_scan() are already wrapped with the onas_scan_lock mutex so the static variable should be safe. Finally, there were a couple of error responses from clamd that can occur if the file isn't found which we want to silently ignore, so I've tweaked the code which checks for specific error messages to account for these.
2021-01-03 18:40:48 -08:00
else
logg(LOGG_INFO, "Could not connect, will PING again in %lu seconds\n", interval);
clamonacc: Reduce warning log verbosity Users have complained about two specific log events that are extremely verbose in non-critical error conditions: - clamonacc reports "ERROR: Can't send to clamd: Bad address" This may occur when small files are created/destroyed before they can be sent to be scanned. The log message probably should only be reported in verbose mode. - clamonacc reports "ClamMisc: $/proc/XXX vanished before UIDs could be excluded; scanning anyway" This may occur when a process that accessed a file exits before clamonacc find out who accessed the file. This is a fairly frequent occurence. It can still be problematic if `clamd` was the process which accessed the file (like a clamd temp file if watching /tmp), generally it's not an issue and we want to silently scan it anyways. Also addressed copypaste issue in onas_send_stream() wherein fd is set to 0 (aka STDIN) if the provided fd == 0 (should've been -1 for invalid FD) and if filename == NULL. In fact clamonacc never scans STDIN so the scan should fail if filename == NULL and the provided FD is invalid (-1). I also found that "Access denied. ERROR" is easily provoked when using --fdpass or --stream using this simple script: for i in {1..5000}; do echo "blah $i" > tmp-$i && rm tmp-$i; done Clamdscan does not allow for scans to fail quietly because the file does not exist, but for clamonacc it's a common thing and we don't want to output an error. To solve this, I changed it so a return length of -1 will still result in an "internal error" message but return len 0 failures will be silently ignored. I've added a static variable to onas_client_scan() that keeps state in case clamd is stopped and started - that way it won't print an error message for every event when offline. Instead it will log an error for the first connection failure, and log again when the connection is re-established for a future scan. Calls to onas_client_scan() are already wrapped with the onas_scan_lock mutex so the static variable should be safe. Finally, there were a couple of error responses from clamd that can occur if the file isn't found which we want to silently ignore, so I've tweaked the code which checks for specific error messages to account for these.
2021-01-03 18:40:48 -08:00
}
sleep(interval);
}
i++;
} while (i < attempts);
/* timed out */
ret = 1;
clamonacc: Reduce warning log verbosity Users have complained about two specific log events that are extremely verbose in non-critical error conditions: - clamonacc reports "ERROR: Can't send to clamd: Bad address" This may occur when small files are created/destroyed before they can be sent to be scanned. The log message probably should only be reported in verbose mode. - clamonacc reports "ClamMisc: $/proc/XXX vanished before UIDs could be excluded; scanning anyway" This may occur when a process that accessed a file exits before clamonacc find out who accessed the file. This is a fairly frequent occurence. It can still be problematic if `clamd` was the process which accessed the file (like a clamd temp file if watching /tmp), generally it's not an issue and we want to silently scan it anyways. Also addressed copypaste issue in onas_send_stream() wherein fd is set to 0 (aka STDIN) if the provided fd == 0 (should've been -1 for invalid FD) and if filename == NULL. In fact clamonacc never scans STDIN so the scan should fail if filename == NULL and the provided FD is invalid (-1). I also found that "Access denied. ERROR" is easily provoked when using --fdpass or --stream using this simple script: for i in {1..5000}; do echo "blah $i" > tmp-$i && rm tmp-$i; done Clamdscan does not allow for scans to fail quietly because the file does not exist, but for clamonacc it's a common thing and we don't want to output an error. To solve this, I changed it so a return length of -1 will still result in an "internal error" message but return len 0 failures will be silently ignored. I've added a static variable to onas_client_scan() that keeps state in case clamd is stopped and started - that way it won't print an error message for every event when offline. Instead it will log an error for the first connection failure, and log again when the connection is re-established for a future scan. Calls to onas_client_scan() are already wrapped with the onas_scan_lock mutex so the static variable should be safe. Finally, there were a couple of error responses from clamd that can occur if the file isn't found which we want to silently ignore, so I've tweaked the code which checks for specific error messages to account for these.
2021-01-03 18:40:48 -08:00
if (optget(opts, "wait")->enabled) {
logg(LOGG_INFO, "Wait timeout exceeded; Could not connect to clamd\n");
clamonacc: Reduce warning log verbosity Users have complained about two specific log events that are extremely verbose in non-critical error conditions: - clamonacc reports "ERROR: Can't send to clamd: Bad address" This may occur when small files are created/destroyed before they can be sent to be scanned. The log message probably should only be reported in verbose mode. - clamonacc reports "ClamMisc: $/proc/XXX vanished before UIDs could be excluded; scanning anyway" This may occur when a process that accessed a file exits before clamonacc find out who accessed the file. This is a fairly frequent occurence. It can still be problematic if `clamd` was the process which accessed the file (like a clamd temp file if watching /tmp), generally it's not an issue and we want to silently scan it anyways. Also addressed copypaste issue in onas_send_stream() wherein fd is set to 0 (aka STDIN) if the provided fd == 0 (should've been -1 for invalid FD) and if filename == NULL. In fact clamonacc never scans STDIN so the scan should fail if filename == NULL and the provided FD is invalid (-1). I also found that "Access denied. ERROR" is easily provoked when using --fdpass or --stream using this simple script: for i in {1..5000}; do echo "blah $i" > tmp-$i && rm tmp-$i; done Clamdscan does not allow for scans to fail quietly because the file does not exist, but for clamonacc it's a common thing and we don't want to output an error. To solve this, I changed it so a return length of -1 will still result in an "internal error" message but return len 0 failures will be silently ignored. I've added a static variable to onas_client_scan() that keeps state in case clamd is stopped and started - that way it won't print an error message for every event when offline. Instead it will log an error for the first connection failure, and log again when the connection is re-established for a future scan. Calls to onas_client_scan() are already wrapped with the onas_scan_lock mutex so the static variable should be safe. Finally, there were a couple of error responses from clamd that can occur if the file isn't found which we want to silently ignore, so I've tweaked the code which checks for specific error messages to account for these.
2021-01-03 18:40:48 -08:00
} else {
logg(LOGG_INFO, "PING timeout exceeded; No response from clamd\n");
clamonacc: Reduce warning log verbosity Users have complained about two specific log events that are extremely verbose in non-critical error conditions: - clamonacc reports "ERROR: Can't send to clamd: Bad address" This may occur when small files are created/destroyed before they can be sent to be scanned. The log message probably should only be reported in verbose mode. - clamonacc reports "ClamMisc: $/proc/XXX vanished before UIDs could be excluded; scanning anyway" This may occur when a process that accessed a file exits before clamonacc find out who accessed the file. This is a fairly frequent occurence. It can still be problematic if `clamd` was the process which accessed the file (like a clamd temp file if watching /tmp), generally it's not an issue and we want to silently scan it anyways. Also addressed copypaste issue in onas_send_stream() wherein fd is set to 0 (aka STDIN) if the provided fd == 0 (should've been -1 for invalid FD) and if filename == NULL. In fact clamonacc never scans STDIN so the scan should fail if filename == NULL and the provided FD is invalid (-1). I also found that "Access denied. ERROR" is easily provoked when using --fdpass or --stream using this simple script: for i in {1..5000}; do echo "blah $i" > tmp-$i && rm tmp-$i; done Clamdscan does not allow for scans to fail quietly because the file does not exist, but for clamonacc it's a common thing and we don't want to output an error. To solve this, I changed it so a return length of -1 will still result in an "internal error" message but return len 0 failures will be silently ignored. I've added a static variable to onas_client_scan() that keeps state in case clamd is stopped and started - that way it won't print an error message for every event when offline. Instead it will log an error for the first connection failure, and log again when the connection is re-established for a future scan. Calls to onas_client_scan() are already wrapped with the onas_scan_lock mutex so the static variable should be safe. Finally, there were a couple of error responses from clamd that can occur if the file isn't found which we want to silently ignore, so I've tweaked the code which checks for specific error messages to account for these.
2021-01-03 18:40:48 -08:00
}
done:
if (sockd >= 0) {
closesocket(sockd);
}
if (attempt_str) {
free(attempt_str);
}
attempt_str = NULL;
interval_str = NULL;
errchk = NULL;
return ret;
}
/* Turns a relative path into an absolute one
2020-01-03 15:44:07 -05:00
* Returns a pointer to the path (which must be
* freed by the caller) or NULL on error */
static char *makeabs(const char *basepath)
{
int namelen;
char *ret;
if (!(ret = malloc(PATH_MAX + 1))) {
logg(LOGG_WARNING, "Can't make room for fullpath.\n");
return NULL;
}
if (!cli_is_abspath(basepath)) {
if (!getcwd(ret, PATH_MAX)) {
logg(LOGG_WARNING, "Can't get absolute pathname of current working directory.\n");
free(ret);
return NULL;
}
#ifdef _WIN32
if (*basepath == '\\') {
namelen = 2;
basepath++;
} else
#endif
namelen = strlen(ret);
snprintf(&ret[namelen], PATH_MAX - namelen, PATHSEP "%s", basepath);
2003-07-29 15:48:06 +00:00
} else {
strncpy(ret, basepath, PATH_MAX);
2003-07-29 15:48:06 +00:00
}
ret[PATH_MAX] = '\0';
return ret;
}
2003-07-29 15:48:06 +00:00
/* Recursively scans a path with the given scantype
* Returns non zero for serious errors, zero otherwise */
static int client_scan(const char *file, int scantype, int *infected, int *err, int maxlevel, int session, int flags)
{
int ret;
ClamDScan: --fdpass/--stream leak; ExcludePath issues ClamDScan will leak the memory for the scan target filename if using `--fdpass` or using `--stream`. This commit fixes that leak. Resolves: https://bugzilla.clamav.net/show_bug.cgi?id=12648 ClamDScan will fail to scan any file after running into an "ExcludePath" exclusion when using `--fdpass` or `--stream` AND --multiscan (-m). The issue is because the parallel_callback() callback function used by file tree walk (ftw) feature returns an error code for excluded files rather than "success". Memory for the accidentally-excluded paths for a given directory also appears to be leaked. This commit resolves this accidental-abort issue and the memory leak. There was an additional single file path memory leak when using `--fdpass` caused by bad error handling in `cli_ftw()`. This was fixed by removing the confusing ternaries, and using separate pointers for each filename copy. ClamDScan with ExcludePath regex may fail to exclude absolute paths when performing relative scans because the exclude-check function may match using provided relative path (E.g. `/some/path/../another/path`) rather than an absolute path (E.g. `/some/path/another/path`). This issue is resolved by getting the real path at the start of the scan, eliminating `.` and `..` relative pathing from all filepaths. TODO 1: In addition to being recursive (bad for stack safety), the File Tree Walk (FTW) implementation is a spaghetti code and should be refactored. TODO 2: ExcludePath will print out "Excluded" for each path that is excluded when using `--fdpass` or `--stream`, and for each path directly scanned that is directly excluded. But in a recursive regular-scan, the "Excluded" message for the those paths is missing.
2021-06-29 17:27:07 -07:00
char *real_path = NULL;
char *fullpath = NULL;
/* Convert relative path to fullpath */
fullpath = makeabs(file);
/* Convert fullpath to the real path (evaluating symlinks and . and ..).
Doing this early on will ensure that the scan results will appear consistent
across regular scans, --fdpass scans, and --stream scans. */
if (CL_SUCCESS != cli_realpath(fullpath, &real_path)) {
logg(LOGG_DEBUG, "client_scan: Failed to determine real filename of %s.\n", fullpath);
ClamDScan: --fdpass/--stream leak; ExcludePath issues ClamDScan will leak the memory for the scan target filename if using `--fdpass` or using `--stream`. This commit fixes that leak. Resolves: https://bugzilla.clamav.net/show_bug.cgi?id=12648 ClamDScan will fail to scan any file after running into an "ExcludePath" exclusion when using `--fdpass` or `--stream` AND --multiscan (-m). The issue is because the parallel_callback() callback function used by file tree walk (ftw) feature returns an error code for excluded files rather than "success". Memory for the accidentally-excluded paths for a given directory also appears to be leaked. This commit resolves this accidental-abort issue and the memory leak. There was an additional single file path memory leak when using `--fdpass` caused by bad error handling in `cli_ftw()`. This was fixed by removing the confusing ternaries, and using separate pointers for each filename copy. ClamDScan with ExcludePath regex may fail to exclude absolute paths when performing relative scans because the exclude-check function may match using provided relative path (E.g. `/some/path/../another/path`) rather than an absolute path (E.g. `/some/path/another/path`). This issue is resolved by getting the real path at the start of the scan, eliminating `.` and `..` relative pathing from all filepaths. TODO 1: In addition to being recursive (bad for stack safety), the File Tree Walk (FTW) implementation is a spaghetti code and should be refactored. TODO 2: ExcludePath will print out "Excluded" for each path that is excluded when using `--fdpass` or `--stream`, and for each path directly scanned that is directly excluded. But in a recursive regular-scan, the "Excluded" message for the those paths is missing.
2021-06-29 17:27:07 -07:00
} else {
free(fullpath);
fullpath = real_path;
}
if (!fullpath)
return 0;
if (!session)
ret = serial_client_scan(fullpath, scantype, infected, err, maxlevel, flags);
else
ret = parallel_client_scan(fullpath, scantype, infected, err, maxlevel, flags);
free(fullpath);
return ret;
}
2003-07-29 15:48:06 +00:00
int get_clamd_version(const struct optstruct *opts)
{
char *buff;
int len, sockd;
struct RCVLN rcv;
const char zVERSION[] = "zVERSION";
isremote(opts);
if ((sockd = dconnect(clamdopts)) < 0) return 2;
recvlninit(&rcv, sockd);
if (sendln(sockd, zVERSION, sizeof(zVERSION))) {
closesocket(sockd);
return 2;
}
while ((len = recvln(&rcv, &buff, NULL))) {
if (len == -1) {
logg(LOGG_ERROR, "Error occurred while receiving version information.\n");
break;
}
clamd: Add options to toggle SHUTDOWN, RELOAD, STATS and VERSION (#1502) The `clamd` protocol lacks authentication or authorization controls needed to limit access to more administrative commands. Depending on your use case, disabling some commands like `SHUTDOWN` may improve the security of the scanning daemon. This commit adds options to enable/disable the `SHUTDOWN`, `RELOAD`, `STATS` and `VERSION` commands in `clamd.conf`. When a client sends one of the following commands but it is disabled, `clamd` will respond with "COMMAND UNAVAILABLE". The new `clamd.conf` options are: - `EnableShutdownCommand`: Enable the `SHUTDOWN` command. Setting this to no prevents a client to stop `clamd` via the protocol. Default: yes - `EnableReloadCommand` Enable the `RELOAD` command. Setting this to no prevents a client to reload the database. This disables Freshclam's `NotifyClamd` option. `clamd` monitors for database directory changes, so this should Default: yes - `EnableStatsCommand` Enable the `STATS` command. Setting this to no prevents a client from querying statistics. This disables the `clamdtop` program. Default: yes - `EnableVersionCommand` Enable the `VERSION` command. Setting this to no prevents a client from querying version information. This disables the `clamdtop` program and will cause `clamdscan` to display a warning when using the `--version` option. Default: yes Resolves: https://github.com/Cisco-Talos/clamav/issues/922 Resolves: https://github.com/Cisco-Talos/clamav/issues/1169 Related: https://github.com/Cisco-Talos/clamav/pull/347
2025-06-04 16:47:57 +02:00
/* Check if the response was "COMMAND UNAVAILABLE", which means that
clamd has the VERSION command disabled. */
if (len >= 19 && memcmp(buff, "COMMAND UNAVAILABLE", 19) == 0) {
logg(LOGG_WARNING, "VERSION command disabled in clamd, printing the local version.\n");
closesocket(sockd);
return 2;
}
printf("%s\n", buff);
}
2009-09-24 16:21:51 +02:00
closesocket(sockd);
return 0;
}
int reload_clamd_database(const struct optstruct *opts)
{
char *buff;
int len, sockd;
struct RCVLN rcv;
const char zRELOAD[] = "zRELOAD";
isremote(opts);
if ((sockd = dconnect(clamdopts)) < 0) return 2;
recvlninit(&rcv, sockd);
if (sendln(sockd, zRELOAD, sizeof(zRELOAD))) {
closesocket(sockd);
return 2;
}
if (!(len = recvln(&rcv, &buff, NULL)) || len < 10 || memcmp(buff, "RELOADING", 9)) {
logg(LOGG_ERROR, "Clamd did not reload the database\n");
closesocket(sockd);
return 2;
}
2009-09-24 16:21:51 +02:00
closesocket(sockd);
return 0;
}
2010-02-03 18:23:30 +01:00
int client(const struct optstruct *opts, int *infected, int *err)
{
int remote, scantype, session = 0, errors = 0, scandash = 0, maxrec, flags = 0;
const char *fname;
if (optget(opts, "wait")->enabled) {
int16_t ping_result = ping_clamd(opts);
switch (ping_result) {
case 0:
break;
case 1:
return (int)CL_ETIMEOUT;
default:
return (int)CL_ERROR;
}
}
scandash = (opts->filename && opts->filename[0] && !strcmp(opts->filename[0], "-") && !optget(opts, "file-list")->enabled && !opts->filename[1]);
remote = isremote(opts) | optget(opts, "stream")->enabled;
#ifdef HAVE_FD_PASSING
if (!remote && optget(clamdopts, "LocalSocket")->enabled && (optget(opts, "fdpass")->enabled || scandash)) {
scantype = FILDES;
session = optget(opts, "multiscan")->enabled;
} else
#endif
if (remote || scandash) {
scantype = STREAM;
session = optget(opts, "multiscan")->enabled;
} else if (optget(opts, "multiscan")->enabled)
scantype = MULTI;
else if (optget(opts, "allmatch")->enabled)
scantype = ALLMATCH;
else
scantype = CONT;
maxrec = optget(clamdopts, "MaxDirectoryRecursion")->numarg;
maxstream = optget(clamdopts, "StreamMaxLength")->numarg;
if (optget(clamdopts, "FollowDirectorySymlinks")->enabled)
flags |= CLI_FTW_FOLLOW_DIR_SYMLINK;
if (optget(clamdopts, "FollowFileSymlinks")->enabled)
flags |= CLI_FTW_FOLLOW_FILE_SYMLINK;
flags |= CLI_FTW_TRIM_SLASHES;
2003-07-29 15:48:06 +00:00
*infected = 0;
2004-03-29 00:00:58 +00:00
if (scandash) {
int sockd, ret;
STATBUF sb;
if (FSTAT(0, &sb) < 0) {
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
logg(LOGG_INFO, "client.c: fstat failed for file name \"%s\", with %s\n",
opts->filename[0], strerror(errno));
return 2;
}
if ((sb.st_mode & S_IFMT) != S_IFREG) scantype = STREAM;
if ((sockd = dconnect(clamdopts)) >= 0 && (ret = dsresult(sockd, scantype, NULL, &ret, NULL, clamdopts)) >= 0)
*infected = ret;
else
errors = 1;
if (sockd >= 0) closesocket(sockd);
} else if (opts->filename || optget(opts, "file-list")->enabled) {
if (opts->filename && optget(opts, "file-list")->enabled)
logg(LOGG_WARNING, "Only scanning files from --file-list (files passed at cmdline are ignored)\n");
while ((fname = filelist(opts, NULL))) {
if (!strcmp(fname, "-")) {
logg(LOGG_ERROR, "Scanning from standard input requires \"-\" to be the only file argument\n");
continue;
}
errors += client_scan(fname, scantype, infected, err, maxrec, session, flags);
/* this may be too strict
if(errors >= 10) {
logg(LOGG_ERROR, "Too many errors\n");
break;
}
*/
}
}
#ifdef _WIN32
else if (optget(opts, "memory")->enabled) {
struct mem_info minfo;
minfo.d = 1;
minfo.opts = opts;
minfo.ifiles = *infected;
minfo.errors = errors;
int res = scanmem(&minfo);
*infected = minfo.ifiles;
*err = minfo.errors;
}
#endif
else {
errors = client_scan("", scantype, infected, err, maxrec, session, flags);
2003-07-29 15:48:06 +00:00
}
return *infected ? 1 : (errors ? 2 : 0);
2003-07-29 15:48:06 +00:00
}