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