On 2013-02-19 at 15:37 +0100, Stephen Henson via RT wrote: > The hash calculation (use by -CApath) changed from OpenSSL 0.9.8 to 1.0.0 and > later. The two are not compatible. So you need to recreate hash links using > OpenSSL 1.0.0.
I have systems where I need to use both the old and the new versions of OpenSSL, so I use the attached version of c_rehash, which might help Sushil Sharma. I've added a detached PGP signature too. An example use-case: MacOS, where the system ships with 0.9.8 but I have 1.0.1e from MacPorts, for various work I do. I can't break system tools, I also need my stuff to work. So this version detects if OpenSSL is "not version 0.something" and if so, uses -subject_hash_old and creates both sets of symlinks. A second example use-case: centrally managed certificate stores on disk, which need to be used on machines with varying versions of OpenSSL available. -Phil
#!/usr/bin/env perl use warnings; use strict; # Perl c_rehash script, scan all files in a directory # and add symbolic links to their hash values. my $openssl; ## Define this if you have a default certs dir on your system and it's not the ## "certs/" sub-dir of where OpenSSL is installed. my $OVERRIDE_DEFAULT_CERT_DIR = undef; #my $OVERRIDE_DEFAULT_CERT_DIR = '/etc/ssl/certs'; my $dir = "/usr/local/openssl"; my $prefix = "/usr/local"; if (exists $ENV{OPENSSL}) { $openssl = $ENV{OPENSSL}; } else { $openssl = "openssl"; ## MacOS ships with pre-1 OpenSSL, and putting other versions ahead ## in $PATH might break system tools; we explicitly want to grab the ## MacPorts variant, without requiring $OPENSSL to be set in environ. if ( -x "/opt/local/bin/openssl" and $^O eq "darwin" ) { $openssl = "/opt/local/bin/openssl" } $ENV{OPENSSL} = $openssl; } if ((not exists $ENV{'SSL_CERT_DIR'}) and (defined $OVERRIDE_DEFAULT_CERT_DIR)) { $ENV{'SSL_CERT_DIR'} = $OVERRIDE_DEFAULT_CERT_DIR; } my $pwd; eval "require Cwd"; if (defined(&Cwd::getcwd)) { $pwd=Cwd::getcwd(); } else { $pwd=`pwd`; chomp($pwd); } my $path_delim = ($pwd =~ /^[a-z]\:/i) ? ';' : ':'; # DOS/Win32 or Unix delimiter? $ENV{PATH} = "$prefix/bin" . ($ENV{PATH} ? $path_delim . $ENV{PATH} : ""); # prefix our path if(! -x $openssl) { my $found = 0; foreach (split /$path_delim/, $ENV{PATH}) { if(-x "$_/$openssl") { $found = 1; $openssl = "$_/$openssl"; last; } } if($found == 0) { print STDERR "c_rehash: rehashing skipped ('openssl' program not available)\n"; exit 0; } } my $have_openssl_1 = 0; my $openssl_version; if (`$openssl version` =~ /^OpenSSL\s+(\d\S+)/i) { $openssl_version = $1; unless ($openssl_version =~ /^0\./) { $have_openssl_1 = 1; } } my @dirlist; if(@ARGV) { @dirlist = @ARGV; } elsif($ENV{SSL_CERT_DIR}) { @dirlist = split /$path_delim/, $ENV{SSL_CERT_DIR}; } else { $dirlist[0] = "$dir/certs"; } if (-d $dirlist[0]) { chdir $dirlist[0]; $openssl="$pwd/$openssl" if (!-x $openssl); chdir $pwd; } # Might vary per FS; but define before we call funcs which rely upon it. my $SYMLINK_EXISTS = eval {symlink("",""); 1}; foreach (@dirlist) { if(-d $_ and -w $_) { hash_dir($_); } } sub hash_dir { my %hashlist; print "Doing $_[0]\n"; chdir $_[0]; opendir(DIR, "."); my @flist = readdir(DIR); # Delete any existing symbolic links foreach (grep {/^[\da-f]+\.r{0,1}\d+$/} @flist) { if(-l $_) { unlink $_; } } closedir DIR; FILE: foreach my $fname (grep {/\.pem$/} @flist) { # 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, \%hashlist) if($cert); link_hash_crl($fname, \%hashlist) 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 # GlobNIX: note that OpenSSL changed hash algorithm in 1.0.0 but we need to # support both apps linked against the system >=1.0.0 OpenSSL and those with # their own library. sub link_hash_cert { my $fname = $_[0]; my $hashlist = $_[1]; $fname =~ s/'/'\\''/g; my $x509_args = "-subject_hash -subject_hash_old -fingerprint -noout -in"; my ($newhash, $oldhash, $fprint) = (undef, undef, undef); unless ($have_openssl_1) { $x509_args = "-subject_hash -fingerprint -noout -in"; } my @x509_res = `"$openssl" x509 $x509_args "$fname"`; if ($have_openssl_1) { ($newhash, $oldhash, $fprint) = @x509_res; } else { ($newhash, $fprint) = @x509_res; } chomp $newhash; chomp $oldhash if defined $oldhash; chomp $fprint; $fprint =~ s/^.*=//; $fprint =~ tr/://d; # Search for an unused hash filename foreach my $hash ($newhash, $oldhash) { next unless defined $hash; my $suffix = 0; 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"; print "$fname => $hash\n"; if ($SYMLINK_EXISTS) { symlink $fname, $hash; } else { 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; } $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]; my $hashlist = $_[1]; $fname =~ s/'/'\\''/g; my ($hash, $fprint) = `"$openssl" crl -hash -fingerprint -noout -in '$fname'`; 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"; print "$fname => $hash\n"; if ($SYMLINK_EXISTS) { symlink $fname, $hash; } else { system ("cp", $fname, $hash); } $hashlist->{$hash} = $fprint; }
-----BEGIN PGP SIGNATURE----- iEYEABEDAAYFAlEjvD8ACgkQQDBDFTkDY3/BjgCglODysqrnresoIDH06zqooTKz DL8AoIOocyK8Jx2uspwHCk4ST0zvYBwu =D+Nb -----END PGP SIGNATURE-----