samba-vscan/antivir Makefile,NONE,1.1.2.1 vscan-antivir.c,NONE,1.1.2.1 vscan-antivir.conf,NONE,1.1.2.1 vscan-antivir.h,NONE,1.1.2.1 vscan-antivir_core.c,NONE,1.1.2.1 vscan-antivir_core.h,NONE,1.1.2.1

Rainer Link <[email protected]> Wed, 17 Nov 2004 19:21:06 +0000
Newsgroups gmane.comp.security.virus.openantivirus.cvs
Message-ID <[email protected]>
Update of /cvsroot/openantivirus/samba-vscan/antivir
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv30758/antivir

Added Files:
      Tag: VSCAN_0_3
	Makefile vscan-antivir.c vscan-antivir.conf vscan-antivir.h 
	vscan-antivir_core.c vscan-antivir_core.h 
Log Message:
added support for AntiVir, contributed by the H+BEDV folks. Param parsing
not yet up-to-date for sheduled 0.3.6 release


--- NEW FILE: vscan-antivir.h ---
#ifndef __VSCAN_ANTIVIR_H_
#define __VSCAN_ANTIVIR_H_

#include "vscan-global.h"

/* Configuration Section :-) */

/* program name for scanner process */
#define VSCAN_ANTIVIR_PROGRAM_NAME	"/usr/lib/AntiVir/antivir"

/* enable scan in archive files */
#define VSCAN_AVARCH_ENABLED		0
/* maximum compression ratio of files in archive */
#define VSCAN_AVARCH_MAXRATIO		150
/* maximum archived file size (in bytes) */
#define VSCAN_AVARCH_MAXFILESIZE	(1024 * 1048576)
/* maximum recursion level */
#define VSCAN_AVARCH_MAXRECLEVEL	5

/* non virus types of alerts (all off by default) */
#define VSCAN_AVDETECT_DIALER		0
#define VSCAN_AVDETECT_GAME		0
#define VSCAN_AVDETECT_JOKE		0
#define VSCAN_AVDETECT_PMS		0
#define VSCAN_AVDETECT_SPY		0
#define VSCAN_AVDETECT_ALLTYPES		0

/* default location of samba-style configuration file (needs Samba >= 2.2.4
 or Samba 3.0 */

#define PARAMCONF "/etc/samba/vscan-antivir.conf"


/* False = log only concerning file, True = log every file access */

#ifndef VSCAN_VERBOSE_FILE_LOGGING
# define VSCAN_VERBOSE_FILE_LOGGING False
#endif

/* if a file is bigger than VSCAN_MAX_SIZE it won't be scanned. Has to be
   specified in bytes! If it set to 0, the file size check is disabled */

#ifndef VSCAN_MAX_SIZE
# define VSCAN_MAX_SIZE 0
#endif

/* True = scan files on open */

#ifndef VSCAN_SCAN_ON_OPEN
# define VSCAN_SCAN_ON_OPEN True
#endif

/* True = scan files on close */

#ifndef VSCAN_SCAN_ON_CLOSE
# define VSCAN_SCAN_ON_CLOSE True
#endif

/* True = deny access in case of scanning failure */

#ifndef VSCAN_DENY_ACCESS_ON_ERROR
# define VSCAN_DENY_ACCESS_ON_ERROR True
#endif

/* True = deny access in case of minor scanning failure */

#ifndef VSCAN_DENY_ACCESS_ON_MINOR_ERROR
# define VSCAN_DENY_ACCESS_ON_MINOR_ERROR True
#endif

/* True = send a warning message via window messenger service for alerts found */

#ifndef VSCAN_SEND_WARNING_MESSAGE
# define VSCAN_SEND_WARNING_MESSAGE True
#endif

/* default concerning file action */
#define VSCAN_INFECTED_FILE_ACTION INFECTED_QUARANTINE

/* default quarantine settings; hopefully the user changes this */
#define VSCAN_QUARANTINE_DIRECTORY	"/tmp"
#define VSCAN_QUARANTINE_PREFIX		"vir-"

/* set default value for maximum lrufile entries */
#define VSCAN_MAX_LRUFILES 100

/* time after an entry is considered as expired */
#define VSCAN_LRUFILES_INVALIDATE_TIME 5

/* MIME-types of files to be exluded from scanning; that's an
   semi-colon seperated list */
#define VSCAN_FT_EXCLUDE_LIST ""


/* End Configuration Section */

/* functions by vscan-antivir_core */

/* user connects to a share (might startup scanner daemon) */
int vscan_antivir_connect(void);

/* prepare a scan (might open a socket) */
int vscan_antivir_init(void);

/* scan a file */
int vscan_antivir_scanfile(int sockfd, char *scan_file, char *client_ip);

/* end a scan (might close a socket) */
void vscan_antivir_end(int sockfd);

/* user disconnects from a share (might shutdown scanner daemon) */
void vscan_antivir_disconnect(void);


#endif /* __VSCAN_ANTIVIR_H_ */

--- NEW FILE: vscan-antivir_core.c ---
/*
 * $Id: vscan-antivir_core.c,v 1.1.2.1 2004/11/17 19:21:04 reniar Exp $
 *
 * Core Interface for the H+BEDV AntiVir Scanner
 *
 * Copyright (C) Rainer Link, 2001-2002
 *               OpenAntiVirus.org <rainer-pBPPa8WU5k41Tgt60Rntydi2O/[email protected]>
 *               Dariusz Markowicz <[email protected]>, 2003
 *               H+BEDV Datentechnik GmbH <[email protected]>, 2004
 *
 * This software is licensed under the GNU General Public License (GPL)
 * See: http://www.gnu.org/copyleft/gpl.html
 *
 */

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

/* use select(2) or poll(2) to drain antivir(1)'s greeting after process
 * startup?  we default to poll(2) since we cannot guarantee the file
 * descriptor to be <= 1023 */
#undef USE_SELECT
#if defined USE_SELECT
  #include <sys/select.h>
#else
  #include <sys/poll.h>
#endif

#include "vscan-global.h"
#include "vscan-antivir_core.h"

/* used when scanning files */
extern BOOL verbose_file_logging;
extern BOOL send_warning_message;

/* used to startup the scanner process */
extern fstring antivir_program_name;
extern BOOL antivir_arch_scan_enable;
extern int antivir_arch_max_ratio;
extern ssize_t antivir_arch_max_size;
extern int antivir_arch_max_recursion;
extern int antivir_detect_dialer;
extern int antivir_detect_game;
extern int antivir_detect_joke;
extern int antivir_detect_pms;
extern int antivir_detect_spy;
extern int antivir_detect_alltypes;

/* used to manage the scanner process */
static int conn_count = 0;
static int antivir_fd_write = -1;
static int antivir_fd_read = -1;
static FILE *antivir_file_read = NULL;
static pid_t antivir_scanner_pid = 0;

static int connect_to_scanner(void);
static void disconnect_from_scanner(void);

/* user connects to a share (might startup scanner daemon) */
int vscan_antivir_connect(void) {

	/* only act on the very first connection for this smbd process */
	conn_count++;
	if (conn_count > 1)
		return(0);

	if (connect_to_scanner() < 0)
		return(-1);

	return(0);
}

