#!/usr/bin/perl
use strict;
use File::Basename;
use File::Glob;

# File under Perl Artistic Licence 2.0
# http://www.opensource.org/licenses/artistic-license-2.0.php

# Copyright holder: TBS INTERNET SAS, France
# Author: PALLAVIDINO Luc
# Contact: tag-nss-restrict_ca@tbs-internet.com
# http://www.tbs-certificats.com/
#
# Doc at: http://www.tbs-certificats.com/ssl/nss_tools_crl_ca_control.html
#-------------------------------------------------------------------------
# Version: 1.2
# Date: 2009-05-13
#-------------------------------------------------------------------------
# Version: 1.1
# Date: 2009-05-05
#-------------------------------------------------------------------------
# Version: 1.0
# Date: 2009-03-06

# USAGE: nss_root_util [--builtin] [-norestrictca] [-noupdatecrl] [nocache] [-h]


my $caPolicyName = 'nss_restrict_ca.list';
my $crlPolicyName = 'nss_update_crl.list';
my $defaultPolicyDir = '/etc/';
# nssRestrictCa application location
my $nssRestrictCa = '/usr/local/bin/nss_restrict_ca';
# nssUpdateCrl application location
my $nssUpdateCrl = '/usr/local/bin/nss_update_crl';
# fichier d'exclusion
my $exclusionFile = 'nss_root_util_exclusion.list';
# fichier de log
my $logFile = '/var/log/nss_root_util.log';
# On recupere l'argument --Builtin
my $builtIn = '';
if ($ARGV[0] =~ /-{1,2}builtin/) 
{
	$builtIn = '--builtin';
}

# On recupere l'argument -norestrictca, -noupdatecrl et -nocache
# si ils existent
my $noRCA = 1;
my $noUCRL = 1;
my $noCache = "";
foreach my $it (@ARGV)
{
     if($it =~ /^-{0,2}norestrictca$/)
     {
	     $noRCA = 0;
     }
     if($it =~ /^-{0,2}noupdatecrl$/)
     {
	     $noUCRL = 0;
     }
     if($it =~ /^-{0,2}nocache$/)
     {
	     $noCache = "nocache";
     }
     if($it =~ /^-{0,2}h$/)
     {
	print "USAGE : $0 [--builtin] [-norestrictca] [-noupdatecrl] [-nocache]\
	       \t--builtin : works on the builtin object token\
	       \t-norestrictca : don't use the nss restrict ca util\
	       \t-noupdatecrl : don't use the nss update crl util\
	       \t-nocache : don't cache CRLs downloaded\n";
	       exit 0;
     }
}

# On teste la validite des variables de configuration
if ( ! -e $nssRestrictCa )
{
     print "Unable to access to 'nss_restrict_ca'\n";
     exit;
}
if ( ! -e $nssUpdateCrl )
{
     print "Unable to access to 'nss_update_crl'\n";
     exit;
}

# Liste d'arguments (liste de tableaux associatif composé d'un champ indiquant l'emplacement
# du fichier nss, un second champ indiquant l'emplacement de la politique de la restriction des CA à utiliser,
# et un dernier champ indiquant l'emplacement de la politique de la mise à jour des CRLs)
my @lstArgs = ();

# Permet de trouver les repertoire cert8.db de tous les utilisateurs et d'y associer
# la politique globale ou locale
# /!\ Seulement les repertoires cert8.db relatifs aux produits mozilla
my $caPolicy;
my $crlPolicy;
my @tabExcl = ();

# Fichier de log
open(WRITER,">> ".$logFile) || die ("Error : unable to open log file !\n");
print WRITER "* NSS_ROOT_UTIL LOG : ".localtime(time)."\n";

#On crée le tableau contenant la liste des exclusions
unless (open(FH,$defaultPolicyDir.$exclusionFile))
{
     print WRITER "Error : unable to open exclusion file\n";
     close(WRITER);
     die ("Error : unable to open exclusion file");                                                                                                                 
}

while (<FH>) 
{
          next if /^\s*#/;
          next if /^\s*$/;
          chomp($_);
          push(@tabExcl, $_);
}
close(FH);

#On recherche tous les repertoires de connexion des utilisateurs
my $userFile='/etc/passwd';
unless (open(FH,$userFile))
{
     print WRITER "Error : unable to open /etc/passwd\n";
     close(WRITER);
     die ("Error : unable to open /etc/passwd");
}

