From 71a3b727a3121c2bd081fe1f657f9dbe563e7064 Mon Sep 17 00:00:00 2001 From: robiscool Date: Wed, 30 Sep 2009 03:25:18 -0700 Subject: Push snort 1.6 to stable, fix some startup issues --- config/snort-dev/snort.inc | 14 +- config/snort/bin/oinkmaster_contrib/oinkmaster.pl | 2754 ++++++++++++++++++++ config/snort/pfsense_rules/local.rules | 7 + .../snort/pfsense_rules/pfsense_rules.tar.gz.md5 | 2 +- .../snort/pfsense_rules/rules/pfsense-voip.rules | 11 +- config/snort/snort.inc | 425 ++- config/snort/snort.xml | 142 +- config/snort/snort_advanced.xml | 12 +- config/snort/snort_check_for_rule_updates.php | 657 ++++- config/snort/snort_download_rules.php | 345 ++- config/snort/snort_dynamic_ip_reload.php | 5 +- config/snort/snort_rules.php | 63 +- pkg_config.7.xml | 7 +- 13 files changed, 4156 insertions(+), 288 deletions(-) create mode 100644 config/snort/bin/oinkmaster_contrib/oinkmaster.pl create mode 100644 config/snort/pfsense_rules/local.rules diff --git a/config/snort-dev/snort.inc b/config/snort-dev/snort.inc index 3b13ba14..ebcab85c 100644 --- a/config/snort-dev/snort.inc +++ b/config/snort-dev/snort.inc @@ -88,8 +88,6 @@ function sync_package_snort() exec("/bin/cp /usr/local/etc/snort/threshold.conf-sample /usr/local/etc/snort/threshold.conf"); exec("/bin/cp /usr/local/etc/snort/unicode.map-sample /usr/local/etc/snort/unicode.map"); exec("/bin/rm -f /usr/local/etc/rc.d/snort"); - exec("/bin/rm /tmp/snort_download_halt.pid"); - exec("/bin/rm /tmp/snort.sh.pid"); $first = 0; $snortInterfaces = array(); /* -gtm */ @@ -147,34 +145,32 @@ function sync_package_snort() /* TODO; get snort to start under nologin shell */ foreach($snortInterfaces as $snortIf) { - $start .= "sleep 8\n"; + $start .= "sleep 4\n"; $start .= "snort -c /usr/local/etc/snort/snort.conf -l /var/log/snort -D -i {$snortIf} -q\n"; /* define snortbarnyardlog_chk */ $snortbarnyardlog_info_chk = $config['installedpackages']['snortadvanced']['config'][0]['snortbarnyardlog']; if ($snortbarnyardlog_info_chk == on) $start .= "\nsleep 4;barnyard2 -c /usr/local/etc/barnyard2.conf -d /var/log/snort -f snort.u2 -w /usr/local/etc/snort/barnyard2.waldo -D -q\n"; } - $check_if_snort_runs = "\nif [ \"`pgrep -x snort`\" != \"\" ] ; then\n\tlogger -p daemon.info -i -t SnortStartup \"Snort already running...\"\n/usr/local/bin/php -f /usr/local/pkg/pf/snort_dynamic_ip_reload.php\n\texit 1\nfi\n\n"; + $check_if_snort_runs = "\n\tif [ \"`ls -A /usr/local/etc/snort/rules`\" ] ; then\n\techo \"rules exist\"\n\telse\n\techo \"rules DONT exist\"\n\texit 2\n\tfi \n\n\tif [ \"`pgrep -x snort`\" = \"\" ] ; then\n\t/bin/rm /tmp/snort.sh.pid\n\tfi \n\n\tif [ \"`pgrep -x snort`\" != \"\" ] ; then\n\tlogger -p daemon.info -i -t SnortStartup \"Snort already running...\"\n\t/usr/local/bin/php -f /usr/local/pkg/pf/snort_dynamic_ip_reload.php\n\texit 1\n\tfi\n\n"; $if_snort_pid = "\nif ls /tmp/snort.sh.pid > /dev/null\nthen\n echo \"snort.sh is running\"\n exit 0\nelse\n echo \"snort.sh is not running\"\nfi\n"; $echo_snort_sh_pid = "\necho \"snort.sh run\" > /tmp/snort.sh.pid\n"; $echo_snort_sh_startup_log = "\necho \"snort.sh run\" >> /tmp/snort.sh_startup.log\n"; $del_old_pids = "\nrm -f /var/run/snort_*\n"; $sample_before = "BEFORE_MEM=`top | grep Wired | awk '{print \$12}'`\n"; - $sample_after = "AFTER_MEM=`top | grep Wired | awk '{print \$12}'`\n"; + $sample_after = "\n\tAFTER_MEM=`top | grep Wired | awk '{print \$12}'`\n"; if ($snort_performance == "ac-bnfa") $sleep_before_final = "\necho \"Sleeping before final memory sampling...\"\nWAITSECURE=60\n"; else $sleep_before_final = "\necho \"Sleeping before final memory sampling...\"\nWAITSECURE=300\n"; $sleep_before_final .= "while [ \"\$MYSNORTLOG\" = \"\" -a \$WAITSECURE -gt 0 ] ; do\n\tsleep 2\n\tMYSNORTLOG=`/usr/sbin/clog /var/log/system.log | grep snort | tail | grep 'Snort initialization completed successfully'`\n\tWAITSECURE=`expr \$WAITSECURE - 1`\ndone\n"; $total_used_after = "TOTAL_USAGE=`top | grep snort | grep -v grep | awk '{ print \$6 }'`\n"; - $echo_usage = "\nif [ \$WAITSECURE -eq 0 -a \"\$MYSNORTLOG\" = \"\" ] ; then\n\techo \"Snort has not finished starting, please check log for possible errors.\"\n"; - $echo_usage .= "else\n\t" . $sample_after . "\t" . $total_used_after . "\techo \"Ram free BEFORE starting Snort: \$BEFORE_MEM -- Ram free AFTER starting Snort: \$AFTER_MEM -- Mode " . $snort_performance . " -- Snort memory usage: \$TOTAL_USAGE\" | logger -p daemon.info -i -t SnortStartup\nfi\n"; - $rm_snort_sh_pid = "\nrm /tmp/snort.sh.pid\n"; + $echo_usage .= $sample_after . "\t" . $total_used_after . "\techo \"Ram free BEFORE starting Snort: \$BEFORE_MEM -- Ram free AFTER starting Snort: \$AFTER_MEM -- Mode " . $snort_performance . " -- Snort memory usage: \$TOTAL_USAGE\" | logger -p daemon.info -i -t SnortStartup\n\n"; /* write out rc.d start/stop file */ write_rcfile(array( "file" => "snort.sh", - "start" => "{$check_if_snort_runs}{$if_snort_pid}{$echo_snort_sh_pid}{$echo_snort_sh_startup_log}{$del_old_pids}{$sample_before}{$start}{$sleep_before_final}{$echo_usage}{$rm_snort_sh_pid}", + "start" => "{$check_if_snort_runs}{$if_snort_pid}{$echo_snort_sh_pid}{$echo_snort_sh_startup_log}{$del_old_pids}{$sample_before}{$start}{$sleep_before_final}{$echo_usage}", "stop" => "/usr/bin/killall snort; killall barnyard2" ) ); diff --git a/config/snort/bin/oinkmaster_contrib/oinkmaster.pl b/config/snort/bin/oinkmaster_contrib/oinkmaster.pl new file mode 100644 index 00000000..f9c4d215 --- /dev/null +++ b/config/snort/bin/oinkmaster_contrib/oinkmaster.pl @@ -0,0 +1,2754 @@ +#!/usr/bin/perl -w + +# $Id: oinkmaster.pl,v 1.406 2006/02/10 13:02:44 andreas_o Exp $ # + +# Copyright (c) 2001-2006 Andreas Östling +# 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. +# +# 3. Neither the name of the author nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +# CONTRIBUTORS "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 COPYRIGHT OWNER OR +# CONTRIBUTORS 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. + + +use 5.006001; + +use strict; +use File::Basename; +use File::Copy; +use File::Path; +use File::Spec; +use Getopt::Long; +use File::Temp qw(tempdir); + +sub show_usage(); +sub parse_cmdline($); +sub read_config($ $); +sub sanity_check(); +sub download_file($ $); +sub unpack_rules_archive($ $ $); +sub join_tmp_rules_dirs($ $ @); +sub process_rules($ $ $ $ $ $); +sub process_rule($ $ $ $ $ $ $ $); +sub setup_rules_hash($ $); +sub get_first_only($ $ $); +sub print_changes($ $); +sub print_changetype($ $ $ $); +sub print_summary_change($ $); +sub make_backup($ $); +sub get_changes($ $ $); +sub update_rules($ @); +sub copy_rules($ $); +sub is_in_path($); +sub get_next_entry($ $ $ $ $ $); +sub get_new_vars($ $ $ $); +sub add_new_vars($ $); +sub write_new_vars($ $); +sub msdos_to_cygwin_path($); +sub parse_mod_expr($ $ $ $); +sub untaint_path($); +sub approve_changes(); +sub parse_singleline_rule($ $ $); +sub join_multilines($); +sub minimize_diff($ $); +sub catch_sigint(); +sub clean_exit($); + + +my $VERSION = 'Oinkmaster v2.0, Copyright (C) 2001-2006 '. + 'Andreas Östling '; +my $OUTFILE = 'snortrules.tar.gz'; +my $RULES_DIR = 'rules'; + +my $PRINT_NEW = 1; +my $PRINT_OLD = 2; +my $PRINT_BOTH = 3; + +my %config = ( + careful => 0, + check_removed => 0, + config_test_mode => 0, + enable_all => 0, + interactive => 0, + make_backup => 0, + minimize_diff => 0, + min_files => 1, + min_rules => 1, + quiet => 0, + summary_output => 0, + super_quiet => 0, + update_vars => 0, + use_external_bins => 1, + verbose => 0, + use_path_checks => 1, + rule_actions => "alert|drop|log|pass|reject|sdrop|activate|dynamic", + tmp_basedir => $ENV{TMP} || $ENV{TMPDIR} || $ENV{TEMPDIR} || '/tmp', +); + + +# Regexp to match the start of a multi-line rule. +# %ACTIONS% will be replaced with content of $config{actions} later. +# sid and msg will then be looked for in parse_singleline_rule(). +my $MULTILINE_RULE_REGEXP = '^\s*#*\s*(?:%ACTIONS%)'. + '\s.*\\\\\s*\n$'; # '; + +# Regexp to match a single-line rule. +# sid and msg will then be looked for in parse_singleline_rule(). +my $SINGLELINE_RULE_REGEXP = '^\s*#*\s*(?:%ACTIONS%)'. + '\s.+;\s*\)\s*$'; # '; + +# Match var line where var name goes into $1. +my $VAR_REGEXP = '^\s*var\s+(\S+)\s+(\S+)'; + +# Allowed characters in misc paths/filenames, including the ones in the tarball. +my $OK_PATH_CHARS = 'a-zA-Z\d\ _\(\)\[\]\.\-+:\\\/~@,='; + +# Default locations for configuration file. +my @DEFAULT_CONFIG_FILES = qw( + /etc/oinkmaster.conf + /usr/local/etc/oinkmaster.conf +); + +my @DEFAULT_DIST_VAR_FILES = qw( + snort.conf +); + +my (%loaded, $tmpdir); + + + +#### MAIN #### + +# No buffering. +select(STDERR); +$| = 1; +select(STDOUT); +$| = 1; + + +my $start_date = scalar(localtime); + +# Assume the required Perl modules are available if we're on Windows. +$config{use_external_bins} = 0 if ($^O eq "MSWin32"); + +# Parse command line arguments and add at least %config{output_dir}. +parse_cmdline(\%config); + +# If no config was specified on command line, look for one in default locations. +if ($#{$config{config_files}} == -1) { + foreach my $config (@DEFAULT_CONFIG_FILES) { + if (-e "$config") { + push(@{${config{config_files}}}, $config); + last; + } + } +} + +# If no dist var file was specified on command line, set to default file(s). +if ($#{$config{dist_var_files}} == -1) { + foreach my $var_file (@DEFAULT_DIST_VAR_FILES) { + push(@{${config{dist_var_files}}}, $var_file); + } +} + +# If config is still not defined, we can't continue. +if ($#{$config{config_files}} == -1) { + clean_exit("configuration file not found in default locations\n". + "(@DEFAULT_CONFIG_FILES)\n". + "Put it there or use the \"-C \" argument."); +} + +read_config($_, \%config) for @{$config{config_files}}; + +# Now substitute "%ACTIONS%" with $config{rule_actions}, which may have +# been modified after reading the config file. +$SINGLELINE_RULE_REGEXP =~ s/%ACTIONS%/$config{rule_actions}/; +$MULTILINE_RULE_REGEXP =~ s/%ACTIONS%/$config{rule_actions}/; + +# If we're told not to use external binaries, load the Perl modules now. +unless ($config{use_external_bins}) { + print STDERR "Loading Perl modules.\n" if ($config{verbose}); + + eval { + require IO::Zlib; + require Archive::Tar; + require LWP::UserAgent; + }; + + clean_exit("failed to load required Perl modules:\n\n$@\n". + "Install them or set use_external_bins to 1 ". + "if you want to use external binaries instead.") + if ($@); +} + + +# Do some basic sanity checking and exit if something fails. +# A new PATH will be set. +sanity_check(); + +$SIG{INT} = \&catch_sigint; + +# Create temporary dir. +$tmpdir = tempdir("oinkmaster.XXXXXXXXXX", DIR => File::Spec->rel2abs($config{tmp_basedir})) + or clean_exit("could not create temporary directory in $config{tmp_basedir}: $!"); + +# If we're in config test mode and have come this far, we're done. +if ($config{config_test_mode}) { + print "No fatal errors in configuration.\n"; + clean_exit(""); +} + +umask($config{umask}) if exists($config{umask}); + +# Download and unpack all the rules archives into separate tmp dirs. +my @url_tmpdirs; +foreach my $url (@{$config{url}}) { + my $url_tmpdir = tempdir("url.XXXXXXXXXX", DIR => $tmpdir) + or clean_exit("could not create temporary directory in $tmpdir: $!"); + push(@url_tmpdirs, "$url_tmpdir/$RULES_DIR"); + if ($url =~ /^dir:\/\/(.+)/) { + mkdir("$url_tmpdir/$RULES_DIR") + or clean_exit("Could not create $url_tmpdir/$RULES_DIR"); + copy_rules($1, "$url_tmpdir/$RULES_DIR"); + } else { + download_file($url, "$url_tmpdir/$OUTFILE"); + unpack_rules_archive("$url", "$url_tmpdir/$OUTFILE", $RULES_DIR); + } +} + +# Copy all rules files from the tmp dirs into $RULES_DIR in the tmp directory. +# File matching 'skipfile' a directive will not be copied. +# Filenames (with full path) will be stored as %new_files{filename}. +# Will exit in case of duplicate filenames. +my $num_files = join_tmp_rules_dirs("$tmpdir/$RULES_DIR", \my %new_files, @url_tmpdirs); + +# Make sure we have at least the minimum number of files. +clean_exit("not enough rules files in downloaded rules archive(s).\n". + "Number of rules files is $num_files but minimum is set to $config{min_files}.") + if ($num_files < $config{min_files}); + +# This is to read in possible 'localsid' rules. +my %rh_tmp = setup_rules_hash(\%new_files, $config{output_dir}); + +# Disable/modify/clean downloaded rules. +my $num_rules = process_rules(\@{$config{sid_modify_list}}, + \%{$config{sid_disable_list}}, + \%{$config{sid_enable_list}}, + \%{$config{sid_local_list}}, + \%rh_tmp, + \%new_files); + +# Make sure we have at least the minimum number of rules. +clean_exit("not enough rules in downloaded archive(s).\n". + "Number of rules is $num_rules but minimum is set to $config{min_rules}.") + if ($num_rules < $config{min_rules}); + +# Setup a hash containing the content of all processed rules files. +my %rh = setup_rules_hash(\%new_files, $config{output_dir}); + +# Compare the new rules to the old ones. +my %changes = get_changes(\%rh, \%new_files, $RULES_DIR); + +# Check for variables that exist in dist snort.conf(s) but not in local snort.conf. +get_new_vars(\%changes, \@{$config{dist_var_files}}, $config{varfile}, \@url_tmpdirs) + if ($config{update_vars}); + + +# Find out if something had changed. +my $something_changed = 0; + +$something_changed = 1 + if (keys(%{$changes{modified_files}}) || + keys(%{$changes{added_files}}) || + keys(%{$changes{removed_files}}) || + $#{$changes{new_vars}} > -1); + + +# Update files listed in %changes{modified_files} (copy the new files +# from the temporary directory into our output directory) and add new +# variables to the local snort.conf if requested, unless we're running in +# careful mode. Create backup first if running with -b. +my $printed = 0; +if ($something_changed) { + if ($config{careful}) { + print STDERR "Skipping backup since we are running in careful mode.\n" + if ($config{make_backup} && (!$config{quiet})); + } else { + if ($config{interactive}) { + print_changes(\%changes, \%rh); + $printed = 1; + } + + if (!$config{interactive} || ($config{interactive} && approve_changes)) { + make_backup($config{output_dir}, $config{backup_dir}) + if ($config{make_backup}); + + add_new_vars(\%changes, $config{varfile}) + if ($config{update_vars}); + + update_rules($config{output_dir}, keys(%{$changes{modified_files}})); + } + } +} else { + print STDERR "No files modified - no need to backup old files, skipping.\n" + if ($config{make_backup} && !$config{quiet}); +} + +print "\nOinkmaster is running in careful mode - not updating anything.\n" + if ($something_changed && $config{careful}); + +print_changes(\%changes, \%rh) + if (!$printed && ($something_changed || !$config{quiet})); + + +# Everything worked. Do a clean exit without any error message. +clean_exit(""); + + +# END OF MAIN # + + + +# Show usage information and exit. +sub show_usage() +{ + my $progname = basename($0); + + print STDERR << "RTFM"; + +$VERSION + +Usage: $progname -o [options] + + is where to put the new files. +This should be the directory where you store your Snort rules. + +Options: +-b Backup your old rules into before overwriting them +-c Careful mode (dry run) - check for changes but do not update anything +-C Use this configuration file instead of the default + May be specified multiple times to load multiple files +-e Enable all rules that are disabled by default +-h Show this usage information +-i Interactive mode - you will be asked to approve the changes (if any) +-m Minimize diff when printing result by removing common parts in rules +-q Quiet mode - no output unless changes were found +-Q Super-quiet mode - like -q but even more quiet +-r Check for rules files that exist in the output directory + but not in the downloaded rules archive +-s Leave out details in rules results, just print SID, msg and filename +-S Look for new variables in this file in the downloaded archive instead + of the default (@DEFAULT_DIST_VAR_FILES). Used in conjunction with -U. + May be specified multiple times to search multiple files. +-T Config test - just check configuration file(s) for errors/warnings +-u Download from this URL instead of URL(s) in the configuration file + (http|https|ftp|file|scp:// ... .tar.gz|.gz, or dir://) + May be specified multiple times to grab multiple rules archives +-U Merge new variables from downloaded snort.conf(s) into +-v Verbose mode (debug) +-V Show version and exit + +RTFM + exit; +} + + + +# Parse the command line arguments and exit if we don't like them. +sub parse_cmdline($) +{ + my $cfg_ref = shift; + + Getopt::Long::Configure("bundling"); + + my $cmdline_ok = GetOptions( + "b=s" => \$$cfg_ref{backup_dir}, + "c" => \$$cfg_ref{careful}, + "C=s" => \@{$$cfg_ref{config_files}}, + "e" => \$$cfg_ref{enable_all}, + "h" => \&show_usage, + "i" => \$$cfg_ref{interactive}, + "m" => \$$cfg_ref{minimize_diff}, + "o=s" => \$$cfg_ref{output_dir}, + "q" => \$$cfg_ref{quiet}, + "Q" => \$$cfg_ref{super_quiet}, + "r" => \$$cfg_ref{check_removed}, + "s" => \$$cfg_ref{summary_output}, + "S=s" => \@{$$cfg_ref{dist_var_files}}, + "T" => \$$cfg_ref{config_test_mode}, + "u=s" => \@{$$cfg_ref{url}}, + "U=s" => \$$cfg_ref{varfile}, + "v" => \$$cfg_ref{verbose}, + "V" => sub { + print "$VERSION\n"; + exit(0); + } + ); + + + show_usage unless ($cmdline_ok && $#ARGV == -1); + + $$cfg_ref{quiet} = 1 if ($$cfg_ref{super_quiet}); + $$cfg_ref{update_vars} = 1 if ($$cfg_ref{varfile}); + + if ($$cfg_ref{backup_dir}) { + $$cfg_ref{backup_dir} = File::Spec->canonpath($$cfg_ref{backup_dir}); + $$cfg_ref{make_backup} = 1; + } + + # Cannot specify dist var files without specifying var target file. + if (@{$$cfg_ref{dist_var_files}} && !$$cfg_ref{update_vars}) { + clean_exit("You can not specify distribution variable file(s) without ". + "also specifying local file to merge into"); + } + + # -o is the only required option in normal usage. + if ($$cfg_ref{output_dir}) { + $$cfg_ref{output_dir} = File::Spec->canonpath($$cfg_ref{output_dir}); + } else { + warn("Error: no output directory specified.\n"); + show_usage(); + } + + # Mark that url was set on command line (so we don't override it later). + $$cfg_ref{cmdline_url} = 1 if ($#{$config{url}} > -1); +} + + + +# Read in stuff from the configuration file. +sub read_config($ $) +{ + my $config_file = shift; + my $cfg_ref = shift; + my $linenum = 0; + my $multi; + my %templates; + + $config_file = File::Spec->canonpath(File::Spec->rel2abs($config_file)); + + clean_exit("configuration file \"$config_file\" does not exist.\n") + unless (-e "$config_file"); + + clean_exit("\"$config_file\" is not a file.\n") + unless (-f "$config_file"); + + print STDERR "Loading $config_file\n" + unless ($config{quiet}); + + # Avoid loading the same file multiple times to avoid infinite recursion etc. + if ($^O eq "MSWin32") { + clean_exit("attempt to load \"$config_file\" twice.") + if ($loaded{$config_file}++); + } else { + my ($dev, $ino) = (stat($config_file))[0,1] + or clean_exit("unable to stat $config_file: $!"); + clean_exit("attempt to load \"$config_file\" twice.") + if ($loaded{$dev, $ino}++); + } + + open(CONF, "<", "$config_file") + or clean_exit("could not open configuration file \"$config_file\": $!"); + my @conf = ; + close(CONF); + + LINE:while ($_ = shift(@conf)) { + $linenum++; + + unless ($multi) { + s/^\s*//; + s/^#.*//; + } + + # Multi-line start/continuation. + if (/\\\s*\n$/) { + s/\\\s*\n$//; + s/^\s*#.*//; + + # Be strict about removing #comments in modifysid/define_template statements, as + # they may contain other '#' chars. + if (defined($multi) && ($multi =~ /^modifysid/i || $multi =~ /^define_template/i)) { + s/#.*// if (/^\s*\d+[,\s\d]+#/); + } else { + s/\s*\#.*// unless (/^modifysid/i || /^define_template/i); + } + + $multi .= $_; + next LINE; + } + + # Last line of multi-line directive. + if (defined($multi)) { + $multi .= $_; + $_ = $multi; + undef($multi); + } + + # Remove traling whitespaces (*after* a possible multi-line is rebuilt). + s/\s*$//; + + # Remove comments unless it's a modifysid/define_template line + # (the "#" may be part of the modifysid expression). + s/\s*\#.*// unless (/^modifysid/i || /^define_template/i); + + # Skip blank lines. + next unless (/\S/); + + # Use a template and make $_ a "modifysid" line. + if (/^use_template\s+(\S+)\s+(\S+[^"]*)\s*(".*")*(?:#.*)*/i) { + my ($template_name, $sid, $args) = ($1, $2, $3); + + if (exists($templates{$template_name})) { + my $template = $templates{$template_name}; # so we don't substitute %ARGx% globally + + # Evaluate each "%ARGx%" in the template to the corresponding value. + if (defined($args)) { + my @args = split(/"\s+"/, $args); + foreach my $i (1 .. @args) { + $args[$i - 1] =~ s/^"//; + $args[$i - 1] =~ s/"$//; + $template =~ s/%ARG$i%/$args[$i - 1]/g; + } + } + + # There should be no %ARGx% stuff left now. + if ($template =~ /%ARG\d%/) { + warn("WARNING: too few arguments for template \"$template_name\"\n"); + $_ = "error"; # so it will be reported as an invalid line later + } + + unless ($_ eq "error") { + $_ = "modifysid $sid $template\n"; + print STDERR "Template \"$template_name\" expanded to: $_" + if ($config{verbose}); + } + + } else { + warn("WARNING: template \"$template_name\" has not been defined\n"); + } + } + + # new template definition. + if (/^define_template\s+(\S+)\s+(".+"\s+\|\s+".*")\s*(?:#.*)*$/i) { + my ($template_name, $template) = ($1, $2); + + if (exists($templates{$template_name})) { + warn("WARNING: line $linenum in $config_file: ". + "template \"$template_name\" already defined, keeping old\n"); + } else { + $templates{$template_name} = $template; + } + + # modifysid "substthis" | "withthis" + } elsif (/^modifysids*\s+(\S+.*)\s+"(.+)"\s+\|\s+"(.*)"\s*(?:#.*)*$/i) { + my ($sid_list, $subst, $repl) = ($1, $2, $3); + warn("WARNING: line $linenum in $config_file is invalid, ignoring\n") + unless(parse_mod_expr(\@{$$cfg_ref{sid_modify_list}}, + $sid_list, $subst, $repl)); + + # disablesid + } elsif (/^disablesids*\s+(\d.*)/i) { + my $sid_list = $1; + foreach my $sid (split(/\s*,\s*/, $sid_list)) { + if ($sid =~ /^\d+$/) { + $$cfg_ref{sid_disable_list}{$sid}++; + } else { + warn("WARNING: line $linenum in $config_file: ". + "\"$sid\" is not a valid SID, ignoring\n"); + } + } + + # localsid + } elsif (/^localsids*\s+(\d.*)/i) { + my $sid_list = $1; + foreach my $sid (split(/\s*,\s*/, $sid_list)) { + if ($sid =~ /^\d+$/) { + $$cfg_ref{sid_local_list}{$sid}++; + } else { + warn("WARNING: line $linenum in $config_file: ". + "\"$sid\" is not a valid SID, ignoring\n"); + } + } + + # enablesid + } elsif (/^enablesids*\s+(\d.*)/i) { + my $sid_list = $1; + foreach my $sid (split(/\s*,\s*/, $sid_list)) { + if ($sid =~ /^\d+$/) { + $$cfg_ref{sid_enable_list}{$sid}++; + } else { + warn("WARNING: line $linenum in $config_file: ". + "\"$sid\" is not a valid SID, ignoring\n"); + } + } + + # skipfile + } elsif (/^skipfiles*\s+(.*)/i) { + my $args = $1; + foreach my $file (split(/\s*,\s*/, $args)) { + if ($file =~ /^\S+$/) { + $config{verbose} && print STDERR "Adding file to ignore list: $file.\n"; + $$cfg_ref{file_ignore_list}{$file}++; + } else { + warn("WARNING: line $linenum in $config_file is invalid, ignoring\n"); + } + } + + } elsif (/^url\s*=\s*(.*)/i) { + push(@{$$cfg_ref{url}}, $1) + unless ($$cfg_ref{cmdline_url}); + + } elsif (/^path\s*=\s*(.+)/i) { + $$cfg_ref{path} = $1; + + } elsif (/^update_files\s*=\s*(.+)/i) { + $$cfg_ref{update_files} = $1; + + } elsif (/^rule_actions\s*=\s*(.+)/i) { + $$cfg_ref{rule_actions} = $1; + + } elsif (/^umask\s*=\s*([0-7]{4})$/i) { + $$cfg_ref{umask} = oct($1); + + } elsif (/^min_files\s*=\s*(\d+)/i) { + $$cfg_ref{min_files} = $1; + + } elsif (/^min_rules\s*=\s*(\d+)/i) { + $$cfg_ref{min_rules} = $1; + + } elsif (/^tmpdir\s*=\s*(.+)/i) { + $$cfg_ref{tmp_basedir} = $1; + + } elsif (/^use_external_bins\s*=\s*([01])/i) { + $$cfg_ref{use_external_bins} = $1; + + } elsif (/^scp_key\s*=\s*(.+)/i) { + $$cfg_ref{scp_key} = $1; + + } elsif (/^use_path_checks\s*=\s*([01])/i) { + $$cfg_ref{use_path_checks} = $1; + + } elsif (/^user_agent\s*=\s*(.+)/i) { + $$cfg_ref{user_agent} = $1; + + } elsif (/^include\s+(\S+.*)/i) { + my $include = $1; + read_config($include, $cfg_ref); + } else { + warn("WARNING: line $linenum in $config_file is invalid, ignoring\n"); + } + } +} + + + +# Make a few basic tests to make sure things look ok. +# Will also set a new PATH as defined in the config file. +sub sanity_check() +{ + my @req_params = qw(path update_files); # required parameters in conf + my @req_binaries = qw(gzip tar); # required binaries (unless we use modules) + + # Can't use both quiet mode and verbose mode. + clean_exit("quiet mode and verbose mode at the same time doesn't make sense.") + if ($config{quiet} && $config{verbose}); + + # Can't use multiple output modes. + clean_exit("can't use multiple output modes at the same time.") + if ($config{minimize_diff} && $config{summary_output}); + + # Make sure all required variables are defined in the config file. + foreach my $param (@req_params) { + clean_exit("the required parameter \"$param\" is not defined in the configuration file.") + unless (exists($config{$param})); + } + + # We now know a path was defined in the config, so set it. + # If we're under cygwin and path was specified as msdos style, convert + # it to cygwin style to avoid problems. + if ($^O eq "cygwin" && $config{path} =~ /^[a-zA-Z]:[\/\\]/) { + $ENV{PATH} = ""; + foreach my $path (split(/;/, $config{path})) { + $ENV{PATH} .= "$path:" if (msdos_to_cygwin_path(\$path)); + } + chop($ENV{PATH}); + } else { + $ENV{PATH} = $config{path}; + } + + # Reset environment variables that may cause trouble. + delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; + + # Make sure $config{update_files} is a valid regexp. + eval { + "foo" =~ /$config{update_files}/; + }; + + clean_exit("update_files (\"$config{update_files}\") is not a valid regexp: $@") + if ($@); + + # Make sure $config{rule_actions} is a valid regexp. + eval { + "foo" =~ /$config{rule_actions}/; + }; + + clean_exit("rule_actions (\"$config{rule_actions}\") is not a valid regexp: $@") + if ($@); + + # If a variable file (probably local snort.conf) has been specified, + # it must exist. It must also be writable unless we're in careful mode. + if ($config{update_vars}) { + $config{varfile} = untaint_path($config{varfile}); + + clean_exit("variable file \"$config{varfile}\" does not exist.") + unless (-e "$config{varfile}"); + + clean_exit("variable file \"$config{varfile}\" is not a file.") + unless (-f "$config{varfile}"); + + clean_exit("variable file \"$config{varfile}\" is not writable by you.") + if (!$config{careful} && !-w "$config{varfile}"); + + # Make sure dist var files don't contain [back]slashes + # (probably means user confused it with local var file). + my %dist_var_files; + foreach my $dist_var_file (@{${config{dist_var_files}}}) { + clean_exit("variable file \"$dist_var_file\" specified multiple times") + if (exists($dist_var_files{$dist_var_file})); + $dist_var_files{$dist_var_file} = 1; + clean_exit("variable file \"$dist_var_file\" contains slashes or backslashes ". + "but it must be specified as a filename (without path) ". + "that exists in the downloaded rules, e.g. \"snort.conf\"") + if ($dist_var_file =~ /\// || $dist_var_file =~ /\\/); + } + } + + # Make sure all required binaries can be found, unless + # we're used to use Perl modules instead. + # Wget is only required if url is http[s] or ftp. + if ($config{use_external_bins}) { + foreach my $binary (@req_binaries) { + clean_exit("$binary not found in PATH ($ENV{PATH}).") + unless (is_in_path($binary)); + } + } + + # Make sure $url is defined (either by -u or url=... in the conf). + clean_exit("URL not specified. Specify at least one \"url=\" in the \n". + "Oinkmaster configuration file or use the \"-u \" argument") + if ($#{$config{url}} == -1); + + # Make sure all urls look ok, and untaint them. + my @urls = @{$config{url}}; + $#{$config{url}} = -1; + foreach my $url (@urls) { + clean_exit("incorrect URL: \"$url\"") + unless ($url =~ /^((?:https*|ftp|file|scp):\/\/.+\.(?:tar\.gz|tgz))$/ + || $url =~ /^(dir:\/\/.+)/); + my $ok_url = $1; + + if ($ok_url =~ /^dir:\/\/(.+)/) { + my $dir = untaint_path($1); + clean_exit("\"$dir\" does not exist or is not a directory") + unless (-d $dir); + + # Simple check if the output dir is specified as url (probably a mistake). + if (File::Spec->canonpath(File::Spec->rel2abs($dir)) + eq File::Spec->canonpath(File::Spec->rel2abs($config{output_dir}))) { + clean_exit("Download directory can not be same as output directory"); + } + } + push(@{$config{url}}, $ok_url); + } + + # Wget must be found if url is http[s]:// or ftp://. + if ($config{use_external_bins}) { + clean_exit("wget not found in PATH ($ENV{PATH}).") + if ($config{'url'} =~ /^(https*|ftp):/ && !is_in_path("wget")); + } + + # scp must be found if scp://... + clean_exit("scp not found in PATH ($ENV{PATH}).") + if ($config{'url'} =~ /^scp:/ && !is_in_path("scp")); + + # ssh key must exist if specified and url is scp://... + clean_exit("ssh key \"$config{scp_key}\" does not exist.") + if ($config{'url'} =~ /^scp:/ && exists($config{scp_key}) + && !-e $config{scp_key}); + + # Untaint output directory string. + $config{output_dir} = untaint_path($config{output_dir}); + + # Make sure the output directory exists and is readable. + clean_exit("the output directory \"$config{output_dir}\" doesn't exist ". + "or isn't readable by you.") + if (!-d "$config{output_dir}" || !-x "$config{output_dir}"); + + # Make sure the output directory is writable unless running in careful mode. + clean_exit("the output directory \"$config{output_dir}\" isn't writable by you.") + if (!$config{careful} && !-w "$config{output_dir}"); + + # Make sure we have read permission on all rules files in the output dir, + # and also write permission unless we're in careful mode. + # This is to avoid bailing out in the middle of an execution if a copy + # fails because of permission problem. + opendir(OUTDIR, "$config{output_dir}") + or clean_exit("could not open directory $config{output_dir}: $!"); + + while ($_ = readdir(OUTDIR)) { + next if (/^\.\.?$/ || exists($config{file_ignore_list}{$_})); + + if (/$config{update_files}/) { + unless (-r "$config{output_dir}/$_") { + closedir(OUTDIR); + clean_exit("no read permission on \"$config{output_dir}/$_\"\n". + "Read permission is required on all rules files ". + "inside the output directory.\n") + } + + if (!$config{careful} && !-w "$config{output_dir}/$_") { + closedir(OUTDIR); + clean_exit("no write permission on \"$config{output_dir}/$_\"\n". + "Write permission is required on all rules files ". + "inside the output directory.\n") + } + } + } + + closedir(OUTDIR); + + # Make sure the backup directory exists and is writable if running with -b. + if ($config{make_backup}) { + $config{backup_dir} = untaint_path($config{backup_dir}); + clean_exit("the backup directory \"$config{backup_dir}\" doesn't exist or ". + "isn't writable by you.") + if (!-d "$config{backup_dir}" || !-w "$config{backup_dir}"); + } + + # Convert tmp_basedir to cygwin style if running cygwin and msdos style was specified. + if ($^O eq "cygwin" && $config{tmp_basedir} =~ /^[a-zA-Z]:[\/\\]/) { + msdos_to_cygwin_path(\$config{tmp_basedir}) + or clean_exit("could not convert temporary dir to cygwin style"); + } + + # Make sure temporary directory exists. + clean_exit("the temporary directory \"$config{tmp_basedir}\" does not ". + "exist or isn't writable by you.") + if (!-d "$config{tmp_basedir}" || !-w "$config{tmp_basedir}"); + + # Also untaint it. + $config{tmp_basedir} = untaint_path($config{tmp_basedir}); + + # Make sure stdin and stdout are ttys if we're running in interactive mode. + clean_exit("you can not run in interactive mode when STDIN/STDOUT is not a TTY.") + if ($config{interactive} && !(-t STDIN && -t STDOUT)); +} + + + +# Download the rules archive. +sub download_file($ $) +{ + my $url = shift; + my $localfile = shift; + my $log = "$tmpdir/wget.log"; + my $ret; + + # If there seems to be a password in the url, replace it with "*password*" + # and use new string when printing the url to screen. + my $obfuscated_url = $url; + $obfuscated_url = "$1:*password*\@$2" + if ($obfuscated_url =~ /^(\S+:\/\/.+?):.+?@(.+)/); + + # Ofbuscate oinkcode as well. + $obfuscated_url = "$1*oinkcode*$2" + if ($obfuscated_url =~ /^(\S+:\/\/.+\.cgi\/)[0-9a-z]{32,64}(\/.+)/i); + + my @user_agent_opt; + @user_agent_opt = ("-U", $config{user_agent}) if (exists($config{user_agent})); + + # Use wget if URL starts with "http[s]" or "ftp" and we use external binaries. + if ($config{use_external_bins} && $url =~ /^(?:https*|ftp)/) { + print STDERR "Downloading file from $obfuscated_url... " + unless ($config{quiet}); + + if ($config{verbose}) { + print STDERR "\n"; + my @wget_cmd = ("wget", "-v", "-O", $localfile, $url, @user_agent_opt); + clean_exit("could not download from $obfuscated_url") + if (system(@wget_cmd)); + + } else { + my @wget_cmd = ("wget", "-v", "-o", $log, "-O", $localfile, $url, @user_agent_opt); + if (system(@wget_cmd)) { + my $log_output; + open(LOG, "<", "$log") + or clean_exit("could not open $log for reading: $!"); + # Sanitize oinkcode in wget's log (password is automatically sanitized). + while () { + $_ = "$1*oinkcode*$2" + if (/(\S+:\/\/.+\.cgi\/)[0-9a-z]{32,64}(\/.+)/i); + $log_output .= $_; + } + close(LOG); + clean_exit("could not download from $obfuscated_url. ". + "Output from wget follows:\n\n $log_output"); + } + print STDERR "done.\n" unless ($config{quiet}); + } + + # Use LWP if URL starts with "http[s]" or "ftp" and use_external_bins=0. + } elsif (!$config{use_external_bins} && $url =~ /^(?:https*|ftp)/) { + print STDERR "Downloading file from $obfuscated_url... " + unless ($config{quiet}); + + my %lwp_opt; + $lwp_opt{agent} = $config{user_agent} if (exists($config{user_agent})); + + my $ua = LWP::UserAgent->new(%lwp_opt); + $ua->env_proxy; + my $request = HTTP::Request->new(GET => $url); + my $response = $ua->request($request, $localfile); + + clean_exit("could not download from $obfuscated_url: " . $response->status_line) + unless $response->is_success; + + print "done.\n" unless ($config{quiet}); + + # Grab file from local filesystem if file://... + } elsif ($url =~ /^file/) { + $url =~ s/^file:\/\///; + + clean_exit("the file $url does not exist.") + unless (-e "$url"); + + clean_exit("the file $url is empty.") + unless (-s "$url"); + + print STDERR "Copying file from $url... " + unless ($config{quiet}); + + copy("$url", "$localfile") + or clean_exit("unable to copy $url to $localfile: $!"); + + print STDERR "done.\n" + unless ($config{quiet}); + + # Grab file using scp if scp://... + } elsif ($url =~ /^scp/) { + $url =~ s/^scp:\/\///; + + my @cmd; + push(@cmd, "scp"); + push(@cmd, "-i", "$config{scp_key}") if (exists($config{scp_key})); + push(@cmd, "-q") if ($config{quiet}); + push(@cmd, "-v") if ($config{verbose}); + push(@cmd, "$url", "$localfile"); + + print STDERR "Copying file from $url using scp:\n" + unless ($config{quiet}); + + clean_exit("scp returned error when trying to copy $url") + if (system(@cmd)); + + # Unknown download method. + } else { + clean_exit("unknown or unsupported download method\n"); + } + + # Make sure the downloaded file actually exists. + clean_exit("failed to download $url: ". + "local target file $localfile doesn't exist after download.") + unless (-e "$localfile"); + + # Also make sure it's at least non-empty. + clean_exit("failed to download $url: local target file $localfile is empty ". + "after download (perhaps you're out of diskspace or file in url is empty?)") + unless (-s "$localfile"); +} + + + +# Copy all rules files from the tmp dirs (one for each url) +# into a single directory inside the tmp dir, except for files +# matching a 'skipfile' directive'. +# Will exit in case of colliding filenames. +sub join_tmp_rules_dirs($ $ @) +{ + my $rules_dir = shift; + my $new_files_ref = shift; + my @url_tmpdirs = @_; + + my %rules_files; + + clean_exit("failed to create directory \"$rules_dir\": $!") + unless (mkdir($rules_dir)); + + foreach my $url_tmpdir (@url_tmpdirs) { + opendir(URL_TMPDIR, "$url_tmpdir") + or clean_exit("could not open directory \"$url_tmpdir\": $!"); + + while ($_ = readdir(URL_TMPDIR)) { + next if (/^\.\.?$/ || exists($config{file_ignore_list}{$_}) || !/$config{update_files}/); + + if (exists($rules_files{$_})) { + closedir(URL_TMPDIR); + clean_exit("a file called \"$_\" exists in multiple rules archives") + } + + # Make sure it's a regular file. + unless (-f "$url_tmpdir/$_" && !-l "$url_tmpdir/$_") { + closedir(URL_TMPDIR); + clean_exit("downloaded \"$_\" is not a regular file.") + } + + $rules_files{$_} = 1; + $$new_files_ref{"$rules_dir/$_"} = 1; + + my $src_file = untaint_path("$url_tmpdir/$_"); + unless (copy("$src_file", "$rules_dir")) { + closedir(URL_TMPDIR); + clean_exit("could not copy \"$src_file\" to \"$rules_dir\": $!"); + } + } + + closedir(URL_TMPDIR); + } + + return (keys(%$new_files_ref)); +} + + + +# Make a few basic sanity checks on the rules archive and then +# uncompress/untar it if everything looked ok. +sub unpack_rules_archive($ $ $) +{ + my $url = shift; # only used when printing warnings/errors + my $archive = shift; + my $rules_dir = shift; + + my ($tar, @tar_content); + + my $old_dir = untaint_path(File::Spec->rel2abs(File::Spec->curdir())); + + my $dir = dirname($archive); + chdir("$dir") or clean_exit("$url: could not change directory to \"$dir\": $!"); + + if ($config{use_external_bins}) { + + # Run integrity check on the gzip file. + clean_exit("$url: integrity check on gzip file failed (file transfer failed or ". + "file in URL not in gzip format?).") + if (system("gzip", "-t", "$archive")); + + # Decompress it. + system("gzip", "-d", "$archive") + and clean_exit("$url: unable to uncompress $archive."); + + # Suffix has now changed from .tar.gz|.tgz to .tar. + $archive =~ s/\.gz$//; + + # Make sure the .tar file now exists. + # (Gzip may not return an error if it was not a gzipped file...) + clean_exit("$url: failed to unpack gzip file (file transfer failed or ". + "file in URL not in tar'ed gzip format?).") + unless (-e "$archive"); + + my $stdout_file = "$tmpdir/tar_content.out"; + + open(OLDOUT, ">&STDOUT") or clean_exit("could not dup STDOUT: $!"); + open(STDOUT, ">$stdout_file") or clean_exit("could not redirect STDOUT: $!"); + + my $ret = system("tar", "tf", "$archive"); + + close(STDOUT); + open(STDOUT, ">&OLDOUT") or clean_exit("could not dup STDOUT: $!"); + close(OLDOUT); + + clean_exit("$url: could not list files in tar archive (is it broken?)") + if ($ret); + + open(TAR, "$stdout_file") or clean_exit("failed to open $stdout_file: $!"); + @tar_content = ; + close(TAR); + + # use_external_bins=0 + } else { + $tar = Archive::Tar->new($archive, 1); + clean_exit("$url: failed to read $archive (file transfer failed or ". + "file in URL not in tar'ed gzip format?).") + unless (defined($tar)); + @tar_content = $tar->list_files(); + } + + # Make sure we could grab some content from the tarball. + clean_exit("$url: could not list files in tar archive (is it broken?)") + if ($#tar_content < 0); + + # For each filename in the archive, do some basic sanity checks. + foreach my $filename (@tar_content) { + chomp($filename); + + # We don't want absolute filename. + clean_exit("$url: rules archive contains absolute filename. ". + "Offending file/line:\n$filename") + if ($filename =~ /^\//); + + # We don't want to have any weird characters anywhere in the filename. + clean_exit("$url: illegal character in filename in tar archive. Allowed are ". + "$OK_PATH_CHARS\nOffending file/line:\n$filename") + if ($config{use_path_checks} && $filename =~ /[^$OK_PATH_CHARS]/); + + # We don't want to unpack any "../../" junk (check is useless now though). + clean_exit("$url: filename in tar archive contains \"..\".\n". + "Offending file/line:\n$filename") + if ($filename =~ /\.\./); + } + + # Looks good. Now we can untar it. + print STDERR "Archive successfully downloaded, unpacking... " + unless ($config{quiet}); + + if ($config{use_external_bins}) { + clean_exit("failed to untar $archive.") + if system("tar", "xf", "$archive"); + } else { + mkdir("$rules_dir") or clean_exit("could not create \"$rules_dir\" directory: $!\n"); + foreach my $file ($tar->list_files) { + next unless ($file =~ /^$rules_dir\/[^\/]+$/); # only ^rules/$ + + my $content = $tar->get_content($file); + + # Symlinks in the archive will make get_content return undef. + clean_exit("could not get content from file \"$file\" in downloaded archive, ". + "make sure it is a regular file\n") + unless (defined($content)); + + open(RULEFILE, ">", "$file") + or clean_exit("could not open \"$file\" for writing: $!\n"); + print RULEFILE $content; + close(RULEFILE); + } + } + + # Make sure that non-empty rules directory existed in archive. + # We permit empty rules directory if min_files is set to 0 though. + clean_exit("$url: no \"$rules_dir\" directory found in tar file.") + unless (-d "$dir/$rules_dir"); + + my $num_files = 0; + opendir(RULESDIR, "$dir/$rules_dir") + or clean_exit("could not open directory \"$dir/$rules_dir\": $!"); + + while ($_ = readdir(RULESDIR)) { + next if (/^\.\.?$/); + $num_files++; + } + + closedir(RULESDIR); + + clean_exit("$url: directory \"$rules_dir\" in unpacked archive is empty") + if ($num_files == 0 && $config{min_files} != 0); + + chdir($old_dir) + or clean_exit("could not change directory back to $old_dir: $!"); + + print STDERR "done.\n" + unless ($config{quiet}); +} + + + +# Open all rules files in the temporary directory and disable/modify all +# rules/lines as requested in oinkmaster.conf, and then write back to the +# same files. Also clean unwanted whitespaces and duplicate sids from them. +sub process_rules($ $ $ $ $ $) +{ + my $modify_sid_ref = shift; + my $disable_sid_ref = shift; + my $enable_sid_ref = shift; + my $local_sid_ref = shift; + my $rh_tmp_ref = shift; + my $newfiles_ref = shift; + my %sids; + + my %stats = ( + disabled => 0, + enabled => 0, + modified => 0, + total => 0, + ); + + warn("WARNING: all rules that are disabled by default will be enabled\n") + if ($config{enable_all} && !$config{quiet}); + + print STDERR "Processing downloaded rules... " + unless ($config{quiet}); + + print STDERR "\n" + if ($config{verbose}); + + # Phase #1 - process all active rules and store in temporary hash. + # In case of dups, we use the one with the highest rev. + foreach my $file (sort(keys(%$newfiles_ref))) { + + open(INFILE, "<", "$file") + or clean_exit("could not open $file for reading: $!"); + my @infile = ; + close(INFILE); + + my ($single, $multi, $nonrule, $msg, $sid); + + RULELOOP:while (get_next_entry(\@infile, \$single, \$multi, \$nonrule, \$msg, \$sid)) { + + # We don't care about non-rules in this phase. + next RULELOOP if (defined($nonrule)); + + # Even if it was a single-line rule, we want a copy in $multi. + $multi = $single unless (defined($multi)); + + my %rule = ( + single => $single, + multi => $multi, + ); + + # modify/disable/enable this rule as requested unless there is a matching + # localsid statement. Possible verbose messages and warnings will be printed. + unless (exists($$local_sid_ref{$sid})) { + process_rule($modify_sid_ref, $disable_sid_ref, $enable_sid_ref, + \%rule, $sid, \%stats, 1, basename($file)); + } + + $stats{total}++; + + $single = $rule{single}; + $multi = $rule{multi}; + + # Only care about active rules in this phase (the rule may have been + # disabled by a disablesid or a modifysid statement above, so we can't + # do this check earlier). + next RULELOOP if ($multi =~ /^#/); + + # Is it a dup? If so, see if this seems to be more recent (higher rev). + if (exists($sids{$sid})) { + warn("\nWARNING: duplicate SID in downloaded archive, SID=$sid, ". + "only keeping rule with highest 'rev'\n") + unless($config{super_quiet}); + + my ($old_rev) = ($sids{$sid}{single} =~ /\brev\s*:\s*(\d+)\s*;/); + my ($new_rev) = ($single =~ /\brev\s*:\s*(\d+)\s*;/); + + # This is so rules with a rev gets higher prio than + # rules without any rev. + $old_rev = -1 unless (defined($old_rev)); + $new_rev = -1 unless (defined($new_rev)); + + # If this rev is higher than the one in the last stored rule with + # this sid, replace rule with this one. This is also done if the + # revs are equal because we assume the rule appearing last in the + # rules file is the more recent rule. + if ($new_rev >= $old_rev) { + $sids{$sid}{single} = $single; + $sids{$sid}{multi} = $multi; + } + + # No dup. + } else { + $sids{$sid}{single} = $single; + $sids{$sid}{multi} = $multi; + } + } + } + + # Phase #2 - read all rules files again, but when writing active rules + # back to the files, use the one stored in the sid hash (which is free of dups). + foreach my $file (sort(keys(%$newfiles_ref))) { + + open(INFILE, "<", "$file") + or clean_exit("could not open $file for reading: $!"); + my @infile = ; + close(INFILE); + + # Write back to the same file. + open(OUTFILE, ">", "$file") + or clean_exit("could not open $file for writing: $!"); + + my ($single, $multi, $nonrule, $msg, $sid); + + RULELOOP:while (get_next_entry(\@infile, \$single, \$multi, \$nonrule, \$msg, \$sid)) { + if (defined($nonrule)) { + print OUTFILE "$nonrule"; + next RULELOOP; + } + + # Even if it was a single-line rule, we want a copy in $multi. + $multi = $single unless (defined($multi)); + + # If this rule is marked as localized and has not yet been written, + # write the old version to the new rules file. + if (exists($$local_sid_ref{$sid}) && !exists($sids{$sid}{printed})) { + + # Just ignore the rule in the downloaded file if it doesn't + # exist in the same local file. + unless(exists($$rh_tmp_ref{old}{rules}{basename($file)}{$sid})) { + warn("WARNING: SID $sid is marked as local and exists in ". + "downloaded " . basename($file) . " but the SID does not ". + "exist in the local file, ignoring rule\n") + if ($config{verbose}); + + next RULELOOP; + } + + print OUTFILE $$rh_tmp_ref{old}{rules}{basename($file)}{$sid}; + $sids{$sid}{printed} = 1; + + warn("SID $sid is marked as local, keeping your version from ". + basename($file) . ".\n". + "Your version: $$rh_tmp_ref{old}{rules}{basename($file)}{$sid}". + "Downloaded version: $multi\n") + if ($config{verbose}); + + next RULELOOP; + } + + my %rule = ( + single => $single, + multi => $multi, + ); + + # modify/disable/enable this rule. Possible verbose messages and warnings + # will not be printed (again) as this was done in the first phase. + # We send the stats to a dummy var as this was collected on the + # first phase as well. + process_rule($modify_sid_ref, $disable_sid_ref, $enable_sid_ref, + \%rule, $sid, \my %unused_stats, 0, basename($file)); + + $single = $rule{single}; + $multi = $rule{multi}; + + # Disabled rules are printed right back to the file, unless + # there also is an active rule with the same sid. Als o make + # sure we only print the sid once, even though it's disabled. + if ($multi =~ /^#/ && !exists($sids{$sid}) && !exists($sids{$sid}{printed})) { + print OUTFILE $multi; + $sids{$sid}{printed} = 1; + next RULELOOP; + } + + # If this sid has not yet been printed and this is the place where + # the sid with the highest rev was, print the rule to the file. + # (There can be multiple totally different rules with the same sid + # and we don't want to put the wrong rule in the wrong place. + if (!exists($sids{$sid}{printed}) && $single eq $sids{$sid}{single}) { + print OUTFILE $multi; + $sids{$sid}{printed} = 1; + } + } + + close(OUTFILE); + } + + print STDERR "disabled $stats{disabled}, enabled $stats{enabled}, ". + "modified $stats{modified}, total=$stats{total}\n" + unless ($config{quiet}); + + # Print warnings on attempt at enablesid/disablesid/localsid on non-existent + # rule if we're in verbose mode. + if ($config{verbose}) { + foreach my $sid (keys(%$enable_sid_ref)) { + warn("WARNING: attempt to use \"enablesid\" on non-existent SID $sid\n") + unless (exists($sids{$sid})); + } + + foreach my $sid (keys(%$disable_sid_ref)) { + warn("WARNING: attempt to use \"disablesid\" on non-existent SID $sid\n") + unless (exists($sids{$sid})); + } + + foreach my $sid (keys(%$local_sid_ref)) { + warn("WARNING: attempt to use \"localsid\" on non-existent SID $sid\n") + unless (exists($sids{$sid})); + } + } + + # Print warnings on attempt at modifysid'ing non-existent stuff, unless quiet mode. + unless ($config{quiet}) { + my %new_files; + foreach my $file (sort(keys(%$newfiles_ref))) { + $new_files{basename($file)} = 1; + } + + my %mod_tmp; + foreach my $mod_expr (@$modify_sid_ref) { + my ($type, $arg) = ($mod_expr->[2], $mod_expr->[3]); + $mod_tmp{$type}{$arg} = 1; + } + + foreach my $sid (keys(%{$mod_tmp{sid}})) { + warn("WARNING: attempt to use \"modifysid\" on non-existent SID $sid\n") + unless (exists($sids{$sid})); + } + + foreach my $file (keys(%{$mod_tmp{file}})) { + warn("WARNING: attempt to use \"modifysid\" on non-existent file $file\n") + unless(exists($new_files{$file})); + } + } + + # Return total number of valid rules. + return ($stats{total}); +} + + + +# Process (modify/enable/disable) a rule as requested. +sub process_rule($ $ $ $ $ $ $ $) +{ + my $modify_sid_ref = shift; + my $disable_sid_ref = shift; + my $enable_sid_ref = shift; + my $rule_ref = shift; + my $sid = shift; + my $stats_ref = shift; + my $print_messages = shift; + my $filename = shift; + + # Just for easier access. + my $single = $$rule_ref{single}; + my $multi = $$rule_ref{multi}; + + # Some rules may be commented out by default. + # Enable them if -e is specified (both single-line and multi-line, + # version, because we don't know which version one we're going to + # use below. + # Enable them if -e is specified. + if ($multi =~ /^#/ && $config{enable_all}) { + $multi =~ s/^#*//; + $multi =~ s/\n#*/\n/g; + $single =~ s/^#*//; + $$stats_ref{enabled}++; + } + + # Modify rule if requested. For disablesid/enablesid we work + # on the multi-line version of the rule (if exists). For + # modifysid that's no good since we don't know where in the + # rule the trailing backslashes and newlines are going to be + # and we don't want them to affect the regexp. + MOD_EXP:foreach my $mod_expr (@$modify_sid_ref) { + my ($subst, $repl, $type, $arg) = + ($mod_expr->[0], $mod_expr->[1], $mod_expr->[2], $mod_expr->[3]); + + my $print_modify_warnings = 0; + $print_modify_warnings = 1 if (!$config{super_quiet} && $print_messages && $type eq "sid"); + + if ($type eq "wildcard" || ($type eq "sid" && $sid eq $arg) || + ($type eq "file" && $filename eq $arg)) { + + if ($single =~ /$subst/si) { + print STDERR "Modifying rule, SID=$sid, filename=$filename, ". + "match type=$type, subst=$subst, ". + "repl=$repl\nBefore: $single" + if ($print_messages && $config{verbose}); + + + # If user specified a backreference but the regexp did not set $1 - don't modify rule. + if (!defined($1) && ($repl =~ /[^\\]\$\d+/ || $repl =~ /[^\\]\$\{\d+\}/ + || $repl =~ /^qq\/\$\d+/ || $repl =~ /^qq\/\$\{\d+\}/)) { + warn("WARNING: SID $sid matches modifysid expression \"$subst\" but ". + "backreference variable \$1 is undefined after match, ". + "keeping original rule\n") + if ($print_modify_warnings); + next MOD_EXP; + } + + # Do the substitution on the single-line version and put it + # back in $multi. + $single =~ s/$subst/$repl/eei; + $multi = $single; + + print STDERR "After: $single\n" + if ($print_messages && $config{verbose}); + + $$stats_ref{modified}++; + } else { + if ($print_modify_warnings) { + warn("WARNING: SID $sid does not match modifysid ". + "expression \"$subst\", keeping original rule\n"); + } + } + } + } + + # Disable rule if requested and it's not already disabled. + if (exists($$disable_sid_ref{$sid}) && $multi !~ /^\s*#/) { + $multi = "#$multi"; + $multi =~ s/\n([^#].+)/\n#$1/g; + $$stats_ref{disabled}++; + } + + # Enable rule if requested and it's not already enabled. + if (exists($$enable_sid_ref{$sid}) && $multi =~ /^\s*#/) { + $multi =~ s/^#+//; + $multi =~ s/\n#+(.+)/\n$1/g; + $$stats_ref{enabled}++; + } + + $$rule_ref{single} = $single; + $$rule_ref{multi} = $multi; +} + + + +# Setup rules hash. +# Format for rules will be: rh{old|new}{rules{filename}{sid} = single-line rule +# Format for non-rules will be: rh{old|new}{other}{filename} = array of lines +# List of added files will be stored as rh{added_files}{filename} +sub setup_rules_hash($ $) +{ + my $new_files_ref = shift; + my $output_dir = shift; + + my (%rh, %old_sids); + + print STDERR "Setting up rules structures... " + unless ($config{quiet}); + + foreach my $file (sort(keys(%$new_files_ref))) { + warn("\nWARNING: downloaded rules file $file is empty\n") + if (!-s "$file" && $config{verbose}); + + open(NEWFILE, "<", "$file") + or clean_exit("could not open $file for reading: $!"); + my @newfile = ; + close(NEWFILE); + + # From now on we don't care about the path, so remove it. + $file = basename($file); + + my ($single, $multi, $nonrule, $msg, $sid); + + while (get_next_entry(\@newfile, \$single, \$multi, \$nonrule, \$msg, \$sid)) { + if (defined($single)) { + $rh{new}{rules}{"$file"}{"$sid"} = $single; + } else { + push(@{$rh{new}{other}{"$file"}}, $nonrule); + } + } + + # Also read in old (aka local) file if it exists. + # We do a sid dup check in these files. + if (-f "$output_dir/$file") { + open(OLDFILE, "<", "$output_dir/$file") + or clean_exit("could not open $output_dir/$file for reading: $!"); + my @oldfile = ; + close(OLDFILE); + + while (get_next_entry(\@oldfile, \$single, \$multi, \$nonrule, undef, \$sid)) { + if (defined($single)) { + warn("\nWARNING: duplicate SID in your local rules, SID ". + "$sid exists multiple times, you may need to fix this manually!\n") + if (exists($old_sids{$sid})); + + $rh{old}{rules}{"$file"}{"$sid"} = $single; + $old_sids{$sid}++; + } else { + push(@{$rh{old}{other}{"$file"}}, $nonrule); + } + } + } else { + $rh{added_files}{"$file"}++; + } + } + + print STDERR "done.\n" + unless ($config{quiet}); + + return (%rh); +} + + + +# Return lines that exist only in first array but not in second one. +sub get_first_only($ $ $) +{ + my $first_only_ref = shift; + my $first_arr_ref = shift; + my $second_arr_ref = shift; + my %arr_hash; + + @arr_hash{@$second_arr_ref} = (); + + foreach my $line (@$first_arr_ref) { + + # Skip blank lines and CVS Id tags. + next unless ($line =~ /\S/); + next if ($line =~ /^\s*#+\s*\$I\S:.+Exp\s*\$/); + + push(@$first_only_ref, $line) + unless(exists($arr_hash{$line})); + } +} + + + +# Backup files in output dir matching $config{update_files} into the backup dir. +sub make_backup($ $) +{ + my $src_dir = shift; # dir with the rules to be backed up + my $dest_dir = shift; # where to put the backup tarball + + my ($sec, $min, $hour, $mday, $mon, $year) = (localtime)[0 .. 5]; + + my $date = sprintf("%4d%02d%02d-%02d%02d%02d", + $year + 1900, $mon + 1, $mday, $hour, $min, $sec); + + my $backup_tarball = "rules-backup-$date.tar"; + my $backup_tmp_dir = File::Spec->catdir("$tmpdir", "rules-backup-$date"); + my $dest_file = File::Spec->catfile("$dest_dir", "$backup_tarball.gz"); + + print STDERR "Creating backup of old rules..." + unless ($config{quiet}); + + mkdir("$backup_tmp_dir", 0700) + or clean_exit("could not create temporary backup directory $backup_tmp_dir: $!"); + + # Copy all rules files from the rules dir to the temporary backup dir. + opendir(OLDRULES, "$src_dir") + or clean_exit("could not open directory $src_dir: $!"); + + while ($_ = readdir(OLDRULES)) { + next if (/^\.\.?$/); + if (/$config{update_files}/) { + my $src_file = untaint_path("$src_dir/$_"); + copy("$src_file", "$backup_tmp_dir/") + or warn("WARNING: could not copy $src_file to $backup_tmp_dir/: $!"); + } + } + + closedir(OLDRULES); + + # Also backup the -U (as "variable-file.conf") if specified. + if ($config{update_vars}) { + copy("$config{varfile}", "$backup_tmp_dir/variable-file.conf") + or warn("WARNING: could not copy $config{varfile} to $backup_tmp_dir: $!") + } + + my $old_dir = untaint_path(File::Spec->rel2abs(File::Spec->curdir())); + + # Change directory to $tmpdir (so we'll be right below the directory where + # we have our rules to be backed up). + chdir("$tmpdir") or clean_exit("could not change directory to $tmpdir: $!"); + + if ($config{use_external_bins}) { + clean_exit("tar command returned error when archiving backup files.\n") + if (system("tar","cf","$backup_tarball","rules-backup-$date")); + + clean_exit("gzip command returned error when compressing backup file.\n") + if (system("gzip","$backup_tarball")); + + $backup_tarball .= ".gz"; + + } else { + my $tar = Archive::Tar->new; + opendir(RULES, "rules-backup-$date") + or clean_exit("unable to open directory \"rules-backup-$date\": $!"); + + while ($_ = readdir(RULES)) { + next if (/^\.\.?$/); + $tar->add_files("rules-backup-$date/$_"); + } + + closedir(RULES); + + $backup_tarball .= ".gz"; + + # Write tarball. Print stupid error message if it fails as + # we can't use $tar->error or Tar::error on all platforms. + $tar->write("$backup_tarball", 1); + + clean_exit("could not create backup archive: tarball empty after creation\n") + unless (-s "$backup_tarball"); + } + + # Change back to old directory (so it will work with -b as either + # an absolute or a relative path. + chdir("$old_dir") + or clean_exit("could not change directory back to $old_dir: $!"); + + copy("$tmpdir/$backup_tarball", "$dest_file") + or clean_exit("unable to copy $tmpdir/$backup_tarball to $dest_file/: $!\n"); + + print STDERR " saved as $dest_file.\n" + unless ($config{quiet}); +} + + + +# Print the results. +sub print_changes($ $) +{ + my $ch_ref = shift; + my $rh_ref = shift; + + my ($sec, $min, $hour, $mday, $mon, $year) = (localtime)[0 .. 5]; + + my $date = sprintf("%4d%02d%02d %02d:%02d:%02d", + $year + 1900, $mon + 1, $mday, $hour, $min, $sec); + + print "\n[***] Results from Oinkmaster started $date [***]\n"; + + # Print new variables. + if ($config{update_vars}) { + if ($#{$$ch_ref{new_vars}} > -1) { + print "\n[*] New variables: [*]\n"; + foreach my $var (@{$$ch_ref{new_vars}}) { + print " $var"; + } + } else { + print "\n[*] New variables: [*]\n None.\n" + unless ($config{super_quiet}); + } + } + + + # Print rules modifications. + print "\n[*] Rules modifications: [*]\n None.\n" + if (!keys(%{$$ch_ref{rules}}) && !$config{super_quiet}); + + # Print added rules. + if (exists($$ch_ref{rules}{added})) { + print "\n[+++] Added rules: [+++]\n"; + if ($config{summary_output}) { + print_summary_change(\%{$$ch_ref{rules}{added}}, $rh_ref); + } else { + print_changetype($PRINT_NEW, "Added to", + \%{$$ch_ref{rules}{added}}, $rh_ref); + } + } + + # Print enabled rules. + if (exists($$ch_ref{rules}{ena})) { + print "\n[+++] Enabled rules: [+++]\n"; + if ($config{summary_output}) { + print_summary_change(\%{$$ch_ref{rules}{ena}}, $rh_ref); + } else { + print_changetype($PRINT_NEW, "Enabled in", + \%{$$ch_ref{rules}{ena}}, $rh_ref); + } + } + + # Print enabled + modified rules. + if (exists($$ch_ref{rules}{ena_mod})) { + print "\n[+++] Enabled and modified rules: [+++]\n"; + if ($config{summary_output}) { + print_summary_change(\%{$$ch_ref{rules}{ena_mod}}, $rh_ref); + } else { + print_changetype($PRINT_BOTH, "Enabled and modified in", + \%{$$ch_ref{rules}{ena_mod}}, $rh_ref); + } + } + + # Print modified active rules. + if (exists($$ch_ref{rules}{mod_act})) { + print "\n[///] Modified active rules: [///]\n"; + + if ($config{summary_output}) { + print_summary_change(\%{$$ch_ref{rules}{mod_act}}, $rh_ref); + } else { + print_changetype($PRINT_BOTH, "Modified active in", + \%{$$ch_ref{rules}{mod_act}}, $rh_ref); + } + } + + # Print modified inactive rules. + if (exists($$ch_ref{rules}{mod_ina})) { + print "\n[///] Modified inactive rules: [///]\n"; + if ($config{summary_output}) { + print_summary_change(\%{$$ch_ref{rules}{mod_ina}}, $rh_ref); + } else { + print_changetype($PRINT_BOTH, "Modified inactive in", + \%{$$ch_ref{rules}{mod_ina}}, $rh_ref); + } + } + + # Print disabled + modified rules. + if (exists($$ch_ref{rules}{dis_mod})) { + print "\n[---] Disabled and modified rules: [---]\n"; + if ($config{summary_output}) { + print_summary_change(\%{$$ch_ref{rules}{dis_mod}}, $rh_ref); + } else { + print_changetype($PRINT_BOTH, "Disabled and modified in", + \%{$$ch_ref{rules}{dis_mod}}, $rh_ref); + } + } + + # Print disabled rules. + if (exists($$ch_ref{rules}{dis})) { + print "\n[---] Disabled rules: [---]\n"; + if ($config{summary_output}) { + print_summary_change(\%{$$ch_ref{rules}{dis}}, $rh_ref); + } else { + print_changetype($PRINT_NEW, "Disabled in", + \%{$$ch_ref{rules}{dis}}, $rh_ref); + } + } + + # Print removed rules. + if (exists($$ch_ref{rules}{removed})) { + print "\n[---] Removed rules: [---]\n"; + if ($config{summary_output}) { + print_summary_change(\%{$$ch_ref{rules}{removed}}, $rh_ref); + } else { + print_changetype($PRINT_OLD, "Removed from", + \%{$$ch_ref{rules}{removed}}, $rh_ref); + } + } + + + # Print non-rule modifications. + print "\n[*] Non-rule line modifications: [*]\n None.\n" + if (!keys(%{$$ch_ref{other}}) && !$config{super_quiet}); + + # Print added non-rule lines. + if (exists($$ch_ref{other}{added})) { + print "\n[+++] Added non-rule lines: [+++]\n"; + foreach my $file (sort({uc($a) cmp uc($b)} keys(%{$$ch_ref{other}{added}}))) { + my $num = $#{$$ch_ref{other}{added}{$file}} + 1; + print "\n -> Added to $file ($num):\n"; + foreach my $line (@{$$ch_ref{other}{added}{$file}}) { + print " $line"; + } + } + } + + # Print removed non-rule lines. + if (keys(%{$$ch_ref{other}{removed}}) > 0) { + print "\n[---] Removed non-rule lines: [---]\n"; + foreach my $file (sort({uc($a) cmp uc($b)} keys(%{$$ch_ref{other}{removed}}))) { + my $num = $#{$$ch_ref{other}{removed}{$file}} + 1; + print "\n -> Removed from $file ($num):\n"; + foreach my $other (@{$$ch_ref{other}{removed}{$file}}) { + print " $other"; + } + } + } + + + # Print list of added files. + if (keys(%{$$ch_ref{added_files}})) { + print "\n[+] Added files (consider updating your snort.conf to include them if needed): [+]\n\n"; + foreach my $added_file (sort({uc($a) cmp uc($b)} keys(%{$$ch_ref{added_files}}))) { + print " -> $added_file\n"; + } + } else { + print "\n[*] Added files: [*]\n None.\n" + unless ($config{super_quiet} || $config{summary_output}); + } + + # Print list of possibly removed files if requested. + if ($config{check_removed}) { + if (keys(%{$$ch_ref{removed_files}})) { + print "\n[-] Files possibly removed from the archive ". + "(consider removing them from your snort.conf if needed): [-]\n\n"; + foreach my $removed_file (sort({uc($a) cmp uc($b)} keys(%{$$ch_ref{removed_files}}))) { + print " -> $removed_file\n"; + } + } else { + print "\n[*] Files possibly removed from the archive: [*]\n None.\n" + unless ($config{super_quiet} || $config{summary_output}); + } + } + + print "\n"; +} + + + +# Helper for print_changes(). +sub print_changetype($ $ $ $) +{ + my $type = shift; # $PRINT_OLD|$PRINT_NEW|$PRINT_BOTH + my $string = shift; # string to print before filename + my $ch_ref = shift; # reference to an entry in the rules changes hash + my $rh_ref = shift; # reference to rules hash + + foreach my $file (sort({uc($a) cmp uc($b)} keys(%$ch_ref))) { + my $num = keys(%{$$ch_ref{$file}}); + print "\n -> $string $file ($num):\n"; + foreach my $sid (keys(%{$$ch_ref{$file}})) { + if ($type == $PRINT_OLD) { + print " $$rh_ref{old}{rules}{$file}{$sid}" + } elsif ($type == $PRINT_NEW) { + print " $$rh_ref{new}{rules}{$file}{$sid}" + } elsif ($type == $PRINT_BOTH) { + + my $old = $$rh_ref{old}{rules}{$file}{$sid}; + my $new = $$rh_ref{new}{rules}{$file}{$sid}; + + if ($config{minimize_diff}) { + my ($old, $new) = minimize_diff($old, $new); + print "\n old SID $sid: $old"; + print " new SID $sid: $new"; + } else { + print "\n old: $old"; + print " new: $new"; + } + } + } + } +} + + + +# Print changes in bmc style, i.e. only sid and msg, no full details. +sub print_summary_change($ $) +{ + my $ch_ref = shift; # reference to an entry in the rules changes hash + my $rh_ref = shift; # reference to rules hash + + my (@sids, %sidmap); + + print "\n"; + + # First get all the sids (may be spread across multiple files. + foreach my $file (keys(%$ch_ref)) { + foreach my $sid (keys(%{$$ch_ref{$file}})) { + push(@sids, $sid); + if (exists($$rh_ref{new}{rules}{$file}{$sid})) { + $sidmap{$sid}{rule} = $$rh_ref{new}{rules}{$file}{$sid}; + } else { + $sidmap{$sid}{rule} = $$rh_ref{old}{rules}{$file}{$sid}; + } + $sidmap{$sid}{file} = $file; + } + } + + # Print rules, sorted by sid. + foreach my $sid (sort {$a <=> $b} (@sids)) { + my @rule = $sidmap{$sid}{rule}; + my $file = $sidmap{$sid}{file}; + get_next_entry(\@rule, undef, undef, undef, \(my $msg), undef); + printf("%8d - %s (%s)\n", $sid, $msg, $file); + } + + print "\n"; +} + + + +# Compare the new rules to the old ones. +sub get_changes($ $ $) +{ + my $rh_ref = shift; + my $new_files_ref = shift; + my $rules_dir = shift; + my %changes; + + print STDERR "Comparing new files to the old ones... " + unless ($config{quiet}); + + # We have the list of added files (without full path) in $rh_ref{added_files} + # but we'd rather want to have it in $changes{added_files} now. + $changes{added_files} = $$rh_ref{added_files}; + + # New files are also regarded as modified since we want to update + # (i.e. add) those as well. Here we want them with full path. + foreach my $file (keys(%{$changes{added_files}})) { + $changes{modified_files}{"$tmpdir/$rules_dir/$file"}++; + } + + # Add list of possibly removed files if requested. + if ($config{check_removed}) { + opendir(OLDRULES, "$config{output_dir}") + or clean_exit("could not open directory $config{output_dir}: $!"); + + while ($_ = readdir(OLDRULES)) { + next if (/^\.\.?$/); + $changes{removed_files}{"$_"} = 1 + if (/$config{update_files}/ && + !exists($config{file_ignore_list}{$_}) && + !-e "$tmpdir/$rules_dir/$_"); + } + + closedir(OLDRULES); + } + + # For each new rules file... + FILELOOP:foreach my $file_w_path (sort(keys(%$new_files_ref))) { + my $file = basename($file_w_path); + + # Skip comparison if it's an added file. + next FILELOOP if (exists($$rh_ref{added_files}{$file})); + + # For each sid in the new file... + foreach my $sid (keys(%{$$rh_ref{new}{rules}{$file}})) { + my $new_rule = $$rh_ref{new}{rules}{$file}{$sid}; + + # Sid also exists in the old file? + if (exists($$rh_ref{old}{rules}{$file}{$sid})) { + my $old_rule = $$rh_ref{old}{rules}{$file}{$sid}; + + # Are they identical? + unless ($new_rule eq $old_rule) { + $changes{modified_files}{$file_w_path}++; + + # Find out in which way the rules are different. + if ("#$old_rule" eq $new_rule) { + $changes{rules}{dis}{$file}{$sid}++; + } elsif ($old_rule eq "#$new_rule") { + $changes{rules}{ena}{$file}{$sid}++; + } elsif ($old_rule =~ /^\s*#/ && $new_rule !~ /^\s*#/) { + $changes{rules}{ena_mod}{$file}{$sid}++; + } elsif ($old_rule !~ /^\s*#/ && $new_rule =~ /^\s*#/) { + $changes{rules}{dis_mod}{$file}{$sid}++; + } elsif ($old_rule =~ /^\s*#/ && $new_rule =~ /^\s*#/) { + $changes{rules}{mod_ina}{$file}{$sid}++; + } else { + $changes{rules}{mod_act}{$file}{$sid}++; + } + + } + } else { # sid not found in old file, i.e. it's added + $changes{modified_files}{$file_w_path}++; + $changes{rules}{added}{$file}{$sid}++; + } + } # foreach sid + + # Check for removed rules, i.e. sids that exist in the old file but + # not in the new one. + foreach my $sid (keys(%{$$rh_ref{old}{rules}{$file}})) { + unless (exists($$rh_ref{new}{rules}{$file}{$sid})) { + $changes{modified_files}{$file_w_path}++; + $changes{rules}{removed}{$file}{$sid}++; + } + } + + # Check for added non-rule lines. + get_first_only(\my @added, + \@{$$rh_ref{new}{other}{$file}}, + \@{$$rh_ref{old}{other}{$file}}); + + if (scalar(@added)) { + @{$changes{other}{added}{$file}} = @added; + $changes{modified_files}{$file_w_path}++; + } + + # Check for removed non-rule lines. + get_first_only(\my @removed, + \@{$$rh_ref{old}{other}{$file}}, + \@{$$rh_ref{new}{other}{$file}}); + + if (scalar(@removed)) { + @{$changes{other}{removed}{$file}} = @removed; + $changes{modified_files}{$file_w_path}++; + } + + } # foreach new file + + print STDERR "done.\n" unless ($config{quiet}); + + return (%changes); +} + + + +# Simply copy the modified rules files to the output directory. +sub update_rules($ @) +{ + my $dst_dir = shift; + my @modified_files = @_; + + print STDERR "Updating local rules files... " + if (!$config{quiet} || $config{interactive}); + + foreach my $file_w_path (@modified_files) { + copy("$file_w_path", "$dst_dir") + or clean_exit("could not copy $file_w_path to $dst_dir: $!"); + } + + print STDERR "done.\n" + if (!$config{quiet} || $config{interactive}); +} + + +# Simply copy rules files from one dir to another. +# Links are not allowed. +sub copy_rules($ $) +{ + my $src_dir = shift; + my $dst_dir = shift; + + print STDERR "Copying rules from $src_dir... " + if (!$config{quiet} || $config{interactive}); + + opendir(SRC_DIR, $src_dir) + or clean_exit("could not open directory $src_dir: $!"); + + my $num_files = 0; + while ($_ = readdir(SRC_DIR)) { + next if (/^\.\.?$/ || exists($config{file_ignore_list}{$_}) + || !/$config{update_files}/); + + my $src_file = untaint_path("$src_dir/$_"); + + # Make sure it's a regular file. + unless (-f "$src_file" && !-l "$src_file") { + closedir(SRC_DIR); + clean_exit("\"$src_file\" is not a regular file.") + } + + unless (copy($src_file, $dst_dir)) { + closedir(SRC_DIR); + clean_exit("could not copy \"$src_file\" to \"$dst_dir\"/: $!"); + } + $num_files++; + } + + closedir(SRC_DIR); + + print STDERR "$num_files files copied.\n" + if (!$config{quiet} || $config{interactive}); +} + + + +# Return true if file is in PATH and is executable. +sub is_in_path($) +{ + my $file = shift; + + foreach my $dir (File::Spec->path()) { + if ((-f "$dir/$file" && -x "$dir/$file") + || (-f "$dir/$file.exe" && -x "$dir/$file.exe")) { + print STDERR "Found $file binary in $dir\n" + if ($config{verbose}); + return (1); + } + } + + return (0); +} + + + +# get_next_entry() will parse the array referenced in the first arg +# and return the next entry. The array should contain a rules file, +# and the returned entry will be removed from the array. +# An entry is one of: +# - single-line rule (put in 2nd ref) +# - multi-line rule (put in 3rd ref) +# - non-rule line (put in 4th ref) +# If the entry is a multi-line rule, its single-line version is also +# returned (put in the 2nd ref). +# If it's a rule, the msg string will be put in 4th ref and sid in 5th. +sub get_next_entry($ $ $ $ $ $) +{ + my $arr_ref = shift; + my $single_ref = shift; + my $multi_ref = shift; + my $nonrule_ref = shift; + my $msg_ref = shift; + my $sid_ref = shift; + + undef($$single_ref); + undef($$multi_ref); + undef($$nonrule_ref); + undef($$msg_ref); + undef($$sid_ref); + + my $line = shift(@$arr_ref) || return(0); + my $disabled = 0; + my $broken = 0; + + chomp($line); + $line .= "\n"; + + # Possible beginning of multi-line rule? + if ($line =~ /$MULTILINE_RULE_REGEXP/oi) { + $$single_ref = $line; + $$multi_ref = $line; + + $disabled = 1 if ($line =~ /^\s*#/); + + # Keep on reading as long as line ends with "\". + while (!$broken && $line =~ /\\\s*\n$/) { + + # Remove trailing "\" and newline for single-line version. + $$single_ref =~ s/\\\s*\n//; + + # If there are no more lines, this can not be a valid multi-line rule. + if (!($line = shift(@$arr_ref))) { + + warn("\nWARNING: got EOF while parsing multi-line rule: $$multi_ref\n") + if ($config{verbose}); + + @_ = split(/\n/, $$multi_ref); + + undef($$multi_ref); + undef($$single_ref); + + # First line of broken multi-line rule will be returned as a non-rule line. + $$nonrule_ref = shift(@_) . "\n"; + $$nonrule_ref =~ s/\s*\n$/\n/; # remove trailing whitespaces + + # The rest is put back to the array again. + foreach $_ (reverse((@_))) { + unshift(@$arr_ref, "$_\n"); + } + + return (1); # return non-rule + } + + # Multi-line continuation. + $$multi_ref .= $line; + + # If there are non-comment lines in the middle of a disabled rule, + # mark the rule as broken to return as non-rule lines. + if ($line !~ /^\s*#/ && $disabled) { + $broken = 1; + } elsif ($line =~ /^\s*#/ && !$disabled) { + # comment line (with trailing slash) in the middle of an active rule - ignore it + } else { + $line =~ s/^\s*#*\s*//; # remove leading # in single-line version + $$single_ref .= $line; + } + + } # while line ends with "\" + + # Single-line version should now be a valid rule. + # If not, it wasn't a valid multi-line rule after all. + if (!$broken && parse_singleline_rule($$single_ref, $msg_ref, $sid_ref)) { + + $$single_ref =~ s/^\s*//; # remove leading whitespaces + $$single_ref =~ s/^#+\s*/#/; # remove whitespaces next to leading # + $$single_ref =~ s/\s*\n$/\n/; # remove trailing whitespaces + + $$multi_ref =~ s/^\s*//; + $$multi_ref =~ s/\s*\n$/\n/; + $$multi_ref =~ s/^#+\s*/#/; + + return (1); # return multi + + # Invalid multi-line rule. + } else { + warn("\nWARNING: invalid multi-line rule: $$single_ref\n") + if ($config{verbose} && $$multi_ref !~ /^\s*#/); + + @_ = split(/\n/, $$multi_ref); + + undef($$multi_ref); + undef($$single_ref); + + # First line of broken multi-line rule will be returned as a non-rule line. + $$nonrule_ref = shift(@_) . "\n"; + $$nonrule_ref =~ s/\s*\n$/\n/; # remove trailing whitespaces + + # The rest is put back to the array again. + foreach $_ (reverse((@_))) { + unshift(@$arr_ref, "$_\n"); + } + + return (1); # return non-rule + } + + # Check if it's a regular single-line rule. + } elsif (parse_singleline_rule($line, $msg_ref, $sid_ref)) { + $$single_ref = $line; + $$single_ref =~ s/^\s*//; + $$single_ref =~ s/^#+\s*/#/; + $$single_ref =~ s/\s*\n$/\n/; + + return (1); # return single + + # Non-rule line. + } else { + + # Do extra check and warn if it *might* be a rule anyway, + # but that we just couldn't parse for some reason. + warn("\nWARNING: line may be a rule but it could not be parsed ". + "(missing sid?): $line\n") + if ($config{verbose} && $line =~ /^\s*alert .+msg\s*:\s*".+"\s*;/); + + $$nonrule_ref = $line; + $$nonrule_ref =~ s/\s*\n$/\n/; + + return (1); # return non-rule + } +} + + + +# Look for variables that exist in dist var files but not in local var file. +sub get_new_vars($ $ $ $) +{ + my $ch_ref = shift; + my $dist_var_files_ref = shift; + my $local_var_file = shift; + my $url_tmpdirs_ref = shift; + + my %new_vars; + my (%old_vars, %dist_var_files, %found_dist_var_files); + my $confs_found = 0; + + + # Warn in case we can't find a specified dist file. + foreach my $dir (@$url_tmpdirs_ref) { + foreach my $dist_var_file (@$dist_var_files_ref) { + if (-e "$dir/$dist_var_file") { + $found_dist_var_files{$dist_var_file} = 1; + $confs_found++; + } + } + } + + foreach my $dist_var_file (@$dist_var_files_ref) { + unless (exists($found_dist_var_files{$dist_var_file})) { + warn("WARNING: did not find variable file \"$dist_var_file\" in ". + "downloaded archive(s)\n") + unless($config{quiet}); + } + } + + unless ($confs_found) { + unless ($config{quiet}) { + warn("WARNING: no variable files found in downloaded archive(s), ". + "aborting check for new variables\n"); + return; + } + } + + # Read in variable names from old (target) var file. + open(LOCAL_VAR_FILE, "<", "$local_var_file") + or clean_exit("could not open $local_var_file for reading: $!"); + + my @local_var_conf = ; + + foreach $_ (join_multilines(\@local_var_conf)) { + $old_vars{lc($1)}++ if (/$VAR_REGEXP/i); + } + + close(LOCAL_VAR_FILE); + + # Read in variables from new file(s). + foreach my $dir (@$url_tmpdirs_ref) { + foreach my $dist_var_file (@$dist_var_files_ref) { + my $conf = "$dir/$dist_var_file"; + if (-e "$conf") { + my $num_new = 0; + print STDERR "Checking downloaded $dist_var_file for new variables... " + unless ($config{quiet}); + + open(DIST_CONF, "<", "$conf") + or clean_exit("could not open $conf for reading: $!"); + my @dist_var_conf = ; + close(DIST_CONF); + + foreach $_ (join_multilines(\@dist_var_conf)) { + if (/$VAR_REGEXP/i && !exists($old_vars{lc($1)})) { + my ($varname, $varval) = (lc($1), $2); + if (exists($new_vars{$varname})) { + warn("\nWARNING: new variable \"$varname\" is defined multiple ". + "times in downloaded files\n"); + } + s/^\s*//; + push(@{$$ch_ref{new_vars}}, "$_\n"); + $new_vars{$varname} = $varval; + $num_new++; + } + } + + close(DIST_CONF); + print STDERR "$num_new new found.\n" + unless ($config{quiet}); + } + } + } +} + + + +# Add new variables to local snort.conf. +sub add_new_vars($ $) +{ + my $ch_ref = shift; + my $varfile = shift; + my $tmp_varfile = "$tmpdir/tmp_varfile.conf"; + my $new_content; + + return unless ($#{$changes{new_vars}} > -1); + + print STDERR "Adding new variables to $varfile... " + unless ($config{quiet}); + + open(OLD_LOCAL_CONF, "<", "$varfile") + or clean_exit("could not open $varfile for reading: $!"); + my @old_content = ; + close(OLD_LOCAL_CONF); + + open(NEW_LOCAL_CONF, ">", "$tmp_varfile") + or clean_exit("could not open $tmp_varfile for writing: $!"); + + my @old_vars = grep(/$VAR_REGEXP/i, @old_content); + + + # If any vars exist in old file, put new vars right after them. + if ($#old_vars > -1) { + while ($_ = shift(@old_content)) { + print NEW_LOCAL_CONF $_; + last if ($_ eq $old_vars[$#old_vars]); + } + } + + print NEW_LOCAL_CONF @{$changes{new_vars}}; + print NEW_LOCAL_CONF @old_content; + + close(NEW_LOCAL_CONF); + + clean_exit("could not copy $tmp_varfile to $varfile: $!") + unless (copy("$tmp_varfile", "$varfile")); + + print STDERR "done.\n" + unless ($config{quiet}); +} + + + +# Convert msdos style path to cygwin style, e.g. +# c:\foo => /cygdrive/c/foo +sub msdos_to_cygwin_path($) +{ + my $path_ref = shift; + + if ($$path_ref =~ /^([a-zA-Z]):[\/\\](.*)/) { + my ($drive, $dir) = ($1, $2); + $dir =~ s/\\/\//g; + $$path_ref = "/cygdrive/$drive/$dir"; + return (1); + } + + return (0); +} + + + +# Parse and process a modifysid expression. +# Return 1 if valid, or otherwise 0. +sub parse_mod_expr($ $ $ $) +{ + my $mod_list_ref = shift; # where to store valid entries + my $sid_arg_list = shift; # comma-separated list of SIDs/files or wildcard + my $subst = shift; # regexp to look for + my $repl = shift; # regexp to replace it with + + my @tmp_mod_list; + + $sid_arg_list =~ s/\s+$//; + + foreach my $sid_arg (split(/\s*,\s*/, $sid_arg_list)) { + my $type = ""; + + $type = "sid" if ($sid_arg =~ /^\d+$/); + $type = "file" if ($sid_arg =~ /^\S+.*\.\S+$/); + $type = "wildcard" if ($sid_arg eq "*"); + + return (0) unless ($type); + + # Sanity check to make sure user escaped at least all the "$" in $subst. + if ($subst =~ /[^\\]\$./ || $subst =~ /^\$/) { + warn("WARNING: unescaped \$ in expression \"$subst\", all special ". + "characters must be escaped\n"); + return (0); + } + + # Only allow backreference variables. The check should at least catch some user typos. + if (($repl =~ /[^\\]\$(\D.)/ && $1 !~ /{\d/) || $repl =~ /[^\\]\$$/ + || ($repl =~ /^\$(\D.)/ && $1 !~ /{\d/)) { + warn("WARNING: illegal replacement expression \"$repl\": unescaped \$ ". + "that isn't a backreference\n"); + return (0); + } + + # Don't permit unescaped @. + if ($repl =~ /[^\\]\@/ || $repl =~ /^\@/) { + warn("WARNING: illegal replacement expression \"$repl\": unescaped \@\n"); + return (0); + } + + # Make sure the regexp is valid. + my $repl_qq = "qq/$repl/"; + my $dummy = "foo"; + + eval { + $dummy =~ s/$subst/$repl_qq/ee; + }; + + # We should probably check for warnings as well as errors... + if ($@) { + warn("Invalid regexp: $@"); + return (0); + } + + push(@tmp_mod_list, [$subst, $repl_qq, $type, $sid_arg]); + } + + # If we come this far, all sids and the regexp were parsed successfully, so + # append them to real mod list array. + foreach my $mod_entry (@tmp_mod_list) { + push(@$mod_list_ref, $mod_entry); + } + + return (1); +} + + + +# Untaint a path. Die if it contains illegal chars. +sub untaint_path($) +{ + my $path = shift; + my $orig_path = $path; + + return $path unless ($config{use_path_checks}); + + (($path) = $path =~ /^([$OK_PATH_CHARS]+)$/) + or clean_exit("illegal character in path/filename ". + "\"$orig_path\", allowed are $OK_PATH_CHARS\n". + "Fix this or set use_path_checks=0 in oinkmaster.conf ". + "to disable this check completely if it is too strict.\n"); + + return ($path); +} + + + +# Ask user to approve changes. Return 1 for yes, 0 for no. +sub approve_changes() +{ + my $answer = ""; + + while ($answer !~ /^[yn]/i) { + print "Do you approve these changes? [Yn] "; + $answer = ; + $answer = "y" unless ($answer =~ /\S/); + } + + return ($answer =~ /^y/i); +} + + + +# Remove common leading and trailing stuff from two rules. +sub minimize_diff($ $) +{ + my $old_rule = shift; + my $new_rule = shift; + + my $original_old = $old_rule; + my $original_new = $new_rule; + + # Additional chars to print next to the diffing part. + my $additional_chars = 20; + + # Remove the rev keyword from the rules, as it often + # makes the whole diff minimizing useless. + $old_rule =~ s/\s*\b(rev\s*:\s*\d+\s*;)\s*//; + my $old_rev = $1; + + $new_rule =~ s/\s*\b(rev\s*:\s*\d+\s*;)\s*//; + my $new_rev = $1; + + # If rev was the only thing that changed, we want to restore the rev + # before continuing so we don't remove common stuff from rules that + # are identical. + if ($old_rule eq $new_rule) { + $old_rule = $original_old; + $new_rule = $original_new; + } + + # Temporarily remove possible leading # so it works nicely + # with modified rules that are also being either enabled or disabled. + my $old_is_disabled = 0; + my $new_is_disabled = 0; + + $old_is_disabled = 1 if ($old_rule =~ s/^#//); + $new_is_disabled = 1 if ($new_rule =~ s/^#//); + + # Go forward char by char until they aren't equeal. + # $i will bet set to the index where they diff. + my @old = split(//, $old_rule); + my @new = split(//, $new_rule); + + my $i = 0; + while ($i <= $#old && $i <= $#new && $old[$i] eq $new[$i]) { + $i++; + } + + # Now same thing but backwards. + # $j will bet set to the index where they diff. + @old = reverse(split(//, $old_rule)); + @new = reverse(split(//, $new_rule)); + + my $j = 0; + while ($j <= $#old && $j <= $#new && $old[$j] eq $new[$j]) { + $j++; + } + + # Print some additional chars on either side, if there is room for it. + $i -= $additional_chars; + $i = 0 if ($i < 0); + + $j = -$j + $additional_chars; + $j = 0 if ($j > -1); + + my ($old, $new); + + # Print entire rules (i.e. they can not be shortened). + if (!$i && !$j) { + $old = $old_rule; + $new = $new_rule; + + # Leading and trailing stuff can be removed. + } elsif ($i && $j) { + $old = "..." . substr($old_rule, $i, $j) . "..."; + $new = "..." . substr($new_rule, $i, $j) . "..."; + + # Trailing stuff can be removed. + } elsif (!$i && $j) { + $old = substr($old_rule, $i, $j) . "..."; + $new = substr($new_rule, $i, $j) . "..."; + + # Leading stuff can be removed. + } elsif ($i && !$j) { + $old = "..." . substr($old_rule, $i); + $new = "..." . substr($new_rule, $i); + } + + chomp($old, $new); + $old .= "\n"; + $new .= "\n"; + + # Restore possible leading # now. + $old = "#$old" if ($old_is_disabled); + $new = "#$new" if ($new_is_disabled); + + return ($old, $new); +} + + + +# Check a string and return 1 if it's a valid single-line snort rule. +# Msg string is put in second arg, sid in third (those are the only +# required keywords, besides the leading rule actions). +sub parse_singleline_rule($ $ $) +{ + my $line = shift; + my $msg_ref = shift; + my $sid_ref = shift; + + undef($$msg_ref); + undef($$sid_ref); + + if ($line =~ /$SINGLELINE_RULE_REGEXP/oi) { + + if ($line =~ /\bmsg\s*:\s*"(.+?)"\s*;/i) { + $$msg_ref = $1; + } else { + return (0); + } + + if ($line =~ /\bsid\s*:\s*(\d+)\s*;/i) { + $$sid_ref = $1; + } else { + return (0); + } + + return (1); + } + + return (0); +} + + + +# Merge multiline directives in an array by simply removing traling backslashes. +sub join_multilines($) +{ + my $multiline_conf_ref = shift; + my $joined_conf = ""; + + foreach $_ (@$multiline_conf_ref) { + s/\\\s*\n$//; + $joined_conf .= $_; + } + + return (split/\n/, $joined_conf); +} + + + +# Catch SIGINT. +sub catch_sigint() +{ + $SIG{INT} = 'IGNORE'; + print STDERR "\nInterrupted, cleaning up.\n"; + sleep(1); + clean_exit("interrupted by signal"); +} + + + +# Remove temporary directory and exit. +# If a non-empty string is given as argument, it will be regarded +# as an error message and we will use die() with the message instead +# of just exit(0). +sub clean_exit($) +{ + my $err_msg = shift; + + $SIG{INT} = 'DEFAULT'; + + if (defined($tmpdir) && -d "$tmpdir") { + chdir(File::Spec->rootdir()); + rmtree("$tmpdir", 0, 1); + undef($tmpdir); + } + + if (!defined($err_msg) || $err_msg eq "") { + exit(0); + } else { + chomp($err_msg); + die("\n$0: Error: $err_msg\n\nOink, oink. Exiting...\n"); + } +} + + + +#### EOF #### diff --git a/config/snort/pfsense_rules/local.rules b/config/snort/pfsense_rules/local.rules new file mode 100644 index 00000000..a9072733 --- /dev/null +++ b/config/snort/pfsense_rules/local.rules @@ -0,0 +1,7 @@ +# ---------------- +# LOCAL RULES +# ---------------- +# This file intentionally does not come with signatures. Put your local +# additions here. Pfsense first install rule. Rule edit tabe fails with out this file. +# +# \ No newline at end of file diff --git a/config/snort/pfsense_rules/pfsense_rules.tar.gz.md5 b/config/snort/pfsense_rules/pfsense_rules.tar.gz.md5 index 97a55e1d..0aede4a0 100644 --- a/config/snort/pfsense_rules/pfsense_rules.tar.gz.md5 +++ b/config/snort/pfsense_rules/pfsense_rules.tar.gz.md5 @@ -1 +1 @@ -101 \ No newline at end of file +102 \ No newline at end of file diff --git a/config/snort/pfsense_rules/rules/pfsense-voip.rules b/config/snort/pfsense_rules/rules/pfsense-voip.rules index 3142c0b6..12f2fdf2 100644 --- a/config/snort/pfsense_rules/rules/pfsense-voip.rules +++ b/config/snort/pfsense_rules/rules/pfsense-voip.rules @@ -1,11 +1,10 @@ -alert ip any any -> $HOME_NET $SIP_PROXY_PORTS (msg:"OPTIONS SIP scan"; content:"OPTIONS"; depth:7; threshold: type both , track by_src, count 30, seconds 3; sid:5000004; rev:1;) +alert ip any any -> $HOME_NET $SIP_PROXY_PORTS (msg:"OPTIONS SIP scan"; content:"OPTIONS"; depth:7; threshold: type both , track by_src, count 30, seconds 3; sid:5000001; rev:1;) # Excessive number of SIP 4xx Responses Does not work -#### alert ip any any -> $SIP_PROXY_IP $SIP_PROXY_PORTS (msg:"Excessive number of SIP 4xx Responses - possible user or password guessing attack"; pcre:"/^SIP\/2.0 4\d{2}"; threshold: type both, track by_src, count 100, seconds 60; sid:5000008; rev:1;) -alert ip any any -> $SIP_PROXY_IP $SIP_PROXY_PORTS (msg:"Ghost call attack"; content:"SIP/2.0 180"; depth:11; threshold: type both, track by_src, count 100, seconds 60; sid:5000009; rev:1;) - +#### alert ip any any -> $SIP_PROXY_IP $SIP_PROXY_PORTS (msg:"Excessive number of SIP 4xx Responses - possible user or password guessing attack"; pcre:"/^SIP\/2.0 4\d{2}"; threshold: type both, track by_src, count 100, seconds 60; sid:5000002; rev:1;) +alert ip any any -> $SIP_PROXY_IP $SIP_PROXY_PORTS (msg:"Ghost call attack"; content:"SIP/2.0 180"; depth:11; threshold: type both, track by_src, count 100, seconds 60; sid:5000003; rev:1;) # Rule for alerting of INVITE flood attack: -alert ip any any -> $SIP_PROXY_IP $SIP_PROXY_PORTS (msg:"INVITE message flooding"; content:"INVITE"; depth:6; threshold: type both , track by_src, count 100, seconds 60; sid:5000002; rev:1;) +alert ip any any -> $SIP_PROXY_IP $SIP_PROXY_PORTS (msg:"INVITE message flooding"; content:"INVITE"; depth:6; threshold: type both , track by_src, count 100, seconds 60; sid:5000004; rev:1;) # Rule for alerting of REGISTER flood attack: alert ip any any -> $SIP_PROXY_IP $SIP_PROXY_PORTS (msg:"REGISTER message flooding"; content:"REGISTER"; depth:8; threshold: type both , track by_src, count 100, seconds 60; sid:5000005; rev:1;) # Threshold rule for unauthorized responses: -alert ip any any -> $SIP_PROXY_IP $SIP_PROXY_PORTS (msg:"INVITE message flooding"; content:"SIP/2.0 401 Unauthorized"; depth:24; threshold: type both, track by_src, count 100, seconds 60; sid:5000008; rev:1;) +alert ip any any -> $SIP_PROXY_IP $SIP_PROXY_PORTS (msg:"INVITE message flooding"; content:"SIP/2.0 401 Unauthorized"; depth:24; threshold: type both, track by_src, count 100, seconds 60; sid:5000006; rev:1;) diff --git a/config/snort/snort.inc b/config/snort/snort.inc index 884f0883..ebcab85c 100755 --- a/config/snort/snort.inc +++ b/config/snort/snort.inc @@ -53,7 +53,6 @@ function sync_package_snort_reinstall() /* start snort service */ start_service("snort"); } - function sync_package_snort() { global $config, $g; @@ -89,7 +88,7 @@ function sync_package_snort() exec("/bin/cp /usr/local/etc/snort/threshold.conf-sample /usr/local/etc/snort/threshold.conf"); exec("/bin/cp /usr/local/etc/snort/unicode.map-sample /usr/local/etc/snort/unicode.map"); exec("/bin/rm -f /usr/local/etc/rc.d/snort"); - + $first = 0; $snortInterfaces = array(); /* -gtm */ @@ -137,8 +136,8 @@ function sync_package_snort() if($bpfmaxinsns) mwexec_bg("sysctl net.bpf.maxinsns={$bpfmaxinsns}"); - /* always stop snort2c before starting snort -gtm */ - $start .= "/usr/bin/killall snort2c\n"; + /* always stop barnyard2 before starting snort -gtm */ + $start .= "/usr/bin/killall barnyard2\n"; /* start a snort process for each interface -gtm */ /* Note the sleep delay. Seems to help getting mult interfaces to start -gtm */ @@ -146,36 +145,103 @@ function sync_package_snort() /* TODO; get snort to start under nologin shell */ foreach($snortInterfaces as $snortIf) { - $start .= "sleep 8\n"; + $start .= "sleep 4\n"; $start .= "snort -c /usr/local/etc/snort/snort.conf -l /var/log/snort -D -i {$snortIf} -q\n"; + /* define snortbarnyardlog_chk */ + $snortbarnyardlog_info_chk = $config['installedpackages']['snortadvanced']['config'][0]['snortbarnyardlog']; + if ($snortbarnyardlog_info_chk == on) + $start .= "\nsleep 4;barnyard2 -c /usr/local/etc/barnyard2.conf -d /var/log/snort -f snort.u2 -w /usr/local/etc/snort/barnyard2.waldo -D -q\n"; } - - /* if block offenders is checked, start snort2c */ - if($_POST['blockoffenders']) { - $start .= "\nsleep 8\n"; - $start .= "snort2c -w /var/db/whitelist -a /var/log/snort/alert\n"; - } - - $sample_before = "\nBEFORE_MEM=`top | grep Free | grep Wired | awk '{print \$10}'`\n"; - $sample_after = "\nAFTER_MEM=`top | grep Free | grep Wired | awk '{print \$10}'`\n"; - $sleep_before_final = "\necho \"Sleeping before final memory sampling...\"\nsleep 17"; - $total_free_after = "\nTOTAL_USAGE=`top | grep snort | grep -v grep | awk '{ print \$6 }'`\n"; - $echo_usage = "\necho \"Ram free BEFORE starting Snort: \${BEFORE_MEM} -- Ram free AFTER starting Snort: \${AFTER_MEM}\" -- Mode {$snort_performance} -- Snort memory usage: \$TOTAL_USAGE | logger -p daemon.info -i -t SnortStartup\n"; - + $check_if_snort_runs = "\n\tif [ \"`ls -A /usr/local/etc/snort/rules`\" ] ; then\n\techo \"rules exist\"\n\telse\n\techo \"rules DONT exist\"\n\texit 2\n\tfi \n\n\tif [ \"`pgrep -x snort`\" = \"\" ] ; then\n\t/bin/rm /tmp/snort.sh.pid\n\tfi \n\n\tif [ \"`pgrep -x snort`\" != \"\" ] ; then\n\tlogger -p daemon.info -i -t SnortStartup \"Snort already running...\"\n\t/usr/local/bin/php -f /usr/local/pkg/pf/snort_dynamic_ip_reload.php\n\texit 1\n\tfi\n\n"; + $if_snort_pid = "\nif ls /tmp/snort.sh.pid > /dev/null\nthen\n echo \"snort.sh is running\"\n exit 0\nelse\n echo \"snort.sh is not running\"\nfi\n"; + $echo_snort_sh_pid = "\necho \"snort.sh run\" > /tmp/snort.sh.pid\n"; + $echo_snort_sh_startup_log = "\necho \"snort.sh run\" >> /tmp/snort.sh_startup.log\n"; + $del_old_pids = "\nrm -f /var/run/snort_*\n"; + $sample_before = "BEFORE_MEM=`top | grep Wired | awk '{print \$12}'`\n"; + $sample_after = "\n\tAFTER_MEM=`top | grep Wired | awk '{print \$12}'`\n"; + if ($snort_performance == "ac-bnfa") + $sleep_before_final = "\necho \"Sleeping before final memory sampling...\"\nWAITSECURE=60\n"; + else + $sleep_before_final = "\necho \"Sleeping before final memory sampling...\"\nWAITSECURE=300\n"; + $sleep_before_final .= "while [ \"\$MYSNORTLOG\" = \"\" -a \$WAITSECURE -gt 0 ] ; do\n\tsleep 2\n\tMYSNORTLOG=`/usr/sbin/clog /var/log/system.log | grep snort | tail | grep 'Snort initialization completed successfully'`\n\tWAITSECURE=`expr \$WAITSECURE - 1`\ndone\n"; + $total_used_after = "TOTAL_USAGE=`top | grep snort | grep -v grep | awk '{ print \$6 }'`\n"; + $echo_usage .= $sample_after . "\t" . $total_used_after . "\techo \"Ram free BEFORE starting Snort: \$BEFORE_MEM -- Ram free AFTER starting Snort: \$AFTER_MEM -- Mode " . $snort_performance . " -- Snort memory usage: \$TOTAL_USAGE\" | logger -p daemon.info -i -t SnortStartup\n\n"; + /* write out rc.d start/stop file */ write_rcfile(array( "file" => "snort.sh", - "start" => "{$sample_before}{$start}{$sleep_before_final}{$sample_after}{$echo_usage}", - "stop" => "/usr/bin/killall snort; killall snort2c" + "start" => "{$check_if_snort_runs}{$if_snort_pid}{$echo_snort_sh_pid}{$echo_snort_sh_startup_log}{$del_old_pids}{$sample_before}{$start}{$sleep_before_final}{$echo_usage}", + "stop" => "/usr/bin/killall snort; killall barnyard2" ) ); /* create snort configuration file */ create_snort_conf(); +/* create barnyard2 configuration file */ +$snortbarnyardlog_info_chk = $config['installedpackages']['snortadvanced']['config'][0]['snortbarnyardlog']; +if ($snortbarnyardlog_info_chk == on) + create_barnyard2_conf(); + + /* snort will not start on install untill setting are set */ +if ($config['installedpackages']['snort']['config'][0]['autorulesupdate7'] != "") { /* start snort service */ conf_mount_ro(); start_service("snort"); + } +} + +/* open barnyard2.conf for writing */ +function create_barnyard2_conf() { + global $bconfig, $bg; + /* write out barnyard2_conf */ + $barnyard2_conf_text = generate_barnyard2_conf(); +// conf_mount_rw(); + $bconf = fopen("/usr/local/etc/barnyard2.conf", "w"); + if(!$bconf) { + log_error("Could not open /usr/local/etc/barnyard2.conf for writing."); + exit; + } + fwrite($bconf, $barnyard2_conf_text); + fclose($bconf); +// conf_mount_ro(); +} +/* open barnyard2.conf for writing" */ +function generate_barnyard2_conf() { + + global $config, $g; + conf_mount_rw(); + +/* define snortbarnyardlog */ +$snortbarnyardlog_database_info_chk = $config['installedpackages']['snortadvanced']['config'][0]['snortbarnyardlog_database']; + +$barnyard2_conf_text = << 0) { + unset($config['cron']['item'][$x]); + write_config(); + } + configure_cron(); + } + } + + function snort_rules_up_deinstall_cron($should_install) { + global $config, $g; + + $is_installed = false; + + if(!$config['cron']['item']) + return; + + $x=0; + foreach($config['cron']['item'] as $item) { + if (strstr($item['command'], "snort_check_for_rule_updates.php")) { + $is_installed = true; + break; + } + $x++; + } + if($is_installed == true) { + if($x > 0) { + unset($config['cron']['item'][$x]); + write_config(); + } + configure_cron(); + } + } + +snort_rm_blocked_deinstall_cron(""); +snort_rules_up_deinstall_cron(""); + + + /* Unset snort registers in conf.xml IMPORTANT snort will not start with out this */ + /* Keep this as a last step */ + unset($config['installedpackages']['snort']['config'][0]['autorulesupdate7']); + unset($config['installedpackages']['snort']['config'][0]['rm_blocked']); + write_config(); + } function generate_snort_conf() { @@ -241,15 +369,22 @@ $tcpdumplog_info_chk = $config['installedpackages']['snortadvanced']['config'][0 if ($tcpdumplog_info_chk == on) $tcpdumplog_type = "output log_tcpdump: snorttcpd.log"; -/* define snortmysqllog */ -$snortmysqllog_info_chk = $config['installedpackages']['snortadvanced']['config'][0]['snortmysqllog']; +/* define snortbarnyardlog_chk */ +$snortbarnyardlog_info_chk = $config['installedpackages']['snortadvanced']['config'][0]['snortbarnyardlog']; +if ($snortbarnyardlog_info_chk == on) + $snortbarnyardlog_type = "barnyard2 -c /usr/local/etc/barnyard2.conf -d /var/log/snort -f snort.u2 -w /usr/local/etc/snort/barnyard2.waldo -D"; /* define snortunifiedlog */ $snortunifiedlog_info_chk = $config['installedpackages']['snortadvanced']['config'][0]['snortunifiedlog']; if ($snortunifiedlog_info_chk == on) $snortunifiedlog_type = "output unified2: filename snort.u2, limit 128"; -/* define servers and ports snortdefservers */ +/* define spoink */ +$spoink_info_chk = $config['installedpackages']['snort']['config'][0]['blockoffenders7']; +if ($spoink_info_chk == on) + $spoink_type = "output alert_pf: /var/db/whitelist,snort2c"; + + /* define servers and ports snortdefservers */ /* def DNS_SERVSERS */ $def_dns_servers_info_chk = $config['installedpackages']['snortdefservers']['config'][0]['def_dns_servers']; @@ -508,6 +643,230 @@ else else $snort_performance = "ac-bnfa"; + /* set the snort block hosts time IMPORTANT snort has trouble installing if snort_rm_blocked_info_ck != "" */ + $snort_rm_blocked_info_ck = $config['installedpackages']['snort']['config'][0]['rm_blocked']; + if ($snort_rm_blocked_info_ck == "never_b") + $snort_rm_blocked_false = ""; + else + $snort_rm_blocked_false = "true"; + +if ($snort_rm_blocked_info_ck != "") { +function snort_rm_blocked_install_cron($should_install) { + global $config, $g; + + if ($g['booting']==true) + return; + + $is_installed = false; + + if(!$config['cron']['item']) + return; + + $x=0; + foreach($config['cron']['item'] as $item) { + if (strstr($item['command'], "snort2c")) { + $is_installed = true; + break; + } + $x++; + } + $snort_rm_blocked_info_ck = $config['installedpackages']['snort']['config'][0]['rm_blocked']; + if ($snort_rm_blocked_info_ck == "1h_b") { + $snort_rm_blocked_min = "*/5"; + $snort_rm_blocked_hr = "*"; + $snort_rm_blocked_mday = "*"; + $snort_rm_blocked_month = "*"; + $snort_rm_blocked_wday = "*"; + $snort_rm_blocked_expire = "3600"; + } + if ($snort_rm_blocked_info_ck == "3h_b") { + $snort_rm_blocked_min = "*/15"; + $snort_rm_blocked_hr = "*"; + $snort_rm_blocked_mday = "*"; + $snort_rm_blocked_month = "*"; + $snort_rm_blocked_wday = "*"; + $snort_rm_blocked_expire = "10800"; + } + if ($snort_rm_blocked_info_ck == "6h_b") { + $snort_rm_blocked_min = "*/30"; + $snort_rm_blocked_hr = "*"; + $snort_rm_blocked_mday = "*"; + $snort_rm_blocked_month = "*"; + $snort_rm_blocked_wday = "*"; + $snort_rm_blocked_expire = "21600"; + } + if ($snort_rm_blocked_info_ck == "12h_b") { + $snort_rm_blocked_min = "2"; + $snort_rm_blocked_hr = "*/1"; + $snort_rm_blocked_mday = "*"; + $snort_rm_blocked_month = "*"; + $snort_rm_blocked_wday = "*"; + $snort_rm_blocked_expire = "43200"; + } + if ($snort_rm_blocked_info_ck == "1d_b") { + $snort_rm_blocked_min = "2"; + $snort_rm_blocked_hr = "*/2"; + $snort_rm_blocked_mday = "*"; + $snort_rm_blocked_month = "*"; + $snort_rm_blocked_wday = "*"; + $snort_rm_blocked_expire = "86400"; + } + if ($snort_rm_blocked_info_ck == "4d_b") { + $snort_rm_blocked_min = "2"; + $snort_rm_blocked_hr = "*/8"; + $snort_rm_blocked_mday = "*"; + $snort_rm_blocked_month = "*"; + $snort_rm_blocked_wday = "*"; + $snort_rm_blocked_expire = "345600"; + } + if ($snort_rm_blocked_info_ck == "7d_b") { + $snort_rm_blocked_min = "2"; + $snort_rm_blocked_hr = "*/14"; + $snort_rm_blocked_mday = "*"; + $snort_rm_blocked_month = "*"; + $snort_rm_blocked_wday = "*"; + $snort_rm_blocked_expire = "604800"; + } + if ($snort_rm_blocked_info_ck == "28d_b") { + $snort_rm_blocked_min = "2"; + $snort_rm_blocked_hr = "0"; + $snort_rm_blocked_mday = "*/2"; + $snort_rm_blocked_month = "*"; + $snort_rm_blocked_wday = "*"; + $snort_rm_blocked_expire = "2419200"; + } + switch($should_install) { + case true: + if(!$is_installed) { + $cron_item = array(); + $cron_item['minute'] = "$snort_rm_blocked_min"; + $cron_item['hour'] = "$snort_rm_blocked_hr"; + $cron_item['mday'] = "$snort_rm_blocked_mday"; + $cron_item['month'] = "$snort_rm_blocked_month"; + $cron_item['wday'] = "$snort_rm_blocked_wday"; + $cron_item['who'] = "root"; + $cron_item['command'] = "/usr/bin/nice -n20 /usr/local/sbin/expiretable -t $snort_rm_blocked_expire snort2c"; + $config['cron']['item'][] = $cron_item; + write_config("Installed 15 minute filter reload for Time Based Rules"); + configure_cron(); + } + break; + case false: + if($is_installed == true) { + if($x > 0) { + unset($config['cron']['item'][$x]); + write_config(); + } + configure_cron(); + } + break; + } + } + snort_rm_blocked_install_cron(""); + snort_rm_blocked_install_cron($snort_rm_blocked_false); +} + + /* set the snort rules update time */ + $snort_rules_up_info_ck = $config['installedpackages']['snort']['config'][0]['autorulesupdate7']; + if ($snort_rules_up_info_ck == "never_up") + $snort_rules_up_false = ""; + else + $snort_rules_up_false = "true"; + +if ($snort_rules_up_info_ck != "") { +function snort_rules_up_install_cron($should_install) { + global $config, $g; + + if ($g['booting']==true) + return; + + $is_installed = false; + + if(!$config['cron']['item']) + return; + + $x=0; + foreach($config['cron']['item'] as $item) { + if (strstr($item['command'], "snort_check_for_rule_updates.php")) { + $is_installed = true; + break; + } + $x++; + } + $snort_rules_up_info_ck = $config['installedpackages']['snort']['config'][0]['autorulesupdate7']; + if ($snort_rules_up_info_ck == "6h_up") { + $snort_rules_up_min = "3"; + $snort_rules_up_hr = "*/6"; + $snort_rules_up_mday = "*"; + $snort_rules_up_month = "*"; + $snort_rules_up_wday = "*"; + } + if ($snort_rules_up_info_ck == "12h_up") { + $snort_rules_up_min = "3"; + $snort_rules_up_hr = "*/12"; + $snort_rules_up_mday = "*"; + $snort_rules_up_month = "*"; + $snort_rules_up_wday = "*"; + } + if ($snort_rules_up_info_ck == "1d_up") { + $snort_rules_up_min = "3"; + $snort_rules_up_hr = "0"; + $snort_rules_up_mday = "*/1"; + $snort_rules_up_month = "*"; + $snort_rules_up_wday = "*"; + } + if ($snort_rules_up_info_ck == "4d_up") { + $snort_rules_up_min = "3"; + $snort_rules_up_hr = "0"; + $snort_rules_up_mday = "*/4"; + $snort_rules_up_month = "*"; + $snort_rules_up_wday = "*"; + } + if ($snort_rules_up_info_ck == "7d_up") { + $snort_rules_up_min = "3"; + $snort_rules_up_hr = "0"; + $snort_rules_up_mday = "*/7"; + $snort_rules_up_month = "*"; + $snort_rules_up_wday = "*"; + } + if ($snort_rules_up_info_ck == "28d_up") { + $snort_rules_up_min = "3"; + $snort_rules_up_hr = "0"; + $snort_rules_up_mday = "*/28"; + $snort_rules_up_month = "*"; + $snort_rules_up_wday = "*"; + } + switch($should_install) { + case true: + if(!$is_installed) { + $cron_item = array(); + $cron_item['minute'] = "$snort_rules_up_min"; + $cron_item['hour'] = "$snort_rules_up_hr"; + $cron_item['mday'] = "$snort_rules_up_mday"; + $cron_item['month'] = "$snort_rules_up_month"; + $cron_item['wday'] = "$snort_rules_up_wday"; + $cron_item['who'] = "root"; + $cron_item['command'] = "/usr/bin/nice -n20 /usr/local/bin/php -f /usr/local/pkg/snort_check_for_rule_updates.php >> /usr/local/etc/snort_bkup/snort_update.log"; + $config['cron']['item'][] = $cron_item; + write_config("Installed 15 minute filter reload for Time Based Rules"); + configure_cron(); + } + break; + case false: + if($is_installed == true) { + if($x > 0) { + unset($config['cron']['item'][$x]); + write_config(); + } + configure_cron(); + } + break; + } + } + snort_rules_up_install_cron(""); + snort_rules_up_install_cron($snort_rules_up_false); +} + /* open snort2c's whitelist for writing */ $whitelist = fopen("/var/db/whitelist", "w"); if(!$whitelist) { @@ -592,11 +951,11 @@ else /* should we whitelist vpns? */ $whitelistvpns = $config['installedpackages']['snort']['config'][0]['whitelistvpns']; - /* grab a list of vpns and whitelist if user desires */ + /* grab a list of vpns and whitelist if user desires added by nestorfish 954 */ if($whitelistvpns) { $vpns_list = get_vpns_list(); $whitelist_vpns = split(" ", $vpns_list); - foreach($whitelist_split as $wl) + foreach($whitelist_vpns as $wl) if(trim($wl)) fwrite($whitelist, trim($wl) . "\n"); } @@ -772,14 +1131,15 @@ preprocessor frag3_engine: policy first preprocessor frag3_engine: policy bsd detect_anomalies preprocessor stream5_global: max_tcp 8192, track_tcp yes, \ -track_udp yes, track_icmp yes +track_udp yes +# track_icmp yes preprocessor stream5_tcp: bind_to any, policy windows preprocessor stream5_tcp: bind_to any, policy linux preprocessor stream5_tcp: bind_to any, policy vista preprocessor stream5_tcp: bind_to any, policy macos preprocessor stream5_tcp: policy BSD, ports both all, use_static_footprint_sizes preprocessor stream5_udp -preprocessor stream5_icmp +# preprocessor stream5_icmp ########################## # @@ -799,7 +1159,7 @@ preprocessor perfmonitor: time 300 file /var/log/snort/snort.stats pktcnt 10000 preprocessor http_inspect: global iis_unicode_map unicode.map 1252 preprocessor http_inspect_server: server default \ - ports { 80 8080 3128 } \ + ports { 80 8080 } \ no_alerts \ non_strict \ non_rfc_char { 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 } \ @@ -964,6 +1324,7 @@ $alertsystemlog_type $tcpdumplog_type $snortmysqllog_info_chk $snortunifiedlog_type +$spoink_type ################# # diff --git a/config/snort/snort.xml b/config/snort/snort.xml index f8e35c28..2370be0e 100644 --- a/config/snort/snort.xml +++ b/config/snort/snort.xml @@ -46,8 +46,8 @@ Describe your package requirements here Currently there are no FAQ items provided. Snort - 2.8.4.1 - Services: Snort 2.8.4.1 pkg v. 1.5 + 2.8.4.1_1 + Services: Snort 2.8.4.1_2 pkg v. 1.6 /usr/local/pkg/snort.inc Snort @@ -59,6 +59,7 @@ snort snort.sh snort + Snort is the most widely deployed IDS/IPS technology worldwide.. @@ -106,82 +107,87 @@ /usr/local/pkg/ 077 - http://www.pfsense.com/packages/config/snort/snort.inc + http://www.pfsense.com/packages/config/snort-dev/snort.inc /usr/local/bin/ 077 - http://www.pfsense.com/packages/config/snort/bin/snort2c + http://www.pfsense.com/packages/config/snort-dev/bin/barnyard2 /usr/local/bin/ 077 - http://www.pfsense.com/packages/config/snort/bin/oinkmaster_contrib/create-sidmap.pl + http://www.pfsense.com/packages/config/snort-dev/bin/oinkmaster_contrib/create-sidmap.pl /usr/local/bin/ 077 - http://www.pfsense.com/packages/config/snort/bin/mons2c - + http://www.pfsense.com/packages/config/snort-dev/bin/oinkmaster_contrib/oinkmaster.pl + /usr/local/www/ 077 - http://www.pfsense.com/packages/config/snort/snort_download_rules.php + http://www.pfsense.com/packages/config/snort-dev/snort_download_rules.php /usr/local/www/ 077 - http://www.pfsense.com/packages/config/snort/snort_rules.php + http://www.pfsense.com/packages/config/snort-dev/snort_rules.php /usr/local/www/ 077 - http://www.pfsense.com/packages/config/snort/snort_rules_edit.php + http://www.pfsense.com/packages/config/snort-dev/snort_rules_edit.php /usr/local/www/ 077 - http://www.pfsense.com/packages/config/snort/snort_rulesets.php + http://www.pfsense.com/packages/config/snort-dev/snort_rulesets.php /usr/local/pkg/ 077 - http://www.pfsense.com/packages/config/snort/snort_whitelist.xml + http://www.pfsense.com/packages/config/snort-dev/snort_whitelist.xml /usr/local/www/ 077 - http://www.pfsense.com/packages/config/snort/snort_blocked.php + http://www.pfsense.com/packages/config/snort-dev/snort_blocked.php /usr/local/pkg/ 077 - http://www.pfsense.com/packages/config/snort/snort_check_for_rule_updates.php + http://www.pfsense.com/packages/config/snort-dev/snort_check_for_rule_updates.php /usr/local/www/ 077 - http://www.pfsense.com/packages/config/snort/snort_alerts.php + http://www.pfsense.com/packages/config/snort-dev/snort_alerts.php + + + /usr/local/pkg/pf/ + 077 + http://www.pfsense.com/packages/config/snort-dev/snort_dynamic_ip_reload.php /usr/local/pkg/ 077 - http://www.pfsense.com/packages/config/snort/snort_dynamic_ip_reload.php + http://www.pfsense.com/packages/config/snort-dev/snort_advanced.xml /usr/local/pkg/ 077 - http://www.pfsense.com/packages/config/snort/snort_advanced.xml + http://www.pfsense.com/packages/config/snort-dev/snort_define_servers.xml /usr/local/pkg/ 077 - http://www.pfsense.com/packages/config/snort/snort_define_servers.xml + http://www.pfsense.com/packages/config/snort-dev/snort_threshold.xml /usr/local/pkg/ 077 - http://www.pfsense.com/packages/config/snort/snort_threshold.xml + http://www.pfsense.com/packages/config/snort-dev/pfsense_rules/local.rules @@ -194,7 +200,7 @@ true - Performance + Memory Performance performance Lowmem and ac-bnfa are recommended for low end systems, Ac: high memory, best performance, ac-std: moderate memory,high performance, acs: small memory, moderateperformance, ac-banded: small memory,moderate performance, ac-sparsebands: small memory, high performance. select @@ -246,16 +252,92 @@ Block offenders - blockoffenders + blockoffenders7 Checking this option will automatically block hosts that generate a snort alert. checkbox 60 + + Remove blocked hosts every + rm_blocked + Please select the amount of time hosts are blocked + select + + + + + + + + + + + + + + Update rules automatically - automaticrulesupdate - Checking this option will automatically check for and update rules once a week from snort.org. - checkbox + autorulesupdate7 + Please select the update times for rules. + select + + + + + + + + + Whitelist VPNs automatically @@ -275,12 +357,6 @@ Checking this option will automatically associate the blocked reason from the snort alerts file. checkbox - - Sync Snort configuration to secondary cluster members - syncxmlrpc - Checking this option will automatically sync the snort configuration via XMLRPC to CARP cluster members. - checkbox - Install emergingthreats rules. emergingthreats @@ -288,15 +364,15 @@ checkbox - - sync_package_snort(); + + sync_package_snort_reinstall(); snort_deinstall(); - + \ No newline at end of file diff --git a/config/snort/snort_advanced.xml b/config/snort/snort_advanced.xml index fa7f7d0f..6e81123f 100644 --- a/config/snort/snort_advanced.xml +++ b/config/snort/snort_advanced.xml @@ -153,9 +153,15 @@ Snort will log packets to a tcpdump-formatted file. The file then can be analyzed by a wireshark type of application. WARNING: File may become large. checkbox + + Enable Barnyard2. + snortbarnyardlog + This will enable barnyard2 in the snort package. You will also have to set the database credentials. + checkbox + - Log to a mysql database. - snortmysqllog + Barnyard2 Log Mysql Database. + snortbarnyardlog_database Example: output database: log, mysql, dbname=snort user=snort host=localhost password=xyz input 101 @@ -164,7 +170,7 @@ Log Alerts to a snort unified file. snortunifiedlog - Snort will log Alerts to a file in the UNIFIED format. + Snort will log Alerts to a file in the UNIFIED2 format. This is a requirement barnyard2. checkbox diff --git a/config/snort/snort_check_for_rule_updates.php b/config/snort/snort_check_for_rule_updates.php index 90df3bc7..4430c4a2 100644 --- a/config/snort/snort_check_for_rule_updates.php +++ b/config/snort/snort_check_for_rule_updates.php @@ -1,9 +1,7 @@ -#!/usr/local/bin/php -f -/* if we are not a CARP cluster master, sleep for a random - * amount of time allowing for other members to download the configuration - */ -if(!$password) { - $sleepietime = rand(5,700); - sleep($sleepietime); -} -$last_ruleset_download = $config['installedpackages']['snort']['last_ruleset_download']; + $date2ts or !$last_ruleset_download) { - log_error("There is a new set of Snort rules posted. Downloading..."); - if(!$oinkid) { - log_error("Oinkid is not defined. We cannot automatically update the ruleset."); - echo "Oinkid is not defined. We cannot automatically update the ruleset."; - exit; - } - echo "Downloading snort rule updates..."; - /* setup some variables */ +ini_set("memory_limit","125M"); + +/* send current buffer */ +ob_flush(); + +/* define oinkid */ +if($config['installedpackages']['snort']) + $oinkid = $config['installedpackages']['snort']['config'][0]['oinkmastercode']; + +/* if missing oinkid exit */ +if(!$oinkid) { + echo "Please add you oink code\n"; + exit; +} + +/* premium_subscriber check */ +//unset($config['installedpackages']['snort']['config'][0]['subscriber']); +//write_config(); +$premium_subscriber_chk = $config['installedpackages']['snort']['config'][0]['subscriber']; + +if ($premium_subscriber_chk === on) { + $premium_subscriber = "_s"; +}else{ $premium_subscriber = ""; +} - /* Snort version */ - $snort_version = "2.8"; - - /* Are we using the premium subscriber subscription? */ - if($config['installedpackages']['snortadvanced']['config'][0]['subscriber']) { - // http://www.snort.org/pub-bin/downloads.cgi/Download/sub_rules/snortrules-snapshot-CURRENT_s.tar.gz.md5 - $premium_subscriber = "_s"; - $snort_download_prefix = "http://www.snort.org/pub-bin/oinkmaster.cgi"; - } else { - // http://www.snort.org/pub-bin/downloads.cgi/Download/vrt_os/snortrules-snapshot-CURRENT.tar.gz.md5 - $premium_subscriber = ""; - $snort_download_prefix = "http://www.snort.org/pub-bin/oinkmaster.cgi"; - } +$premium_url_chk = $config['installedpackages']['snort']['config'][0]['subscriber']; +if ($premium_url_chk === on) { + $premium_url = "sub-rules"; +}else{ + $premium_url = "reg-rules"; +} + +/* send current buffer */ +ob_flush(); + +/* remove old $tmpfname files */ +if (file_exists("{$tmpfname}")) { + exec("/bin/rm -r {$tmpfname}"); + apc_clear_cache(); +} + +/* send current buffer */ +ob_flush(); + +/* If tmp dir does not exist create it */ +if (file_exists($tmpfname)) { + echo "The directory tmp exists...\n"; +} else { + mkdir("{$tmpfname}", 700); +} + +/* download md5 sig from snort.org */ +if (file_exists("{$tmpfname}/{$snort_filename_md5}")) { + echo "md5 temp file exists...\n"; +} else { + echo "Downloading md5 file...\n"; + ini_set('user_agent','Mozilla/4.0 (compatible; MSIE 6.0)'); + $image = @file_get_contents("http://dl.snort.org/{$premium_url}/snortrules-snapshot-2.8{$premium_subscriber}.tar.gz.md5?oink_code={$oinkid}"); +// $image = @file_get_contents("http://www.mtest.local/pub-bin/oinkmaster.cgi/{$oinkid}/snortrules-snapshot-2.8{$premium_subscriber}.tar.gz.md5"); + $f = fopen("{$tmpfname}/snortrules-snapshot-2.8.tar.gz.md5", 'w'); + fwrite($f, $image); + fclose($f); + echo "Done. downloading md5\n"; +} + +/* download md5 sig from emergingthreats.net */ +$emergingthreats_url_chk = $config['installedpackages']['snort']['config'][0]['emergingthreats']; +if ($emergingthreats_url_chk == on) { + echo "Downloading md5 file...\n"; + ini_set('user_agent','Mozilla/4.0 (compatible; MSIE 6.0)'); + $image = @file_get_contents("http://www.emergingthreats.net/version.txt"); +// $image = @file_get_contents("http://www.mtest.local/pub-bin/oinkmaster.cgi/{$oinkid}/version.txt"); + $f = fopen("{$tmpfname}/version.txt", 'w'); + fwrite($f, $image); + fclose($f); + echo "Done. downloading md5\n"; +} + +/* download md5 sig from pfsense.org */ +if (file_exists("{$tmpfname}/{$pfsense_rules_filename_md5}")) { + echo "md5 temp file exists...\n"; +} else { + echo "Downloading pfsense md5 file...\n"; + ini_set('user_agent','Mozilla/4.0 (compatible; MSIE 6.0)'); + $image = @file_get_contents("http://www.pfsense.com/packages/config/snort/pfsense_rules/pfsense_rules.tar.gz.md5"); +// $image = @file_get_contents("http://www.mtest.local/pub-bin/oinkmaster.cgi/{$oinkid}/pfsense_rules.tar.gz.md5"); + $f = fopen("{$tmpfname}/pfsense_rules.tar.gz.md5", 'w'); + fwrite($f, $image); + fclose($f); + echo "Done. downloading md5\n"; +} - /* Set snort rules download filename */ - $snort_filename = "snortrules-snapshot-{$snort_version}{$premium_subscriber}.tar.gz"; - $snort_filename_md5 = "snortrules-snapshot-{$snort_version}{$premium_subscriber}.tar.gz.md5"; +/* Time stamps define */ +$last_md5_download = $config['installedpackages']['snort']['last_md5_download']; +$last_rules_install = $config['installedpackages']['snort']['last_rules_install']; - /* multi user system, request new filename and create directory */ - $tmpfname = tempnam("/tmp", "snortRules"); - exec("/bin/rm -rf {$tmpfname};/bin/mkdir -p {$tmpfname}"); +/* If md5 file is empty wait 15min exit */ +if (0 == filesize("{$tmpfname}/snortrules-snapshot-2.8.tar.gz.md5")){ + echo "Please wait... You may only check for New Rules every 15 minutes...\n"; + echo "Rules are released every month from snort.org. You may download the Rules at any time.\n"; + exit(0); +} - /* download snort rules */ - exec("fetch -q -o {$tmpfname}/{$snort_filename} $dl"); - verify_downloaded_file($tmpfname . "/{$snort_filename}"); +/* If emergingthreats md5 file is empty wait 15min exit not needed */ - /* download snort rules md5 file */ - $static_output = gettext("Downloading current snort rules md5... "); - exec("fetch -q -o {$tmpfname}/{$snort_filename_md5} $dl_md5"); - verify_downloaded_file($tmpfname . "/{$snort_filename_md5}"); +/* If pfsense md5 file is empty wait 15min exit */ +if (0 == filesize("{$tmpfname}/$pfsense_rules_filename_md5")){ + echo "Please wait... You may only check for New Pfsense Rules every 15 minutes...\n"; + echo "Rules are released to support Pfsense packages.\n"; + exit(0); +} - /* verify downloaded rules signature */ - verify_snort_rules_md5($tmpfname); +/* Check if were up to date snort.org */ +if (file_exists("{$snortdir}/snortrules-snapshot-2.8.tar.gz.md5")){ +$md5_check_new_parse = file_get_contents("{$tmpfname}/{$snort_filename_md5}"); +$md5_check_new = `/bin/echo "{$md5_check_new_parse}" | /usr/bin/awk '{ print $1 }'`; +$md5_check_old_parse = file_get_contents("{$snortdir}/{$snort_filename_md5}"); +$md5_check_old = `/bin/echo "{$md5_check_old_parse}" | /usr/bin/awk '{ print $1 }'`; +/* Write out time of last sucsessful md5 to cache */ +$config['installedpackages']['snort']['last_md5_download'] = date("Y-M-jS-h:i-A"); +write_config(); +if ($md5_check_new == $md5_check_old) { + echo "Your rules are up to date...\n"; + echo "You may start Snort now, check update.\n"; + $snort_md5_check_ok = on; + } +} - /* extract rules */ - extract_snort_rules_md5($tmpfname); +/* Check if were up to date emergingthreats.net */ +$emergingthreats_url_chk = $config['installedpackages']['snort']['config'][0]['emergingthreats']; +if ($emergingthreats_url_chk == on) { +if (file_exists("{$snortdir}/version.txt")){ +$emerg_md5_check_new_parse = file_get_contents("{$tmpfname}/version.txt"); +$emerg_md5_check_new = `/bin/echo "{$emerg_md5_check_new_parse}" | /usr/bin/awk '{ print $1 }'`; +$emerg_md5_check_old_parse = file_get_contents("{$snortdir}/version.txt"); +$emerg_md5_check_old = `/bin/echo "{$emerg_md5_check_old_parse}" | /usr/bin/awk '{ print $1 }'`; +/* Write out time of last sucsessful md5 to cache */ +$config['installedpackages']['snort']['last_md5_download'] = date("Y-M-jS-h:i-A"); +write_config(); +if ($emerg_md5_check_new == $emerg_md5_check_old) { + echo "Your emergingthreats rules are up to date...\n"; + echo "You may start Snort now, check update.\n"; + $emerg_md5_check_chk_ok = on; + } + } +} - $config['installedpackages']['snort']['last_ruleset_download'] = date("Y-m-d"); - write_config(); +/* Check if were up to date pfsense.org */ +if (file_exists("{$snortdir}/$pfsense_rules_filename_md5")){ +$pfsense_md5_check_new_parse = file_get_contents("{$tmpfname}/{$snort_filename_md5}"); +$pfsense_md5_check_new = `/bin/echo "{$pfsense_md5_check_new_parse}" | /usr/bin/awk '{ print $1 }'`; +$pfsense_md5_check_old_parse = file_get_contents("{$snortdir}/{$snort_filename_md5}"); +$pfsense_md5_check_old = `/bin/echo "{$md5_check_old_parse}" | /usr/bin/awk '{ print $1 }'`; +if ($pfsense_md5_check_new == $pfsense_md5_check_old) { + $pfsense_md5_check_ok = on; + } +} + +/* Make Clean Snort Directory emergingthreats not checked */ +if ($snort_md5_check_ok == on && $emergingthreats_url_chk != on) { + echo "Cleaning the snort Directory...\n"; + echo "removing...\n"; + exec("/bin/rm {$snortdir}/rules/emerging*\n"); + exec("/bin/rm {$snortdir}/version.txt"); + echo "Done making cleaning emrg direcory.\n"; +} +/* Check if were up to date exits */ +if ($snort_md5_check_ok == on && $emerg_md5_check_chk_ok == on && $pfsense_md5_check_ok == on) { + echo "Your rules are up to date...\n"; + echo "You may start Snort now...\n"; + exit(0); +} + +if ($snort_md5_check_ok == on && $pfsense_md5_check_ok == on && $emergingthreats_url_chk != on) { + echo "Your rules are up to date...\n"; + echo "You may start Snort now...\n"; + exit(0); +} + +/* You are Not Up to date, always stop snort when updating rules for low end machines */; +echo "You are NOT up to date...\n"; +echo "Stopping Snort service...\n"; +$chk_if_snort_up = exec("pgrep -x snort"); +if ($chk_if_snort_up != "") { + exec("/usr/bin/touch /tmp/snort_download_halt.pid"); stop_service("snort"); sleep(2); - start_service("snort"); +} + +/* download snortrules file */ +if ($snort_md5_check_ok != on) { +if (file_exists("{$tmpfname}/{$snort_filename}")) { + echo "Snortrule tar file exists...\n"; +} else { + + echo "There is a new set of Snort rules posted. Downloading...\n"; + echo "May take 4 to 10 min...\n"; + ini_set('user_agent','Mozilla/4.0 (compatible; MSIE 6.0)'); + $image = @file_get_contents("http://dl.snort.org/{$premium_url}/snortrules-snapshot-2.8{$premium_subscriber}.tar.gz?oink_code={$oinkid}"); +// $image = @file_get_contents("http://www.mtest.local/pub-bin/oinkmaster.cgi/{$oinkid}/snortrules-snapshot-2.8{$premium_subscriber}.tar.gz"); + $f = fopen("{$tmpfname}/snortrules-snapshot-2.8.tar.gz", 'w'); + fwrite($f, $image); + fclose($f); + echo "Done downloading rules file.\n"; + if (150000 > filesize("{$tmpfname}/$snort_filename")){ + echo "Error with the snort rules download...\n"; + echo "Snort rules file downloaded failed...\n"; + exit(0); + } + } +} + +/* download emergingthreats rules file */ +if ($emergingthreats_url_chk == on) { +if ($emerg_md5_check_chk_ok != on) { +if (file_exists("{$tmpfname}/{$emergingthreats_filename}")) { + echo "Emergingthreats tar file exists...\n"; +} else { + echo "There is a new set of Emergingthreats rules posted. Downloading...\n"; + echo "May take 4 to 10 min...\n"; + ini_set('user_agent','Mozilla/4.0 (compatible; MSIE 6.0)'); + $image = @file_get_contents("http://www.emergingthreats.net/rules/emerging.rules.tar.gz"); +// $image = @file_get_contents("http://www.emergingthreats.net/rules/emerging.rules.tar.gz"); + $f = fopen("{$tmpfname}/emerging.rules.tar.gz", 'w'); + fwrite($f, $image); + fclose($f); + echo "Done downloading Emergingthreats rules file.\n"; + } + } + } + +/* download pfsense rules file */ +if ($pfsense_md5_check_ok != on) { +if (file_exists("{$tmpfname}/{$pfsense_rules_filename}")) { + echo "Snortrule tar file exists...\n"; +} else { + + echo "There is a new set of Pfsense rules posted. Downloading...\n"; + echo "May take 4 to 10 min...\n"; + ini_set('user_agent','Mozilla/4.0 (compatible; MSIE 6.0)'); + $image = @file_get_contents("http://www.pfsense.com/packages/config/snort/pfsense_rules/pfsense_rules.tar.gz"); +// $image = @file_get_contents("http://www.mtest.local/pub-bin/oinkmaster.cgi/{$oinkid}/pfsense_rules.tar.gz"); + $f = fopen("{$tmpfname}/pfsense_rules.tar.gz", 'w'); + fwrite($f, $image); + fclose($f); + echo "Done downloading rules file.\n"; + } +} + +/* Untar snort rules file individually to help people with low system specs */ +if ($snort_md5_check_ok != on) { +if (file_exists("{$tmpfname}/{$snort_filename}")) { + echo "Extracting rules...\n"; + echo "May take a while...\n"; + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} etc/"); + exec("`/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/precompiled/FreeBSD-7.0/i386/2.8.4/*`"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/bad-traffic.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/chat.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/dos.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/exploit.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/imap.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/misc.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/multimedia.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/netbios.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/nntp.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/p2p.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/smtp.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/sql.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/web-client.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/web-misc.rules/"); + echo "Done extracting Rules.\n"; +} else { + echo "The Download rules file missing...\n"; + echo "Error rules extracting failed...\n"; + exit(0); + } +} + +/* Untar emergingthreats rules to tmp */ +if ($emergingthreats_url_chk == on) { +if ($emerg_md5_check_chk_ok != on) { +if (file_exists("{$tmpfname}/{$emergingthreats_filename}")) { + echo "Extracting rules...\n"; + echo "May take a while...\n"; + exec("/usr/bin/tar xzf {$tmpfname}/{$emergingthreats_filename} -C {$snortdir} rules/"); + } + } +} + +/* Untar Pfsense rules to tmp */ +if ($pfsense_md5_check_ok != on) { +if (file_exists("{$tmpfname}/{$pfsense_rules_filename}")) { + echo "Extracting Pfsense rules...\n"; + echo "May take a while...\n"; + exec("/usr/bin/tar xzf {$tmpfname}/{$pfsense_rules_filename} -C {$snortdir} rules/"); + } +} + +/* Untar snort signatures */ +if ($snort_md5_check_ok != on) { +if (file_exists("{$tmpfname}/{$snort_filename}")) { +$signature_info_chk = $config['installedpackages']['snortadvanced']['config'][0]['signatureinfo']; +if ($premium_url_chk == on) { + echo "Extracting Signatures...\n"; + echo "May take a while...\n"; + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} doc/signatures/"); + echo "Done extracting Signatures.\n"; + } + } +} + +/* Make Clean Snort Directory */ +//if ($snort_md5_check_ok != on && $emerg_md5_check_chk_ok != on && $pfsense_md5_check_ok != on) { +//if (file_exists("{$snortdir}/rules")) { +// echo "Cleaning the snort Directory...\n"; +// echo "removing...\n"; +// exec("/bin/mkdir -p {$snortdir}"); +// exec("/bin/mkdir -p {$snortdir}/rules"); +// exec("/bin/mkdir -p {$snortdir}/signatures"); +// exec("/bin/rm {$snortdir}/*"); +// exec("/bin/rm {$snortdir}/rules/*"); +// exec("/bin/rm {$snortdir_wan}/*"); +// exec("/bin/rm {$snortdir_wan}/rules/*"); +// exec("/bin/rm /usr/local/lib/snort/dynamicrules/*"); +//} else { +// echo "Making Snort Directory...\n"; +// echo "should be fast...\n"; +// exec("/bin/mkdir {$snortdir}"); +// exec("/bin/mkdir {$snortdir}/rules"); +// exec("/bin/rm {$snortdir_wan}/\*"); +// exec("/bin/rm {$snortdir_wan}/rules/*"); +// exec("/bin/rm /usr/local/lib/snort/dynamicrules/\*"); +// echo "Done making snort direcory.\n"; +// } +//} + +/* Copy so_rules dir to snort lib dir */ +if ($snort_md5_check_ok != on) { +if (file_exists("{$snortdir}/so_rules/precompiled/FreeBSD-7.0/i386/2.8.4/")) { + echo "Copying so_rules...\n"; + echo "May take a while...\n"; + sleep(2); + exec("`/bin/cp -f {$snortdir}/so_rules/precompiled/FreeBSD-7.0/i386/2.8.4/* /usr/local/lib/snort/dynamicrules/`"); + exec("/bin/cp {$snortdir}/so_rules/bad-traffic.rules {$snortdir}/rules/bad-traffic.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/chat.rules {$snortdir}/rules/chat.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/dos.rules {$snortdir}/rules/dos.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/exploit.rules {$snortdir}/rules/exploit.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/imap.rules {$snortdir}/rules/imap.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/misc.rules {$snortdir}/rules/misc.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/multimedia.rules {$snortdir}/rules/multimedia.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/netbios.rules {$snortdir}/rules/netbios.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/nntp.rules {$snortdir}/rules/nntp.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/p2p.rules {$snortdir}/rules/p2p.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/smtp.rules {$snortdir}/rules/smtp.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/sql.rules {$snortdir}/rules/sql.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/web-client.rules {$snortdir}/rules/web-client.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/web-misc.rules {$snortdir}/rules/web-misc.so.rules"); + exec("/bin/rm -r {$snortdir}/so_rules"); + echo "Done copying so_rules.\n"; +} else { + echo "Directory so_rules does not exist...\n"; + echo "Error copping so_rules...\n"; + exit(0); + } +} + +/* enable disable setting will carry over with updates */ +/* TODO carry signature changes with the updates */ +if ($snort_md5_check_ok != on || $emerg_md5_check_chk_ok != on || $pfsense_md5_check_ok != on) { + +if (!empty($config['installedpackages']['snort']['rule_sid_on'])) { +$enabled_sid_on = $config['installedpackages']['snort']['rule_sid_on']; +$enabled_sid_on_array = split('\|\|', $enabled_sid_on); +foreach($enabled_sid_on_array as $enabled_item_on) +$selected_sid_on_sections .= "$enabled_item_on\n"; + } + +if (!empty($config['installedpackages']['snort']['rule_sid_off'])) { +$enabled_sid_off = $config['installedpackages']['snort']['rule_sid_off']; +$enabled_sid_off_array = split('\|\|', $enabled_sid_off); +foreach($enabled_sid_off_array as $enabled_item_off) +$selected_sid_off_sections .= "$enabled_item_off\n"; + } + +$snort_sid_text = << /usr/local/etc/snort_bkup/gen-msg.map"); + +/* Run oinkmaster to snort_wan and cp configs */ +/* If oinkmaster is not needed cp rules normally */ +/* TODO add per interface settings here */ +if ($snort_md5_check_ok != on || $emerg_md5_check_chk_ok != on || $pfsense_md5_check_ok != on) { + + if (empty($config['installedpackages']['snort']['rule_sid_on']) || empty($config['installedpackages']['snort']['rule_sid_off'])) { +echo "Your first set of rules are being copied...\n"; +echo "May take a while...\n"; + + exec("/bin/cp {$snortdir}/rules/* {$snortdir_wan}/rules/"); + exec("/bin/cp {$snortdir}/classification.config {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/gen-msg.map {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/generators {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/reference.config {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/sid {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/sid-msg.map {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/snort.conf {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/threshold.conf {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/unicode.map {$snortdir_wan}"); + +} else { + echo "Your enable and disable changes are being applied to your fresh set of rules...\n"; + echo "May take a while...\n"; + exec("/bin/cp {$snortdir}/classification.config {$snortdir_wan}"); +// exec("/bin/cp {$snortdir}/gen-msg.map {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/generators {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/reference.config {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/sid {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/sid-msg.map {$snortdir_wan}"); +// exec("/bin/cp {$snortdir}/snort.conf {$snortdir_wan}"); +// exec("/bin/cp {$snortdir}/threshold.conf {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/unicode.map {$snortdir_wan}"); + + /* oinkmaster.pl will convert saved changes for the new updates then we have to change #alert to # alert for the gui */ + /* might have to add a sleep for 3sec for flash drives or old drives */ + exec("/usr/local/bin/perl /usr/local/bin/oinkmaster.pl -C /usr/local/etc/snort_bkup/oinkmaster.conf -o /usr/local/etc/snort/rules > /usr/local/etc/snort_bkup/oinkmaster.log"); + exec("/usr/local/bin/perl -pi -e 's/#alert/# alert/g' /usr/local/etc/snort/rules/*.rules"); + exec("/usr/local/bin/perl -pi -e 's/##alert/# alert/g' /usr/local/etc/snort/rules/*.rules"); + exec("/usr/local/bin/perl -pi -e 's/## alert/# alert/g' /usr/local/etc/snort/rules/*.rules"); + + } +} + +/* remove old $tmpfname files */ +if (file_exists("{$tmpfname}")) { + echo "Cleaning up...\n"; + exec("/bin/rm -r /tmp/snort_rules_up"); +} + +/* php code to flush out cache some people are reportting missing files this might help */ +sleep(5); +apc_clear_cache(); +exec("/bin/sync ;/bin/sync ;/bin/sync ;/bin/sync ;/bin/sync ;/bin/sync ;/bin/sync ;/bin/sync"); + +/* if snort is running hardrestart, if snort is not running do nothing */ +if (file_exists("/tmp/snort_download_halt.pid")) { + start_service("snort"); + echo "The Rules update finished...\n"; + echo "Snort has restarted with your new set of rules...\n"; + exec("/bin/rm /tmp/snort_download_halt.pid"); } else { - echo "Rules are up to date.\n"; - log_error("Snort rules are up to date. Not updating."); + echo "The Rules update finished...\n"; + echo "You may start snort now...\n"; } -?> \ No newline at end of file +?> diff --git a/config/snort/snort_download_rules.php b/config/snort/snort_download_rules.php index 904f8057..8eca3d34 100644 --- a/config/snort/snort_download_rules.php +++ b/config/snort/snort_download_rules.php @@ -29,7 +29,8 @@ /* Setup enviroment */ $tmpfname = "/tmp/snort_rules_up"; -$snortdir = "/usr/local/etc/snort"; +$snortdir = "/usr/local/etc/snort_bkup"; +$snortdir_wan = "/usr/local/etc/snort"; $snort_filename_md5 = "snortrules-snapshot-2.8.tar.gz.md5"; $snort_filename = "snortrules-snapshot-2.8.tar.gz"; $emergingthreats_filename_md5 = "version.txt"; @@ -168,6 +169,11 @@ if (file_exists("{$tmpfname}")) { apc_clear_cache(); } +/* Make shure snortdir exits */ +exec("/bin/mkdir -p {$snortdir}"); +exec("/bin/mkdir -p {$snortdir}/rules"); +exec("/bin/mkdir -p {$snortdir}/signatures"); + /* send current buffer */ ob_flush(); @@ -290,9 +296,6 @@ if ($emerg_md5_check_new == $emerg_md5_check_old) { update_output_window(gettext("You may start Snort now, check update.")); hide_progress_bar_status(); $emerg_md5_check_chk_ok = on; - /* Timestamps to html */ -// echo "\n

