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