987f7a7d |
/* |
7894638e |
* Copyright (C) 2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved. |
987f7a7d |
* |
7894638e |
* Authors: Mickey Sola |
987f7a7d |
*
* 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
#include <stdio.h>
#include <string.h>
#include <stdlib.h> |
a02cc492 |
#include <pthread.h>
#include <signal.h> |
987f7a7d |
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifndef _WIN32
#include <sys/time.h>
#endif
#include <time.h>
#include <signal.h> |
f972802f |
#if defined(FANOTIFY)
#include <sys/fanotify.h>
#include <fcntl.h>
#endif |
987f7a7d |
|
56426d28 |
#include "libclamav/clamav.h"
#include "libclamav/others.h"
#include "shared/output.h"
#include "shared/misc.h"
#include "shared/optparser.h"
#include "shared/actions.h" |
fe7a6972 |
|
56426d28 |
#include "clamonacc.h"
#include "client/client.h"
#include "fanotif/fanotif.h"
#include "inotif/inotif.h"
#include "scan/queue.h" |
987f7a7d |
|
43422579 |
pthread_t ddd_pid = 0; |
ad49ff53 |
pthread_t scan_queue_pid = 0; |
987f7a7d |
|
a02cc492 |
static void onas_handle_signals(); |
a3ae175e |
static int startup_checks(struct onas_context *ctx); |
a02cc492 |
static struct onas_context *g_ctx = NULL;
static void onas_clamonacc_exit(int sig)
{ |
43422579 |
logg("*Clamonacc: onas_clamonacc_exit(), signal %d\n", sig);
if (sig == 11) {
logg("!Clamonacc: clamonacc has experienced a fatal error, if you continue to see this error, please run clamonacc with --debug and report the issue and crash report to the developpers\n");
}
if (g_ctx) {
if (g_ctx->fan_fd) {
close(g_ctx->fan_fd);
}
g_ctx->fan_fd = 0;
}
logg("*Clamonacc: attempting to stop event consumer thread ...\n");
if (scan_queue_pid > 0) {
pthread_cancel(scan_queue_pid);
pthread_join(scan_queue_pid, NULL);
}
scan_queue_pid = 0;
logg("*Clamonacc: attempting to stop ddd thread ... \n");
if (ddd_pid > 0) {
pthread_cancel(ddd_pid);
pthread_join(ddd_pid, NULL);
}
ddd_pid = 0;
logg("Clamonacc: stopped\n");
onas_cleanup(g_ctx);
pthread_exit(NULL); |
a02cc492 |
} |
a3ae175e |
|
987f7a7d |
int main(int argc, char **argv)
{ |
43422579 |
const struct optstruct *opts;
const struct optstruct *clamdopts;
struct onas_context *ctx;
int ret = 0;
/* Initialize context */
ctx = onas_init_context();
if (ctx == NULL) {
logg("!Clamonacc: can't initialize context\n");
return 2;
}
/* Parse out all our command line options */
opts = optparse(NULL, argc, argv, 1, OPT_CLAMONACC, OPT_CLAMSCAN, NULL);
if (opts == NULL) {
logg("!Clamonacc: can't parse command line options\n");
return 2;
}
ctx->opts = opts;
/* And our config file options */
clamdopts = optparse(optget(opts, "config-file")->strarg, 0, NULL, 1, OPT_CLAMD, 0, NULL);
if (clamdopts == NULL) {
logg("!Clamonacc: can't parse clamd configuration file %s\n", optget(opts, "config-file")->strarg);
return 2;
}
ctx->clamdopts = clamdopts;
/* Make sure we're good to begin spinup */
ret = startup_checks(ctx);
if (ret) {
goto clean_up;
} |
b49436ff |
#ifndef _WIN32 |
43422579 |
/* Daemonize if sanity checks are good to go */
if (!optget(ctx->opts, "foreground")->enabled) {
if (-1 == daemonize()) {
logg("!Clamonacc: could not daemonize\n");
return 2; |
b49436ff |
} |
43422579 |
} |
b49436ff |
#endif
|
43422579 |
/* Setup our client */
switch (onas_setup_client(&ctx)) {
case CL_SUCCESS:
if (CL_SUCCESS == onas_check_client_connection(&ctx)) {
break;
}
case CL_BREAK:
ret = 0;
logg("*Clamonacc: not setting up client\n");
goto clean_up;
break;
case CL_EARG:
default:
logg("!Clamonacc: can't setup client\n");
ret = 2;
goto clean_up;
break;
}
/* Setup our event queue */
ctx->maxthreads = optget(ctx->clamdopts, "OnAccessMaxThreads")->numarg;
switch (onas_scan_queue_start(&ctx)) {
case CL_SUCCESS:
break;
case CL_BREAK:
case CL_EARG:
case CL_ECREAT:
default:
ret = 2;
logg("!Clamonacc: can't setup event consumer queue\n");
goto clean_up;
break;
}
#if defined(FANOTIFY)
/* Setup fanotify */
switch (onas_setup_fanotif(&ctx)) {
case CL_SUCCESS:
break;
case CL_BREAK:
ret = 0;
goto clean_up;
break;
case CL_EARG:
default:
mprintf("!Clamonacc: can't setup fanotify\n");
ret = 2;
goto clean_up;
break;
}
if (ctx->ddd_enabled) {
/* Setup inotify and kickoff DDD system */
switch (onas_enable_inotif_ddd(&ctx)) { |
7039a5ea |
case CL_SUCCESS:
break;
case CL_BREAK: |
43422579 |
ret = 0;
goto clean_up;
break; |
7039a5ea |
case CL_EARG:
default: |
43422579 |
mprintf("!Clamonacc: can't setup fanotify\n"); |
7039a5ea |
ret = 2;
goto clean_up;
break;
} |
43422579 |
} |
01456705 |
#else |
43422579 |
mprintf("!Clamonacc: currently, this application only runs on linux systems with fanotify enabled\n");
goto clean_up; |
01456705 |
#endif |
987f7a7d |
|
43422579 |
/* Setup signal handling */
g_ctx = ctx;
onas_handle_signals(); |
a02cc492 |
|
43422579 |
logg("*Clamonacc: beginning event loops\n");
/* Kick off event loop(s) */
ret = onas_start_eloop(&ctx); |
987f7a7d |
|
43422579 |
/* Clean up */ |
01456705 |
clean_up: |
43422579 |
onas_cleanup(ctx);
exit(ret); |
01456705 |
} |
987f7a7d |
|
43422579 |
static void onas_handle_signals()
{
sigset_t sigset;
struct sigaction act;
/* ignore all signals except SIGUSR1 */
sigfillset(&sigset);
sigdelset(&sigset, SIGUSR1);
sigdelset(&sigset, SIGUSR2);
/* The behavior of a process is undefined after it ignores a |
a02cc492 |
* SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */ |
43422579 |
sigdelset(&sigset, SIGFPE);
sigdelset(&sigset, SIGILL);
sigdelset(&sigset, SIGSEGV);
sigdelset(&sigset, SIGINT);
sigdelset(&sigset, SIGTERM); |
a02cc492 |
#ifdef SIGBUS |
43422579 |
sigdelset(&sigset, SIGBUS); |
a02cc492 |
#endif |
43422579 |
pthread_sigmask(SIG_SETMASK, &sigset, NULL);
memset(&act, 0, sizeof(struct sigaction));
act.sa_handler = onas_clamonacc_exit;
sigfillset(&(act.sa_mask));
sigaction(SIGUSR2, &act, NULL);
sigaction(SIGTERM, &act, NULL);
sigaction(SIGSEGV, &act, NULL);
sigaction(SIGINT, &act, NULL); |
a02cc492 |
}
|
43422579 |
struct onas_context *onas_init_context(void)
{
struct onas_context *ctx = (struct onas_context *)cli_malloc(sizeof(struct onas_context)); |
01456705 |
if (NULL == ctx) {
return NULL; |
987f7a7d |
}
|
01456705 |
memset(ctx, 0, sizeof(struct onas_context));
return ctx;
} |
987f7a7d |
|
43422579 |
cl_error_t onas_check_client_connection(struct onas_context **ctx)
{ |
987f7a7d |
|
43422579 |
cl_error_t err = CL_SUCCESS; |
424c912b |
|
43422579 |
/* 0 local, non-zero remote, errno set on error */
(*ctx)->isremote = onas_check_remote(ctx, &err);
if (CL_SUCCESS == err) {
logg("*Clamonacc: ");
(*ctx)->isremote ? logg("*daemon is remote\n") : logg("*daemon is local\n");
}
return err ? CL_EACCES : CL_SUCCESS; |
01456705 |
} |
987f7a7d |
|
43422579 |
int onas_start_eloop(struct onas_context **ctx)
{
int ret = 0; |
987f7a7d |
|
43422579 |
if (!ctx || !*ctx) {
mprintf("!Clamonacc: unable to start clamonacc. (bad context)\n");
return CL_EARG;
} |
01456705 |
|
fe7a6972 |
#if defined(FANOTIFY) |
43422579 |
ret = onas_fan_eloop(ctx); |
01456705 |
#endif
|
43422579 |
return ret; |
987f7a7d |
}
|
43422579 |
static int startup_checks(struct onas_context *ctx)
{ |
a3ae175e |
|
f972802f |
#if defined(FANOTIFY) |
43422579 |
char faerr[128]; |
f972802f |
#endif |
43422579 |
int ret = 0;
cl_error_t err = CL_SUCCESS; |
a3ae175e |
|
43422579 |
if (optget(ctx->opts, "help")->enabled) {
help();
ret = 2;
goto done;
} |
a3ae175e |
|
43422579 |
if (optget(ctx->opts, "version")->enabled) {
onas_print_server_version(&ctx);
ret = 2;
goto done;
} |
a3ae175e |
|
f972802f |
#if defined(FANOTIFY) |
43422579 |
ctx->fan_fd = fanotify_init(FAN_CLASS_CONTENT | FAN_UNLIMITED_QUEUE | FAN_UNLIMITED_MARKS, O_LARGEFILE | O_RDONLY);
if (ctx->fan_fd < 0) {
logg("!Clamonacc: fanotify_init failed: %s\n", cli_strerror(errno, faerr, sizeof(faerr)));
if (errno == EPERM) {
logg("!Clamonacc: clamonacc must have elevated permissions ... exiting ...\n");
}
ret = 2;
goto done;
} |
f972802f |
#endif
|
43422579 |
if (curl_global_init(CURL_GLOBAL_NOTHING)) {
ret = 2;
goto done;
}
if (0 == onas_check_remote(&ctx, &err)) {
if (CL_SUCCESS != err) {
logg("!Clamonacc: daemon is local, but a connection could not be established\n");
ret = 2;
goto done;
}
if (!optget(ctx->clamdopts, "OnAccessExcludeUID")->enabled &&
!optget(ctx->clamdopts, "OnAccessExcludeUname")->enabled && !optget(ctx->clamdopts, "OnAccessExcludeRootUID")->enabled) {
logg("!Clamonacc: at least one of OnAccessExcludeUID, OnAccessExcludeUname, or OnAccessExcludeRootUID must be specified ... it is reccomended you exclude the clamd instance UID or uname to prevent infinite event scanning loops\n");
ret = 2;
goto done;
}
} |
a3ae175e |
done: |
43422579 |
return ret; |
a3ae175e |
}
|
987f7a7d |
void help(void)
{
mprintf_stdout = 1;
mprintf("\n");
mprintf(" ClamAV: On Access Scanning Application and Client %s\n", get_version());
mprintf(" By The ClamAV Team: https://www.clamav.net/about.html#credits\n"); |
7894638e |
mprintf(" (C) 2019 Cisco Systems, Inc.\n"); |
987f7a7d |
mprintf("\n");
mprintf(" clamonacc [options] [file/directory/-]\n");
mprintf("\n");
mprintf(" --help -h Show this help\n");
mprintf(" --version -V Print version number and exit\n");
mprintf(" --verbose -v Be verbose\n");
mprintf(" --log=FILE -l FILE Save scanning output to FILE\n"); |
9707132c |
mprintf(" --foreground -F Output to foreground and do not daemonize\n"); |
01456705 |
mprintf(" --watch-list=FILE -w FILE Watch directories from FILE\n"); |
d18a2a67 |
mprintf(" --exclude-list=FILE -e FILE Exclude directories from FILE\n"); |
987f7a7d |
mprintf(" --remove Remove infected files. Be careful!\n");
mprintf(" --move=DIRECTORY Move infected files into DIRECTORY\n");
mprintf(" --copy=DIRECTORY Copy infected files into DIRECTORY\n");
mprintf(" --config-file=FILE Read configuration from FILE.\n");
mprintf(" --allmatch -z Continue scanning within file after finding a match.\n");
mprintf(" --fdpass Pass filedescriptor to clamd (useful if clamd is running as a different user)\n");
mprintf(" --stream Force streaming files to clamd (for debugging and unit testing)\n");
mprintf("\n");
exit(0);
} |
01456705 |
|
43422579 |
void *onas_cleanup(struct onas_context *ctx)
{
onas_context_cleanup(ctx);
logg_close(); |
01456705 |
}
|
43422579 |
void *onas_context_cleanup(struct onas_context *ctx)
{
close(ctx->fan_fd);
optfree((struct optstruct *)ctx->opts);
optfree((struct optstruct *)ctx->clamdopts);
ctx->opts = NULL;
ctx->clamdopts = NULL;
free(ctx); |
01456705 |
} |