]> git.ipfire.org Git - ipfire-2.x.git/blobdiff - html/cgi-bin/ids.cgi
ids.cgi: Introduce ruleset-source.list
[ipfire-2.x.git] / html / cgi-bin / ids.cgi
index 86a469cb2b54cea26b7bc20f4b4670183a83ab27..9eff5233df1e2fa1e8b3de5b1f7ebd6d8adf168b 100644 (file)
@@ -33,6 +33,7 @@ require "${General::swroot}/header.pl";
 my %color = ();
 my %mainsettings = ();
 my %netsettings = ();
+my %snortrules = ();
 my %snortsettings=();
 my %cgiparams=();
 my %checked=();
@@ -52,7 +53,6 @@ $snortsettings{'ENABLE_SNORT'} = 'off';
 $snortsettings{'ENABLE_SNORT_GREEN'} = 'off';
 $snortsettings{'ENABLE_SNORT_BLUE'} = 'off';
 $snortsettings{'ENABLE_SNORT_ORANGE'} = 'off';
-$snortsettings{'ACTION'} = '';
 $snortsettings{'RULES'} = '';
 $snortsettings{'OINKCODE'} = '';
 $snortsettings{'INSTALLDATE'} = '';
@@ -61,10 +61,16 @@ $snortsettings{'INSTALLDATE'} = '';
 &Header::getcgihash(\%cgiparams);
 
 my $snortrulepath = "/etc/snort/rules";
-my $restartsnortrequired = 0;
-my %snortrules;
+my $snortusedrulefilesfile = "${General::swroot}/snort/snort-used-rulefiles.conf";
 my $errormessage;
-my $url;
+
+# Try to determine if oinkmaster is running.
+my $oinkmaster_pid = `pidof oinkmaster.pl -x`;
+
+# If oinkmaster is running display output.
+if ($oinkmaster_pid) {
+       &working("$Lang::tr{'snort working'}");
+}
 
 ## Grab all available snort rules and store them in the snortrules hash.
 #
@@ -92,6 +98,40 @@ opendir(DIR, $snortrulepath) or die $!;
 
 closedir(DIR);
 
