1#!/usr/bin/perl 2 3# Perl c_rehash script, scan all files in a directory 4# and add symbolic links to their hash values. 5 6my $dir = "/usr/local/ssl"; 7my $prefix = "/usr/local/ssl"; 8 9my $openssl = $ENV{OPENSSL} || "openssl"; 10my $pwd; 11my $x509hash = "-subject_hash"; 12my $crlhash = "-hash"; 13my $verbose = 0; 14my $symlink_exists=eval {symlink("",""); 1}; 15my $removelinks = 1; 16 17## Parse flags. 18while ( $ARGV[0] =~ /^-/ ) { 19 my $flag = shift @ARGV; 20 last if ( $flag eq '--'); 21 if ( $flag eq '-old') { 22 $x509hash = "-subject_hash_old"; 23 $crlhash = "-hash_old"; 24 } elsif ( $flag eq '-h') { 25 help(); 26 } elsif ( $flag eq '-n' ) { 27 $removelinks = 0; 28 } elsif ( $flag eq '-v' ) { 29 $verbose++; 30 } 31 else { 32 print STDERR "Usage error; try -help.\n"; 33 exit 1; 34 } 35} 36 37sub help { 38 print "Usage: c_rehash [-old] [-h] [-v] [dirs...]\n"; 39 print " -old use old-style digest\n"; 40 print " -h print this help text\n"; 41 print " -v print files removed and linked\n"; 42 exit 0; 43} 44 45eval "require Cwd"; 46if (defined(&Cwd::getcwd)) { 47 $pwd=Cwd::getcwd(); 48} else { 49 $pwd=`pwd`; 50 chomp($pwd); 51} 52 53# DOS/Win32 or Unix delimiter? Prefix our installdir, then search. 54my $path_delim = ($pwd =~ /^[a-z]\:/i) ? ';' : ':'; 55$ENV{PATH} = "$prefix/bin" . ($ENV{PATH} ? $path_delim . $ENV{PATH} : ""); 56 57if(! -x $openssl) { 58 my $found = 0; 59 foreach (split /$path_delim/, $ENV{PATH}) { 60 if(-x "$_/$openssl") { 61 $found = 1; 62 $openssl = "$_/$openssl"; 63 last; 64 } 65 } 66 if($found == 0) { 67 print STDERR "c_rehash: rehashing skipped ('openssl' program not available)\n"; 68 exit 0; 69 } 70} 71 72if(@ARGV) { 73 @dirlist = @ARGV; 74} elsif($ENV{SSL_CERT_DIR}) { 75 @dirlist = split /$path_delim/, $ENV{SSL_CERT_DIR}; 76} else { 77 $dirlist[0] = "$dir/certs"; 78} 79 80if (-d $dirlist[0]) { 81 chdir $dirlist[0]; 82 $openssl="$pwd/$openssl" if (!-x $openssl); 83 chdir $pwd; 84} 85 86foreach (@dirlist) { 87 if(-d $_ and -w $_) { 88 hash_dir($_); 89 } 90} 91 92sub hash_dir { 93 my %hashlist; 94 print "Doing $_[0]\n"; 95 chdir $_[0]; 96 opendir(DIR, "."); 97 my @flist = readdir(DIR); 98 closedir DIR; 99 if ( $removelinks ) { 100 # Delete any existing symbolic links 101 foreach (grep {/^[\da-f]+\.r{0,1}\d+$/} @flist) { 102 if(-l $_) { 103 unlink $_; 104 print "unlink $_" if $verbose; 105 } 106 } 107 } 108 FILE: foreach $fname (grep {/\.(pem)|(crt)|(cer)|(crl)$/} @flist) { 109 # Check to see if certificates and/or CRLs present. 110 my ($cert, $crl) = check_file($fname); 111 if(!$cert && !$crl) { 112 print STDERR "WARNING: $fname does not contain a certificate or CRL: skipping\n"; 113 next; 114 } 115 link_hash_cert($fname) if($cert); 116 link_hash_crl($fname) if($crl); 117 } 118} 119 120sub check_file { 121 my ($is_cert, $is_crl) = (0,0); 122 my $fname = $_[0]; 123 open IN, $fname; 124 while(<IN>) { 125 if(/^-----BEGIN (.*)-----/) { 126 my $hdr = $1; 127 if($hdr =~ /^(X509 |TRUSTED |)CERTIFICATE$/) { 128 $is_cert = 1; 129 last if($is_crl); 130 } elsif($hdr eq "X509 CRL") { 131 $is_crl = 1; 132 last if($is_cert); 133 } 134 } 135 } 136 close IN; 137 return ($is_cert, $is_crl); 138} 139 140 141# Link a certificate to its subject name hash value, each hash is of 142# the form <hash>.<n> where n is an integer. If the hash value already exists 143# then we need to up the value of n, unless its a duplicate in which 144# case we skip the link. We check for duplicates by comparing the 145# certificate fingerprints 146 147sub link_hash_cert { 148 my $fname = $_[0]; 149 $fname =~ s/'/'\\''/g; 150 my ($hash, $fprint) = `"$openssl" x509 $x509hash -fingerprint -noout -in "$fname"`; 151 chomp $hash; 152 chomp $fprint; 153 $fprint =~ s/^.*=//; 154 $fprint =~ tr/://d; 155 my $suffix = 0; 156 # Search for an unused hash filename 157 while(exists $hashlist{"$hash.$suffix"}) { 158 # Hash matches: if fingerprint matches its a duplicate cert 159 if($hashlist{"$hash.$suffix"} eq $fprint) { 160 print STDERR "WARNING: Skipping duplicate certificate $fname\n"; 161 return; 162 } 163 $suffix++; 164 } 165 $hash .= ".$suffix"; 166 if ($symlink_exists) { 167 symlink $fname, $hash; 168 print "link $fname -> $hash\n" if $verbose; 169 } else { 170 open IN,"<$fname" or die "can't open $fname for read"; 171 open OUT,">$hash" or die "can't open $hash for write"; 172 print OUT <IN>; # does the job for small text files 173 close OUT; 174 close IN; 175 print "copy $fname -> $hash\n" if $verbose; 176 } 177 $hashlist{$hash} = $fprint; 178} 179 180# Same as above except for a CRL. CRL links are of the form <hash>.r<n> 181 182sub link_hash_crl { 183 my $fname = $_[0]; 184 $fname =~ s/'/'\\''/g; 185 my ($hash, $fprint) = `"$openssl" crl $crlhash -fingerprint -noout -in '$fname'`; 186 chomp $hash; 187 chomp $fprint; 188 $fprint =~ s/^.*=//; 189 $fprint =~ tr/://d; 190 my $suffix = 0; 191 # Search for an unused hash filename 192 while(exists $hashlist{"$hash.r$suffix"}) { 193 # Hash matches: if fingerprint matches its a duplicate cert 194 if($hashlist{"$hash.r$suffix"} eq $fprint) { 195 print STDERR "WARNING: Skipping duplicate CRL $fname\n"; 196 return; 197 } 198 $suffix++; 199 } 200 $hash .= ".r$suffix"; 201 if ($symlink_exists) { 202 symlink $fname, $hash; 203 print "link $fname -> $hash\n" if $verbose; 204 } else { 205 system ("cp", $fname, $hash); 206 print "cp $fname -> $hash\n" if $verbose; 207 } 208 $hashlist{$hash} = $fprint; 209} 210 211