/* user disconnects from a share (might shutdown scanner daemon) */
void vscan_antivir_disconnect(void) {
	conn_count--;
	if (conn_count == 0)
		disconnect_from_scanner();
}

/*
 * introduces a scan request, (re)connect to the scanner process;
 * returns -1 on error or 0 on success
 */
int vscan_antivir_init(void)
{
	int rc;

	rc = connect_to_scanner();

	return(rc);
}

/*
 * If an alert was found, logs the filename and concern into syslog
 */
void vscan_antivir_log_alert(char *concerning_file, char *results, char *client_ip) {
	vscan_syslog_alert("ALERT - Scan result: found '%s' in file '%s', client: '%s'", results, concerning_file, client_ip);
	if ( send_warning_message )
		vscan_send_warning_message(concerning_file, results, client_ip);
}

/*
 * avoid non printables in the scanner communication protocol
 * XXX: does the condition need further splitting on EBCDIC machines?
 * but OTOH isprint(3) does not really match what we would need here
 */
static int needsescape(const char c) {
	return(((c <= ' ') || (c > '~') || (c == '\\')) ? 1 : 0);
#if 0
	/* will this do better / more portable? */
	if (c == '\\')
		return(1);
	if (isspace((int)c))
		return(1);
	if (isprint((int)c))
		return(0);
	return(1);
#endif
}

/*
 * Scans a file (NOT a directory) for concerns
 *
 * Gets the vscan_antivir_init() return value (not really needed), the name of
 * the file to scan and an IP to send notification to
 *
 * Returns
 * -2 on minor error,
 * -1 on error,
 *  0 if nothing was found,
 *  1 if a concern was found
 */
int vscan_antivir_scanfile(int sockfd, char *scan_file, char *client_ip) {
	char *request;
	size_t len;
	int bEsc;
	char escbuff[5];	/* "\\xNN" plus NUL */
	char buff[1024];
	char *p1, *p2;

	/* (re)connect to the scanner process */
	if (connect_to_scanner() < 0)
		return(-1);

	/* prepare antivir command (SCAN request) */
	/* +1 is for '\0' termination --metze */
	/* +3 for every special (non printable) char (escape) */
	len = strlen("SCAN:") + strlen(scan_file) + strlen("\n") + 1;
	bEsc = 0;
	for (p1 = scan_file; (p1 != NULL) && (*p1 != '\0'); p1++) {
		if (needsescape(*p1)) {
			len += 3;
			bEsc++;
		}
	}
	request = (char *)malloc(len);
	if (NULL == request) {
		vscan_syslog("ERROR: can not allocate memory");
		return -1; /* error allocating memory */
	}
	safe_strcpy(request, "SCAN:", len-1);
	if (! bEsc) {
		/* simple concatenation of the complete filename */
		safe_strcat(request, scan_file, len-1);
	} else {
		/* a more expensive approach to avoid non printable chars in the protocol */
		/* XXX TODO optimization (memcpy() with a fork?) */
		for (p1 = scan_file; (p1 != NULL) && (*p1 != '\0'); p1++) {
			if (needsescape(*p1)) {
				snprintf(escbuff, sizeof(escbuff), "\\x%02X", (*((unsigned char *)p1)) & 0xFF);
			} else {
				/* there must be an easier way to accomplish this :) */
				snprintf(escbuff, sizeof(escbuff), "%c", *p1);
			}
			safe_strcat(request, escbuff, len - 1);
		}
	}
	snprintf(escbuff, sizeof(escbuff), "\n");
	safe_strcat(request, escbuff, len - 1);

	if (verbose_file_logging)
		vscan_syslog("INFO: Scanning file : '%s'", scan_file);

	/* send request */
	len = write(antivir_fd_write, request, strlen(request));
	if (len != strlen(request)) {
		free(request);
		vscan_syslog("ERROR: can not write to the antivir socket");
		return -1; /* error writing to the antivir socket */
	}
	free(request);
	request = NULL;

	/* get response (in a loop, we might have to skip some lines) */
	do {
		memset(buff, 0, sizeof(buff));
		p1 = fgets((char *)&buff, sizeof(buff), antivir_file_read);
		if (p1 == NULL)
			break;
		/* remove trailing spaces */
		p1 = buff + strlen(buff);
		while ((p1 > buff) && (isspace((int)*(p1-1)))) {
			p1--;
			*p1 = '\0';
		}

		/* an updater might have restarted our scanner process */
		if (strncmp(buff, "Running in DEMO mode.", strlen("Running in DEMO mode.")) == 0) {
			/* log a warning? */
			continue;
		}
		if (strncmp(buff, "BANNER ", strlen("BANNER ")) == 0) {
			continue;
		}

		/*
		 * we got a response to our request;
		 * do NOT close the scanner, reuse it for multiple scans
		 */

		/* split response into keyword and (optional) parameter */
		p1 = buff;
		p2 = strchr(p1, ':');
		if (p2 == NULL)
			p2 = p1 + strlen(p1);
		if (*p2 == ':') {
			*p2 = '\0';
			p2++;
		}
		while (isspace((int)*p2))
			p2++;

		/* found an alert */
		if (strcmp(p1, "FOUND") == 0) {
			vscan_antivir_log_alert(scan_file, p2, client_ip);
			return 1;
		/* file good */
		} else if (strcmp(p1, "OK") == 0) {
			if (verbose_file_logging) {
				vscan_syslog("INFO: file %s is clean", scan_file);
			}
			return 0;
		/* no explicit check for "ERROR", caught here, too */
		} else {
			if (verbose_file_logging) {
				vscan_syslog("ERROR: file %s not found, not readable or an error occured", scan_file);
			}
			return -2;
		}
	} while (0);

	/* read error or EOF, close the scanner process */
	disconnect_from_scanner();

	vscan_syslog("ERROR: can not get result from antivir");
	return -1;
}


/*
 * ends a scan request
 *
 * gets the vscan_antivir_init() return value (not really needed)
 */
void vscan_antivir_end(int sockfd) {

	/* leave the connection open, we will reuse it for the
	 * next file to scan and close it on (last) disconnect
	 */
	/* EMPTY */
}

/*
 * connect or reconnect to a scanner process;
 * returns -1 on errors, non negative codes otherwise
 */
static int connect_to_scanner(void) {
	int execargc;
	char *execargv[16];
	int rc;
	int fdreq[2];
	int fdrsp[2];
	pid_t pid;
	char buff[256];
	struct timeval tv;
	int fd;

	/*
	 * check if the scanner process is alive (handles dangling
	 * connections); we do not strictly need this but I want to
	 * avoid SIGPIPEs (we're in a shared object, attached to an
	 * smbd(8) process -- would SIG_IGN be appropriate? read(2)
	 * and fgets(3) would return errors, anyway ...)
	 */
	if ((antivir_scanner_pid != 0) && (kill(antivir_scanner_pid, 0) != 0))
		disconnect_from_scanner();

	/* shortcut for "already (completely) open?" */
	if ((antivir_fd_write != -1) && (antivir_fd_read != -1) &&
	    (antivir_file_read != NULL) && (antivir_scanner_pid != 0))
		return(0);

	/* one of the parameters might be wrong, close what is left open (if any) */
	disconnect_from_scanner();

	/* open up a new scanner process ... */

#define DEL_CMD_WORDS do {			 \
	while (execargc > 0) {			 \
		execargc--;			 \
		free(execargv[execargc]);	 \
	}					 \
} while (0)

