]> git.ipfire.org Git - people/stevee/ipfire-2.x.git/blobdiff - html/cgi-bin/ids.cgi
IDS: Redesign backend for enabled/disabled sids in rulefiles.
[people/stevee/ipfire-2.x.git] / html / cgi-bin / ids.cgi
index b8bdd4fe2635b97a5f052b8f7996f0813d1c9e32..c6bb86ae4fbc731e62b99461eb3248db32e9d37f 100644 (file)
@@ -254,6 +254,10 @@ if (-e $IDS::storederrorfile) {
 if ($cgiparams{'RULESET'}) {
        ## Grab all available rules and store them in the idsrules hash.
        #
+
+       # Get enabled providers.
+       my @enabled_providers = &IDS::get_enabled_providers();
+
        # Open rules directory and do a directory listing.
        opendir(DIR, $IDS::rulespath) or die $!;
                # Loop through the direcory.
@@ -274,6 +278,15 @@ if ($cgiparams{'RULESET'}) {
                        # Skip whitelist rules file.
                        next if( $file eq "whitelist.rules");
 
+                       # Splitt vendor from filename.
+                       my @filename_parts = split(/-/, $file);
+
+                       # Assign vendor name for easy processing.
+                       my $vendor = @filename_parts[0];
+
+                       # Skip rulefile if the provider is disabled.
+                       next unless ($vendor ~~ @enabled_providers);
+
                        # Call subfunction to read-in rulefile and add rules to
                        # the idsrules hash.
                        &readrulesfile("$file");
@@ -281,130 +294,29 @@ if ($cgiparams{'RULESET'}) {
 
        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";
-                               }
+       # Loop through the array of used providers.
+       foreach my $provider (@enabled_providers) {
+               # Gather used rulefiles.
+               my @used_rulesfiles = &IDS::read_used_provider_rulesfiles($provider);
+
+               # Loop through the array of used rulesfiles.
+               foreach my $rulefile (@used_rulesfiles) {
+                       # 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";
                        }
                }
        }
 }
 
-# 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 the choosen vendor (URL) requires an subscription/oinkcode.
-       if ($IDS::Ruleset::Providers{$cgiparams{'RULES'}}{'requires_subscription'} eq "True") {
-               # 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 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 enough 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'}");
-
-                               # Write the modify sid's file and pass the taken ruleaction.
-                               &IDS::write_modify_sids_file();
-
-                               # 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.
-} elsif ($cgiparams{'RULESET'} eq $Lang::tr{'ids apply'}) {
+if ($cgiparams{'RULESET'} eq $Lang::tr{'ids apply'}) {
        # 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;
-
        # Store if a restart of suricata is required.
        my $suricata_restart_required;
 
@@ -426,86 +338,141 @@ if ($cgiparams{'RULESET'} eq $Lang::tr{'save'}) {
                }
        }
 
-       # 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));
+       # Open oinkmaster main include file for provider modifications.
+       open(OINKM_INCL_FILE, ">", "$IDS::oinkmaster_provider_includes_file") or die "Could not open $IDS::oinkmaster_provider_includes_file. $!\n";
 
-       # 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";
+       # Print file header and notice about autogenerated file.
+       print OINKM_INCL_FILE "#Autogenerated file. Any custom changes will be overwritten!\n";
+
+       # Get enabled providers.
+       my @enabled_providers = &IDS::get_enabled_providers();
+
+       # Loop through the array of enabled providers.
+       foreach my $provider (@enabled_providers) {
+               # Hash to store the used-enabled and disabled sids.
+               my %enabled_disabled_sids;
+
+               # Generate modified sids file name for the current processed provider.
+               my $providers_modified_sids_file = "$IDS::settingsdir/oinkmaster-$provider-modified-sids.conf";
+
+               # Check if a modified sids file for this provider exists.
+               if (-f $providers_modified_sids_file) {
+                       # Read-in the file for enabled/disabled sids.
+                       %enabled_disabled_sids = &read_enabled_disabled_sids_file($providers_modified_sids_file);
+               }
+
+               # Loop through the hash of idsrules.
+               foreach my $rulefile (keys %idsrules) {
+                       # Split the rulefile to get the vendor.
+                       my @filename_parts = split(/-/, $rulefile);
+
+                       # Assign rulefile vendor.
+                       my $rulefile_vendor = @filename_parts[0];
+
+                       # Skip the rulefile if the vendor is not our current processed provider.
+                       next unless ($rulefile_vendor eq $provider);
+
+                       # 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};
                                        }
                                }
-                       } 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};
-                               }
                        }
                }
