#!/usr/bin/perl
# Copyright (c) 2017-2020, AT&T Intellectual Property.  All rights reserved.
# Copyright (c) 2016 by Brocade Communications Systems, Inc.
# All rights reserved.

# SPDX-License-Identifier: GPL-2.0-only

use strict;

use File::Copy;
use Crypt::OpenSSL::X509;

use lib "/opt/vyatta/share/perl5";
use Vyatta::Config;
use Vyatta::VPN::Util qw(vpn_exec vpn_die);

my $CA_CERT_PATH = '/etc/ipsec.d/cacerts';
my $CRL_PATH = '/etc/ipsec.d/crls';
my $SERVER_CERT_PATH = '/etc/ipsec.d/certs';
my $SERVER_KEY_PATH = '/etc/ipsec.d/private';

my @DIRS = ( $CA_CERT_PATH, $CRL_PATH, $SERVER_CERT_PATH, $SERVER_KEY_PATH );

my $vcVPN = Vyatta::Config->new('security vpn');


sub get_x509_hash {

  my ($clinode, $src, $is_file) = @_;
  my $x509_obj;

  eval {
    if ($is_file) {
      $x509_obj = Crypt::OpenSSL::X509->new_from_file($src);
    } else {
      $x509_obj = Crypt::OpenSSL::X509->new_from_string($src);
    }
  };
  vpn_die([split(' ', $clinode)], "Invalid $clinode: \"$src\" can not parse as x509 certificate.")
    if ($@);
  return $x509_obj->hash();
}

sub copy_cert_file_to_dir {
  my ($src, $clinode, $destdir, $is_crl) = @_;

  my $new_fn;
  return unless defined($src);

  vpn_die([split(' ', $clinode)], "Invalid $clinode: \"$src\" does not exist.") unless (-f $src);


  if ($is_crl) {
     my $hash = `/usr/bin/openssl crl -in '$src'  -noout -hash`;
     $hash =~ s/\R//g;
     vpn_die([split(' ', $clinode)], "Invalid $clinode: \"$src\" can not parse as CRL.")
       if ($?);

     $new_fn = $hash.".crl";

  } else {
     my $x509_hash = get_x509_hash($clinode, $src, 1);
     $new_fn = $x509_hash.".crt";
  }

  my $dest = $destdir."/".$new_fn;

  if (!copy($src, $dest)) {
    vpn_die([split(' ', $clinode)] , "Cannot copy $clinode \"$src\"");
  }

}

# Copy x509 certificate to dstination with certificate hash file name
sub copy_x509_cert_file_to_dir {
  my ($src, $clinode, $destdir) = @_;

  copy_cert_file_to_dir($src, $clinode, $destdir, 0);
}

sub copy_x509_cert_content_to_dir {
  my ($x509_content, $clinode, $destdir) = @_;

  $x509_content =~ s|\\n|\n|g;

  my $x509_hash = get_x509_hash($clinode, $x509_content, 0);
  my $dest = $destdir."/".$x509_hash.".crt";

  open my $x509_fd, '>', $dest
    or die "Can't write X.509 CA certificate file: $dest: $!";
  print $x509_fd $x509_content;
  close $x509_fd;
}


# Copy CRL to dstination with certificate hash file name
sub copy_crl_file_to_dir {
  my ($src, $clinode, $destdir) = @_;

  copy_cert_file_to_dir($src, $clinode, $destdir, 1);
}

##
## 1. vanish existing files
##
foreach my $dir (@DIRS) {
  my @files = glob( $dir . '/*' );
  unlink( @files );
}

##
## 2. copy current configured files
##

# IPsec
my @peers = $vcVPN->listNodes('ipsec site-to-site peer');
foreach my $peer (@peers) {
  my $pfx  = "ipsec site-to-site peer $peer authentication x509 ";
  my $ca_file = $vcVPN->returnValue("$pfx ca-cert-file");
  my $crl_file = $vcVPN->returnValue("$pfx crl-file");
  copy_x509_cert_file_to_dir($ca_file, 'ca-cert-file', $CA_CERT_PATH);
  copy_crl_file_to_dir($crl_file, 'crl-file', $CRL_PATH);

  ## No need to copy keys or node certificates.
  ## Using full path from CLI for configuration.
}

# DMVPN: x509 certs is not using legacy CRL or CA-cert CLIs.

# RA-VPN
my $pfx = 'l2tp remote-access ipsec-settings authentication x509';
my $ca_file = $vcVPN->returnValue("$pfx ca-cert-file");
my $crl_file = $vcVPN->returnValue("$pfx crl-file");
copy_x509_cert_file_to_dir($ca_file, 'ca-cert-file', $CA_CERT_PATH);
copy_crl_file_to_dir($crl_file, 'crl-file', $CRL_PATH);

## No need to copy keys or node certificates.
## Using full path from CLI for configuration.


my @cacerts = $vcVPN->returnValues('x509 ca-certs');
foreach my $ca_cert (@cacerts) {
  my $l = substr($ca_cert, 0, 1);
  if ($l eq '/') {
    copy_x509_cert_file_to_dir($ca_cert, 'x509 ca-certs', $CA_CERT_PATH);
  } else {
    copy_x509_cert_content_to_dir($ca_cert, 'x509 ca-certs', $CA_CERT_PATH);
  }
}

##
## 3. re-read secrets: in the configd hook, once all new configs got generated.
##