#define ADD_CMD_WORD(word) do {						 \
	char *copy;							 \
									 \
	if (word != NULL) {						 \
		copy = strdup(word);					 \
		if (copy == NULL) {					 \
			DEL_CMD_WORDS;					 \
			return(-1);					 \
		}							 \
	} else {							 \
		copy = NULL;						 \
	}								 \
	execargv[execargc] = copy;					 \
	execargc++;							 \
	if (execargc >= sizeof(execargv) / sizeof(execargv[0])) {	 \
		DEL_CMD_WORDS;						 \
		disconnect_from_scanner();				 \
		return(-1);						 \
	}								 \
	execargv[execargc] = NULL;					 \
} while (0)

	/* ... build a command line ... */
	{
		execargc = 0;
		ADD_CMD_WORD(antivir_program_name);
		ADD_CMD_WORD("--samba-vscan");
		if (antivir_arch_scan_enable) {
			char printbuff[40];

			ADD_CMD_WORD("--scan-in-archive");
			/* for some reason the %z format spec did not work :( */
			snprintf(printbuff, sizeof(printbuff), "--archive-max-size=%lld", (long long)antivir_arch_max_size);
			ADD_CMD_WORD(printbuff);
			snprintf(printbuff, sizeof(printbuff), "--archive-max-recursion=%d", antivir_arch_max_recursion);
			ADD_CMD_WORD(printbuff);
			snprintf(printbuff, sizeof(printbuff), "--archive-max-ratio=%d", antivir_arch_max_ratio);
			ADD_CMD_WORD(printbuff);
		}
		/* optionally add a "--temp=" option? */
		if (antivir_detect_alltypes)
			ADD_CMD_WORD("--alltypes");
		if (antivir_detect_dialer)
			ADD_CMD_WORD("--with-dialer");
		if (antivir_detect_game)
			ADD_CMD_WORD("--with-game");
		if (antivir_detect_joke)
			ADD_CMD_WORD("--with-joke");
		if (antivir_detect_pms)
			ADD_CMD_WORD("--with-pms");
		if (antivir_detect_spy)
			ADD_CMD_WORD("--with-spy");
	}

	/* ... setup a communication pipe ... */
	rc = pipe(fdreq);
	if (rc < 0) {
		DEL_CMD_WORDS;
		disconnect_from_scanner();
		return(-1);
	}
	rc = pipe(fdrsp);
	if (rc < 0) {
		DEL_CMD_WORDS;
		disconnect_from_scanner();
		return(-1);
	}

	/* ... fork off the process ... */
	pid = fork();
	switch (pid) {
	case -1:	/* failure */
		DEL_CMD_WORDS;
		disconnect_from_scanner();
		return(-1);
	case 0: 	/* the child (scanner) */
		/* redirect stdin */
		rc = dup2(fdreq[0], STDIN_FILENO);
		if (rc == -1)
			exit(1);
		close(fdreq[0]); /* redirected */
		fdreq[0] = -1;
		close(fdreq[1]); /* not needed */
		fdreq[1] = -1;
		/* redirect stdout */
		rc = dup2(fdrsp[1], STDOUT_FILENO);
		if (rc == -1)
			exit(1);
		close(fdrsp[1]); /* redirected */
		fdrsp[1] = -1;
		close(fdrsp[0]); /* not needed */
		fdrsp[0] = -1;
		/* redirect stderr */
		fclose(stderr); freopen("/dev/null", "w", stderr);
		/* close all fds */
		/* XXX how to portably get the max fd?
		 *   fd = open("/dev/null", O_RDONLY);
		 * does not work reliably in the smbd(8) case,
		 * since there is a gap in the fd sequence
		 */
		fd = 1024; /* aux upper limit */
		while (fd > STDERR_FILENO) {
			close(fd);
			fd--;
		}
		/* execute the scanner process */
		rc = execvp(execargv[0], execargv);
		/* UNREACH */
		exit(1);
	default:	/* parent (samba-vscan) */
		/* release the command line */
		DEL_CMD_WORDS;
		/* get the read/write handles to the scanner process */
		close(fdreq[0]); fdreq[0] = -1;
		antivir_fd_write = fdreq[1];
		close(fdrsp[1]); fdrsp[1] = -1;
		antivir_fd_read = fdrsp[0];
		/* drain banners / greetings / help or synopsis on errors / etc */
		tv.tv_sec = 1; tv.tv_usec = 0;
		do {
			ssize_t ct;
#if defined USE_SELECT
			fd_set r;

			FD_ZERO(&r);
			FD_SET(antivir_fd_read, &r);
			rc = select(antivir_fd_read + 1, &r, NULL, NULL, &tv);
			tv.tv_sec = 0; tv.tv_usec = 0;
			if (rc < 0) {
				vscan_syslog("ERROR: Can not read from scanner - %s", strerror(errno));
				disconnect_from_scanner();
				return -1;
			}
			if (rc == 0)
				break;
			if (! FD_ISSET(antivir_fd_read, &r))
				break;
#else /* USE_SELECT */
			struct pollfd pfd[1];

			memset(pfd, 0, sizeof(pfd));
			pfd[0].fd = antivir_fd_read;
			pfd[0].events = POLLIN;
			rc = poll(pfd, 1, tv.tv_sec * 1000);
			tv.tv_sec = 0; tv.tv_usec = 0;
			if (rc < 0) {
				vscan_syslog("ERROR: Can not read from scanner - %s", strerror(errno));
				disconnect_from_scanner();
				return -1;
			}
			if (rc == 0)
				break;
#endif /* USE_SELECT */
			ct = read(antivir_fd_read, buff, sizeof(buff));
			if (ct <= 0) {
				vscan_syslog("ERROR: Can not read from scanner - %s", strerror(errno));
				disconnect_from_scanner();
				return -1;
			}
		} while (1);
		/* convert "int fd" to "FILE *" for fgets(3) */
		antivir_file_read = fdopen(antivir_fd_read, "r");
		if (antivir_file_read == NULL) {
			vscan_syslog("ERROR: Can not read from scanner - %s", strerror(errno));
			disconnect_from_scanner();
			return -1;
		}
		/* scanner process gone after the greeting? */
		if (kill(pid, 0) != 0) {
			vscan_syslog("ERROR: scanner disappeared at connect time");
			disconnect_from_scanner();
			return(-1);
		}
		antivir_scanner_pid = pid;
		/* connection setup done, now we're happy */
		return(0);
	}
	/* UNREACH */
	return(-1);
}

/*
 * invalidate the scanner process
 * (detach and clear vars after use or failure)
 */