+# Gather used rulefiles.
+#
+# Check if the file for activated rulefiles is not empty.
+if(-f $snortusedrulefilesfile) {
+       # Open the file for used rulefile and read-in content.
+       open(FILE, $snortusedrulefilesfile) or die "Could not open $snortusedrulefilesfile. $!\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 =~ /.*include \$RULE_PATH\/(.*)/) {
+                       my $rulefile = $1;
+
+                       # Add the rulefile to the %snortrules hash.
+                       $snortrules{$rulefile}{'Rulefile'}{'State'} = "on";
+               }
+       }
+}
+
 # Save ruleset.
 if ($cgiparams{'RULESET'} eq $Lang::tr{'update'}) {
        my $enabled_sids_file = "${General::swroot}/snort/oinkmaster-enabled-sids.conf";
@@ -100,11 +140,27 @@ if ($cgiparams{'RULESET'} eq $Lang::tr{'update'}) {
        # Arrays to store sid which should be added to the corresponding files.
        my @enabled_sids;
        my @disabled_sids;
+       my @enabled_rulefiles;
 
        # Loop through the hash of snortrules.
        foreach my $rulefile(keys %snortrules) {
+               # 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};
+               }
+       }
+
+       # Loop through the hash of snortrules.
+       foreach my $rulefile (keys %snortrules) {
                # Loop through the single rules of the rulefile.
                foreach my $sid (keys %{$snortrules{$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.
@@ -115,7 +171,7 @@ if ($cgiparams{'RULESET'} eq $Lang::tr{'update'}) {
                                                push(@enabled_sids, $sid);
 
                                                # Drop item from cgiparams hash.
-                                               delete $cgiparams{$sid};
+                                               delete $cgiparams{$rulefile}{$sid};
                                        }
                                }
                        } else {
@@ -128,132 +184,173 @@ if ($cgiparams{'RULESET'} eq $Lang::tr{'update'}) {
                                        push(@disabled_sids, $sid);
 
                                        # Drop item from cgiparams hash.
-                                       delete $cgiparams{$sid};
+                                       delete $cgiparams{$rulefile}{$sid};
                                }
                        }
                }
        }
 
-       # Check if the enabled_sids array contains any sid's.
-       if (@enabled_sids) {
-               # Open enabled sid's file for writing.
-               open(FILE, ">$enabled_sids_file") or die "Could not write to $enabled_sids_file. $!\n";
+       # Open enabled sid's file for writing.
+       open(FILE, ">$enabled_sids_file") or die "Could not write to $enabled_sids_file. $!\n";
 
-               # Write header to file.
-               print FILE "#Autogenerated file. Any custom changes will be overwritten!\n";
+       # Write header to file.
+       print FILE "#Autogenerated file. Any custom changes will be overwritten!\n";
 
+       # Check if the enabled_sids array contains any sid's.
+       if (@enabled_sids) {
                # Loop through the array of enabled sids and write them to the file.
                foreach my $sid (@enabled_sids) {
-                       print FILE "enable_sid $sid\n";
+                       print FILE "enablesid $sid\n";
                }
-
-               # Close file after writing.
-               close(FILE);
        }
 
+       # Close file after writing.
+       close(FILE);
+
+       # Open disabled sid's file for writing.
+       open(FILE, ">$disabled_sids_file") or die "Could not write to $disabled_sids_file. $!\n";
+
+       # Write header to file.
+       print FILE "#Autogenerated file. Any custom changes will be overwritten!\n";
+
        # Check if the enabled_sids array contains any sid's.
-       if (@disabled_sids) {
-                # Open disabled sid's file for writing.
-                open(FILE, ">$disabled_sids_file") or die "Could not write to $disabled_sids_file. $!\n";
+        if (@disabled_sids) {
+               # Loop through the array of disabled sids and write them to the file.
+               foreach my $sid (@disabled_sids) {
+                       print FILE "disablesid $sid\n";
+               }
+       }
 
-                # Write header to file.
-                print FILE "#Autogenerated file. Any custom changes will be overwritten!\n";
+       # Close file after writing.
+       close(FILE);
 
-                # Loop through the array of disabled sids and write them to the file.
-                foreach my $sid (@disabled_sids) {
-                        print FILE "disable_sid $sid\n";
-                }
+       # Open file for used rulefiles.
+       open (FILE, ">$snortusedrulefilesfile") or die "Could not write to $snortusedrulefilesfile. $!\n";
 
-                # Close file after writing.
-                close(FILE);
-        }
-}
+       # Write header to file.
+       print FILE "#Autogenerated file. Any custom changes will be overwritten!\n";
 
-if ($snortsettings{'OINKCODE'} ne "") {
-       $errormessage = $Lang::tr{'invalid input for oink code'} unless ($snortsettings{'OINKCODE'} =~ /^[a-z0-9]+$/);
-}
+       # Check if the enabled_rulefiles array contains any entries.
+       if (@enabled_rulefiles) {
+               # Loop through the array of rulefiles which should be loaded and write the to the file.
+               foreach my $file (@enabled_rulefiles) {
+                       print FILE "include \$RULE_PATH/$file\n";
+               }
+       }
 
-if (!$errormessage) {
-       if ($snortsettings{'RULES'} eq 'subscripted') {
-               $url=" https://www.snort.org/rules/snortrules-snapshot-29111.tar.gz?oinkcode=$snortsettings{'OINKCODE'}";
-       } elsif ($snortsettings{'RULES'} eq 'registered') {
-               $url=" https://www.snort.org/rules/snortrules-snapshot-29111.tar.gz?oinkcode=$snortsettings{'OINKCODE'}";
-       } elsif ($snortsettings{'RULES'} eq 'community') {
-               $url=" https://www.snort.org/rules/community";
-       } else {
-               $url="http://rules.emergingthreats.net/open/snort-2.9.0/emerging.rules.tar.gz";
+       # Close file after writing.
+       close(FILE);
+
+       # Call oinkmaster to alter the ruleset.
+       &oinkmaster();
+
+# Download new ruleset.
+} elsif ($cgiparams{'RULESET'} eq $Lang::tr{'download new ruleset'}) {
+       # Local var.
+       my $return;
+
+       # Call diskfree to gather the free disk space of /var.
+       my @df = `/bin/df -B M /var`;
+
+       # Loop through the output.
+       foreach my $line (@df) {
+               # Ignore header line.
+               next if $line =~ m/^Filesystem/;
+
+               # Search for a line with the device information.
+               if ($line =~ m/dev/ ) {
+                       # Split the line into single pieces.
+                       my @values = split(' ', $line);
+                       my ($filesystem, $blocks, $used, $available, $used_perenctage, $mounted_on) = @values;
+
+                       # Check if the available disk space is more than 300MB.
+                       if ($available < 300) {
+                               # If there is not enough space, print out an error message.
+                               $errormessage = "$Lang::tr{'not enough disk space'} < 300MB, /var $1MB";
+                       } else {
+                               # Call subfunction to download the ruleset.
+                               &downloadrulesfile();
+
+                               # Sleep for 3 seconds.
+                               sleep(3);
+
+                               # Gather return of the external wget.
+                               $return = `cat /var/tmp/log 2>/dev/null`;
+                       }
+
+                       # Check if there was an error.
+                       if ($return =~ "ERROR") {
+                               # Store error message for display.
+                               $errormessage = "<br /><pre>".$return."</pre>";
+                       } else {
+                               # Remove logfile.
+                               unlink("/var/tmp/log");
+
+                               # Call subfunction to launch oinkmaster.
+                               &oinkmaster();
+
+                               # Sleep for 2 seconds.
+                               sleep(2);
+                       }
+               }
+       }
+# Save snort settings.
+} elsif ($cgiparams{'SNORT'} eq $Lang::tr{'save'}) {
+       # Prevent form name from been stored in conf file.
+       delete $cgiparams{'SNORT'};
+
+       # 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{'ACTION'} eq $Lang::tr{'save'} && $snortsettings{'ACTION2'} eq "snort" ) {
-               &General::writehash("${General::swroot}/snort/settings", \%snortsettings);
-               if ($snortsettings{'ENABLE_SNORT'} eq 'on')
-               {
+       # Go on if there are no error messages.
+       if (!$errormessage) {
+               # Store settings into settings file.
+               &General::writehash("${General::swroot}/snort/settings", \%cgiparams);
+
+               # Create/Remove control files for snort.
+               if ($snortsettings{'ENABLE_SNORT'} eq 'on') {
                        system ('/usr/bin/touch', "${General::swroot}/snort/enable");
                } else {
                        unlink "${General::swroot}/snort/enable";
                }
-               if ($snortsettings{'ENABLE_SNORT_GREEN'} eq 'on')
-               {
+
+               if ($snortsettings{'ENABLE_SNORT_GREEN'} eq 'on') {
                        system ('/usr/bin/touch', "${General::swroot}/snort/enable_green");
                } else {
                        unlink "${General::swroot}/snort/enable_green";
                }
-               if ($snortsettings{'ENABLE_SNORT_BLUE'} eq 'on')
-               {
+
+               if ($snortsettings{'ENABLE_SNORT_BLUE'} eq 'on') {
                        system ('/usr/bin/touch', "${General::swroot}/snort/enable_blue");
                } else {
                        unlink "${General::swroot}/snort/enable_blue";
                }
-               if ($snortsettings{'ENABLE_SNORT_ORANGE'} eq 'on')
-               {
+
+               if ($snortsettings{'ENABLE_SNORT_ORANGE'} eq 'on') {
                        system ('/usr/bin/touch', "${General::swroot}/snort/enable_orange");
                } else {
                        unlink "${General::swroot}/snort/enable_orange";
                }
-               if ($snortsettings{'ENABLE_PREPROCESSOR_HTTP_INSPECT'} eq 'on')
-               {
+
+               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";
                }
 
+               # Call snortctrl to restart snort
                system('/usr/local/bin/snortctrl restart >/dev/null');
        }
-
-       # INSTALLMD5 is not in the form, so not retrieved by getcgihash
-       &General::readhash("${General::swroot}/snort/settings", \%snortsettings);
-
-       if ($snortsettings{'ACTION'} eq $Lang::tr{'download new ruleset'}) {
-               my @df = `/bin/df -B M /var`;
-               foreach my $line (@df) {
-                       next if $line =~ m/^Filesystem/;
-                       my $return;
-
-                       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`;
-
-                                       }
-
-                                       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);
-                                       }
-                               }
-                       }
-               }
-       }
 }
 
+# Read-in snortsettings
+&General::readhash("${General::swroot}/snort/settings", \%snortsettings);
+
 $checked{'ENABLE_SNORT'}{'off'} = '';
 $checked{'ENABLE_SNORT'}{'on'} = '';
 $checked{'ENABLE_SNORT'}{$snortsettings{'ENABLE_SNORT'}} = "checked='checked'";
@@ -289,19 +386,6 @@ END
 
 &Header::openbigbox('100%', 'left', '', $errormessage);
 
-###############
-# DEBUG DEBUG
-# &Header::openbox('100%', 'left', 'DEBUG');
-# my $debugCount = 0;
-# foreach my $line (sort keys %snortsettings) {
-# print "$line = $snortsettings{$line}<br />\n";
-# $debugCount++;
-# }
-# print "&nbsp;Count: $debugCount\n";
-# &Header::closebox();
-# DEBUG DEBUG
-###############
-
 if ($errormessage) {
        &Header::openbox('100%', 'left', $Lang::tr{'error messages'});
        print "<class name='base'>$errormessage\n";
@@ -309,37 +393,6 @@ if ($errormessage) {
        &Header::closebox();
 }
 
-my $return = `pidof oinkmaster.pl -x`;
-chomp($return);
-if ($return) {
-       &Header::openbox( 'Waiting', 1, "<meta http-equiv='refresh' content='10;'>" );
-       print <<END;
-       <table>
-               <tr><td>
-                               <img src='/images/indicator.gif' alt='$Lang::tr{'aktiv'}' />&nbsp;
-                       <td>
-                               $Lang::tr{'snort working'}
-               <tr><td colspan='2' align='center'>
-                       <form method='post' action='$ENV{'SCRIPT_NAME'}'>
-                               <input type='image' alt='$Lang::tr{'reload'}' title='$Lang::tr{'reload'}' src='/images/view-refresh.png' />
-                       </form>
-               <tr><td colspan='2' align='left'><pre>
-END
-       my @output = `tail -20 /var/tmp/log`;
-       foreach (@output) {
-               print "$_";
-       }
-       print <<END;
-                       </pre>
-               </table>
-END
-       &Header::closebox();
-       &Header::closebigbox();
-       &Header::closepage();
-       exit;
-       refreshpage();
-}
-
 &Header::openbox('100%', 'left', $Lang::tr{'intrusion detection system'});
 print <<END
 <form method='post' action='$ENV{'SCRIPT_NAME'}'><table width='100%'>
@@ -382,7 +435,7 @@ print <<END
        <td nowrap='nowrap'>Oinkcode:&nbsp;<input type='text' size='40' name='OINKCODE' value='$snortsettings{'OINKCODE'}' /></td>
 </tr>
 <tr>
-       <td width='30%' align='left'><br><input type='submit' name='ACTION' value='$Lang::tr{'download new ruleset'}' />
+       <td width='30%' align='left'><br><input type='submit' name='RULESET' value='$Lang::tr{'download new ruleset'}' />
 END
 ;
 if ( -e "/var/tmp/snortrules.tar.gz"){
@@ -397,7 +450,7 @@ print <<END
 <br><br>
 <table width='100%'>
 <tr>
-       <td align='right'><input type='hidden' name='ACTION2' value='snort' /><input type='submit' name='ACTION' value='$Lang::tr{'save'}' /></td>
+       <td align='right'><input type='submit' name='SNORT' value='$Lang::tr{'save'}' /></td>
 </tr>
 </table>
 </form>
@@ -421,14 +474,14 @@ END
                my $rulechecked = '';
 
                # Check if rule file is enabled
-               if ($snortrules{$rulefile}{"State"} eq 'On') {
+               if ($snortrules{$rulefile}{'Rulefile'}{'State'} eq 'on') {
                        $rulechecked = 'CHECKED';
                }
 
                # Table and rows for the rule files.
                print"<tr>\n";
                print"<td class='base' width='5%'>\n";
-               print"<input type='checkbox' name='SNORT_RULE_$rulefile' $rulechecked>\n";
+               print"<input type='checkbox' name='$rulefile' $rulechecked>\n";
                print"</td>\n";
                print"<td class='base' width='90%'><b>$rulefile</b></td>\n";
                print"<td class='base' width='5%' align='right'>\n";
@@ -453,6 +506,9 @@ END
                        # Local vars
                        my $ruledefchecked = '';
 
+                       # Skip rulefile itself.
+                       next if ($sid eq "Rulefile");
+
                        # If 2 rules have been displayed, start a new row
                        if (($lines % 2) == 0) {
                                print "</tr><tr>\n";
@@ -510,14 +566,33 @@ print <<END
 END
 ;
 &Header::closebox();
-
 &Header::closebigbox();
 &Header::closepage();
 
-sub refreshpage {
-       &Header::openbox( 'Waiting', 1, "<meta http-equiv='refresh' content='1;'>" );
-               print "<center><img src='/images/clock.gif' alt='' /><br/><font color='red'>$Lang::tr{'pagerefresh'}</font></center>";
-       &Header::closebox();
+sub working ($) {
+       my $message = $_[0];
+
+        &Header::openpage($Lang::tr{'intrusion detection system'}, 1, '');
+        &Header::openbigbox('100%', 'left', '', $errormessage);
+        &Header::openbox( 'Waiting', 1, "<meta http-equiv='refresh' content='1'>" );
+        print <<END;
+        <table>
+                <tr>
+                        <td><img src='/images/indicator.gif' alt='$Lang::tr{'aktiv'}' /></td>
+                        <td>$message</td>
+                </tr>
+                <tr>
+                        <td colspan='2' align='center'>
+                        <form method='post' action='$ENV{'SCRIPT_NAME'}'>
+                                <input type='image' alt='$Lang::tr{'reload'}' title='$Lang::tr{'reload'}' src='/images/view-refresh.png' />
+                        </form>
+                </tr>
+        </table>
+END
+        &Header::closebox();
+        &Header::closebigbox();
+        &Header::closepage();
+        exit;
 }
 
 sub downloadrulesfile {
@@ -531,6 +606,26 @@ sub downloadrulesfile {
                return undef;
        }
 
+       # Gather snort settings.
+       my %snortsettings = ();
+       &General::readhash("${General::swroot}/snort/settings", \%snortsettings);
+
+       # Get all available ruleset locations.
+       my %urls=();
+       &General::readhash("${General::swroot}/snort/ruleset-sources.list", \%urls);
+
+       # Grab the right url based on the configured vendor.
+       my $url = $urls{$snortsettings{'RULES'}};
+
+       # Check and pass oinkcode if the vendor requires one.
+       $url =~ s/\<oinkcode\>/$snortsettings{'OINKCODE'}/g;
+
+       # Abort if no url could be determined for the vendor.
+       unless($url) {
+               $errormessage = $Lang::tr{'could not download latest updates'};
+               return undef;
+       }
+
        my %proxysettings=();
        &General::readhash("${General::swroot}/proxy/settings", \%proxysettings);
 
@@ -545,6 +640,11 @@ sub downloadrulesfile {
        }
 }
 
+sub oinkmaster () {
+       # Call oinkmaster to generate ruleset.
+       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 &");
+}
+
 sub readrulesfile ($) {
        my $rulefile = shift;