1 #!{- $config{HASHBANGPERL} -} 2 {- use OpenSSL::Util; -} 3 # {- join("\n# ", @autowarntext) -} 4 # Copyright 1999-2025 The OpenSSL Project Authors. All Rights Reserved. 5 # 6 # Licensed under the Apache License 2.0 (the "License"). You may not use 7 # this file except in compliance with the License. You can obtain a copy 8 # in the file LICENSE in the source distribution or at 9 # https://www.openssl.org/source/license.html 10 11 # Perl c_rehash script, scan all files in a directory 12 # and add symbolic links to their hash values. 13 14 my $dir = {- quotify1($config{openssldir}) -}; 15 my $prefix = {- quotify1($config{prefix}) -}; 16 17 my $errorcount = 0; 18 my $openssl = $ENV{OPENSSL} || "openssl"; 19 my $pwd; 20 my $x509hash = "-subject_hash"; 21 my $crlhash = "-hash"; 22 my $verbose = 0; 23 my $symlink_exists=eval {symlink("",""); 1}; 24 my $removelinks = 1; 25 26 ## Parse flags. 27 while ( $ARGV[0] =~ /^-/ ) { 28 my $flag = shift @ARGV; 29 last if ( $flag eq '--'); 30 if ( $flag eq '-old') { 31 $x509hash = "-subject_hash_old"; 32 $crlhash = "-hash_old"; 33 } elsif ( $flag eq '-h' || $flag eq '-help' ) { 34 help(); 35 } elsif ( $flag eq '-n' ) { 36 $removelinks = 0; 37 } elsif ( $flag eq '-v' ) { 38 $verbose++; 39 } 40 else { 41 print STDERR "Usage error; try -h.\n"; 42 exit 1; 43 } 44 } 45 46 sub help { 47 print "Usage: c_rehash [-old] [-h] [-help] [-v] [dirs...]\n"; 48 print " -old use old-style digest\n"; 49 print " -h or -help print this help text\n"; 50 print " -v print files removed and linked\n"; 51 exit 0; 52 } 53 54 eval "require Cwd"; 55 if (defined(&Cwd::getcwd)) { 56 $pwd=Cwd::getcwd(); 57 } else { 58 $pwd=`pwd`; 59 chomp($pwd); 60 } 61 62 # DOS/Win32 or Unix delimiter? Prefix our installdir, then search. 63 my $path_delim = ($pwd =~ /^[a-z]\:/i) ? ';' : ':'; 64 $ENV{PATH} = "$prefix/bin" . ($ENV{PATH} ? $path_delim . $ENV{PATH} : ""); 65 66 if (!(-f $openssl && -x $openssl)) { 67 my $found = 0; 68 foreach (split /$path_delim/, $ENV{PATH}) { 69 if (-f "$_/$openssl" && -x "$_/$openssl") { 70 $found = 1; 71 $openssl = "$_/$openssl"; 72 last; 73 } 74 } 75 if ($found == 0) { 76 print STDERR "c_rehash: rehashing skipped ('openssl' program not available)\n"; 77 exit 0; 78 } 79 } 80 81 if (@ARGV) { 82 @dirlist = @ARGV; 83 } elsif ($ENV{SSL_CERT_DIR}) { 84 @dirlist = split /$path_delim/, $ENV{SSL_CERT_DIR}; 85 } else { 86 $dirlist[0] = "$dir/certs"; 87 } 88 89 if (-d $dirlist[0]) { 90 chdir $dirlist[0]; 91 $openssl="$pwd/$openssl" if (!(-f $openssl && -x $openssl)); 92 chdir $pwd; 93 } 94 95 foreach (@dirlist) { 96 if (-d $_ ) { 97 if ( -w $_) { 98 hash_dir($_); 99 } else { 100 print "Skipping $_, can't write\n"; 101 $errorcount++; 102 } 103 } 104 } 105 exit($errorcount); 106 107 sub copy_file { 108 my ($src_fname, $dst_fname) = @_; 109 110 if (open(my $in, "<", $src_fname)) { 111 if (open(my $out, ">", $dst_fname)) { 112 print $out $_ while (<$in>); 113 close $out; 114 } else { 115 warn "Cannot open $dst_fname for write, $!"; 116 } 117 close $in; 118 } else { 119 warn "Cannot open $src_fname for read, $!"; 120 } 121 } 122 123 sub hash_dir { 124 my $dir = shift; 125 my %hashlist; 126 127 print "Doing $dir\n"; 128 129 if (!chdir $dir) { 130 print STDERR "WARNING: Cannot chdir to '$dir', $!\n"; 131 return; 132 } 133 134 opendir(DIR, ".") || print STDERR "WARNING: Cannot opendir '.', $!\n"; 135 my @flist = sort readdir(DIR); 136 closedir DIR; 137 if ( $removelinks ) { 138 # Delete any existing symbolic links 139 foreach (grep {/^[\da-f]+\.r{0,1}\d+$/} @flist) { 140 if (-l $_) { 141 print "unlink $_\n" if $verbose; 142 unlink $_ || warn "Can't unlink $_, $!\n"; 143 } 144 } 145 } 146 FILE: foreach $fname (grep {/\.(pem|crt|cer|crl)$/} @flist) { 147 # Check to see if certificates and/or CRLs present. 148 my ($cert, $crl) = check_file($fname); 149 if (!$cert && !$crl) { 150 print STDERR "WARNING: $fname does not contain a certificate or CRL: skipping\n"; 151 next; 152 } 153 link_hash_cert($fname) if ($cert); 154 link_hash_crl($fname) if ($crl); 155 } 156 157 chdir $pwd; 158 } 159 160 sub check_file { 161 my ($is_cert, $is_crl) = (0,0); 162 my $fname = $_[0]; 163 164 open(my $in, "<", $fname); 165 while(<$in>) { 166 if (/^-----BEGIN (.*)-----/) { 167 my $hdr = $1; 168 if ($hdr =~ /^(X509 |TRUSTED |)CERTIFICATE$/) { 169 $is_cert = 1; 170 last if ($is_crl); 171 } elsif ($hdr eq "X509 CRL") { 172 $is_crl = 1; 173 last if ($is_cert); 174 } 175 } 176 } 177 close $in; 178 return ($is_cert, $is_crl); 179 } 180 181 sub compute_hash { 182 my $fh; 183 if ( $^O eq "VMS" ) { 184 # VMS uses the open through shell 185 # The file names are safe there and list form is unsupported 186 if (!open($fh, "-|", join(' ', @_))) { 187 print STDERR "Cannot compute hash on '$fname'\n"; 188 return; 189 } 190 } else { 191 if (!open($fh, "-|", @_)) { 192 print STDERR "Cannot compute hash on '$fname'\n"; 193 return; 194 } 195 binmode($fh, ":crlf"); 196 } 197 return (<$fh>, <$fh>); 198 } 199 200 # Link a certificate to its subject name hash value, each hash is of 201 # the form <hash>.<n> where n is an integer. If the hash value already exists 202 # then we need to up the value of n, unless its a duplicate in which 203 # case we skip the link. We check for duplicates by comparing the 204 # certificate fingerprints 205 206 sub link_hash_cert { 207 link_hash($_[0], 'cert'); 208 } 209 210 # Same as above except for a CRL. CRL links are of the form <hash>.r<n> 211 212 sub link_hash_crl { 213 link_hash($_[0], 'crl'); 214 } 215 216 sub link_hash { 217 my ($fname, $type) = @_; 218 my $is_cert = $type eq 'cert'; 219 220 my ($hash, $fprint) = compute_hash($openssl, 221 $is_cert ? "x509" : "crl", 222 $is_cert ? $x509hash : $crlhash, 223 "-fingerprint", "-noout", 224 "-in", $fname); 225 chomp $hash; 226 $hash =~ s/^.*=// if !$is_cert; 227 chomp $fprint; 228 return if !$hash; 229 $fprint =~ s/^.*=//; 230 $fprint =~ tr/://d; 231 my $suffix = 0; 232 # Search for an unused hash filename 233 my $crlmark = $is_cert ? "" : "r"; 234 while(exists $hashlist{"$hash.$crlmark$suffix"}) { 235 # Hash matches: if fingerprint matches its a duplicate cert 236 if ($hashlist{"$hash.$crlmark$suffix"} eq $fprint) { 237 my $what = $is_cert ? 'certificate' : 'CRL'; 238 print STDERR "WARNING: Skipping duplicate $what $fname\n"; 239 return; 240 } 241 $suffix++; 242 } 243 $hash .= ".$crlmark$suffix"; 244 if ($symlink_exists) { 245 print "link $fname -> $hash\n" if $verbose; 246 symlink $fname, $hash || warn "Can't symlink, $!"; 247 } else { 248 print "copy $fname -> $hash\n" if $verbose; 249 copy_file($fname, $hash); 250 } 251 $hashlist{$hash} = $fprint; 252 } 253