1167612Ssimon#!/usr/bin/perl
255714Skris
355714Skris
468651Skris# Perl c_rehash script, scan all files in a directory
568651Skris# and add symbolic links to their hash values.
655714Skris
768651Skrismy $openssl;
855714Skris
968651Skrismy $dir = "/usr/local/ssl";
10215697Ssimonmy $prefix = "/usr/local/ssl";
1155714Skris
1268651Skrisif(defined $ENV{OPENSSL}) {
1368651Skris	$openssl = $ENV{OPENSSL};
1468651Skris} else {
1568651Skris	$openssl = "openssl";
1668651Skris	$ENV{OPENSSL} = $openssl;
1768651Skris}
1868651Skris
1968651Skris$ENV{PATH} .= ":$dir/bin";
2068651Skris
21109998Smarkmif(! -x $openssl) {
2268651Skris	my $found = 0;
2368651Skris	foreach (split /:/, $ENV{PATH}) {
24109998Smarkm		if(-x "$_/$openssl") {
2568651Skris			$found = 1;
2668651Skris			last;
2768651Skris		}	
2868651Skris	}
2968651Skris	if($found == 0) {
3068651Skris		print STDERR "c_rehash: rehashing skipped ('openssl' program not available)\n";
3168651Skris		exit 0;
3268651Skris	}
3368651Skris}
3468651Skris
3568651Skrisif(@ARGV) {
3668651Skris	@dirlist = @ARGV;
3768651Skris} elsif($ENV{SSL_CERT_DIR}) {
3868651Skris	@dirlist = split /:/, $ENV{SSL_CERT_DIR};
3968651Skris} else {
4068651Skris	$dirlist[0] = "$dir/certs";
4168651Skris}
4268651Skris
4368651Skris
4468651Skrisforeach (@dirlist) {
4568651Skris	if(-d $_ and -w $_) {
4668651Skris		hash_dir($_);
4768651Skris	}
4868651Skris}
4968651Skris
5068651Skrissub hash_dir {
5168651Skris	my %hashlist;
5268651Skris	print "Doing $_[0]\n";
5368651Skris	chdir $_[0];
5468651Skris	opendir(DIR, ".");
5568651Skris	my @flist = readdir(DIR);
5668651Skris	# Delete any existing symbolic links
5768651Skris	foreach (grep {/^[\da-f]+\.r{0,1}\d+$/} @flist) {
5868651Skris		if(-l $_) {
5968651Skris			unlink $_;
6068651Skris		}
6168651Skris	}
6268651Skris	closedir DIR;
6368651Skris	FILE: foreach $fname (grep {/\.pem$/} @flist) {
6468651Skris		# Check to see if certificates and/or CRLs present.
6568651Skris		my ($cert, $crl) = check_file($fname);
6668651Skris		if(!$cert && !$crl) {
6768651Skris			print STDERR "WARNING: $fname does not contain a certificate or CRL: skipping\n";
6868651Skris			next;
6968651Skris		}
7068651Skris		link_hash_cert($fname) if($cert);
7168651Skris		link_hash_crl($fname) if($crl);
7268651Skris	}
7368651Skris}
7468651Skris
7568651Skrissub check_file {
7668651Skris	my ($is_cert, $is_crl) = (0,0);
7768651Skris	my $fname = $_[0];
7868651Skris	open IN, $fname;
7968651Skris	while(<IN>) {
8068651Skris		if(/^-----BEGIN (.*)-----/) {
8168651Skris			my $hdr = $1;
8268651Skris			if($hdr =~ /^(X509 |TRUSTED |)CERTIFICATE$/) {
8368651Skris				$is_cert = 1;
8468651Skris				last if($is_crl);
8568651Skris			} elsif($hdr eq "X509 CRL") {
8668651Skris				$is_crl = 1;
8768651Skris				last if($is_cert);
8868651Skris			}
8968651Skris		}
9068651Skris	}
9168651Skris	close IN;
9268651Skris	return ($is_cert, $is_crl);
9368651Skris}
9468651Skris
9568651Skris
9668651Skris# Link a certificate to its subject name hash value, each hash is of
9768651Skris# the form <hash>.<n> where n is an integer. If the hash value already exists
9868651Skris# then we need to up the value of n, unless its a duplicate in which
9968651Skris# case we skip the link. We check for duplicates by comparing the
10068651Skris# certificate fingerprints
10168651Skris
10268651Skrissub link_hash_cert {
10368651Skris		my $fname = $_[0];
104109998Smarkm		$fname =~ s/'/'\\''/g;
105109998Smarkm		my ($hash, $fprint) = `"$openssl" x509 -hash -fingerprint -noout -in '$fname'`;
10668651Skris		chomp $hash;
10768651Skris		chomp $fprint;
10868651Skris		$fprint =~ s/^.*=//;
10968651Skris		$fprint =~ tr/://d;
11068651Skris		my $suffix = 0;
11168651Skris		# Search for an unused hash filename
11268651Skris		while(exists $hashlist{"$hash.$suffix"}) {
11368651Skris			# Hash matches: if fingerprint matches its a duplicate cert
11468651Skris			if($hashlist{"$hash.$suffix"} eq $fprint) {
11568651Skris				print STDERR "WARNING: Skipping duplicate certificate $fname\n";
11668651Skris				return;
11768651Skris			}
11868651Skris			$suffix++;
11968651Skris		}
12068651Skris		$hash .= ".$suffix";
12168651Skris		print "$fname => $hash\n";
12276866Skris		$symlink_exists=eval {symlink("",""); 1};
12376866Skris		if ($symlink_exists) {
12476866Skris			symlink $fname, $hash;
12576866Skris		} else {
12676866Skris			system ("cp", $fname, $hash);
12776866Skris		}
12868651Skris		$hashlist{$hash} = $fprint;
12968651Skris}
13068651Skris
13168651Skris# Same as above except for a CRL. CRL links are of the form <hash>.r<n>
13268651Skris
13368651Skrissub link_hash_crl {
13468651Skris		my $fname = $_[0];
135109998Smarkm		$fname =~ s/'/'\\''/g;
136109998Smarkm		my ($hash, $fprint) = `"$openssl" crl -hash -fingerprint -noout -in '$fname'`;
13768651Skris		chomp $hash;
13868651Skris		chomp $fprint;
13968651Skris		$fprint =~ s/^.*=//;
14068651Skris		$fprint =~ tr/://d;
14168651Skris		my $suffix = 0;
14268651Skris		# Search for an unused hash filename
14368651Skris		while(exists $hashlist{"$hash.r$suffix"}) {
14468651Skris			# Hash matches: if fingerprint matches its a duplicate cert
14568651Skris			if($hashlist{"$hash.r$suffix"} eq $fprint) {
14668651Skris				print STDERR "WARNING: Skipping duplicate CRL $fname\n";
14768651Skris				return;
14868651Skris			}
14968651Skris			$suffix++;
15068651Skris		}
15168651Skris		$hash .= ".r$suffix";
15268651Skris		print "$fname => $hash\n";
15376866Skris		$symlink_exists=eval {symlink("",""); 1};
15476866Skris		if ($symlink_exists) {
15576866Skris			symlink $fname, $hash;
15676866Skris		} else {
15776866Skris			system ("cp", $fname, $hash);
15876866Skris		}
15968651Skris		$hashlist{$hash} = $fprint;
16068651Skris}
16168651Skris
162