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