#!/usr/bin/perl use strict; use File::Copy; # File under Perl Artistic Licence 2.0 # http://www.opensource.org/licenses/artistic-license-2.0.php # Copyright holder: TBS INTERNET SAS, France # Author: JP Donnio # 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.4 # Date: 2009-07-22 #--------------------------------------------------------------------- # Version: 1.3 # Date: 2009-05-13 #--------------------------------------------------------------------- # Version: 1.2 # Date: 2009-05-05 #--------------------------------------------------------------------- # Version: 1.1 # Date: 2009-03-06 #--------------------------------------------------------------------- # Version: 1.0 # Date: 2008-08-20 # USAGE: nss_restrict_ca [--builtin] my $DEBUG=0; my $VERBOSE=1; # process the default or the builtin store? # use --builtin for the builtin store my $BUILTINONLY; my $hwdev; if ($ARGV[0] =~ /-{1,2}builtin/) { $BUILTINONLY="Builtin Object Token:"; $hwdev="-h \"Builtin Object Token\""; shift @ARGV; } # set default DB dir my $DBDIR; if ( -d "$ARGV[0]" ) { $DBDIR=$ARGV[0]; } else { print "Can't access to DB dir $ARGV[0]"; exit; } # set default Policy dir my $CERTLIST; if ( -e "$ARGV[1]" ) { $CERTLIST=$ARGV[1]; if ( ! -f $CERTLIST ) { print "You're certificate list i s not a valid file"; exit } } else { # SET HERE your ca policy file location print "Can't access to certificate list $ARGV[1]"; exit } my $CERTUTIL="/usr/bin/certutil"; my %trusted; # lets load the trusted list open FHCERTLIST, $CERTLIST or die "Could not open $CERTLIST"; while () { chomp; next if /^\s*#/; next if /^\s*$/; my ($thecn,$theflag) = split /\t/; $thecn =~ s/\s+$//; $trusted{"$thecn"}=$theflag; } close FHCERTLIST; # Check certificate(s) in NSS DB ? my $CERLISTLOCATION; my $TEMPDB=""; my @CERTTOCHECK; if ( -e "$DBDIR/nss_verification_certificate.list" ) { $CERLISTLOCATION="$DBDIR/nss_verification_certificate.list"; $TEMPDB="$DBDIR/cert8.db.".rand(); # On ne garde que les certificats de la liste qui sont contenus dans la base NSS open FHCERTLIST, $CERLISTLOCATION or die "Could not open $CERLISTLOCATION"; while () { chomp; next if /^\s*#/; next if /^\s*$/; if ( `$CERTUTIL -L -d \"$DBDIR\" | grep \"$_\"` ) { # Si le certificat est contenu dans la base, on vérifie qu'il est actuellement valide, sinon on ne réalise pas les traitements if ( `$CERTUTIL -L -d \"$DBDIR\" -n \"$_\" 2>&1 | grep \"bad database\"` ) { # On met un # devant le libellé de l'erreur pour indiquer que l'on va renvoyer cette erreur sur la sortie std dans nss_root_util print "#Failed : $_ invalid, I can't perform restrictions on CA in ths NSS database ( $DBDIR )\n"; exit (2); } unshift (@CERTTOCHECK, $_); } } close FHCERTLIST; } # read the database if (open NSSLIST, "$CERTUTIL -L $hwdev -d \"$DBDIR\" |") { while () { # On crée une copie temporaire de la base if( "$CERLISTLOCATION" ) { copy("$DBDIR/cert8.db","$TEMPDB") or die "Copy failed: $!"; } chomp; next if (/\s+[uUpP]*,[uUpP]*,[uUpP]*\s*$/); # dont care about peer or user certs if (/$BUILTINONLY(.*)\s+([cTCG]*,[cTC]*,[cTC]*)\s*$/) # found, save the CN and the flags { my $nsscn=$1; my $nssflag=$2; $nsscn =~ s/\s+$//; if (! $trusted{"$nsscn"}) # we do not trust this item, make sure it is inactive { my $newflag=lc($nssflag); $newflag =~ s/t//g; # lower case t does not exist # we need to ignore the G (stepup) my $cmp1 = $newflag; $cmp1 =~ s/g//ig; my $cmp2 = $nssflag; $cmp2 =~ s/g//ig; if ($cmp1 ne $cmp2) { my $rc = 0xffff & system("$CERTUTIL -M $hwdev -d \"$DBDIR\" -n \"$BUILTINONLY$nsscn\" -t \"$newflag\" "); if ($rc != 0) { print "$DBDIR: Failed to change flags of $nsscn to $newflag with error $rc\n"; } else { print "$DBDIR: Info: disabled $nsscn (was $nssflag now $newflag)\n" if ($VERBOSE); } } } else { # WE TRUST IT - make sure the flags are the same if ($trusted{"$nsscn"} ne $nssflag) # hmm, lets correct the flags { # we need to ignore the G my $cmp1 = $trusted{"$nsscn"}; $cmp1 =~ s/g//ig; my $cmp2 = $nssflag; $cmp2 =~ s/g//ig; if ($cmp1 ne $cmp2) { my $rc = 0xffff & system("$CERTUTIL -M $hwdev -d \"$DBDIR\" -n \"$BUILTINONLY$nsscn\" -t \"$trusted{$nsscn}\" "); if ($rc != 0) { print "$DBDIR: Failed to change flags of trusted $nsscn to $trusted{$nsscn} (was $nssflag) with error $rc\n"; } else { print "$DBDIR: Warning: flags of trusted $nsscn was reset to $trusted{$nsscn} (was $nssflag)\n"; } } } } } else { print "$DBDIR: MISMATCH:$_\n" if ($DEBUG); } # On vérifie la validité des certificats présent dans la liste aprés chaque modification foreach my $CERT (@CERTTOCHECK) { # Si le certificat contenu dans la base NSS est invalide on passe à la version antérieure de la base if ( `$CERTUTIL -L -d \"$DBDIR\" -n \"$CERT\" 2>&1 | grep \"bad database\"` ) { # On met un # devant le libellé de l'erreur pour indiquer que l'on va renvoyer cette erreur sur la sortie std dans nss_root_util print "#Failed : $_ invalid, go back to the previous database version ( $DBDIR )\n"; copy("$TEMPDB","$DBDIR/cert8.db") or die "Copy failed: $!"; } } } #On supprime le fichier temporaire si il existe if ( -e "$TEMPDB" ) { unlink ($TEMPDB); } } else { die "Failed to call $CERTUTIL\n"; } close NSSLIST;