static void disconnect_from_scanner(void) {
	if (antivir_fd_write != -1) {
		close(antivir_fd_write);
		antivir_fd_write = -1;
	}
	/* XXX drain possible input? */
	if (antivir_file_read != NULL) {
		fclose(antivir_file_read);
		antivir_file_read = NULL;
		antivir_fd_read = -1;
	} else if (antivir_fd_read != -1) {
		close(antivir_fd_read);
		antivir_fd_read = -1;
	}
	antivir_scanner_pid = 0;
}

--- NEW FILE: vscan-antivir_core.h ---
#ifndef __VSCAN_ANTIVIR_CORE_H_
#define __VSCAN_ANTIVIR_CORE_H_

#include "vscan-antivir.h"

#endif /* __VSCAN_ANTIVIR_CORE_H */

--- NEW FILE: Makefile ---
#
# Makefile for vscan-antivir, part of samba-vscan
#
# $Id: Makefile,v 1.1.2.1 2004/11/17 19:21:04 reniar Exp $
#

# Variables

CC = gcc
LIBTOOL = libtool

SAMBA_SRC = ../../../../source
SAMBA_INCL = ../../../../source/include
UBIQX_SRC = ../../../../source/ubiqx
SMBWR_SRC = ../../../../source/smbwrapper
SMBVS_INCL = ../include
SMBVS_GLB = ../global
CFLAGS = -I$(SAMBA_SRC) -I$(SAMBA_INCL) -I$(UBIQX_SRC) -I$(SMBWR_SRC) -I$(SMBVS_INCL) -Wall -g -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -fPIC
VFS_OBJS = vscan-antivir.so
SOURCES = $(SMBVS_GLB)/vscan-functions.c $(SMBVS_GLB)/vscan-message.c $(SMBVS_GLB)/vscan-quarantine.c $(SMBVS_GLB)/vscan-fileaccesslog.c vscan-antivir.c vscan-antivir_core.c vscan-antivir.h vscan-antivir_core.h
OBJS = vscan-functions.lo vscan-message.lo vscan-quarantine.lo vscan-fileaccesslog.lo vscan-antivir.lo vscan-antivir_core.lo
LIBS = 


# Default target

all: $(VFS_OBJS)

vscan-antivir.so: $(SOURCES)
	$(LIBTOOL) $(CC) $(CFLAGS) -c $(SMBVS_GLB)/vscan-functions.c
	$(LIBTOOL) $(CC) $(CFLAGS) -c $(SMBVS_GLB)/vscan-message.c
	$(LIBTOOL) $(CC) $(CFLAGS) -c $(SMBVS_GLB)/vscan-quarantine.c
	$(LIBTOOL) $(CC) $(CFLAGS) -c $(SMBVS_GLB)/vscan-fileaccesslog.c
	$(LIBTOOL) $(CC) $(CPPFLAGS) $(CFLAGS) -c vscan-antivir_core.c
	$(LIBTOOL) $(CC) $(CPPFLAGS) $(CFLAGS) -c vscan-antivir.c
	$(LIBTOOL) $(CC) -shared $(LDFLAGS) $(LIBS) $(OBJS) -o vscan-antivir.so


# Misc targets

clean:
	rm -rf .libs
	rm -f core *~ *% *.bak *.so *.lo *.o

--- NEW FILE: vscan-antivir.conf ---

[samba-vscan]
; run-time configuration for vscan-samba using
; antivir
; all options are set to default values

; do not scan files larger than X bytes. If set to 0 (default),
; this feature is disable (i.e. all files are scanned)
max file size = 0

; log all file access (yes/no). If set to yes, every access will
; be logged. If set to no (default), only access to files containing
; an alert will be logged
verbose file logging = no

; if set to yes (default), a file will be scanned while opening
scan on open = yes
; if set to yes, a file will be scanned while closing (default is yes)
scan on close = yes

; if communication to antivir fails, should access to file be denied?
; (default: yes)
deny access on error = yes

; if daemon failes with a minor error (corruption, etc.),
; should access to file be denied?
; (default: yes)
deny access on minor error = yes

; send a warning message via Windows Messenger service
; when an alert was found?
; (default: yes)
send warning message = yes

; what to do with a file containing an alert
; quarantine: try to move to quantine directory; delete it if moving fails
; delete:     delete concerning file
; nothing:    do nothing (default)
concerning file action = quarantine

; where to put concerning files - you really want to change this!
quarantine directory  = /tmp
; prefix for files in quarantine
quarantine prefix = vir-

; as Windows tries to open a file multiple time in a (very) short time
; of period, samba-vscan use a last recently used file mechanism to avoid
; multiple scans of a file. This setting specified the maximum number of
; elements of the last recently used file list. (default: 100)
max lru files entries = 100

; an entry is invalidad after lru file entry lifetime (in seconds).
; (Default: 5)
lru file entry lifetime = 5

; exclude files from being scanned based on the MIME-type! Semi-colon
; seperated list (default: empty list). Use this with care!
exclude file types =


; executable name of antivir (default: /usr/lib/AntiVir/antivir)
antivir program name = /usr/lib/AntiVir/antivir

; enable scan of archive files
antivir scan in archive = no

; maximum compression ratio of files in archive (default: 150)
antivir max ratio in archive = 150

; maximum archived file size, in bytes (default: 1 GB)
antivir max archived file size = 1073741824

; maximum recursion level (default: 5)
antivir max recursion level = 5

; detect non virus alerts, too, i.e. different kinds of unwanted software
; (default: off)
antivir detect dialer = no
antivir detect game = no
antivir detect joke = no
antivir detect pms = no
antivir detect spy = no

; a cumulative switch to enable *all* of the above categories
antivir detect alltypes = no


--- NEW FILE: vscan-antivir.c ---
/*
 * $Id: vscan-antivir.c,v 1.1.2.1 2004/11/17 19:21:04 reniar Exp $
 *
 * virusscanning VFS module for samba.  Log concerning files via syslog
 * facility and block access using the H+BEDV AntiVir scanner.
 *
 * Copyright (C) Rainer Link, 2001-2003
 *               OpenAntiVirus.org <rainer-pBPPa8WU5k41Tgt60Rntydi2O/[email protected]>
 *               Dariusz Markowicz <[email protected]>, 2003
 * Copyright (C) Stefan (metze) Metzmacher, 2003
 *               <[email protected]>
 * Copyright (C) H+BEDV Datentechnik GmbH, 2004
 *               <[email protected]>
 *
 * based on the audit VFS module by
 * Copyright (C) Tim Potter, 1999-2000
 * Copyright (C) Alexander Bokovoy, 2002
 *
 *
 * Credits to
 * - Dave Collier-Brown for his VFS tutorial (http://www.geocities.com/orville_torpid/papers/vfs_tutorial.html)
 * - REYNAUD Jean-Samuel for helping me to solve some general Samba VFS issues at the first place
 * - Simon Harrison for his solution without Samba VFS (http://www.smh.uklinux.net/linux/sophos.html)
 * - the whole Samba Team :)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


#include "vscan-global.h"
#include "vscan-antivir.h"

#include "vscan-vfs.h"

#define VSCAN_MODULE_STR "vscan-antivir"

fstring config_file;            /* location of config file, either
                                   PARAMCONF or as set via vfs options
                                */