while (<FH>) 
{
     #On decoupe les entrées du fichier passwd afin de recuperer les repertoires de connexion
     my @tabTmp = split(/:/, $_);                                                                                                                                   
     my $flagNext = 0;
     # si on trouve l'exclusion d'un utilisateur on passe à l'utilisateur suivant
     foreach my $it (@tabExcl)
     {
          if($it eq $tabTmp[0])
          { 
	     $flagNext = 1;
	     last;
          }
     }
     next if($flagNext == 1);
     
     unless (open(FHEX,$defaultPolicyDir.$exclusionFile))
     { 
          print WRITER "Error : unable to open exclusion file\n"; 
          close(WRITER);
          die ("Error : unable to open exclusion file");
     }
     
     foreach(glob($tabTmp[5]."/.{mozilla,thunderbird,mozilla-thunderbird,firefox}/*/*/cert8.db"))
     {
          # si on trouve l'exclusion d'un utilisateur on passe à l'utilisateur suivant
          my $flagNext1 = 0;
          foreach my $it (@tabExcl)
          {
	if($_ =~ /^$it.*/)
	{
	          $flagNext1 = 1;
	          last;
	}
          }
          next if($flagNext1 == 1);
	
          $caPolicy = $defaultPolicyDir.$caPolicyName;
          $crlPolicy = $defaultPolicyDir.$crlPolicyName;
          #On verifie si l'utilisateur a une politique locale (CA)
          if( -e dirname($_)."/".$caPolicyName)
          {
	     $caPolicy = dirname($_).$caPolicyName;
          }
          #On verifie si l'utilisateur a une politique locale (CRL)
          if( -e dirname($_)."/".$crlPolicyName)
          {
	     $crlPolicy = dirname($_).$crlPolicyName;
          }
          #On enregistre les triplets fichier cert8.db, police CA, police CRL
          push(@lstArgs, {"nssFile" => dirname($_),"caPolicy" => $caPolicy,"crlPolicy" => $crlPolicy});
     }                                                                                                                                                              
}       
close(FH);     

# On lance le programme de mise à jour des CRLs
if($noUCRL == 1)
{
     foreach (@lstArgs)
     {
          next if (! -d "$_->{nssFile}" or ! -e "$_->{crlPolicy}");
          my $logFileTmp = $logFile.rand();
          system("$nssUpdateCrl \"$_->{nssFile}\" \"$_->{crlPolicy}\" \"$noCache\" 2>&1 >> $logFileTmp");
          open(READER,"< ".$logFileTmp) || die ("Error : unable to open temp log file !\n");
          open(WRITER,">> ".$logFile) || die ("Error : unable to open log file !\n");
          # On parcourt le fichier de log temporaire afin de vérifier si l'on doit rediriger une erreur vers la sortie std (afin d'envoyer un mail de notification via cron)
          while (my $log = <READER>)
          {
	# Si une erreur commence par un # on l'écrit sur la sortie std 
	if( $log =~ s/^\s*#//g)
	{
	     print("NSS Database error :\n$log");
	}
	print WRITER "$log";
          }
          close(READER);
          close(WRITER);
          # On supprime le fichier de log temporaire
          if ( -e $logFileTmp )
          {
	unlink ($logFileTmp);
          }
     } 
}

# On lance le programme de restriction des ACs
if($noRCA == 1)
{
     foreach (@lstArgs)
     {
          next if (! -d "$_->{nssFile}" or ! -e "$_->{caPolicy}");
          my $logFileTmp = $logFile.rand();
          system("$nssRestrictCa $builtIn \"$_->{nssFile}\" \"$_->{caPolicy}\" 2>&1 >> $logFileTmp");
          open(READER,"< ".$logFileTmp) || die ("Error : unable to open temp log file !\n");
          open(WRITER,">> ".$logFile) || die ("Error : unable to open log file !\n");
          # On parcourt le fichier de log temporaire afin de vérifier si l'on doit rediriger une erreur vers la sortie std (afin d'envoyer un mail de notification via cron)
          while (my $log = <READER>)
          {
	# Si une erreur commence par un # on l'écrit sur la sortie std
	if( $log =~ s/^\s*#//g)
	{
	     print("NSS Database error :\n$log");
	}
	print WRITER "$log";
          }
          close(READER);
          close(WRITER);
          # On supprime le fichier de log temporaire
          if ( -e $logFileTmp )
          {
	unlink ($logFileTmp);
          }
     } 
}

close(WRITER);
