samba3-vscan/avast vscan-avastd.c,NONE,1.1 vscan-avastd.conf,NONE,1.1 vscan-avastd.h,NONE,1.1 vscan-avastd_core.c,NONE,1.1 vscan-avastd_core.h,NONE,1.1

Rainer Link <[email protected]> Mon, 29 Aug 2005 12:51:24 +0000
Newsgroups gmane.comp.security.virus.openantivirus.cvs
Message-ID <[email protected]>
Update of /cvsroot/openantivirus/samba3-vscan/avast
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv20465/avast

Added Files:
	vscan-avastd.c vscan-avastd.conf vscan-avastd.h 
	vscan-avastd_core.c vscan-avastd_core.h 
Log Message:
added support for avast4Linux, contributed by Frantisek Mensik,
ALWIL Software


--- NEW FILE: vscan-avastd.c ---
/*
 * virusscanning VFS module for samba.  Log infected files via syslog
 * facility and block access using avast! daemon.
 *
 * Copyright (C) Frantisek Mensik, 2005
 *               ALWIL Software <[email protected]>
 *
 * base on the F-Prot Daemon module by
 * Copyright (C) Rainer Link, 2001-2003
 *               OpenAntiVirus.org <rainer-pBPPa8WU5k41Tgt60Rntydi2O/[email protected]>
 * Copyright (C) Stefan (metze) Metzmacher, 2003
 *               <[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-avastd.h"

#include "vscan-vfs.h"

#define VSCAN_MODULE_STR "vscan-avastd"

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 False
                                */

BOOL deny_access_on_error;      /* if connection to daemon fails,  should access to any
                                   file be denied? Defaults to True 
				*/


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? */

fstring avastd_ip;		/* IP avast! daemon runs on */
pstring avastd_port;		/* port number avast! daemon listens on */
fstring avastd_socket_file;	/* socket name for avast! daemon */

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

enum infected_file_action_enum infected_file_action; /* what to do with infected files;
                                                        defaults to quarantine */

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 */



