1#!/usr/bin/env perl 2 3# WARNING: do not edit! 4# Generated by Makefile from tools/c_rehash.in 5# Copyright 1999-2025 The OpenSSL Project Authors. All Rights Reserved. 6# 7# Licensed under the Apache License 2.0 (the "License"). You may not use 8# this file except in compliance with the License. You can obtain a copy 9# in the file LICENSE in the source distribution or at 10# https://www.openssl.org/source/license.html 11 12# Perl c_rehash script, scan all files in a directory 13# and add symbolic links to their hash values. 14 15my $dir = "etc"; 16my $prefix = "/usr"; 17 18my $errorcount = 0; 19my $openssl = $ENV{OPENSSL} || "openssl"; 20my $pwd; 21my $x509hash = "-subject_hash"; 22my $crlhash = "-hash"; 23my $verbose = 0; 24my $symlink_exists=eval {symlink("",""); 1}; 25my $removelinks = 1; 26 27## Parse flags. 28while ( $ARGV[0] =~ /^-/ ) { 29 my $flag = shift @ARGV; 30 last if ( $flag eq '--'); 31 if ( $flag eq '-old') { 32 $x509hash = "-subject_hash_old"; 33 $crlhash = "-hash_old"; 34 } elsif ( $flag eq '-h' || $flag eq '-help' ) { 35 help(); 36 } elsif ( $flag eq '-n' ) { 37 $removelinks = 0; 38 } elsif ( $flag eq '-v' ) { 39 $verbose++; 40 } 41 else { 42 print STDERR "Usage error; try -h.\n"; 43 exit 1; 44 } 45} 46 47sub help { 48 print "Usage: c_rehash [-old] [-h] [-help] [-v] [dirs...]\n"; 49 print " -old use old-style digest\n"; 50 print " -h or -help print this help text\n"; 51 print " -v print files removed and linked\n"; 52 exit 0; 53} 54 55eval "require Cwd"; 56if (defined(&Cwd::getcwd)) { 57 $pwd=Cwd::getcwd(); 58} else { 59 $pwd=`pwd`; 60 chomp($pwd); 61} 62 63# DOS/Win32 or Unix delimiter? Prefix our installdir, then search. 64my $path_delim = ($pwd =~ /^[a-z]\:/i) ? ';' : ':'; 65$ENV{PATH} = "$prefix/bin" . ($ENV{PATH} ? $path_delim . $ENV{PATH} : ""); 66 67if (!(-f $openssl && -x $openssl)) { 68 my $found = 0; 69 foreach (split /$path_delim/, $ENV{PATH}) { 70 if (-f "$_/$openssl" && -x "$_/$openssl") { 71 $found = 1; 72 $openssl = "$_/$openssl"; 73 last; 74 } 75 } 76 if ($found == 0) { 77 print STDERR "c_rehash: rehashing skipped ('openssl' program not available)\n"; 78 exit 0; 79 } 80} 81 82if (@ARGV) { 83 @dirlist = @ARGV; 84} elsif ($ENV{SSL_CERT_DIR}) { 85 @dirlist = split /$path_delim/, $ENV{SSL_CERT_DIR}; 86} else { 87 $dirlist[0] = "$dir/certs"; 88} 89 90if (-d $dirlist[0]) { 91 chdir $dirlist[0]; 92 $openssl="$pwd/$openssl" if (!(-f $openssl && -x $openssl)); 93 chdir $pwd; 94} 95 96foreach (@dirlist) { 97 if (-d $_ ) { 98 if ( -w $_) { 99 hash_dir($_); 100 } else { 101 print "Skipping $_, can't write\n"; 102 $errorcount++; 103 } 104 } 105} 106exit($errorcount); 107 108sub copy_file { 109 my ($src_fname, $dst_fname) = @_; 110 111 if (open(my $in, "<", $src_fname)) { 112 if (open(my $out, ">", $dst_fname)) { 113 print $out $_ while (<$in>); 114 close $out; 115 } else { 116 warn "Cannot open $dst_fname for write, $!"; 117 } 118 close $in; 119 } else { 120 warn "Cannot open $src_fname for read, $!"; 121 } 122} 123 124sub hash_dir { 125 my $dir = shift; 126 my %hashlist; 127 128 print "Doing $dir\n"; 129 130 if (!chdir $dir) { 131 print STDERR "WARNING: Cannot chdir to '$dir', $!\n"; 132 return; 133 } 134 135 opendir(DIR, ".") || print STDERR "WARNING: Cannot opendir '.', $!\n"; 136 my @flist = sort readdir(DIR); 137 closedir DIR; 138 if ( $removelinks ) { 139 # Delete any existing symbolic links 140 foreach (grep {/^[\da-f]+\.r{0,1}\d+$/} @flist) { 141 if (-l $_) { 142 print "unlink $_\n" if $verbose; 143 unlink $_ || warn "Can't unlink $_, $!\n"; 144 } 145 } 146 } 147 FILE: foreach $fname (grep {/\.(pem|crt|cer|crl)$/} @flist) { 148 # Check to see if certificates and/or CRLs present. 149 my ($cert, $crl) = check_file($fname); 150 if (!$cert && !$crl) { 151 print STDERR "WARNING: $fname does not contain a certificate or CRL: skipping\n"; 152 next; 153 } 154 link_hash_cert($fname) if ($cert); 155 link_hash_crl($fname) if ($crl); 156 } 157 158 chdir $pwd; 159} 160 161sub check_file { 162 my ($is_cert, $is_crl) = (0,0); 163 my $fname = $_[0]; 164 165 open(my $in, "<", $fname); 166 while(<$in>) { 167 if (/^-----BEGIN (.*)-----/) { 168 my $hdr = $1; 169 if ($hdr =~ /^(X509 |TRUSTED |)CERTIFICATE$/) { 170 $is_cert = 1; 171 last if ($is_crl); 172 } elsif ($hdr eq "X509 CRL") { 173 $is_crl = 1; 174 last if ($is_cert); 175 } 176 } 177 } 178 close $in; 179 return ($is_cert, $is_crl); 180} 181 182sub compute_hash { 183 my $fh; 184 if ( $^O eq "VMS" ) { 185 # VMS uses the open through shell 186 # The file names are safe there and list form is unsupported 187 if (!open($fh, "-|", join(' ', @_))) { 188 print STDERR "Cannot compute hash on '$fname'\n"; 189 return; 190 } 191 } else { 192 if (!open($fh, "-|", @_)) { 193 print STDERR "Cannot compute hash on '$fname'\n"; 194 return; 195 } 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 206sub 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 212sub link_hash_crl { 213 link_hash($_[0], 'crl'); 214} 215 216sub 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