mirror of https://github.com/Cisco-Talos/clamav
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
254 lines
7.2 KiB
254 lines
7.2 KiB
/*
|
|
* Copyright (C) 2007-2009 Sourcefire, Inc.
|
|
* Author: Tomasz Kojm
|
|
* Author: John Ogness <dazukocode@ogness.net>
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#if HAVE_CONFIG_H
|
|
#include "clamav-config.h"
|
|
#endif
|
|
|
|
#ifdef CLAMUKO
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <signal.h>
|
|
#include <pthread.h>
|
|
|
|
#include "libclamav/clamav.h"
|
|
#include "libclamav/scanners.h"
|
|
|
|
#include "shared/optparser.h"
|
|
#include "shared/output.h"
|
|
|
|
#include "server.h"
|
|
#include "others.h"
|
|
#include "dazukofs.h"
|
|
#include "clamuko.h"
|
|
|
|
static pthread_mutex_t running_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
static dazukofs_handle_t shutdown_hndl;
|
|
static pthread_cond_t shutdown_cond;
|
|
|
|
static void clamuko_exit(int sig)
|
|
{
|
|
pthread_cond_signal(&shutdown_cond);
|
|
}
|
|
|
|
static int setup_shutdown_handle(const char *groupname)
|
|
{
|
|
/* is another server thread is already running? */
|
|
if(shutdown_hndl) return -1;
|
|
|
|
if(pthread_cond_init(&shutdown_cond, NULL)) return -1;
|
|
|
|
/* handle used for shutdown by signal */
|
|
shutdown_hndl = dazukofs_open(groupname, DAZUKOFS_TRACK_GROUP);
|
|
if(!shutdown_hndl) {
|
|
logg("!Clamuko: Can't register with DazukoFS\n");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void shutdown_clamuko(void)
|
|
{
|
|
dazukofs_handle_t hndl = shutdown_hndl;
|
|
|
|
/* Set shutdown_hndl before closing because the close will
|
|
* immediately cause the scan threads to be interrupted.
|
|
* But they will only abort if shutdown_hndl is NULL. */
|
|
shutdown_hndl = NULL;
|
|
|
|
if(hndl) dazukofs_close(hndl, DAZUKOFS_REMOVE_GROUP);
|
|
}
|
|
|
|
static void *clamuko_scanth(void *arg)
|
|
{
|
|
struct thrarg *tharg = (struct thrarg *) arg;
|
|
sigset_t sigset;
|
|
unsigned int sizelimit = 0, virsize;
|
|
struct stat sb;
|
|
dazukofs_handle_t scan_hndl;
|
|
struct dazukofs_access acc;
|
|
const char *groupname = "ClamAV";
|
|
int skip_scan = 0, extinfo;
|
|
const char *virname;
|
|
char filename[4096], virhash[33];
|
|
|
|
/* ignore all signals */
|
|
sigfillset(&sigset);
|
|
/* The behavior of a process is undefined after it ignores a
|
|
* SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */
|
|
sigdelset(&sigset, SIGFPE);
|
|
sigdelset(&sigset, SIGILL);
|
|
sigdelset(&sigset, SIGSEGV);
|
|
#ifdef SIGBUS
|
|
sigdelset(&sigset, SIGBUS);
|
|
#endif
|
|
pthread_sigmask(SIG_SETMASK, &sigset, NULL);
|
|
|
|
/* register */
|
|
scan_hndl = dazukofs_open(groupname, DAZUKOFS_TRACK_GROUP);
|
|
if(!scan_hndl) {
|
|
logg("!Clamuko: Can't register with DazukoFS\n");
|
|
return NULL;
|
|
} else {
|
|
logg("Clamuko: Correctly registered with DazukoFS.\n");
|
|
}
|
|
|
|
/* access mask (not used by DazukoFS) */
|
|
if(optget(tharg->opts, "ClamukoScanOnOpen")->enabled)
|
|
logg("!Clamuko: ClamukoScanOnOpen ignored when using DazukoFS.\n");
|
|
if(optget(tharg->opts, "ClamukoScanOnClose")->enabled)
|
|
logg("!Clamuko: ClamukoScanOnClose ignored when using DazukoFS.\n");
|
|
if(optget(tharg->opts, "ClamukoScanOnExec")->enabled)
|
|
logg("!Clamuko: ClamukoScanOnExec ignored when using DazukoFS.\n");
|
|
if(optget(tharg->opts, "ClamukoIncludePath")->enabled)
|
|
logg("!Clamuko: ClamukoIncludePath ignored when using DazukoFS.\n");
|
|
if(optget(tharg->opts, "ClamukoExcludePath")->enabled)
|
|
logg("!Clamuko: ClamukoExcludePath ignored when using DazukoFS.\n");
|
|
|
|
sizelimit = optget(tharg->opts, "ClamukoMaxFileSize")->numarg;
|
|
if(sizelimit)
|
|
logg("Clamuko: Max file size limited to %u bytes.\n", sizelimit);
|
|
else
|
|
logg("Clamuko: File size limit disabled.\n");
|
|
|
|
extinfo = optget(tharg->opts, "ExtendedDetectionInfo")->enabled;
|
|
|
|
while(1) {
|
|
if(dazukofs_get_access(scan_hndl, &acc)) {
|
|
if(!shutdown_hndl)
|
|
break;
|
|
continue;
|
|
}
|
|
|
|
if(!fstat(acc.fd, &sb)) {
|
|
if(S_ISDIR(sb.st_mode)) {
|
|
/* don't try to scan directories */
|
|
skip_scan = 1;
|
|
} else if(sb.st_size > sizelimit) {
|
|
dazukofs_get_filename(&acc, filename, sizeof(filename));
|
|
logg("*Clamuko: %s skipped (too big)\n", filename);
|
|
skip_scan = 1;
|
|
}
|
|
}
|
|
|
|
if(skip_scan) {
|
|
acc.deny = 0;
|
|
/* reset skip flag */
|
|
skip_scan = 0;
|
|
} else if(cli_scandesc_stats(acc.fd, &virname, virhash, &virsize, NULL, tharg->engine,
|
|
tharg->options) == CL_VIRUS) {
|
|
dazukofs_get_filename(&acc, filename, sizeof(filename));
|
|
if(extinfo && virsize)
|
|
logg("Clamuko: %s: %s(%s:%u) FOUND\n", filename, virname, virhash, virsize);
|
|
else
|
|
logg("Clamuko: %s: %s FOUND\n", filename, virname);
|
|
/* we can not perform any special action because it will
|
|
* trigger DazukoFS recursively */
|
|
acc.deny = 1;
|
|
} else {
|
|
acc.deny = 0;
|
|
}
|
|
|
|
if(dazukofs_return_access(scan_hndl, &acc)) {
|
|
if(shutdown_hndl)
|
|
logg("!Clamuko: Can't return access to DazukoFS.\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
dazukofs_close(scan_hndl, 0);
|
|
|
|
if(shutdown_hndl)
|
|
logg("!Clamuko: A scanner thread has unexpectedly shutdown.\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void *clamukofsth(void *arg)
|
|
{
|
|
struct thrarg *tharg = (struct thrarg *) arg;
|
|
sigset_t sigset;
|
|
struct sigaction act;
|
|
pthread_t *clamuko_pids = NULL;
|
|
const char *groupname = "ClamAV";
|
|
int count;
|
|
int started;
|
|
|
|
/* is another server thread already working? */
|
|
if(pthread_mutex_trylock(&running_mutex))
|
|
return NULL;
|
|
|
|
/* ignore all signals except SIGUSR1 */
|
|
sigfillset(&sigset);
|
|
sigdelset(&sigset, SIGUSR1);
|
|
/* The behavior of a process is undefined after it ignores a
|
|
* SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */
|
|
sigdelset(&sigset, SIGFPE);
|
|
sigdelset(&sigset, SIGILL);
|
|
sigdelset(&sigset, SIGSEGV);
|
|
#ifdef SIGBUS
|
|
sigdelset(&sigset, SIGBUS);
|
|
#endif
|
|
pthread_sigmask(SIG_SETMASK, &sigset, NULL);
|
|
|
|
count = optget(tharg->opts, "ClamukoScannerCount")->numarg;
|
|
if(count < 1) goto out;
|
|
|
|
clamuko_pids = calloc(count, sizeof(pthread_t));
|
|
if(!clamuko_pids) goto out;
|
|
|
|
if(setup_shutdown_handle(groupname)) goto out;
|
|
|
|
act.sa_handler = clamuko_exit;
|
|
sigfillset(&(act.sa_mask));
|
|
sigaction(SIGUSR1, &act, NULL);
|
|
sigaction(SIGSEGV, &act, NULL);
|
|
|
|
for(started = 0; started < count; started++) {
|
|
pthread_attr_t clamuko_attr;
|
|
|
|
if(pthread_attr_init(&clamuko_attr)) break;
|
|
pthread_attr_setdetachstate(&clamuko_attr, PTHREAD_CREATE_JOINABLE);
|
|
if(pthread_create(&clamuko_pids[started], &clamuko_attr,
|
|
clamuko_scanth, tharg)) break;
|
|
logg("Clamuko: Started scanner thread %d.\n", started);
|
|
}
|
|
|
|
pthread_cond_wait(&shutdown_cond, &running_mutex);
|
|
logg("Clamuko: Stop signal received.\n");
|
|
|
|
shutdown_clamuko();
|
|
|
|
for(started-- ; started >= 0; started--) {
|
|
logg("Clamuko: Waiting for scanner thread %d to finish.\n", started);
|
|
pthread_join(clamuko_pids[started], NULL);
|
|
}
|
|
|
|
logg("Clamuko: Stopped.\n");
|
|
out:
|
|
if(clamuko_pids) free(clamuko_pids);
|
|
pthread_mutex_unlock(&running_mutex);
|
|
return NULL;
|
|
}
|
|
|
|
#endif
|
|
|