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