diff options
author | Ermal <ermal.luci@gmail.com> | 2014-04-08 08:48:48 +0000 |
---|---|---|
committer | Ermal <ermal.luci@gmail.com> | 2014-04-08 08:48:48 +0000 |
commit | b98fe3e8ff1560b664dc0f18c7b344ad5b1aac2c (patch) | |
tree | e72081da96f024eb8c1d466835b6f6157316fbd3 /config/snort-old/bin/oinkmaster_contrib | |
parent | 5e67a462ddf630b383b2f06fdc8b8bdabf9c0bb9 (diff) | |
download | pfsense-packages-b98fe3e8ff1560b664dc0f18c7b344ad5b1aac2c.tar.gz pfsense-packages-b98fe3e8ff1560b664dc0f18c7b344ad5b1aac2c.tar.bz2 pfsense-packages-b98fe3e8ff1560b664dc0f18c7b344ad5b1aac2c.zip |
Get rid of the snort-old folder since its way out of usage nowdays
Diffstat (limited to 'config/snort-old/bin/oinkmaster_contrib')
-rw-r--r-- | config/snort-old/bin/oinkmaster_contrib/README.contrib | 84 | ||||
-rw-r--r-- | config/snort-old/bin/oinkmaster_contrib/addmsg.pl | 299 | ||||
-rw-r--r-- | config/snort-old/bin/oinkmaster_contrib/addsid.pl | 382 | ||||
-rw-r--r-- | config/snort-old/bin/oinkmaster_contrib/create-sidmap.pl | 280 | ||||
-rw-r--r-- | config/snort-old/bin/oinkmaster_contrib/makesidex.pl | 261 | ||||
-rw-r--r-- | config/snort-old/bin/oinkmaster_contrib/oinkgui.pl | 1046 | ||||
-rw-r--r-- | config/snort-old/bin/oinkmaster_contrib/oinkmaster.pl | 2754 |
7 files changed, 0 insertions, 5106 deletions
diff --git a/config/snort-old/bin/oinkmaster_contrib/README.contrib b/config/snort-old/bin/oinkmaster_contrib/README.contrib deleted file mode 100644 index 6923fa26..00000000 --- a/config/snort-old/bin/oinkmaster_contrib/README.contrib +++ /dev/null @@ -1,84 +0,0 @@ -# $Id: README.contrib,v 1.21 2005/10/18 10:41:20 andreas_o Exp $ # - -------------------------------------------------------------------------------- -* oinkgui.pl by Andreas Östling <andreaso@it.su.se> - - A graphical front-end to Oinkmaster written in Perl/Tk. - See README.gui for complete documentation. -------------------------------------------------------------------------------- - - - -------------------------------------------------------------------------------- -* addsid.pl by Andreas Östling <andreaso@it.su.se> - - A script that parses *.rules in all specified directories and adds a - SID to (active) rules that don't have any. (Actually, rev and classtype - are also added if missing, unless you edit addsid.pl and tune this.) The - script first looks for the current highest SID (even in inactive rules) - and starts at the next one, unless this value is below MIN_SID (defined - inside addsid.pl). By default, this value is set to 1000001 since this - is the lowest SID assigned for local usage. Handles multi-line rules. -------------------------------------------------------------------------------- - - - -------------------------------------------------------------------------------- -* create-sidmap.pl by Andreas Östling <andreaso@it.su.se> - - A script that parses all active rules in *.rules in all specified - directories and creates a SID map. (Like Snort's regen-sidmap, but this - one handles multi-line rules.) Result goes to standard output which can - be redirected to a sid-msg.map file. -------------------------------------------------------------------------------- - - - -------------------------------------------------------------------------------- -* makesidex.pl, originally by Jerry Applebaum but later rewritten by - Andreas Östling <andreaso@it.su.se> to handle multi-line rules and - multiple rules directories. - - It reads *.rules in all specified directories, looks for all disabled - rules and prints a "disablesid <sid> # <msg>" line for each disabled rule. - The output can be appended to oinkmaster.conf. - Useful to new Oinkmaster users. -------------------------------------------------------------------------------- - - - -------------------------------------------------------------------------------- -* addmsg.pl by Andreas Östling <andreaso@it.su.se>: - - A script that will parse your oinkmaster.conf for - localsid/enablesid/disablesid lines and add their rule message as a #comment. - If your oinkmaster.conf looks like this before addmsg.pl has been run: - - disablesid 286 - disablesid 287 - disablesid 288 - - It will look something like this afterward: - - disablesid 286 # POP3 EXPLOIT x86 bsd overflow - disablesid 287 # POP3 EXPLOIT x86 bsd overflow - disablesid 288 # POP3 EXPLOIT x86 linux overflow - - addmsg.pl will not touch lines that already has a comment in them. - It's not able to handle SID lists when written like this: - disablesid 1,2,3, ... - But it should handle them if written like this: - disablesid \ - 1, \ - 2, \ - 3 - - The new config file will be printed to standard output, so you - probably want to redirect the output to a file, for example: - - ./addmsg.pl oinkmaster.conf rules/ > oinkmaster.conf.new - - If oinkmaster.conf.new looks ok, simply rename it to oinkmaster.conf. - Do NOT redirect to the same file you read from, as this will destroy - that file. -------------------------------------------------------------------------------- diff --git a/config/snort-old/bin/oinkmaster_contrib/addmsg.pl b/config/snort-old/bin/oinkmaster_contrib/addmsg.pl deleted file mode 100644 index e5866d6f..00000000 --- a/config/snort-old/bin/oinkmaster_contrib/addmsg.pl +++ /dev/null @@ -1,299 +0,0 @@ -#!/usr/bin/perl -w - -# $Id: addmsg.pl,v 1.19 2005/12/31 13:42:46 andreas_o Exp $ # - -# Copyright (c) 2004-2006 Andreas Östling <andreaso@it.su.se> -# 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 strict; - -sub get_next_entry($ $ $ $ $ $); -sub parse_singleline_rule($ $ $); - - -my $USAGE = << "RTFM"; - -Parse Oinkmaster configuration file and add the rule's "msg" string as a -#comment for each disablesid/enablesid line. - -Usage: $0 <oinkmaster.conf> <rulesdir> [rulesdir2, ...] - -The new config file will be printed to standard output, so you -probably want to redirect the output to a new file (*NOT* the same -file you used as input, because that will destroy the file!). -For example: - -$0 /etc/oinkmaster.conf /etc/rules/ > oinkmaster.conf.new - -If oinkmaster.conf.new looks ok, simply rename it to /etc/oinkmaster.conf. - -RTFM - - -# Regexp to match the start of a multi-line rule. -# %ACTIONS% will be replaced with content of $config{actions} later. -my $MULTILINE_RULE_REGEXP = '^\s*#*\s*(?:%ACTIONS%)'. - '\s.*\\\\\s*\n$'; # '; - -# Regexp to match a single-line rule. -my $SINGLELINE_RULE_REGEXP = '^\s*#*\s*(?:%ACTIONS%)'. - '\s.+;\s*\)\s*$'; # '; - - -my $config = shift || die($USAGE); - -my @rulesdirs = @ARGV; -die($USAGE) unless ($#rulesdirs > -1); - -my $verbose = 1; -my (%sidmsgmap, %config); - -$config{rule_actions} = "alert|drop|log|pass|reject|sdrop|activate|dynamic"; - -$SINGLELINE_RULE_REGEXP =~ s/%ACTIONS%/$config{rule_actions}/; -$MULTILINE_RULE_REGEXP =~ s/%ACTIONS%/$config{rule_actions}/; - - - -# Read in oinkmaster.conf. -open(CONFIG, "<" , "$config") or die("could not open \"$config\" for reading: $!\n"); -my @config = <CONFIG>; -close(CONFIG); - - -# Read in *.rules in all rulesdirs and create %sidmsgmap ($sidmsgmap{sid} = msg). -foreach my $rulesdir (@rulesdirs) { - opendir(RULESDIR, "$rulesdir") or die("could not open \"$rulesdir\": $!\n"); - - while (my $file = readdir(RULESDIR)) { - next unless ($file =~ /\.rules$/); - - open(FILE, "<", "$rulesdir/$file") or die("could not open \"$rulesdir/$file\": $!\n"); - my @file = <FILE>; - close(FILE); - - my ($single, $multi, $nonrule, $msg, $sid); - - while (get_next_entry(\@file, \$single, \$multi, \$nonrule, \$msg, \$sid)) { - $sidmsgmap{$sid} = $msg - if (defined($single)); - } - } -} - - -# Print new oinkmaster.conf. -while ($_ = shift(@config)) { - if (/^\s*(?:disable|enable|local)sid\s+(\d+)\s*$/ || /^\s*(\d+)\s*,\s*\\$/ || /^\s*(\d+)\s*$/) { - my $sid = $1; - my $is_multiline = 0; - chomp; - - if (/\\$/) { - $is_multiline = 1; - s/\\$//; - } - - $_ = sprintf("%-25s", $_); - if (exists($sidmsgmap{$sid})) { - print "$_ # $sidmsgmap{$sid}"; - } else { - print "$_"; - } - print " \\" if ($is_multiline); - print "\n"; - } else { - print; - } -} - - - -# From oinkmaster.pl. -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; - - # 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 - } 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 - } - } 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 - } else { # non-rule line - - # 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 or msg?): $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 - } -} - - - -# From oinkmaster.pl. -sub parse_singleline_rule($ $ $) -{ - my $line = shift; - my $msg_ref = shift; - my $sid_ref = shift; - - 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); -} diff --git a/config/snort-old/bin/oinkmaster_contrib/addsid.pl b/config/snort-old/bin/oinkmaster_contrib/addsid.pl deleted file mode 100644 index 64255d22..00000000 --- a/config/snort-old/bin/oinkmaster_contrib/addsid.pl +++ /dev/null @@ -1,382 +0,0 @@ -#!/usr/bin/perl -w - -# $Id: addsid.pl,v 1.30 2005/12/31 13:42:46 andreas_o Exp $ # - -# Copyright (c) 2004-2006 Andreas Östling <andreaso@it.su.se> -# 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 strict; - - -sub get_next_entry($ $ $ $ $ $); -sub parse_singleline_rule($ $ $); -sub get_next_available_sid(@); - - -# Set this to the default classtype you want to add, if missing. -# Set to 0 or "" if you don't want to add a classtype. -my $CLASSTYPE = "misc-attack"; - -# If ADD_REV is set to 1, "rev: 1;" will be added to rule if it has no rev. -# Set to 0 if you don't want to add it. -my $ADD_REV = 1; - -# Minimum SID to add. Normally, the next available SID will be used, -# unless it's below this value. Only SIDs >= 1000000 are reserved for -# personal use. -my $MIN_SID = 1000001; - -# Regexp to match the start of a multi-line rule. -# %ACTIONS% will be replaced with content of $config{actions} later. -my $MULTILINE_RULE_REGEXP = '^\s*#*\s*(?:%ACTIONS%)'. - '\s.*\\\\\s*\n$'; # '; - -# Regexp to match a single-line rule. -my $SINGLELINE_RULE_REGEXP = '^\s*#*\s*(?:%ACTIONS%)'. - '\s.+;\s*\)\s*$'; # '; - - -my $USAGE = << "RTFM"; - -Parse *.rules in one or more directories and add "sid:<sid>;" to -active rules that don't have any "sid" entry, starting with the next -available SID after parsing all rules files (but $MIN_SID at minumum). -Also, "rev:1;" is added to rules without a "rev" entry, and -"classtype:misc-attack;" is added to rules without a "classtype" entry -(edit options at the top of $0 if you want to change this). - -Usage: $0 <rulesdir> [rulesdir2, ...] - -RTFM - - -# Start in verbose mode. -my $verbose = 1; - -my (%all_sids, %active_sids, %config); - -my @rulesdirs = @ARGV; - -die($USAGE) unless ($#rulesdirs > -1); - -$config{rule_actions} = "alert|drop|log|pass|reject|sdrop|activate|dynamic"; - -$SINGLELINE_RULE_REGEXP =~ s/%ACTIONS%/$config{rule_actions}/; -$MULTILINE_RULE_REGEXP =~ s/%ACTIONS%/$config{rule_actions}/; - - -# Find out the next available SID. -my $next_sid = get_next_available_sid(@rulesdirs); - -# Avoid seeing possible warnings about broken rules twice. -$verbose = 0; - -# Add sid/rev/classtype to active rules that don't have any. -foreach my $dir (@rulesdirs) { - opendir(RULESDIR, "$dir") or die("could not open \"$dir\": $!\n"); - - while (my $file = readdir(RULESDIR)) { - next unless ($file =~ /\.rules$/); - - open(OLDFILE, "$dir/$file") - or die("could not open \"$dir/$file\": $!\n"); - my @file = <OLDFILE>; - close(OLDFILE); - - open(NEWFILE, ">", "$dir/$file") - or die("could not open \"$dir/$file\" for writing: $!\n"); - - my ($single, $multi, $nonrule, $msg, $sid); - while (get_next_entry(\@file, \$single, \$multi, \$nonrule, \$msg, \$sid)) { - - if (defined($nonrule)) { - print NEWFILE "$nonrule"; - next; - } - - $multi = $single unless (defined($multi)); - - # Don't care about inactive rules. - if ($single =~ /^\s*#/) { - print NEWFILE "$multi"; - next; - } - - my $added; - - # Add SID. - if ($single !~ /sid\s*:\s*\d+\s*;/) { - $added .= "SID $next_sid,"; - $multi =~ s/\)\s*\n/sid:$next_sid;)\n/; - $next_sid++; - } - - # Add revision. - if ($ADD_REV && $single !~ /rev\s*:\s*\d+\s*;/) { - $added .= "rev,"; - $multi =~ s/\)\s*\n/rev:1;)\n/; - } - - # Add classtype. - if ($CLASSTYPE && $single !~ /classtype\s*:\s*.+\s*;/) { - $added .= "classtype $CLASSTYPE,"; - $multi =~ s/\)\s*\n/classtype:$CLASSTYPE;)\n/; - } - - if (defined($added)) { - $added =~ s/,$//; - print "Adding $added to rule \"$msg\"\n" - if (defined($added)); - } - - print NEWFILE "$multi"; - } - - close(NEWFILE); - } - - closedir(RULESDIR); -} - - - -# Read in *.rules in given directory and return highest SID. -sub get_next_available_sid(@) -{ - my @dirs = @_; - - foreach my $dir (@dirs) { - opendir(RULESDIR, "$dir") or die("could not open \"$dir\": $!\n"); - - # Only care about *.rules. - while (my $file = readdir(RULESDIR)) { - next unless ($file =~ /\.rules$/); - - open(OLDFILE, "<$dir/$file") or die("could not open \"$dir/$file\": $!\n"); - my @file = <OLDFILE>; - close(OLDFILE); - - my ($single, $multi, $nonrule, $msg, $sid); - - while (get_next_entry(\@file, \$single, \$multi, \$nonrule, \$msg, \$sid)) { - if (defined($single) && defined($sid)) { - $all_sids{$sid}++; - - # If this is an active rule add to %active_sids and - # warn if it already exists. - if ($single =~ /^\s*alert/) { - print STDERR "WARNING: duplicate SID: $sid\n" - if (exists($active_sids{$sid})); - $active_sids{$sid}++ - } - } - } - } - } - - # Sort sids and use highest one + 1, unless it's below MIN_SID. - @_ = sort {$a <=> $b} keys(%all_sids); - my $sid = pop(@_); - - if (!defined($sid)) { - $sid = $MIN_SID - } else { - $sid++; - } - - # If it's below MIN_SID, use MIN_SID instead. - $sid = $MIN_SID if ($sid < $MIN_SID); - - return ($sid) -} - - - -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; - - # 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 - } 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 - } - } 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 - } else { # non-rule line - - # 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 or msg?): $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 - } -} - - - -# From oinkmaster.pl except that this version -# has been modified so that the sid is *optional*. -sub parse_singleline_rule($ $ $) -{ - my $line = shift; - my $msg_ref = shift; - my $sid_ref = shift; - - 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); -} diff --git a/config/snort-old/bin/oinkmaster_contrib/create-sidmap.pl b/config/snort-old/bin/oinkmaster_contrib/create-sidmap.pl deleted file mode 100644 index 26a9040c..00000000 --- a/config/snort-old/bin/oinkmaster_contrib/create-sidmap.pl +++ /dev/null @@ -1,280 +0,0 @@ -#!/usr/local/bin/perl -w - -# $Id: create-sidmap.pl,v 1.21 2005/12/31 13:42:46 andreas_o Exp $ # - -# Copyright (c) 2004-2006 Andreas Östling <andreaso@it.su.se> -# 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 strict; - -sub get_next_entry($ $ $ $ $ $); -sub parse_singleline_rule($ $ $); - -# Files to ignore. -my %skipfiles = ( - 'deleted.rules' => 1, -); - -# Regexp to match the start of a multi-line rule. -# %ACTIONS% will be replaced with content of $config{actions} later. -my $MULTILINE_RULE_REGEXP = '^\s*#*\s*(?:%ACTIONS%)'. - '\s.*\\\\\s*\n$'; # '; - -# Regexp to match a single-line rule. -my $SINGLELINE_RULE_REGEXP = '^\s*#*\s*(?:%ACTIONS%)'. - '\s.+;\s*\)\s*$'; # '; - -my $USAGE = << "RTFM"; - -Parse active rules in *.rules in one or more directories and create a SID -map. Result is sent to standard output, which can be redirected to a -sid-msg.map file. - -Usage: $0 <rulesdir> [rulesdir2, ...] - -RTFM - -my $verbose = 1; - -my (%sidmap, %config); - -my @rulesdirs = @ARGV; - -die($USAGE) unless ($#rulesdirs > -1); - -$config{rule_actions} = "alert|drop|log|pass|reject|sdrop|activate|dynamic"; - -$SINGLELINE_RULE_REGEXP =~ s/%ACTIONS%/$config{rule_actions}/; -$MULTILINE_RULE_REGEXP =~ s/%ACTIONS%/$config{rule_actions}/; - - -# Read in all rules from each rules file (*.rules) in each rules dir. -# into %sidmap. -foreach my $rulesdir (@rulesdirs) { - opendir(RULESDIR, "$rulesdir") or die("could not open \"$rulesdir\": $!\n"); - - while (my $file = readdir(RULESDIR)) { - next unless ($file =~ /\.rules$/); - next if ($skipfiles{$file}); - - open(FILE, "$rulesdir/$file") or die("could not open \"$rulesdir/$file\": $!\n"); - my @file = <FILE>; - close(FILE); - - my ($single, $multi, $nonrule, $msg, $sid); - - while (get_next_entry(\@file, \$single, \$multi, \$nonrule, \$msg, \$sid)) { - if (defined($single)) { - - warn("WARNING: duplicate SID: $sid (discarding old)\n") - if (exists($sidmap{$sid})); - - $sidmap{$sid} = "$sid || $msg"; - - # Print all references. Borrowed from Brian Caswell's regen-sidmap script. - my $ref = $single; - while ($ref =~ s/(.*)reference\s*:\s*([^\;]+)(.*)$/$1 $3/) { - $sidmap{$sid} .= " || $2" - } - - $sidmap{$sid} .= "\n"; - } - } - } -} - -# Print results. -foreach my $sid (sort { $a <=> $b } keys(%sidmap)) { - print "$sidmap{$sid}"; -} - - - -# Same as in oinkmaster.pl. -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; - - # 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 - } 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 - } - } 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 - } else { # non-rule line - - # 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 or msg?): $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 - } -} - - - -# Same as in oinkmaster.pl. -sub parse_singleline_rule($ $ $) -{ - my $line = shift; - my $msg_ref = shift; - my $sid_ref = shift; - - 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); -} diff --git a/config/snort-old/bin/oinkmaster_contrib/makesidex.pl b/config/snort-old/bin/oinkmaster_contrib/makesidex.pl deleted file mode 100644 index 80354735..00000000 --- a/config/snort-old/bin/oinkmaster_contrib/makesidex.pl +++ /dev/null @@ -1,261 +0,0 @@ -#!/usr/bin/perl -w - -# $Id: makesidex.pl,v 1.11 2005/12/31 13:42:46 andreas_o Exp $ # - -# Copyright (c) 2004-2006 Andreas Östling <andreaso@it.su.se> -# 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 strict; - -sub get_next_entry($ $ $ $ $ $); -sub parse_singleline_rule($ $ $); - - -# Regexp to match the start of a multi-line rule. -# %ACTIONS% will be replaced with content of $config{actions} later. -my $MULTILINE_RULE_REGEXP = '^\s*#*\s*(?:%ACTIONS%)'. - '\s.*\\\\\s*\n$'; # '; - -# Regexp to match a single-line rule. -my $SINGLELINE_RULE_REGEXP = '^\s*#*\s*(?:%ACTIONS%)'. - '\s.+;\s*\)\s*$'; # '; - -my $USAGE = << "RTFM"; - -Parse *.rules in one or more directories and look for all rules that are -disabled (i.e. begin with "#") and print "disablesid <sid> # <msg>" to -standard output for all those rules. This output can be redirected to a -file, which will be understood by Oinkmaster. - -Usage: $0 <rulesdir> [rulesdir2, ...] - -RTFM - -my $verbose = 1; - -my (%disabled, %config); - -my @rulesdirs = @ARGV; - -die($USAGE) unless ($#rulesdirs > -1); - -$config{rule_actions} = "alert|drop|log|pass|reject|sdrop|activate|dynamic"; - -$SINGLELINE_RULE_REGEXP =~ s/%ACTIONS%/$config{rule_actions}/; -$MULTILINE_RULE_REGEXP =~ s/%ACTIONS%/$config{rule_actions}/; - -foreach my $rulesdir (@rulesdirs) { - opendir(RULESDIR, "$rulesdir") or die("could not open \"$rulesdir\": $!\n"); - - while (my $file = readdir(RULESDIR)) { - next unless ($file =~ /\.rules$/); - - open(FILE, "$rulesdir/$file") or die("could not open \"$rulesdir/$file\": $!\n"); - my @file = <FILE>; - close(FILE); - - my ($single, $multi, $nonrule, $msg, $sid); - - while (get_next_entry(\@file, \$single, \$multi, \$nonrule, \$msg, \$sid)) { - $single = $multi if (defined($multi)); - $disabled{$sid} = $msg - if (defined($single) && $single =~ /^\s*#/); - } - } -} - -# Print results. -foreach my $sid (sort { $a <=> $b } keys(%disabled)) { - printf("%-25s # %s\n", "disablesid $sid", $disabled{$sid}); -} - - - -# Same as in oinkmaster.pl. -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; - - # 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 - } 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 - } - } 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 - } else { # non-rule line - - # 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 or msg?): $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 - } -} - - - -# Same as in oinkmaster.pl. -sub parse_singleline_rule($ $ $) -{ - my $line = shift; - my $msg_ref = shift; - my $sid_ref = shift; - - 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); -} diff --git a/config/snort-old/bin/oinkmaster_contrib/oinkgui.pl b/config/snort-old/bin/oinkmaster_contrib/oinkgui.pl deleted file mode 100644 index 4e96f7db..00000000 --- a/config/snort-old/bin/oinkmaster_contrib/oinkgui.pl +++ /dev/null @@ -1,1046 +0,0 @@ -#!/usr/bin/perl -w - -# $Id: oinkgui.pl,v 1.52 2005/12/31 13:42:46 andreas_o Exp $ # - -# Copyright (c) 2004-2006 Andreas Östling <andreaso@it.su.se> -# 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::Spec; -use Tk; -use Tk::Balloon; -use Tk::BrowseEntry; -use Tk::FileSelect; -use Tk::NoteBook; -use Tk::ROText; - -use constant CSIDL_DRIVES => 17; - -sub update_rules(); -sub clear_messages(); -sub create_cmdline($); -sub fileDialog($ $ $ $); -sub load_config(); -sub save_config(); -sub save_messages(); -sub update_file_label_color($ $ $); -sub create_fileSelectFrame($ $ $ $ $ $); -sub create_checkbutton($ $ $); -sub create_radiobutton($ $ $); -sub create_actionbutton($ $ $); -sub execute_oinkmaster(@); -sub logmsg($ $); - - -my $version = 'Oinkmaster GUI v1.1'; - -my @oinkmaster_conf = qw( - /etc/oinkmaster.conf - /usr/local/etc/oinkmaster.conf -); - -# List of URLs that will show up in the URL BrowseEntry. -my @urls = qw( - http://www.bleedingsnort.com/bleeding.rules.tar.gz - http://www.snort.org/pub-bin/downloads.cgi/Download/comm_rules/Community-Rules.tar.gz - http://www.snort.org/pub-bin/oinkmaster.cgi/<oinkcode>/snortrules-snapshot-CURRENT.tar.gz - http://www.snort.org/pub-bin/oinkmaster.cgi/<oinkcode>/snortrules-snapshot-2.3.tar.gz -); - -my %color = ( - background => 'Bisque3', - button => 'Bisque2', - label => 'Bisque1', - notebook_bg => 'Bisque2', - notebook_inact => 'Bisque3', - file_label_ok => '#00e000', - file_label_not_ok => 'red', - out_frame_fg => 'white', - out_frame_bg => 'black', - entry_bg => 'white', - button_active => 'white', - button_bg => 'Bisque4', -); - -my %config = ( - animate => 1, - careful => 0, - enable_all => 0, - check_removed => 0, - output_mode => 'normal', - diff_mode => 'detailed', - perl => $^X, - oinkmaster => "", - oinkmaster_conf => "", - outdir => "", - url => "", - varfile => "", - backupdir => "", - editor => "", -); - -my %help = ( - - # File locations. - oinkscript => 'Location of the executable Oinkmaster script (oinkmaster.pl).', - oinkconf => 'The Oinkmaster configuration file to use.', - outdir => 'Where to put the new rules. This should be the directory where you '. - 'store your current rules.', - - url => 'Alternate location of rules archive to download/copy. '. - 'Leave empty to use the location set in oinkmaster.conf.', - varfile => 'Variables that exist in downloaded snort.conf but not in '. - 'this file will be added to it. Leave empty to skip.', - backupdir => 'Directory to put tarball of old rules before overwriting them. '. - 'Leave empty to skip backup.', - editor => 'Full path to editor to execute when pressing the "edit" button '. - '(wordpad is recommended on Windows). ', - - # Checkbuttons. - careful => 'In careful mode, Oinkmaster will just check for changes, '. - 'not update anything.', - enable => 'Some rules may be commented out by default (for a reason!). '. - 'This option will make Oinkmaster enable those.', - removed => 'Check for rules files that exist in the output directory but not '. - 'in the downloaded rules archive.', - - # Action buttons. - clear => 'Clear current output messages.', - save => 'Save current output messages to file.', - exit => 'Exit the GUI.', - update => 'Execute Oinkmaster to update the rules.', - test => 'Test current Oinkmaster configuration. ' . - 'If there are no fatal errors, you are ready to update the rules.', - version => 'Request version information from Oinkmaster.', -); - - -my $gui_config_file = ""; -my $use_fileop = 0; - - -#### MAIN #### - -select STDERR; -$| = 1; -select STDOUT; -$| = 1; - -# Find out if can use Win32::FileOp. -if ($^O eq 'MSWin32') { - BEGIN { $^W = 0 } - $use_fileop = 1 if (eval "require Win32::FileOp"); -} - -# Find out which oinkmaster.pl file to default to. -foreach my $dir (File::Spec->path()) { - my $file = "$dir/oinkmaster"; - if (-f "$file" && (-x "$file" || $^O eq 'MSWin32')) { - $config{oinkmaster} = $file; - last; - } elsif (-f "$file.pl" && (-x "$file" || $^O eq 'MSWin32')) { - $config{oinkmaster} = "$file.pl"; - last; - } -} - -# Find out which oinkmaster config file to default to. -foreach my $file (@oinkmaster_conf) { - if (-e "$file") { - $config{oinkmaster_conf} = $file; - last; - } -} - -# Find out where the GUI config file is (it's not required). -if ($ENV{HOME}) { - $gui_config_file = "$ENV{HOME}/.oinkguirc" -} elsif ($ENV{HOMEDRIVE} && $ENV{HOMEPATH}) { - $gui_config_file = "$ENV{HOMEDRIVE}$ENV{HOMEPATH}\\.oinkguirc"; -} - - -# Create main window. -my $main = MainWindow->new( - -background => "$color{background}", - -title => "$version", -); - - -# Create scrolled frame with output messages. -my $out_frame = $main->Scrolled('ROText', - -setgrid => 'true', - -scrollbars => 'e', - -background => $color{out_frame_bg}, - -foreground => $color{out_frame_fg}, -); - - -my $help_label = $main->Label( - -relief => 'groove', - -background => "$color{label}", -); - -my $balloon = $main->Balloon( - -statusbar => $help_label, -); - - -# Create notebook. -my $notebook = $main->NoteBook( - -ipadx => 6, - -ipady => 6, - -background => $color{notebook_bg}, - -inactivebackground => $color{notebook_inact}, - -backpagecolor => $color{background}, -); - - -# Create tab with required files/dirs. -my $req_tab = $notebook->add("required", - -label => "Required files and directories", - -underline => 0, -); - -$req_tab->configure(-bg => "$color{notebook_inact}"); - - -# Create frame with oinkmaster.pl location. -my $filetypes = [ - ['Oinkmaster script', 'oinkmaster.pl'], - ['All files', '*' ] -]; - -my $oinkscript_frame = - create_fileSelectFrame($req_tab, "oinkmaster.pl", 'EXECFILE', - \$config{oinkmaster}, 'NOEDIT', $filetypes); - -$balloon->attach($oinkscript_frame, -statusmsg => $help{oinkscript}); - - -# Create frame with oinkmaster.conf location. -$filetypes = [ - ['configuration files', '.conf'], - ['All files', '*' ] -]; - -my $oinkconf_frame = - create_fileSelectFrame($req_tab, "oinkmaster.conf", 'ROFILE', - \$config{oinkmaster_conf}, 'EDIT', $filetypes); - -$balloon->attach($oinkconf_frame, -statusmsg => $help{oinkconf}); - - -# Create frame with output directory. -my $outdir_frame = - create_fileSelectFrame($req_tab, "output directory", 'WRDIR', - \$config{outdir}, 'NOEDIT', undef); - -$balloon->attach($outdir_frame, -statusmsg => $help{outdir}); - - - -# Create tab with optional files/dirs. -my $opt_tab = $notebook->add("optional", - -label => "Optional files and directories", - -underline => 0, -); - -$opt_tab->configure(-bg => "$color{notebook_inact}"); - -# Create frame with alternate URL location. -$filetypes = [ - ['compressed tar files', '.tar.gz'] -]; - -my $url_frame = - create_fileSelectFrame($opt_tab, "Alternate URL", 'URL', - \$config{url}, 'NOEDIT', $filetypes); - -$balloon->attach($url_frame, -statusmsg => $help{url}); - - -# Create frame with variable file. -$filetypes = [ - ['Snort configuration files', ['.conf', '.config']], - ['All files', '*' ] -]; - -my $varfile_frame = - create_fileSelectFrame($opt_tab, "Variable file", 'WRFILE', - \$config{varfile}, 'EDIT', $filetypes); - -$balloon->attach($varfile_frame, -statusmsg => $help{varfile}); - - -# Create frame with backup dir location. -my $backupdir_frame = - create_fileSelectFrame($opt_tab, "Backup directory", 'WRDIR', - \$config{backupdir}, 'NOEDIT', undef); - -$balloon->attach($backupdir_frame, -statusmsg => $help{backupdir}); - - -# Create frame with editor location. -$filetypes = [ - ['executable files', ['.exe']], - ['All files', '*' ] -]; - -my $editor_frame = - create_fileSelectFrame($opt_tab, "Editor", 'EXECFILE', - \$config{editor}, 'NOEDIT', $filetypes); - -$balloon->attach($editor_frame, -statusmsg => $help{editor}); - - - -$notebook->pack( - -expand => 'no', - -fill => 'x', - -padx => '5', - -pady => '5', - -side => 'top' -); - - -# Create the frame to the left. -my $left_frame = $main->Frame( - -background => "$color{label}", - -border => '2', -)->pack( - -side => 'left', - -fill => 'y', -); - - -# Create "GUI settings" label. -$left_frame->Label( - -text => "GUI settings:", - -background => "$color{label}", -)->pack( - -side => 'top', - -fill => 'x', -); - - -create_actionbutton($left_frame, "Load saved settings", \&load_config); -create_actionbutton($left_frame, "Save current settings", \&save_config); - - -# Create "options" label at the top of the left frame. -$left_frame->Label( - -text => "Options:", - -background => "$color{label}", -)->pack(-side => 'top', - -fill => 'x', -); - - -# Create checkbuttons in the left frame. -$balloon->attach( - create_checkbutton($left_frame, "Careful mode", \$config{careful}), - -statusmsg => $help{careful} -); - -$balloon->attach( - create_checkbutton($left_frame, "Enable all", \$config{enable_all}), - -statusmsg => $help{enable} -); - -$balloon->attach( - create_checkbutton($left_frame, "Check for removed files", \$config{check_removed}), - -statusmsg => $help{removed} -); - - -# Create "mode" label. -$left_frame->Label( - -text => "Output mode:", - -background => "$color{label}", -)->pack( - -side => 'top', - -fill => 'x', -); - -# Create mode radiobuttons in the left frame. -create_radiobutton($left_frame, "super-quiet", \$config{output_mode}); -create_radiobutton($left_frame, "quiet", \$config{output_mode}); -create_radiobutton($left_frame, "normal", \$config{output_mode}); -create_radiobutton($left_frame, "verbose", \$config{output_mode}); - -# Create "Diff mode" label. -$left_frame->Label( - -text => "Diff mode:", - -background => "$color{label}", -)->pack( - -side => 'top', - -fill => 'x', -); - -create_radiobutton($left_frame, "detailed", \$config{diff_mode}); -create_radiobutton($left_frame, "summarized", \$config{diff_mode}); -create_radiobutton($left_frame, "remove common", \$config{diff_mode}); - - -# Create "activity messages" label. -$main->Label( - -text => "Output messages:", - -width => '130', - -background => "$color{label}", -)->pack( - -side => 'top', - -fill => 'x', -); - - - -# Pack output frame. -$out_frame->pack( - -expand => 'yes', - -fill => 'both', -); - - -# Pack help label below output window. -$help_label->pack( - -fill => 'x', -); - - -# Create "actions" label. -$left_frame->Label( - -text => "Actions:", - -background => "$color{label}", -)->pack( - -side => 'top', - -fill => 'x', -); - - -# Create action buttons. - -$balloon->attach( - create_actionbutton($left_frame, "Update rules!", \&update_rules), - -statusmsg => $help{update} -); - -$balloon->attach( - create_actionbutton($left_frame, "Clear output messages", \&clear_messages), - -statusmsg => $help{clear} -); - -$balloon->attach( - create_actionbutton($left_frame, "Save output messages", \&save_messages), - -statusmsg => $help{save} -); - -$balloon->attach( - create_actionbutton($left_frame, "Exit", \&exit), - -statusmsg => $help{exit} -); - - - -# Make the mousewheel scroll the output window. Taken from Mastering Perl/Tk. -if ($^O eq 'MSWin32') { - $out_frame->bind('<MouseWheel>' => - [ sub { $_[0]->yview('scroll', -($_[1] / 120) * 3, 'units')}, - Ev('D') ] - ); -} else { - $out_frame->bind('<4>' => sub { - $_[0]->yview('scroll', -3, 'units') unless $Tk::strictMotif; - }); - - $out_frame->bind('<5>' => sub { - $_[0]->yview('scroll', +3, 'units') unless $Tk::strictMotif; - }); -} - - - -# Now the fun begins. -if ($config{animate}) { - foreach (split(//, "Welcome to $version")) { - logmsg("$_", 'MISC'); - $out_frame->after(5); - } -} else { - logmsg("Welcome to $version", 'MISC'); -} - -logmsg("\n\n", 'MISC'); - -# Load gui settings into %config. -load_config(); - - -# Warn if any required file/directory is not set. -logmsg("No oinkmaster.pl set, please select one above!\n\n", 'ERROR') - if ($config{oinkmaster} !~ /\S/); - -logmsg("No oinkmaster configuration file set, please select one above!\n\n", 'ERROR') - if ($config{oinkmaster_conf} !~ /\S/); - -logmsg("Output directory is not set, please select one above!\n\n", 'ERROR') - if ($config{outdir} !~ /\S/); - - -MainLoop; - - - -#### END #### - - - -sub fileDialog($ $ $ $) -{ - my $var_ref = shift; - my $title = shift; - my $type = shift; - my $filetypes = shift; - my $dirname; - - if ($type eq 'WRDIR') { - if ($use_fileop) { - $dirname = Win32::FileOp::BrowseForFolder("title", CSIDL_DRIVES); - } else { - my $fs = $main->FileSelect(); - $fs->configure(-verify => ['-d', '-w'], -title => $title); - $dirname = $fs->Show; - } - $$var_ref = $dirname if ($dirname); - } elsif ($type eq 'EXECFILE' || $type eq 'ROFILE' || $type eq 'WRFILE' || $type eq 'URL') { - my $filename = $main->getOpenFile(-title => $title, -filetypes => $filetypes); - $$var_ref = $filename if ($filename); - } elsif ($type eq 'SAVEFILE') { - my $filename = $main->getSaveFile(-title => $title, -filetypes => $filetypes); - $$var_ref = $filename if ($filename); - } else { - logmsg("Unknown type ($type)\n", 'ERROR'); - } -} - - - -sub update_file_label_color($ $ $) -{ - my $label = shift; - my $filename = shift; - my $type = shift; - - $filename =~ s/^\s+//; - $filename =~ s/\s+$//; - - unless ($filename) { - $label->configure(-background => $color{file_label_not_ok}); - return (1); - } - - if ($type eq "URL") { - if ($filename =~ /^(?:http|ftp|scp):\/\/.+\.tar\.gz$/) { - $label->configure(-background => $color{file_label_ok}); - } elsif ($filename =~ /^(?:file:\/\/)*(.+\.tar\.gz)$/) { - my $file = $1; - if (-f "$file" && -r "$file") { - $label->configure(-background => $color{file_label_ok}); - } else { - $label->configure(-background => $color{file_label_not_ok}); - } - } else { - $label->configure(-background => $color{file_label_not_ok}); - } - } elsif ($type eq "ROFILE") { - if (-f "$filename" && -r "$filename") { - $label->configure(-background => $color{file_label_ok}); - } else { - $label->configure(-background => $color{file_label_not_ok}); - } - } elsif ($type eq "EXECFILE") { - if (-f "$filename" && (-x "$filename" || $^O eq 'MSWin32')) { - $label->configure(-background => $color{file_label_ok}); - } else { - $label->configure(-background => $color{file_label_not_ok}); - } - } elsif ($type eq "WRFILE") { - if (-f "$filename" && -w "$filename") { - $label->configure(-background => $color{file_label_ok}); - } else { - $label->configure(-background => $color{file_label_not_ok}); - } - } elsif ($type eq "WRDIR") { - if (-d "$filename" && -w "$filename") { - $label->configure(-background => $color{file_label_ok}); - } else { - $label->configure(-background => $color{file_label_not_ok}); - } - } else { - print STDERR "incorrect type ($type)\n"; - exit; - } - - return (1); -} - - - -sub create_checkbutton($ $ $) -{ - my $frame = shift; - my $name = shift; - my $var_ref = shift; - - my $button = $frame->Checkbutton( - -text => $name, - -background => $color{button}, - -activebackground => $color{button_active}, - -highlightbackground => $color{button_bg}, - -variable => $var_ref, - -relief => 'raise', - -anchor => 'w', - )->pack( - -fill => 'x', - -side => 'top', - -pady => '1', - ); - - return ($button); -} - - - -sub create_actionbutton($ $ $) -{ - my $frame = shift; - my $name = shift; - my $func_ref = shift; - - my $button = $frame->Button( - -text => $name, - -command => sub { - &$func_ref; - $out_frame->focus; - }, - -background => $color{button}, - -activebackground => $color{button_active}, - -highlightbackground => $color{button_bg}, - )->pack( - -fill => 'x', - ); - - return ($button); -} - - - -sub create_radiobutton($ $ $) -{ - my $frame = shift; - my $name = shift; - my $mode_ref = shift; - - my $button = $frame->Radiobutton( - -text => $name, - -highlightbackground => $color{button_bg}, - -background => $color{button}, - -activebackground => $color{button_active}, - -variable => $mode_ref, - -relief => 'raised', - -anchor => 'w', - -value => $name, - )->pack( - -side => 'top', - -pady => '1', - -fill => 'x', - ); - - return ($button); -} - - - -# Create <label><entry><browsebutton> in given frame. -sub create_fileSelectFrame($ $ $ $ $ $) -{ - my $win = shift; - my $name = shift; - my $type = shift; # FILE|DIR|URL - my $var_ref = shift; - my $edtype = shift; # EDIT|NOEDIT - my $filetypes = shift; - - # Create frame. - my $frame = $win->Frame( - -bg => $color{background}, - )->pack( - -padx => '2', - -pady => '2', - -fill => 'x' - ); - - # Create label. - my $label = $frame->Label( - -text => $name, - -width => '16', - -relief => 'raised', - -background => "$color{file_label_not_ok}", - )->pack( - -side => 'left' - ); - - my $entry; - - if ($type eq 'URL') { - $entry = $frame->BrowseEntry( - -textvariable => $var_ref, - -background => $color{entry_bg}, - -width => '80', - -choices => \@urls, - -validate => 'key', - -validatecommand => sub { update_file_label_color($label, $_[0], $type) }, - )->pack( - -side => 'left', - -expand => 'yes', - -fill => 'x' - ); - } else { - $entry = $frame->Entry( - -textvariable => $var_ref, - -background => $color{entry_bg}, - -width => '80', - -validate => 'key', - -validatecommand => sub { update_file_label_color($label, $_[0], $type) }, - )->pack( - -side => 'left', - -expand => 'yes', - -fill => 'x' - ); - } - - # Create edit-button if file is ediable. - if ($edtype eq 'EDIT') { - my $edit_but = $frame->Button( - -text => "Edit", - -background => "$color{button}", - -command => sub { - unless (-e "$$var_ref") { - logmsg("Select an existing file first!\n\n", 'ERROR'); - return; - } - - if ($config{editor}) { - $main->Busy(-recurse => 1); - logmsg("Launching " . $config{editor} . - ", close it to continue the GUI.\n\n", 'MISC'); - sleep(2); - system($config{editor}, $$var_ref); # MainLoop will be put on hold... - $main->Unbusy; - } else { - logmsg("No editor set\n\n", 'ERROR'); - } - } - )->pack( - -side => 'left', - ); - } - - # Create browse-button. - my $but = $frame->Button( - -text => "browse ...", - -background => $color{button}, - -command => sub { - fileDialog($var_ref, $name, $type, $filetypes); - } - )->pack( - -side => 'left', - ); - - return ($frame); -} - - - -sub logmsg($ $) -{ - my $text = shift; - my $type = shift; - - return unless (defined($text)); - - $out_frame->tag(qw(configure OUTPUT -foreground grey)); - $out_frame->tag(qw(configure ERROR -foreground red)); - $out_frame->tag(qw(configure MISC -foreground white)); - $out_frame->tag(qw(configure EXEC -foreground bisque2)); - - $out_frame->insert('end', "$text", "$type"); - $out_frame->see('end'); - $out_frame->update; -} - - - - -sub execute_oinkmaster(@) -{ - my @cmd = @_; - my @obfuscated_cmd; - - # Obfuscate possible password in url. - foreach my $line (@cmd) { - if ($line =~ /^(\S+:\/\/.+?):.+?@(.+)/) { - push(@obfuscated_cmd, "$1:*password*\@$2"); - } else { - push(@obfuscated_cmd, $line); - } - } - - logmsg("@obfuscated_cmd:\n", 'EXEC'); - - $main->Busy(-recurse => 1); - - if ($^O eq 'MSWin32') { - open(OINK, "@cmd 2>&1|"); - while (<OINK>) { - logmsg($_, 'OUTPUT'); - } - close(OINK); - } else { - if (open(OINK,"-|")) { - while (<OINK>) { - logmsg($_, 'OUTPUT'); - } - } else { - open(STDERR, '>&STDOUT'); - exec(@cmd); - } - close(OINK); - } - - $main->Unbusy; - logmsg("done.\n\n", 'EXEC'); -} - - - -sub clear_messages() -{ - $out_frame->delete('1.0','end'); - $out_frame->update; -} - - - -sub save_messages() -{ - my $text = $out_frame->get('1.0', 'end'); - my $title = 'Save output messages'; - my $filename; - - my $filetypes = [ - ['Log files', ['.log', '.txt']], - ['All files', '*' ] - ]; - - - if (length($text) > 1) { - fileDialog(\$filename, $title, 'SAVEFILE', $filetypes); - if (defined($filename)) { - - unless (open(LOG, ">", "$filename")) { - logmsg("Could not open $filename for writing: $!\n\n", 'ERROR'); - return; - } - - print LOG $text; - close(LOG); - logmsg("Successfully saved output messages to $filename\n\n", 'MISC'); - } - - } else { - logmsg("Nothing to save.\n\n", 'ERROR'); - } -} - - - -sub update_rules() -{ - my @cmd; - - create_cmdline(\@cmd) || return; - clear_messages(); - execute_oinkmaster(@cmd); -} - - - -sub create_cmdline($) -{ - my $cmd_ref = shift; - - my $oinkmaster = $config{oinkmaster}; - my $oinkmaster_conf = $config{oinkmaster_conf}; - my $outdir = $config{outdir}; - my $varfile = $config{varfile}; - my $url = $config{url}; - my $backupdir = $config{backupdir}; - - # Assume file:// if url prefix is missing. - if ($url) { - $url = "file://$url" unless ($url =~ /(?:http|ftp|file|scp):\/\//); - if ($url =~ /.+<oinkcode>.+/) { - logmsg("You must replace <oinkcode> with your real oinkcode, see the FAQ!\n\n", 'ERROR'); - return (0); - } - } - - $oinkmaster = File::Spec->rel2abs($oinkmaster) - if ($oinkmaster); - - $outdir = File::Spec->canonpath("$outdir"); - $backupdir = File::Spec->canonpath("$backupdir"); - - # Clean leading/trailing whitespaces. - foreach my $var_ref (\$oinkmaster, \$oinkmaster_conf, \$outdir, - \$varfile, \$url, \$backupdir) { - $$var_ref =~ s/^\s+//; - $$var_ref =~ s/\s+$//; - } - - unless ($config{oinkmaster} && -f "$config{oinkmaster}" && - (-x "$config{oinkmaster}" || $^O eq 'MSWin32')) { - logmsg("Location of oinkmaster.pl is not set correctly!\n\n", 'ERROR'); - return; - } - - unless ($oinkmaster_conf && -f "$oinkmaster_conf") { - logmsg("Location of configuration file is not set correctlyy!\n\n", 'ERROR'); - return (0); - } - - unless ($outdir && -d "$outdir") { - logmsg("Output directory is not set correctly!\n\n", 'ERROR'); - return (0); - } - - # Add leading/trailing "" if win32. - foreach my $var_ref (\$oinkmaster, \$oinkmaster_conf, \$outdir, - \$varfile, \$url, \$backupdir) { - if ($^O eq 'MSWin32' && $$var_ref) { - $$var_ref = "\"$$var_ref\""; - } - } - - push(@$cmd_ref, - "$config{perl}", "$oinkmaster", - "-C", "$oinkmaster_conf", - "-o", "$outdir"); - - push(@$cmd_ref, "-c") if ($config{careful}); - push(@$cmd_ref, "-e") if ($config{enable_all}); - push(@$cmd_ref, "-r") if ($config{check_removed}); - push(@$cmd_ref, "-q") if ($config{output_mode} eq "quiet"); - push(@$cmd_ref, "-Q") if ($config{output_mode} eq "super-quiet"); - push(@$cmd_ref, "-v") if ($config{output_mode} eq "verbose"); - push(@$cmd_ref, "-m") if ($config{diff_mode} eq "remove common"); - push(@$cmd_ref, "-s") if ($config{diff_mode} eq "summarized"); - push(@$cmd_ref, "-U", "$varfile") if ($varfile); - push(@$cmd_ref, "-b", "$backupdir") if ($backupdir); - - push(@$cmd_ref, "-u", "$url") - if ($url); - - return (1); -} - - - -# Load $config file into %config hash. -sub load_config() -{ - unless (defined($gui_config_file) && $gui_config_file) { - logmsg("Unable to determine config file location, is your \$HOME set?\n\n", 'ERROR'); - return; - } - - unless (-e "$gui_config_file") { - logmsg("$gui_config_file does not exist, keeping current/default settings\n\n", 'MISC'); - return; - } - - unless (open(RC, "<", "$gui_config_file")) { - logmsg("Could not open $gui_config_file for reading: $!\n\n", 'ERROR'); - return; - } - - while (<RC>) { - next unless (/^(\S+)=(.*)/); - $config{$1} = $2; - } - - close(RC); - logmsg("Successfully loaded GUI settings from $gui_config_file\n\n", 'MISC'); -} - - - -# Save %config into file $config. -sub save_config() -{ - unless (defined($gui_config_file) && $gui_config_file) { - logmsg("Unable to determine config file location, is your \$HOME set?\n\n", 'ERROR'); - return; - } - - unless (open(RC, ">", "$gui_config_file")) { - logmsg("Could not open $gui_config_file for writing: $!\n\n", 'ERROR'); - return; - } - - print RC "# Automatically created by Oinkgui. ". - "Do not edit directly unless you have to.\n"; - - foreach my $option (sort(keys(%config))) { - print RC "$option=$config{$option}\n"; - } - - close(RC); - logmsg("Successfully saved current GUI settings to $gui_config_file\n\n", 'MISC'); -} diff --git a/config/snort-old/bin/oinkmaster_contrib/oinkmaster.pl b/config/snort-old/bin/oinkmaster_contrib/oinkmaster.pl deleted file mode 100644 index f9c4d215..00000000 --- a/config/snort-old/bin/oinkmaster_contrib/oinkmaster.pl +++ /dev/null @@ -1,2754 +0,0 @@ -#!/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 <andreaso@it.su.se> -# 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 <andreaso@it.su.se>'; -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 <file>\" 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 <outdir> [options] - -<outdir> is where to put the new files. -This should be the directory where you store your Snort rules. - -Options: --b <dir> Backup your old rules into <dir> before overwriting them --c Careful mode (dry run) - check for changes but do not update anything --C <file> 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 <file> 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 <url> Download from this URL instead of URL(s) in the configuration file - (http|https|ftp|file|scp:// ... .tar.gz|.gz, or dir://<dir>) - May be specified multiple times to grab multiple rules archives --U <file> Merge new variables from downloaded snort.conf(s) into <file> --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 <dir> 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 = <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 <SIDORFILE[,SIDORFILE, ...]> "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 <SID[,SID, ...]> - } 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 <SID[,SID, ...]> - } 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 <SID[,SID, ...]> - } 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 <file[,file, ...]> - } 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 <url> or url=... in the conf). - clean_exit("URL not specified. Specify at least one \"url=<url>\" in the \n". - "Oinkmaster configuration file or use the \"-u <url>\" 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 (<LOG>) { - $_ = "$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 = <TAR>; - 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/<file>$ - - 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 = <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 = <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 = <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 = <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 <file> (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 <directory> 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 = <LOCAL_VAR_FILE>; - - 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 = <DIST_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 = <OLD_LOCAL_CONF>; - 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 = <STDIN>; - $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 #### |