#!/usr/bin/perl
use strict;
use warnings;
use English '-no_match_vars';


my $RSA_LENGTH = 1024;

my %COMPROMISED_FILES_SUMS = (
			'key' => '554056bd16e9f34db567dfd613b706bd',
			'crt'=> 'c2b7b96aa141f8e0e415f6a977193433',
			'pem' => '9b51c428fe86e6ac03fc1427c5bc0958',
		       );


_checkCredentials();
_checkPrograms();

my $eboxDir  = '/var/lib/ebox/conf/';

my ($keyFile, $keyUpdated)  = _generateRSAKey($eboxDir, $RSA_LENGTH);
my $certFile = _generateCert($eboxDir, $keyFile, $keyUpdated);
_generatePem($eboxDir, $certFile, $keyFile, $keyUpdated);
print "All server's certificate files in place\n\n";

sub _checkCredentials
{
  if ($EUID != 0) {
    die "This script can only be run by root";
  }
  
  my ($gid) = split '\s', $EGID;
  if ($gid != 0) {
    die "To run this script your primary group must be set to root";
  }
}

sub _checkPrograms
{
  my @programs = qw(openssl md5sum);
  foreach (@programs) {
    system "which $_";
    if ($? != 0) {
      die "$_ program not found in the path. Make sure it is installed";
    }
  }
}

sub _generateRSAKey
{
  my ($eboxDir, $length) = @_;

  my $type    = 'key';
  my ($keyFile, $alreadyExists) = _generateFileInfraestructure($type, $eboxDir);

  return  ($keyFile, 0)  if   $alreadyExists;

  my @cmds = (
	      "openssl genrsa $RSA_LENGTH > $keyFile",
	      "chmod 0400 $keyFile",
	     );

  foreach (@cmds) {
    system $_;
    if ($? != 0) {
      die "Generation of RSA key failed";
    }
  }

  print "New key file generated\n";
  return ($keyFile, 1);
}


sub _eboxDir
{
  my ($sysconfDir) = @_;
  
  my $eboxDir = "$sysconfDir/ebox";
  if (not -d $eboxDir) {
    die "eBox's directory $eboxDir not found";
  }
  

  return $eboxDir;
}


sub _generateCert
{
  my ($eboxDir, $keyFile, $keyUpdated) = @_;

  my $type = 'crt';
  my ($certFile, $alreadyExists) = _generateFileInfraestructure($type, $eboxDir, $keyUpdated, 'cert');

  return $certFile if $alreadyExists;

  my $subject = q{/CN=eBox\'s\ Server/};

  my @cmds = (
	      "openssl req -new -x509 -batch -subj $subject  -sha1 -days 3650 -key $keyFile > $certFile",
	      "chmod 0400 $certFile",
	     );

  foreach (@cmds) {
    system $_;
    if ($? != 0) {
      die "Generation of CERT file failed";
    }
  }

  print "New certificate file generated\n";
  return $certFile;
}


sub _generatePem
{
  my ($eboxDir, $certFile, $keyFile, $keyUpdated) = @_;

  my $type = 'pem';
  my ($pemFile, $alreadyExists) = _generateFileInfraestructure($type, $eboxDir, $keyUpdated);

  return $pemFile if $alreadyExists;

  my @cmds = (
	      "cat $certFile $keyFile > $pemFile",
	      "chmod 0400 $pemFile",
	     );

  foreach (@cmds) {
    system $_;
    if ($? != 0) {
      die "Generation of PEM file failed";
    }
  }

  print "New PEM file generated\n";
}


sub _generateFileInfraestructure
{
  my ($type, $eboxDir, $alwaysDelete, $extension) = @_;
  defined $alwaysDelete or $alwaysDelete = 0;
  $extension            or $extension    = $type;

  my $sslDir  = _sslDir($eboxDir, $type);
  my $file     = "$sslDir/ebox.$extension";

  if (_correctFileExists($type, $file, $alwaysDelete)) {
    print "$file already exists. Skipping generation\n";
    return ($file, 1);
  }

  my  @cmds =  (
		"touch $file",
		 "chmod 0600 $file",
		 );


  foreach (@cmds) {
    system $_;
    if ($? != 0) {
      die "Generation of $type file failed";
    }
  }

  return ($file, 0);
}

sub _sslDir
{
  my ($eboxDir, $postfix) = @_;

  my $sslDir = "$eboxDir/ssl.$postfix";
  if (not -d $sslDir) {
    print "Creating eBox's ssl.$postfix directory\n";
    mkdir $sslDir, 0600;
  }

  return $sslDir;
}



sub _correctFileExists
{
  my ($type, $file, $alwaysDelete) = @_;

  if ( -e $file) {
    my $compromisedSum = $COMPROMISED_FILES_SUMS{$type};
    if (not $compromisedSum) {
      warn "md5sum for type $type not found. For safety we assume it was compromised";
    }
    else {
      my $md5sumOutput = `md5sum $file`;
      ($? == 0) or die "Error calculating md5 sum of file $file";

      my ($fileSum) = split '\s', $md5sumOutput; # removing file name

      if ($fileSum eq $compromisedSum) {
	print "File $file was compromissed. Removing it\n";
      }
      elsif ($alwaysDelete) {
	print "File $file is not updated. Removing it\n";
      }
      else {
	# left file untouched...
	return 1;
      }

    }

    unlink $file or die "Unable to remove $file";
  }

  return 0;
}

1;
