added scanning without sockets to clamd

pull/1448/head
nicbat 4 months ago
parent 86a3835218
commit d905e5a92c
  1. 4
      README.md
  2. 84
      clamd/clamd.c
  3. 20
      clamd/scanner.c
  4. 8
      clamd/server-th.c
  5. 3
      clamd/server.h
  6. 8
      clamd/session.c
  7. 7
      clamd/thrmgr.c
  8. 5
      common/optparser.c
  9. 3
      libclamav/clamav.h

@ -1,3 +1,7 @@
# AE-Scan changes
- added a flag "--local-scanning-file" to `clamd` to select which directoryto scan
- this will disable the socket functionality and run the scan without a daemon
# ClamAV
<p align="center">

@ -19,6 +19,7 @@
* MA 02110-1301, USA.
*/
#include "thrmgr.h"
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
@ -143,7 +144,7 @@ int main(int argc, char **argv)
struct rlimit rlim;
#endif
time_t currtime;
const char *dbdir, *cfgfile;
const char *dbdir, *cfgfile, *local_scan_file;
char *pua_cats = NULL, *pt;
int ret, tcpsock = 0, localsock = 0, min_port, max_port;
unsigned int sigs = 0;
@ -154,6 +155,12 @@ int main(int argc, char **argv)
int j;
int num_fd;
pid_t parentPid = getpid();
/* AEScan Variables */
int do_local_scan = 0;
threadpool_t *thr_pool;
int idletimeout;
int max_threads, max_queue = 0;
#ifdef C_LINUX
STATBUF sb;
#endif
@ -195,6 +202,13 @@ int main(int argc, char **argv)
debug_mode = 1;
}
/* AE Scan Option */
if (optget(opts, "LocalScanningFile")->enabled) {
local_scan_file = optget(opts, "LocalScanningFile")->strarg;
do_local_scan = 1;
fprintf(stderr, "LocalScanningFile: %s\n", local_scan_file);
}
/* check foreground option from command line to override config file */
for (j = 0; j < argc; j += 1) {
if ((memcmp(argv[j], "--foreground", 12) == 0) || (memcmp(argv[j], "-F", 2) == 0)) {
@ -211,6 +225,10 @@ int main(int argc, char **argv)
}
}
for (j = 0; j < argc; j += 1) {
}
num_fd = sd_listen_fds(0);
/* parse the config file */
@ -418,8 +436,8 @@ int main(int argc, char **argv)
logg(LOGG_INFO_NF, "Received %d file descriptor(s) from systemd.\n", num_fd);
if (!tcpsock && !localsock && num_fd == 0) {
logg(LOGG_ERROR, "Please define server type (local and/or TCP).\n");
if (!tcpsock && !localsock && num_fd == 0 && do_local_scan == 0) {
logg(LOGG_ERROR, "Please define server type (local and/or TCP), or pass a file for local scanning.\n");
ret = 1;
break;
}
@ -737,6 +755,52 @@ int main(int argc, char **argv)
}
}
#ifndef _WIN32
if (do_local_scan) {
/*logg(LOGG_ERROR, "Going to do a local scan!!\n");*/
max_threads = optget(opts, "MaxThreads")->numarg;
max_queue = optget(opts, "MaxQueue")->numarg;
idletimeout = optget(opts, "IdleTimeout")->numarg;
if ((thr_pool = thrmgr_new(max_threads, idletimeout, max_queue, scanner_thread)) == NULL) {
/*logg(LOGG_ERROR, "thrmgr_new failed\n");*/
exit(-1);
} else {
/*logg(LOGG_ERROR, "thrmgr_new success\n");*/
}
client_conn_t conn;
/*const char *cmd = NULL;*/
/*int rc;*/
struct cl_scan_options options;
memset(&options, 0, sizeof(struct cl_scan_options));
/* New data available to read on socket. */
options.general |= AE_SCAN_LOCAL_SCAN;
options.general |= CL_SCAN_GENERAL_COLLECT_METADATA;
memset(&conn, 0, sizeof(conn));
/*conn.scanfd = buf->recvfd;*/
/*buf->recvfd = -1;*/
/*conn.sd = buf->fd;*/
conn.options = &options;
conn.opts = opts;
conn.thrpool = thr_pool;
conn.engine = engine;
/*conn.group = buf->group;*/
/*conn.id = buf->id;*/
/*conn.quota = buf->quota;*/
/*conn.filename = buf->dumpname;*/
/*conn.mode = buf->mode;*/
/*conn.term = buf->term;*/
execute_or_dispatch_command(&conn, COMMAND_MULTISCAN, local_scan_file);
/*logg(LOGG_INFO, "Dispatch done, now waiting for threads to finish.\n");*/
thrmgr_wait_for_threads(thr_pool);
logg(LOGG_INFO, "Threads finished!\n");
thrmgr_destroy(thr_pool);
logg(LOGG_INFO, "killed pool\n");
}
if (localsock && num_fd == 0) {
int *t;
mode_t sock_mode, umsk = umask(0777); /* socket is created with 000 to avoid races */
@ -845,17 +909,21 @@ int main(int argc, char **argv)
}
#endif
if (nlsockets == 0) {
if (nlsockets == 0 && do_local_scan == 0) {
logg(LOGG_ERROR, "Not listening on any interfaces\n");
ret = 1;
break;
}
ret = recvloop(lsockets, nlsockets, engine, dboptions, opts);
if (do_local_scan == 0) {
ret = recvloop(lsockets, nlsockets, engine, dboptions, opts);
} else {
/* wait for threadpool to be done? */
}
} while (0);
if (num_fd == 0) {
if (num_fd == 0 && do_local_scan == 0) {
logg(LOGG_DEBUG, "Closing the main socket%s.\n", (nlsockets > 1) ? "s" : "");
for (i = 0; i < nlsockets; i++) {
@ -873,7 +941,9 @@ int main(int argc, char **argv)
#endif
}
free(lsockets);
if (do_local_scan == 0) {
free(lsockets);
}
logg_close();
optfree(opts);

@ -145,7 +145,9 @@ cl_error_t scan_callback(STATBUF *sb, char *filename, const char *msg, enum cli_
struct cb_context context;
char *real_filename = NULL;
if (NULL != filename) {
/*logg(LOGG_ERROR, "scanning a file!\n");*/
if (CL_SUCCESS != cli_realpath((const char *)filename, &real_filename)) {
conn_reply_errno(scandata->conn, msg, "File path check failure:");
logg(LOGG_WARNING, "File path check failure for: %s\n", filename);
@ -157,8 +159,10 @@ cl_error_t scan_callback(STATBUF *sb, char *filename, const char *msg, enum cli_
}
/* detect disconnected socket,
* this should NOT detect half-shutdown sockets (SHUT_WR) */
if (send(scandata->conn->sd, &ret, 0, 0) == -1 && errno != EINTR) {
* this should NOT detect half-shutdown sockets (SHUT_WR)
* skip if we are doing a local scan */
int still_check_send = !(scandata->conn->options->general & AE_SCAN_LOCAL_SCAN);
if (still_check_send && send(scandata->conn->sd, &ret, 0, 0) == -1 && errno != EINTR) {
logg(LOGG_DEBUG_NV, "Client disconnected while command was active!\n");
thrmgr_group_terminate(scandata->conn->group);
if (reason == visit_file)
@ -190,7 +194,7 @@ cl_error_t scan_callback(STATBUF *sb, char *filename, const char *msg, enum cli_
free(filename);
return CL_SUCCESS;
case warning_skipped_dir:
logg(LOGG_WARNING, "Directory recursion limit reached, skipping %s\n", msg);
/*logg(LOGG_WARNING, "Directory recursion limit reached, skipping %s\n", msg);*/
free(filename);
return CL_SUCCESS;
case warning_skipped_link:
@ -227,11 +231,12 @@ cl_error_t scan_callback(STATBUF *sb, char *filename, const char *msg, enum cli_
if (type == TYPE_MULTISCAN) {
client_conn_t *client_conn = (client_conn_t *)calloc(1, sizeof(struct client_conn_tag));
/*logg(LOGG_ERROR, "multiscanning a file!\n");*/
if (client_conn) {
client_conn->scanfd = -1;
client_conn->sd = scandata->odesc;
client_conn->filename = filename;
client_conn->cmdtype = COMMAND_MULTISCANFILE;
client_conn->cmdtype = COMMAND_MULTISCANFILE; // NOTE new command - multiscanfile instead of multiscan
client_conn->term = scandata->conn->term;
client_conn->options = scandata->options;
client_conn->opts = scandata->opts;
@ -272,12 +277,12 @@ cl_error_t scan_callback(STATBUF *sb, char *filename, const char *msg, enum cli_
if (thrmgr_group_need_terminate(scandata->conn->group)) {
free(filename);
logg(LOGG_DEBUG, "Client disconnected while scanjob was active\n");
logg(LOGG_ERROR, "Client disconnected while scanjob was active\n");
return ret == CL_ETIMEOUT ? ret : CL_BREAK;
}
if ((ret == CL_VIRUS) && (virname == NULL)) {
logg(LOGG_DEBUG, "%s: reported CL_VIRUS but no virname returned!\n", filename);
logg(LOGG_ERROR, "%s: reported CL_VIRUS but no virname returned!\n", filename);
ret = CL_EMEM;
}
@ -302,7 +307,8 @@ cl_error_t scan_callback(STATBUF *sb, char *filename, const char *msg, enum cli_
} else {
scandata->infected++;
virusaction(filename, virname, scandata->opts);
if (conn_reply_virus(scandata->conn, filename, virname) == -1) {
int still_check_send = !(scandata->conn->options->general & AE_SCAN_LOCAL_SCAN);
if (still_check_send && conn_reply_virus(scandata->conn, filename, virname) == -1) {
free(filename);
return CL_ETIMEOUT;
}

@ -99,7 +99,8 @@ static struct cl_stat dbstat;
void *event_wake_recv = NULL;
void *event_wake_accept = NULL;
static void scanner_thread(void *arg)
/*static void scanner_thread(void *arg)*/
void scanner_thread(void *arg)
{
client_conn_t *conn = (client_conn_t *)arg;
#ifndef _WIN32
@ -123,6 +124,7 @@ static void scanner_thread(void *arg)
sigdelset(&sigset, SIGCONT);
pthread_sigmask(SIG_SETMASK, &sigset, NULL);
#endif
/*logg(LOGG_ERROR, "in scanthread, trying to handle command\n");*/
ret = command(conn, &virus);
if (ret == -1) {
@ -1385,8 +1387,10 @@ int recvloop(int *socketds, unsigned nsockets, struct cl_engine *engine, unsigne
#endif
/* JSON check to prevent engine loading if specified without libjson-c */
if (optget(opts, "GenerateMetadataJson")->enabled)
if (optget(opts, "GenerateMetadataJson")->enabled) {
options.general |= CL_SCAN_GENERAL_COLLECT_METADATA;
logg(LOGG_INFO, "Metadata collection enabled.\n");
}
selfchk = optget(opts, "SelfCheck")->numarg;
if (!selfchk) {

@ -48,6 +48,9 @@ void sighandler(int sig);
void sighandler_th(int sig);
void sigsegv(int sig);
/* added for AEScan */
void scanner_thread(void *arg);
extern pthread_mutex_t exit_mutex, reload_mutex;
extern int progexit, reload;

@ -237,6 +237,7 @@ int command(client_conn_t *conn, int *virus)
break;
case COMMAND_MULTISCAN: {
int multiscan, max, alive;
/*logg(LOGG_ERROR, "MULTISCAN command\n");*/
/* use MULTISCAN only for directories (bb #1869) */
if (CLAMSTAT(conn->filename, &sb) == 0 &&
@ -283,6 +284,7 @@ int command(client_conn_t *conn, int *virus)
scandata.type = TYPE_SCAN;
scandata.thr_pool = NULL;
/* TODO: check ret value */
/*logg(LOGG_ERROR, "MULTISCANFILE command\n");*/
ret = scan_callback(NULL, conn->filename, conn->filename, visit_file, &data); /* callback freed it */
conn->filename = NULL;
*virus = scandata.infected;
@ -439,6 +441,9 @@ static int dispatch_command(client_conn_t *conn, enum commands cmd, const char *
case COMMAND_CONTSCAN:
case COMMAND_MULTISCAN:
case COMMAND_ALLMATCHSCAN:
/*logg(LOGG_ERROR, "testing the argument\n");*/
/*logg(LOGG_ERROR, argument);*/
/*logg(LOGG_ERROR, "argument: %s\n", argument);*/
dup_conn->filename = cli_strdup_to_utf8(argument);
if (!dup_conn->filename) {
logg(LOGG_ERROR, "Failed to allocate memory for filename\n");
@ -461,11 +466,14 @@ static int dispatch_command(client_conn_t *conn, enum commands cmd, const char *
}
if (!dup_conn->group)
bulk = 0;
/*logg(LOGG_ERROR, "made it to the group dispatch\n");*/
if (!ret && !thrmgr_group_dispatch(dup_conn->thrpool, dup_conn->group, dup_conn, bulk)) {
logg(LOGG_ERROR, "thread dispatch failed\n");
ret = -2;
}
/*logg(LOGG_ERROR, "exited dispatch\n");*/
if (ret) {
/*logg(LOGG_ERROR, "freeing engine\n");*/
cl_engine_free(dup_conn->engine);
free(dup_conn);
}

@ -667,6 +667,7 @@ static void *thrmgr_worker(void *arg)
/* loop looking for work */
for (;;) {
/*logg(LOGG_ERROR, "looking for work\n");*/
if (pthread_mutex_lock(&(threadpool->pool_mutex)) != 0) {
logg(LOGG_ERROR, "Fatal: mutex lock failed\n");
exit(-2);
@ -687,6 +688,7 @@ static void *thrmgr_worker(void *arg)
&(threadpool->pool_mutex), &timeout);
if (retval == ETIMEDOUT) {
must_exit = TRUE;
logg(LOGG_ERROR, "timeout: exiting\n");
break;
}
}
@ -767,6 +769,7 @@ static int thrmgr_dispatch_internal(threadpool_t *threadpool, void *user_data, i
ret = FALSE;
break;
}
/*logg(LOGG_ERROR, "added to queue\n");*/
items = threadpool->single_queue->item_count + threadpool->bulk_queue->item_count;
if ((threadpool->thr_idle < items) &&
@ -777,6 +780,7 @@ static int thrmgr_dispatch_internal(threadpool_t *threadpool, void *user_data, i
logg(LOGG_ERROR, "pthread_create failed\n");
} else {
threadpool->thr_alive++;
/*logg(LOGG_ERROR, "made a thread\n");*/
}
}
pthread_cond_signal(&(threadpool->pool_cond));
@ -804,6 +808,9 @@ int thrmgr_group_dispatch(threadpool_t *threadpool, jobgroup_t *group, void *use
logg(LOGG_DEBUG_NV, "THRMGR: active jobs for %p: %d\n", group, group->jobs);
pthread_mutex_unlock(&group->mutex);
}
/*} else {*/
/* logg(LOGG_ERROR, "group is null thrmgr:807\n");*/
/*}*/
if (!(ret = thrmgr_dispatch_internal(threadpool, user_data, bulk)) && group) {
pthread_mutex_lock(&group->mutex);
group->jobs--;

@ -667,6 +667,11 @@ const struct clam_option __clam_options[] = {
{"TCPSocket", NULL, 0, CLOPT_TYPE_NUMBER, MATCH_NUMBER, -1, NULL, 0, OPT_MILTER | OPT_DEPRECATED, "", ""},
{"TemporaryDirectory", NULL, 0, CLOPT_TYPE_STRING, NULL, -1, NULL, 0, OPT_MILTER | OPT_DEPRECATED, "", ""},
/* AEScan Specific Options */
/* name, longopt, sopt, argtype, regex, num, str, flags, owner, description, suggested */
/*{"DoLocalScan", "do-localscan", 'do-local-scan', CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM, "Enable Local Scan", "no"},*/
{"LocalScanningFile", "local-scanning-file", 'local-scanning-file', CLOPT_TYPE_STRING, NULL, -1, NULL, 0, OPT_CLAMD | OPT_FRESHCLAM, "Scan files directly without using a daemon. File path for scanning.", "/home/"},
{NULL, NULL, 0, 0, NULL, 0, NULL, 0, 0, NULL, NULL}};
const struct clam_option *clam_options = __clam_options;

@ -170,6 +170,9 @@ struct cl_scan_options {
#define CL_SCAN_GENERAL_UNPRIVILEGED 0x10 /* scanner will not have read access to files. */
#define CL_SCAN_GENERAL_STORE_HTML_URLS 0x20 /* Store urls found in html <a and <form tags when recording JSON metadata */
/* AEScan options */
#define AE_SCAN_LOCAL_SCAN 0x40 /* scan local files only */
/* parsing capabilities options */
#define CL_SCAN_PARSE_ARCHIVE 0x1
#define CL_SCAN_PARSE_ELF 0x2

Loading…
Cancel
Save