#!/usr/bin/perl
###############################################################################
#                                                                             #
# IPFire.org - A linux based firewall                                         #
# Copyright (C) 2020 IPFire Development Team <info@ipfire.org>                #
#                                                                             #
# This program is free software: you can redistribute it and/or modify        #
# it under the terms of the GNU General Public License as published by        #
# the Free Software Foundation, either version 3 of the License, or           #
# (at your option) any later version.                                         #
#                                                                             #
# This program is distributed in the hope that it will be useful,             #
# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
# GNU General Public License for more details.                                #
#                                                                             #
# You should have received a copy of the GNU General Public License           #
# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
#                                                                             #
###############################################################################

use strict;

require '/var/ipfire/general-functions.pl';
require "${General::swroot}/ids-functions.pl";
require "${General::swroot}/network-functions.pl";

# Snort settings file, which contains the settings from the WUI.
my $snort_settings_file = "${General::swroot}/snort/settings";

# Main snort config file.
my $snort_config_file = "/etc/snort/snort.conf";

# Snort rules tarball.
my $snort_rules_tarball = "/var/tmp/snortrules.tar.gz";

#
## Step 1: Convert snort user and group to suricata if exist.
#

# Check if the snort user exists.
if (getpwnam("snort")) {
	# Change username.
	my @command = (
		'/usr/sbin/usermod',
		'-l', 'suricata', 'snort'
	);

	system(@command) == 0 or die "Could not change username: @command failed: $?\n";

	# Adjust home directory.
	@command = (
		'/usr/sbin/usermod',
		'-d', "/var/log/suricata",
		'suricata'
	);

	system(@command) == 0 or die "Failed to adjust home directory: @command failed: $?\n";
}

# Check if the snort group exists.
if (getgrnam("snort")) {
	# Change groupname
	my @command = (
		'/usr/sbin/groupmod',
		'-n', 'suricata', 'snort'
	);

	system(@command) == 0 or die "Could not rename groupname: @command failed: $?\n";
}

#
## Step 2: Setup directory and file layout, if not present and set correct
##         ownership. The converter runs as a privileged user, but the files
##         needs to be full access-able by the WUI user and group (nobody:nobody).
#

# Check if the settings directory exists.
unless (-d $IDS::settingsdir) {
	# Create the directory.
	mkdir($IDS::settingsdir);
}

# Check if the rules directory exists.
unless (-d $IDS::rulespath) {
	# Create the directory.
	mkdir($IDS::rulespath);
}

# Create file layout, if not exists yet.
&IDS::check_and_create_filelayout();

# Set correct ownership for settingsdir and rulespath.
&IDS::set_ownership("$IDS::settingsdir");
&IDS::set_ownership("$IDS::rulespath");

# Check if a snort settings file exists.
unless( -f "$snort_settings_file") {
	print "$snort_settings_file not found - Nothing to do. Exiting!\n";
	exit(0);
}

# Check if the snort settings file is empty.
if (-z "$snort_settings_file") {
	print "$snort_settings_file is empty - Nothing to do. Exiting!\n";
	exit(0);
}

#
## Step 3: Import snort settings and convert to the required format for the new IDS
##         (suricata).
#

# Hash which contains the "old" snort settings.
my %snortsettings;

# Hash which contains the IDS (suricata) settings.
#
# Add default value for MONITOR_TRAFFIC_ONLY which will be "on"
# when migrating from snort to the new IDS.
my %idssettings = (
	"MONITOR_TRAFFIC_ONLY" => "on",
);

# Hash which contains the RULES settings.
#
# Set default value for UPDATE_INTERVAL to weekly.
my %rulessettings = (
	"AUTOUPDATE_INTERVAL" => "weekly",
);

# Get all available network zones.
my @network_zones = &Network::get_available_network_zones();

# Read-in snort settings file.
&General::readhash("$snort_settings_file", \%snortsettings);

# Loop through the array of network zones.
foreach my $zone (@network_zones) {
	# Convert current zone into upper case.
	my $zone_upper = uc($zone);

	# Check if the current network zone is "red".
	if($zone eq "red") {
		# Check if snort was enabled and enabled on red.
		if ($snortsettings{"ENABLE_SNORT"} eq "on") {
			# Enable the IDS.
			$idssettings{"ENABLE_IDS"} = "on";

			# Enable the IDS on RED.
			$idssettings{"ENABLE_IDS_$zone_upper"} = "on";
		}
	} else {
		# Check if snort was enabled on the current zone.
		if ($snortsettings{"ENABLE_SNORT_$zone_upper"} eq "on") {
			# Enable the IDS on this zone too.
			$idssettings{"ENABLE_IDS_$zone_upper"} = "on";
		}
	}
}

# Grab the choosen ruleset from snort settings hash and store it in the rules
# settings hash.
$rulessettings{"RULES"} = $snortsettings{"RULES"};

# Check if an oinkcode has been provided.
if($snortsettings{"OINKCODE"}) {
	# Take the oinkcode from snort settings hash and store it in the rules
	# settings hash.
	$rulessettings{"OINKCODE"} = $snortsettings{"OINKCODE"};
}

#
## Step 4: Import guardian settings and whitelist if the addon is installed.
#

# Pakfire meta file for owncloud.
# (File exists when the addon is installed.)
my $guardian_meta = "/opt/pakfire/db/installed/meta-guardian";

