#!/usr/bin/env perl
# Copyright 2017 Frank Breedijk
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ------------------------------------------------------------------------------
# This script will call the SSLyze scanner and import the results as IVIL
# ------------------------------------------------------------------------------

use strict;
use SeccubusV2;
use Seccubus::IVIL;

use Getopt::Long;
use Carp;

my (
	$sslyze_path,
	$sslyze_options,
	$hosts_file,
	$workspace,
	$scan,
	$nodelete,
	$help,
	$verbose,
	$quiet,
   );

# Create default values
$help = 0;
$sslyze_options = "--regular";


GetOptions(
		'sslyze_path|p=s'	=> \$sslyze_path,
		'sslyze_options|o=s'	=> \$sslyze_options,
		'hosts=s'		=> \$hosts_file,
		'workspace|ws=s'	=> \$workspace,
		'scan|sc=s'		=> \$scan,
		'nodelete'		=> \$nodelete,
		'verbose|v+'		=> \$verbose,
		'quiet|q!'		=> \$quiet,
		'help|h'		=> \$help,
	  );

help() if $help;
$verbose = 0 if $quiet;

my $config = get_config();
my $load_ivil = "perl -I$config->{paths}->{modules} $config->{paths}->{bindir}\/load_ivil";
my $attach_file = "perl -I$config->{paths}->{modules} $config->{paths}->{bindir}\/attach_file";

print "Hosts file specified $hosts_file\n" if $verbose;
if ( ! $hosts_file || ! -e $hosts_file ) {
	print "You must specify a valid hosts file";
	help();
} elsif ( ! $workspace ) {
	print "You must specify a workspace name";
	help();
} elsif ( ! $scan ){
	print "You must specify a scan name";
	help();
};

$sslyze_path = get_sslyze_path() unless $sslyze_path;
if ( ! $sslyze_path || !  -e $sslyze_path ) {
	print "Unable to find sslyze on your system. I suggest you use the --sslyze_path option\nto provide a path to sslyze\n";
	help();
}
print "SSLyze found in $sslyze_path\n" if $verbose;
my $sslyze_version = get_sslyze_version($sslyze_path);

my $tempfile = "/tmp/seccubus.sslyze.$$";

my $sslyze_options = "--targets_in='$hosts_file' --xml_out='$tempfile.xml' $sslyze_options";
print "SSLyze options: $sslyze_options\n" if $verbose;

open(my $TMP, ">", "$tempfile.txt") or die "Unable to open tempfile $tempfile for writing";

my $timestamp = make_timestamp();
print "Timestamp = $timestamp\n" if $verbose;

print "Execuing $sslyze_path $sslyze_options\n" unless $quiet;
open(my $SSLYZE, "-|", "$sslyze_path $sslyze_options") or die "Unable to open pipe";

while ( <$SSLYZE> ) {
	print $_ if $verbose;
	print $TMP $_;
}
close $TMP;
close $SSLYZE;

print "Scanning done, converting .xml to ivil\n" unless $quiet;
my $cmd = "perl -I$config->{paths}->{modules} $config->{paths}->{bindir}\/sslyze2ivil --workspace '$workspace' --scan '$scan' --timestamp=$timestamp --infile '$tempfile.xml' ";
$cmd .= "-v" if $verbose > 1;

print "Executing $cmd\n" if $verbose;
my $result = `$cmd 2>&1`;
print "$result\n" if $verbose;

print "Importing ivil\n" unless $quiet;
$cmd = "$load_ivil --workspace '$workspace' --scan '$scan' --timestamp $timestamp";
$cmd .= " -v" if $verbose > 1;
$cmd .= " '$tempfile.ivil.xml'";
print "Execuing $cmd\n" if $verbose > 1;
my $result = `$cmd 2>&1`;
print "$result\n" if $verbose > 1;

print "Scan imported, adding files to scan $scan in workspace $workspace\n" unless $quiet;