-       }
 
-       # 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;
+               # Check if the hash for enabled/disabled sids contains any entries.
+               if (%enabled_disabled_sids) {
+                       # Open providers modified sids file for writing.
+                       open(PROVIDER_MOD_FILE, ">$providers_modified_sids_file") or die "Could not write to $providers_modified_sids_file. $!\n";
+
+                       # Write header to the files.
+                       print PROVIDER_MOD_FILE "#Autogenerated file. Any custom changes will be overwritten!\n";
+
+                       # 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 PROVIDER_MOD_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 PROVIDER_MOD_FILE "disablesid $sid\n";
+                               # Something strange happende - skip the current sid.
+                               } else {
+                                       next;
+                               }
                        }
+
+                       # Close file handle for the providers modified sids file.
+                       close(PROVIDER_MOD_FILE);
+
+                       # Add the file to the oinkmasters include file.
+                       print OINKM_INCL_FILE "include $providers_modified_sids_file\n";
                }
        }
 
-       # Close file for enabled_sids after writing.
-       close(ENABLED_FILE);
+       # Close the file handle after writing.
+       close(OINKM_INCL_FILE);
+
+       # Handle enabled / disabled rulefiles.
+       #
+
+       # Loop through the array of enabled providers.
+       foreach my $provider(@enabled_providers) {
+               # Array to store the rulefiles which belong to the current processed provider.
+               my @provider_rulefiles = ();
+
+               # Loop through the array of enabled rulefiles.
+               foreach my $rulesfile (@enabled_rulefiles) {
+                       # Split the rulefile name.
+                       my @filename_parts = split(/-/, "$rulesfile");
 
-       # Close file for disabled_sids after writing.
-       close(DISABLED_FILE);
+                       # Assign vendor name for easy processings.
+                       my $vendor = @filename_parts[0];
+
+                       # Check if the rulesvendor is our current processed enabled provider.
+                       if ("$vendor" eq "$provider") {
+                               # Add the rulesfile to the array of provider rulesfiles.
+                               push(@provider_rulefiles, $rulesfile);
+                       }
+
+                       # Check if any rulesfiles have been found for this provider.
+                       if (@provider_rulefiles) {
+                               # Call function and write the providers used rulesfile file.
+                               &IDS::write_used_provider_rulefiles_file($provider, @provider_rulefiles);
+                       }
+               }
+       }
 
        # Call function to generate and write the used rulefiles file.
-       &IDS::write_used_rulefiles_file(@enabled_rulefiles);
+       &IDS::write_main_used_rulefiles_file(@enabled_providers);
 
        # Lock the webpage and print message.
        &working_notice("$Lang::tr{'ids apply ruleset changes'}");