You last checked for updates: {$last_md5_download}

\n"; -// echo "\n

You last installed for rules: {$last_rules_install}

\n"; } } } @@ -314,6 +317,8 @@ if ($snort_md5_check_ok == on && $emergingthreats_url_chk != on) { update_output_window(gettext("removing...")); exec("/bin/rm {$snortdir}/rules/emerging*"); exec("/bin/rm {$snortdir}/version.txt"); + exec("/bin/rm {$snortdir_wan}/rules/emerging*"); + exec("/bin/rm {$snortdir_wan}/version.txt"); update_status(gettext("Done making cleaning emrg direcory.")); } @@ -330,12 +335,15 @@ if ($snort_md5_check_ok == on && $pfsense_md5_check_ok == on && $emergingthreats exit(0); } -/* "You are Not Up to date */; +/* You are Not Up to date, always stop snort when updating rules for low end machines */; update_status(gettext("You are NOT up to date...")); - update_output_window(gettext("Stopping Snort service...")); -stop_service("snort"); -sleep(2); -// start_service("snort"); +update_output_window(gettext("Stopping Snort service...")); +$chk_if_snort_up = exec("pgrep -x snort"); +if ($chk_if_snort_up != "") { + exec("/usr/bin/touch /tmp/snort_download_halt.pid"); + stop_service("snort"); + sleep(2); +} /* download snortrules file */ if ($snort_md5_check_ok != on) { @@ -421,23 +429,23 @@ if ($snort_md5_check_ok != on) { if (file_exists("{$tmpfname}/{$snort_filename}")) { update_status(gettext("Extracting rules...")); update_output_window(gettext("May take a while...")); - exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$tmpfname} rules/"); - exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$tmpfname} etc/"); - exec("`/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$tmpfname} so_rules/precompiled/FreeBSD-7.0/i386/2.8.4/*`"); - exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$tmpfname} so_rules/bad-traffic.rules/"); - exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$tmpfname} so_rules/chat.rules/"); - exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$tmpfname} so_rules/dos.rules/"); - exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$tmpfname} so_rules/exploit.rules/"); - exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$tmpfname} so_rules/imap.rules/"); - exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$tmpfname} so_rules/misc.rules/"); - exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$tmpfname} so_rules/multimedia.rules/"); - exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$tmpfname} so_rules/netbios.rules/"); - exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$tmpfname} so_rules/nntp.rules/"); - exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$tmpfname} so_rules/p2p.rules/"); - exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$tmpfname} so_rules/smtp.rules/"); - exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$tmpfname} so_rules/sql.rules/"); - exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$tmpfname} so_rules/web-client.rules/"); - exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$tmpfname} so_rules/web-misc.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} etc/"); + exec("`/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/precompiled/FreeBSD-7.0/i386/2.8.4/*`"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/bad-traffic.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/chat.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/dos.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/exploit.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/imap.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/misc.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/multimedia.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/netbios.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/nntp.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/p2p.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/smtp.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/sql.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/web-client.rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} so_rules/web-misc.rules/"); update_status(gettext("Done extracting Rules.")); } else { update_status(gettext("The Download rules file missing...")); @@ -452,7 +460,7 @@ if ($emerg_md5_check_chk_ok != on) { if (file_exists("{$tmpfname}/{$emergingthreats_filename}")) { update_status(gettext("Extracting rules...")); update_output_window(gettext("May take a while...")); - exec("/usr/bin/tar xzf {$tmpfname}/{$emergingthreats_filename} -C {$tmpfname} rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$emergingthreats_filename} -C {$snortdir} rules/"); } } } @@ -462,7 +470,7 @@ if ($pfsense_md5_check_ok != on) { if (file_exists("{$tmpfname}/{$pfsense_rules_filename}")) { update_status(gettext("Extracting Pfsense rules...")); update_output_window(gettext("May take a while...")); - exec("/usr/bin/tar xzf {$tmpfname}/{$pfsense_rules_filename} -C {$tmpfname} rules/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$pfsense_rules_filename} -C {$snortdir} rules/"); } } @@ -473,43 +481,125 @@ $signature_info_chk = $config['installedpackages']['snortadvanced']['config'][0] if ($premium_url_chk == on) { update_status(gettext("Extracting Signatures...")); update_output_window(gettext("May take a while...")); - exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$tmpfname} doc/signatures/"); + exec("/usr/bin/tar xzf {$tmpfname}/{$snort_filename} -C {$snortdir} doc/signatures/"); update_status(gettext("Done extracting Signatures.")); } } } /* Make Clean Snort Directory */ -if ($snort_md5_check_ok != on && $emerg_md5_check_chk_ok != on && $pfsense_md5_check_ok != on) { -if (file_exists("{$snortdir}/rules")) { - update_status(gettext("Cleaning the snort Directory...")); - update_output_window(gettext("removing...")); - exec("/bin/rm {$snortdir}/*"); - exec("/bin/rm {$snortdir}/rules/*"); - exec("/bin/rm /usr/local/lib/snort/dynamicrules/*"); +//if ($snort_md5_check_ok != on && $emerg_md5_check_chk_ok != on && $pfsense_md5_check_ok != on) { +//if (file_exists("{$snortdir}/rules")) { +// update_status(gettext("Cleaning the snort Directory...")); +// update_output_window(gettext("removing...")); +// exec("/bin/mkdir -p {$snortdir}"); +// exec("/bin/mkdir -p {$snortdir}/rules"); +// exec("/bin/mkdir -p {$snortdir}/signatures"); +// exec("/bin/rm {$snortdir}/*"); +// exec("/bin/rm {$snortdir}/rules/*"); +// exec("/bin/rm {$snortdir_wan}/*"); +// exec("/bin/rm {$snortdir_wan}/rules/*"); + +// exec("/bin/rm /usr/local/lib/snort/dynamicrules/*"); +//} else { +// update_status(gettext("Making Snort Directory...")); +// update_output_window(gettext("should be fast...")); +// exec("/bin/mkdir -p {$snortdir}"); +// exec("/bin/mkdir -p {$snortdir}/rules"); +// exec("/bin/rm {$snortdir_wan}/*"); +// exec("/bin/rm {$snortdir_wan}/rules/*"); +// exec("/bin/rm /usr/local/lib/snort/dynamicrules/\*"); +// update_status(gettext("Done making snort direcory.")); +// } +//} + +/* Copy so_rules dir to snort lib dir */ +if ($snort_md5_check_ok != on) { +if (file_exists("{$snortdir}/so_rules/precompiled/FreeBSD-7.0/i386/2.8.4/")) { + update_status(gettext("Copying so_rules...")); + update_output_window(gettext("May take a while...")); + exec("`/bin/cp -f {$snortdir}/so_rules/precompiled/FreeBSD-7.0/i386/2.8.4/* /usr/local/lib/snort/dynamicrules/`"); + exec("/bin/cp {$snortdir}/so_rules/bad-traffic.rules {$snortdir}/rules/bad-traffic.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/chat.rules {$snortdir}/rules/chat.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/dos.rules {$snortdir}/rules/dos.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/exploit.rules {$snortdir}/rules/exploit.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/imap.rules {$snortdir}/rules/imap.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/misc.rules {$snortdir}/rules/misc.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/multimedia.rules {$snortdir}/rules/multimedia.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/netbios.rules {$snortdir}/rules/netbios.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/nntp.rules {$snortdir}/rules/nntp.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/p2p.rules {$snortdir}/rules/p2p.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/smtp.rules {$snortdir}/rules/smtp.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/sql.rules {$snortdir}/rules/sql.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/web-client.rules {$snortdir}/rules/web-client.so.rules"); + exec("/bin/cp {$snortdir}/so_rules/web.misc.rules {$snortdir}/rules/web.misc.so.rules"); + exec("/bin/rm -r {$snortdir}/so_rules"); + update_status(gettext("Done copying so_rules.")); } else { - update_status(gettext("Making Snort Directory...")); - update_output_window(gettext("should be fast...")); - exec("/bin/mkdir {$snortdir}"); - exec("/bin/mkdir {$snortdir}/rules"); - exec("/bin/rm /usr/local/lib/snort/dynamicrules/*"); - update_status(gettext("Done making snort direcory.")); - } + update_status(gettext("Directory so_rules does not exist...")); + update_output_window(gettext("Error copping so_rules...")); + exit(0); + } } -/* Copy snort rules and emergingthreats and pfsense dir to snort dir */ +/* enable disable setting will carry over with updates */ +/* TODO carry signature changes with the updates */ if ($snort_md5_check_ok != on || $emerg_md5_check_chk_ok != on || $pfsense_md5_check_ok != on) { -if (file_exists("{$tmpfname}/rules")) { - update_status(gettext("Copying rules...")); - update_output_window(gettext("May take a while...")); - exec("/bin/cp {$tmpfname}/rules/* {$snortdir}/rules"); - update_status(gettext("Done copping rules.")); - /* Write out time of last sucsessful rule install catch */ - $config['installedpackages']['snort']['last_rules_install'] = date("Y-M-jS-h:i-A"); - write_config(); + +if (!empty($config['installedpackages']['snort']['rule_sid_on'])) { +$enabled_sid_on = $config['installedpackages']['snort']['rule_sid_on']; +$enabled_sid_on_array = split('\|\|', $enabled_sid_on); +foreach($enabled_sid_on_array as $enabled_item_on) +$selected_sid_on_sections .= "$enabled_item_on\n"; + } + +if (!empty($config['installedpackages']['snort']['rule_sid_off'])) { +$enabled_sid_off = $config['installedpackages']['snort']['rule_sid_off']; +$enabled_sid_off_array = split('\|\|', $enabled_sid_off); +foreach($enabled_sid_off_array as $enabled_item_off) +$selected_sid_off_sections .= "$enabled_item_off\n"; + } + +$snort_sid_text = << /usr/local/etc/snort_bkup/gen-msg.map"); + +/* Run oinkmaster to snort_wan and cp configs */ +/* If oinkmaster is not needed cp rules normally */ +/* TODO add per interface settings here */ +if ($snort_md5_check_ok != on || $emerg_md5_check_chk_ok != on || $pfsense_md5_check_ok != on) { + + if (empty($config['installedpackages']['snort']['rule_sid_on']) || empty($config['installedpackages']['snort']['rule_sid_off'])) { + update_status(gettext("Your first set of rules are being copied...")); + update_output_window(gettext("May take a while...")); + exec("/bin/cp {$snortdir}/rules/* {$snortdir_wan}/rules/"); + exec("/bin/cp {$snortdir}/classification.config {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/gen-msg.map {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/generators {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/reference.config {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/sid {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/sid-msg.map {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/snort.conf {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/threshold.conf {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/unicode.map {$snortdir_wan}"); + +} else { + update_status(gettext("Your enable and disable changes are being applied to your fresh set of rules...")); + update_output_window(gettext("May take a while...")); + + exec("/bin/cp {$snortdir}/classification.config {$snortdir_wan}"); +// exec("/bin/cp {$snortdir}/gen-msg.map {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/generators {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/reference.config {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/sid {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/sid-msg.map {$snortdir_wan}"); +// exec("/bin/cp {$snortdir}/snort.conf {$snortdir_wan}"); +// exec("/bin/cp {$snortdir}/threshold.conf {$snortdir_wan}"); + exec("/bin/cp {$snortdir}/unicode.map {$snortdir_wan}"); + + /* oinkmaster.pl will convert saved changes for the new updates then we have to change #alert to # alert for the gui */ + /* might have to add a sleep for 3sec for flash drives or old drives */ + exec("/usr/local/bin/perl /usr/local/bin/oinkmaster.pl -C /usr/local/etc/snort_bkup/oinkmaster.conf -o /usr/local/etc/snort/rules > /usr/local/etc/snort_bkup/oinkmaster.log"); + exec("/usr/local/bin/perl -pi -e 's/#alert/# alert/g' /usr/local/etc/snort/rules/*.rules"); + exec("/usr/local/bin/perl -pi -e 's/##alert/# alert/g' /usr/local/etc/snort/rules/*.rules"); + exec("/usr/local/bin/perl -pi -e 's/## alert/# alert/g' /usr/local/etc/snort/rules/*.rules"); + + + } } -if (file_exists("/usr/local/lib/snort/dynamicrules//lib_sfdynamic_example_rule.so")) { - exec("/bin/rm /usr/local/lib/snort/dynamicrules//lib_sfdynamic_example_rule.so"); - exec("/bin/rm /usr/local/lib/snort/dynamicrules//lib_sfdynamic_example*"); +/* remove old $tmpfname files */ +if (file_exists("{$tmpfname}")) { + update_status(gettext("Cleaning up...")); + exec("/bin/rm -r /tmp/snort_rules_up"); +// apc_clear_cache(); } /* php code to flush out cache some people are reportting missing files this might help */ -sleep(5); +sleep(2); apc_clear_cache(); exec("/bin/sync ;/bin/sync ;/bin/sync ;/bin/sync ;/bin/sync ;/bin/sync ;/bin/sync ;/bin/sync"); - -update_status(gettext("Updating Alert Messages...")); -update_output_window(gettext("Please Wait...")); -exec("/usr/local/bin/create-sidmap.pl /usr/local/etc/snort/rules > /usr/local/etc/snort/gen-msg.map"); - -/* php code finish */ -update_status(gettext("The Rules update finished...")); -update_output_window(gettext("You may start snort now...")); +/* if snort is running hardrestart, if snort is not running do nothing */ +if (file_exists("/tmp/snort_download_halt.pid")) { + start_service("snort"); + update_status(gettext("The Rules update finished...")); + update_output_window(gettext("Snort has restarted with your new set of rules...")); + exec("/bin/rm /tmp/snort_download_halt.pid"); +} else { + update_status(gettext("The Rules update finished...")); + update_output_window(gettext("You may start snort now...")); +} /* hide progress bar and lets end this party */ hide_progress_bar_status(); diff --git a/config/snort/snort_dynamic_ip_reload.php b/config/snort/snort_dynamic_ip_reload.php index dbd6d015..7933ba16 100644 --- a/config/snort/snort_dynamic_ip_reload.php +++ b/config/snort/snort_dynamic_ip_reload.php @@ -40,7 +40,10 @@ if($config['interfaces']['wan']['ipaddr'] == "pppoe" or create_snort_conf(); mwexec("/sbin/pfctl -t snort2c -T flush"); exec("killall -HUP snort"); - exec("/usr/bin/killall snort2c; snort2c -w /var/db/whitelist -a /var/log/snort/alert"); + /* define snortbarnyardlog_chk */ + $snortbarnyardlog_info_chk = $config['installedpackages']['snortadvanced']['config'][0]['snortbarnyardlog']; + if ($snortbarnyardlog_info_chk == on) + exec("/usr/bin/killall barnyard2; /usr/local/bin/barnyard2 -c /usr/local/etc/barnyard2.conf -d /var/log/snort -f snort.u2 -w /usr/local/etc/snort/barnyard2.waldo -D -q\n"); } ?> \ No newline at end of file diff --git a/config/snort/snort_rules.php b/config/snort/snort_rules.php index 76413727..fa4a5a4a 100644 --- a/config/snort/snort_rules.php +++ b/config/snort/snort_rules.php @@ -27,9 +27,10 @@ POSSIBILITY OF SUCH DAMAGE. */ require("guiconfig.inc"); +require("config.inc"); if(!is_dir("/usr/local/etc/snort/rules")) - Header("Location: snort_download_rules.php"); + header("Location: snort_rules.php", false); function get_middle($source, $beginning, $ending, $init_pos) { $beginning_pos = strpos($source, $beginning, $init_pos); @@ -200,10 +201,10 @@ if ($_POST) } if ($_POST['apply']) { - stop_service("snort"); - sleep(2); - start_service("snort"); - $savemsg = "The snort rules selections have been saved. Restarting Snort."; +// stop_service("snort"); +// sleep(2); +// start_service("snort"); + $savemsg = "The snort rules selections have been saved. Please restart snort by clicking save on the settings tab."; $stopMsg = false; } @@ -250,6 +251,54 @@ else if ($_GET['act'] == "toggle") $splitcontents = load_rule_file($file); $stopMsg = true; + + //write disable/enable sid to config.xml + if ($disabled == false) { + $string_sid = strstr($tempstring, 'sid:'); + $sid_pieces = explode(";", $string_sid); + $sid_off_cut = $sid_pieces[0]; + // sid being turned off + $sid_off = str_replace("sid:", "", $sid_off_cut); + // rule_sid_on registers + $sid_on_pieces = $config['installedpackages']['snort']['rule_sid_on']; + // if off sid is the same as on sid remove it + $sid_on_old = str_replace("||enablesid $sid_off", "", "$sid_on_pieces"); + // write the replace sid back as empty + $config['installedpackages']['snort']['rule_sid_on'] = $sid_on_old; + // rule sid off registers + $sid_off_pieces = $config['installedpackages']['snort']['rule_sid_off']; + // if off sid is the same as off sid remove it + $sid_off_old = str_replace("||disablesid $sid_off", "", "$sid_off_pieces"); + // write the replace sid back as empty + $config['installedpackages']['snort']['rule_sid_off'] = $sid_off_old; + // add sid off registers to new off sid + $config['installedpackages']['snort']['rule_sid_off'] = "||disablesid $sid_off" . $config['installedpackages']['snort']['rule_sid_off']; + write_config(); + } + else + { + $string_sid = strstr($tempstring, 'sid:'); + $sid_pieces = explode(";", $string_sid); + $sid_on_cut = $sid_pieces[0]; + // sid being turned off + $sid_on = str_replace("sid:", "", $sid_on_cut); + // rule_sid_off registers + $sid_off_pieces = $config['installedpackages']['snort']['rule_sid_off']; + // if off sid is the same as on sid remove it + $sid_off_old = str_replace("||disablesid $sid_on", "", "$sid_off_pieces"); + // write the replace sid back as empty + $config['installedpackages']['snort']['rule_sid_off'] = $sid_off_old; + // rule sid on registers + $sid_on_pieces = $config['installedpackages']['snort']['rule_sid_on']; + // if on sid is the same as on sid remove it + $sid_on_old = str_replace("||enablesid $sid_on", "", "$sid_on_pieces"); + // write the replace sid back as empty + $config['installedpackages']['snort']['rule_sid_on'] = $sid_on_old; + // add sid on registers to new on sid + $config['installedpackages']['snort']['rule_sid_on'] = "||enablesid $sid_on" . $config['installedpackages']['snort']['rule_sid_on']; + write_config(); + } + } @@ -410,8 +459,8 @@ function go() $counter2++; $destination_port = $rule_content[$counter2];//destination port location - $message = get_middle($tempstring, 'msg:"', '";', 0); - + $message = get_middle($tempstring, 'msg:"', '";', 0); + echo ""; echo ""; echo $textss; diff --git a/pkg_config.7.xml b/pkg_config.7.xml index 63c74e40..cc2c0102 100755 --- a/pkg_config.7.xml +++ b/pkg_config.7.xml @@ -234,15 +234,16 @@ snort http://forum.pfsense.org/index.php/topic,16847.0.html http://www.snort.org - Snort is a libpcap-based packet sniffer/logger which can be used as a lightweight network intrusion detection system. It features rules based logging and can perform content searching/matching in addition to being used to detect a variety of other attacks and probes, such as buffer overflows, stealth port scans, CGI attacks, SMB probes, and much more. + Used by fortune 500 companies and goverments Snort is the most widely deployed IDS/IPS technology worldwide. It features rules based logging and can perform content searching/matching in addition to being used to detect a variety of other attacks and probes, such as buffer overflows, stealth port scans, CGI attacks, SMB probes, and much more. Security http://files.pfsense.org/packages/70/All/ libdnet-1.11_3.tbz pcre-7.9.tbz + perl-5.8.9_3.tbz mysql-client-5.1.34.tbz - snort-2.8.4.1.tbz + snort-2.8.4.1_1.tbz http://www.pfsense.com/packages/config/snort/snort.xml - 2.8.4.1 + 2.8.4.1 pkg v.1.6 1.2.2 Stable snort.xml -- cgit v1.2.3