<?php
/*
 * suricata_rules_flowbits.php
 * Copyright (C) 2014 Bill Meeks
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

require_once("guiconfig.inc");
require_once("/usr/local/pkg/suricata/suricata.inc");

global $g, $rebuild_rules;

$suricatadir = SURICATADIR;
$flowbit_rules_file = FLOWBITS_FILENAME;
$rules_map = array();
$supplist = array();

if (!is_array($config['installedpackages']['suricata']['rule'])) {
	$config['installedpackages']['suricata']['rule'] = array();
}
$a_nat = &$config['installedpackages']['suricata']['rule'];

if (isset($_POST['id']) && is_numericint($_POST['id']))
	$id = $_POST['id'];
elseif (isset($_GET['id']) && is_numericint($_GET['id']))
	$id = htmlspecialchars($_GET['id']);

if (is_null($id)) {
	header("Location: /suricata/suricata_interfaces.php");
	exit;
}

// Set who called us so we can return to the correct page with
// the RETURN ('cancel') button.
if ($_POST['referrer'])
	$referrer = $_POST['referrer'];
else
	$referrer = $_SERVER['HTTP_REFERER'];

// Make sure a rule index ID is appended to the return URL
if (strpos($referrer, "?id={$id}") === FALSE)
	$referrer .= "?id={$id}";

// If RETURN button clicked, exit to original calling page
if ($_POST['cancel']) {
	header("Location: {$referrer}");
	exit;
}

$if_real = get_real_interface($a_nat[$id]['interface']);
$suricata_uuid = $a_nat[$id]['uuid'];

/* We should normally never get to this page if Auto-Flowbits are disabled, but just in case... */
if ($a_nat[$id]['autoflowbitrules'] == 'on') {
	if (file_exists("{$suricatadir}suricata_{$suricata_uuid}_{$if_real}/rules/{$flowbit_rules_file}") &&
	    filesize("{$suricatadir}suricata_{$suricata_uuid}_{$if_real}/rules/{$flowbit_rules_file}") > 0) {
		$rules_map = suricata_load_rules_map("{$suricatadir}suricata_{$suricata_uuid}_{$if_real}/rules/{$flowbit_rules_file}");
	}
	else
		$savemsg = gettext("There are no flowbit-required rules necessary for the current enforcing rule set.");
}
else
	$input_errors[] = gettext("Auto-Flowbit rule generation is disabled for this interface!");

if ($_POST['addsuppress'] && is_numeric($_POST['sid']) && is_numeric($_POST['gid'])) {
	$descr = suricata_get_msg($rules_map[$_POST['gid']][$_POST['sid']]['rule']);
	$suppress = gettext("## -- This rule manually suppressed from the Auto-Flowbits list. -- ##\n");
	if (empty($descr))
		$suppress .= "suppress gen_id {$_POST['gid']}, sig_id {$_POST['sid']}\n";
	else
		$suppress .= "# {$descr}\nsuppress gen_id {$_POST['gid']}, sig_id {$_POST['sid']}\n";
	if (!is_array($config['installedpackages']['suricata']['suppress']))
		$config['installedpackages']['suricata']['suppress'] = array();
	if (!is_array($config['installedpackages']['suricata']['suppress']['item']))
		$config['installedpackages']['suricata']['suppress']['item'] = array();
	$a_suppress = &$config['installedpackages']['suricata']['suppress']['item'];
	$found_list = false;

	if (empty($a_nat[$id]['suppresslistname']) || $a_nat[$id]['suppresslistname'] == 'default') {
		$s_list = array();
		$s_list['uuid'] = uniqid();
		$s_list['name'] = $a_nat[$id]['interface'] . "suppress" . "_" . $s_list['uuid'];
		$s_list['descr']  =  "Auto-generated list for Alert suppression";
		$s_list['suppresspassthru'] = base64_encode($suppress);
		$a_suppress[] = $s_list;
		$a_nat[$id]['suppresslistname'] = $s_list['name'];
		$found_list = true;
	} else {
		/* If we get here, a Suppress List is defined for the interface so see if we can find it */
		foreach ($a_suppress as $a_id => $alist) {
			if ($alist['name'] == $a_nat[$id]['suppresslistname']) {
				$found_list = true;
				if (!empty($alist['suppresspassthru'])) {
					$tmplist = base64_decode($alist['suppresspassthru']);
					$tmplist .= "\n{$suppress}";
					$alist['suppresspassthru'] = base64_encode($tmplist);
					$a_suppress[$a_id] = $alist;
				}
				else {
					$alist['suppresspassthru'] = base64_encode($suppress);
					$a_suppress[$a_id] = $alist;
				}
			}
		}
	}
	if ($found_list) {
		write_config();
		$rebuild_rules = false;
		sync_suricata_package_config();
		suricata_reload_config($a_nat[$id]);
		$savemsg = gettext("An entry to suppress the Alert for 'gen_id {$_POST['gid']}, sig_id {$_POST['sid']}' has been added to Suppress List '{$a_nat[$id]['suppresslistname']}'.");
	}
	else {
		/* We did not find the defined list, so notify the user with an error */
		$input_errors[] = gettext("Suppress List '{$a_nat[$id]['suppresslistname']}' is defined for this interface, but it could not be found!");
	}
}