ssize_t max_size;          	/* do not scan files greater than max_size
                                   if max_size = 0, scan any file
                                */

BOOL verbose_file_logging;  	/* log ever file access */

BOOL scan_on_open;         	/* scan a file before it is opened
                                   Defaults to True
                                */

BOOL scan_on_close;        	/* scan a new file put on share or
                                   if file was modified
                                   Defaults to True
                                */

BOOL deny_access_on_error;	/* if communication to the scanner fails,
				   should access to any file be denied? */

BOOL deny_access_on_minor_error; /* if daemon returns non-critical error,
                                    should access to the file be denied? */

BOOL send_warning_message;	/* send a warning message using the windows
				   messenger service? */

enum infected_file_action_enum concerning_file_action; /* what to do with concerning files;
                                                        defaults to quarantine */

fstring quarantine_dir;	/* directory for concerning files */
fstring quarantine_prefix;	/* prefix    for concerning files */

int max_lrufiles;               /* specified the maximum entries in lrufiles list */
time_t lrufiles_invalidate_time; /* specified the time in seconds after the lifetime
                                    of an entry is expired and entry will be invalidated */

pstring exclude_file_types;	/* list of file types which should be excluded from scanning */

fstring	antivir_program_name;	/* name of antivir executable */

BOOL antivir_arch_scan_enable;  /* archive related settings for the AntiVir scanner */
int antivir_arch_max_ratio;
ssize_t antivir_arch_max_size;
int antivir_arch_max_recursion;

BOOL antivir_detect_dialer;       /* non virus type alerts related settings for the AntiVir scanner */
BOOL antivir_detect_game;
BOOL antivir_detect_joke;
BOOL antivir_detect_pms;
BOOL antivir_detect_spy;
BOOL antivir_detect_alltypes;

/* module version */
static const char module_id[]=VSCAN_MODULE_STR" "SAMBA_VSCAN_VERSION_STR;


static BOOL do_parameter(const char *param, const char *value)
{
	if ( StrCaseCmp("max file size", param) == 0 ) {
		max_size = atoll(value);
		/* the %z format spec for size_t does not work :( */
		DEBUG(3, ("max file size is: %lld\n", (long long)max_size));
	} else if ( StrCaseCmp("verbose file logging", param) == 0 ) {
		set_boolean(&verbose_file_logging, value);
		DEBUG(3, ("verbose file logging is: %d\n", verbose_file_logging));
	} else if ( StrCaseCmp("scan on open", param) == 0 ) {
		set_boolean(&scan_on_open, value);
		DEBUG(3, ("scan on open: %d\n", scan_on_open));
	} else if ( StrCaseCmp("scan on close", param) == 0 ) {
		set_boolean(&scan_on_close, value);
		DEBUG(3, ("scan on close is: %d\n", scan_on_close));
	} else if ( StrCaseCmp("deny access on error", param) == 0 ) {
		set_boolean(&deny_access_on_error, value);
		DEBUG(3, ("deny access on error is: %d\n", deny_access_on_error));
	} else if ( StrCaseCmp("deny access on minor error", param) == 0 ) {
		set_boolean(&deny_access_on_minor_error, value);
		DEBUG(3, ("deny access on minor error is: %d\n", deny_access_on_minor_error));
	} else if ( StrCaseCmp("send warning message", param) == 0 ) {
		set_boolean(&send_warning_message, value);
		DEBUG(3, ("send warning message is: %d\n", send_warning_message));
	} else if ((StrCaseCmp("concerning file action", param) == 0)
#if 1 /* for compatibility */
		|| (StrCaseCmp("infected file action", param) == 0)
#endif
	) {
		if (StrCaseCmp("quarantine", value) == 0) {
			concerning_file_action = INFECTED_QUARANTINE;
		} else if (StrCaseCmp("delete", value) == 0) {
			concerning_file_action = INFECTED_DELETE;
		} else if (StrCaseCmp("nothing", value) == 0) {
			concerning_file_action = INFECTED_DO_NOTHING;
		} else {
			DEBUG(2, ("samba-vscan: badly formed concerning file action in configuration file, parameter %s\n", value));
		}
		DEBUG(3, ("concerning file action is: %d\n", concerning_file_action));
	} else if ( StrCaseCmp("quarantine directory", param) == 0 ) {
		fstrcpy(quarantine_dir, value);
		DEBUG(3, ("quarantine directory is: %s\n", quarantine_dir));
	} else if ( StrCaseCmp("quarantine prefix", param) == 0 ) {
		fstrcpy(quarantine_prefix, value);
		DEBUG(3, ("quarantine prefix is: %s\n", quarantine_prefix));
	} else if ( StrCaseCmp("max lru files entries", param) == 0 ) {
		max_lrufiles = atoi(value);
		DEBUG(3, ("max lru files entries is: %d\n", max_lrufiles));
	} else if ( StrCaseCmp("lru file entry lifetime", param) == 0 ) {
		lrufiles_invalidate_time = atol(value);
		DEBUG(3, ("lru file entry lifetime is: %li\n", (long)lrufiles_invalidate_time));
	} else if ( StrCaseCmp("exclude file types", param) == 0 ) {
		pstrcpy(exclude_file_types, value);
		DEBUG(3, ("exclude file types is: %s\n", exclude_file_types));
	} else if ( StrCaseCmp("antivir program name", param) == 0) {
		fstrcpy(antivir_program_name, value);
		DEBUG(3, ("antivir program name is %s\n", antivir_program_name));
	} else if ( StrCaseCmp("antivir scan in archive", param) == 0) {
		set_boolean(&antivir_arch_scan_enable, value);
		DEBUG(3, ("antivir scan in archive is %d\n", antivir_arch_scan_enable));
	} else if ( StrCaseCmp("antivir max ratio in archive", param) == 0) {
		antivir_arch_max_ratio = atoi(value);
		DEBUG(3, ("antivir max ratio in archive is %d\n", antivir_arch_max_ratio));
	} else if ( StrCaseCmp("antivir max archived file size", param) == 0) {
		antivir_arch_max_size = atoll(value);
		DEBUG(3, ("antivir max archived file size is %lld\n", (long long)antivir_arch_max_size));
	} else if ( StrCaseCmp("antivir max recursion level", param) == 0) {
		antivir_arch_max_recursion = atoi(value);
		DEBUG(3, ("antivir max recursion level is %d\n", antivir_arch_max_recursion));
	} else if ( StrCaseCmp("antivir detect dialer", param) == 0) {
		set_boolean(&antivir_detect_dialer, value);
		DEBUG(3, ("antivir detect dialer is %d\n", antivir_detect_dialer));
	} else if ( StrCaseCmp("antivir detect game", param) == 0) {
		set_boolean(&antivir_detect_game, value);
		DEBUG(3, ("antivir detect game is %d\n", antivir_detect_game));
	} else if ( StrCaseCmp("antivir detect joke", param) == 0) {
		set_boolean(&antivir_detect_joke, value);
		DEBUG(3, ("antivir detect joke is %d\n", antivir_detect_joke));
	} else if ( StrCaseCmp("antivir detect pms", param) == 0) {
		set_boolean(&antivir_detect_pms, value);
		DEBUG(3, ("antivir detect pms is %d\n", antivir_detect_pms));
	} else if ( StrCaseCmp("antivir detect spy", param) == 0) {
		set_boolean(&antivir_detect_spy, value);
		DEBUG(3, ("antivir detect spy is %d\n", antivir_detect_spy));
	} else if ( StrCaseCmp("antivir detect alltypes", param) == 0) {
		set_boolean(&antivir_detect_alltypes, value);
		DEBUG(3, ("antivir detect alltypes is %d\n", antivir_detect_alltypes));
	} else
		DEBUG(3, ("unknown parameter: %s\n", param));
		/* return False; ? */

	return True;
}

