diff options
Diffstat (limited to 'config/snort/bin')
-rw-r--r-- | config/snort/bin/barnyard2 | bin | 0 -> 641791 bytes | |||
-rw-r--r-- | config/snort/bin/oinkmaster_contrib/README.contrib | 84 | ||||
-rw-r--r-- | config/snort/bin/oinkmaster_contrib/addmsg.pl | 299 | ||||
-rw-r--r-- | config/snort/bin/oinkmaster_contrib/addsid.pl | 382 | ||||
-rw-r--r-- | config/snort/bin/oinkmaster_contrib/create-sidmap.pl | 280 | ||||
-rw-r--r-- | config/snort/bin/oinkmaster_contrib/makesidex.pl | 261 | ||||
-rw-r--r-- | config/snort/bin/oinkmaster_contrib/oinkgui.pl | 1046 |
7 files changed, 2352 insertions, 0 deletions
diff --git a/config/snort/bin/barnyard2 b/config/snort/bin/barnyard2 Binary files differnew file mode 100644 index 00000000..b942e87f --- /dev/null +++ b/config/snort/bin/barnyard2 diff --git a/config/snort/bin/oinkmaster_contrib/README.contrib b/config/snort/bin/oinkmaster_contrib/README.contrib new file mode 100644 index 00000000..6923fa26 --- /dev/null +++ b/config/snort/bin/oinkmaster_contrib/README.contrib @@ -0,0 +1,84 @@ +# $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/bin/oinkmaster_contrib/addmsg.pl b/config/snort/bin/oinkmaster_contrib/addmsg.pl new file mode 100644 index 00000000..e5866d6f --- /dev/null +++ b/config/snort/bin/oinkmaster_contrib/addmsg.pl @@ -0,0 +1,299 @@ +#!/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/bin/oinkmaster_contrib/addsid.pl b/config/snort/bin/oinkmaster_contrib/addsid.pl new file mode 100644 index 00000000..64255d22 --- /dev/null +++ b/config/snort/bin/oinkmaster_contrib/addsid.pl @@ -0,0 +1,382 @@ +#!/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/bin/oinkmaster_contrib/create-sidmap.pl b/config/snort/bin/oinkmaster_contrib/create-sidmap.pl new file mode 100644 index 00000000..e1ce12ab --- /dev/null +++ b/config/snort/bin/oinkmaster_contrib/create-sidmap.pl @@ -0,0 +1,280 @@ +#!/usr/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/bin/oinkmaster_contrib/makesidex.pl b/config/snort/bin/oinkmaster_contrib/makesidex.pl new file mode 100644 index 00000000..80354735 --- /dev/null +++ b/config/snort/bin/oinkmaster_contrib/makesidex.pl @@ -0,0 +1,261 @@ +#!/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/bin/oinkmaster_contrib/oinkgui.pl b/config/snort/bin/oinkmaster_contrib/oinkgui.pl new file mode 100644 index 00000000..4e96f7db --- /dev/null +++ b/config/snort/bin/oinkmaster_contrib/oinkgui.pl @@ -0,0 +1,1046 @@ +#!/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'); +} |