/* Load up an array with the current Suppression List GID,SID values */
$supplist = suricata_load_suppress_sigs($a_nat[$id]);

$if_friendly = convert_friendly_interface_to_friendly_descr($a_nat[$id]['interface']);
$pgtitle = gettext("Suricata: Interface {$if_friendly} - Flowbit Rules");
include_once("head.inc");

?>

<body link="#0000CC" vlink="#0000CC" alink="#0000CC" >

<?php
include("fbegin.inc");
if ($input_errors) print_input_errors($input_errors);
if ($savemsg)
	print_info_box($savemsg);
?>
<form action="suricata_rules_flowbits.php" method="post" name="iform" id="iform">
<input type="hidden" name="id" value="<?=$id;?>"/>
<input type="hidden" name="referrer" value="<?=$referrer;?>"/>
<input type="hidden" name="sid" id="sid" value=""/>
<input type="hidden" name="gid" id="gid" value=""/>
<div id="boxarea">
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td class="tabcont">
<table width="100%" border="0" cellpadding="6" cellspacing="0">
	<tr>
		<td valign="middle" class="listtopic"><?php echo gettext("Auto-Generated Flowbit-Required Rules"); ?></td>
	</tr>
	<tr>
		<td width="78%" class="vncell">
		<?php echo gettext("The rules listed below are required to be included in the rules set ") . 
		gettext("because they set flowbits that are checked and relied upon by rules in the enforcing rules set.  ") . 
		gettext("If these dependent flowbits are not set, then some of your chosen rules may not fire.  ") . 
		gettext("Enabling all the rules that set these dependent flowbits ensures your chosen rules fire as intended.  ") . 
		gettext("Most flowbits rules contain the \"noalert\" keyword to prevent an alert from firing ") . 
		gettext("when the flowbit is detected.  For those flowbit rules that do not contain the \"noalert\" option, click the ") . 
		gettext("icon displayed beside the Signature ID (SID) to add the alert to the Suppression List if desired."); ?></td> 
	</tr>
	<tr>
		<td valign="middle" class="listtopic"><?php echo gettext("Flowbit-Required Rules for {$if_friendly}"); ?></td>
	</tr>
	<tr>
		<td width="78%" class="vncell">
			<table width="100%" border="0" cellspacing="2" cellpadding="0">
				<tr>
					<td width="17px"><img src="../themes/<?=$g['theme']?>/images/icons/icon_plus.gif" width='12' height='12' border='0'/></td>
					<td><span class="vexpl"><?php echo gettext("Alert is Not Suppressed"); ?></span></td>
					<td rowspan="3" align="right"><input id="cancel" name="cancel" type="submit" class="formbtn" <?php 
					echo "value=\"" . gettext("Return") . "\" title=\"" . gettext("Return to previous page") . "\""; ?>/>
					<input name="id" type="hidden" value="<?=$id;?>" /></td>
				</tr>
				<tr>
					<td width="17px"><img src="../themes/<?=$g['theme']?>/images/icons/icon_plus_d.gif" width='12' height='12' border='0'/></td>
					<td><span class="vexpl"><?php echo gettext("Alert has been Suppressed"); ?></span></td>
				</tr>
				<tr>
					<td width="17px"> </td>
					<td colspan="2" class="vexpl"><?php echo "<span class=\"red\"><strong>" . 
					gettext("Note:  ") . "</strong></span>". gettext("the icon is only ") . 
					gettext("displayed for flowbit rules without the \"noalert\" option."); ?></td>
				</tr>
			</table>
		</td>
	</tr>
	<tr>
		<td>
		<table id="myTable" width="100%" class="sortable" style="table-layout: fixed;" border="0" cellpadding="0" cellspacing="0">
			<colgroup>
				<col width="11%" axis="number">
				<col width="52" axis="string">
				<col width="14%" axis="string">
				<col width="14%" axis="string">
				<col width="24%" axis="string">
				<col axis="string">
			</colgroup>
			<thead>
			   <tr>
				<th class="listhdrr" axis="number"><?php echo gettext("SID"); ?></th>
				<th class="listhdrr" axis="string"><?php echo gettext("Proto"); ?></th>
				<th class="listhdrr" axis="string"><?php echo gettext("Source"); ?></th>
				<th class="listhdrr" axis="string"><?php echo gettext("Destination"); ?></th>
				<th class="listhdrr" axis="string"><?php echo gettext("Flowbits"); ?></th>
				<th class="listhdrr" axis="string"><?php echo gettext("Message"); ?></th>
			   </tr>
			<thead>
			<tbody>
				<?php
					$count = 0;
					foreach ($rules_map as $k1 => $rulem) {
						foreach ($rulem as $k2 => $v) {
							$sid = suricata_get_sid($v['rule']);
							$gid = suricata_get_gid($v['rule']);

							// Pick off the first section of the rule (prior to the start of the MSG field),
							// and then use a REGX split to isolate the remaining fields into an array.
							$tmp = substr($v['rule'], 0, strpos($v['rule'], "("));
							$tmp = trim(preg_replace('/^\s*#+\s*/', '', $tmp));
							$rule_content = preg_split('/[\s]+/', $tmp);

							$protocol = $rule_content[1]; //protocol
							$source = $rule_content[2]; //source
							$destination = $rule_content[5]; //destination
							$message = suricata_get_msg($v['rule']);
							$flowbits = implode("; ", suricata_get_flowbits($v['rule']));
							if (strstr($flowbits, "noalert"))
								$supplink = "";
							else {
								if (!isset($supplist[$gid][$sid])) {
									$supplink = "<input type=\"image\" name=\"addsuppress[]\" onClick=\"document.getElementById('sid').value='{$sid}';";
									$supplink .= "document.getElementById('gid').value='{$gid}';\" ";
									$supplink .= "src=\"../themes/{$g['theme']}/images/icons/icon_plus.gif\" ";
									$supplink .= "width='12' height='12' border='0' title='";
									$supplink .= gettext("Click to add to Suppress List") . "'/>";
								}
								else {
									$supplink = "<img src=\"../themes/{$g['theme']}/images/icons/icon_plus_d.gif\" ";
									$supplink .= "width='12' height='12' border='0' title='";
									$supplink .= gettext("Alert has been suppressed") . "'/>";
								}
							}

							// Use "echo" to write the table HTML row-by-row.
							echo "<tr>" . 
								"<td class=\"listr\" sorttable_customkey=\"{$sid}\">{$sid}&nbsp;{$supplink}</td>" . 
								"<td class=\"listr\" style=\"text-align:center;\">{$protocol}</td>" . 
								"<td class=\"listr ellipsis\" nowrap style=\"text-align:center;\"><span title=\"{$rule_content[2]}\">{$source}</span></td>" . 
								"<td class=\"listr ellipsis\" nowrap style=\"text-align:center;\"><span title=\"{$rule_content[5]}\">{$destination}</span></td>" . 
								"<td class=\"listr\" style=\"word-wrap:break-word; word-break:normal;\">{$flowbits}</td>" . 
								"<td class=\"listbg\" style=\"word-wrap:break-word; word-break:normal;\">{$message}</td>" . 
							"</tr>";
							$count++;
						}
					}
					unset($rulem, $v);
				?>
			</tbody>
		</table>
		</td>
	</tr>
	<?php if ($count > 20): ?>
	<tr>
		<td align="center" valign="middle">
			<input id="cancel" name="cancel" type="submit" class="formbtn" <?php 
			echo "value=\"" . gettext("Return") . "\" title=\"" . gettext("Return to previous page") . "\""; ?>/>
		</td>
	</tr>
	<?php endif; ?>
</table>
</td>
</tr>
</table>
</div>
</form>
<?php include("fend.inc"); ?>
</body>
</html>