clamd/clamuko.c
e3aaff8e
 /*
086eab5c
  *  Copyright (C) 2007-2009 Sourcefire, Inc.
  *
  *  Authors: Tomasz Kojm
e3aaff8e
  *
  *  This program is free software; you can redistribute it and/or modify
bb34cb31
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
e3aaff8e
  *
  *  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
48b7b4a7
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  *  MA 02110-1301, USA.
e3aaff8e
  */
 
6d6e8271
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
e3aaff8e
 #ifdef CLAMUKO
 
 #include <stdio.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <signal.h>
 #include <pthread.h>
389adb9e
 #include <string.h>
bd8603aa
 
 #include "libclamav/clamav.h"
edbba730
 #include "libclamav/scanners.h"
bd8603aa
 
064b4a0c
 #include "shared/optparser.h"
bd8603aa
 #include "shared/output.h"
e3aaff8e
 
 #include "server.h"
 #include "others.h"
 #include "dazukoio.h"
adc16eb5
 #include "clamukofs.h"
e3aaff8e
 #include "clamuko.h"
769f37a6
 #include "scanner.h"
e3aaff8e
 
32fc1d7b
 struct dazuko_access *acc;
949c6fe5
 short int clamuko_scanning;
fc83da82
 static void clamuko_exit(int sig)
e3aaff8e
 {
 
     logg("*Clamuko: clamuko_exit(), signal %d\n", sig);
 
     if(clamuko_scanning) {
32fc1d7b
 	logg("*Clamuko: stopped while scanning %s\n", acc->filename);
 	acc->deny = 0;
e3aaff8e
 	dazukoReturnAccess(&acc); /* is it needed ? */
     }
 
32fc1d7b
     if(dazukoUnregister())
 	logg("!Can't unregister with Dazuko\n");
 
46c2e927
     logg("Clamuko stopped.\n");
2b278a02
 
     pthread_exit(NULL);
e3aaff8e
 }
 
adc16eb5
 static void *clamukolegacyth(void *arg)