# Check if the guardian addon is installed.
if (-f $guardian_meta) {
	# File which contains the taken setting for guardian.
	my $guardian_settings_file = "${General::swroot}/guardian/settings";

	# File which contains the white-listed hosts.
	my $guardian_ignored_file = "${General::swroot}/guardian/ignored";

	# Hash which will contain the settings of guardian.
	my %guardiansettings;

	# Check if the settings file of guardian is empty.
	unless (-z $guardian_settings_file) {
		# Read-in settings.
		&General::readhash("$guardian_settings_file", \%guardiansettings);
	}

	# Check if guardian is not configured to take actions on snort events.
	if ($guardiansettings{"GUARDIAN_MONITOR_SNORT"} eq "on") {
		# Change the IDS into MONITOR_TRAFFIC_ONLY mode.
		$idssettings{"MONITOR_TRAFFIC_ONLY"} = "off";
	}

	# Check if guardian has any white-listed hosts configured.
	unless (-z $guardian_ignored_file) {
		# Temporary hash to store the ignored hosts.
		my %ignored_hosts;

		# Read-in white-listed hosts and store them in the hash.
		&General::readhasharray($guardian_ignored_file, \%ignored_hosts);

		# Write-out the white-listed hosts for the IDS system.
		&General::writehasharray($IDS::ignored_file, \%ignored_hosts);

		# Call subfunction to generate the file for white-listing the hosts.
		&IDS::generate_ignored_file();
	}

}

#
## Step 5: Save IDS and rules settings.
#

# Write IDS settings.
&General::writehash("$IDS::ids_settings_file", \%idssettings);

# Write rules settings.
&General::writehash("$IDS::rules_settings_file", \%rulessettings);

#
## Step 6: Generate and write the file to modify the ruleset.
#

# Call subfunction and pass the desired IDS action.
&IDS::write_modify_sids_file();

# Set correct ownership.
&IDS::set_ownership("$IDS::modify_sids_file");

#
## Step 7: Move rulestarball to its new location.
#

# Check if a rulestarball has been downloaded yet.
if (-f $snort_rules_tarball) {
	# Load perl module which contains the move command.
	use File::Copy;

	# Move the rulestarball to the new location.
	move($snort_rules_tarball, $IDS::rulestarball);

	# Set correct ownership.
	&IDS::set_ownership("$IDS::rulestarball");

# In case no tarball is present, try to download the ruleset.
} else {
	# Check if enought disk space is available.
	if(&IDS::checkdiskspace()) {
		# Print error message.
		print "Could not download ruleset - Not enough free diskspace available.\n";
	} else {
		# Call the download function and grab the new ruleset.
		&IDS::downloadruleset();
	}
}

#
## Step 8: Call oinkmaster to extract and setup the rules structures.
#

# Check if a rulestarball is present.
if (-f $IDS::rulestarball) {
	# Launch oinkmaster by calling the subfunction.
	&IDS::oinkmaster();

	# Set correct ownership for the rulesdir and files.
	&IDS::set_ownership("$IDS::rulespath");
}

#
## Step 9: Generate file for the HOME Net.
#

# Call subfunction to generate the file.
&IDS::generate_home_net_file();

# Set correct ownership for the homenet file.
&IDS::set_ownership("$IDS::homenet_file");

#
## Step 10: Generate file for the DNS servers.
#

# Call subfunction to generate the file.
&IDS::generate_dns_servers_file();

# Set correct ownership for the dns_servers_file.
&IDS::set_ownership("$IDS::dns_servers_file");

#
## Step 11: Generate file which contains the HTTP ports.
#

# Call subfunction to generate the file.
&IDS::generate_http_ports_file();

# Set correct ownership for the http_ports_file.
&IDS::set_ownership("$IDS::http_ports_file");

#
## Step 12: Setup automatic ruleset updates.
#

# Check if a ruleset is configured.
if($rulessettings{"RULES"}) {
	# Call suricatactrl and setup the periodic update mechanism.
	&IDS::call_suricatactrl("cron", $rulessettings{'AUTOUPDATE_INTERVAL'});
}

#
## Step 13: Grab used ruleset files from snort config file and convert
##         them into the new format.
#

# Check if the snort config file exists.
unless (-f $snort_config_file) {
	print "$snort_config_file does not exist - Nothing to do. Exiting!\n";
	exit(0);
}

# Array to store the enabled rules files.
my @enabled_rule_files;

# Open snort config file.
open(SNORTCONF, $snort_config_file) or die "Could not open $snort_config_file. $!\n";

# Loop through the file content.
while (my $line = <SNORTCONF>) {
	# Skip comments.
	next if ($line =~ /\#/);

	# Skip blank  lines.
	next if ($line =~ /^\s*$/);

	# Remove newlines.
	chomp($line);

	# Check for a line with .rules
	if ($line =~ /\.rules$/) {
		# Parse out rule file name
		my $rulefile = $line;
		$rulefile =~ s/\$RULE_PATH\///i;
		$rulefile =~ s/ ?include ?//i;

		# Add the enabled rulefile to the array of enabled rule files.
		push(@enabled_rule_files, $rulefile);
	}
}

# Close filehandle.
close(SNORTCONF);

# Pass the array of enabled rule files to the subfunction and write the file.
&IDS::write_used_rulefiles_file(@enabled_rule_files);

#
## Step 14: Start the IDS if enabled.
#

# Check if the IDS should be started.
if($idssettings{"ENABLE_IDS"} eq "on") {
	# Call suricatactrl and launch the IDS.
	&IDS::call_suricatactrl("start");
}
