#!/usr/bin/perl

#
#
# 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.
#

use strict;

use lib qw(/opt/traffic_ops/install/lib /opt/traffic_ops/app/local/lib/perl5 /opt/traffic_ops/app/lib);

use JSON;
use InstallUtils;
use File::Temp;
use Data::Dumper;
use File::Copy;

my $ca       = "/etc/pki/tls/certs/localhost.ca";
my $csr      = "/etc/pki/tls/certs/localhost.csr";
my $cert     = "/etc/pki/tls/certs/localhost.crt";
my $cdn_conf = "/opt/traffic_ops/app/conf/cdn.conf";
my $key      = "/etc/pki/tls/private/localhost.key";
my $msg      = << 'EOF';

	We're now running a script to generate a self signed X509 SSL certificate.
	When prompted to enter a pass phrase, just enter 'pass' each time.  The
	pass phrase will be stripped from the private key before installation.

	When prompted to enter a 'challenge password', just hit the ENTER key.

	The remaining enformation Country, State, Locality, etc... are required to
	generate a properly formatted SSL certificate.

EOF

sub writeCdn_conf {
	my $cdn_conf = shift;


	# load as perl hash to find string to be replaced
	my $cdnh = do $cdn_conf;

	# get existing port, if any
	my $listen = $cdnh->{hypnotoad}{listen}[0];
	my ($port) = $listen =~ /:(\d+)/;
	if (!defined($port)) {
			$port = 60443;
	}
	# listen param to be inserted
	my $listen_str = "https://[::]:${port}?cert=${cert}&key=${key}&ca=${ca}&verify=0x00&ciphers=AES128-GCM-SHA256:HIGH:!RC4:!MD5:!aNULL:!EDH:!ED";

	if ( exists $cdnh->{hypnotoad} ) {
		$cdnh->{hypnotoad}{listen} = [$listen_str];
	}
	else {

		# add the whole hypnotoad config without affecting anything else in the config
		$cdnh->{hypnotoad} = {
			listen   => [$listen_str],
			user     => 'trafops',
			group    => 'trafops',
			pid_file => '/var/run/traffic_ops.pid',
			workers  => 48,
		};
	}

	# dump conf data in compact but readable form
	my $dumper = Data::Dumper->new( [$cdnh] );
	$dumper->Indent(1)->Terse(1)->Quotekeys(0);

	# write whole config to temp file in pwd (keeps in same filesystem)
	my $tmpfile = File::Temp->new(DIR => '.');
	print $tmpfile $dumper->Dump();
	close $tmpfile;

	# make backup of current file
	my $backup_num = 0;
	my $backup_name;
	do {
		$backup_num++;
		$backup_name = "$cdn_conf.backup$backup_num";
	} while ( -e $backup_name );
	rename( $cdn_conf, $backup_name ) or die("rename(): $!");

	# rename temp file to cdn.conf and set ownership/permissions same as backup
	my @stats = stat($backup_name);
	my ( $uid, $gid, $perm ) = @stats[ 4, 5, 2 ];
	move( "$tmpfile", $cdn_conf ) or die("move(): $!");

	chown $uid, $gid, $cdn_conf;
	chmod $perm, $cdn_conf;
}

InstallUtils::execCommand( "/usr/bin/tput", "clear" );
print $msg;
InstallUtils::promptUser( "Hit Enter when you are ready to continue", "" );

print "Postinstall SSL Certificate Creation.\n\n";

# execOpenssl takes a description of the command being done, and an array of arguments to OpenSSL,
# and tries to execute the command, on failure prompting the user to retry.
# The description should be capitalized, but not terminated with punctuation.
# Returns the OpenSSL exit code.
sub execOpenssl {
	my ( $description, @args ) = @_;
	print $description . ".\n\n";
	my $result = 1;
	while ( $result != 0 ) {
		$result = InstallUtils::execCommand( "openssl", @args );
		if ( $result != 0 ) {
			my $ans = "";
			while ( $ans !~ /^[yY]/ && $ans !~ /^[nN]/) {
				$ans = InstallUtils::promptUser( $description . " failed. Try again (y/n)", "y" );
			}
			if ( $ans =~ /^[nN]/ ) {
				return $result
			}
		}
	}
	return $result;
}

if ( execOpenssl( "Generating an RSA Private Server Key", "genrsa", "-des3", "-out", "server.key", "1024" ) != 0 ) {
	exit 1;
}
print "\nThe server key has been generated.\n\n";

if ( execOpenssl( "Creating a Certificate Signing Request (CSR)", "req", "-new", "-key", "server.key", "-out", "server.csr" ) != 0 ) {
	exit 1;
}
print "\nThe Certificate Signing Request has been generated.\n";

InstallUtils::execCommand( "/bin/mv", "server.key", "server.key.orig" );

if ( execOpenssl( "Removing the pass phrase from the server key", "rsa", "-in", "server.key.orig", "-out", "server.key" ) != 0 ) {
	exit 1;
}
print "\nThe pass phrase has been removed from the server key.\n";

if ( execOpenssl( "Generating a Self-signed certificate", "x509", "-req", "-days", "365", "-in", "server.csr", "-signkey", "server.key", "-out", "server.crt" ) != 0 ) {
	exit 1;
}
print "\nA server key and self signed certificate has been generated.\n";

print "\nInstalling the server key and server certificate.\n";

my $result = InstallUtils::execCommand( "/bin/cp", "server.key", "$key" );
if ( $result != 0 ) {
	print "Failed to install the private server key.\n";
	exit 3;
}
$result = InstallUtils::execCommand( "/bin/chmod", "600",             "$key" );
$result = InstallUtils::execCommand( "/bin/chown", "trafops:trafops", "$key" );

if ( $result != 0 ) {
	print "Failed to install the private server key.\n";
	exit 4;
}

print "\nThe private key has been installed.\n";
print "\nInstalling the self signed certificate.\n";

$result = InstallUtils::execCommand( "/bin/cp", "server.crt", "$cert" );

if ( $result != 0 ) {
	print "Failed to install the self signed certificate.\n";
	exit 5;
}

$result = InstallUtils::execCommand( "/bin/chmod", "600",             "$cert" );
$result = InstallUtils::execCommand( "/bin/chown", "trafops:trafops", "$cert" );

if ( $result != 0 ) {
	print "Failed to install the self signed certificate.\n";
	exit 6;
}

print "\nSaving the self signed csr.\n";
$result = InstallUtils::execCommand( "/bin/cp", "server.csr", "$csr" );

if ( $result != 0 ) {
	print "Failed to save the self signed csr.\n";
	exit 7;
}
$result = InstallUtils::execCommand( "/bin/chmod", "664",             "$csr" );
$result = InstallUtils::execCommand( "/bin/chown", "trafops:trafops", "$csr" );

writeCdn_conf($cdn_conf);

my $msg = << 'EOF';

	The self signed certificate has now been installed. 
	
	You may obtain a certificate signed by a Certificate Authority using the
	server.csr file saved in the current directory.  Once you have obtained
	a signed certificate, copy it to /etc/pki/tls/certs/localhost.crt and
	restart Traffic Ops.

EOF

print $msg, "\n";

exit 0;