static BOOL do_section(const char *section)
{
	/* simply return true, there's only one section :-) */
	return True;
}


/* Implementation of vfs_ops.  */

#if (SMB_VFS_INTERFACE_VERSION >= 6)
static int vscan_connect(vfs_handle_struct *handle, connection_struct *conn, const char *svc, const char *user)
#else
static int vscan_connect(struct connection_struct *conn, PROTOTYPE_CONST char *svc, PROTOTYPE_CONST char *user)
#endif
{
	#if (SAMBA_VERSION_MAJOR==2 && SAMBA_VERSION_RELEASE>=4) || SAMBA_VERSION_MAJOR==3
	 #if !(SMB_VFS_INTERFACE_VERSION >= 6)
	  pstring opts_str;
	  PROTOTYPE_CONST char *p;
	 #endif
	#endif
	int retval;

#if (SMB_VFS_INTERFACE_VERSION >= 6)
	vscan_syslog("samba-vscan (%s) connected (Samba 3.0), (c) by Rainer Link, OpenAntiVirus.org", module_id);
#endif

	/* set default value for configuration files */
	fstrcpy(config_file, PARAMCONF);

	/* set default value for max file size */
	max_size = VSCAN_MAX_SIZE;

	/* set default value for file logging */
	verbose_file_logging = VSCAN_VERBOSE_FILE_LOGGING;

	/* set default value for scan on open() */
	scan_on_open = VSCAN_SCAN_ON_OPEN;

	/* set default value for scan on close() */
	scan_on_close = VSCAN_SCAN_ON_CLOSE;

	/* set default value for deny access on error */
	deny_access_on_error = VSCAN_DENY_ACCESS_ON_ERROR;

	/* set default value for deny access on minor error */
	deny_access_on_minor_error = VSCAN_DENY_ACCESS_ON_MINOR_ERROR;

	/* set default value for send warning message */
	send_warning_message = VSCAN_SEND_WARNING_MESSAGE;

	/* set default value for maximum lrufile entries */
	max_lrufiles = VSCAN_MAX_LRUFILES;

	/* time after an entry is considered as expired */
	lrufiles_invalidate_time = VSCAN_LRUFILES_INVALIDATE_TIME;

	/* file type exclude ist */
	pstrcpy(exclude_file_types, VSCAN_FT_EXCLUDE_LIST);

	/* name of AntiVir executable file */
	fstrcpy(antivir_program_name, VSCAN_ANTIVIR_PROGRAM_NAME);

	/* AntiVir archive related settings */
	antivir_arch_scan_enable = VSCAN_AVARCH_ENABLED;
	antivir_arch_max_ratio = VSCAN_AVARCH_MAXRATIO;
	antivir_arch_max_size = VSCAN_AVARCH_MAXFILESIZE;
	antivir_arch_max_recursion = VSCAN_AVARCH_MAXRECLEVEL;

	/* AntiVir non virus alerts related settings */
	antivir_detect_dialer = VSCAN_AVDETECT_DIALER;
	antivir_detect_game = VSCAN_AVDETECT_GAME;
	antivir_detect_joke = VSCAN_AVDETECT_JOKE;
	antivir_detect_pms = VSCAN_AVDETECT_PMS;
	antivir_detect_spy = VSCAN_AVDETECT_SPY;
	antivir_detect_alltypes = VSCAN_AVDETECT_ALLTYPES;

	vscan_syslog("INFO: connect to service %s by user %s",
	       svc, user);

	#if (SAMBA_VERSION_MAJOR==2 && SAMBA_VERSION_RELEASE>=4) || SAMBA_VERSION_MAJOR==3
	 #if (SMB_VFS_INTERFACE_VERSION >= 6)
	  fstrcpy(config_file, lp_parm_const_string(SNUM(conn),VSCAN_MODULE_STR,"config-file",PARAMCONF));
	 #else
	  pstrcpy(opts_str, (const char*) lp_vfs_options(SNUM(conn)));
	  if( !*opts_str ) {
		DEBUG(3, ("samba-vscan: no configuration file set - using default value (%s).\n", lp_vfs_options(SNUM(conn))));
	  } else {
		p = opts_str;
		if ( next_token(&p, config_file, "=", sizeof(config_file)) ) {
			trim_string(config_file, " ", " ");
			if ( !strequal("config-file", config_file) ) {
				DEBUG(3, ("samba-vscan - connect: options %s is not config-file\n", config_file));
				/* setting default value */
				fstrcpy(config_file, PARAMCONF);

			} else {
				if ( !next_token(&p, config_file," \n",sizeof(config_file)) ) {
					DEBUG(3, ("samba-vscan - connect: no option after config-file=\n"));
					/* setting default value */
					fstrcpy(config_file, PARAMCONF);
				} else {
					trim_string(config_file, " ", " ");
					DEBUG(3, ("samba-vscan - connect: config file name is %s\n", config_file));
				}
			}
		}
	  }
	  #endif /*  #if (SMB_VFS_INTERFACE_VERSION >= 6)*/
	  retval = pm_process(config_file, do_section, do_parameter);
	  DEBUG(10, ("pm_process returned %d\n", retval));
	  if (!retval) vscan_syslog("ERROR: could not parse configuration file '%s'. File not found or not read-able. Using compiled-in defaults", config_file);
	#endif

	/* initialise lrufiles list */
	DEBUG(5, ("init lrufiles list\n"));
	lrufiles_init(max_lrufiles, lrufiles_invalidate_time);

	/* initialise filetype */
	DEBUG(5, ("init file type\n"));
	filetype_init(0, exclude_file_types);

	/* tell core we have a(nother) session, ignore return code */
	(void)vscan_antivir_connect();

	#if (SMB_VFS_INTERFACE_VERSION >= 6)
	 return SMB_VFS_NEXT_CONNECT(handle, conn, svc, user);
	#else
	 return default_vfs_ops.connect(conn, svc, user);
	#endif

}

#if (SMB_VFS_INTERFACE_VERSION >= 6)
static void vscan_disconnect(vfs_handle_struct *handle, connection_struct *conn)
#else/* Samba 3.0 alphaX */
static void vscan_disconnect(struct connection_struct *conn)
#endif
{

	/* tell core the (one) session has gone */
	vscan_antivir_disconnect();

	vscan_syslog("INFO: disconnected");

	lrufiles_destroy_all();
	filetype_close();

#if (SMB_VFS_INTERFACE_VERSION >= 6)
	SMB_VFS_NEXT_DISCONNECT(handle, conn);
#else
	default_vfs_ops.disconnect(conn);
#endif
}


