}
}
-####################### End added for snort rules control #################################
+# Check if any error has been stored.
+if (-e $IDS::storederrorfile) {
+ # Open file to read in the stored error message.
+ open(FILE, "<$IDS::storederrorfile") or die "Could not open $IDS::storederrorfile. $!\n";
+
+ # Read the stored error message.
+ $errormessage = <FILE>;
-if ($snortsettings{'OINKCODE'} ne "") {
- $errormessage = $Lang::tr{'invalid input for oink code'} unless ($snortsettings{'OINKCODE'} =~ /^[a-z0-9]+$/);
+ # Close file.
+ close (FILE);
+
+ # Delete the file, which is now not longer required.
+ unlink($IDS::storederrorfile);
}
-
-if (!$errormessage) {
- if ($snortsettings{'RULES'} eq 'subscripted') {
- $url=" https://www.snort.org/rules/snortrules-snapshot-29120.tar.gz?oinkcode=$snortsettings{'OINKCODE'}";
- } elsif ($snortsettings{'RULES'} eq 'registered') {
- $url=" https://www.snort.org/rules/snortrules-snapshot-29120.tar.gz?oinkcode=$snortsettings{'OINKCODE'}";
- } elsif ($snortsettings{'RULES'} eq 'community') {
- $url=" https://www.snort.org/rules/community";
- } else {
- $url="https://rules.emergingthreats.net/open/snort-2.9.0/emerging.rules.tar.gz";
+## Grab all available snort rules and store them in the idsrules hash.
+#
+# Open snort rules directory and do a directory listing.
+opendir(DIR, $IDS::rulespath) or die $!;
+ # Loop through the direcory.
+ while (my $file = readdir(DIR)) {
+
+ # We only want files.
+ next unless (-f "$IDS::rulespath/$file");
+
+ # Ignore empty files.
+ next if (-z "$IDS::rulespath/$file");
+
+ # Use a regular expression to find files ending in .rules
+ next unless ($file =~ m/\.rules$/);
+
+ # Ignore files which are not read-able.
+ next unless (-R "$IDS::rulespath/$file");
+
+ # Skip whitelist rules file.
+ next if( $file eq "whitelist.rules");
+
+ # Call subfunction to read-in rulefile and add rules to
+ # the idsrules hash.
+ &readrulesfile("$file");
}
- if ($snortsettings{'ACTION'} eq $Lang::tr{'save'} && $snortsettings{'ACTION2'} eq "snort" ) {
- &General::writehash("${General::swroot}/snort/settings", \%snortsettings);
- if ($snortsettings{'ENABLE_SNORT'} eq 'on')
- {
- system ('/usr/bin/touch', "${General::swroot}/snort/enable");
- } else {
- unlink "${General::swroot}/snort/enable";
+closedir(DIR);
+
+# Gather used rulefiles.
+#
+# Check if the file for activated rulefiles is not empty.
+if(-f $IDS::used_rulefiles_file) {
+ # Open the file for used rulefile and read-in content.
+ open(FILE, $IDS::used_rulefiles_file) or die "Could not open $IDS::used_rulefiles_file. $!\n";
+
+ # Read-in content.
+ my @lines = <FILE>;
+
+ # Close file.
+ close(FILE);
+
+ # Loop through the array.
+ foreach my $line (@lines) {
+ # Remove newlines.
+ chomp($line);
+
+ # Skip comments.
+ next if ($line =~ /\#/);
+
+ # Skip blank lines.
+ next if ($line =~ /^\s*$/);
+
+ # Gather rule sid and message from the ruleline.
+ if ($line =~ /.*- (.*)/) {
+ my $rulefile = $1;
+
+ # Check if the current rulefile exists in the %idsrules hash.
+ # If not, the file probably does not exist anymore or contains
+ # no rules.
+ if($idsrules{$rulefile}) {
+ # Add the rulefile state to the %idsrules hash.
+ $idsrules{$rulefile}{'Rulefile'}{'State'} = "on";
+ }
}
- if ($snortsettings{'ENABLE_SNORT_GREEN'} eq 'on')
- {
- system ('/usr/bin/touch', "${General::swroot}/snort/enable_green");
- } else {
- unlink "${General::swroot}/snort/enable_green";
+ }
+}
+
+# Save ruleset configuration.
+if ($cgiparams{'RULESET'} eq $Lang::tr{'save'}) {
+ my %oldsettings;
+
+ # Read-in current (old) IDS settings.
+ &General::readhash("$IDS::rules_settings_file", \%oldsettings);
+
+ # Prevent form name from been stored in conf file.
+ delete $cgiparams{'RULESET'};
+
+ # Check if an oinkcode has been provided.
+ if ($cgiparams{'OINKCODE'}) {
+ # Check if the oinkcode contains unallowed chars.
+ unless ($cgiparams{'OINKCODE'} =~ /^[a-z0-9]+$/) {
+ $errormessage = $Lang::tr{'invalid input for oink code'};
}
- if ($snortsettings{'ENABLE_SNORT_BLUE'} eq 'on')
- {
- system ('/usr/bin/touch', "${General::swroot}/snort/enable_blue");
- } else {
- unlink "${General::swroot}/snort/enable_blue";
+ }
+
+ # Go on if there are no error messages.
+ if (!$errormessage) {
+ # Store settings into settings file.
+ &General::writehash("$IDS::rules_settings_file", \%cgiparams);
+ }
+
+ # Check if the the automatic rule update hass been touched.
+ if($cgiparams{'AUTOUPDATE_INTERVAL'} ne $oldsettings{'AUTOUPDATE_INTERVAL'}) {
+ # Call suricatactrl to set the new interval.
+ &IDS::call_suricatactrl("cron", $cgiparams{'AUTOUPDATE_INTERVAL'});
+ }
+
+# Save ruleset.
+} elsif ($cgiparams{'RULESET'} eq $Lang::tr{'update'}) {
+ # Arrays to store which rulefiles have been enabled and will be used.
+ my @enabled_rulefiles;
+
+ # Hash to store the user-enabled and disabled sids.
+ my %enabled_disabled_sids;
+
+ # Loop through the hash of idsrules.
+ foreach my $rulefile(keys %idsrules) {
+ # Check if the rulefile is enabled.
+ if ($cgiparams{$rulefile} eq "on") {
+ # Add rulefile to the array of enabled rulefiles.
+ push(@enabled_rulefiles, $rulefile);
+
+ # Drop item from cgiparams hash.
+ delete $cgiparams{$rulefile};
}
- if ($snortsettings{'ENABLE_SNORT_ORANGE'} eq 'on')
- {
- system ('/usr/bin/touch', "${General::swroot}/snort/enable_orange");
- } else {
- unlink "${General::swroot}/snort/enable_orange";
+ }
+
+ # Read-in the files for enabled/disabled sids.
+ # This will be done by calling the read_enabled_disabled_sids_file function two times
+ # and merge the returned hashes together into the enabled_disabled_sids hash.
+ %enabled_disabled_sids = (
+ &read_enabled_disabled_sids_file($IDS::disabled_sids_file),
+ &read_enabled_disabled_sids_file($IDS::enabled_sids_file));
+
+ # Loop through the hash of idsrules.
+ foreach my $rulefile (keys %idsrules) {
+ # Loop through the single rules of the rulefile.
+ foreach my $sid (keys %{$idsrules{$rulefile}}) {
+ # Skip the current sid if it is not numeric.
+ next unless ($sid =~ /\d+/ );
+
+ # Check if there exists a key in the cgiparams hash for this sid.
+ if (exists($cgiparams{$sid})) {
+ # Look if the rule is disabled.
+ if ($idsrules{$rulefile}{$sid}{'State'} eq "off") {
+ # Check if the state has been set to 'on'.
+ if ($cgiparams{$sid} eq "on") {
+ # Add/Modify the sid to/in the enabled_disabled_sids hash.
+ $enabled_disabled_sids{$sid} = "enabled";
+
+ # Drop item from cgiparams hash.
+ delete $cgiparams{$rulefile}{$sid};
+ }
+ }
+ } else {
+ # Look if the rule is enabled.
+ if ($idsrules{$rulefile}{$sid}{'State'} eq "on") {
+ # Check if the state is 'on' and should be disabled.
+ # In this case there is no entry
+ # for the sid in the cgiparams hash.
+ # Add/Modify it to/in the enabled_disabled_sids hash.
+ $enabled_disabled_sids{$sid} = "disabled";
+
+ # Drop item from cgiparams hash.
+ delete $cgiparams{$rulefile}{$sid};
+ }
+ }
}
- if ($snortsettings{'ENABLE_PREPROCESSOR_HTTP_INSPECT'} eq 'on')
- {
- system ('/usr/bin/touch', "${General::swroot}/snort/enable_preprocessor_http_inspect");
- } else {
- unlink "${General::swroot}/snort/enable_preprocessor_http_inspect";
+ }
+
+ # Open enabled sid's file for writing.
+ open(ENABLED_FILE, ">$IDS::enabled_sids_file") or die "Could not write to $IDS::enabled_sids_file. $!\n";
+
+ # Open disabled sid's file for writing.
+ open(DISABLED_FILE, ">$IDS::disabled_sids_file") or die "Could not write to $IDS::disabled_sids_file. $!\n";
+
+ # Write header to the files.
+ print ENABLED_FILE "#Autogenerated file. Any custom changes will be overwritten!\n";
+ print DISABLED_FILE "#Autogenerated file. Any custom changes will be overwritten!\n";
+
+ # Check if the hash for enabled/disabled files contains any entries.
+ if (%enabled_disabled_sids) {
+ # Loop through the hash.
+ foreach my $sid (keys %enabled_disabled_sids) {
+ # Check if the sid is enabled.
+ if ($enabled_disabled_sids{$sid} eq "enabled") {
+ # Print the sid to the enabled_sids file.
+ print ENABLED_FILE "enablesid $sid\n";
+ # Check if the sid is disabled.
+ } elsif ($enabled_disabled_sids{$sid} eq "disabled") {
+ # Print the sid to the disabled_sids file.
+ print DISABLED_FILE "disablesid $sid\n";
+ # Something strange happende - skip the current sid.
+ } else {
+ next;
+ }
}
+ }
+
+ # Close file for enabled_sids after writing.
+ close(ENABLED_FILE);
+
+ # Close file for disabled_sids after writing.
+ close(DISABLED_FILE);
+
+ # Call function to generate and write the used rulefiles file.
+ &IDS::write_used_rulefiles_file(@enabled_rulefiles);
+
+ # Lock the webpage and print message.
+ &working_notice("$Lang::tr{'snort working'}");
- system('/usr/local/bin/snortctrl restart >/dev/null');
+ # Call oinkmaster to alter the ruleset.
+ &IDS::oinkmaster();
+
+ # Check if the IDS is running.
+ if(&IDS::ids_is_running()) {
+ # Call suricatactrl to perform a reload.
+ &IDS::call_suricatactrl("reload");
}
- # INSTALLMD5 is not in the form, so not retrieved by getcgihash
- &General::readhash("${General::swroot}/snort/settings", \%snortsettings);
+ # Reload page.
+ &reload();
- if ($snortsettings{'ACTION'} eq $Lang::tr{'download new ruleset'} || $snortsettings{'ACTION'} eq $Lang::tr{'upload new ruleset'}) {
- my @df = `/bin/df -B M /var`;
- foreach my $line (@df) {
- next if $line =~ m/^Filesystem/;
- my $return;
+# Download new ruleset.
+} elsif ($cgiparams{'RULESET'} eq $Lang::tr{'download new ruleset'}) {
+ # Check if the red device is active.
+ unless (-e "${General::swroot}/red/active") {
+ $errormessage = $Lang::tr{'could not download latest updates'};
+ }
- if ($line =~ m/dev/ ) {
- $line =~ m/^.* (\d+)M.*$/;
- my @temp = split(/ +/,$line);
- if ($1<300) {
- $errormessage = "$Lang::tr{'not enough disk space'} < 300MB, /var $1MB";
- } else {
- if ( $snortsettings{'ACTION'} eq $Lang::tr{'download new ruleset'}) {
- &downloadrulesfile();
- sleep(3);
- $return = `cat /var/tmp/log 2>/dev/null`;
-
- } elsif ( $snortsettings{'ACTION'} eq $Lang::tr{'upload new ruleset'}) {
- my $upload = $a->param("UPLOAD");
- open UPLOADFILE, ">/var/tmp/snortrules.tar.gz";
- binmode $upload;
- while ( <$upload> ) {
- print UPLOADFILE;
- }
- close UPLOADFILE;
- }
+ # Check if enought free disk space is availabe.
+ if(&IDS::checkdiskspace()) {
+ $errormessage = "$Lang::tr{'not enough disk space'}";
+ }
- if ($return =~ "ERROR") {
- $errormessage = "<br /><pre>".$return."</pre>";
- } else {
- system("/usr/local/bin/oinkmaster.pl -v -s -u file:///var/tmp/snortrules.tar.gz -C /var/ipfire/snort/oinkmaster.conf -o /etc/snort/rules >>/var/tmp/log 2>&1 &");
- sleep(2);
- }
- }
+ # Check if any errors happend.
+ unless ($errormessage) {
+ # Lock the webpage and print notice about downloading
+ # a new ruleset.
+ &working_notice("$Lang::tr{'snort working'}");
+
+ # Call subfunction to download the ruleset.
+ if(&IDS::downloadruleset()) {
+ $errormessage = $Lang::tr{'could not download latest updates'};
+
+ # Call function to store the errormessage.
+ &IDS::_store_error_message($errormessage);
+
+ # Preform a reload of the page.
+ &reload();
+ } else {
+ # Call subfunction to launch oinkmaster.
+ &IDS::oinkmaster();
+
+ # Check if the IDS is running.
+ if(&IDS::ids_is_running()) {
+ # Call suricatactrl to perform a reload.
+ &IDS::call_suricatactrl("reload");
+ }
+
+ # Perform a reload of the page.
+ &reload();
+ }
+ }
+# Save snort settings.
+} elsif ($cgiparams{'IDS'} eq $Lang::tr{'save'}) {
+ my %oldidssettings;
+ my $reload_page;
+ my $monitored_zones = 0;
+
+ # Read-in current (old) IDS settings.
+ &General::readhash("$IDS::ids_settings_file", \%oldidssettings);
+
+ # Prevent form name from been stored in conf file.
+ delete $cgiparams{'IDS'};
+
+ # Check if the IDS should be enabled.
+ if ($cgiparams{'ENABLE_IDS'} eq "on") {
+ # Check if any ruleset is available. Otherwise abort and display an error.
+ unless(%idsrules) {
+ $errormessage = $Lang::tr{'ids no ruleset available'};
+ }
+
+ # Loop through the array of available interfaces.
+ foreach my $zone (@network_zones) {
+ # Convert interface name into upper case.
+ my $zone_upper = uc($zone);
+
+ # Check if the IDS is enabled for this interaces.
+ if ($cgiparams{"ENABLE_IDS_$zone_upper"}) {
+ # Increase count.
+ $monitored_zones++;
}
}
+
+ # Check if at least one zone should be monitored, or show an error.
+ unless ($monitored_zones >= 1) {
+ $errormessage = $Lang::tr{'ids no network zone'};
+ }
+ }
+
+ # Go on if there are no error messages.
+ if (!$errormessage) {
+ # Store settings into settings file.
+ &General::writehash("$IDS::ids_settings_file", \%cgiparams);
+ }
+
+ # Generate file to store the home net.
+ &IDS::generate_home_net_file();
+
+ # Temporary variable to set the ruleaction.
+ # Default is "drop" to use suricata as IPS.
+ my $ruleaction="drop";
+
+ # Check if the traffic only should be monitored.
+ if($cgiparams{'MONITOR_TRAFFIC_ONLY'} eq 'on') {
+ # Switch the ruleaction to "alert".
+ # Suricata acts as an IDS only.
+ $ruleaction="alert";
+ }
+
+ # Write the modify sid's file and pass the taken ruleaction.
+ &IDS::write_modify_sids_file($ruleaction);
+
+ # Check if "MONITOR_TRAFFIC_ONLY" has been changed.
+ if($cgiparams{'MONITOR_TRAFFIC_ONLY'} ne $oldidssettings{'MONITOR_TRAFFIC_ONLY'}) {
+ # Check if a ruleset exists.
+ if (%idsrules) {
+ # Lock the webpage and print message.
+ &working_notice("$Lang::tr{'snort working'}");
+
+ # Call oinkmaster to alter the ruleset.
+ &IDS::oinkmaster();
+
+ # Set reload_page to "True".
+ $reload_page="True";
+ }
+ }
+
+ # Check if the IDS currently is running.
+ if(&IDS::ids_is_running()) {
+ # Check if ENABLE_IDS is set to on.
+ if($cgiparams{'ENABLE_IDS'} eq "on") {
+ # Call suricatactrl to perform a reload of suricata.
+ &IDS::call_suricatactrl("reload");
+ } else {
+ # Call suricatactrl to stop suricata.
+ &IDS::call_suricatactrl("stop");
+ }
+ } else {
+ # Call suricatactrl to start suricata.
+ &IDS::call_suricatactrl("start");
+ }
+
+ # Check if the page should be reloaded.
+ if ($reload_page) {
+ # Perform a reload of the page.
+ &reload();
}
}