c_rehash.in 5.0 KB
Newer Older
1 2 3 4 5 6
#!/usr/local/bin/perl

# Perl c_rehash script, scan all files in a directory
# and add symbolic links to their hash values.

my $dir;
D
Dr. Stephen Henson 已提交
7
my $prefix;
8

9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
my $openssl = $ENV{OPENSSL} || "openssl";
my $pwd;
my $x509hash = "-subject_hash";
my $crlhash = "-hash";
my $verbose = 0;
my $symlink_exists=eval {symlink("",""); 1};
my $removelinks = 1;

##  Parse flags.
while ( $ARGV[0] =~ '-.*' ) {
    my $flag = shift @ARGV;
    last if ( $flag eq '--');
    if ( $flag =~ /-old/) {
	    $x509hash = "-subject_hash_old";
	    $crlhash = "-hash_old";
    } elsif ( $flag =~ /-h/) {
	    help();
    } elsif ( $flag eq '-n' ) {
	    $removelinks = 0;
    } elsif ( $flag eq '-v' ) {
	    $verbose++;
    }
    else {
	    print STDERR "Usage error; try -help.\n";
	    exit 1;
    }
}

sub help {
	print "Usage: c_rehash [-old] [-h] [-v] [dirs...]\n";
	print "   -old use old-style digest\n";
	print "   -h print this help text\n";
	print "   -v print files removed and linked\n";
	exit 0;
43 44
}

45 46 47 48
eval "require Cwd";
if (defined(&Cwd::getcwd)) {
	$pwd=Cwd::getcwd();
} else {
49 50
	$pwd=`pwd`;
	chomp($pwd);
51
}
52

53 54 55
# DOS/Win32 or Unix delimiter?  Prefix our installdir, then search.
my $path_delim = ($pwd =~ /^[a-z]\:/i) ? ';' : ':';
$ENV{PATH} = "$prefix/bin" . ($ENV{PATH} ? $path_delim . $ENV{PATH} : "");
56

57
if(! -x $openssl) {
58
	my $found = 0;
59
	foreach (split /$path_delim/, $ENV{PATH}) {
60
		if(-x "$_/$openssl") {
61
			$found = 1;
62
			$openssl = "$_/$openssl";
63 64 65 66 67 68 69 70 71 72 73 74
			last;
		}	
	}
	if($found == 0) {
		print STDERR "c_rehash: rehashing skipped ('openssl' program not available)\n";
		exit 0;
	}
}

if(@ARGV) {
	@dirlist = @ARGV;
} elsif($ENV{SSL_CERT_DIR}) {
75
	@dirlist = split /$path_delim/, $ENV{SSL_CERT_DIR};
76 77 78 79
} else {
	$dirlist[0] = "$dir/certs";
}

80 81 82 83 84
if (-d $dirlist[0]) {
	chdir $dirlist[0];
	$openssl="$pwd/$openssl" if (!-x $openssl);
	chdir $pwd;
}
85 86 87 88 89 90 91 92 93 94 95

foreach (@dirlist) {
	if(-d $_ and -w $_) {
		hash_dir($_);
	}
}

sub hash_dir {
	my %hashlist;
	print "Doing $_[0]\n";
	chdir $_[0];
R
Rich Salz 已提交
96 97 98
	opendir(DIR, ".");
	my @flist = readdir(DIR);
	closedir DIR;
99 100 101 102 103 104 105
	if ( $removelinks ) {
		# Delete any existing symbolic links
		foreach (grep {/^[\da-f]+\.r{0,1}\d+$/} @flist) {
			if(-l $_) {
				unlink $_;
				print "unlink $_" if $verbose;
			}
106 107
		}
	}
108
	FILE: foreach $fname (grep {/\.(pem)|(crt)|(cer)|(crl)$/} @flist) {
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
		# Check to see if certificates and/or CRLs present.
		my ($cert, $crl) = check_file($fname);
		if(!$cert && !$crl) {
			print STDERR "WARNING: $fname does not contain a certificate or CRL: skipping\n";
			next;
		}
		link_hash_cert($fname) if($cert);
		link_hash_crl($fname) if($crl);
	}
}

sub check_file {
	my ($is_cert, $is_crl) = (0,0);
	my $fname = $_[0];
	open IN, $fname;
	while(<IN>) {
		if(/^-----BEGIN (.*)-----/) {
			my $hdr = $1;
			if($hdr =~ /^(X509 |TRUSTED |)CERTIFICATE$/) {
				$is_cert = 1;
				last if($is_crl);
			} elsif($hdr eq "X509 CRL") {
				$is_crl = 1;
				last if($is_cert);
			}
		}
	}
	close IN;
	return ($is_cert, $is_crl);
}


# Link a certificate to its subject name hash value, each hash is of
# the form <hash>.<n> where n is an integer. If the hash value already exists
# then we need to up the value of n, unless its a duplicate in which
# case we skip the link. We check for duplicates by comparing the
# certificate fingerprints

sub link_hash_cert {
		my $fname = $_[0];
149
		$fname =~ s/'/'\\''/g;
150
		my ($hash, $fprint) = `"$openssl" x509 $x509hash -fingerprint -noout -in "$fname"`;
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
		chomp $hash;
		chomp $fprint;
		$fprint =~ s/^.*=//;
		$fprint =~ tr/://d;
		my $suffix = 0;
		# Search for an unused hash filename
		while(exists $hashlist{"$hash.$suffix"}) {
			# Hash matches: if fingerprint matches its a duplicate cert
			if($hashlist{"$hash.$suffix"} eq $fprint) {
				print STDERR "WARNING: Skipping duplicate certificate $fname\n";
				return;
			}
			$suffix++;
		}
		$hash .= ".$suffix";
166 167
		if ($symlink_exists) {
			symlink $fname, $hash;
168
			print "link $fname -> $hash\n" if $verbose;
169
		} else {
170 171 172 173 174
			open IN,"<$fname" or die "can't open $fname for read";
			open OUT,">$hash" or die "can't open $hash for write";
			print OUT <IN>;	# does the job for small text files
			close OUT;
			close IN;
175
			print "copy $fname -> $hash\n" if $verbose;
176
		}
177 178 179 180 181 182 183
		$hashlist{$hash} = $fprint;
}

# Same as above except for a CRL. CRL links are of the form <hash>.r<n>

sub link_hash_crl {
		my $fname = $_[0];
184
		$fname =~ s/'/'\\''/g;
185
		my ($hash, $fprint) = `"$openssl" crl $crlhash -fingerprint -noout -in '$fname'`;
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
		chomp $hash;
		chomp $fprint;
		$fprint =~ s/^.*=//;
		$fprint =~ tr/://d;
		my $suffix = 0;
		# Search for an unused hash filename
		while(exists $hashlist{"$hash.r$suffix"}) {
			# Hash matches: if fingerprint matches its a duplicate cert
			if($hashlist{"$hash.r$suffix"} eq $fprint) {
				print STDERR "WARNING: Skipping duplicate CRL $fname\n";
				return;
			}
			$suffix++;
		}
		$hash .= ".r$suffix";
201 202
		if ($symlink_exists) {
			symlink $fname, $hash;
203
			print "link $fname -> $hash\n" if $verbose;
204 205
		} else {
			system ("cp", $fname, $hash);
206
			print "cp $fname -> $hash\n" if $verbose;
207
		}
208 209 210
		$hashlist{$hash} = $fprint;
}