/* 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 ) {
		/* FIXME: sanity check missing! what, if value is out of range?
		   atoi returns int - what about LFS? atoi should be avoided!
		*/
                max_size = atoi(value);
                DEBUG(3, ("max file size is: %d\n", 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("infected file action", param) == 0 ) {
		if (StrCaseCmp("quarantine", value) == 0) {
			infected_file_action = INFECTED_QUARANTINE;
		} else if (StrCaseCmp("delete", value) == 0) {
			infected_file_action = INFECTED_DELETE;
		} else if (StrCaseCmp("nothing", value) == 0) {
			infected_file_action = INFECTED_DO_NOTHING;
		} else {
			DEBUG(2, ("samba-vscan: badly formed infected file action in configuration file, parameter %s\n", value));
		}
                DEBUG(3, ("infected file action is: %d\n", infected_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 list is: %s\n", exclude_file_types));
	} else if ( StrCaseCmp("avastd ip", param) == 0 ) {
		fstrcpy(avastd_ip, value);
		DEBUG(3, ("avastd ip is: %s\n", avastd_ip));
	} else if ( StrCaseCmp("avastd port", param) == 0 ) {
		pstrcpy(avastd_port, value);
		DEBUG(3, ("avastd port is: %s\n", avastd_port));
	} else if ( StrCaseCmp("avastd socket file", param) == 0 ) {
		fstrcpy(avastd_socket_file, value);
		DEBUG(3, ("avastd socket file is %s\n", avastd_socket_file));
        } else
                DEBUG(3, ("unknown parameter: %s\n", param));

        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 infected file action */
        infected_file_action = VSCAN_INFECTED_FILE_ACTION;

        /* set default value for quarantine directory */
        fstrcpy(quarantine_dir, VSCAN_QUARANTINE_DIRECTORY);

        /* set default value for quarantine prefix */
        fstrcpy(quarantine_prefix, VSCAN_QUARANTINE_PREFIX);

	/* set default value for avast! daemon IP */
	fstrcpy(avastd_ip, VSCAN_AVASTD_IP);

	/* set default value for avast! daemon port */
	pstrcat(avastd_port, VSCAN_AVASTD_PORT);

	/* set default value for avast! daemon socket file */
	fstrcpy(avastd_socket_file, VSCAN_AVASTD_SOCKET_FILE);

        /* 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);	


	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);


	#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
{

	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  
	{
		/* open socket */
		sockfd = vscan_avastd_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 ) 
		{

			/* get client IP */
			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 infected */
                                /* deny access */
                                if ( verbose_file_logging )
                                        vscan_syslog("File '%s' has already been scanned and marked as infected. Not scanned any more. Access denied", filepath);
			
				/* close socket */
				vscan_avastd_end(sockfd);

				/* deny access */
				errno = EACCES;
				return -1;
			} else if ( must_be_checked == 0 )  {
				/* file has already been checked, not marked as infected and not modified */
				if ( verbose_file_logging )
					vscan_syslog("File '%s' has already been scanned, not marked as infected and not modified. Not scanned anymore. Access granted", filepath);
	
				/* close socket */
				vscan_avastd_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_avastd_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);

				/* close socket */
				vscan_avastd_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);

				/* close socket */
				vscan_avastd_end(sockfd);

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

                                /* deny access */

                                errno = EACCES;
                                return -1;
			} else if ( retval == 1 ) {
				/* close socket */
				vscan_avastd_end(sockfd);
				/* do action ... */
				
#if (SMB_VFS_INTERFACE_VERSION >= 6)
				rc = vscan_do_infected_file_action(handle, conn, filepath, quarantine_dir, quarantine_prefix, infected_file_action);
#else
				rc = vscan_do_infected_file_action(&default_vfs_ops, conn, filepath, quarantine_dir, quarantine_prefix, infected_file_action);
#endif

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

				/* virus 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);
                        }
		}

		/* close socket */
		vscan_avastd_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;
	}


	/* scan only file, do nothing */	

	sockfd = vscan_avastd_init();	
	if ( sockfd >= 0 ) {
		safe_strcpy(client_ip, fsp->conn->client_address, CLIENT_IP_SIZE -1);
		rv = vscan_avastd_scanfile(sockfd, filepath, client_ip);
		vscan_avastd_end(sockfd);
		if ( rv == 1 ) {
			/* virus was found */
#if (SMB_VFS_INTERFACE_VERSION >= 6)
			rc = vscan_do_infected_file_action(handle, fsp->conn, filepath, quarantine_dir, quarantine_prefix, infected_file_action);
#else
			rc = vscan_do_infected_file_action(&default_vfs_ops, fsp->conn, filepath, quarantine_dir, quarantine_prefix, infected_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);
	DEBUG(5,("samba-vscan (%s) registered (Samba 3.0), (c) by Rainer Link, OpenAntiVirus.org\n", module_id));
	openlog("smbd_"VSCAN_MODULE_STR, LOG_PID, SYSLOG_FACILITY);
		
	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) */

--- NEW FILE: vscan-avastd_core.c ---
/* 
 * Core Interface for avast! Antivirus Daemon			
 *
 * Copyright (C) Frantisek Mensik, 2005
 *               ALWIL Software
 *
 * base on the F-Prot Daemon module by
 * Copyright (C) Rainer Link, 2001-2003
 *               OpenAntiVirus.org <rainer-pBPPa8WU5k41Tgt60Rntydi2O/[email protected]>
 *
 * Credits to W. Richard Stevens - RIP
 * 
 * This software is licensed under the GNU General Public License (GPL)
 * See: http://www.gnu.org/copyleft/gpl.html
 *
*/

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

/* hum, global vars ... */
extern BOOL verbose_file_logging;
extern BOOL send_warning_message;
extern fstring avastd_ip;
extern pstring avastd_port;
extern fstring avastd_socket_file; 



/* initialise socket to avast! daemon 
   returns -1 on error or the socket descriptor */
int vscan_avastd_init(void)
{
	int sockfd;

	if( strlen(avastd_ip) && strlen(avastd_port) ) {
		struct sockaddr_in servaddr;

		/* create socket */
    		if (( sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
	               vscan_syslog("ERROR: can not create socket!");
    		       return -1; 
	        }

		bzero(&servaddr, sizeof(servaddr));
	        servaddr.sin_family = AF_INET;

		/* hm, inet_pton may not exist on all systems - FIXME ! */
 		if ( inet_pton(AF_INET, avastd_ip, &servaddr.sin_addr) <= 0 ) {
			vscan_syslog("ERROR: inet_pton failed!");
			return -1;
		}

		servaddr.sin_port = htons(atoi(avastd_port));
		/* connect to socket */
		if ( connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )
		{
			vscan_syslog("ERROR: can not connect to avast! daemon (IP: '%s', port: '%s')!", avastd_ip, avastd_port);
			return -1;
		} else {
			/* OK, we got a connection. */
			return sockfd;
		}
	} else {
	        struct sockaddr_un servaddr;

	        /* create socket */
	        if (( sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0 ) {
			vscan_syslog("ERROR: can not create socket!");
			return -1; 
		}

		bzero(&servaddr, sizeof(servaddr));
		servaddr.sun_family = AF_UNIX;
		safe_strcpy(servaddr.sun_path, avastd_socket_file, sizeof(servaddr.sun_path)-1);

		/* connect to socket */
		if ( connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 ) {
			vscan_syslog("ERROR: can not connect to avast! daemon (socket: '%s')!", avastd_socket_file);
			return -1;
		} else {
			/* OK, we got a connection. */
			return sockfd;
		}
	}

	/* Uh, no connection was possible */
	return -1;
}

/*
  If virus is found, logs the filename/virusname into syslog
*/
void vscan_avastd_log_virus(char *infected_file, char *result, char* client_ip)
{
        char *str;
        size_t len;

        /* remove "\r\n" from the end of the result string to get only the virus name */

	len = strlen(result);
        /* sanity check ... */
        if ( len < 2 ) {
            /* hum, sth went wrong ... */
            vscan_syslog_alert("ALERT - Scan result: '%s' infected with virus 'UNKOWN', client: '%s'", infected_file, client_ip);
	    if ( send_warning_message )
		vscan_send_warning_message(infected_file, "UNKNOWN", client_ip);

        } else {
            str = result;
            str[strlen(str)-2]= '\0';
            vscan_syslog_alert("ALERT - Scan result: '%s' infected with virus '%s', client: '%s'", infected_file, str, client_ip);
	    if ( send_warning_message )
		vscan_send_warning_message(infected_file, str, client_ip);
        }
}



/*
  Scans a file (*FILE*, not a directory - keep that in mind) for a virus
  Expects socket descriptor and file name to scan for
  Returns -2 on minor error,  -1 on error, 0 if no virus was found, 
  1 if a virus was found 
*/
int vscan_avastd_scanfile(int sockfd, char *scan_file, char* client_ip)
{
	char recvline[MAXLINE + 1];
	pstring avastdCommand;	/* the command line to be send to daemon */
	char *str;
	FILE *fpin, *fpout;
	BOOL receiving_data = False; /* indicates receiving scanner results */
	BOOL received_error = False; /* indicates file scan error */

	/* open stream sockets */
        fpin = fdopen(sockfd, "r");
        if ( fpin == NULL ) {
                vscan_syslog("ERROR: Can not open stream for reading - %s", strerror(errno));
                return -1;
        }

        fpout = fdopen(sockfd, "w");
        if ( fpout == NULL ) {
		/* close fpin */
		fclose(fpin);
                vscan_syslog("ERROR: Can not open stream for writing - %s", strerror(errno));
                return -1;
        }

	/* read from socket-stream */
	if ( fgets(recvline, MAXLINE, fpin) == NULL ) {
		/* close streams */
		fclose(fpin);
		fclose(fpout);
		vscan_syslog("ERROR: can not get result from avast! daemon");
		return -1;
	}

	/* check for welcome string */
	if ( strncmp(recvline, "220", 3) != 0 ) {
		/* close streams */
		fclose(fpin);
		fclose(fpout);
		vscan_syslog("ERROR: avast! daemon is not available");
		return -1;
	}

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

	/* avast! daemon expects "SCAN filename\r\n" */
	/* what about if the <filename> itself contains '\n'? */
        pstrcpy(avastdCommand, "SCAN ");
        pstrcat(avastdCommand, scan_file);
        pstrcat(avastdCommand, "\r\n");

	/* write to socket */
	/* NOTE: what happens if scan_file is very long? */

	if ( fputs(avastdCommand, fpout) == EOF ) {
		vscan_syslog("ERROR: can not send file name to avast! daemon!");
		/* closing streams */
		fclose(fpout);
		fclose(fpin);
		return -1;
	}

	/* hum, instead of flush()ing, use setvbuf to set to line-buffering? */
        if ( fflush(fpout) == EOF ) {
                vscan_syslog("ERROR: can not flush output stream - %s", strerror(errno));
		/* closing streams */
		fclose(fpout);
		fclose(fpin);
		/* better safe than sorry ... */
		return -1;
        }


	/* read from socket, line by line */
	setvbuf(fpin, (char *)NULL, _IOLBF, 0);
	/* setlinebuf(fpin); */

	while ( (fgets(recvline, MAXLINE, fpin)) != NULL ) {

		if( receiving_data == False ) {
			/* check for ok command */
			if ( strncmp(recvline, "200", 3) != 0 ) {
				/* close streams */
				fclose(fpin);
				fclose(fpout);
				vscan_syslog("ERROR: file %s not found, not readable or an error occured", scan_file);
				return -1;
			} else {
				/* ready for reading scanned file result */
				receiving_data = True;
			}
		} else {
			if ( strncmp(recvline, "\r\n", 2 ) == 0 ) {
				break;
			} else if ( (str = strstr(recvline, "\t[L]\t" )) != NULL ) {
				/* virus found */
				vscan_avastd_log_virus(scan_file, str+strlen("\t[L]\t"), client_ip);
				/* closing streams */
				fclose(fpout);
				fclose(fpin);
				return 1;
			} else if ( (str = strstr(recvline, "\t[E]\t" )) != NULL ) {
				received_error = True;
			}
		}
	}

	/* did we receive any data from daemon? */
	if ( !receiving_data ) {
		vscan_syslog("ERROR: can not get result from avast! daemon!");
		/* closing streams */
		fclose(fpout);
		fclose(fpin);
		return -1;
         } else {

	 	/* OK */
		if ( verbose_file_logging )
        		vscan_syslog("INFO: file %s is clean", scan_file);
	}

	/* closing streams */
	fclose(fpout);
	fclose(fpin);

	if ( received_error == True ) return -2;
	return 0;
}


/*
  close socket
*/
void vscan_avastd_end(int sockfd)
{
	/* sockfd == -1 indicates an error while connecting to socket */
	if ( sockfd >= 0 ) {
		close(sockfd);
	}

}

--- NEW FILE: vscan-avastd_core.h ---
#ifndef __VSCAN_AVASTD_CORE_H_
#define __VSCAN_AVASTD_CORE_H_

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "vscan-avastd.h"

#endif /* __VSCAN_AVASTD_CORE_H */

--- NEW FILE: vscan-avastd.h ---
#ifndef __VSCAN_AVASTD_H_
#define __VSCAN_AVASTD_H_

#include "vscan-global.h"


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

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


/* False = log only infected 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 False
#endif

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

#ifndef VSCAN_DENY_ACCESS_ON_ERROR
# define VSCAN_DENY_ACCESS_ON_ERROR True
#endif

/* True = deny access in case of minor virus 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 viruses found */

#ifndef VSCAN_SEND_WARNING_MESSAGE
# define VSCAN_SEND_WARNING_MESSAGE True
#endif

/* default infected 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 ""


/* IP:PORT avast! daemon listens on */
#define VSCAN_AVASTD_IP		""
#define VSCAN_AVASTD_PORT 	""	

/* default socket name for avast! daemon */
#define VSCAN_AVASTD_SOCKET_FILE	"/var/run/avast4filescanner"

/* End Configuration Section */



#ifndef MAXLINE
 #define MAXLINE 4096
#endif

/* functions by vscan-avastd_core */
/* opens a socket */
int vscan_avastd_init(void); 
/* scans a file */
int vscan_avastd_scanfile(int sockfd, char *scan_file, char* client_ip);
/* closes socket */
void vscan_avastd_end(int sockfd);


#endif /* __VSCAN_AVASTD_H_ */

--- NEW FILE: vscan-avastd.conf ---
[samba-vscan]
; run-time configure options for vscan-samba using
; avast! Daemon. All options 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 infected files
; 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 daemon fails, should access to file denied?
; (default: yes)
deny access on error = yes

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

; send a warning message via Windows Messenger service
; when virus is found?
; (default: yes)
send warning message = yes

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

; where to put infected 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. If set to 0, this
; mechanism is disabled completely (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 =


; name of avast! daemon socket file (default: /var/run/avast4/filescanner.sock)
avastd socket file = /var/run/avast4/filescanner.sock

; IP of avast! daemon 
;avastd ip = 127.0.0.1
avastd ip = 

; port number, avast! daemon listens on
;avastd port = 5037
avastd port = 




-------------------------------------------------------
SF.Net email is Sponsored by the Better Software Conference & EXPO
September 19-22, 2005 * San Francisco, CA * Development Lifecycle Practices
Agile & Plan-Driven Development * Managing Projects & Teams * Testing & QA
Security * Process Improvement & Measurement * http://www.sqe.com/bsce5sf