<?php /* havp.inc Part of pfSense package Copyright (C) 2009 Serg Dvorianceev All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* ! for HAVP v.0.88 ! */ /* ! Real virus collection for tests http://www.nvkz.kuzbass.net/as/ ! */ require_once('globals.inc'); require_once('config.inc'); require_once('util.inc'); require_once('system.inc'); require_once('pfsense-utils.inc'); require_once('pkg-utils.inc'); require_once('service-utils.inc'); if(!function_exists("filter_configure")) require_once("filter.inc"); # ------------------------------------------------------------------------------ # globals # ------------------------------------------------------------------------------ # Debug / uncomment next for debug / define('HV_DEBUG', 'false'); # use Clamd daemon (another - use libclam) define('HV_USE_CLAMD', 'true'); define('HV_CLAMD_TCPSOCKET', 'true'); # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # RAM Disk - use as 'tmp' dir for more quick work # note: this options allow RAM Disk allocation # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # set 'true' for enable RAM Disk define('HV_USE_TMPRAMDISK', 'true'); # set 'false' for disable RAM Disk on VM (if you have troubles on VM) define('HV_VM_TMPRAMDISK', 'true'); # ------------------------------------------------------------------------------ # forms # ------------------------------------------------------------------------------ define('HVFORM_HAVP', 'havp'); define('HVFORM_FSCAN', 'havpfscan'); define('HVFORM_AVSET', 'havpavset'); # ------------------------------------------------------------------------------ # defines # ------------------------------------------------------------------------------ # havp define('HVDEF_ADDR', '127.0.0.1'); define('HVDEF_PROXYPORT', '8080'); define('HVDEF_MAXSCANSIZE', '5000000'); # [bytes] ! do not enter 0 or big size ! define('HVDEF_MAXARCSCANSIZE', '5000000'); # [bytes] ! do not enter 0 or big size ! define('HVDEF_PID_FILE', '/var/run/havp.pid'); define('HVDEF_WORK_DIR', '/usr/local/etc/havp'); $pfSversion = str_replace("\s", "", file_get_contents("/etc/version")); if(preg_match("/^2.0/",$pfSversion)) define('HVDEF_LOG_DIR', '/var/log/havp'); else define('HVDEF_LOG_DIR', '/var/log'); define('HVDEF_TEMP_DIR', '/var/tmp'); define('HVDEF_HAVPTEMP_DIR', HVDEF_TEMP_DIR.'/havp'); define('HVDEF_RAMTEMP_DIR', HVDEF_TEMP_DIR.'/havpRAM'); define('HVDEF_SCANTEMPFILE', '/havp-XXXXXX'); define('HVDEF_TEMPLATES', '/usr/local/share/examples/havp/templates'); define('HVDEF_TEMPLATES_EX', HVDEF_TEMPLATES . '_ex'); define('HVDEF_FILTER_RULES', '/tmp/rules.havp'); define('HVDEF_HAVP_CONFIG', HVDEF_WORK_DIR.'/havp.config'); define('HVDEF_HAVP_XMLCONF', HVDEF_WORK_DIR.'/havp_conf.xml'); define('HVDEF_HAVP_WHITELIST', HVDEF_WORK_DIR.'/whitelist'); define('HVDEF_HAVP_BLACKLIST', HVDEF_WORK_DIR.'/blacklist'); define('HVDEF_HAVP_ACCESSLOG', HVDEF_LOG_DIR .'/access.log'); define('HVDEF_HAVP_ERRORLOG', HVDEF_LOG_DIR .'/havp.log'); define('HVDEF_HAVP_MINSRV', '3'); define('HVDEF_HAVP_MAXSRV', '100'); # Clam #define('HVDEF_CLAM_RUNDIR', '/var/run/clamav'); define('HVDEF_CLAM_RUNDIR', '/var/run'); define('HVDEF_CLAM_DBDIR', '/var/db/clamav'); if(preg_match("/^2./",$pfSversion)) define('HVDEF_AVLOG_DIR', '/var/log/clamav'); else define('HVDEF_AVLOG_DIR', '/var/log'); define('HVDEF_CLAM_SOCKET', HVDEF_CLAM_RUNDIR.'/clamd.sock'); define('HVDEF_CLAM_PID', HVDEF_CLAM_RUNDIR.'/clamd.pid'); define('HVDEF_CLAM_LOG', HVDEF_AVLOG_DIR . '/clamd.log'); define('HVDEF_CLAM_WORKDIR', '/usr/local/etc'); define('HVDEF_CLAM_CONFIG', '/usr/local/etc/clamd.conf'); define('HVDEF_CLAM_TCPSOCKET', '3310'); define('HVDEF_FRESHCLAM_CONF', '/usr/local/etc/freshclam.conf'); define('HVDEF_FRESHCLAM_LOG', HVDEF_AVLOG_DIR . '/freshclam.log'); define('HVDEF_CLAMSCAN_LOG', '/var/log/clamscan.log'); define('HVDEF_STATUS_FILE', '/var/tmp/havp.status'); # script's define('HVDEF_SCRIPT_DIR', '/usr/local/etc/rc.d'); define('HVDEF_AVCRON_SCRIPT', '/clamav-freshclam'); define('HVDEF_FILTER_RESYNC_SCRIPT', '/usr/local/pkg/pf/havp_filter_resync.sh'); define('HVDEF_HAVP_STARTUP_SCRIPT', HVDEF_SCRIPT_DIR . '/havp.sh'); define('HVDEF_CLAM_STARTUP_SCRIPT', HVDEF_SCRIPT_DIR . '/clamd'); define('HVDEF_AVUPD_SCRIPT', HVDEF_SCRIPT_DIR . '/havp_avupdate'); # status define('HVDEF_HAVP_STATUS_FILE', '/tmp/havp.status'); define('HVDEF_CLAM_STATUS_FILE', '/tmp/clam.status'); define('HVDEF_UPD_STATUS_FILE', '/tmp/havp.update.status'); define('HVDEF_FRESHCLAM_STATUS_FILE', '/tmp/havp.freshclam.status'); # cron define('HVDEF_CLAM_UPD_CRONNAME', 'havp_clam_update'); define('HVDEF_CLAM_UPD_CRONCMD', HVDEF_SCRIPT_DIR . HVDEF_AVCRON_SCRIPT . " start"); define('HVDEF_CLAM_UPD_CRONKEY', HVDEF_AVCRON_SCRIPT); # user define('HVDEF_USER', 'havp'); define('HVDEF_GROUP', 'havp'); define('HVDEF_AVUSER', HVDEF_USER); # fields define('HV_SCANTEMPFILE', 'hv_scan_tempfile'); # ------------------------------------------------------------------------------ # XML fields # ------------------------------------------------------------------------------ define('F_ENABLE', 'enable'); define('F_PROXYMODE', 'proxymode'); define('F_PROXYINTERFACE', 'proxyinterface'); define('F_PROXYBINDIFACE', 'proxybindiface'); # internal var define('F_PROXYPORT', 'proxyport'); define('F_PARENTPROXY', 'parentproxy'); define('F_LANGUAGE', 'lang'); define('F_MAXDOWNLOADSIZE', 'maxdownloadsize'); define('F_RANGE', 'range'); define('F_WHITELIST', 'whitelist'); define('F_BLACKLIST', 'blacklist'); define('F_ENABLEFORWARDEDIP', 'enableforwardedip'); define('F_ENABLEXFORWARDEDFOR', 'enablexforwardedfor'); define('F_ENABLERAMDISK', 'enableramdisk'); # scanner define('F_FAILSCANERROR', 'failscanerror'); define('F_SCANMAXSIZE', 'scanmaxsize'); define('F_SCANIMG', 'scanimg'); define('F_SCANARC', 'scanarc'); define('F_SCANSTREAM', 'scanstream'); define('F_SCANARCMAXSIZE', 'scanarcmaxsize'); define('F_SCANBROKENEXE', 'scanbrokenexe'); # antivirus options define('F_HAVPUPDATE', 'havpavupdate'); define('F_DBREGION', 'dbregion'); define('F_AVUPDATESERVER', 'avupdateserver'); # log define('F_SYSLOG', 'syslog'); define('F_LOG', 'log'); define('F_AVSETSYSLOG', 'avsetsyslog'); define('F_AVSETLOG', 'avsetlog'); # define('F_TEMPLATEPATH', 'templatepath'); # internal var # file scanner [HVFORM_FSCAN] define('F_SCANFILEPATH', 'scanfilepath'); # ���� ��������� ��� ��� � ��� � ��� ������ define('F_DISABLEXFORWARD', 'disablexforward'); # + forwarded ip define('F_FORWARDEDIP', 'forwardedip'); # ------------------------------------------------------------------------------ # global config # ------------------------------------------------------------------------------ $havp_config = array(); $havp_config[HV_SCANTEMPFILE] = HVDEF_HAVPTEMP_DIR . HVDEF_SCANTEMPFILE; # ------------------------------------------------------------------------------ # Initialization # ------------------------------------------------------------------------------ havp_convert_pfxml_xml(); # ============================================================================== # Installation and config # ============================================================================== function havp_install() { update_status("HAVP check system..\n"); havp_check_system(); havp_fix(); havp_avset_resync(); havp_update_AV(); update_status("Start update Antivirus bases. Wait 5-20 min before use .."); } # ------------------------------------------------------------------------------ function havp_deinstall() { havp_setup_cron(HVDEF_AVUPD_SCRIPT,"", ""); mwexec("killall -9 havp"); mwexec("rm -rf " . HVDEF_HAVP_STARTUP_SCRIPT); mwexec("rm -rf " . HVDEF_FILTER_RESYNC_SCRIPT); mwexec("rm -rf " . HVDEF_PID_FILE); # mwexec("rm -rf " . HVDEF_CLAM_STARTUP_SCRIPT); # mwexec("rm -rf " . HVDEF_AVUPD_SCRIPT); # mwexec("rm -rf " . HVDEF_CLAM_PID); # mwexec("rm -rf " . HVDEF_CLAM_SOCKET); umountRAMDisk(); } # ============================================================================== # Events # ============================================================================== # before form # ------------------------------------------------------------------------------ function havp_before_form($pkg) { } # ------------------------------------------------------------------------------ function havp_fscan_before_form($pkg) { if(is_array($pkg['fields']['field'])) { foreach($pkg['fields']['field'] as $key => $field) { if ($field['fieldname'] === F_SCANFILEPATH) { $pkg['fields']['field'][$key]['description'] .= havp_fscan_html(); break; } } } } # ------------------------------------------------------------------------------ # validation # ------------------------------------------------------------------------------ function havp_validate_settings($post, $input_errors) { $submit = isset($_GET['submit']) ? $_GET['submit'] : $_POST['submit']; # manual update AV database if ($submit === 'Update_AV') { havp_update_AV(); } # Scan file or dir elseif($submit === 'Start_scan') { if (file_exists($post[F_SCANFILEPATH])) start_antivirus_scanner($post[F_SCANFILEPATH]); else $input_errors[] = "File or path not exists '{$post[F_SCANFILEPATH]}'."; } else { # ifaces if (!isset($post[F_PROXYINTERFACE]) || empty($post[F_PROXYINTERFACE])) { $post[F_PROXYINTERFACE] = "lan"; } # port validate $prxport = trim($post[F_PROXYPORT]); if (!empty($prxport) && !is_port($prxport)) $input_errors[] = 'You must enter a valid port number in the \'Proxy port\' field'; # parent proxy validate $parent = trim($post[F_PARENT]); # max download size validate $maxval = trim($post[F_MAXDOWNLOADSIZE]); if (!empty($maxval) && !is_numericint($maxval)) # is_port - validate value $input_errors[] = 'You must enter a valid numeric value in \'Max download size\' field.'; # scan max file size validate $maxval = trim($post[F_SCANMAXSIZE]); if (!empty($maxval) && !is_numericint($maxval)) # is_port - validate value $input_errors[] = 'You must enter a valid numeric value in \'Scan max file size\' field.'; # whitelist validate $lst = str_replace(array(" ", ";"), "\n", $post[F_WHITELIST]); $lst = explode("\n", $lst); foreach ($lst as $dm) { $dm = trim($dm); if ($dm && check_bw_domain($dm) === false) $input_errors[] = "Invalid whitelist element '$dm'. Example: '*domain.com, domain.com/*path*'."; } # blacklist validate $lst = str_replace(array(" ", ";"), "\n", $post[F_BLACKLIST]); $lst = explode("\n", $lst); foreach ($lst as $dm) { $dm = trim($dm); if ($dm && check_bw_domain($dm) === false) $input_errors[] = "Invalid blacklist element '$dm'. Example: '*domain.com, domain.com/*path*'."; } } } # ------------------------------------------------------------------------------ # resync # ------------------------------------------------------------------------------ function havp_resync() { global $havp_config; havp_convert_pfxml_xml(); havp_check_system(); # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # whitelist and blacklist # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # also white-listed by default: $whitelist = havp_whitelist_def() . "\n" . str_replace(";", "\n", $havp_config[F_WHITELIST]); $blacklist = str_replace(";", "\n", $havp_config[F_BLACKLIST]); # fix: stupid havp parser - error on 0x0D: $whitelist = str_replace("\r", "", $whitelist); $blacklist = str_replace("\r", "", $blacklist); file_put_contents(HVDEF_HAVP_WHITELIST, $whitelist); file_put_contents(HVDEF_HAVP_BLACKLIST, $blacklist); # reconfigure clamd havp_reconfigure_clamd(); # config havp file_put_contents (HVDEF_HAVP_CONFIG, havp_config_havp()); havp_set_file_access(HVDEF_WORK_DIR, HVDEF_USER, '0755'); if ($havp_config[F_ENABLE] === 'true') { mwexec_bg(HVDEF_HAVP_STARTUP_SCRIPT . " restart"); log_error("Starting HAVP"); } else { mwexec_bg(HVDEF_HAVP_STARTUP_SCRIPT . " stop"); log_error("Stopping HAVP"); } # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # reconfigure squid havp_configure_squid(); # reconfigure AV parts havp_reconfigure_freshclam(); havp_reconfigure_cron(); # configure system filter for 2.xx if (pfsense_version_() !== '1') filter_configure(); } # ------------------------------------------------------------------------------ function havp_avset_resync() { havp_convert_pfxml_xml(); havp_check_system(); # reconfigure havp_reconfigure_clamd(); havp_reconfigure_freshclam(); havp_reconfigure_cron(); } # ============================================================================== # check system # ============================================================================== function havp_check_system() { global $havp_config; # check user group $grp = exec('pw group show ' . HVDEF_GROUP); if (strpos($grp, HVDEF_GROUP) !== 0) { exec('pw group add ' . HVDEF_GROUP); log_error("Antivirus: Group '" . HVDEF_GROUP . "' was added."); } $usr = exec('pw usershow -n ' . HVDEF_USER); if (strpos($usr, HVDEF_USER) !== 0) { exec('pw useradd ' . HVDEF_USER . ' -g ' . HVDEF_GROUP . ' -h - -s "/sbin/nologin" -d "/nonexistent" -c "havp daemon"'); log_error("Antivirus: User '" . HVDEF_USER . "' was added."); } # workdir permissions havp_set_file_access(HVDEF_WORK_DIR, HVDEF_USER, ''); # havp tempdir if (!file_exists(HVDEF_HAVPTEMP_DIR)) mwexec("mkdir -p " . HVDEF_HAVPTEMP_DIR); havp_set_file_access(HVDEF_HAVPTEMP_DIR, HVDEF_USER, ''); # clamav dbdir if (!file_exists(HVDEF_CLAM_DBDIR)) mwexec("mkdir -p " . HVDEF_CLAM_DBDIR); havp_set_file_access(HVDEF_CLAM_DBDIR, HVDEF_AVUSER, ''); # RAM tempdir if (!file_exists(HVDEF_RAMTEMP_DIR)) mwexec("mkdir -p " . HVDEF_RAMTEMP_DIR); havp_set_file_access(HVDEF_RAMTEMP_DIR, HVDEF_USER, ''); # template permissions if (!file_exists(HVDEF_TEMPLATES_EX)) mwexec("mkdir -p " . HVDEF_TEMPLATES_EX); havp_set_file_access(HVDEF_TEMPLATES, HVDEF_USER, ''); havp_set_file_access(HVDEF_TEMPLATES_EX, HVDEF_USER, ''); # log files exists ? if (!file_exists(HVDEF_HAVP_ACCESSLOG)) file_put_contents(HVDEF_HAVP_ACCESSLOG, ''); if (!file_exists(HVDEF_HAVP_ERRORLOG)) file_put_contents(HVDEF_HAVP_ERRORLOG, ''); # log dir permissions havp_set_file_access(HVDEF_LOG_DIR, HVDEF_USER, '0764'); # pid file if (!file_exists(HVDEF_PID_FILE)) file_put_contents(HVDEF_PID_FILE, ''); havp_set_file_access(HVDEF_PID_FILE, HVDEF_USER, '0664'); # freshclam config permissions if (!file_exists(HVDEF_FRESHCLAM_CONF)) file_put_contents(HVDEF_FRESHCLAM_CONF, ''); havp_set_file_access(HVDEF_FRESHCLAM_CONF, HVDEF_AVUSER, '0664'); # log files exists ? if (!file_exists(HVDEF_CLAM_LOG)) file_put_contents(HVDEF_CLAM_LOG, ''); if (!file_exists(HVDEF_FRESHCLAM_LOG)) file_put_contents(HVDEF_FRESHCLAM_LOG, ''); # log dir permissions if (!file_exists(HVDEF_AVLOG_DIR)) mwexec("mkdir -p " . HVDEF_AVLOG_DIR); havp_set_file_access(HVDEF_AVLOG_DIR, HVDEF_USER, '0777'); # =-= ClamAV =-= # catalog for Pid and Socket files # if (!file_exists(HVDEF_CLAM_RUNDIR)) # mwexec("mkdir -p " . HVDEF_CLAM_RUNDIR); # havp_set_file_access(HVDEF_CLAM_RUNDIR, HVDEF_USER, '0774'); # AV update script file_put_contents(HVDEF_AVUPD_SCRIPT, havp_AVupdate_script()); havp_set_file_access(HVDEF_AVUPD_SCRIPT, HVDEF_AVUSER, '0755'); # AV update notification script # file_put_contents(HVDEF_ON_AVUPD_SCRIPT, havp_on_avupd_script()); # havp_set_file_access(HVDEF_ON_AVUPD_SCRIPT, HVDEF_AVUSER, '0755'); # startup script's (havp and clamd) havp_startup_script(); hv_clamd_startup_script(); # havp filter script if (pfsense_version_() == '1') { # script exists only for 1.2.x file_put_contents(HVDEF_FILTER_RESYNC_SCRIPT, havp_filter_resync_script()); havp_set_file_access(HVDEF_FILTER_RESYNC_SCRIPT, HVDEF_AVUSER, '0755'); } else { # delete script if exists if (file_exists(HVDEF_FILTER_RESYNC_SCRIPT)) mwexec("rm -f " . HVDEF_FILTER_RESYNC_SCRIPT); } # mount RAMDisk mountRAMdisk(true); } # ============================================================================== # Reconfigure package parts # ============================================================================== function havp_reconfigure_clamd() { file_put_contents (HVDEF_CLAM_CONFIG, havp_config_clam()); havp_set_file_access(HVDEF_CLAM_CONFIG, HVDEF_USER, '0664'); } # ------------------------------------------------------------------------------ function havp_reconfigure_freshclam() { # config freshclam file_put_contents (HVDEF_FRESHCLAM_CONF, havp_config_freshclam()); havp_set_file_access(HVDEF_FRESHCLAM_CONF, HVDEF_USER, '0664'); } # ------------------------------------------------------------------------------ function havp_reconfigure_cron() { global $havp_config; # cron task $on = false; $optval = array("", "*/1", "*/2", "*/3", "*/4", "*/6", "*/8", "*/12", "0"); $opt = array("0", "*", "*", "*", "*", "root", "/usr/bin/nice -n20 " . HVDEF_AVUPD_SCRIPT); $opt[1] = $optval[$havp_config[F_HAVPUPDATE]]; $on = ($opt[1] !== ""); havp_setup_cron(HVDEF_AVUPD_SCRIPT, $opt, $on); } # ------------------------------------------------------------------------------ # Convert conf to XML # ------------------------------------------------------------------------------ function havp_convert_pfxml_xml() { global $config, $havp_config; $pfconf = $config['installedpackages'][HVFORM_HAVP]['config'][0]; # === GUI Fields === $havp_config[F_ENABLE] = ( $pfconf[F_ENABLE] === 'on' ? 'true' : 'false' ); # proxy $havp_config[F_PROXYMODE] = ( !empty($pfconf[F_PROXYMODE]) ? $pfconf[F_PROXYMODE] : 'standard' ); # ToDo: add check squid transparent $havp_config[F_PROXYINTERFACE] = $pfconf[F_PROXYINTERFACE]; $havp_config[F_PROXYPORT] = ( !empty($pfconf[F_PROXYPORT]) ? $pfconf[F_PROXYPORT] : HVDEF_PROXYPORT ); # ToDo: add check squid proxy port # parent proxy # [F_PARENTPROXY] = "proxy_ip:port" $pfconf[F_PARENTPROXY] = trim($pfconf[F_PARENTPROXY]); if (!empty($pfconf[F_PARENTPROXY])) { $parent = explode(":", trim($pfconf[F_PARENTPROXY])); $havp_config[F_PARENTPROXY] = array( 'ip' => $parent[0], 'port' => $parent[1] ); } else $havp_config[F_PARENTPROXY] = ''; # language $havp_config[F_LANGUAGE] = trim($pfconf[F_LANGUAGE]); # proxy settings $havp_config[F_ENABLEFORWARDEDIP] = ( $pfconf[F_ENABLEFORWARDEDIP] === 'on' ? 'true' : 'false' ); $havp_config[F_ENABLEXFORWARDEDFOR] = ( $pfconf[F_ENABLEXFORWARDEDFOR] === 'on' ? 'true' : 'false' ); $havp_config[F_MAXDOWNLOADSIZE] = ( is_numeric($pfconf[F_MAXDOWNLOADSIZE]) ? $pfconf[F_MAXDOWNLOADSIZE] : 0 ); $havp_config[F_RANGE] = ( $pfconf[F_RANGE] === 'on' ? 'true' : 'false' ); $havp_config[F_ENABLERAMDISK] = ( $pfconf[F_ENABLERAMDISK] === 'on' ? 'true' : 'false' ); # whitelist $havp_config[F_WHITELIST] = base64_decode($pfconf[F_WHITELIST]); $havp_config[F_WHITELIST] = str_replace(";", "\n", $havp_config[F_WHITELIST]); $havp_config[F_WHITELIST] = str_replace(";", " ", $havp_config[F_WHITELIST]); # blacklist $havp_config[F_BLACKLIST] = base64_decode($pfconf[F_BLACKLIST]); $havp_config[F_BLACKLIST] = str_replace(";", "\n", $havp_config[F_BLACKLIST]); $havp_config[F_BLACKLIST] = str_replace(";", " ", $havp_config[F_BLACKLIST]); # =-= Temp RAMDisk =-= # use RAMDisk if only capacity > calculated [MAXSCANSIZE * 50 connections] # =-= # before config manage Temp Dir = RAMDisk|Hard Disk $havp_config[HV_SCANTEMPFILE] = HVDEF_HAVPTEMP_DIR . HVDEF_SCANTEMPFILE; if ($havp_config[F_ENABLERAMDISK] === 'true') { $sys_capacity = get_memory(); $mem_capacity = intval($sys_capacity[0]) / 4; # [Mb] $calculated = 50 * $havp_config[F_SCANMAXSIZE] / (1024 * 1024); # [Mb] # this is restriction need for balancing between pfSense and HAVP work speed # we can not allocate memory at the expense of other services of the pfSense if ($mem_capacity > $calculated) { # re-define temp file to RAM Disk $havp_config[HV_SCANTEMPFILE] = HVDEF_RAMTEMP_DIR . HVDEF_SCANTEMPFILE; } else log_error("havp: RAMDisk not used. Diagnostic: system {$sys_capacity[0]}Mb, avialable {$mem_capacity}Mb, calculated {$calculated}Mb. Try reducing 'MAXSCANSIZE' value."); } # scanner $havp_config[F_FAILSCANERROR] = ( $pfconf[F_FAILSCANERROR] === 'on' ? 'true' : 'false' ); $havp_config[F_SCANMAXSIZE] = ( is_numeric($pfconf[F_SCANMAXSIZE]) ? $pfconf[F_SCANMAXSIZE] : HVDEF_MAXSCANSIZE ) * 1024; # KB -> Byte $havp_config[F_SCANIMG] = ( $pfconf[F_SCANIMG] === 'on' ? 'true' : 'false' ); $havp_config[F_SCANARC] = ( $pfconf[F_SCANARC] === 'on' ? 'true' : 'false' ); $havp_config[F_SCANSTREAM] = ( $pfconf[F_SCANSTREAM] === 'on' ? 'true' : 'false' ); $havp_config[F_SCANBROKENEXE] = ( $pfconf[F_SCANBROKENEXE] === 'on' ? 'true' : 'false' ); $havp_config[F_SCANARCMAXSIZE] = ( is_numeric($pfconf[F_SCANARCMAXSIZE]) ? $pfconf[F_SCANARCMAXSIZE] : HVDEF_MAXARCSCANSIZE ); # log $havp_config[F_SYSLOG] = ( $pfconf[F_SYSLOG] === 'on' ? 'true' : 'false' ); $havp_config[F_LOG] = ( $pfconf[F_LOG] === 'on' ? 'true' : 'false' ); # # =-= Internal variables =-= # proxy $havp_config[F_PROXYBINDIFACE] = 'localhost'; # language template files path $lng = $havp_config[F_LANGUAGE] ? $havp_config[F_LANGUAGE] : "en"; $havp_config[F_TEMPLATEPATH] = ( file_exists(HVDEF_TEMPLATES_EX . "/$lng") ? HVDEF_TEMPLATES_EX : HVDEF_TEMPLATES ); $havp_config[F_TEMPLATEPATH] .= "/$lng"; # # =-= HVFORM_AVSET =-= # av settings $pf_avset_conf = $config['installedpackages'][HVFORM_AVSET]['config'][0]; $havp_config[F_HAVPUPDATE] = $pf_avset_conf[F_HAVPUPDATE]; $havp_config[F_DBREGION] = $pf_avset_conf[F_DBREGION]; $havp_config[F_AVUPDATESERVER] = $pf_avset_conf[F_AVUPDATESERVER]; # avlog $havp_config[F_AVSETSYSLOG] = $pf_avset_conf[F_AVSETSYSLOG] === 'on' ? 'true' : 'false'; $havp_config[F_AVSETLOG] = $pf_avset_conf[F_AVSETLOG] === 'on' ? 'true' : 'false'; # # store havp config cache $cfg_xml = dump_xml_config($havp_config, 'havp'); file_put_contents(HVDEF_HAVP_XMLCONF, $cfg_xml); return $havp_config; } # ------------------------------------------------------------------------------ # config # ------------------------------------------------------------------------------ # HAVP config function havp_config_havp() { global $havp_config; $conf = array(); $conf[] = "# ============================================================ # HAVP config file # This file generated automaticly with HAVP configurator (part of pfSense) # (C)2008 Serg Dvoriancev # email: dv_serg@mail.ru # ============================================================ "; $conf[] = "USER " . HVDEF_USER; $conf[] = "GROUP " . HVDEF_GROUP; $conf[] = "DAEMON true"; $conf[] = "PIDFILE " . HVDEF_PID_FILE; $conf[] = "\n# For small home use, 8 should be minimum."; $conf[] = "# For 500 users corporate use, start at 40."; $conf[] = "SERVERNUMBER " . HVDEF_HAVP_MINSRV; $conf[] = "MAXSERVERS " . HVDEF_HAVP_MAXSRV; # log $conf[] = "\n# log "; $conf[] = "ACCESSLOG " . HVDEF_HAVP_ACCESSLOG; $conf[] = "ERRORLOG " . HVDEF_HAVP_ERRORLOG; # syslog $conf[] = "\n# syslog"; $conf[] = "USESYSLOG {$havp_config[F_SYSLOG]}"; $conf[] = "SYSLOGNAME havp"; $conf[] = "SYSLOGFACILITY daemon"; $conf[] = "SYSLOGLEVEL " . (HV_DEBUG === 'true' ? "debug" : "info"); # err | warning | info | debug # $conf[] = "\n# Level of HAVP logging\n# 0 = Only serious errors and information\n# 1 = Less interesting information is included"; $conf[] = "LOG_OKS false"; # false - access_log requests viruses only, true - access_log all requests $conf[] = "LOGLEVEL " . ( HV_DEBUG === 'true' ? "1" : "0" ); # 0 - work level, 1 - debug level # temp $conf[] = "\n# temp "; $conf[] = "SCANTEMPFILE " . $havp_config[HV_SCANTEMPFILE]; $conf[] = "TEMPDIR " . HVDEF_TEMP_DIR; # $conf[] = "\n#"; $conf[] = "DBRELOAD 180"; $conf[] = "TRANSPARENT " . ( $havp_config[F_PROXYMODE] === 'transparent' ? "true" : "false" ); # X-FORWARD, X-FORWARDED-FOR options $conf[] = "\n# if HAVP is used as parent proxy by some other proxy, this allows to write the real users IP to log, instead of proxy IP."; $conf[] = "FORWARDED_IP " . $havp_config[F_ENABLEFORWARDEDIP]; $conf[] = "X_FORWARDED_FOR " . $havp_config[F_ENABLEXFORWARDEDFOR]; # parent proxy = [proxy:port] if (!empty($havp_config[F_PARENTPROXY])) { $conf[] = "\n# parent proxy "; $conf[] = "PARENTPROXY {$havp_config[F_PARENTPROXY]['ip']}"; $conf[] = "PARENTPORT {$havp_config[F_PARENTPROXY]['port']}"; } # proxy listening on $conf[] = "\n# havp is listening on "; $conf[] = "PORT {$havp_config[F_PROXYPORT]}"; # bind to ip address $bind_iface = get_real_interface_address($havp_config[F_PROXYBINDIFACE]); $conf[] = "BIND_ADDRESS {$bind_iface[0]}"; # template files language $conf[] = "\n# Path to template files "; $conf[] = "TEMPLATEPATH {$havp_config[F_TEMPLATEPATH]}"; # $conf[] = "\n# whitelist and blacklist"; $conf[] = "WHITELISTFIRST true"; $conf[] = "WHITELIST " . HVDEF_HAVP_WHITELIST; $conf[] = "BLACKLIST " . HVDEF_HAVP_BLACKLIST; # failscanerror - pass/block files if scanner error $conf[] = "\n# block file if error scanning"; $conf[] = "FAILSCANERROR {$havp_config[F_FAILSCANERROR]}"; # $conf[] = "\n# scanner "; $conf[] = "SCANNERTIMEOUT 10"; # if ($havp_config[F_SCANSTREAM] === 'true') { # $conf[] = "\n# always allow range, if stream scan enabled"; $conf[] = "RANGE true"; $conf[] = "\n# stream scan enabled"; $conf[] = "STREAMUSERAGENT Player Winamp iTunes QuickTime Audio RMA/ MAD/ Foobar2000 XMMS"; $conf[] = "STREAMSCANSIZE 2000"; } else { # renew downloads ? $conf[] = "RANGE {$havp_config[F_RANGE]}"; $conf[] = "\n# stream scan disabled"; $conf[] = "STREAMSCANSIZE 0"; } # scan options $conf[] = "SCANIMAGES {$havp_config[F_SCANIMG]}"; $conf[] = "MAXSCANSIZE {$havp_config[F_SCANMAXSIZE]}"; # $conf[] = "KEEPBACKBUFFER 200000"; $conf[] = "KEEPBACKTIME 5"; # $conf[] = "# After Trickling Time (seconds), some bytes are sent to browser to keep the connection alive"; $conf[] = "TRICKLING 10"; $conf[] = "TRICKLINGBYTES 1"; # $conf[] = "# Downloads larger than MAXDOWNLOADSIZE will be blocked."; $conf[] = "MAXDOWNLOADSIZE {$havp_config[F_MAXDOWNLOADSIZE]}"; # $conf[] = "\n# ClamAV Library Scanner (libclamav) "; $conf[] = "ENABLECLAMLIB " . (HV_USE_CLAMD !== 'true' ? "true" : "false"); # use clamd, if configured if (HV_USE_CLAMD === 'true') { $conf[] = "\n# Clamd scanner (Clam daemon)"; $conf[] = "ENABLECLAMD true"; # clamd socket if (HV_CLAMD_TCPSOCKET === 'true') { $conf[] = "CLAMDSERVER 127.0.0.1"; $conf[] = "CLAMDPORT " . HVDEF_CLAM_TCPSOCKET; } else $conf[] = "CLAMDSOCKET " . HVDEF_CLAM_SOCKET; } $conf[] = ""; return implode("\n", $conf); } # ------------------------------------------------------------------------------ # Clamd config # ------------------------------------------------------------------------------ function havp_config_clam() { global $havp_config; $conf = array(); $conf[] = "# ============================================================================== # CLAMD config file # This file generated automaticly with HAVP configurator (part of pfSense) # (C)2008 Serg Dvoriancev # email: dv_serg@mail.ru # ============================================================================== "; $conf[] = "# log"; $conf[] = "LogFileUnlock yes"; $conf[] = "LogFileMaxSize 2M"; $conf[] = "LogTime yes"; $conf[] = "LogClean no"; $conf[] = "LogFacility LOG_LOCAL6"; $conf[] = "LogVerbose " . ( HV_DEBUG === "true" ? "yes" : "no" ); # Syslog $islog = $havp_config[F_AVSETLOG] === 'true'; $issyslog = $havp_config[F_AVSETSYSLOG] === 'true'; $conf[] = "LogSyslog " . ($islog && $issyslog ? 'yes' : 'no'); if ($islog && !$issyslog) $conf[] = "LogFile " . HVDEF_CLAM_LOG; # $conf[] = "\n# sysdirs"; $conf[] = "PidFile " . HVDEF_CLAM_PID; $conf[] = "TemporaryDirectory " . HVDEF_TEMP_DIR; $conf[] = "DatabaseDirectory /var/db/clamav"; # $conf[] = "\n# socket"; $conf[] = "LocalSocket " . HVDEF_CLAM_SOCKET; $conf[] = "FixStaleSocket yes"; # if (HV_CLAMD_TCPSOCKET === 'true') { $conf[] = "TCPAddr 127.0.0.1"; $conf[] = "TCPSocket " . HVDEF_CLAM_TCPSOCKET; } $conf[] = "MaxConnectionQueueLength 30"; # $conf[] = "\n# daemon"; $conf[] = "MaxThreads 100"; # $conf[] = "\n# scanner"; $conf[] = "MaxDirectoryRecursion 255"; $conf[] = "FollowDirectorySymlinks no"; # not need scan symbol links dirs $conf[] = "FollowFileSymlinks yes"; $conf[] = "# perform a database check.(sec) [3600 sec = 60 min]"; $conf[] = "SelfCheck 3600"; $conf[] = "# detect possibly unwanted applications."; $conf[] = "DetectPUA no"; # possible unwanted applications $conf[] = "AlgorithmicDetection yes"; $conf[] = "# executable"; if ($havp_config[F_SCANBROKENEXE] === 'true') {$conf[] = "DetectBrokenExecutables yes";} else {$conf[] = "DetectBrokenExecutables no";} # $conf[] = "ScanPE yes"; $conf[] = "ScanELF yes"; $conf[] = "# documents"; $conf[] = "ScanOLE2 yes"; $conf[] = "ScanPDF yes"; $conf[] = "# email"; $conf[] = "ScanMail yes"; $conf[] = "MailFollowURLs no"; $conf[] = "PhishingSignatures yes"; $conf[] = "PhishingScanURLs yes"; $conf[] = "PhishingAlwaysBlockSSLMismatch no"; $conf[] = "PhishingAlwaysBlockCloak no"; $conf[] = "# html"; $conf[] = "ScanHTML yes"; $conf[] = "# archives"; $conf[] = "ScanArchive yes"; # $conf[] = "ArchiveLimitMemoryUsage no"; # deprecated on 0.95 $conf[] = "ArchiveBlockEncrypted no"; $conf[] = "# limits"; $conf[] = "MaxScanSize 50M"; $conf[] = "MaxFileSize 30M"; $conf[] = "MaxRecursion 255"; $conf[] = "MaxFiles 10000"; # $conf[] = "\n# system"; $conf[] = "User root"; # . HVDEF_USER; # mast have full access to files for scan $conf[] = "AllowSupplementaryGroups yes"; $conf[] = "Debug " . (HV_DEBUG === 'true' ? "yes" : "no"); # $conf[] = ""; return implode("\n", $conf); } # ------------------------------------------------------------------------------ # FreshClamAV config # ------------------------------------------------------------------------------ function havp_config_freshclam() { global $havp_config; $pfconf = $havp_config; $conf = array(); $conf[] = "# ============================================================================== # freshclam(HAVP) config file # This file generated automaticly with HAVP configurator (part of pfSense) # (C)2008 Serg Dvoriancev # email: dv_serg@mail.ru # ============================================================================== "; $conf[] = "DatabaseDirectory /var/db/clamav"; # log $conf[] = "LogFileMaxSize 2M"; $conf[] = "LogTime yes"; $conf[] = "LogVerbose " . ( HV_DEBUG === "true" ? "yes" : "no" ); $conf[] = "LogFacility LOG_LOCAL6"; # LOG_LOCAL6 | LOG_MAIL $conf[] = "\n# syslog"; # Syslog $is_syslog = ($pfconf[F_AVSETLOG] === 'true') && ($pfconf[F_AVSETSYSLOG] === 'true'); $conf[] = "LogSyslog " . ( $is_syslog ? 'yes' : 'no'); unset ($is_syslog); # log # freshclam for 1.2.x have a bug with logfile permissions; now disable logfile for 1.2.x - only syslog $is_log = (pfsense_version_() != "1") && ($pfconf[F_AVSETLOG] === 'true'); if ($is_log) { $conf[] = "UpdateLogFile " . HVDEF_FRESHCLAM_LOG; } else { $conf[] = "# for pfsense 1.2.x Log disabled - permission bug exists!"; } unset ($is_log); $conf[] = "\n# pid"; $conf[] = "PidFile /var/run/clamav/freshclam.pid"; $conf[] = "\n# db"; $conf[] = "DatabaseOwner havp"; $conf[] = "AllowSupplementaryGroups yes"; $conf[] = "DNSDatabaseInfo current.cvd.clamav.net"; $avsrv = $pfconf[F_AVUPDATESERVER]; $avsrv = explode(" ", trim($avsrv)); foreach ($avsrv as $asr) if (!empty($asr)) $conf[] = "DatabaseMirror $asr"; # regional mirror if (!empty($pfconf[F_DBREGION])) { $conf[] = '# regional db'; switch($pfconf[F_DBREGION]) { case 'au': $conf[] = "DatabaseMirror clamav.mirror.ayudahosting.com.au"; break; # australia case 'ca': $conf[] = "DatabaseMirror clamav.mirror.rafal.ca"; break; # canada case 'cn': $conf[] = "DatabaseMirror 4most2.clamav.ialfa.net"; break; # china case 'eu': $conf[] = "DatabaseMirror clamav.edpnet.net"; break; # europe case 'id': $conf[] = "DatabaseMirror db.clamav.or.id"; break; # indonesia case 'jp': $conf[] = "DatabaseMirror clamavdb2.ml-club.jp"; break; # japan case 'kr': $conf[] = "DatabaseMirror clamav.hostway.co.kr"; break; # korea case 'ml': $conf[] = "DatabaseMirror clamav.doubleukay.com"; break; # malaysia case 'ru': $conf[] = "DatabaseMirror clamav.citrin.ru"; break; # russia case 'sa': $conf[] = "DatabaseMirror clamav.dial-up.net"; break; # south africa case 'tw': $conf[] = "DatabaseMirror clamav.cs.pu.edu.tw"; break; # taiwan case 'uk': $conf[] = "DatabaseMirror clamav.oucs.ox.ac.uk"; break; # united kingdom case 'us': $conf[] = "DatabaseMirror db.us.clamav.net "; break; # united states default: break; } } $conf[] = "DatabaseMirror db.at.clamav.net"; $conf[] = "DatabaseMirror db.au.clamav.net"; $conf[] = "DatabaseMirror db.ba.clamav.net"; $conf[] = "DatabaseMirror db.be.clamav.net"; $conf[] = "DatabaseMirror db.ca.clamav.net"; $conf[] = "DatabaseMirror db.ch.clamav.net"; $conf[] = "DatabaseMirror db.cn.clamav.net"; $conf[] = "DatabaseMirror db.cr.clamav.net"; $conf[] = "DatabaseMirror db.cy.clamav.net"; $conf[] = "DatabaseMirror db.cz.clamav.net"; $conf[] = "DatabaseMirror db.de.clamav.net"; $conf[] = "DatabaseMirror db.dk.clamav.net"; $conf[] = "DatabaseMirror db.ec.clamav.net"; $conf[] = "DatabaseMirror db.ee.clamav.net"; $conf[] = "DatabaseMirror db.es.clamav.net"; $conf[] = "DatabaseMirror db.fi.clamav.net"; $conf[] = "DatabaseMirror db.fr.clamav.net"; $conf[] = "DatabaseMirror db.gr.clamav.net"; $conf[] = "DatabaseMirror db.hk.clamav.net"; $conf[] = "DatabaseMirror db.hu.clamav.net"; $conf[] = "DatabaseMirror db.id.clamav.net"; $conf[] = "DatabaseMirror db.ie.clamav.net"; $conf[] = "DatabaseMirror db.it.clamav.net"; $conf[] = "DatabaseMirror db.jp.clamav.net"; $conf[] = "DatabaseMirror db.kr.clamav.net"; $conf[] = "DatabaseMirror db.li.clamav.net"; $conf[] = "DatabaseMirror db.lt.clamav.net"; $conf[] = "DatabaseMirror db.lv.clamav.net"; $conf[] = "DatabaseMirror db.mt.clamav.net"; $conf[] = "DatabaseMirror db.my.clamav.net"; $conf[] = "DatabaseMirror db.ml.clamav.net"; $conf[] = "DatabaseMirror db.no.clamav.net"; $conf[] = "DatabaseMirror db.pl.clamav.net"; $conf[] = "DatabaseMirror db.pt.clamav.net"; $conf[] = "DatabaseMirror db.ro.clamav.net"; $conf[] = "DatabaseMirror db.ru.clamav.net"; $conf[] = "DatabaseMirror db.se.clamav.net"; $conf[] = "DatabaseMirror db.sk.clamav.net"; $conf[] = "DatabaseMirror db.th.clamav.net"; $conf[] = "DatabaseMirror db.tr.clamav.net"; $conf[] = "DatabaseMirror db.tw.clamav.net"; $conf[] = "DatabaseMirror db.ua.clamav.net"; $conf[] = "DatabaseMirror db.uk.clamav.net"; $conf[] = "DatabaseMirror db.za.clamav.net"; $conf[] = "\n# DO NOT TOUCH the following line "; $conf[] = "DatabaseMirror database.clamav.net"; $conf[] = "\n# Number of database checks per day. Default: 12 (every two hours)"; $chks = 0; $conf[] = "Checks $chks"; $conf[] = "# notification"; $conf[] = "OnUpdateExecute date \"+%Y.%m.%d %H:%M:%S Antivirus update success\" > " . HVDEF_FRESHCLAM_STATUS_FILE; $conf[] = "OnErrorExecute date \"+%Y.%m.%d %H:%M:%S Antivirus update error\" > " . HVDEF_FRESHCLAM_STATUS_FILE; $conf[] = "Debug " . (HV_DEBUG === 'true' ? "yes" : "no"); # $conf[] = "# Proxy settings"; # future #HTTPProxyServer myproxy.com #HTTPProxyPort 1234 #HTTPProxyUsername myusername #HTTPProxyPassword mypass # MAKE GUI Errors display # Run command when database update process fails. # Default: disabled #OnErrorExecute command # Run command when freshclam reports outdated version. # In the command string %v will be replaced by the new version number. # Default: disabled #OnOutdatedExecute command # Enable debug messages in libclamav. # Default: disabled #Debug yes # use google safesearch AV database $conf[] = "SafeBrowsing yes"; $conf[] = ""; return implode("\n", $conf); } # ------------------------------------------------------------------------------ # configure squid function havp_configure_squid() { global $config, $havp_config; $new_opt = array(); $on_configure = ($havp_config[F_PROXYMODE] === 'squid' ? true : false); if (!isset($config['installedpackages']['squid']['config'][0]['custom_options'])) return; if ($on_configure === true) { $new_opt[] = "never_direct allow all"; $new_opt[] = "cache_peer 127.0.0.1 parent {$havp_config[F_PROXYPORT]} 0 name=havp no-query no-digest no-netdb-exchange default"; } # copy options, but not 'cache_peer' option $cust_opt = explode(";", $config['installedpackages']['squid']['config'][0]['custom_options']); foreach($cust_opt as $key => $val) { if (strpos($val, "never_direct") !== false) continue; if (strpos($val, "cache_peer 127.0.0.1 parent") !== false) continue; $new_opt[] = $val; } $new_opt = implode(";", $new_opt); if (/*is_package_installed('squid') && */file_exists('/usr/local/pkg/squid.inc')) { # squid config update $config['installedpackages']['squid']['config'][0]['custom_options'] = $new_opt; # disable upstream proxy if ($on_configure === true) $config['installedpackages']['squidupstream']['config'][0]['proxy_forwarding'] = ''; write_config('Update redirector options to squid config.'); require_once('squid.inc'); squid_resync(); } } # ------------------------------------------------------------------------------ function havp_whitelist_def() { $whitelist = array(); $whitelist[] = "*sourceforge.net/*clamav-*"; $whitelist[] = "*pfsense.com/*"; $whitelist[] = "*.microsoft.com/*"; $whitelist[] = "*.windowsupdate.com/*"; # M$ & M$ update # media and image extensions $whitelist[] = "*/*.gif\n*/*.swf\n*/*.png\n*/*.jpg\n*/*.jpeg\n*/*.mov\n*/*.avi\n*/*.flv\n*/*.bmp\n*/*.ico\n*/*.pdf\n*/*.mp3\n*/*.wma\n*/*.wmv\n*/*.ogg"; return implode("\n", $whitelist); } # ============================================================================== # Utils # ============================================================================== function havp_set_file_access($dir, $owner, $mod) { if ( file_exists($dir) ) { mwexec("chgrp -R -v $owner $dir"); mwexec("chown -R -v $owner $dir"); if (!empty($mod)) { mwexec( "chmod -R -v $mod $dir"); } } } # ------------------------------------------------------------------------------ # Src from squid.inc Copyright (C) 2006 Scott Ullrich, Fernando Lemos function get_real_interface_address($iface) { global $config; if ($iface === 'localhost') return array('127.0.0.1', ''); $iface = convert_friendly_interface_to_real_interface_name($iface); $line = trim(shell_exec("ifconfig $iface | grep inet | grep -v inet6")); list($dummy, $ip, $dummy2, $netmask) = explode(" ", $line); return array($ip, long2ip(hexdec($netmask))); } #------------------------------------------------------------------------------- # *** check black/white list domain *** # Lines can hold URLs with wildcards with following rules: # Line must cointain Domain/Path # Domains can have a wildcard at begin. # Pages can hav a wildcard at begin and end. # URLs without wildcards are exact # Examples: # (1) www.server-side.de (Only this URL is whitelisted) # (2) www.server-side.de/* (Domain is completely whitelisted) # (3) *server-side.de/index.html # (4) */*.gif (All .gif are whitelisted) # (5) www.server-side.de/novirus* # (6) www.server-side.de/*novirus* #------------------------------------------------------------------------------- function check_bw_domain($_dm) { $domain = ""; $path = ""; if (!is_string($_dm)) return false; $pos = strpos($_dm, "/"); if ($pos === false) { $domain = $_dm; $path = ""; } else { $domain = substr($_dm, 0, $pos); $path = substr($_dm, $pos+1); } # Domains can have a wildcard at begin '*domain.xx' - *my.domain.com # Path can have a wildcard(*) at begin and end '*xxx*' # Regex: * - {0,}; + - {1,}; ? = {0,1} $df = "[a-zA-Z0-9\-]"; $dm_fmt = "^((\*)|(\*\.))?($df+\.)+$df{2,}$"; # d.com *d.com *.d.com $ph_fmt = "^((\*)|((\*)?([^\*]+)(\*)?))$"; # *path* if (empty($path)) { # d.com *d.com *.d.com return eregi($dm_fmt, $domain); } else { if (!empty($domain)) { return (($domain === '*') || eregi($dm_fmt, $domain)) && eregi($ph_fmt, $path); } } return false; } # ------------------------------------------------------------------------------ # cron # ------------------------------------------------------------------------------ # $options: [0]='minute', [1]='hour', [2]='mday', [3]='month', [4]='wday', [5]='who', [6]='command' # function havp_setup_cron($task_key, $options, $on_off) { global $config; $cron_item = array(); # $on_off = TRUE/FALSE - install/deinstall cron task: # prepare new cron item if (is_array($options)) { $cron_item['minute'] = $options[0]; $cron_item['hour'] = $options[1]; $cron_item['mday'] = $options[2]; $cron_item['month'] = $options[3]; $cron_item['wday'] = $options[4]; $cron_item['who'] = ($options[5]) ? $options[5] : 'nobody'; $cron_item['command'] = $options[6]; } # unset old cron task with $task_key if (!empty($task_key)) { $flag_cron_upd = false; # delete old cron task if exists if (is_array($config['cron']['item'])) { foreach($config['cron']['item'] as $key => $val) { if (strpos($config['cron']['item'][$key]['command'], $task_key) !== false) { unset($config['cron']['item'][$key]); $flag_cron_upd = true; break; } } } # set new cron task if (($on_off === true) and !empty($cron_item)) { $config['cron']['item'][] = $cron_item; $flag_cron_upd = true; } # write config and configure cron only if cron task modified if ($flag_cron_upd === true) { write_config("Installed cron task '$task_key' for 'havp' package"); configure_cron(); } } else { # ! error $name ! return; } } # ------------------------------------------------------------------------------ # filter rules # ------------------------------------------------------------------------------ function havp_generate_rules($type = 'filter') { # pfSense v.2.x - welcome ! # 'nat' 'filter' global $config, $havp_config; $rules = array(); # no rules if havp disabled if ($havp_config[F_ENABLE] !== 'true') { return ''; } $proxymode = $havp_config[F_PROXYMODE]; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # =-= HAVP always listen 127.0.0.1:port =-= # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Proxy mode: # Standard - Filter: Rdr ifaces:port => 127.0.0.1:port # Parent for Squid - Filter: No # Transparent - Filter: Rdr ifaces:port => 127.0.0.1:port; # Rdr Any Http => 127.0.0.1:port + Allow Http traffic via iface # If Squid transparent, then as Standard. # Internal - Filter: No # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ $proxybindiface = 'lo0'; # 127.0.0.1 $ifaces = array_map('convert_friendly_interface_to_real_interface_name', explode(',', $havp_config[F_PROXYINTERFACE])); $proxyport = ( $havp_config[F_PROXYPORT] ? $havp_config[F_PROXYPORT] : HVDEF_PROXYPORT ); # squid already transparent $squid_transparent_proxy = ($config['installedpackages']['squid']['config'][0]['transparent_proxy'] == 'on'); if (($proxymode === 'transparent') && $squid_transparent_proxy) { $proxymode = 'standard'; log_error("Havp: Squid is already configured as transparent proxy. Use 'Standard' proxy mode."); } # nat if ($type == 'nat') { $rules[] = ""; $rules[] = "# havp proxy ifaces redirect"; foreach($ifaces as $iface) { switch($proxymode) { case 'transparent': # rdr any http => localhost:port $rules[] = "rdr on $iface proto tcp from any to !($iface) port 80 -> $proxybindiface port $proxyport"; case 'standard': case 'squid': # rdr iface:port => localhost:port $rules[] = "rdr on $iface proto tcp from any to ($iface) port $proxyport -> $proxybindiface port $proxyport"; break; # no more rdr case 'internal': default: break; } } $rules[] = ""; } # filter if ($type == 'filter' || $type == 'rule') { $rules[] = ""; $rules[] = "# havp proxy ifaces rules"; foreach($ifaces as $iface) { switch($proxymode) { case 'transparent': # pass http on iface $rules[] = "pass in quick on $iface proto tcp from any to !($iface) port 80 flags S/SA keep state"; break; # no more rules case 'standard': case 'squid': case 'internal': default: break; } } $rules[] = ""; } if ($type == 'pfearly') { } if ($type == 'pflate') { } # test # file_put_contents("/tmp/havp_".$type, "state: $proxymode\n" . implode("\n", $rules)); return implode("\n", $rules); } # ------------------------------------------------------------------------------ function havp_filter_update_3() { # for 1.x only if (pfsense_version_() != '1') return; $rules_file = '/tmp/rules.debug'; if (file_exists($rules_file)) { $newrules = array(); $rules = file_get_contents($rules_file); $rules = explode("\n", $rules); foreach($rules as $val) { $newrules[] = $val; # rdr if (trim($val) === "rdr-anchor \"miniupnpd\"") { $newrules[] = "# havp rdr"; $newrules[] = havp_generate_rules('nat'); $newrules[] = ""; } # rules elseif(trim($val) === "anchor \"miniupnpd\"") { $newrules[] = "# havp rules"; $newrules[] = havp_generate_rules('filter'); $newrules[] = ""; } $rules = implode("\n", $newrules); } file_put_contents($rules_file, $rules); mwexec("pfctl -f $rules_file"); } } # ------------------------------------------------------------------------------ function havp_update_AV() { # AV update script file_put_contents(HVDEF_AVUPD_SCRIPT, havp_AVupdate_script()); havp_set_file_access(HVDEF_AVUPD_SCRIPT, HVDEF_AVUSER, '0755'); mwexec_bg(HVDEF_AVUPD_SCRIPT); # run update background } # ============================================================================== # Scripts # ============================================================================== # AV update script function havp_AVupdate_script() { $f = HVDEF_UPD_STATUS_FILE; $u = HVDEF_FRESHCLAM_STATUS_FILE; return <<<EOD #!/bin/sh date +"%Y.%m.%d %H:%M:%S Antivirus update started." > $f date +"%Y.%m.%d %H:%M:%S Antivirus database already is updated." > $u /usr/local/bin/freshclam wait cat $u >> $f /usr/local/bin/sigtool --unpack-current daily.cvd /usr/local/bin/sigtool --unpack-current main.cvd wait date +"%Y.%m.%d %H:%M:%S Antivirus update end." >> $f EOD; } # ------------------------------------------------------------------------------ # HAVP service startup script function havp_startup_script() { global $havp_config; $pid = HVDEF_PID_FILE; $havpchk = "ps auxw | grep \"[h]avp -c\"|awk '{print $2}'"; $clamdchk = "ps auxw | grep \"[c]lamd -c\"|awk '{print $2}'"; # rc script $rc = array(); $rc['file'] = basename(HVDEF_HAVP_STARTUP_SCRIPT); $s[] = "\t# init"; $s[] = "\techo 'Starting ..' > " . HVDEF_HAVP_STATUS_FILE; $s[] = "\t# start"; $s[] = "\tif [ -z \"`{$havpchk}`\" ];then"; if (HV_USE_CLAMD === 'true') { $clampid_dir = HVDEF_CLAM_RUNDIR; $s[] = "\t\t# start clamd before (to be sure)"; $s[] = "\t\t" . HVDEF_CLAM_STARTUP_SCRIPT . " start"; $s[] = "\t\tsleep 2"; $s[] = ""; $s[] = "\t\t# if clamd started"; $s[] = "\t\tif [ -n \"`{$clamdchk}`\" ];then"; $s[] = "\t\t\t# Waiting CLAMD"; $s[] = "\t\t\techo -n \"Waiting CLAMD \""; $s[] = "\t\t\techo 'Waiting CLAMD' > " . HVDEF_HAVP_STATUS_FILE; $s[] = "\t\t\twhile [ \"`{$clamdchk}`\" != \"`/bin/cat {$clampid_dir}/clamd.pid`\" ];do"; $s[] = "\t\t\t\techo -n '.'"; $s[] = "\t\t\t\tsleep 1"; $s[] = "\t\t\tdone"; $s[] = "\t\t\techo"; $s[] = "\t\tfi"; $s[] = ""; } $s[] = "\t\t/usr/local/sbin/havp -c " . HVDEF_HAVP_CONFIG . " 2>/dev/null"; $s[] = "\t\twait"; $s[] = "\tfi"; $s[] = "\t# Status"; $s[] = "\tif [ -z \"`{$havpchk}`\" ];then"; $s[] = "\t\techo 'Stopped' > " . HVDEF_HAVP_STATUS_FILE; $s[] = "\telse"; $s[] = "\t\techo 'Started' > " . HVDEF_HAVP_STATUS_FILE; $s[] = "\tfi"; $s[] = ""; $rc['start'] = implode("\n", $s); unset($s); $s[] = "# stop"; $s[] = "\t killall havp 2>/dev/null"; $s[] = "\t sleep 2"; $s[] = "\t killall -9 havp 2>/dev/null"; $s[] = "\t wait"; $s[] = "\t echo 'Stopped' > " . HVDEF_HAVP_STATUS_FILE; $s[] = ""; $rc['stop'] = implode("\n", $s); unset($s); # we don't use start if package disabled if ($havp_config[F_ENABLE] !== 'true') { $rc['start'] = "\t echo 'Disabled' > " . HVDEF_HAVP_STATUS_FILE; } write_rcfile($rc); } # ------------------------------------------------------------------------------ # clamd service startup script function hv_clamd_startup_script() { global $havp_config; $pid = HVDEF_CLAM_PID; $clamdchk = "ps auxw | grep \"[c]lamd -c\"|awk '{print $2}'"; # rc script $rc = array(); $rc['file'] = basename(HVDEF_CLAM_STARTUP_SCRIPT); $s[] = "\t\techo 'Starting..' > " . HVDEF_CLAM_STATUS_FILE; $s[] = "# start"; $s[] = "\tif [ -z \"`{$clamdchk}`\" ];then"; $s[] = "\t\t/usr/local/sbin/clamd -c " . HVDEF_CLAM_CONFIG . " 2>/dev/null"; $s[] = "\t\twait"; $s[] = "\tfi"; $s[] = "\techo 'Started' > " . HVDEF_CLAM_STATUS_FILE; $s[] = ""; $rc['start'] = implode("\n", $s); unset($s); $s[] = "#stop"; $s[] = "\t killall clamd 2>/dev/null"; $s[] = "\t sleep 2"; $s[] = "\t killall -9 clamd 2>/dev/null"; $s[] = "\t wait"; $s[] = "\t\techo 'Stopped' > " . HVDEF_CLAM_STATUS_FILE; $s[] = ""; $rc['stop'] = implode("\n", $s); unset($s); write_rcfile($rc); } # ------------------------------------------------------------------------------ # HAVP filter resync script function havp_filter_resync_script() { return <<<EOD #!/usr/local/bin/php -f <?php # havp filter hook if (file_exists('/usr/local/pkg/havp.inc')) { require_once('havp.inc'); havp_filter_update_3(); } ?> EOD; } # ============================================================================== # RAM Disk # ============================================================================== function mountRAMdisk($free_and_mount = true) { global $havp_config; $mnt_point = HVDEF_RAMTEMP_DIR; $mnt_flag_file = "$mnt_point/.mnt"; # RAM Disk disabled if (HV_USE_TMPRAMDISK !== 'true') { umountRAMDisk(); return; } # RAM Disk on VM disabled if ((HV_VM_TMPRAMDISK !== 'true') && VMWare_detect()) { umountRAMDisk(); log_error("havp: RAMDisk on VM disabled."); return; } # free RAMDisk only if ($free_and_mount !== true) { umountRAMDisk(); return; } # =-= Temp RAMDisk =-= # note: use 1/4 of system memory capacity $ramdisk_capacity = get_memory(); $ramdisk_capacity = intval(intval($ramdisk_capacity[0]) / 4); # [Mb] # RAMDisk already exists? if (file_exists("/dev/md10")) return; # umount old RAMDisk # umountRAMDisk(); # create and mount a swap backed file system on /var/tmp/havp by /dev/md10: # SWAP # mwexec("mdconfig -a -t swap -s {$ramdisk_capacity}M -u 10"); # mwexec("newfs -U /dev/md10"); # mwexec("mount /dev/md10 $mnt_point"); # RAM - more quickly, used physical RAM mwexec("/sbin/mdmfs -s {$ramdisk_capacity}M md10 {$mnt_point}"); mwexec("chmod 1777 {$mnt_point}"); # create flag file file_put_contents($mnt_flag_file, "{$ramdisk_capacity}"); # syslog if (HV_DEBUG === 'true') log_error("havp: Create RAMDisk {$ramdisk_capacity}Mb."); } # ------------------------------------------------------------------------------ function umountRAMDisk() { global $havp_config; # detach and free all resources used by /dev/md10: mwexec("umount -f " . HVDEF_RAMTEMP_DIR); mwexec("mdconfig -d -u 10"); } # ============================================================================== # Utilites # ============================================================================== function VMWare_detect() { global $g; $fc = ''; if (file_exists("{$g['varlog_path']}/dmesg.boot") !== false) $fc = file_get_contents("{$g['varlog_path']}/dmesg.boot"); return (strpos($fc, "<VMware Virtual") !== false); } function pfsense_version_() { $ver = '1'; if (file_exists('/etc/version')) { $s = file_get_contents('/etc/version'); $s = str_replace('-', '.', $s); # '2.0-Beta' > '2.0.Beta' $s = explode(".", $s); $ver = $s ? $s[0] : '1'; } return intval($ver); } # ------------------------------------------------------------------------------ function start_antivirus_scanner($filename) { $param = array(); # $param[] = "-v"; # verbose if (HV_DEBUG === 'true') $param[] = "--debug"; # debug option else $param[] = "--quiet"; # output only errors $param[] = "--stdout"; # Write to stdout instead of stderr # $param[] = "--no-summary"; # Disable summary at end of scanning $param[] = "-i"; # Only print infected files $param[] = "--tempdir=" . HVDEF_TEMP_DIR; # Create temporary files in DIRECTORY # $param[] = "-d FILE/DIR"; # Load virus database from FILE or load all .cvd and .db[2] files from DIR $param[] = "-l " . HVDEF_CLAMSCAN_LOG; # Save scan report to FILE $param[] = "-r"; # Scan subdirectories recursively $param[] = "--remove"; # Remove infected files. Be careful! $param[] = "--detect-broken"; # Try to detect broken executable files $param[] = "--max-filesize=10000000"; # Files larger than this will be skipped and assumed clean $param[] = "--max-scansize=5000000"; # The maximum amount of data to scan for each container file (*) $param[] = "--max-files=10000"; # The maximum number of files to scan for each container file (*) $param[] = "--max-recursion=255"; # Maximum archive recursion level for container file (*) $param[] = "--max-dir-recursion=255"; # Maximum directory recursion level $param = implode(" ", $param); if (HV_USE_CLAMD === 'true') $param = "clamdscan $param $filename"; # use clamd daemon (more quickly) else $param = "clamscan $param $filename"; # debug clamscan cmd if (HV_DEBUG === 'true') file_put_contents("/tmp/clamscan.cmd", $param); if (file_exists($filename)) { log_error("Antivirus: Starting file '$filename' scanner. Log file is '" . HVDEF_CLAMSCAN_LOG . "'. Wait 5-10 minutes."); # put to log scanning file $cont="Starting scan file {$filename}\n"; file_put_contents(HVDEF_CLAMSCAN_LOG, $cont); mwexec_bg("$param"); exec("date +\"%Y.%m.%d %H:%M:%S Starting scan file '$filename'.\" > " . HVDEF_CLAMSCAN_LOG); } else log_error("Antivirus: Can't starting file scanner. File '$filename' not exists."); } # ------------------------------------------------------------------------------ # HTML # ------------------------------------------------------------------------------ function havp_fscan_html() { global $g; $clamscan_log = HVDEF_CLAMSCAN_LOG; return <<<EOD <hr> <span onClick="document.getElementById('scanfilepath').value = '/var/squid';" style="cursor: pointer;"> <img src='./themes/{$g['theme']}/images/icons/icon_pass.gif' title='Click here'> <font size='-1'><u> Squid cache path (scan your squid cache now).</u></font> </img> </span> <br> <span onClick="document.getElementById('scanfilepath').value = '/var/db';" style="cursor: pointer;"> <img src='./themes/{$g['theme']}/images/icons/icon_pass.gif' title='Click here'> <font size='-1'><u> Common DB path.</u></font> </img> </span> <br> <span onClick="document.getElementById('scanfilepath').value = '/tmp';" style="cursor: pointer;"> <img src='./themes/{$g['theme']}/images/icons/icon_pass.gif' title='Click here'> <font size='-1'><u> Temp path.</u></font> </img> </span> <hr> <input name='submit' type='submit' value='Start_scan'><br> Press button for start antivirus scanner now. After 5-10 minutes look log file '{$clamscan_log}'.<br> (Diagnostics: Execute Shell command: <b>'cat {$clamscan_log}'</b>) EOD; } /* Future - in next time */ # blacklist, dns, down, error, invalid, maxsize, request, scanner, virus function havp_html_notification_page($type, $title, $notify, $message) { $class = ''; switch($type) { case 'blacklist': $class = 'notify-warn'; break; case 'dns': $class = 'notify-standart'; break; case 'down': $class = 'notify-standart'; break; case 'error': $class = 'notify-standart'; break; case 'invalid': $class = 'notify-standart'; break; case 'maxsize': $class = 'notify-warn'; break; case 'request': $class = 'notify-standart'; break; case 'scanner': $class = 'notify-warn'; break; case 'virus': $class = 'notify-danger'; break; } return <<<EOD <html> <head> <meta http-equiv="content-type" content="text/html; "> <style type="text/css"> <!-- .havp_scheme {width: 100%; border: 0px; color: black; vertical-align: bottom; text-align: center; font-family: arial,helvetica; padding-bottom: 3%} .havp_scheme.header {font-size: 10pt; font-weight: bold; background-color: #FFFFFF; color: #446699;} .havp_scheme.notify {font-size: 14pt; font-weight: bold; background-color: #E9E9E9; color: #446699;} .havp_scheme.notify-standart {font-size: 14pt; font-weight: bold; background-color: #E9E9E9; color: #446699;} .havp_scheme.notify-strong {font-size: 14pt; font-weight: bold; background-color: #E9E9E9; color: #446699;} .havp_scheme.notify-danger {font-size: 14pt; font-weight: bold; background-color: #FFEFEF; color: #FF6666;} .havp_scheme.notify-warn {font-size: 14pt; font-weight: bold; background-color: #FFEFDF; color: #FF9966;} .havp_scheme.message {font-size: 10pt; background-color: #FFFFFF; color: #000066;} .havp_scheme.footer {font-size: 10pt; background-color: #DDDDDD; color: #000066;} --> </style <title>HTTP AntiVirus Proxy: $type</title> </head> <body> <table class='havp_scheme' cellpadding='2' cellspacing='0' align='center'> <tr class='header'><td>$title<br>HTTP AntiVirus Proxy: $type</td></tr> <tr class='$class'><td>$notify</td></tr> <tr class='message'><td>$message<br><!--message--></td></tr> <tr class='footer' ><td>Powered by havp.</td></tr> </table> </body> </html> EOD; } # ============================================================================== # Status, widgets # ============================================================================== function havp_get_scan_log() { $s = ''; $clamscanlog = "/var/log/clamscan.log"; if (file_exists($clamscanlog)) { $s = file_get_contents("/var/log/clamscan.log"); } if (empty($s)) $s = "Not found."; return $s; } function havp_get_filescanlist() { $slist = array(); $slist[0]['descr'] = 'Squid cache path (scan you squid cache now).'; $slist[0]['path'] = '/var/squid'; $slist[1]['descr'] = 'Common DB path.'; $slist[1]['path'] = '/var/db'; $slist[2]['descr'] = 'Temp path.'; $slist[2]['path'] = '/tmp'; return $slist; } function havp_get_av_viruslog() { $s = array(); if (file_exists(HVDEF_HAVP_ACCESSLOG)) { $log = file_get_contents(HVDEF_HAVP_ACCESSLOG); $log = explode("\n", $log); $count = 0; foreach($log as $ln) { if (substr_count(strtolower($ln), "virus clamd:")) $s[] = $ln; } } return $s; } function havp_get_av_statistic() { $s = "Unknown."; if (file_exists(HVDEF_HAVP_ACCESSLOG)) { $log = file_get_contents(HVDEF_HAVP_ACCESSLOG); $count = substr_count(strtolower($log), "virus clamd:"); $s = "Found $count viruses (total)."; } return $s; } # ------------------------------------------------------------------------------ # Fix # ------------------------------------------------------------------------------ function havp_fix() { # remove old named scripts # now must exists 'havp.sh'/'clamd' mwexec(HVDEF_SCRIPT_DIR . "/havp"); mwexec(HVDEF_SCRIPT_DIR . "/clamd.sh"); } ?>