$cmd = "$attach_file --workspace '$workspace' --scan '$scan' --timestamp $timestamp --file '$tempfile.txt' --description 'Command line output'";
$cmd .= " -v" if $verbose > 1;
print "Execuing $cmd\n" if $verbose > 1;
my $result = `$cmd 2>&1`;
print "$result\n" if $verbose > 1;

$cmd = "$attach_file --workspace '$workspace' --scan '$scan' --timestamp $timestamp --file '$tempfile.xml' --description 'Native SSLyze xml output'";
$cmd .= " -v" if $verbose > 1;
print "Execuing $cmd\n" if $verbose > 1;
my $result = `$cmd 2>&1`;
print "$result\n" if $verbose > 1;

$cmd = "$attach_file --workspace '$workspace' --scan '$scan' --timestamp $timestamp --file '$tempfile.ivil.xml' --description 'IVIL output'";
$cmd .= " -v" if $verbose > 1;
print "Execuing $cmd\n" if $verbose > 1;
my $result = `$cmd 2>&1`;
print "$result\n" if $verbose > 1;

# Cleanup
unless ( $nodelete ) {
	unlink "$tempfile.txt" if -e "$tempfile.txt";
	unlink "$tempfile.xml" if -e "$tempfile.xml";
	unlink "$tempfile.ivil.xml" if -e "$tempfile.ivil.xml";
}

exit(0);

sub help() {
	print "

Usage: scan 	[--sslyze_path|p <path to sslyze>] \\
		[--sslyze_options <additional sslyze options>]] \\
		--hosts <hosts file> [--verbose|v] [--quiet|q] [--help|h]

Arguments:
--sslyze_path	- You can use this optional parameter to provide the script with
(-p)		  the path to sslyze.py or sslyze. If you do not provide this the
		  script tries to find the files itself and fails if it cannot
		  fidn them.
--sslyze_options	- Additional command line options to provide to sslyze see
(-o)		  'sslyze -Help' for more information. Please quote the options so that they
		  are passed as a single block. E.g. -o '-option1 -option2'

--hosts		- The file containing the 'hosts' to scan
--workspace	- Name of the workspace to load the findings into
--scan		- Name of the scan to load the findings into
--verbose (-v)	- Be verbose during execution
--nodelete	- Don't delete temporary files
--quiet (-q)	- Don't print output
--help (-h)	- Print this message
";
	exit(1);
}

sub get_sslyze_path() {
	my $path = `which sslyze`;
	if ( $path =~ /(^\/.*sslyze$)/ ) {
		return $1;
	}
	$path = `which sslyze.py`;
	if ( $path =~ /(^\/.*sslyze\.py$)/ ) {
		return $1;
	}
	if ( -e '/opt/sslyze/sslyze.py' ) {
		return '/opt/sslyze/sslyze.py';
	}
	return;
}

sub get_sslyze_version() {
	my $sslyze_path = shift;

	# Added some error handling to fix #16
	my $version=`$sslyze_path -Version 2>&1`;
	if ( $version =~ /SSLyze main\s+(\d+\.\d+\.\d+)/ ) {
		return $1;
	} else {
		return "ERROR: $version";
	}
}

sub check_sslyze_version() {
	my $version = shift;
	$version =~ /(\d+)\.(\d+)\.(\d+)/;
	if ( ( $1 > 2 ) ||
	     ( $1 == 2 && $2 > 1 ) ||
	     ($1 == 2 && $2 == 1 && $3 >= 2 )
	   ) {
		return 1;
	} else {
		return 0;
	}
}

sub make_timestamp() {
	my ($second, $minute, $hour, $day, $month, $year) = localtime();
	$month++;
	$second = "0" . $second if $second < 10;
	$minute = "0" . $minute if $minute <10;
	$hour = "0". $hour if $hour < 10;
	$day = "0". $day if $day <10;
	$month = "0" . $month if $month <10;
	$year += 1900;

	return "$year$month$day$hour$minute$second";
}

