diff --git a/CHANGES b/CHANGES index ab8f8531fa..bb2e353d59 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,12 @@ Changes between 0.9.5a and 0.9.6 [xx XXX 2000] + *) Enhance c_rehash script. Old version would mishandle certificates + with the same subject name hash and wouldn't handle CRLs at all. + Added -fingerprint option to crl utility, to support new c_rehash + features. + [Steve Henson] + *) Eliminate non-ANSI declarations in crypto.h and stack.h. [Ulf Möller] diff --git a/Configure b/Configure index 1de6c3fb10..3e86c05a87 100755 --- a/Configure +++ b/Configure @@ -890,12 +890,13 @@ EOF ### (system 'make depend') == 0 or exit $? if $depflags ne ""; # Run "make depend" manually if you want to be able to delete # the source code files of ciphers you left out. - &dofile("tools/c_rehash",$openssldir,'^DIR=', 'DIR=%s',); if ( $perl =~ m@^/@) { + &dofile("tools/c_rehash",$perl,'^#!/', '#!%s','^my \$dir;$', 'my $dir = "' . $openssldir . '";'); &dofile("apps/der_chop",$perl,'^#!/', '#!%s'); &dofile("apps/CA.pl",$perl,'^#!/', '#!%s'); } else { # No path for Perl known ... + &dofile("tools/c_rehash",'/usr/local/bin/perl','^#!/', '#!%s','^my \$dir;$', 'my $dir = "' . $openssldir . '";'); &dofile("apps/der_chop",'/usr/local/bin/perl','^#!/', '#!%s'); &dofile("apps/CA.pl",'/usr/local/bin/perl','^#!/', '#!%s'); } diff --git a/Makefile.org b/Makefile.org index 5ca1493925..cf98fb515e 100644 --- a/Makefile.org +++ b/Makefile.org @@ -262,7 +262,7 @@ dclean: rehash: rehash.time rehash.time: certs - @(OPENSSL="`pwd`/apps/openssl"; export OPENSSL; sh tools/c_rehash certs) + @(OPENSSL="`pwd`/apps/openssl"; export OPENSSL; $(PERL) tools/c_rehash certs) touch rehash.time test: tests diff --git a/apps/Makefile.ssl b/apps/Makefile.ssl index fef670db23..0d0cd888b4 100644 --- a/apps/Makefile.ssl +++ b/apps/Makefile.ssl @@ -135,7 +135,7 @@ $(DLIBCRYPTO): $(PROGRAM): progs.h $(E_OBJ) $(PROGRAM).o $(DLIBCRYPTO) $(DLIBSSL) $(RM) $(PROGRAM) $(CC) -o $(PROGRAM) $(CFLAGS) $(PROGRAM).o $(E_OBJ) $(PEX_LIBS) $(LIBSSL) $(LIBCRYPTO) $(EX_LIBS) - @(cd ..; OPENSSL="`pwd`/apps/openssl"; export OPENSSL; sh tools/c_rehash certs) + @(cd ..; OPENSSL="`pwd`/apps/openssl"; export OPENSSL; $(PERL) tools/c_rehash certs) progs.h: progs.pl $(PERL) progs.pl $(E_EXE) >progs.h diff --git a/apps/crl.c b/apps/crl.c index 338f46d97c..b1c3325f21 100644 --- a/apps/crl.c +++ b/apps/crl.c @@ -104,6 +104,7 @@ int MAIN(int argc, char **argv) int informat,outformat; char *infile=NULL,*outfile=NULL; int hash=0,issuer=0,lastupdate=0,nextupdate=0,noout=0,text=0; + int fingerprint = 0; char **pp,buf[256]; X509_STORE *store = NULL; X509_STORE_CTX ctx; @@ -111,6 +112,7 @@ int MAIN(int argc, char **argv) X509_OBJECT xobj; EVP_PKEY *pkey; int do_ver = 0; + const EVP_MD *md_alg,*digest=EVP_md5(); apps_startup(); @@ -183,6 +185,13 @@ int MAIN(int argc, char **argv) nextupdate= ++num; else if (strcmp(*argv,"-noout") == 0) noout= ++num; + else if (strcmp(*argv,"-fingerprint") == 0) + fingerprint= ++num; + else if ((md_alg=EVP_get_digestbyname(*argv + 1))) + { + /* ok */ + digest=md_alg; + } else { BIO_printf(bio_err,"unknown option %s\n",*argv); @@ -274,6 +283,26 @@ bad: BIO_printf(bio_out,"NONE"); BIO_printf(bio_out,"\n"); } + if (fingerprint == i) + { + int j; + unsigned int n; + unsigned char md[EVP_MAX_MD_SIZE]; + + if (!X509_CRL_digest(x,digest,md,&n)) + { + BIO_printf(bio_err,"out of memory\n"); + goto end; + } + BIO_printf(bio_out,"%s Fingerprint=", + OBJ_nid2sn(EVP_MD_type(digest))); + for (j=0; j<(int)n; j++) + { + BIO_printf(bio_out,"%02X%c",md[j], + (j+1 == (int)n) + ?'\n':':'); + } + } } } diff --git a/crypto/x509/x509.h b/crypto/x509/x509.h index d2ad77815e..a0aaad8366 100644 --- a/crypto/x509/x509.h +++ b/crypto/x509/x509.h @@ -660,6 +660,8 @@ int X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const EVP_MD *md); int NETSCAPE_SPKI_sign(NETSCAPE_SPKI *x, EVP_PKEY *pkey, const EVP_MD *md); int X509_digest(X509 *data,const EVP_MD *type,unsigned char *md,unsigned int *len); +int X509_CRL_digest(X509_CRL *data,const EVP_MD *type,unsigned char *md,unsigned int *len); +int X509_REQ_digest(X509_REQ *data,const EVP_MD *type,unsigned char *md,unsigned int *len); int X509_NAME_digest(X509_NAME *data,const EVP_MD *type, unsigned char *md,unsigned int *len); #endif diff --git a/crypto/x509/x_all.c b/crypto/x509/x_all.c index d2bf3c8e1c..b67ca243dc 100644 --- a/crypto/x509/x_all.c +++ b/crypto/x509/x_all.c @@ -417,6 +417,18 @@ int X509_digest(X509 *data, const EVP_MD *type, unsigned char *md, return(ASN1_digest((int (*)())i2d_X509,type,(char *)data,md,len)); } +int X509_CRL_digest(X509_CRL *data, const EVP_MD *type, unsigned char *md, + unsigned int *len) + { + return(ASN1_digest((int (*)())i2d_X509_CRL,type,(char *)data,md,len)); + } + +int X509_REQ_digest(X509_REQ *data, const EVP_MD *type, unsigned char *md, + unsigned int *len) + { + return(ASN1_digest((int (*)())i2d_X509_REQ,type,(char *)data,md,len)); + } + int X509_NAME_digest(X509_NAME *data, const EVP_MD *type, unsigned char *md, unsigned int *len) { diff --git a/tools/c_rehash.in b/tools/c_rehash.in index cc3b65871f..baec7c14ff 100644 --- a/tools/c_rehash.in +++ b/tools/c_rehash.in @@ -1,61 +1,148 @@ -#!/bin/sh -# -# redo the hashes for the certificates in your cert path or the ones passed -# on the command line. -# +#!/usr/local/bin/perl -if [ "$OPENSSL"x = "x" -o ! -x "$OPENSSL" ]; then - OPENSSL='openssl' - export OPENSSL -fi -DIR=/usr/local/ssl -PATH=$DIR/bin:$PATH -if [ ! -f "$OPENSSL" ]; then - found=0 - for dir in . `echo $PATH | sed -e 's/:/ /g'`; do - if [ -f "$dir/$OPENSSL" ]; then - found=1 - break - fi - done - if [ $found = 0 ]; then - echo "c_rehash: rehashing skipped ('openssl' program not available)" 1>&2 - exit 0 - fi -fi +# Perl c_rehash script, scan all files in a directory +# and add symbolic links to their hash values. -SSL_DIR=$DIR/certs +my $openssl; -if [ "$*" = "" ]; then - CERTS=${*:-${SSL_CERT_DIR:-$SSL_DIR}} -else - CERTS=$* -fi +my $dir; + +if(defined $ENV{OPENSSL}) { + $openssl = $ENV{OPENSSL}; +} else { + $openssl = "openssl"; + $ENV{OPENSSL} = $openssl; +} + +$ENV{PATH} .= ":$dir/bin"; + +if(! -f $openssl) { + my $found = 0; + foreach (split /:/, $ENV{PATH}) { + if(-f "$_/$openssl") { + $found = 1; + 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}) { + @dirlist = split /:/, $ENV{SSL_CERT_DIR}; +} else { + $dirlist[0] = "$dir/certs"; +} + + +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 $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) 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() { + 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 . 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]; + my ($hash, $fprint) = `$openssl x509 -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.$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"; + symlink $fname, $hash; + $hashlist{$hash} = $fprint; +} + +# Same as above except for a CRL. CRL links are of the form .r + +sub link_hash_crl { + my $fname = $_[0]; + 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"; + symlink $fname, $hash; + $hashlist{$hash} = $fprint; +} -IFS=': ' -for i in $CERTS -do - ( - IFS=' ' - if [ -d $i -a -w $i ]; then - cd $i - echo "Doing $i" - for i in *.pem - do - if [ $i != '*.pem' ]; then - h=`$OPENSSL x509 -hash -noout -in $i` - if [ "x$h" = "x" ]; then - echo $i does not contain a certificate - else - if [ -f $h.0 ]; then - /bin/rm -f $h.0 - fi - echo "$i => $h.0" - ln -s $i $h.0 - fi - fi - done - fi - ) -done