]> git.ipfire.org Git - ipfire-2.x.git/blobdiff - html/cgi-bin/ids.cgi
ids.cgi: Format and show date of the current ruleset again
[ipfire-2.x.git] / html / cgi-bin / ids.cgi
index 3e4822cd41f327afe10d76a3aec168777d049ec6..313714486553d5debd53ec67477eb91912370009 100644 (file)
@@ -34,6 +34,7 @@ my %color = ();
 my %mainsettings = ();
 my %idsrules = ();
 my %idssettings=();
+my %rulessettings=();
 my %rulesetsources = ();
 my %cgiparams=();
 my %checked=();
@@ -48,36 +49,18 @@ my %ignored=();
 # the list of zones in an array.
 my @network_zones = &IDS::get_available_network_zones();
 
-# File where the used rulefiles are stored.
-my $idsusedrulefilesfile = "$IDS::settingsdir/suricata-used-rulefiles.yaml";
-
-# File where the addresses of the homenet are stored.
-my $idshomenetfile = "$IDS::settingsdir/suricata-homenet.yaml";
-
-# File which contains the enabled sids.
-my $enabled_sids_file = "$IDS::settingsdir/oinkmaster-enabled-sids.conf";
-
-# File which contains the disabled sids.
-my $disabled_sids_file = "$IDS::settingsdir/oinkmaster-disabled-sids.conf";
-
-# File which contains wheater the rules should be changed.
-my $modify_sids_file = "$IDS::settingsdir/oinkmaster-modify-sids.conf";
-
-# File which stores the configured settings for whitelisted addresses.
-my $ignoredfile = "$IDS::settingsdir/ignored";
-
-# File which contains the rules to whitelist addresses on suricata.
-my $whitelistfile = "$IDS::rulespath/whitelist.rules";
-
 my $errormessage;
 
 # Create files if they does not exist yet.
-unless (-f "$enabled_sids_file") { &IDS::create_empty_file($enabled_sids_file); }
-unless (-f "$disabled_sids_file") { &IDS::create_empty_file($disabled_sids_file); }
-unless (-f "$modify_sids_file") { &IDS::create_empty_file($modify_sids_file); }
-unless (-f "$idsusedrulefilesfile") { &IDS::create_empty_file($idsusedrulefilesfile); }
-unless (-f "$ignoredfile") { &IDS::create_empty_file($ignoredfile); }
-unless (-f "$whitelistfile" ) { &IDS::create_empty_file($whitelistfile); }
+&IDS::check_and_create_filelayout();
+
+# Hash which contains the colour code of a network zone.
+my %colourhash = (
+       'red' => $Header::colourred,
+       'green' => $Header::colourgreen,
+       'blue' => $Header::colourblue,
+       'orange' => $Header::colourorange
+);
 
 &Header::showhttpheaders();
 