#if (SMB_VFS_INTERFACE_VERSION >= 6)
static int vscan_open(vfs_handle_struct *handle, connection_struct *conn, const char *fname, int flags, mode_t mode)
#else
static int vscan_open(struct connection_struct *conn, PROTOTYPE_CONST char *fname, int flags, mode_t mode)
#endif
{
	int retval, must_be_checked;
	SMB_STRUCT_STAT stat_buf;
	int sockfd;
	pstring filepath;
	char client_ip[CLIENT_IP_SIZE];
	int rc;

	/* Assemble complete file path */
	pstrcpy(filepath, conn->connectpath);
	pstrcat(filepath, "/");
	pstrcat(filepath, fname);

	/* scan files while opening? */
	if ( !scan_on_open ) {
		DEBUG(3, ("samba-vscan - open: File '%s' not scanned as scan_on_open is not set\n", fname));
#if (SMB_VFS_INTERFACE_VERSION >= 6)
		return SMB_VFS_NEXT_OPEN(handle, conn, fname, flags, mode);
#else
		return default_vfs_ops.open(conn, fname, flags, mode);
#endif
	}

#if (SMB_VFS_INTERFACE_VERSION >= 6)
	if ( (SMB_VFS_NEXT_STAT(handle, conn, fname, &stat_buf)) != 0 )    /* an error occured */
		return SMB_VFS_NEXT_OPEN(handle, conn, fname, flags, mode);
#else
	if ( (default_vfs_ops.stat(conn, fname, &stat_buf)) != 0 )    /* an error occured */
		return default_vfs_ops.open(conn, fname, flags, mode);
#endif
	else if ( S_ISDIR(stat_buf.st_mode) ) 	/* is it a directory? */
#if (SMB_VFS_INTERFACE_VERSION >= 6)
		return SMB_VFS_NEXT_OPEN(handle, conn, fname, flags, mode);
#else
		return default_vfs_ops.open(conn, fname, flags, mode);
#endif
	else if ( ( stat_buf.st_size > max_size ) && ( max_size > 0 ) ) /* file is too large */
		vscan_syslog("INFO: File %s is larger than specified maximum file size! Not scanned!", fname);
	else if ( stat_buf.st_size == 0 ) /* do not scan empty files */
#if (SMB_VFS_INTERFACE_VERSION >= 6)
		return SMB_VFS_NEXT_OPEN(handle, conn, fname, flags, mode);
#else
		return default_vfs_ops.open(conn, fname, flags, mode);
#endif
	else if ( filetype_skipscan(filepath) == 1 ) {
		if ( verbose_file_logging )
			vscan_syslog("File '%s' not scanned as file type is on exclude list", filepath);
#if (SMB_VFS_INTERFACE_VERSION >= 6)
		return SMB_VFS_NEXT_OPEN(handle, conn, fname, flags, mode);
#else
		return default_vfs_ops.open(conn, fname, flags, mode);
#endif

	} else
	{
		/* prepare file scan, (re)open connection to the scanner */
		sockfd = vscan_antivir_init();
		if ( sockfd == -1 && deny_access_on_error ) {
			/* an error occured - can not communicate to daemon - deny access */
			vscan_syslog("ERROR: can not communicate to daemon - access denied");
			errno = EACCES;
			return -1;
		} else if ( sockfd >= 0 ) {
			/* we now have a scanner */
			safe_strcpy(client_ip, conn->client_address, CLIENT_IP_SIZE -1);

			/* must file actually be scanned? */
			must_be_checked = lrufiles_must_be_checked(filepath, stat_buf.st_mtime);
			if ( must_be_checked == -1 ) {
				/* file has already been checked and marked as containing an alert */
				/* deny access */
				if ( verbose_file_logging )
					vscan_syslog("File '%s' has already been scanned and marked as containing an alert. Not scanned any more. Access denied", filepath);

				/* postprocess file scan */
				vscan_antivir_end(sockfd);

				/* deny access */
				errno = EACCES;
				return -1;
			} else if ( must_be_checked == 0 )  {
				/* file has already been checked, not marked as containing an alert and not modified */
				if ( verbose_file_logging )
					vscan_syslog("File '%s' has already been scanned, not marked as containing an alert and not modified. Not scanned anymore. Access granted", filepath);

				/* postprocess file scan */
				vscan_antivir_end(sockfd);

				/* grant access */
#if (SMB_VFS_INTERFACE_VERSION >= 6)
				return SMB_VFS_NEXT_OPEN(handle, conn, fname, flags, mode);
#else
				return default_vfs_ops.open(conn, fname, flags, mode);
#endif
			}
			/* ok, we must check the file */

			/* scan file */
			retval = vscan_antivir_scanfile(sockfd, filepath, client_ip);
			if ( retval == -2 && deny_access_on_minor_error ) {
				/* a minor error occured - deny access */
				vscan_syslog("ERROR: daemon failed with a minor error - access to file %s denied", fname);
				vscan_antivir_end(sockfd);

				/* to be safe, remove file from lrufiles */
				lrufiles_delete(filepath);

				/* deny access */
				errno = EACCES;
				return -1;
			} else if ( retval == -1 && deny_access_on_error ) {
			/* an error occured - can not communicate to daemon - deny access */
				vscan_syslog("ERROR: can not communicate to daemon - access to file %s denied", fname);
				/* to be safe, remove file from lrufiles */
				lrufiles_delete(filepath);

				/* deny access */

				errno = EACCES;
				return -1;
			} else if ( retval == 1 ) {
				/* an alert was found */
				vscan_antivir_end(sockfd);
				/* do action ... */

#if (SMB_VFS_INTERFACE_VERSION >= 6)
				rc = vscan_do_infected_file_action(handle, conn, filepath, quarantine_dir, quarantine_prefix, concerning_file_action);
#else
				rc = vscan_do_infected_file_action(&default_vfs_ops, conn, filepath, quarantine_dir, quarantine_prefix, concerning_file_action);
#endif

				/* add/update file. mark file as containing an alert! */
				lrufiles_add(filepath, stat_buf.st_mtime, True);

				/* alert found, deny acces */
				errno = EACCES;
				return -1;
			} else if ( retval == 0 ) {
				/* file is clean, add to lrufiles */
				lrufiles_add(filepath, stat_buf.st_mtime, False);
			}
		}

		/* scan done, post process file scan */
		vscan_antivir_end(sockfd);
	}
#if (SMB_VFS_INTERFACE_VERSION >= 6)
	return SMB_VFS_NEXT_OPEN(handle, conn, fname, flags, mode);
#else
	return default_vfs_ops.open(conn, fname, flags, mode);
#endif
}

