| 1 | #!/usr/bin/perl |
|---|
| 2 | |
|---|
| 3 | |
|---|
| 4 | # Perl c_rehash script, scan all files in a directory |
|---|
| 5 | # and add symbolic links to their hash values. |
|---|
| 6 | |
|---|
| 7 | my $openssl; |
|---|
| 8 | |
|---|
| 9 | my $dir; |
|---|
| 10 | |
|---|
| 11 | if(defined $ENV{OPENSSL}) { |
|---|
| 12 | $openssl = $ENV{OPENSSL}; |
|---|
| 13 | } else { |
|---|
| 14 | $openssl = "openssl"; |
|---|
| 15 | $ENV{OPENSSL} = $openssl; |
|---|
| 16 | } |
|---|
| 17 | |
|---|
| 18 | $ENV{PATH} .= ":$dir/bin"; |
|---|
| 19 | |
|---|
| 20 | if(! -x $openssl) { |
|---|
| 21 | my $found = 0; |
|---|
| 22 | foreach (split /:/, $ENV{PATH}) { |
|---|
| 23 | if(-x "$_/$openssl") { |
|---|
| 24 | $found = 1; |
|---|
| 25 | last; |
|---|
| 26 | } |
|---|
| 27 | } |
|---|
| 28 | if($found == 0) { |
|---|
| 29 | print STDERR "c_rehash: rehashing skipped ('openssl' program not available)\n"; |
|---|
| 30 | exit 0; |
|---|
| 31 | } |
|---|
| 32 | } |
|---|
| 33 | |
|---|
| 34 | if(@ARGV) { |
|---|
| 35 | @dirlist = @ARGV; |
|---|
| 36 | } elsif($ENV{SSL_CERT_DIR}) { |
|---|
| 37 | @dirlist = split /:/, $ENV{SSL_CERT_DIR}; |
|---|
| 38 | } else { |
|---|
| 39 | $dirlist[0] = "$dir/certs"; |
|---|
| 40 | } |
|---|
| 41 | |
|---|
| 42 | |
|---|
| 43 | foreach (@dirlist) { |
|---|
| 44 | if(-d $_ and -w $_) { |
|---|
| 45 | hash_dir($_); |
|---|
| 46 | } |
|---|
| 47 | } |
|---|
| 48 | |
|---|
| 49 | sub hash_dir { |
|---|
| 50 | my %hashlist; |
|---|
| 51 | print "Doing $_[0]\n"; |
|---|
| 52 | chdir $_[0]; |
|---|
| 53 | opendir(DIR, "."); |
|---|
| 54 | my @flist = readdir(DIR); |
|---|
| 55 | # Delete any existing symbolic links |
|---|
| 56 | foreach (grep {/^[\da-f]+\.r{0,1}\d+$/} @flist) { |
|---|
| 57 | if(-l $_) { |
|---|
| 58 | unlink $_; |
|---|
| 59 | } |
|---|
| 60 | } |
|---|
| 61 | closedir DIR; |
|---|
| 62 | FILE: foreach $fname (grep {/\.pem$/} @flist) { |
|---|
| 63 | # Check to see if certificates and/or CRLs present. |
|---|
| 64 | my ($cert, $crl) = check_file($fname); |
|---|
| 65 | if(!$cert && !$crl) { |
|---|
| 66 | print STDERR "WARNING: $fname does not contain a certificate or CRL: skipping\n"; |
|---|
| 67 | next; |
|---|
| 68 | } |
|---|
| 69 | link_hash_cert($fname) if($cert); |
|---|
| 70 | link_hash_crl($fname) if($crl); |
|---|
| 71 | } |
|---|
| 72 | } |
|---|
| 73 | |
|---|
| 74 | sub check_file { |
|---|
| 75 | my ($is_cert, $is_crl) = (0,0); |
|---|
| 76 | my $fname = $_[0]; |
|---|
| 77 | open IN, $fname; |
|---|
| 78 | while(<IN>) { |
|---|
| 79 | if(/^-----BEGIN (.*)-----/) { |
|---|
| 80 | my $hdr = $1; |
|---|
| 81 | if($hdr =~ /^(X509 |TRUSTED |)CERTIFICATE$/) { |
|---|
| 82 | $is_cert = 1; |
|---|
| 83 | last if($is_crl); |
|---|
| 84 | } elsif($hdr eq "X509 CRL") { |
|---|
| 85 | $is_crl = 1; |
|---|
| 86 | last if($is_cert); |
|---|
| 87 | } |
|---|
| 88 | } |
|---|
| 89 | } |
|---|
| 90 | close IN; |
|---|
| 91 | return ($is_cert, $is_crl); |
|---|
| 92 | } |
|---|
| 93 | |
|---|
| 94 | |
|---|
| 95 | # Link a certificate to its subject name hash value, each hash is of |
|---|
| 96 | # the form <hash>.<n> where n is an integer. If the hash value already exists |
|---|
| 97 | # then we need to up the value of n, unless its a duplicate in which |
|---|
| 98 | # case we skip the link. We check for duplicates by comparing the |
|---|
| 99 | # certificate fingerprints |
|---|
| 100 | |
|---|
| 101 | sub link_hash_cert { |
|---|
| 102 | my $fname = $_[0]; |
|---|
| 103 | $fname =~ s/'/'\\''/g; |
|---|
| 104 | my ($hash, $fprint) = `"$openssl" x509 -hash -fingerprint -noout -in '$fname'`; |
|---|
| 105 | chomp $hash; |
|---|
| 106 | chomp $fprint; |
|---|
| 107 | $fprint =~ s/^.*=//; |
|---|
| 108 | $fprint =~ tr/://d; |
|---|
| 109 | my $suffix = 0; |
|---|
| 110 | # Search for an unused hash filename |
|---|
| 111 | while(exists $hashlist{"$hash.$suffix"}) { |
|---|
| 112 | # Hash matches: if fingerprint matches its a duplicate cert |
|---|
| 113 | if($hashlist{"$hash.$suffix"} eq $fprint) { |
|---|
| 114 | print STDERR "WARNING: Skipping duplicate certificate $fname\n"; |
|---|
| 115 | return; |
|---|
| 116 | } |
|---|
| 117 | $suffix++; |
|---|
| 118 | } |
|---|
| 119 | $hash .= ".$suffix"; |
|---|
| 120 | print "$fname => $hash\n"; |
|---|
| 121 | $symlink_exists=eval {symlink("",""); 1}; |
|---|
| 122 | if ($symlink_exists) { |
|---|
| 123 | symlink $fname, $hash; |
|---|
| 124 | } else { |
|---|
| 125 | system ("cp", $fname, $hash); |
|---|
| 126 | } |
|---|
| 127 | $hashlist{$hash} = $fprint; |
|---|
| 128 | } |
|---|
| 129 | |
|---|
| 130 | # Same as above except for a CRL. CRL links are of the form <hash>.r<n> |
|---|
| 131 | |
|---|
| 132 | sub link_hash_crl { |
|---|
| 133 | my $fname = $_[0]; |
|---|
| 134 | $fname =~ s/'/'\\''/g; |
|---|
| 135 | my ($hash, $fprint) = `"$openssl" crl -hash -fingerprint -noout -in '$fname'`; |
|---|
| 136 | chomp $hash; |
|---|
| 137 | chomp $fprint; |
|---|
| 138 | $fprint =~ s/^.*=//; |
|---|
| 139 | $fprint =~ tr/://d; |
|---|
| 140 | my $suffix = 0; |
|---|
| 141 | # Search for an unused hash filename |
|---|
| 142 | while(exists $hashlist{"$hash.r$suffix"}) { |
|---|
| 143 | # Hash matches: if fingerprint matches its a duplicate cert |
|---|
| 144 | if($hashlist{"$hash.r$suffix"} eq $fprint) { |
|---|
| 145 | print STDERR "WARNING: Skipping duplicate CRL $fname\n"; |
|---|
| 146 | return; |
|---|
| 147 | } |
|---|
| 148 | $suffix++; |
|---|
| 149 | } |
|---|
| 150 | $hash .= ".r$suffix"; |
|---|
| 151 | print "$fname => $hash\n"; |
|---|
| 152 | $symlink_exists=eval {symlink("",""); 1}; |
|---|
| 153 | if ($symlink_exists) { |
|---|
| 154 | symlink $fname, $hash; |
|---|
| 155 | } else { |
|---|
| 156 | system ("cp", $fname, $hash); |
|---|
| 157 | } |
|---|
| 158 | $hashlist{$hash} = $fprint; |
|---|
| 159 | } |
|---|
| 160 | |
|---|