@@ -110,7 +93,7 @@ if (($cgiparams{'WHITELIST'} eq $Lang::tr{'add'}) || ($cgiparams{'WHITELIST'} eq
                my $new_entry_remark = $cgiparams{'IGNORE_ENTRY_REMARK'};
 
                # Read-in ignoredfile.
-               &General::readhasharray($ignoredfile, \%ignored);
+               &General::readhasharray($IDS::ignored_file, \%ignored);
 
                # Check if we should edit an existing entry and got an ID.
                if (($cgiparams{'WHITELIST'} eq $Lang::tr{'update'}) && ($cgiparams{'ID'})) {
@@ -145,10 +128,10 @@ if (($cgiparams{'WHITELIST'} eq $Lang::tr{'add'}) || ($cgiparams{'WHITELIST'} eq
                $ignored{$id} = ["$new_entry_address", "$new_entry_remark", "$status"];
 
                # Write the changed ignored hash to the ignored file.
-               &General::writehasharray($ignoredfile, \%ignored);
+               &General::writehasharray($IDS::ignored_file, \%ignored);
 
                # Regenerate the ignore file.
-               &GenerateIgnoreFile();
+               &IDS::generate_ignore_file();
        }
 
        # Check if the IDS is running.
@@ -172,7 +155,7 @@ if (($cgiparams{'WHITELIST'} eq $Lang::tr{'add'}) || ($cgiparams{'WHITELIST'} eq
                undef($cgiparams{'ID'});
 
                # Read-in ignoredfile.
-               &General::readhasharray($ignoredfile, \%ignored);
+               &General::readhasharray($IDS::ignored_file, \%ignored);
 
                # Grab the configured status of the corresponding entry.
                my $status = $ignored{$id}[2];
@@ -188,10 +171,10 @@ if (($cgiparams{'WHITELIST'} eq $Lang::tr{'add'}) || ($cgiparams{'WHITELIST'} eq
                $ignored{$id} = ["$ignored{$id}[0]", "$ignored{$id}[1]", "$status"];
 
                # Write the changed ignored hash to the ignored file.
-               &General::writehasharray($ignoredfile, \%ignored);
+               &General::writehasharray($IDS::ignored_file, \%ignored);
 
                # Regenerate the ignore file.
-               &GenerateIgnoreFile();
+               &IDS::generate_ignore_file();
 
                # Check if the IDS is running.
                if(&IDS::ids_is_running()) {
@@ -206,7 +189,7 @@ if (($cgiparams{'WHITELIST'} eq $Lang::tr{'add'}) || ($cgiparams{'WHITELIST'} eq
        my %ignored = ();
 
        # Read-in ignoredfile.
-       &General::readhasharray($ignoredfile, \%ignored);
+       &General::readhasharray($IDS::ignored_file, \%ignored);
 
        # Drop entry from the hash.
        delete($ignored{$cgiparams{'ID'}});
@@ -215,10 +198,10 @@ if (($cgiparams{'WHITELIST'} eq $Lang::tr{'add'}) || ($cgiparams{'WHITELIST'} eq
        undef($cgiparams{'ID'});
 
        # Write the changed ignored hash to the ignored file.
-       &General::writehasharray($ignoredfile, \%ignored);
+       &General::writehasharray($IDS::ignored_file, \%ignored);
 
        # Regenerate the ignore file.
-       &GenerateIgnoreFile();
+       &IDS::generate_ignore_file();
 
        # Check if the IDS is running.
        if(&IDS::ids_is_running()) {
@@ -227,6 +210,22 @@ if (($cgiparams{'WHITELIST'} eq $Lang::tr{'add'}) || ($cgiparams{'WHITELIST'} eq
        }
 }
 
+# Check if the page is locked, in this case, the ids_page_lock_file exists.
+if (-e $IDS::ids_page_lock_file) {
+       # Lock the webpage and print notice about autoupgrade of the ruleset
+       # is in progess.
+       &working_notice("$Lang::tr{'ids ruleset autoupdate in progress'}");
+
+       # Loop and check if the file still exists.
+       while(-e $IDS::ids_page_lock_file) {
+               # Sleep for a second and re-check.
+               sleep 1;
+       }
+
+       # Page has been unlocked, perform a reload.
+       &reload();
+}
+
 # Check if any error has been stored.
 if (-e $IDS::storederrorfile) {
         # Open file to read in the stored error message.
@@ -242,10 +241,9 @@ if (-e $IDS::storederrorfile) {
         unlink($IDS::storederrorfile);
 }
 
-
-## Grab all available snort rules and store them in the idsrules hash.
+## Grab all available rules and store them in the idsrules hash.
 #
-# Open snort rules directory and do a directory listing.
+# Open rules directory and do a directory listing.
 opendir(DIR, $IDS::rulespath) or die $!;
        # Loop through the direcory.
        while (my $file = readdir(DIR)) {
@@ -275,9 +273,9 @@ closedir(DIR);
 # Gather used rulefiles.
 #
 # Check if the file for activated rulefiles is not empty.
-if(-f $idsusedrulefilesfile) {
+if(-f $IDS::used_rulefiles_file) {
        # Open the file for used rulefile and read-in content.
-       open(FILE, $idsusedrulefilesfile) or die "Could not open $idsusedrulefilesfile. $!\n";
+       open(FILE, $IDS::used_rulefiles_file) or die "Could not open $IDS::used_rulefiles_file. $!\n";
 
        # Read-in content.
        my @lines = <FILE>;
@@ -311,8 +309,94 @@ if(-f $idsusedrulefilesfile) {
        }
 }
 
+# Save ruleset configuration.
+if ($cgiparams{'RULESET'} eq $Lang::tr{'save'}) {
+       my %oldsettings;
+       my %rulesetsources;
+
+       # Read-in current (old) IDS settings.
+       &General::readhash("$IDS::rules_settings_file", \%oldsettings);
+
+       # Get all available ruleset locations.
+       &General::readhash("$IDS::rulesetsourcesfile", \%rulesetsources);
+
+       # Prevent form name from been stored in conf file.
+       delete $cgiparams{'RULESET'};
+
+       # Grab the URL based on the choosen vendor.
+       my $url = $rulesetsources{$cgiparams{'RULES'}};
+
+       # Check if the choosen vendor (URL) requires an subscription/oinkcode.
+       if ($url =~ /\<oinkcode\>/ ) {
+               # Check if an subscription/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'};
+                       }
+               } else {
+                       # Print an error message, that an subsription/oinkcode is required for this
+                       # vendor.
+                       $errormessage = $Lang::tr{'ids oinkcode required'};
+               }
+       }
+
+       # 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'});
+               }
+
+               # Check if a ruleset is present - if not or the source has been changed download it.
+               if((! %idsrules) || ($oldsettings{'RULES'} ne $cgiparams{'RULES'})) {
+                       # Check if the red device is active.
+                       unless (-e "${General::swroot}/red/active") {
+                               $errormessage = "$Lang::tr{'could not download latest updates'} - $Lang::tr{'system is offline'}";
+                       }
+
+                       # Check if enought free disk space is availabe.
+                       if(&IDS::checkdiskspace()) {
+                               $errormessage = "$Lang::tr{'not enough disk space'}";
+                       }
+
+                       # Check if any errors happend.
+                       unless ($errormessage) {
+                               # Lock the webpage and print notice about downloading
+                               # a new ruleset.
+                               &working_notice("$Lang::tr{'ids 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);
+                               } else {
+                                       # Call subfunction to launch oinkmaster.
+                                       &IDS::oinkmaster();
+                               }
+
+                               # Check if the IDS is running.
+                               if(&IDS::ids_is_running()) {
+                                       # Call suricatactrl to stop the IDS - because of the changed
+                                       # ruleset - the use has to configure it before suricata can be
+                                       # used again.
+                                       &IDS::call_suricatactrl("stop");
+                               }
+
+                               # Perform a reload of the page.
+                               &reload();
+                       }
+               }
+       }
+
 # Save ruleset.
-if ($cgiparams{'RULESET'} eq $Lang::tr{'update'}) {
+} elsif ($cgiparams{'RULESET'} eq $Lang::tr{'ids apply'}) {
        # Arrays to store which rulefiles have been enabled and will be used.
        my @enabled_rulefiles;
 
@@ -335,8 +419,8 @@ if ($cgiparams{'RULESET'} eq $Lang::tr{'update'}) {
        # 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($disabled_sids_file),
-               &read_enabled_disabled_sids_file($enabled_sids_file));
+               &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) {
@@ -375,10 +459,10 @@ if ($cgiparams{'RULESET'} eq $Lang::tr{'update'}) {
        }
 
        # Open enabled sid's file for writing.
-       open(ENABLED_FILE, ">$enabled_sids_file") or die "Could not write to $enabled_sids_file. $!\n";
+       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, ">$disabled_sids_file") or die "Could not write to $disabled_sids_file. $!\n";
+       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";
@@ -409,32 +493,11 @@ if ($cgiparams{'RULESET'} eq $Lang::tr{'update'}) {
        # Close file for disabled_sids after writing.
        close(DISABLED_FILE);
 
-       # Open file for used rulefiles.
-       open (FILE, ">$idsusedrulefilesfile") or die "Could not write to $idsusedrulefilesfile. $!\n";
-
-       # Write yaml header to the file.
-       print FILE "%YAML 1.1\n";
-       print FILE "---\n\n";
-
-       # Write header to file.
-       print FILE "#Autogenerated file. Any custom changes will be overwritten!\n";
-
-       # Check if the enabled_rulefiles array contains any entries.
-       if (@enabled_rulefiles) {
-               # Allways load the whitelist.
-               print FILE " - whitelist.rules\n";
-
-               # Loop through the array of rulefiles which should be loaded and write them to the file.
-               foreach my $file (@enabled_rulefiles) {
-                       print FILE " - $file\n";
-               }
-       }
-
-       # Close file after writing.
-       close(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'}");
+       &working_notice("$Lang::tr{'ids apply ruleset changes'}");
 
        # Call oinkmaster to alter the ruleset.
        &IDS::oinkmaster();
@@ -449,10 +512,10 @@ if ($cgiparams{'RULESET'} eq $Lang::tr{'update'}) {
        &reload();
 
 # Download new ruleset.
-} elsif ($cgiparams{'RULESET'} eq $Lang::tr{'download new ruleset'}) {
+} elsif ($cgiparams{'RULESET'} eq $Lang::tr{'update ruleset'}) {
        # Check if the red device is active.
        unless (-e "${General::swroot}/red/active") {
-               $errormessage = $Lang::tr{'could not download latest updates'};
+               $errormessage = "$Lang::tr{'could not download latest updates'} - $Lang::tr{'system is offline'}";
        }
 
        # Check if enought free disk space is availabe.
@@ -464,7 +527,7 @@ if ($cgiparams{'RULESET'} eq $Lang::tr{'update'}) {
        unless ($errormessage) {
                # Lock the webpage and print notice about downloading
                # a new ruleset.
-               &working_notice("$Lang::tr{'snort working'}");
+               &working_notice("$Lang::tr{'ids download new ruleset'}");
 
                # Call subfunction to download the ruleset.
                if(&IDS::downloadruleset()) {
@@ -489,61 +552,72 @@ if ($cgiparams{'RULESET'} eq $Lang::tr{'update'}) {
                        &reload();
                }
        }
-# Save snort settings.
+# Save IDS 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::settingsdir/settings", \%oldidssettings);
+       &General::readhash("$IDS::ids_settings_file", \%oldidssettings);
 
        # Prevent form name from been stored in conf file.
        delete $cgiparams{'IDS'};
 
-       # 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'};
+       # 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::settingsdir/settings", \%cgiparams);
+               &General::writehash("$IDS::ids_settings_file", \%cgiparams);
        }
 
        # Generate file to store the home net.
-       &generate_home_net_file();
-
-       # Check if the the automatic rule update hass been touched.
-       if($cgiparams{'AUTOUPDATE_INTERVAL'} ne $oldidssettings{'AUTOUPDATE_INTERVAL'}) {
-               # Call suricatactrl to set the new interval.
-               &IDS::call_suricatactrl("cron", $cgiparams{'AUTOUPDATE_INTERVAL'});
-       }
-
-       # Check if the runmode has been changed.
-       if($cgiparams{'RUN_MODE'} ne $oldidssettings{'RUN_MODE'}) {
-               # Open modify sid's file for writing.
-               open(FILE, ">$modify_sids_file") or die "Could not write to $modify_sids_file. $!\n";
+       &IDS::generate_home_net_file();
 
-               # Write file header.
-               print FILE "#Autogenerated file. Any custom changes will be overwritten!\n";
+       # Temporary variable to set the ruleaction.
+       # Default is "drop" to use suricata as IPS.
+       my $ruleaction="drop";
 
-               # Check if the configured runmode is IPS.
-               if ($cgiparams{'RUN_MODE'} eq 'IPS') {
-                       # Tell oinkmaster to switch all rules from alert to drop.
-                       print FILE "modifysid \* \"alert\" \| \"drop\"\n";
-               }
+       # 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";
+       }
 
-               # Close file handle.
-               close(FILE);
+       # 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'}");
+                       &working_notice("$Lang::tr{'ids working'}");
 
                        # Call oinkmaster to alter the ruleset.
                        &IDS::oinkmaster();
@@ -575,13 +649,14 @@ if ($cgiparams{'RULESET'} eq $Lang::tr{'update'}) {
        }
 }
 
-# Read-in idssettings
-&General::readhash("$IDS::settingsdir/settings", \%idssettings);
+# Read-in idssettings and rulesetsettings
+&General::readhash("$IDS::ids_settings_file", \%idssettings);
+&General::readhash("$IDS::rules_settings_file", \%rulessettings);
 
-# If the runmode has not been configured yet, set default value.
-unless(exists($idssettings{'RUN_MODE'})) {
-        # Set default to IPS.
-        $idssettings{'RUN_MODE'} = 'IPS';
+# If no autoupdate intervall has been configured yet, set default value.
+unless(exists($rulessettings{'AUTOUPDATE_INTERVAL'})) {
+       # Set default to "weekly".
+       $rulessettings{'AUTOUPDATE_INTERVAL'} = 'weekly';
 }
 
 # Read-in ignored hosts.
@@ -590,29 +665,59 @@ unless(exists($idssettings{'RUN_MODE'})) {
 $checked{'ENABLE_IDS'}{'off'} = '';
 $checked{'ENABLE_IDS'}{'on'} = '';
 $checked{'ENABLE_IDS'}{$idssettings{'ENABLE_IDS'}} = "checked='checked'";
-$checked{'RUN_MODE'}{'IDS'} = '';
-$checked{'RUN_MODE'}{'IPS'} = '';
-$checked{'RUN_MODE'}{$idssettings{'RUN_MODE'}} = "checked='checked'";
+$checked{'MONITOR_TRAFFIC_ONLY'}{'off'} = '';
+$checked{'MONITOR_TRAFFIC_ONLY'}{'on'} = '';
+$checked{'MONITOR_TRAFFIC_ONLY'}{$idssettings{'MONITOR_TRAFFIC_ONLY'}} = "checked='checked'";
 $selected{'RULES'}{'nothing'} = '';
 $selected{'RULES'}{'community'} = '';
 $selected{'RULES'}{'emerging'} = '';
 $selected{'RULES'}{'registered'} = '';
 $selected{'RULES'}{'subscripted'} = '';
-$selected{'RULES'}{$idssettings{'RULES'}} = "selected='selected'";
+$selected{'RULES'}{$rulessettings{'RULES'}} = "selected='selected'";
 $selected{'AUTOUPDATE_INTERVAL'}{'off'} = '';
 $selected{'AUTOUPDATE_INTERVAL'}{'daily'} = '';
 $selected{'AUTOUPDATE_INTERVAL'}{'weekly'} = '';
-$selected{'AUTOUPDATE_INTERVAL'}{$idssettings{'AUTOUPDATE_INTERVAL'}} = "selected='selected'";
+$selected{'AUTOUPDATE_INTERVAL'}{$rulessettings{'AUTOUPDATE_INTERVAL'}} = "selected='selected'";
 
 &Header::openpage($Lang::tr{'intrusion detection system'}, 1, '');
 
 ### Java Script ###
+print"<script>\n";
+
+# Java script variable declaration for show and hide.
+print"var show = \"$Lang::tr{'ids show'}\"\;\n";
+print"var hide = \"$Lang::tr{'ids hide'}\"\;\n";
+
 print <<END
-<script>
+       // JQuery function to show/hide the text input field for
+       // Oinkcode/Subscription code.
+       \$(function() {
+               \$('#RULES').change(function(){
+                       if(\$('#RULES').val() == 'registered') {
+                               \$('#code').show();
+                       } else if(\$('#RULES').val() == 'subscripted') {
+                               \$('#code').show();
+                       } else if(\$('#RULES').val() == 'emerging_pro') {
+                               \$('#code').show();
+                       } else {
+                               \$('#code').hide();
+                       }
+               });
+       });
+
        // Tiny java script function to show/hide the rules
        // of a given category.
        function showhide(tblname) {
                \$("#" + tblname).toggle();
+
+               // Get current content of the span element.
+               var content = document.getElementById("span_" + tblname);
+
+               if (content.innerHTML === show) {
+                       content.innerHTML = hide;
+               } else {
+                       content.innerHTML = show;
+               }
        }
 </script>
 END
@@ -677,131 +782,140 @@ END
                </table>
 END
 }
-&Header::closebox();
 
-# Draw elements for IDS configuration.
-&Header::openbox('100%', 'center', $Lang::tr{'settings'});
+# Only show this area, if a ruleset is present.
+if (%idsrules) {
 
-my $rulesdate;
+       print <<END
 
-# Check if a ruleset allready has been downloaded.
-if ( -f "$IDS::rulestarball"){
-       # Call stat on the filename to obtain detailed information.
-        my @Info = stat("$IDS::rulestarball");
+       <br><br><h2>$Lang::tr{'settings'}</h2>
 
-       # Grab details about the creation time.
-        $rulesdate = localtime($Info[9]);
-}
+       <form method='post' action='$ENV{'SCRIPT_NAME'}'>
+               <table width='100%' border='0'>
+                       <tr>
+                               <td class='base' colspan='2'>
+                                       <input type='checkbox' name='ENABLE_IDS' $checked{'ENABLE_IDS'}{'on'}>&nbsp;$Lang::tr{'ids enable'}
+                               </td>
 
-print <<END
-<form method='post' action='$ENV{'SCRIPT_NAME'}'>
-       <table width='100%' border='0'>
-               <tr>
-                       <td class='base' colspan='4'>
-                               <input type='checkbox' name='ENABLE_IDS' $checked{'ENABLE_IDS'}{'on'}>$Lang::tr{'ids activate'} $Lang::tr{'intrusion detection system'}
+                               <td class='base' colspan='2'>
+                                       <input type='checkbox' name='MONITOR_TRAFFIC_ONLY' $checked{'MONITOR_TRAFFIC_ONLY'}{'on'}>&nbsp;$Lang::tr{'ids monitor traffic only'}
                        </td>
-               </tr>
+                       </tr>
 
-               <tr>
-                       <td colspan='4'><br><br></td>
-               </tr>
+                       <tr>
+                               <td><br><br></td>
+                               <td><br><br></td>
+                               <td><br><br></td>
+                               <td><br><br></td>
+                       </tr>
 
-               <tr>
-                       <td class='base' colspan='4'><b>$Lang::tr{'runmode'}</b></td>
-               </tr>
+                       <tr>
+                               <td colspan='4'><b>$Lang::tr{'ids monitored interfaces'}</b><br></td>
+                       </tr>
 
-               <tr>
-                       <td class='base' colspan='4'>
-                               <input type='radio' name='RUN_MODE' value='IDS' $checked{'RUN_MODE'}{'IDS'}>$Lang::tr{'intrusion detection system2'} &nbsp&nbsp&nbsp
-                               <input type='radio' name='RUN_MODE' value='IPS' $checked{'RUN_MODE'}{'IPS'}>$Lang::tr{'intrusion prevention system'}
-                       </td>
-               </tr>
+                       <tr>
+END
+;
 
-               <tr>
-                       <td colspan='4'><br></td>
-               </tr>
+       # Loop through the array of available networks and print config options.
+       foreach my $zone (@network_zones) {
+               my $checked_input;
+               my $checked_forward;
 
-               <tr>
-                       <td colspan='4'><b>$Lang::tr{'ids traffic analyze'}</b><br></td>
-               </tr>
+               # Convert current zone name to upper case.
+               my $zone_upper = uc($zone);
 
-               <tr>
-END
-;
+               # Set zone name.
+               my $zone_name = $zone;
 
-# Loop through the array of available networks and print config options.
-foreach my $zone (@network_zones) {
-       my $checked_input;
-       my $checked_forward;
+               # Dirty hack to get the correct language string for the red zone.
+               if ($zone eq "red") {
+                       $zone_name = "red1";
+               }
 
-       # Convert current zone name to upper case.
-       my $zone_upper = uc($zone);
+               # Grab checkbox status from settings hash.
+               if ($idssettings{"ENABLE_IDS_$zone_upper"} eq "on") {
+                       $checked_input = "checked = 'checked'";
+               }
 
-       # Grab checkbox status from settings hash.
-       if ($idssettings{"ENABLE_IDS_$zone_upper"} eq "on") {
-               $checked_input = "checked = 'checked'";
+               print "<td class='base' width='25%'>\n";
+               print "<input type='checkbox' name='ENABLE_IDS_$zone_upper' $checked_input>\n";
+               print "&nbsp;$Lang::tr{'enabled on'}<font color='$colourhash{$zone}'> $Lang::tr{$zone_name}</font>\n";
+               print "</td>\n";
        }
 
-       print "<td class='base' width='25%'>\n";
-       print "<input type='checkbox' name='ENABLE_IDS_$zone_upper' $checked_input>$Lang::tr{'enabled on'} $Lang::tr{$zone}\n";
-       print "</td>\n";
+print <<END
+                       </tr>
+               </table>
+
+               <br><br>
+
+               <table width='100%'>
+                       <tr>
+                               <td align='right'><input type='submit' name='IDS' value='$Lang::tr{'save'}' /></td>
+                       </tr>
+               </table>
+       </form>
+END
+;
+
 }
 
-print <<END
-               </tr>
+&Header::closebox();
 
-               <tr>
-                       <td colspan='4'><br><br></td>
-               </tr>
+# Draw elements for ruleset configuration.
+&Header::openbox('100%', 'center', $Lang::tr{'ids ruleset settings'});
 
+print <<END
+<form method='post' action='$ENV{'SCRIPT_NAME'}'>
+        <table width='100%' border='0'>
                <tr>
-                       <td colspan='2'><b>$Lang::tr{'ids rules update'}</b></td>
-                       <td colspan='2'><b>$Lang::tr{'ids automatic rules update'}</b></td>
+                       <td><b>$Lang::tr{'ids rules update'}</b></td>
+                       <td><b>$Lang::tr{'ids automatic rules update'}</b></td>
                </tr>
 
                <tr>
-                       <td colspan='2'><select name='RULES'>
-                               <option value='nothing' $selected{'RULES'}{'nothing'} >$Lang::tr{'no'}</option>
+                       <td><select name='RULES' id='RULES'>
                                <option value='emerging' $selected{'RULES'}{'emerging'} >$Lang::tr{'emerging rules'}</option>
+                               <option value='emerging_pro' $selected{'RULES'}{'emerging_pro'} >$Lang::tr{'emerging pro rules'}</option>
                                <option value='community' $selected{'RULES'}{'community'} >$Lang::tr{'community rules'}</option>
                                <option value='registered' $selected{'RULES'}{'registered'} >$Lang::tr{'registered user rules'}</option>
                                <option value='subscripted' $selected{'RULES'}{'subscripted'} >$Lang::tr{'subscripted user rules'}</option>
                        </select>
                        </td>
 
-                       <td colspan='2'>
+                       <td>
                                <select name='AUTOUPDATE_INTERVAL'>
-                                       <option value='off' $selected{'AUTOUPDATE_INTERVAL'}{'off'} >$Lang::tr{'no'}</option>
-                                       <option value='daily' $selected{'AUTOUPDATE_INTERVAL'}{'daily'} >$Lang::tr{'urlfilter daily'}</option>
-                                       <option value='weekly' $selected{'AUTOUPDATE_INTERVAL'}{'weekly'} >$Lang::tr{'urlfilter weekly'}</option>
+                                       <option value='off' $selected{'AUTOUPDATE_INTERVAL'}{'off'} >- $Lang::tr{'Disabled'} -</option>
+                                       <option value='daily' $selected{'AUTOUPDATE_INTERVAL'}{'daily'} >$Lang::tr{'Daily'}</option>
+                                       <option value='weekly' $selected{'AUTOUPDATE_INTERVAL'}{'weekly'} >$Lang::tr{'Weekly'}</option>
                                </select>
                        </td>
                </tr>
 
                <tr>
-                       <td colspan='4'>
-                               <br>$Lang::tr{'ids rules license'} <a href='https://www.snort.org/subscribe' target='_blank'>www.snort.org</a>$Lang::tr{'ids rules license1'}</br>
-                               <br>$Lang::tr{'ids rules license2'} <a href='https://www.snort.org/account/oinkcode' target='_blank'>Get an Oinkcode</a>, $Lang::tr{'ids rules license3'}</br>
-                       </td>
+                       <td colspan='2'><br><br></td>
                </tr>
 
-               <tr>
-                       <td colspan='4' nowrap='nowrap'>Oinkcode:&nbsp;<input type='text' size='40' name='OINKCODE' value='$idssettings{'OINKCODE'}'></td>
+               <tr style='display:none' id='code'>
+                       <td colspan='2'>Oinkcode:&nbsp;<input type='text' size='40' name='OINKCODE' value='$rulessettings{'OINKCODE'}'></td>
                </tr>
 
                <tr>
-                       <td colspan='4' align='left'><br>
-                               <input type='submit' name='RULESET' value='$Lang::tr{'download new ruleset'}'>&nbsp;$Lang::tr{'updates installed'}: $rulesdate
-                       </td>
+                       <td>&nbsp;</td>
 
-               </tr>
-       </table>
-
-       <br><br>
+                       <td align='right'>
+END
+;
+                       # Show the "Update Ruleset"-Button only if a ruleset has been downloaded yet and automatic updates are disabled.
+                       if ((%idsrules) && ($rulessettings{'AUTOUPDATE_INTERVAL'} eq "off")) {
+                               # Display button to update the ruleset.
+                               print"<input type='submit' name='RULESET' value='$Lang::tr{'update ruleset'}'>\n";
+               }
+print <<END;
+                               <input type='submit' name='RULESET' value='$Lang::tr{'save'}'>
+                       </td>
 
-       <table width='100%'>
-               <tr>
-                       <td align='right'><input type='submit' name='IDS' value='$Lang::tr{'save'}' /></td>
                </tr>
        </table>
 </form>
@@ -944,17 +1058,26 @@ END
 
 # Only show the section for configuring the ruleset if one is present.
 if (%idsrules) {
-       &Header::openbox('100%', 'LEFT', $Lang::tr{'intrusion detection system rules'});
+       # Load neccessary perl modules for file stat and to format the timestamp.
+       use File::stat;
+       use POSIX qw( strftime );
+
+       # Call stat on the rulestarball.
+       my $stat = stat("$IDS::rulestarball");
+
+       # Get timestamp the file creation.
+       my $mtime = $stat->mtime;
+
+       # Convert into human read-able format.
+       my $rulesdate = strftime('%Y-%m-%d %H:%M:%S', localtime($mtime));
+
+       &Header::openbox('100%', 'LEFT', "$Lang::tr{'intrusion detection system rules'} ($rulesdate)" );
 
                print"<form method='POST' action='$ENV{'SCRIPT_NAME'}'>\n";
 
                # Output display table for rule files
                print "<table width='100%'>\n";
 
-               # Local variable required for java script to show/hide
-               # rules of a rulefile.
-               my $rulesetcount = 1;
-
                # Loop over each rule file
                foreach my $rulefile (sort keys(%idsrules)) {
                        my $rulechecked = '';
@@ -964,6 +1087,9 @@ if (%idsrules) {
                                $rulechecked = 'CHECKED';
                        }
 
+                       # Convert rulefile name into category name.
+                       my $categoryname = &_rulefile_to_category($rulefile);
+
                        # Table and rows for the rule files.
                        print"<tr>\n";
                        print"<td class='base' width='5%'>\n";
@@ -971,12 +1097,12 @@ if (%idsrules) {
                        print"</td>\n";
                        print"<td class='base' width='90%'><b>$rulefile</b></td>\n";
                        print"<td class='base' width='5%' align='right'>\n";
-                       print"<a href=\"javascript:showhide('ruleset$rulesetcount')\">SHOW</a>\n";
+                       print"<a href=\"javascript:showhide('$categoryname')\"><span id='span_$categoryname'>$Lang::tr{'ids show'}</span></a>\n";
                        print"</td>\n";
                        print"</tr>\n";
 
                        # Rows which will be hidden per default and will contain the single rules.
-                       print"<tr  style='display:none' id='ruleset$rulesetcount'>\n";
+                       print"<tr  style='display:none' id='$categoryname'>\n";
                        print"<td colspan='3'>\n";
 
                        # Local vars
@@ -1032,9 +1158,6 @@ if (%idsrules) {
 
                        # Close display table
                        print "</tr></table></td></tr>";
-
-                       # Finished whith the rule file, increase count.
-                       $rulesetcount++;
                }
 
                # Close display table
@@ -1043,9 +1166,7 @@ if (%idsrules) {
 print <<END
 <table width='100%'>
 <tr>
-       <td width='100%' align='right'><input type='submit' name='RULESET' value='$Lang::tr{'update'}'>
-               &nbsp; <!-- space for future online help link -->
-       </td>
+       <td width='100%' align='right'><input type='submit' name='RULESET' value='$Lang::tr{'ids apply'}'></td>
 </tr>
 </table>
 </form>
@@ -1182,135 +1303,6 @@ sub get_memory_usage($) {
        return;
 }
 
-#
-## Function to generate the file which contains the home net information.
-#
-sub generate_home_net_file() {
-       my %netsettings;
-
-       # Read-in network settings.
-       &General::readhash("${General::swroot}/ethernet/settings", \%netsettings);
-
-       # Get available network zones.
-       my @network_zones = &IDS::get_available_network_zones();
-
-       # Temporary array to store network address and prefix of the configured
-       # networks.
-       my @networks;
-
-       # Loop through the array of available network zones.
-       foreach my $zone (@network_zones) {
-               # Skip the red network - It never can be part to the home_net!
-               next if($zone eq "red");
-
-               # Convert current zone name into upper case.
-               $zone = uc($zone);
-
-               # Generate key to access the required data from the netsettings hash.
-               my $zone_netaddress = $zone . "_NETADDRESS";
-               my $zone_netmask = $zone . "_NETMASK";
-
-               # Obtain the settings from the netsettings hash.
-               my $netaddress = $netsettings{$zone_netaddress};
-               my $netmask = $netsettings{$zone_netmask};
-
-               # Convert the subnetmask into prefix notation.
-               my $prefix = &Network::convert_netmask2prefix($netmask);
-
-               # Generate full network string.
-               my $network = join("/", $netaddress,$prefix);
-
-               # Check if the network is valid.
-               if(&Network::check_subnet($network)) {
-                       # Add the generated network to the array of networks.
-                       push(@networks, $network);
-               }
-       }
-
-       # Format home net declaration.
-       my $line = "\"\[";
-
-       # Loop through the array of networks.
-       foreach my $network (@networks) {
-               # Add the network to the line.
-               $line = "$line" . "$network";
-
-               # Check if the current network was the last in the array.
-               if ($network eq $networks[-1]) {
-                       # Close the line.
-                       $line = "$line" . "\]\"";
-               } else {
-                       # Add "," for the next network.
-                       $line = "$line" . "\,";
-               }
-       }
-
-       # Open file to store the addresses of the home net.
-       open(FILE, ">$idshomenetfile") or die "Could not open $idshomenetfile. $!\n";
-
-       # Print yaml header.
-       print FILE "%YAML 1.1\n";
-       print FILE "---\n\n";
-
-       # Print notice about autogenerated file.
-       print FILE "#Autogenerated file. Any custom changes will be overwritten!\n";
-
-       # Print the generated and required HOME_NET declaration to the file.
-       print FILE "HOME_NET:\t$line\n";
-
-       # Close file handle.
-       close(FILE);
-
-}
-
-#
-## Function to generate the rules file with whitelisted addresses.
-#
-sub GenerateIgnoreFile() {
-       my %ignored = ();
-
-       # SID range 1000000-1999999 Reserved for Local Use
-       # Put your custom rules in this range to avoid conflicts
-       my $sid = 1500000;
-
-       # Read-in ignoredfile.
-       &General::readhasharray($ignoredfile, \%ignored);
-
-       # Open ignorefile for writing.
-       open(FILE, ">$whitelistfile") or die "Could not write to $whitelistfile. $!\n";
-
-       # Config file header.
-       print FILE "# Autogenerated file.\n";
-       print FILE "# All user modifications will be overwritten.\n\n";
-
-       # Add all user defined addresses to the whitelist.
-       #
-       # Check if the hash contains any elements.
-       if (keys (%ignored)) {
-               # Loop through the entire hash and write the host/network
-               # and remark to the ignore file.
-               while ( (my $key) = each %ignored) {
-                       my $address = $ignored{$key}[0];
-                       my $remark = $ignored{$key}[1];
-                       my $status = $ignored{$key}[2];
-
-                       # Check if the status of the entry is "enabled".
-                       if ($status eq "enabled") {
-                               # Check if the address/network is valid.
-                               if ((&General::validip($address)) || (&General::validipandmask($address))) {
-                                       # Write rule line to the file to pass any traffic from this IP
-                                       print FILE "pass ip $address any -> any any (msg:\"pass all traffic from/to $address\"\; sid:$sid\;)\n";
-
-                                       # Increment sid.
-                                       $sid++;
-                               }
-                       }
-                }
-       }
-
-       close(FILE);
-}
-
 #
 ## Function to read-in the given enabled or disables sids file.
 #
@@ -1361,3 +1353,27 @@ sub read_enabled_disabled_sids_file($) {
        # Return the hash.
        return %temphash;
 }
+
+#
+## Private function to convert a given rulefile to a category name.
+## ( No file extension anymore and if the name contained a dot, it
+## would be replaced by a underline sign.)
+#
+sub _rulefile_to_category($) {
+        my ($filename) = @_;
+
+       # Splitt the filename into single chunks and store them in a
+       # temorary array.
+        my @parts = split(/\./, $filename);
+
+       # Return / Remove last element of the temporary array.
+       # This removes the file extension.
+        pop @parts;
+
+       # Join together the single elements of the temporary array.
+       # If these are more than one, use a "underline" for joining.
+        my $category = join '_', @parts;
+
+       # Return the converted filename.
+        return $category;
+}