@@ -583,9 +550,12 @@ if ($cgiparams{'RULESET'} eq $Lang::tr{'save'}) {
 
        # Check if the IDS should be enabled.
        if ($cgiparams{'ENABLE_IDS'} eq "on") {
+               # Get enabled providers.
+               my @enabled_providers = &IDS::get_enabled_providers();
+
                # Check if any ruleset is available. Otherwise abort and display an error.
-               unless(%used_providers) {
-                       $errormessage = $Lang::tr{'ids no ruleset available'};
+               unless(@enabled_providers) {
+                       $errormessage = $Lang::tr{'ids no enabled ruleset provider'};
                }
 
                # Loop through the array of available interfaces.
@@ -724,9 +694,8 @@ if ($cgiparams{'RULESET'} eq $Lang::tr{'save'}) {
                foreach my $id ( keys %used_providers) {
                        # Check if the choosen provider is already in use.
                        if ($used_providers{$id}[0] eq "$provider") {
-                               # XXX - add to language file.
                                # Assign error message.
-                               $errormessage = "The choosen provider is already in use.";
+                               $errormessage = "$Lang::tr{'ids the choosen provider is already in use'}";
                        }
                }
        }
@@ -786,6 +755,32 @@ if ($cgiparams{'RULESET'} eq $Lang::tr{'save'}) {
                # Write the changed hash to the providers settings file.
                &General::writehasharray($IDS::providers_settings_file, \%used_providers);
 
+               # Check if a new provider will be added.
+               if ($cgiparams{'PROVIDERS'} eq $Lang::tr{'add'}) {
+                       # Lock the webpage and print notice about downloading
+                       # a new ruleset.
+                       &working_notice("$Lang::tr{'ids working'}");
+
+                       # Download the ruleset.
+                       &IDS::downloadruleset($provider);
+
+                       # Extract the ruleset
+                       &IDS::extractruleset($provider);
+
+                       # Move the ruleset.
+                       &IDS::move_tmp_ruleset();
+
+                       # Cleanup temporary directory.
+                       &IDS::cleanup_tmp_directory();
+
+                       # Create new empty file for used rulefiles
+                       # for this provider.
+                       &IDS::write_used_provider_rulefiles_file($provider);
+
+                       # Perform a reload of the page.
+                       &reload();
+               }
+
                # Undefine providers flag.
                undef($cgiparams{'PROVIDERS'});
        }
@@ -822,14 +817,28 @@ if ($cgiparams{'RULESET'} eq $Lang::tr{'save'}) {
                # Write the changed hash to the providers settings file.
                &General::writehasharray($IDS::providers_settings_file, \%used_providers);
 
-               # XXX - The ruleset needs to be regenerated
-               # XXX - Suricata requires a reload or if the last provider
-               #       has been disabled suricata needs to be stopped.
+               # Get all enabled providers.
+               my @enabled_providers = &IDS::get_enabled_providers();
+
+               # Write the main providers include file.
+               &IDS::write_main_used_rulefiles_file(@enabled_providers);
+
                # Check if the IDS is running.
-               #if(&IDS::ids_is_running()) {
-               #       # Call suricatactrl to perform a reload.
-               #       &IDS::call_suricatactrl("reload");
-               #}
+               if(&IDS::ids_is_running()) {
+                       # Gather the amount of enabled providers (elements in the array).
+                       my $amount = @enabled_providers;
+
+                       # Check if there are still enabled ruleset providers.
+                       if ($amount >= 1) {
+                               # Call suricatactrl to perform a restart.
+                               &IDS::call_suricatactrl("restart");
+
+                       # No active ruleset provider, suricata has to be stopped.
+                       } else {
+                               # Stop suricata.
+                               &IDS::call_suricatactrl("stop");
+                       }
+               }
 
                # Undefine providers flag.
                undef($cgiparams{'PROVIDERS'});
@@ -843,6 +852,9 @@ if ($cgiparams{'RULESET'} eq $Lang::tr{'save'}) {
        # Read-in provider settings file.
        &General::readhasharray($IDS::providers_settings_file, \%used_providers);
 
+       # Grab the provider name bevore deleting it from hash.
+       my $provider = $used_providers{$cgiparams{'ID'}}[0];
+
        # Drop entry from the hash.
        delete($used_providers{$cgiparams{'ID'}});
 
@@ -852,18 +864,49 @@ if ($cgiparams{'RULESET'} eq $Lang::tr{'save'}) {
        # Write the changed hash to the provide settings file.
        &General::writehasharray($IDS::providers_settings_file, \%used_providers);
 
-       # XXX - The ruleset of the provider needs to be dropped.
-       # XXX - The remain rulest of suricata needs to be regenerated.
-       # XXX - Suricata requires a reload or if the last provider has
-       #       been removed it has to be stopped.
+       # Lock the webpage and print message.
+       &working_notice("$Lang::tr{'ids apply ruleset changes'}");
+
+       # Drop the stored ruleset file.
+       &IDS::drop_dl_rulesfile($provider);
+
+       # Get the name of the provider rulessets include file.
+       my $provider_used_rulefile = &get_used_provider_rulesfile_file($provider);
+
+       # Drop the file, it is not longer needed.
+       unlink("$provider_used_rulefile");
+
+       # Regenerate ruleset.
+       &IDS::oinkmaster();
+
+       # Gather all enabled providers.
+       my @enabled_providers = &IDS::get_enabled_providers();
+
+       # Regenerate main providers include file.
+       &IDS::write_main_used_rulefiles_file(@enabled_providers);
+
        # Check if the IDS is running.
-       #if(&IDS::ids_is_running()) {
-       # Call suricatactrl to perform a reload.
-       #       &IDS::call_suricatactrl("reload");
-       #}
+       if(&IDS::ids_is_running()) {
+               # Get amount of enabled providers.
+               my $amount = @enabled_providers;
+
+               # Check if at least one enabled provider remains.
+               if ($amount >= 1) {
+                       # Call suricatactrl to perform a reload.
+                       &IDS::call_suricatactrl("restart");
+
+               # Stop suricata if no enabled provider remains.
+               } else {
+                       # Call suricatactrel to perform the stop.
+                       &IDS::call_suricatactrl("stop");
+               }
+       }
        
        # Undefine providers flag.
        undef($cgiparams{'PROVIDERS'});
+
+       # Reload page.
+       &reload();
 }
 
 &Header::openpage($Lang::tr{'intrusion detection system'}, 1, '');
@@ -1099,9 +1142,7 @@ END
                                # Assign data array positions to some nice variable names.
                                my $provider = $used_providers{$id}[0];
                                my $provider_name = &get_provider_name($provider);
-
-                               #XXX my $rulesdate = &IDS::get_ruleset_date($provider);
-                               my $rulesdate;
+                               my $rulesetdate = &IDS::get_ruleset_date($provider);
 
                                my $subscription_code = $used_providers{$id}[1];
                                my $autoupdate_status = $used_providers{$id}[2];
@@ -1141,7 +1182,7 @@ END
 print <<END;
                                <tr>
                                        <td width='33%' class='base' $col>$provider_name</td>
-                                       <td width='30%' class='base' $col>$rulesdate</td>
+                                       <td width='30%' class='base' $col>$rulesetdate</td>
 
                                        <td align='center' $col>
                                                <form method='post' action='$ENV{'SCRIPT_NAME'}'>
@@ -1598,9 +1639,21 @@ print <<END
                        <tr>
                                <td width='40%'>
                                        <input type='hidden' name='ID' value='$cgiparams{'ID'}'>
-                                       <select name='PROVIDER' id='PROVIDER'>
 END
 ;
+                                       # Value to allow disabling the dropdown menu.
+                                       my $disabled;
+
+                                       # Check if we are in edit mode.
+                                       if ($cgiparams{'PROVIDERS'} eq "$Lang::tr{'edit'}") {
+                                               $disabled = "disabled";
+
+                                               # Add hidden input with the provider because the disable select does not provider
+                                               # this.
+                                               print "<input type='hidden' name='PROVIDER' value='$used_providers{$cgiparams{'ID'}}[0]'>\n";
+                                       }
+
+                                       print "<select name='PROVIDER' id='PROVIDER' $disabled>\n";
                                                # Loop through the array of ruleset providers.
                                                foreach my $provider (@ruleset_providers) {
                                                        # Get the provider name.