e3aaff8e
 {
 	struct thrarg *tharg = (struct thrarg *) arg;
 	sigset_t sigset;
fb787a06
 	const char *virname;
e3aaff8e
         struct sigaction act;
 	unsigned long mask = 0;
064b4a0c
 	const struct optstruct *pt;
e3aaff8e
 	short int scan;
edbba730
 	int sizelimit = 0, extinfo;
e3aaff8e
 	struct stat sb;
769f37a6
 	struct cb_context context;
e3aaff8e
 
 
46c2e927
     clamuko_scanning = 0;
e3aaff8e
 
     /* ignore all signals except SIGUSR1 */
     sigfillset(&sigset);
     sigdelset(&sigset, SIGUSR1);
efac2f09
     /* The behavior of a process is undefined after it ignores a 
      * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */
     sigdelset(&sigset, SIGFPE);
     sigdelset(&sigset, SIGILL);
ac480646
     sigdelset(&sigset, SIGSEGV);
efac2f09
 #ifdef SIGBUS    
     sigdelset(&sigset, SIGBUS);
 #endif
e3aaff8e
     pthread_sigmask(SIG_SETMASK, &sigset, NULL);
7af32d0b
     memset(&act, 0, sizeof(struct sigaction));
e3aaff8e
     act.sa_handler = clamuko_exit;
     sigfillset(&(act.sa_mask));
     sigaction(SIGUSR1, &act, NULL);
ac480646
     sigaction(SIGSEGV, &act, NULL);
e3aaff8e
 
     /* register */
32fc1d7b
     if(dazukoRegister("ClamAV", "r+")) {
e3aaff8e
 	logg("!Clamuko: Can't register with Dazuko\n");
 	return NULL;
     } else
 	logg("Clamuko: Correctly registered with Dazuko.\n");
 
     /* access mask */
064b4a0c
     if(optget(tharg->opts, "ClamukoScanOnOpen")->enabled) {
e3aaff8e
 	logg("Clamuko: Scan-on-open mode activated.\n");
32fc1d7b
 	mask |= DAZUKO_ON_OPEN;
e3aaff8e
     }
064b4a0c
     if(optget(tharg->opts, "ClamukoScanOnClose")->enabled) {
e3aaff8e
 	logg("Clamuko: Scan-on-close mode activated.\n");
32fc1d7b
 	mask |= DAZUKO_ON_CLOSE;
e3aaff8e
     }
064b4a0c
     if(optget(tharg->opts, "ClamukoScanOnExec")->enabled) {
e3aaff8e
 	logg("Clamuko: Scan-on-exec mode activated.\n");
32fc1d7b
 	mask |= DAZUKO_ON_EXEC;
e3aaff8e
     }
 
     if(!mask) {
 	logg("!Access mask is not configured properly.\n");
35c49455
 	dazukoUnregister();
e3aaff8e
 	return NULL;
     }
 
     if(dazukoSetAccessMask(mask)) {
 	logg("!Clamuko: Can't set access mask in Dazuko.\n");
35c49455
 	dazukoUnregister();
e3aaff8e
 	return NULL;
     }
 
064b4a0c
     if((pt = optget(tharg->opts, "ClamukoIncludePath"))->enabled) {
e3aaff8e
 	while(pt) {
 	    if((dazukoAddIncludePath(pt->strarg))) {
 		logg("!Clamuko: Dazuko -> Can't include path %s\n", pt->strarg);
35c49455
 		dazukoUnregister();
e3aaff8e
 		return NULL;
 	    } else
 		logg("Clamuko: Included path %s\n", pt->strarg);
 
064b4a0c
 	    pt = (struct optstruct *) pt->nextarg;
e3aaff8e
 	}
     } else {
 	logg("!Clamuko: please include at least one path.\n");
35c49455
 	dazukoUnregister();
e3aaff8e
 	return NULL;
     }
 
064b4a0c
     if((pt = optget(tharg->opts, "ClamukoExcludePath"))->enabled) {
e3aaff8e
 	while(pt) {
 	    if((dazukoAddExcludePath(pt->strarg))) {
 		logg("!Clamuko: Dazuko -> Can't exclude path %s\n", pt->strarg);
35c49455
 		dazukoUnregister();
e3aaff8e
 		return NULL;
 	    } else
 		logg("Clamuko: Excluded path %s\n", pt->strarg);
 
064b4a0c
 	    pt = (struct optstruct *) pt->nextarg;
e3aaff8e
 	}
     }
 
064b4a0c
     sizelimit = optget(tharg->opts, "ClamukoMaxFileSize")->numarg;
e3aaff8e
     if(sizelimit)
 	logg("Clamuko: Max file size limited to %d bytes.\n", sizelimit);
     else
 	logg("Clamuko: File size limit disabled.\n");
 
edbba730
     extinfo = optget(tharg->opts, "ExtendedDetectionInfo")->enabled;
 
e3aaff8e
     while(1) {
 
 	if(dazukoGetAccess(&acc) == 0) {
 	    clamuko_scanning = 1;
 	    scan = 1;
 
 	    if(sizelimit) {
32fc1d7b
 		stat(acc->filename, &sb);
e3aaff8e
 		if(sb.st_size > sizelimit) {
 		    scan = 0;
32fc1d7b
 		    logg("*Clamuko: %s skipped (too big)\n", acc->filename);
e3aaff8e
 		}
 	    }
 
23b57d67
 	    if(clamuko_checkowner(acc->pid, tharg->opts)) {
 		scan = 0;
 		logg("*Clamuko: %s skipped (excluded UID)\n", acc->filename);
 	    }
 
769f37a6
 	    context.filename = acc->filename;
 	    context.virsize = 0;
 	    if(scan && cl_scanfile_callback(acc->filename, &virname, NULL, tharg->engine, tharg->options, &context) == CL_VIRUS) {
 		if(context.virsize)
49f8de22
 		    detstats_add(virname, acc->filename, context.virsize, context.virhash);
 		if(extinfo && context.virsize)
769f37a6
 		    logg("Clamuko: %s: %s(%s:%llu) FOUND\n", acc->filename, virname, context.virhash, context.virsize);
edbba730
 		else
 		    logg("Clamuko: %s: %s FOUND\n", acc->filename, virname);
064b4a0c
 		virusaction(acc->filename, virname, tharg->opts);
32fc1d7b
 		acc->deny = 1;
e3aaff8e
 	    } else
32fc1d7b
 		acc->deny = 0;
e3aaff8e
 
 	    if(dazukoReturnAccess(&acc)) {
 		logg("!Can't return access to Dazuko.\n");
 		logg("Clamuko stopped.\n");
 		dazukoUnregister();
 		clamuko_scanning = 0;
 		return NULL;
 	    }
 
 	    clamuko_scanning = 0;
 	}
     }
 
     /* can't be ;) */
     return NULL;
 }
 