#if (SMB_VFS_INTERFACE_VERSION >= 6)
static int vscan_close(vfs_handle_struct *handle, files_struct *fsp, int fd)
#else
static int vscan_close(struct files_struct *fsp, int fd)
#endif
{
	pstring filepath;
	int retval, rv, rc;
	int sockfd;
	char client_ip[CLIENT_IP_SIZE];

	/* First close the file */
#if (SMB_VFS_INTERFACE_VERSION >= 6)
	retval = SMB_VFS_NEXT_CLOSE(handle, fsp, fd);
#else
	retval = default_vfs_ops.close(fsp, fd);
#endif

	if ( !scan_on_close ) {
		DEBUG(3, ("samba-vscan - close: File '%s' not scanned as scan_on_close is not set\n", fsp->fsp_name));
		return retval;
	}


	/* get the file name */
	pstrcpy(filepath, fsp->conn->connectpath);
	pstrcat(filepath, "/");
	pstrcat(filepath, fsp->fsp_name);

	/* Don't scan directorys */
	if ( fsp->is_directory )
	    return retval;


	if ( !fsp->modified ) {
		if ( verbose_file_logging )
			vscan_syslog("INFO: file %s was not modified - not scanned", filepath);

		return retval;
	}

	/* don't scan files which are in the list of exclude file types */
	if ( filetype_skipscan(filepath) == 1 ) {
		if ( verbose_file_logging )
			vscan_syslog("File '%s' not scanned as file type is on exclude list", filepath);
		return retval;
	}


	sockfd = vscan_antivir_init();
	if ( sockfd >= 0 ) {
		safe_strcpy(client_ip, fsp->conn->client_address, CLIENT_IP_SIZE -1);
		/* scan only file, do nothing */
		rv = vscan_antivir_scanfile(sockfd, filepath, client_ip);
		vscan_antivir_end(sockfd);
		if ( rv == 1 ) {
			/* alert was found */
#if (SMB_VFS_INTERFACE_VERSION >= 6)
			rc = vscan_do_infected_file_action(handle, fsp->conn, filepath, quarantine_dir, quarantine_prefix, concerning_file_action);
#else
			rc = vscan_do_infected_file_action(&default_vfs_ops, fsp->conn, filepath, quarantine_dir, quarantine_prefix, concerning_file_action);
#endif
		}

	}
	return retval;
}


#if (SMB_VFS_INTERFACE_VERSION >= 6)
/* Samba 3.0 */
NTSTATUS init_module(void)
{
	NTSTATUS ret;

	ret = smb_register_vfs(SMB_VFS_INTERFACE_VERSION, VSCAN_MODULE_STR, vscan_ops);
	openlog("smbd_"VSCAN_MODULE_STR, LOG_PID, SYSLOG_FACILITY);
	vscan_syslog("samba-vscan (%s) registered (Samba 3.0), (c) by Rainer Link, OpenAntiVirus.org", module_id);
	DEBUG(5,("samba-vscan (%s) registered (Samba 3.0), (c) by Rainer Link, OpenAntiVirus.org\n", module_id));

	return ret;
}
#else
/* VFS initialisation function.  Return initialised vfs_ops structure
   back to SAMBA. */
#if SAMBA_VERSION_MAJOR==3
 /* Samba 3.0 alphaX */
 vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops,
			struct smb_vfs_handle_struct *vfs_handle)
#else
 /* Samba 2.2.x */
 #if SAMBA_VERSION_RELEASE>=4
  /* Samba 2.2.4 */
  struct vfs_ops *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops)
 #elif SAMBA_VERSION_RELEASE==2
  /* Samba 2.2.2 / Samba 2.2.3 !!! */
  struct vfs_ops *vfs_init(int* Version, struct vfs_ops *ops)
 #elif SAMBA_VERSION_RELEASE==1
  /* Samba 2.2.1 */
  struct vfs_ops *vfs_module_init(int *vfs_version)
 #else
  /* Samba 2.2.0 */
  struct vfs_ops *vfs_init(int *vfs_version)
 #endif
#endif
{
	#if SAMBA_VERSION_MAJOR!=3
 	 #if SAMBA_VERSION_RELEASE>=4
	  /* Samba 2.2.4 */
	  struct vfs_ops tmp_ops;
	 #endif
	#endif

	openlog("smbd_"VSCAN_MODULE_STR, LOG_PID, SYSLOG_FACILITY);

	#if SAMBA_VERSION_MAJOR==3
	 /* Samba 3.0 alphaX */
	 *vfs_version = SMB_VFS_INTERFACE_VERSION;
	 vscan_syslog("samba-vscan (%s) loaded (Samba 3.x), (c) by Rainer Link, OpenAntiVirus.org", module_id);
	#else
	 /* Samba 2.2.x */
	 #if SAMBA_VERSION_RELEASE>=4
	  /* Samba 2.2.4 */
	  *vfs_version = SMB_VFS_INTERFACE_VERSION;
	  vscan_syslog("samba-vscan (%s) loaded (Samba >=2.2.4), (c) by Rainer Link, OpenAntiVirus.org", module_id);
	 #elif SAMBA_VERSION_RELEASE==2
	  /* Samba 2.2.2 / Samba 2.2.3 !!! */
	  *Version = SMB_VFS_INTERFACE_VERSION;
	  vscan_syslog("samba-vscan (%s) loaded (Samba 2.2.2/2.2.3), (c) by Rainer Link, OpenAntiVirus.org", module_id);
	 #else
	  /* Samba 2.2.1 / Samba 2.2.0 */
	  *vfs_version = SMB_VFS_INTERFACE_VERSION;
	  vscan_syslog("samba-vscan (%s) loaded (Samba 2.2.0/2.2.1), (c) by Rainer Link, OpenAntiVirus.org",
	       module_id);
	 #endif
	#endif


	#if SAMBA_VERSION_MAJOR==3
	 /* Samba 3.0 alphaX */
	 DEBUG(3, ("Initialising default vfs hooks\n"));
	 memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops));

	 /* Remember vfs_handle for further allocation and referencing of
	    private information in vfs_handle->data
	 */
	 vscan_handle = vfs_handle;
	 return vscan_ops;
	#else
	 /* Samba 2.2.x */
	 #if SAMBA_VERSION_RELEASE>=4
	  /* Samba 2.2.4 */

	  *vfs_version = SMB_VFS_INTERFACE_VERSION;
	  memcpy(&tmp_ops, def_vfs_ops, sizeof(struct vfs_ops));
	  tmp_ops.connect = vscan_connect;
	  tmp_ops.disconnect = vscan_disconnect;
	  tmp_ops.open = vscan_open;
	  tmp_ops.close = vscan_close;
	  memcpy(&vscan_ops, &tmp_ops, sizeof(struct vfs_ops));
	  return(&vscan_ops);

	 #else
	  /* Samba 2.2.3-2.2.0 */
	  return(&vscan_ops);
	 #endif
	#endif
}


#if SAMBA_VERSION_MAJOR==3
/* VFS finalization function */
void vfs_done(connection_struct *conn)
{
	DEBUG(3, ("Finalizing default vfs hooks\n"));
}
#endif

#endif /* #if (SMB_VFS_INTERFACE_VERSION >= 6) */



-------------------------------------------------------
This SF.Net email is sponsored by: InterSystems CACHE
FREE OODBMS DOWNLOAD - A multidimensional database that combines
robust object and relational technologies, making it a perfect match
for Java, C++,COM, XML, ODBC and JDBC. www.intersystems.com/match8