clamav/clamdtop/clamdtop.c

1668 lines
48 KiB
C
Raw Permalink Normal View History

/*
* ClamdTOP
*
2025-02-14 10:24:30 -05:00
* Copyright (C) 2013-2025 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
* Copyright (C) 2008-2013 Sourcefire, Inc.
*
* Authors: Török Edvin
*
* 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.
*
* 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.
*/
#define _GNU_SOURCE
#define __EXTENSIONS
#define GCC_PRINTF
#define GCC_SCANF
#ifdef HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include CURSES_INCLUDE
#include <time.h>
#include <ctype.h>
#include <signal.h>
#ifdef _WIN32
#include <windows.h>
#include <winsock2.h>
/* this is not correct, perhaps winsock errors are not mapped on errno */
#define herror perror
#else
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/time.h>
#endif
#include <assert.h>
#include <errno.h>
#include "platform.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
// libclamav
#include "clamav.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 "misc.h"
/* Types, prototypes and globals*/
typedef struct connection {
int sd;
char *remote;
int tcp;
struct timeval tv_conn;
char *version;
int line;
} conn_t;
struct global_stats {
struct task *tasks;
ssize_t n;
struct stats *all_stats;
size_t num_clamd;
conn_t *conn;
};
struct stats {
const char *remote;
char *engine_version;
char *db_version;
struct tm db_time;
const char *version;
int stats_unsupp;
uint8_t conn_hr, conn_min, conn_sec;
/* threads - primary */
unsigned prim_live, prim_idle, prim_max;
/* threads - sum */
unsigned live, idle, max;
/* queue */
unsigned biggest_queue, current_q;
double mem; /* in megabytes */
double heapu, mmapu, totalu, totalf, releasable, pools_used, pools_total;
unsigned pools_cnt;
};
static void cleanup(void);
static int send_string_noreconn(conn_t *conn, const char *cmd);
static void send_string(conn_t *conn, const char *cmd);
static int read_version(conn_t *conn);
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
static int check_stats_available(conn_t *conn);
char *get_ip(const char *ip);
char *get_port(const char *ip);
char *make_ip(const char *host, const char *port);
enum exit_reason {
FAIL_CMDLINE = 1,
FAIL_INITIAL_CONN,
OUT_OF_MEMORY,
RECONNECT_FAIL,
SIGINT_REASON
};
static void exit_program(enum exit_reason reason, const char *func, unsigned line);
#if __GNUC__ >= 3
#define EXIT_PROGRAM(r) exit_program(r, __PRETTY_FUNCTION__, __LINE__);
#else
#define EXIT_PROGRAM(r) exit_program(r, "<unknown>", __LINE__);
#endif
#define OOM_CHECK(p) \
do { \
if (!p) EXIT_PROGRAM(OUT_OF_MEMORY); \
} while (0)
static struct global_stats global;
static SCREEN *curses_scr = NULL;
static int curses_inited = 0;
static int maxystats = 0;
2008-11-13 16:00:10 +00:00
static int detail_selected = -1;
static int detail_exists(void)
2008-11-13 16:00:10 +00:00
{
return global.num_clamd != 1;
2008-11-13 16:00:10 +00:00
}
static int detail_is_selected(int idx)
{
if (!detail_exists()) {
assert(idx == 0);
return 1;
}
return idx == detail_selected;
2008-11-13 16:00:10 +00:00
}
/* ---------------------- NCurses routines -----------------*/
enum colors {
header_color = 1,
version_color,
error_color,
value_color,
descr_color,
selected_color,
queue_header_color,
activ_color,
dim_color,
red_color,
};
#define UPDATE_INTERVAL 2
#define MIN_INTERVAL 1
/* the default color of the terminal in ncurses */
#define DEFAULT_COLOR -1
#define VALUE_ATTR A_BOLD | COLOR_PAIR(value_color)
#define DESCR_ATTR COLOR_PAIR(descr_color)
#define ERROR_ATTR A_BOLD | COLOR_PAIR(error_color)
static WINDOW *header_window = NULL;
static WINDOW *stats_head_window = NULL;
static WINDOW *stats_window = NULL;
static WINDOW *status_bar_window = NULL;
static WINDOW *mem_window = NULL;
static char *status_bar_keys[10];
static unsigned maxy = 0, maxx = 0;
static char *queue_header = NULL;
static char *multi_queue_header = NULL;
static char *clamd_header = NULL;
2020-07-24 08:32:47 -07:00
#define CMDHEAD " COMMAND QUEUEDSINCE FILE"
#define CMDHEAD2 "NO COMMAND QUEUEDSINCE FILE"
/*
* CLAMD - which local/remote clamd this is
* CONNTIM - since when we are connected (TODO: zeroed at reconnect)
* QUEUE - no of items in queue (total)
* MAXQUEUE - max no of items in queue observed
* LIVETHR - sum of live threads
* IDLETHR - sum of idle threads
*/
#define SUMHEAD "NO CONNTIME LIV IDL QUEUE MAXQ MEM ENGINE DBVER DBTIME HOST"
static void resize(void)
{
char *p;
int new_maxy, new_maxx;
getmaxyx(stdscr, new_maxy, new_maxx);
if (new_maxy == -1 || new_maxx == -1) {
fprintf(stderr, "Failed to get terminal size\n");
return;
}
if ((unsigned int)new_maxy == maxy && (unsigned int)new_maxx == maxx) {
// no change
return;
}
maxx = (unsigned int)new_maxx;
maxy = (unsigned int)new_maxy;
free(queue_header);
free(clamd_header);
queue_header = malloc(maxx + 1);
OOM_CHECK(queue_header);
clamd_header = malloc(maxx + 1);
OOM_CHECK(clamd_header);
assert(clamd_header && queue_header);
strncpy(queue_header, CMDHEAD, maxx);
strncpy(clamd_header, SUMHEAD, maxx);
queue_header[maxx] = '\0';
clamd_header[maxx] = '\0';
2020-07-24 08:32:47 -07:00
p = queue_header + strlen(queue_header);
while (p < queue_header + maxx)
*p++ = ' ';
p = clamd_header + strlen(clamd_header);
while (p < clamd_header + maxx)
*p++ = ' ';
if (global.num_clamd > 1) {
free(multi_queue_header);
multi_queue_header = malloc(maxx + 1);
OOM_CHECK(multi_queue_header);
assert(multi_queue_header);
strncpy(multi_queue_header, CMDHEAD2, maxx);
multi_queue_header[maxx] = '\0';
2020-07-24 08:32:47 -07:00
p = multi_queue_header + strlen(multi_queue_header);
while (p < multi_queue_header + maxx)
*p++ = ' ';
}
}
static void rm_windows(void)
{
if (header_window) {
delwin(header_window);
header_window = NULL;
}
if (mem_window) {
delwin(mem_window);
mem_window = NULL;
}
if (stats_window) {
delwin(stats_window);
stats_window = NULL;
}
if (stats_head_window) {
delwin(stats_head_window);
stats_head_window = NULL;
}
if (status_bar_window) {
delwin(status_bar_window);
status_bar_window = NULL;
}
}
static void init_windows(int num_clamd)
{
resize();
rm_windows();
/* non-overlapping windows */
header_window = subwin(stdscr, 1, maxx, 0, 0);
stats_head_window = subwin(stdscr, num_clamd + 1, maxx, 1, 0);
maxystats = maxy - num_clamd - 3;
stats_window = subwin(stdscr, maxystats, maxx, num_clamd + 2, 0);
status_bar_window = subwin(stdscr, 1, maxx, maxy - 1, 0);
/* memwindow overlaps, used only in details mode */
mem_window = derwin(stats_window, 6, 41, 1, maxx - 41);
touchwin(stdscr);
werase(stdscr);
refresh();
memset(status_bar_keys, 0, sizeof(status_bar_keys));
status_bar_keys[0] = "H - help";
status_bar_keys[1] = "Q - quit";
status_bar_keys[2] = "R - reset maximums";
if (num_clamd > 1) {
status_bar_keys[3] = "^ - previous clamd";
status_bar_keys[4] = "v - next clamd";
}
}
static void init_ncurses(int num_clamd, int use_default)
{
int default_bg = use_default ? DEFAULT_COLOR : COLOR_BLACK;
int default_fg = use_default ? DEFAULT_COLOR : COLOR_WHITE;
/* newterm() allows us to free curses-allocated memory with delscreen() */
if (!(curses_scr = newterm(NULL, stdout, stdin))) {
fprintf(stderr, "Failed to initialize curses\n");
exit(EXIT_FAILURE);
}
curses_inited = 1;
start_color();
keypad(stdscr, TRUE); /* enable keyboard mapping */
nonl(); /* tell curses not to do NL->CR/NL on output */
halfdelay(UPDATE_INTERVAL * 10); /* timeout of 2s when waiting for input*/
2023-11-26 15:01:19 -08:00
noecho(); /* don't echo input */
curs_set(0); /* turn off cursor */
if (use_default)
use_default_colors();
init_pair(header_color, COLOR_BLACK, COLOR_WHITE);
init_pair(version_color, default_fg, default_bg);
init_pair(error_color, COLOR_WHITE, COLOR_RED);
init_pair(value_color, COLOR_GREEN, default_bg);
init_pair(descr_color, COLOR_CYAN, default_bg);
init_pair(selected_color, COLOR_BLACK, COLOR_CYAN);
init_pair(queue_header_color, COLOR_BLACK, COLOR_GREEN);
init_pair(activ_color, COLOR_MAGENTA, default_bg);
init_pair(dim_color, COLOR_GREEN, default_bg);
init_pair(red_color, COLOR_RED, default_bg);
init_windows(num_clamd);
}
static void win_start(WINDOW *win, enum colors col)
{
wattrset(win, COLOR_PAIR(col));
wbkgd(win, COLOR_PAIR(col));
werase(win);
}
static void print_colored(WINDOW *win, const char *p)
{
while (*p) {
wattron(win, DESCR_ATTR);
while (*p && !isdigit(*p))
waddch(win, *p++);
wattroff(win, DESCR_ATTR);
wattron(win, VALUE_ATTR);
while (*p && isdigit(*p))
waddch(win, *p++);
wattroff(win, VALUE_ATTR);
}
}
static void header(void)
{
size_t i, x = 0;
time_t t;
win_start(header_window, header_color);
mvwprintw(header_window, 0, 0, " ClamdTOP version %s ", get_version());
time(&t);
wprintw(header_window, "%s", ctime(&t));
wrefresh(header_window);
2020-07-24 08:32:47 -07:00
/*
win_start(version_window, version_color);
mvwprintw(version_window, 0, 0, "Connected to: ");
print_colored(version_window, clamd_version ? clamd_version : "Unknown");
wrefresh(version_window);
*/
werase(status_bar_window);
for (i = 0; i < sizeof(status_bar_keys) / sizeof(status_bar_keys[0]); i++) {
const char *s = status_bar_keys[i];
if (!s)
continue;
wattron(status_bar_window, A_REVERSE);
if (s[0] == '^') {
mvwaddch(status_bar_window, 0, x, ACS_UARROW);
s++;
x++;
} else if (s[0] == 'v') {
mvwaddch(status_bar_window, 0, x, ACS_DARROW);
s++;
x++;
}
mvwprintw(status_bar_window, 0, x, "%s", s);
wattroff(status_bar_window, A_REVERSE);
x += strlen(s) + 1;
}
wrefresh(status_bar_window);
}
static void show_bar(WINDOW *win, size_t i, unsigned live, unsigned idle,
unsigned max, int blink)
{
int y, x, z = 0;
unsigned len = 39;
unsigned start = 1;
unsigned activ = max ? ((live - idle) * (len - start - 2) + (max / 2)) / max : 0;
unsigned dim = max ? idle * (len - start - 2) / max : 0;
unsigned rem = len - activ - dim - start - 2;
assert(activ + 2 < len && activ + dim + 2 < len && activ + dim + rem + 2 < len && "Invalid values");
mvwaddch(win, i, start, '[' | A_BOLD);
wattron(win, A_BOLD | COLOR_PAIR(activ_color));
for (i = 0; i < activ; i++)
waddch(win, '|');
wattroff(win, A_BOLD | COLOR_PAIR(activ_color));
wattron(win, A_DIM | COLOR_PAIR(dim_color));
for (i = 0; i < dim; i++)
waddch(win, '|');
wattroff(win, A_DIM | COLOR_PAIR(dim_color));
for (i = 0; i < rem; i++)
waddch(win, ' ');
waddch(win, ']' | A_BOLD);
if (blink) {
getyx(win, y, x);
if ((x < 0) || (y < 0)) {
return; /* if getyx() failed, nevermind the blinking */
}
if (x >= 2) {
z = x - 2;
}
mvwaddch(win, y, z, '>' | A_BLINK | COLOR_PAIR(red_color));
move(y, z);
}
}
/* --------------------- Error handling ---------------------*/
static int normal_exit = 0;
static const char *exit_reason = NULL;
static const char *exit_func = NULL;
static unsigned exit_line = 0;
static void cleanup(void)
{
unsigned i;
if (curses_inited) {
if (status_bar_window) {
werase(status_bar_window);
wrefresh(status_bar_window);
}
rm_windows();
2020-01-03 15:53:29 -05:00
endwin();
delscreen(curses_scr);
}
curses_inited = 0;
for (i = 0; i < global.num_clamd; i++) {
if (global.conn[i].sd && global.conn[i].sd != -1) {
Fix some coverity warnings in clamdtop/clamdtop.c I had some time over the weekend and implemented fixes for warnings in clamdtop/clamdtop.c: - 279008 - In make_connection_real: Code can never be reached because of a logical contradiction (CWE-561). host and port are freed and set to NULL before they are used to set conn->remote. The fix is to cleanup host and port after conn->remote is set. - 147369 - In get_ip: Code can never be reached because of a logical contradiction (CWE-561). Removed an unnecessary return statement that could never be reached. - 147624 - In get_port: Leak of memory or pointers to system resources (CWE-404). dupip wasn’t being freed in the case where no port could be found. - 279007 - In make_connection_real: Pointer is checked against null but then dereferenced anyway (CWE-476). This function checked soname for NULL but wouldn’t fail in this case, and subsequent code would dereference it. The NULL check could just be removed, though, because the only calling function of this static function ensured that soname is not NULL. - 147316 - In cleanup: Value returned from a library function is not checked for errors before being used. This value may indicate an error condition. (CWE-252). The code ignores the value of send when exiting because at that point it doesn’t really matter if the send succeeds. The fix is to cast this function call to void to explicitly ignore the return value. - 147318 - In recv_line: Value returned from a library function is not checked for errors before being used. This value may indicate an error condition. (CWE-252) The code ignores the value of send when exiting because at that point it doesn’t really matter if the send succeeds. The fix is to cast this function call to void to explicitly ignore the return value.
2020-05-11 14:46:12 -04:00
(void)send_string_noreconn(&global.conn[i], "nEND\n");
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
#ifndef _WIN32
2020-01-03 15:53:29 -05:00
close(global.conn[i].sd);
#else
2020-01-03 15:53:29 -05:00
closesocket(global.conn[i].sd);
#endif
}
free(global.conn[i].version);
free(global.conn[i].remote);
}
free(global.all_stats);
free(global.conn);
free(queue_header);
if (global.num_clamd > 1)
free(multi_queue_header);
free(clamd_header);
if (!normal_exit) {
fprintf(stderr, "Abnormal program termination");
if (exit_reason) fprintf(stderr, ": %s", exit_reason);
if (exit_func) fprintf(stderr, " in %s", exit_func);
if (exit_line) fprintf(stderr, " at line %u", exit_line);
fputc('\n', stderr);
}
}
#ifdef __GNUC__
#define __noreturn __attribute__((noreturn))
#else
#define __noreturn
#endif
static void __noreturn exit_program(enum exit_reason reason, const char *func, unsigned line)
{
switch (reason) {
case FAIL_CMDLINE:
exit_reason = "Invalid command-line arguments";
break;
case FAIL_INITIAL_CONN:
exit_reason = "Unable to connect to all clamds";
break;
case OUT_OF_MEMORY:
exit_reason = "Out of memory";
break;
case RECONNECT_FAIL:
exit_reason = "Failed to reconnect to clamd after connection was lost";
break;
case SIGINT_REASON:
exit_reason = "User interrupt";
break;
default:
exit_reason = "Unknown";
break;
}
exit_func = func;
exit_line = line;
exit(reason);
}
struct task {
char *line;
double tim;
int clamd_no;
};
static int tasks_compare(const void *a, const void *b)
{
const struct task *ta = a;
const struct task *tb = b;
if (ta->tim < tb->tim)
return 1;
if (ta->tim > tb->tim)
return -1;
return 0;
}
/* ----------- Socket routines ----------------------- */
#ifdef __GNUC__
static void print_con_info(conn_t *conn, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
#endif
static void print_con_info(conn_t *conn, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (stats_head_window) {
char *buf = malloc(maxx + 1);
char *nl = NULL;
OOM_CHECK(buf);
memset(buf, ' ', maxx + 1);
vsnprintf(buf, maxx + 1, fmt, ap);
if ((nl = strrchr(buf, '\n')) != NULL)
*nl = ' ';
buf[strlen(buf)] = ' ';
buf[maxx] = '\0';
wattron(stats_head_window, ERROR_ATTR);
mvwprintw(stats_head_window, conn->line, 0, "%s", buf);
wattroff(stats_head_window, ERROR_ATTR);
wrefresh(stats_head_window);
free(buf);
} else
vfprintf(stdout, fmt, ap);
va_end(ap);
}
2014-02-20 12:13:47 -05:00
char *get_ip(const char *ip)
{
char *dupip = NULL;
2020-07-24 08:32:47 -07:00
char *p1 = NULL;
unsigned int i;
2014-02-20 12:13:47 -05:00
/*
* Expected format of ip:
* 1) IPv4
* 2) IPv4:Port
* 3) IPv6
* 4) [IPv6]:Port
2020-01-03 15:44:07 -05:00
*
2014-02-20 12:13:47 -05:00
* Use of IPv6:Port is incorrect. An IPv6 address must be enclosed in brackets.
*/
dupip = strdup(ip);
if (!(dupip))
return NULL;
if (dupip[0] == '[') {
/* IPv6 */
p1 = strchr(dupip, ']');
if (!(p1)) {
free(dupip);
return NULL;
}
*p1 = '\0';
p1 = strdup(dupip + 1);
free(dupip);
dupip = NULL;
return p1;
2014-02-20 12:13:47 -05:00
}
p1 = dupip;
i = 0;
2014-03-26 13:33:14 -04:00
while ((p1 = strchr(p1, ':'))) {
i++;
p1++;
2014-02-20 12:13:47 -05:00
}
if (i == 1) {
p1 = strchr(dupip, ':');
*p1 = '\0';
2014-02-20 12:13:47 -05:00
}
return dupip;
2014-02-20 12:13:47 -05:00
}
char *get_port(const char *ip)
{
char *dupip, *p;
unsigned int offset = 0;
2014-02-20 12:13:47 -05:00
dupip = get_ip(ip);
if (!(dupip))
return NULL;
if (ip[0] == '[')
offset += 2;
2014-03-26 13:33:14 -04:00
p = (char *)ip + strlen(dupip) + offset;
if (*p == ':') {
p = strdup(p + 1);
free(dupip);
return p;
}
2014-02-20 12:13:47 -05:00
Fix some coverity warnings in clamdtop/clamdtop.c I had some time over the weekend and implemented fixes for warnings in clamdtop/clamdtop.c: - 279008 - In make_connection_real: Code can never be reached because of a logical contradiction (CWE-561). host and port are freed and set to NULL before they are used to set conn->remote. The fix is to cleanup host and port after conn->remote is set. - 147369 - In get_ip: Code can never be reached because of a logical contradiction (CWE-561). Removed an unnecessary return statement that could never be reached. - 147624 - In get_port: Leak of memory or pointers to system resources (CWE-404). dupip wasn’t being freed in the case where no port could be found. - 279007 - In make_connection_real: Pointer is checked against null but then dereferenced anyway (CWE-476). This function checked soname for NULL but wouldn’t fail in this case, and subsequent code would dereference it. The NULL check could just be removed, though, because the only calling function of this static function ensured that soname is not NULL. - 147316 - In cleanup: Value returned from a library function is not checked for errors before being used. This value may indicate an error condition. (CWE-252). The code ignores the value of send when exiting because at that point it doesn’t really matter if the send succeeds. The fix is to cast this function call to void to explicitly ignore the return value. - 147318 - In recv_line: Value returned from a library function is not checked for errors before being used. This value may indicate an error condition. (CWE-252) The code ignores the value of send when exiting because at that point it doesn’t really matter if the send succeeds. The fix is to cast this function call to void to explicitly ignore the return value.
2020-05-11 14:46:12 -04:00
free(dupip);
return NULL;
2014-02-20 12:13:47 -05:00
}
char *make_ip(const char *host, const char *port)
{
char *ip;
size_t len;
int ipv6;
2020-07-24 08:32:47 -07:00
if (!host || !port) {
return NULL;
}
2014-02-20 12:13:47 -05:00
len = strlen(host) + strlen(port);
ipv6 = (strchr(host, ':') != NULL);
len += (ipv6 ? 4 : 3);
2014-02-20 12:13:47 -05:00
ip = calloc(1, len);
if (!(ip))
return NULL;
snprintf(ip, len, "%s%s%s:%s", ipv6 ? "[" : "", host, ipv6 ? "]" : "", port);
return ip;
}
static int make_connection_real(const char *soname, conn_t *conn)
{
int s = -1;
2014-02-06 16:54:29 -05:00
struct timeval tv;
char *port = NULL;
char *pt = NULL;
char *host = pt;
struct addrinfo hints, *res = NULL, *p;
2014-02-20 12:13:47 -05:00
int err;
int ret = 0;
2014-02-06 16:54:29 -05:00
Fix some coverity warnings in clamdtop/clamdtop.c I had some time over the weekend and implemented fixes for warnings in clamdtop/clamdtop.c: - 279008 - In make_connection_real: Code can never be reached because of a logical contradiction (CWE-561). host and port are freed and set to NULL before they are used to set conn->remote. The fix is to cleanup host and port after conn->remote is set. - 147369 - In get_ip: Code can never be reached because of a logical contradiction (CWE-561). Removed an unnecessary return statement that could never be reached. - 147624 - In get_port: Leak of memory or pointers to system resources (CWE-404). dupip wasn’t being freed in the case where no port could be found. - 279007 - In make_connection_real: Pointer is checked against null but then dereferenced anyway (CWE-476). This function checked soname for NULL but wouldn’t fail in this case, and subsequent code would dereference it. The NULL check could just be removed, though, because the only calling function of this static function ensured that soname is not NULL. - 147316 - In cleanup: Value returned from a library function is not checked for errors before being used. This value may indicate an error condition. (CWE-252). The code ignores the value of send when exiting because at that point it doesn’t really matter if the send succeeds. The fix is to cast this function call to void to explicitly ignore the return value. - 147318 - In recv_line: Value returned from a library function is not checked for errors before being used. This value may indicate an error condition. (CWE-252) The code ignores the value of send when exiting because at that point it doesn’t really matter if the send succeeds. The fix is to cast this function call to void to explicitly ignore the return value.
2020-05-11 14:46:12 -04:00
pt = strdup(soname);
OOM_CHECK(pt);
2014-02-06 16:54:29 -05:00
conn->tcp = 0;
#ifndef _WIN32
if (cli_is_abspath(soname) || (access(soname, F_OK) == 0)) {
2014-02-06 16:54:29 -05:00
struct sockaddr_un addr;
s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s < 0) {
2014-02-06 16:54:29 -05:00
perror("socket");
ret = -1;
goto done;
2014-02-06 16:54:29 -05:00
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, soname, sizeof(addr.sun_path));
addr.sun_path[sizeof(addr.sun_path) - 1] = 0x0;
print_con_info(conn, "Connecting to: %s\n", soname);
if (connect(s, (struct sockaddr *)&addr, sizeof(addr))) {
perror("connect");
2020-01-03 15:53:29 -05:00
close(s);
ret = -1;
goto done;
2014-02-06 16:54:29 -05:00
}
goto end;
}
#endif
2014-02-06 16:54:29 -05:00
memset(&hints, 0x00, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
2014-02-06 16:54:29 -05:00
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
2014-02-06 16:54:29 -05:00
2014-02-20 12:13:47 -05:00
host = get_ip(soname);
if (!(host)) {
ret = -1;
goto done;
}
2014-02-20 12:13:47 -05:00
port = get_port(soname);
conn->tcp = 1;
2014-02-06 16:54:29 -05:00
2014-02-20 12:13:47 -05:00
print_con_info(conn, "Looking up: %s:%s\n", host, port ? port : "3310");
if ((err = getaddrinfo(host, (port != NULL) ? port : "3310", &hints, &res))) {
print_con_info(conn, "Could not look up %s:%s, getaddrinfo returned: %s\n",
host, port ? port : "3310", gai_strerror(err));
ret = -1;
goto done;
2014-02-20 12:13:47 -05:00
}
2014-02-06 16:54:29 -05:00
for (p = res; p != NULL; p = p->ai_next) {
if ((s = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) {
perror("socket");
continue;
}
2014-02-20 12:13:47 -05:00
print_con_info(conn, "Connecting to: %s\n", soname);
2014-02-06 16:54:29 -05:00
if (connect(s, p->ai_addr, p->ai_addrlen)) {
perror("connect");
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
#ifndef _WIN32
close(s);
#else
2020-01-03 15:53:29 -05:00
closesocket(s);
#endif
2014-02-06 16:54:29 -05:00
continue;
}
break;
}
if (p == NULL) {
ret = -1;
goto done;
}
end:
conn->sd = s;
gettimeofday(&conn->tv_conn, NULL);
tv.tv_sec = 30;
tv.tv_usec = 0;
setsockopt(conn->sd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
if (conn->remote != soname) {
/* when we reconnect, they are the same */
if (NULL != conn->remote) {
free(conn->remote);
conn->remote = NULL;
}
conn->remote = make_ip(host, (port != NULL) ? port : "3310");
}
done:
if (NULL != res) {
2014-02-06 16:54:29 -05:00
freeaddrinfo(res);
res = NULL;
}
2014-02-06 16:54:29 -05:00
if (NULL != pt) {
free(pt);
pt = NULL;
}
if (NULL != host) {
free(host);
host = NULL;
}
if (NULL != port) {
free(port);
port = NULL;
}
2014-02-06 16:54:29 -05:00
return ret;
}
static int make_connection(const char *soname, conn_t *conn)
{
int rc;
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
int rv;
if (!soname) {
return -1;
}
if ((rc = make_connection_real(soname, conn)))
2014-02-06 16:54:29 -05:00
return rc;
send_string(conn, "nIDSESSION\nnVERSION\n");
free(conn->version);
conn->version = NULL;
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
rv = read_version(conn);
if (rv == -3) {
print_con_info(conn, "VERSION command unavailable, consider enabling it in the clamd configuration.\n");
EXIT_PROGRAM(FAIL_INITIAL_CONN);
} else if (!rv) {
// check if STATS command is available
if (check_stats_available(conn)) {
return 0;
} else {
print_con_info(conn, "STATS command unavailable, consider enabling it in the clamd configuration.\n");
EXIT_PROGRAM(FAIL_INITIAL_CONN);
}
}
/* clamd < 0.95 */
if ((rc = make_connection_real(soname, conn)))
2014-02-06 16:54:29 -05:00
return rc;
send_string(conn, "nSESSION\nnVERSION\n");
conn->version = NULL;
if (!read_version(conn))
2014-02-06 16:54:29 -05:00
return 0;
return -1;
}
static void reconnect(conn_t *conn);
static int send_string_noreconn(conn_t *conn, const char *cmd)
{
assert(cmd);
assert(conn && conn->sd > 0);
return send(conn->sd, cmd, strlen(cmd), 0);
}
static void send_string(conn_t *conn, const char *cmd)
{
while (send_string_noreconn(conn, cmd) == -1) {
reconnect(conn);
}
}
static int tries = 0;
static void reconnect(conn_t *conn)
{
if (++tries > 3) {
EXIT_PROGRAM(RECONNECT_FAIL);
}
2020-01-03 15:53:29 -05:00
if (conn->sd != -1) {
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
#ifndef _WIN32
2020-01-03 15:53:29 -05:00
close(conn->sd);
#else
2020-01-03 15:53:29 -05:00
closesocket(conn->sd);
#endif
2020-01-03 15:53:29 -05:00
}
if (make_connection(conn->remote, conn) < 0) {
print_con_info(conn, "Unable to reconnect to %s: %s", conn->remote, strerror(errno));
EXIT_PROGRAM(RECONNECT_FAIL);
}
tries = 0;
}
static int recv_line(conn_t *conn, char *buf, size_t len)
{
assert(len > 0);
assert(conn);
assert(buf);
len--;
if (!len || conn->sd == -1)
return 0;
assert(conn->sd > 0);
while (len > 0) {
ssize_t nread = recv(conn->sd, buf, len, MSG_PEEK);
if (nread <= 0) {
print_con_info(conn, "%s: %s", conn->remote, strerror(errno));
/* it could be a timeout, be nice and send an END */
Fix some coverity warnings in clamdtop/clamdtop.c I had some time over the weekend and implemented fixes for warnings in clamdtop/clamdtop.c: - 279008 - In make_connection_real: Code can never be reached because of a logical contradiction (CWE-561). host and port are freed and set to NULL before they are used to set conn->remote. The fix is to cleanup host and port after conn->remote is set. - 147369 - In get_ip: Code can never be reached because of a logical contradiction (CWE-561). Removed an unnecessary return statement that could never be reached. - 147624 - In get_port: Leak of memory or pointers to system resources (CWE-404). dupip wasn’t being freed in the case where no port could be found. - 279007 - In make_connection_real: Pointer is checked against null but then dereferenced anyway (CWE-476). This function checked soname for NULL but wouldn’t fail in this case, and subsequent code would dereference it. The NULL check could just be removed, though, because the only calling function of this static function ensured that soname is not NULL. - 147316 - In cleanup: Value returned from a library function is not checked for errors before being used. This value may indicate an error condition. (CWE-252). The code ignores the value of send when exiting because at that point it doesn’t really matter if the send succeeds. The fix is to cast this function call to void to explicitly ignore the return value. - 147318 - In recv_line: Value returned from a library function is not checked for errors before being used. This value may indicate an error condition. (CWE-252) The code ignores the value of send when exiting because at that point it doesn’t really matter if the send succeeds. The fix is to cast this function call to void to explicitly ignore the return value.
2020-05-11 14:46:12 -04:00
(void)send_string_noreconn(conn, "nEND\n");
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
#ifndef _WIN32
2020-01-03 15:53:29 -05:00
close(conn->sd);
#else
2020-01-03 15:53:29 -05:00
closesocket(conn->sd);
#endif
conn->sd = -1;
return 0;
} else {
char *p = memchr(buf, '\n', nread);
if (p) {
len = p - buf + 1;
} else
len = nread;
assert(len > 0);
assert(len <= (size_t)nread);
nread = recv(conn->sd, buf, len, 0);
if (nread == -1)
reconnect(conn);
else {
assert(nread > 0 && (size_t)nread == len);
buf += nread;
}
if (p)
break;
}
}
*buf = '\0';
return 1;
}
static void output_queue(size_t line, ssize_t max)
{
ssize_t i, j;
int tasks_truncd = 0;
struct task *tasks = global.tasks;
struct task *filtered_tasks = calloc(global.n, sizeof(*filtered_tasks));
OOM_CHECK(filtered_tasks);
for (i = 0, j = 0; i < global.n; i++) {
if (detail_selected == -1 || detail_is_selected(tasks[i].clamd_no - 1)) {
filtered_tasks[j++] = tasks[i];
}
}
wattron(stats_window, COLOR_PAIR(queue_header_color));
if (detail_selected == -1 && global.num_clamd > 1)
mvwprintw(stats_window, line, 0, "%s", multi_queue_header);
else
mvwprintw(stats_window, line, 0, "%s", queue_header);
wattroff(stats_window, COLOR_PAIR(queue_header_color));
if (max < j) {
--max;
tasks_truncd = 1;
}
if (max < 0) max = 0;
for (i = 0; i < j && i < max; i++) {
char *cmde;
assert(tasks);
cmde = strchr(filtered_tasks[i].line, ' ');
if (cmde) {
char cmd[16];
const char *filstart = strchr(cmde + 1, ' ');
strncpy(cmd, filtered_tasks[i].line, sizeof(cmd) - 1);
cmd[15] = '\0';
if (filtered_tasks[i].line + 15 > cmde)
cmd[cmde - filtered_tasks[i].line] = '\0';
if (filstart) {
size_t oldline = ++line;
2020-07-24 08:32:47 -07:00
char *nl = strrchr(++filstart, '\n');
if (nl != NULL)
*nl = '\0';
wattron(stats_window, A_BOLD);
if (detail_selected == -1 && global.num_clamd > 1)
mvwprintw(stats_window, line, 0, "%2u %s", filtered_tasks[i].clamd_no, cmd + 1);
else
mvwprintw(stats_window, line, 0, " %s", cmd + 1);
wattroff(stats_window, A_BOLD);
mvwprintw(stats_window, line, 15, "%10.03fs", filtered_tasks[i].tim);
mvwprintw(stats_window, line, 30, "%s", filstart);
line = getcury(stats_window);
if (line > oldline)
max -= line - oldline;
if (!tasks_truncd && max < j) {
--max;
tasks_truncd = 1;
}
}
}
}
if (tasks_truncd) {
/* in summary mode we can only show a max amount of tasks */
wattron(stats_window, A_DIM | COLOR_PAIR(header_color));
mvwprintw(stats_window, maxystats - 1, 0, "*** %u more task(s) not shown ***", (unsigned)(j - i));
wattroff(stats_window, A_DIM | COLOR_PAIR(header_color));
}
free(filtered_tasks);
}
/* ---------------------- stats parsing routines ------------------- */
static void parse_queue(conn_t *conn, char *buf, size_t len, unsigned idx)
{
do {
double tim;
const char *t = strchr(buf, ' ');
if (!t)
continue;
if (sscanf(t, "%lf", &tim) != 1)
continue;
++global.n;
global.tasks = realloc(global.tasks, sizeof(*global.tasks) * global.n);
OOM_CHECK(global.tasks);
global.tasks[global.n - 1].line = strdup(buf);
OOM_CHECK(global.tasks[global.n - 1].line);
global.tasks[global.n - 1].tim = tim;
global.tasks[global.n - 1].clamd_no = idx + 1;
} while (recv_line(conn, buf, len) && buf[0] == '\t' && strcmp("END\n", buf) != 0);
}
static unsigned biggest_mem = 0;
static void output_memstats(struct stats *stats)
{
char buf[128];
unsigned long totalmem;
int blink = 0;
werase(mem_window);
if (stats->mem > 0 || (stats->mem >= 0 && (stats->pools_total > 0))) {
box(mem_window, 0, 0);
if (stats->mem > 0)
snprintf(buf, sizeof(buf), "heap %4.0fM mmap %4.0fM unused%4.0fM",
stats->heapu, stats->mmapu, stats->releasable);
else
snprintf(buf, sizeof(buf), "heap N/A mmap N/A unused N/A");
mvwprintw(mem_window, 1, 1, "Mem: ");
print_colored(mem_window, buf);
mvwprintw(mem_window, 2, 1, "Libc: ");
if (stats->mem > 0)
snprintf(buf, sizeof(buf), "used %4.0fM free %4.0fM total %4.0fM",
stats->totalu, stats->totalf, stats->totalu + stats->totalf);
else
snprintf(buf, sizeof(buf), "used N/A free N/A total N/A");
print_colored(mem_window, buf);
mvwprintw(mem_window, 3, 1, "Pool: ");
snprintf(buf, sizeof(buf), "count %4u used %4.0fM total %4.0fM",
stats->pools_cnt, stats->pools_used, stats->pools_total);
print_colored(mem_window, buf);
totalmem = (stats->heapu + stats->mmapu + stats->pools_total) * 1000;
if (totalmem > biggest_mem) {
biggest_mem = totalmem;
blink = 1;
}
show_bar(mem_window, 4, totalmem,
(stats->mmapu + stats->releasable + stats->pools_total - stats->pools_used) * 1000,
biggest_mem, blink);
}
wrefresh(mem_window);
}
static void parse_memstats(const char *line, struct stats *stats)
{
if (sscanf(line, " heap %lfM mmap %lfM used %lfM free %lfM releasable %lfM pools %u pools_used %lfM pools_total %lfM",
&stats->heapu, &stats->mmapu, &stats->totalu, &stats->totalf, &stats->releasable,
&stats->pools_cnt, &stats->pools_used, &stats->pools_total) != 8) {
if (sscanf(line, " heap N/A mmap N/A used N/A free N/A releasable N/A pools %u pools_used %lfM pools_total %lfM",
&stats->pools_cnt, &stats->pools_used, &stats->pools_total) != 3) {
stats->mem = -1;
return;
}
stats->mem = 0;
return;
}
stats->mem = stats->heapu + stats->mmapu + stats->pools_total;
}
static int output_stats(struct stats *stats, unsigned idx)
{
char buf[128];
char timbuf[14];
int blink = 0;
size_t i = 0;
char mem[6];
WINDOW *win = stats_head_window;
int sel = detail_is_selected(idx);
char *line = malloc(maxx + 1);
int len = 0;
OOM_CHECK(line);
if (stats->mem <= 0 || stats->stats_unsupp) {
strncpy(mem, "N/A", sizeof(mem));
mem[sizeof(mem) - 1] = '\0';
} else {
const char *format;
char c;
double s;
if (stats->mem >= 1024) {
c = 'G';
s = stats->mem / 1024.0;
} else {
c = 'M';
s = stats->mem;
}
if (s >= 99.95)
format = "%.0f%c";
else if (s >= 9.995)
format = "%.1f%c";
else
format = "%.2f%c";
snprintf(mem, sizeof(mem), format, s, c);
mem[sizeof(mem) - 1] = '\0';
}
i = idx + 1;
if (!stats->db_time.tm_year) {
strncpy(timbuf, "N/A", sizeof(timbuf));
timbuf[sizeof(timbuf) - 1] = '\0';
} else
snprintf(timbuf, sizeof(timbuf), "%04u-%02u-%02uT%02u",
1900 + stats->db_time.tm_year,
stats->db_time.tm_mon + 1,
stats->db_time.tm_mday,
stats->db_time.tm_hour);
memset(line, ' ', maxx + 1);
if (!stats->stats_unsupp) {
len = snprintf(line, maxx + 1, "%2u %02u:%02u:%02u %3u %3u %5u %5u %5s %-7s %5s %-13s %s",
idx + 1, stats->conn_hr, stats->conn_min, stats->conn_sec,
stats->live, stats->idle,
stats->current_q, stats->biggest_queue,
mem,
stats->engine_version, stats->db_version, timbuf, stats->remote);
} else {
len = snprintf(line, maxx + 1, "%2u %02u:%02u:%02u N/A N/A N/A N/A N/A %-7s %5s %-13s %s",
idx + 1, stats->conn_hr, stats->conn_min, stats->conn_sec,
stats->engine_version, stats->db_version, timbuf, stats->remote);
}
line[maxx] = '\0';
line[strlen(line)] = ' ';
if (sel) {
wattron(win, COLOR_PAIR(selected_color));
}
mvwprintw(win, i, 0, "%s", line);
if (sel) {
wattroff(win, COLOR_PAIR(selected_color));
}
2020-07-24 08:32:47 -07:00
if ((unsigned)len > maxx) {
wattron(win, A_DIM | COLOR_PAIR(header_color));
mvwprintw(win, i, maxx - 3, "...");
wattroff(win, A_DIM | COLOR_PAIR(header_color));
}
win = stats_window;
i = 0;
if (sel && !stats->stats_unsupp) {
memset(line, ' ', maxx + 1);
snprintf(line, maxx + 1, "Details for Clamd version: %s", stats->version);
line[maxx] = '\0';
line[strlen(line)] = ' ';
wattron(win, COLOR_PAIR(queue_header_color));
mvwprintw(win, i++, 0, "%s", line);
wattroff(win, COLOR_PAIR(queue_header_color));
mvwprintw(win, i++, 0, "Primary threads: ");
snprintf(buf, sizeof(buf), "live%3u idle%3u max%3u", stats->prim_live, stats->prim_idle, stats->prim_max);
print_colored(win, buf);
show_bar(win, i++, stats->prim_live, stats->prim_idle, stats->prim_max, 0);
2020-07-24 08:32:47 -07:00
/*
mvwprintw(win, i++, 0, "Multiscan pool : ");
snprintf(buf, sizeof(buf), "live %3u idle %3u max %3u", stats->live, stats->idle, stats->max);
print_colored(win, buf);
show_bar(win, i++, stats->live, stats->idle, stats->max, 0);
*/
blink = 0;
if (stats->current_q > stats->biggest_queue) {
stats->biggest_queue = stats->current_q;
blink = 1;
}
mvwprintw(win, i++, 0, "Queue:");
snprintf(buf, sizeof(buf), "%6u items %6u max", stats->current_q, stats->biggest_queue);
print_colored(win, buf);
show_bar(win, i++, stats->current_q, 0, stats->biggest_queue, blink);
i += 2;
werase(mem_window);
output_memstats(stats);
}
free(line);
return i;
}
static void output_all(void)
{
unsigned i, stats_line = 0;
werase(stats_head_window);
werase(stats_window);
wattron(stats_head_window, COLOR_PAIR(queue_header_color));
mvwprintw(stats_head_window, 0, 0, "%s", clamd_header);
wattroff(stats_head_window, COLOR_PAIR(queue_header_color));
for (i = 0; i < global.num_clamd; i++) {
unsigned j = output_stats(&global.all_stats[i], i);
if (j > stats_line)
stats_line = j;
}
output_queue(stats_line, maxystats - stats_line - 1);
wrefresh(stats_head_window);
wrefresh(stats_window);
if (detail_exists()) {
/* overlaps, must be done at the end */
wrefresh(mem_window);
}
}
static void parse_stats(conn_t *conn, struct stats *stats, unsigned idx)
{
char buf[1025];
size_t j;
struct timeval tv;
unsigned conn_dt;
int primary = 0;
const char *pstart, *p, *vstart;
if (conn->tcp)
stats->remote = conn->remote;
else
stats->remote = "local";
if (!conn->version) {
stats->engine_version = strdup("???");
OOM_CHECK(stats->engine_version);
return;
}
p = pstart = vstart = strchr(conn->version, ' ');
if (!vstart) {
stats->engine_version = strdup("???");
OOM_CHECK(stats->engine_version);
return;
}
/* find digit in version */
while (*p && !isdigit(*p))
p++;
/* rewind to first space or dash */
while (p > pstart && *p && *p != ' ' && *p != '-')
p--;
if (*p) p++;
/* keep only base version, and cut -exp, and -gittags */
pstart = p;
while (*p && *p != ' ' && *p != '-' && *p != '/')
p++;
stats->engine_version = malloc(p - pstart + 1);
OOM_CHECK(stats->engine_version);
memcpy(stats->engine_version, pstart, p - pstart);
stats->engine_version[p - pstart] = '\0';
pstart = strchr(p, '/');
if (!pstart) {
stats->db_version = strdup("????");
OOM_CHECK(stats->db_version);
} else {
pstart++;
p = strchr(pstart, '/');
if (!p)
p = pstart + strlen(pstart);
stats->db_version = malloc(p - pstart + 1);
OOM_CHECK(stats->db_version);
memcpy(stats->db_version, pstart, p - pstart);
stats->db_version[p - pstart] = '\0';
if (*p) p++;
if (!*p || !strptime(p, "%a %b %d %H:%M:%S %Y", &stats->db_time)) {
memset(&stats->db_time, 0, sizeof(stats->db_time));
}
}
if (maxx > 61 && strlen(stats->db_version) > (maxx - 61)) {
stats->db_version[maxx - 61] = '\0';
}
stats->version = vstart; /* for details view */
gettimeofday(&tv, NULL);
tv.tv_sec -= conn->tv_conn.tv_sec;
tv.tv_usec -= conn->tv_conn.tv_usec;
conn_dt = tv.tv_sec + tv.tv_usec / 1e6;
stats->live = stats->idle = stats->max = 0;
stats->conn_hr = conn_dt / 3600;
stats->conn_min = (conn_dt / 60) % 60;
stats->conn_sec = conn_dt % 60;
stats->current_q = 0;
buf[sizeof(buf) - 1] = 0x0;
while (recv_line(conn, buf, sizeof(buf) - 1) && strcmp("END\n", buf) != 0) {
char *val = strchr(buf, ':');
if (buf[0] == '\t') {
parse_queue(conn, buf, sizeof(buf) - 1, idx);
continue;
} else if (val)
*val++ = '\0';
if (!strcmp("MEMSTATS", buf)) {
parse_memstats(val, stats);
continue;
}
if (!strncmp("UNKNOWN COMMAND", buf, 15)) {
stats->stats_unsupp = 1;
break;
}
for (j = 1; j < strlen(buf); j++)
buf[j] = tolower(buf[j]);
2020-07-24 08:32:47 -07:00
/*
mvwprintw(win, i, 0, "%s", buf);
if(!val) {
i++;
continue;
}
waddch(win, ':');
print_colored(win, val);
i++;
*/
if (!strncmp("State", buf, 5)) {
if (strstr(val, "PRIMARY")) {
/* primary thread pool */
primary = 1;
} else {
/* multiscan pool */
primary = 0;
}
}
if (!strcmp("Threads", buf)) {
unsigned live, idle, max;
if (sscanf(val, " live %u idle %u max %u", &live, &idle, &max) != 3)
continue;
if (primary) {
stats->prim_live = live;
stats->prim_idle = idle;
assert(!stats->prim_max && "There can be only one primary pool!");
stats->prim_max = max;
}
stats->live += live;
stats->idle += idle;
stats->max += max;
} else if (!strcmp("Queue", buf)) {
unsigned len;
if (sscanf(val, "%u", &len) != 1)
continue;
stats->current_q += len;
}
}
}
static int read_version(conn_t *conn)
{
char buf[1024];
unsigned i;
Fix static analysis code quality issues (#1582) `libclamav/libmspack.c`: Initialize variables before first `goto done;` to fix unitialized variable use in an error condition. `libclamav/others.c`: Explicitly ignore return values for calls to add JSON values when subsequent calls don't depend on them. If we were to add error handling here, the only thing we'd do is debug- log it. I don't think it's worth adding the extra lines of code. `libclamav/unarj.c`: Removed dead code. The `status` variable is immediately set afterwards based on whether or not any files may be extracted. `libclamav/unzip.c`: Removed dead code. The `ret` variable is checked immediately after being set, above. This check after the `do`-`while()` loop is dead code. `sigtool/sigtool.c`: Fix potential NULL deref in error handling. This is a fix for the same issue as was fixed in a previous commit. I somehow overlooked this one. Copy/paste bug. `libclamav/pdfdecode.c`: Fix leaked `stream` memory when `filter_lzwdecode()` fails. `clamdtop/clamdtop.c`: Fix possible NULL dereference if `strchr` returns NULL in `read_version()` and `check_stats_available()`. `libclamav/rtf.c`: Fix memory leak in `rtf_object_process()` if `cli_gentemp_with_prefix()` fails. Also change empty for-loop to resolve clang-format weirdness and make it more obvious the for-loop has no body. `libclamav/aspack.c`: Ensure that `endoff - old` is not negative in `build_decrypt_array()` before passing to `CLI_ISCONTAINED()` which expects unsigned values. `libclamav/upx.c`: Fix integer overflow checks in multiple functions. `libclamav/vba_extract.c`: Set `entries` pointer back to NULL after free in `word_read_macro_entry()` error condition. `libclamav/unzip.c`: Remove logic to return `CL_EMAXFILES` from `index_local_file_headers()`. It seems it only overwrote the status when not `CL_SUCCESS` in which case it could be overriding a more serious failure. Further, updates to the how the ZIP parser works has made it so this needs to return `CL_SUCCESS` in order for the caller to at least scan the files found so far. Finally, the calling function has checks of its own to make sure we don't exceeds the max-files limit. `libclamav/unzip.c`: Fix issue where `cli_append_potentially_unwanted()` in `index_local_file_headers()` might overwrite an error in `status` with `CL_CLEAN`. Instead, it now checks the return value and only overwrites the `CL_EFORMAT` status with a different value if not `CL_SUCCESS`. `libclamav/unzip.c`: Fix a potential leak with `combined_catalogue` and `temp_catalogue` in an error condition. We should always free them if not NULL, not just if the function failed. And to make this safe, we must set `combined_catalogue` to NULL when we give ownership to `*catalogue`. `libclamav/scanners.c`: Fix a potential leak in error handling for the `cli_ole2_tempdir_scan_vba()` function. CLAM-2768
2025-10-02 11:46:14 -04:00
if (!recv_line(conn, buf, sizeof(buf)))
return -1;
Fix static analysis code quality issues (#1582) `libclamav/libmspack.c`: Initialize variables before first `goto done;` to fix unitialized variable use in an error condition. `libclamav/others.c`: Explicitly ignore return values for calls to add JSON values when subsequent calls don't depend on them. If we were to add error handling here, the only thing we'd do is debug- log it. I don't think it's worth adding the extra lines of code. `libclamav/unarj.c`: Removed dead code. The `status` variable is immediately set afterwards based on whether or not any files may be extracted. `libclamav/unzip.c`: Removed dead code. The `ret` variable is checked immediately after being set, above. This check after the `do`-`while()` loop is dead code. `sigtool/sigtool.c`: Fix potential NULL deref in error handling. This is a fix for the same issue as was fixed in a previous commit. I somehow overlooked this one. Copy/paste bug. `libclamav/pdfdecode.c`: Fix leaked `stream` memory when `filter_lzwdecode()` fails. `clamdtop/clamdtop.c`: Fix possible NULL dereference if `strchr` returns NULL in `read_version()` and `check_stats_available()`. `libclamav/rtf.c`: Fix memory leak in `rtf_object_process()` if `cli_gentemp_with_prefix()` fails. Also change empty for-loop to resolve clang-format weirdness and make it more obvious the for-loop has no body. `libclamav/aspack.c`: Ensure that `endoff - old` is not negative in `build_decrypt_array()` before passing to `CLI_ISCONTAINED()` which expects unsigned values. `libclamav/upx.c`: Fix integer overflow checks in multiple functions. `libclamav/vba_extract.c`: Set `entries` pointer back to NULL after free in `word_read_macro_entry()` error condition. `libclamav/unzip.c`: Remove logic to return `CL_EMAXFILES` from `index_local_file_headers()`. It seems it only overwrote the status when not `CL_SUCCESS` in which case it could be overriding a more serious failure. Further, updates to the how the ZIP parser works has made it so this needs to return `CL_SUCCESS` in order for the caller to at least scan the files found so far. Finally, the calling function has checks of its own to make sure we don't exceeds the max-files limit. `libclamav/unzip.c`: Fix issue where `cli_append_potentially_unwanted()` in `index_local_file_headers()` might overwrite an error in `status` with `CL_CLEAN`. Instead, it now checks the return value and only overwrites the `CL_EFORMAT` status with a different value if not `CL_SUCCESS`. `libclamav/unzip.c`: Fix a potential leak with `combined_catalogue` and `temp_catalogue` in an error condition. We should always free them if not NULL, not just if the function failed. And to make this safe, we must set `combined_catalogue` to NULL when we give ownership to `*catalogue`. `libclamav/scanners.c`: Fix a potential leak in error handling for the `cli_ole2_tempdir_scan_vba()` function. CLAM-2768
2025-10-02 11:46:14 -04:00
if (!strcmp(buf, "UNKNOWN COMMAND\n"))
return -2;
Fix static analysis code quality issues (#1582) `libclamav/libmspack.c`: Initialize variables before first `goto done;` to fix unitialized variable use in an error condition. `libclamav/others.c`: Explicitly ignore return values for calls to add JSON values when subsequent calls don't depend on them. If we were to add error handling here, the only thing we'd do is debug- log it. I don't think it's worth adding the extra lines of code. `libclamav/unarj.c`: Removed dead code. The `status` variable is immediately set afterwards based on whether or not any files may be extracted. `libclamav/unzip.c`: Removed dead code. The `ret` variable is checked immediately after being set, above. This check after the `do`-`while()` loop is dead code. `sigtool/sigtool.c`: Fix potential NULL deref in error handling. This is a fix for the same issue as was fixed in a previous commit. I somehow overlooked this one. Copy/paste bug. `libclamav/pdfdecode.c`: Fix leaked `stream` memory when `filter_lzwdecode()` fails. `clamdtop/clamdtop.c`: Fix possible NULL dereference if `strchr` returns NULL in `read_version()` and `check_stats_available()`. `libclamav/rtf.c`: Fix memory leak in `rtf_object_process()` if `cli_gentemp_with_prefix()` fails. Also change empty for-loop to resolve clang-format weirdness and make it more obvious the for-loop has no body. `libclamav/aspack.c`: Ensure that `endoff - old` is not negative in `build_decrypt_array()` before passing to `CLI_ISCONTAINED()` which expects unsigned values. `libclamav/upx.c`: Fix integer overflow checks in multiple functions. `libclamav/vba_extract.c`: Set `entries` pointer back to NULL after free in `word_read_macro_entry()` error condition. `libclamav/unzip.c`: Remove logic to return `CL_EMAXFILES` from `index_local_file_headers()`. It seems it only overwrote the status when not `CL_SUCCESS` in which case it could be overriding a more serious failure. Further, updates to the how the ZIP parser works has made it so this needs to return `CL_SUCCESS` in order for the caller to at least scan the files found so far. Finally, the calling function has checks of its own to make sure we don't exceeds the max-files limit. `libclamav/unzip.c`: Fix issue where `cli_append_potentially_unwanted()` in `index_local_file_headers()` might overwrite an error in `status` with `CL_CLEAN`. Instead, it now checks the return value and only overwrites the `CL_EFORMAT` status with a different value if not `CL_SUCCESS`. `libclamav/unzip.c`: Fix a potential leak with `combined_catalogue` and `temp_catalogue` in an error condition. We should always free them if not NULL, not just if the function failed. And to make this safe, we must set `combined_catalogue` to NULL when we give ownership to `*catalogue`. `libclamav/scanners.c`: Fix a potential leak in error handling for the `cli_ole2_tempdir_scan_vba()` function. CLAM-2768
2025-10-02 11:46:14 -04:00
char *p = strchr(buf, ':');
if (NULL == p)
return -1;
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 VERSION command is available
Fix static analysis code quality issues (#1582) `libclamav/libmspack.c`: Initialize variables before first `goto done;` to fix unitialized variable use in an error condition. `libclamav/others.c`: Explicitly ignore return values for calls to add JSON values when subsequent calls don't depend on them. If we were to add error handling here, the only thing we'd do is debug- log it. I don't think it's worth adding the extra lines of code. `libclamav/unarj.c`: Removed dead code. The `status` variable is immediately set afterwards based on whether or not any files may be extracted. `libclamav/unzip.c`: Removed dead code. The `ret` variable is checked immediately after being set, above. This check after the `do`-`while()` loop is dead code. `sigtool/sigtool.c`: Fix potential NULL deref in error handling. This is a fix for the same issue as was fixed in a previous commit. I somehow overlooked this one. Copy/paste bug. `libclamav/pdfdecode.c`: Fix leaked `stream` memory when `filter_lzwdecode()` fails. `clamdtop/clamdtop.c`: Fix possible NULL dereference if `strchr` returns NULL in `read_version()` and `check_stats_available()`. `libclamav/rtf.c`: Fix memory leak in `rtf_object_process()` if `cli_gentemp_with_prefix()` fails. Also change empty for-loop to resolve clang-format weirdness and make it more obvious the for-loop has no body. `libclamav/aspack.c`: Ensure that `endoff - old` is not negative in `build_decrypt_array()` before passing to `CLI_ISCONTAINED()` which expects unsigned values. `libclamav/upx.c`: Fix integer overflow checks in multiple functions. `libclamav/vba_extract.c`: Set `entries` pointer back to NULL after free in `word_read_macro_entry()` error condition. `libclamav/unzip.c`: Remove logic to return `CL_EMAXFILES` from `index_local_file_headers()`. It seems it only overwrote the status when not `CL_SUCCESS` in which case it could be overriding a more serious failure. Further, updates to the how the ZIP parser works has made it so this needs to return `CL_SUCCESS` in order for the caller to at least scan the files found so far. Finally, the calling function has checks of its own to make sure we don't exceeds the max-files limit. `libclamav/unzip.c`: Fix issue where `cli_append_potentially_unwanted()` in `index_local_file_headers()` might overwrite an error in `status` with `CL_CLEAN`. Instead, it now checks the return value and only overwrites the `CL_EFORMAT` status with a different value if not `CL_SUCCESS`. `libclamav/unzip.c`: Fix a potential leak with `combined_catalogue` and `temp_catalogue` in an error condition. We should always free them if not NULL, not just if the function failed. And to make this safe, we must set `combined_catalogue` to NULL when we give ownership to `*catalogue`. `libclamav/scanners.c`: Fix a potential leak in error handling for the `cli_ole2_tempdir_scan_vba()` function. CLAM-2768
2025-10-02 11:46:14 -04:00
if (!strcmp(p, ": COMMAND UNAVAILABLE\n"))
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
return -3;
conn->version = strdup(buf);
OOM_CHECK(conn->version);
for (i = 0; i < strlen(conn->version); i++)
if (conn->version[i] == '\n')
conn->version[i] = ' ';
return 0;
}
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
static int check_stats_available(conn_t *conn)
{
char buf[1024];
send_string(conn, "nSTATS\n");
Fix static analysis code quality issues (#1582) `libclamav/libmspack.c`: Initialize variables before first `goto done;` to fix unitialized variable use in an error condition. `libclamav/others.c`: Explicitly ignore return values for calls to add JSON values when subsequent calls don't depend on them. If we were to add error handling here, the only thing we'd do is debug- log it. I don't think it's worth adding the extra lines of code. `libclamav/unarj.c`: Removed dead code. The `status` variable is immediately set afterwards based on whether or not any files may be extracted. `libclamav/unzip.c`: Removed dead code. The `ret` variable is checked immediately after being set, above. This check after the `do`-`while()` loop is dead code. `sigtool/sigtool.c`: Fix potential NULL deref in error handling. This is a fix for the same issue as was fixed in a previous commit. I somehow overlooked this one. Copy/paste bug. `libclamav/pdfdecode.c`: Fix leaked `stream` memory when `filter_lzwdecode()` fails. `clamdtop/clamdtop.c`: Fix possible NULL dereference if `strchr` returns NULL in `read_version()` and `check_stats_available()`. `libclamav/rtf.c`: Fix memory leak in `rtf_object_process()` if `cli_gentemp_with_prefix()` fails. Also change empty for-loop to resolve clang-format weirdness and make it more obvious the for-loop has no body. `libclamav/aspack.c`: Ensure that `endoff - old` is not negative in `build_decrypt_array()` before passing to `CLI_ISCONTAINED()` which expects unsigned values. `libclamav/upx.c`: Fix integer overflow checks in multiple functions. `libclamav/vba_extract.c`: Set `entries` pointer back to NULL after free in `word_read_macro_entry()` error condition. `libclamav/unzip.c`: Remove logic to return `CL_EMAXFILES` from `index_local_file_headers()`. It seems it only overwrote the status when not `CL_SUCCESS` in which case it could be overriding a more serious failure. Further, updates to the how the ZIP parser works has made it so this needs to return `CL_SUCCESS` in order for the caller to at least scan the files found so far. Finally, the calling function has checks of its own to make sure we don't exceeds the max-files limit. `libclamav/unzip.c`: Fix issue where `cli_append_potentially_unwanted()` in `index_local_file_headers()` might overwrite an error in `status` with `CL_CLEAN`. Instead, it now checks the return value and only overwrites the `CL_EFORMAT` status with a different value if not `CL_SUCCESS`. `libclamav/unzip.c`: Fix a potential leak with `combined_catalogue` and `temp_catalogue` in an error condition. We should always free them if not NULL, not just if the function failed. And to make this safe, we must set `combined_catalogue` to NULL when we give ownership to `*catalogue`. `libclamav/scanners.c`: Fix a potential leak in error handling for the `cli_ole2_tempdir_scan_vba()` function. CLAM-2768
2025-10-02 11:46:14 -04:00
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
if (!recv_line(conn, buf, sizeof(buf)))
return 0;
Fix static analysis code quality issues (#1582) `libclamav/libmspack.c`: Initialize variables before first `goto done;` to fix unitialized variable use in an error condition. `libclamav/others.c`: Explicitly ignore return values for calls to add JSON values when subsequent calls don't depend on them. If we were to add error handling here, the only thing we'd do is debug- log it. I don't think it's worth adding the extra lines of code. `libclamav/unarj.c`: Removed dead code. The `status` variable is immediately set afterwards based on whether or not any files may be extracted. `libclamav/unzip.c`: Removed dead code. The `ret` variable is checked immediately after being set, above. This check after the `do`-`while()` loop is dead code. `sigtool/sigtool.c`: Fix potential NULL deref in error handling. This is a fix for the same issue as was fixed in a previous commit. I somehow overlooked this one. Copy/paste bug. `libclamav/pdfdecode.c`: Fix leaked `stream` memory when `filter_lzwdecode()` fails. `clamdtop/clamdtop.c`: Fix possible NULL dereference if `strchr` returns NULL in `read_version()` and `check_stats_available()`. `libclamav/rtf.c`: Fix memory leak in `rtf_object_process()` if `cli_gentemp_with_prefix()` fails. Also change empty for-loop to resolve clang-format weirdness and make it more obvious the for-loop has no body. `libclamav/aspack.c`: Ensure that `endoff - old` is not negative in `build_decrypt_array()` before passing to `CLI_ISCONTAINED()` which expects unsigned values. `libclamav/upx.c`: Fix integer overflow checks in multiple functions. `libclamav/vba_extract.c`: Set `entries` pointer back to NULL after free in `word_read_macro_entry()` error condition. `libclamav/unzip.c`: Remove logic to return `CL_EMAXFILES` from `index_local_file_headers()`. It seems it only overwrote the status when not `CL_SUCCESS` in which case it could be overriding a more serious failure. Further, updates to the how the ZIP parser works has made it so this needs to return `CL_SUCCESS` in order for the caller to at least scan the files found so far. Finally, the calling function has checks of its own to make sure we don't exceeds the max-files limit. `libclamav/unzip.c`: Fix issue where `cli_append_potentially_unwanted()` in `index_local_file_headers()` might overwrite an error in `status` with `CL_CLEAN`. Instead, it now checks the return value and only overwrites the `CL_EFORMAT` status with a different value if not `CL_SUCCESS`. `libclamav/unzip.c`: Fix a potential leak with `combined_catalogue` and `temp_catalogue` in an error condition. We should always free them if not NULL, not just if the function failed. And to make this safe, we must set `combined_catalogue` to NULL when we give ownership to `*catalogue`. `libclamav/scanners.c`: Fix a potential leak in error handling for the `cli_ole2_tempdir_scan_vba()` function. CLAM-2768
2025-10-02 11:46:14 -04:00
char *p = strchr(buf, ':');
if (NULL == p)
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
return 0;
Fix static analysis code quality issues (#1582) `libclamav/libmspack.c`: Initialize variables before first `goto done;` to fix unitialized variable use in an error condition. `libclamav/others.c`: Explicitly ignore return values for calls to add JSON values when subsequent calls don't depend on them. If we were to add error handling here, the only thing we'd do is debug- log it. I don't think it's worth adding the extra lines of code. `libclamav/unarj.c`: Removed dead code. The `status` variable is immediately set afterwards based on whether or not any files may be extracted. `libclamav/unzip.c`: Removed dead code. The `ret` variable is checked immediately after being set, above. This check after the `do`-`while()` loop is dead code. `sigtool/sigtool.c`: Fix potential NULL deref in error handling. This is a fix for the same issue as was fixed in a previous commit. I somehow overlooked this one. Copy/paste bug. `libclamav/pdfdecode.c`: Fix leaked `stream` memory when `filter_lzwdecode()` fails. `clamdtop/clamdtop.c`: Fix possible NULL dereference if `strchr` returns NULL in `read_version()` and `check_stats_available()`. `libclamav/rtf.c`: Fix memory leak in `rtf_object_process()` if `cli_gentemp_with_prefix()` fails. Also change empty for-loop to resolve clang-format weirdness and make it more obvious the for-loop has no body. `libclamav/aspack.c`: Ensure that `endoff - old` is not negative in `build_decrypt_array()` before passing to `CLI_ISCONTAINED()` which expects unsigned values. `libclamav/upx.c`: Fix integer overflow checks in multiple functions. `libclamav/vba_extract.c`: Set `entries` pointer back to NULL after free in `word_read_macro_entry()` error condition. `libclamav/unzip.c`: Remove logic to return `CL_EMAXFILES` from `index_local_file_headers()`. It seems it only overwrote the status when not `CL_SUCCESS` in which case it could be overriding a more serious failure. Further, updates to the how the ZIP parser works has made it so this needs to return `CL_SUCCESS` in order for the caller to at least scan the files found so far. Finally, the calling function has checks of its own to make sure we don't exceeds the max-files limit. `libclamav/unzip.c`: Fix issue where `cli_append_potentially_unwanted()` in `index_local_file_headers()` might overwrite an error in `status` with `CL_CLEAN`. Instead, it now checks the return value and only overwrites the `CL_EFORMAT` status with a different value if not `CL_SUCCESS`. `libclamav/unzip.c`: Fix a potential leak with `combined_catalogue` and `temp_catalogue` in an error condition. We should always free them if not NULL, not just if the function failed. And to make this safe, we must set `combined_catalogue` to NULL when we give ownership to `*catalogue`. `libclamav/scanners.c`: Fix a potential leak in error handling for the `cli_ole2_tempdir_scan_vba()` function. CLAM-2768
2025-10-02 11:46:14 -04:00
if (!strcmp(p, ": COMMAND UNAVAILABLE\n"))
return 0;
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
return 1;
}
static void sigint(int a)
{
UNUSEDPARAM(a);
EXIT_PROGRAM(SIGINT_REASON);
}
static void help(void)
{
printf("\n");
printf(" Clam AntiVirus: Monitoring Tool %s\n", get_version());
printf(" By The ClamAV Team: https://www.clamav.net/about.html#credits\n");
2025-02-14 10:24:30 -05:00
printf(" (C) 2025 Cisco Systems, Inc.\n");
printf("\n");
printf(" clamdtop [-hVc] [host[:port] /path/to/clamd.sock ...]\n");
printf("\n");
printf(" --help -h Show this help\n");
printf(" --version -V Show version\n");
printf(" --config-file=FILE -c FILE Read clamd's configuration files from FILE\n");
2020-07-24 08:32:47 -07:00
printf(" --defaultcolors -d Use default terminal colors\n");
printf(" host[:port] Connect to clamd on host at port (default 3310)\n");
printf(" /path/to/clamd.sock Connect to clamd over a local socket\n");
printf("\n");
return;
}
static int default_colors = 0;
/* -------------------------- Initialization ---------------- */
static void setup_connections(int argc, char *argv[])
{
2014-02-06 16:54:29 -05:00
struct optstruct *opts;
struct optstruct *clamd_opts;
unsigned i;
char *conn = NULL;
opts = optparse(NULL, argc, argv, 1, OPT_CLAMDTOP, 0, NULL);
if (!opts) {
fprintf(stderr, "ERROR: Can't parse command line options\n");
EXIT_PROGRAM(FAIL_CMDLINE);
}
if (optget(opts, "help")->enabled) {
2014-02-06 16:54:29 -05:00
optfree(opts);
help();
normal_exit = 1;
exit(0);
}
if (optget(opts, "version")->enabled) {
2014-02-06 16:54:29 -05:00
printf("Clam AntiVirus Monitoring Tool %s\n", get_version());
optfree(opts);
normal_exit = 1;
exit(0);
}
if (optget(opts, "defaultcolors")->enabled)
2014-02-06 16:54:29 -05:00
default_colors = 1;
memset(&global, 0, sizeof(global));
if (!opts->filename || !opts->filename[0]) {
const struct optstruct *opt;
const char *clamd_conf = optget(opts, "config-file")->strarg;
if ((clamd_opts = optparse(clamd_conf, 0, NULL, 1, OPT_CLAMD, 0, NULL)) == NULL) {
fprintf(stderr, "Can't parse clamd configuration file %s\n", clamd_conf);
EXIT_PROGRAM(FAIL_CMDLINE);
}
if ((opt = optget(clamd_opts, "LocalSocket"))->enabled) {
2014-02-06 16:54:29 -05:00
conn = strdup(opt->strarg);
if (!conn) {
fprintf(stderr, "Can't strdup LocalSocket value\n");
EXIT_PROGRAM(FAIL_INITIAL_CONN);
}
} else if ((opt = optget(clamd_opts, "TCPSocket"))->enabled) {
char buf[512];
const struct optstruct *opt_addr;
const char *host = "localhost";
if ((opt_addr = optget(clamd_opts, "TCPAddr"))->enabled) {
host = opt_addr->strarg;
}
2014-02-20 12:13:47 -05:00
snprintf(buf, sizeof(buf), "%lld", opt->numarg);
conn = make_ip(host, buf);
2014-02-06 16:54:29 -05:00
} else {
fprintf(stderr, "Can't find how to connect to clamd\n");
EXIT_PROGRAM(FAIL_INITIAL_CONN);
}
optfree(clamd_opts);
global.num_clamd = 1;
} else {
unsigned i = 0;
while (opts->filename[i]) {
i++;
}
2014-02-06 16:54:29 -05:00
global.num_clamd = i;
}
#ifdef _WIN32
2014-02-06 16:54:29 -05:00
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != NO_ERROR) {
2014-02-06 16:54:29 -05:00
fprintf(stderr, "Error at WSAStartup(): %d\n", WSAGetLastError());
EXIT_PROGRAM(FAIL_INITIAL_CONN);
}
#endif
2014-02-06 16:54:29 -05:00
/* clamdtop */
puts(" __ ____");
2014-02-06 16:54:29 -05:00
puts(" _____/ /___ _____ ___ ____/ / /_____ ____");
puts(" / ___/ / __ `/ __ `__ \\/ __ / __/ __ \\/ __ \\");
puts("/ /__/ / /_/ / / / / / / /_/ / /_/ /_/ / /_/ /");
puts("\\___/_/\\__,_/_/ /_/ /_/\\__,_/\\__/\\____/ .___/");
puts(" /_/");
global.all_stats = calloc(global.num_clamd, sizeof(*global.all_stats));
OOM_CHECK(global.all_stats);
global.conn = calloc(global.num_clamd, sizeof(*global.conn));
OOM_CHECK(global.conn);
for (i = 0; i < global.num_clamd; i++) {
const char *soname;
if (!conn && !opts->filename) {
soname = NULL;
} else {
2020-07-24 08:32:47 -07:00
soname = conn ? conn : opts->filename[i];
}
global.conn[i].line = i + 1;
2014-02-06 16:54:29 -05:00
if (make_connection(soname, &global.conn[i]) < 0) {
2020-07-24 08:32:47 -07:00
EXIT_PROGRAM(FAIL_INITIAL_CONN);
2014-02-06 16:54:29 -05:00
}
}
optfree(opts);
free(conn);
#ifndef _WIN32
2014-02-06 16:54:29 -05:00
signal(SIGPIPE, SIG_IGN);
signal(SIGINT, sigint);
#endif
}
static void free_global_stats(void)
{
unsigned i;
for (i = 0; i < (unsigned)global.n; i++) {
free(global.tasks[i].line);
}
for (i = 0; i < global.num_clamd; i++) {
free(global.all_stats[i].engine_version);
free(global.all_stats[i].db_version);
}
free(global.tasks);
global.tasks = NULL;
global.n = 0;
}
2008-11-12 21:23:35 +00:00
static int help_line;
static void explain(const char *abbrev, const char *msg)
{
wattron(stdscr, A_BOLD);
mvwprintw(stdscr, help_line++, 0, "%-15s", abbrev);
wattroff(stdscr, A_BOLD);
wprintw(stdscr, " %s", msg);
2008-11-12 21:23:35 +00:00
}
static int show_help(void)
{
int ch;
werase(stdscr);
help_line = 0;
explain("NO", "Unique clamd number");
explain("CONNTIME", "How long it is connected");
explain("LIV", "Total number of live threads");
explain("IDL", "Total number of idle threads");
explain("QUEUE", "Number of items in queue");
explain("MAXQ", "Maximum number of items observed in queue");
explain("MEM", "Total memory usage (if available)");
explain("ENGINE", "Engine version");
explain("DBVER", "Database version");
explain("DBTIME", "Database publish time");
explain("HOST", "Which clamd, local means unix socket");
explain("Primary threads", "Threadpool used to receive commands");
/*explain("Multiscan pool", "Threadpool used for multiscan");*/
explain("live", "Executing commands, or scanning");
explain("idle", "Waiting for commands, will exit after idle_timeout");
explain("max", "Maximum number of threads configured for this pool");
explain("Queue", "Tasks queued for processing, but not yet picked up by a thread");
explain("COMMAND", "Command this thread is executing");
explain("QUEUEDSINCE", "How long this task is executing");
explain("FILE", "Which file it is processing (if applicable)");
explain("Mem", "Memory usage reported by libc");
explain("Libc", "Used/free memory reported by libc");
explain("Pool", "Memory usage reported by libclamav's pool");
wrefresh(stdscr);
werase(status_bar_window);
wattron(status_bar_window, A_REVERSE);
mvwprintw(status_bar_window, 0, 0, "Press any key to exit help");
wattroff(status_bar_window, A_REVERSE);
wrefresh(status_bar_window);
/* getch() times out after a few seconds */
do {
ch = getch();
/* we do need to exit on resize, because the text scroll out of
2020-07-24 08:32:47 -07:00
* view */
} while (ch == -1 /*|| ch == KEY_RESIZE*/);
return ch == KEY_RESIZE ? KEY_RESIZE : -1;
2008-11-12 21:23:35 +00:00
}
int main(int argc, char *argv[])
{
int ch = 0;
struct timeval tv_last, tv;
unsigned i;
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8);
#endif
atexit(cleanup);
setup_connections(argc, argv);
init_ncurses(global.num_clamd, default_colors);
memset(&tv_last, 0, sizeof(tv_last));
do {
if (toupper(ch) == 'H') {
ch = show_help();
}
switch (ch) {
case KEY_RESIZE:
resize();
endwin();
refresh();
init_windows(global.num_clamd);
break;
case 'R':
case 'r':
for (i = 0; i < global.num_clamd; i++)
global.all_stats[i].biggest_queue = 1;
biggest_mem = 0;
break;
case KEY_UP:
if (global.num_clamd > 1) {
if (detail_selected == -1)
detail_selected = global.num_clamd - 1;
else
--detail_selected;
}
break;
case KEY_DOWN:
if (global.num_clamd > 1) {
if (detail_selected == -1)
detail_selected = 0;
else {
if ((unsigned)++detail_selected >= global.num_clamd)
detail_selected = -1;
}
}
break;
}
gettimeofday(&tv, NULL);
header();
if (tv.tv_sec - tv_last.tv_sec >= MIN_INTERVAL) {
free_global_stats();
for (i = 0; i < global.num_clamd; i++) {
unsigned biggest_q;
struct stats *stats = &global.all_stats[i];
if (global.conn[i].sd != -1)
send_string(&global.conn[i], "nSTATS\n");
biggest_q = stats->biggest_queue;
memset(stats, 0, sizeof(*stats));
stats->biggest_queue = biggest_q;
parse_stats(&global.conn[i], stats, i);
}
if (global.tasks)
qsort(global.tasks, global.n, sizeof(*global.tasks), tasks_compare);
tv_last = tv;
}
/* always show, so that screen resizes take effect instantly*/
output_all();
for (i = 0; i < global.num_clamd; i++) {
if (global.conn[i].sd == -1)
reconnect(&global.conn[i]);
}
} while (toupper(ch = getch()) != 'Q');
free_global_stats();
normal_exit = 1;
return 0;
}