adc16eb5
 void *clamukoth(void *arg)
 {
 	struct stat s;
 
     /* we use DazukoFS if /dev/dazukofs.ctrl exists and it is a
      * character device, otherwise we use Dazuko */
     if(stat("/dev/dazukofs.ctrl", &s) != 0) return clamukolegacyth(arg);
     if(!S_ISCHR(s.st_mode)) return clamukolegacyth(arg);
     return clamukofsth(arg);
 }
 
0f41f5ec
 #elif defined(CLAMAUTH)
 
 #include <stdio.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/uio.h>
 #include <fcntl.h>
 #include <signal.h>
 #include <pthread.h>
 #include <string.h>
 #include <errno.h>
 
 #include "libclamav/clamav.h"
 #include "libclamav/scanners.h"
 
 #include "shared/optparser.h"
 #include "shared/output.h"
 
 #include "server.h"
 #include "others.h"
 #include "scanner.h"
 
 #define SUPPORTED_PROTOCOL  2
 
 static int cauth_fd = -1;
 
 struct ClamAuthEvent {
     unsigned int action;
     char path[1024];
     unsigned int pid;
 };
 
 static void cauth_exit(int sig)
 {
     logg("*ClamAuth: cauth_exit(), signal %d\n", sig);
     if(cauth_fd > 0)
 	close(cauth_fd);
     pthread_exit(NULL);
     logg("ClamAuth: stopped\n");
 }
 
 static int cauth_scanfile(const char *fname, int extinfo, struct thrarg *tharg)
 {
 	struct cb_context context;
 	const char *virname;
 	int ret = 0, fd;
 
     context.filename = fname;
     context.virsize = 0;
 
     fd = open(fname, O_RDONLY);
     if(fd == -1)
 	return -1;
 
     if(cl_scandesc_callback(fd, &virname, NULL, tharg->engine, tharg->options, &context) == CL_VIRUS) {
 	if(context.virsize)
 	    detstats_add(virname, fname, context.virsize, context.virhash);
 	if(extinfo && context.virsize)
 	    logg("ClamAuth: %s: %s(%s:%llu) FOUND\n", fname, virname, context.virhash, context.virsize);
 	else
 	    logg("ClamAuth: %s: %s FOUND\n", fname, virname);
 	virusaction(fname, virname, tharg->opts);
     }
     close(fd);
     return ret;
 }
 
 void *clamukoth(void *arg)
 {
 	struct thrarg *tharg = (struct thrarg *) arg;
 	sigset_t sigset;
         struct sigaction act;
 	int eventcnt = 1, extinfo;
 	char err[128];
 	struct ClamAuthEvent event;
 
     /* 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);
     memset(&act, 0, sizeof(struct sigaction));
     act.sa_handler = cauth_exit;
     sigfillset(&(act.sa_mask));
     sigaction(SIGUSR1, &act, NULL);
     sigaction(SIGSEGV, &act, NULL);
 
     extinfo = optget(tharg->opts, "ExtendedDetectionInfo")->enabled;
 
     cauth_fd = open("/dev/clamauth", O_RDONLY);
     if(cauth_fd == -1) {
 	logg("!ClamAuth: Can't open /dev/clamauth\n");
 	if(errno == ENOENT)
 	    logg("!ClamAuth: Please make sure ClamAuth.kext is loaded\n");
 	else if(errno == EACCES)
 	    logg("!ClamAuth: This application requires root privileges\n");
 	else
 	    logg("!ClamAuth: /dev/clamauth: %s\n", cli_strerror(errno, err, sizeof(err)));
 
 	return NULL;
     }
 
     while(1) {
 	if(read(cauth_fd, &event, sizeof(event)) > 0) {
 	    if(eventcnt == 1) {
 		if(event.action != SUPPORTED_PROTOCOL) {
 		    logg("!ClamAuth: Protocol version mismatch (tool: %d, driver: %d)\n", SUPPORTED_PROTOCOL, event.action);
 		    close(cauth_fd);
 		    return NULL;
 		}
 		if(strncmp(event.path, "ClamAuth", 8)) {
 		    logg("!ClamAuth: Invalid version event\n");
 		    close(cauth_fd);
 		    return NULL;
 		}
 		logg("ClamAuth: Driver version: %s, protocol version: %d\n", &event.path[9], event.action);
 	    } else {
 		cauth_scanfile(event.path, extinfo, tharg);
 	    }
 	    eventcnt++;
 	} else {
 	    if(errno == ENODEV) {
 		printf("^ClamAuth: ClamAuth module deactivated, terminating\n");
 		close(cauth_fd);
 		return NULL;
 	    }
 	}
 	usleep(200);
     }
 }
e3aaff8e
 #endif