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