1#!{- $config{HASHBANGPERL} -} 2 3# {- join("\n# ", @autowarntext) -} 4# Copyright 1999-2022 The OpenSSL Project Authors. All Rights Reserved. 5# 6# Licensed under the OpenSSL license (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 14my $dir = {- quotify1($config{openssldir}) -}; 15my $prefix = {- quotify1($config{prefix}) -}; 16 17my $errorcount = 0; 18my $openssl = $ENV{OPENSSL} || "openssl"; 19my $pwd; 20my $x509hash = "-subject_hash"; 21my $crlhash = "-hash"; 22my $verbose = 0; 23my $symlink_exists=eval {symlink("",""); 1}; 24my $removelinks = 1; 25 26## Parse flags. 27while ( $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 46sub 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 54eval "require Cwd"; 55if (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. 63my $path_delim = ($pwd =~ /^[a-z]\:/i) ? ';' : ':'; 64$ENV{PATH} = "$prefix/bin" . ($ENV{PATH} ? $path_delim . $ENV{PATH} : ""); 65 66if (! -x $openssl) { 67 my $found = 0; 68 foreach (split /$path_delim/, $ENV{PATH}) { 69 if (-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 81if (@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 89if (-d $dirlist[0]) { 90 chdir $dirlist[0]; 91 $openssl="$pwd/$openssl" if (!-x $openssl); 92 chdir $pwd; 93} 94 95foreach (@dirlist) { 96 if (-d $_ ) { 97 if ( -w $_) { 98 hash_dir($_); 99 } else { 100 print "Skipping $_, can't write\n"; 101 $errorcount++; 102 } 103 } 104} 105exit($errorcount); 106 107sub 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 123sub 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 160sub 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 181sub 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 } 196 return (<$fh>, <$fh>); 197} 198 199# Link a certificate to its subject name hash value, each hash is of 200# the form <hash>.<n> where n is an integer. If the hash value already exists 201# then we need to up the value of n, unless its a duplicate in which 202# case we skip the link. We check for duplicates by comparing the 203# certificate fingerprints 204 205sub link_hash_cert { 206 link_hash($_[0], 'cert'); 207} 208 209# Same as above except for a CRL. CRL links are of the form <hash>.r<n> 210 211sub link_hash_crl { 212 link_hash($_[0], 'crl'); 213} 214 215sub link_hash { 216 my ($fname, $type) = @_; 217 my $is_cert = $type eq 'cert'; 218 219 my ($hash, $fprint) = compute_hash($openssl, 220 $is_cert ? "x509" : "crl", 221 $is_cert ? $x509hash : $crlhash, 222 "-fingerprint", "-noout", 223 "-in", $fname); 224 chomp $hash; 225 chomp $fprint; 226 return if !$hash; 227 $fprint =~ s/^.*=//; 228 $fprint =~ tr/://d; 229 my $suffix = 0; 230 # Search for an unused hash filename 231 my $crlmark = $is_cert ? "" : "r"; 232 while(exists $hashlist{"$hash.$crlmark$suffix"}) { 233 # Hash matches: if fingerprint matches its a duplicate cert 234 if ($hashlist{"$hash.$crlmark$suffix"} eq $fprint) { 235 my $what = $is_cert ? 'certificate' : 'CRL'; 236 print STDERR "WARNING: Skipping duplicate $what $fname\n"; 237 return; 238 } 239 $suffix++; 240 } 241 $hash .= ".$crlmark$suffix"; 242 if ($symlink_exists) { 243 print "link $fname -> $hash\n" if $verbose; 244 symlink $fname, $hash || warn "Can't symlink, $!"; 245 } else { 246 print "copy $fname -> $hash\n" if $verbose; 247 copy_file($fname, $hash); 248 } 249 $hashlist{$hash} = $fprint; 250}