2018-12-03 13:17:07 -05:00
/*
2020-01-03 15:44:07 -05:00
* Copyright ( C ) 2019 - 2020 Cisco Systems , Inc . and / or its affiliates . All rights reserved .
2018-12-03 13:17:07 -05:00
*
2019-06-05 15:33:26 -04:00
* Authors : Mickey Sola
2018-12-03 13:17:07 -05:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* 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>
2019-07-10 16:54:53 -04:00
# include <pthread.h>
# include <signal.h>
2018-12-03 13:17:07 -05:00
# ifdef HAVE_UNISTD_H
# include <unistd.h>
# endif
# ifndef _WIN32
# include <sys/time.h>
# endif
# include <time.h>
# include <signal.h>
2019-06-20 16:54:08 -04:00
# if defined(FANOTIFY)
# include <sys/fanotify.h>
# include <fcntl.h>
# endif
2018-12-03 13:17:07 -05:00
2019-07-25 12:21:07 -04:00
# include "libclamav/clamav.h"
# include "libclamav/others.h"
# include "shared/output.h"
# include "shared/misc.h"
# include "shared/optparser.h"
# include "shared/actions.h"
2019-06-05 11:52:45 -04:00
2019-07-25 12:21:07 -04:00
# include "clamonacc.h"
# include "client/client.h"
# include "fanotif/fanotif.h"
# include "inotif/inotif.h"
# include "scan/queue.h"
2018-12-03 13:17:07 -05:00
2019-07-25 12:42:08 -04:00
pthread_t ddd_pid = 0 ;
2019-07-18 16:09:34 -04:00
pthread_t scan_queue_pid = 0 ;
2018-12-03 13:17:07 -05:00
2019-07-10 16:54:53 -04:00
static void onas_handle_signals ( ) ;
2019-06-10 17:39:41 -04:00
static int startup_checks ( struct onas_context * ctx ) ;
2019-07-10 16:54:53 -04:00
static struct onas_context * g_ctx = NULL ;
static void onas_clamonacc_exit ( int sig )
{
2019-07-25 12:42:08 -04:00
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 ) ;
2019-07-10 16:54:53 -04:00
}
2019-06-10 17:39:41 -04:00
2018-12-03 13:17:07 -05:00
int main ( int argc , char * * argv )
{
2019-07-25 12:42:08 -04:00
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 ;
}
2019-06-14 14:52:20 -04:00
# ifndef _WIN32
2019-07-25 12:42:08 -04:00
/* 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 ;
2019-06-14 14:52:20 -04:00
}
2019-07-25 12:42:08 -04:00
}
2019-06-14 14:52:20 -04:00
# endif
2019-07-25 12:42:08 -04:00
/* Setup our client */
switch ( onas_setup_client ( & ctx ) ) {
case CL_SUCCESS :
if ( CL_SUCCESS = = onas_check_client_connection ( & ctx ) ) {
break ;
}
2020-01-28 14:05:44 -05:00
__attribute__ ( ( fallthrough ) ) ;
2019-07-25 12:42:08 -04:00
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 ) ) {
2019-05-09 12:42:33 -04:00
case CL_SUCCESS :
break ;
case CL_BREAK :
2019-07-25 12:42:08 -04:00
ret = 0 ;
goto clean_up ;
break ;
2019-05-09 12:42:33 -04:00
case CL_EARG :
default :
2019-07-25 12:42:08 -04:00
mprintf ( " !Clamonacc: can't setup fanotify \n " ) ;
2019-05-09 12:42:33 -04:00
ret = 2 ;
goto clean_up ;
break ;
}
2019-07-25 12:42:08 -04:00
}
2019-02-28 13:13:14 -05:00
# else
2019-07-25 12:42:08 -04:00
mprintf ( " !Clamonacc: currently, this application only runs on linux systems with fanotify enabled \n " ) ;
goto clean_up ;
2019-02-28 13:13:14 -05:00
# endif
2018-12-03 13:17:07 -05:00
2019-07-25 12:42:08 -04:00
/* Setup signal handling */
g_ctx = ctx ;
onas_handle_signals ( ) ;
2019-07-10 16:54:53 -04:00
2019-07-25 12:42:08 -04:00
logg ( " *Clamonacc: beginning event loops \n " ) ;
/* Kick off event loop(s) */
ret = onas_start_eloop ( & ctx ) ;
2018-12-03 13:17:07 -05:00
2019-07-25 12:42:08 -04:00
/* Clean up */
2019-02-28 13:13:14 -05:00
clean_up :
2019-07-25 12:42:08 -04:00
onas_cleanup ( ctx ) ;
exit ( ret ) ;
2019-02-28 13:13:14 -05:00
}
2018-12-03 13:17:07 -05:00
2019-07-25 12:42:08 -04:00
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
2019-07-10 16:54:53 -04:00
* SIGFPE , SIGILL , SIGSEGV , or SIGBUS signal */
2019-07-25 12:42:08 -04:00
sigdelset ( & sigset , SIGFPE ) ;
sigdelset ( & sigset , SIGILL ) ;
sigdelset ( & sigset , SIGSEGV ) ;
sigdelset ( & sigset , SIGINT ) ;
sigdelset ( & sigset , SIGTERM ) ;
2019-07-10 16:54:53 -04:00
# ifdef SIGBUS
2019-07-25 12:42:08 -04:00
sigdelset ( & sigset , SIGBUS ) ;
2019-07-10 16:54:53 -04:00
# endif
2019-07-25 12:42:08 -04:00
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 ) ;
2019-07-10 16:54:53 -04:00
}
2019-07-25 12:42:08 -04:00
struct onas_context * onas_init_context ( void )
{
struct onas_context * ctx = ( struct onas_context * ) cli_malloc ( sizeof ( struct onas_context ) ) ;
2019-02-28 13:13:14 -05:00
if ( NULL = = ctx ) {
return NULL ;
2018-12-03 13:17:07 -05:00
}
2019-02-28 13:13:14 -05:00
memset ( ctx , 0 , sizeof ( struct onas_context ) ) ;
return ctx ;
}
2018-12-03 13:17:07 -05:00
2019-07-25 12:42:08 -04:00
cl_error_t onas_check_client_connection ( struct onas_context * * ctx )
{
2018-12-03 13:17:07 -05:00
2019-07-25 12:42:08 -04:00
cl_error_t err = CL_SUCCESS ;
2019-03-26 11:34:54 -04:00
2019-07-25 12:42:08 -04:00
/* 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 ;
2019-02-28 13:13:14 -05:00
}
2018-12-03 13:17:07 -05:00
2019-07-25 12:42:08 -04:00
int onas_start_eloop ( struct onas_context * * ctx )
{
int ret = 0 ;
2018-12-03 13:17:07 -05:00
2019-07-25 12:42:08 -04:00
if ( ! ctx | | ! * ctx ) {
mprintf ( " !Clamonacc: unable to start clamonacc. (bad context) \n " ) ;
return CL_EARG ;
}
2019-02-28 13:13:14 -05:00
2019-06-05 11:52:45 -04:00
# if defined(FANOTIFY)
2019-07-25 12:42:08 -04:00
ret = onas_fan_eloop ( ctx ) ;
2019-02-28 13:13:14 -05:00
# endif
2019-07-25 12:42:08 -04:00
return ret ;
2018-12-03 13:17:07 -05:00
}
2019-07-25 12:42:08 -04:00
static int startup_checks ( struct onas_context * ctx )
{
2019-06-10 17:39:41 -04:00
2019-06-20 16:54:08 -04:00
# if defined(FANOTIFY)
2019-07-25 12:42:08 -04:00
char faerr [ 128 ] ;
2019-06-20 16:54:08 -04:00
# endif
2019-07-25 12:42:08 -04:00
int ret = 0 ;
cl_error_t err = CL_SUCCESS ;
2019-06-10 17:39:41 -04:00
2019-07-25 12:42:08 -04:00
if ( optget ( ctx - > opts , " help " ) - > enabled ) {
help ( ) ;
ret = 2 ;
goto done ;
}
2019-06-10 17:39:41 -04:00
2019-06-20 16:54:08 -04:00
# if defined(FANOTIFY)
2019-07-25 12:42:08 -04:00
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 ;
}
2019-06-20 16:54:08 -04:00
# endif
2019-07-25 12:42:08 -04:00
if ( curl_global_init ( CURL_GLOBAL_NOTHING ) ) {
ret = 2 ;
goto done ;
}
2019-12-20 17:04:23 -05:00
if ( optget ( ctx - > opts , " version " ) - > enabled ) {
onas_print_server_version ( & ctx ) ;
ret = 2 ;
goto done ;
}
if ( optget ( ctx - > opts , " ping " ) - > enabled & & ! optget ( ctx - > opts , " wait " ) - > enabled ) {
onas_ping_clamd ( & ctx ) ;
ret = 2 ;
goto done ;
}
if ( optget ( ctx - > opts , " wait " ) - > enabled ) {
ret = onas_ping_clamd ( & ctx ) ;
if ( ret = = 0 ) {
ret = 0 ;
} else {
ret = 2 ;
goto done ;
}
}
2019-07-25 12:42:08 -04:00
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 ;
}
}
2019-06-10 17:39:41 -04:00
done :
2019-07-25 12:42:08 -04:00
return ret ;
2019-06-10 17:39:41 -04:00
}
2018-12-03 13:17:07 -05:00
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 " ) ;
2020-01-03 15:44:07 -05:00
mprintf ( " (C) 2020 Cisco Systems, Inc. \n " ) ;
2018-12-03 13:17:07 -05:00
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 " ) ;
2019-06-03 16:56:35 -04:00
mprintf ( " --foreground -F Output to foreground and do not daemonize \n " ) ;
2019-02-28 13:13:14 -05:00
mprintf ( " --watch-list=FILE -w FILE Watch directories from FILE \n " ) ;
2019-12-20 17:04:23 -05:00
mprintf ( " --exclude-list=FILE -e FILE Exclude directories from FILE \n " ) ;
mprintf ( " --ping -p A[:I] Ping clamd A times at optional interval I. \n " ) ;
mprintf ( " --wait -w Wait for clamd to start. Optionally use alongside ping to set attempts [A] and interval [I] to check clamd. \n " ) ;
2018-12-03 13:17:07 -05:00
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 ) ;
}
2019-02-28 13:13:14 -05:00
2019-09-11 11:17:49 -07:00
void onas_cleanup ( struct onas_context * ctx )
2019-07-25 12:42:08 -04:00
{
onas_context_cleanup ( ctx ) ;
logg_close ( ) ;
2019-02-28 13:13:14 -05:00
}
2019-09-11 11:17:49 -07:00
void onas_context_cleanup ( struct onas_context * ctx )
2019-07-25 12:42:08 -04:00
{
close ( ctx - > fan_fd ) ;
optfree ( ( struct optstruct * ) ctx - > opts ) ;
optfree ( ( struct optstruct * ) ctx - > clamdopts ) ;
ctx - > opts = NULL ;
ctx - > clamdopts = NULL ;
free ( ctx ) ;
2019-02-28 13:13:14 -05:00
}