#! /usr/bin/perl use Getopt::Long; # # for every CA, a symbolic abbrev should be defined, and in the CWD # there should be a self-signed CERT named "cacert-${caname}.pem" # if the CRL URL is correct, it will be retreived automatically, # otherwise, put the file by hand in this directory as "cacrl-${caname}.pem". # the hash is compuled automatically and symlinks to cert and crl made # accordingly. The "hash.0" and "hash.r0" MUST be symlinks. # # The "auth" entry in the hash is compulsory: it should be a list # of DN prefixes signed by that CA, inclusing any wildcards. # # The "cert" entry is NOT used: it would be catastrophic to retreive # a CA cert insecurely and at random. Use of the "cert" field is # to be forbidden for automatic updates! # # if the "dis" entry is defined, the CA will not be added to the # ca-signing-policy.conf file. The textual value of the "dis" entry # will be echoed to the screen. # # by default, the script will wait up to 10 minutes to run, to even # out the load on CA web servers. You can force it to run NOW # by giving the "--now" option. You will want this in case you # initialize the whole thing (or blew your setup :-) # # setup of paths $openssl="/usr/local/bin/openssl"; -f "$openssl" or $openssl="/global/ices/toolset/ibin/openssl"; -f "$openssl" or $openssl="openssl"; # just use default path $polname="ca-signing-policy.conf"; $httpget="/usr/local/bin/lynx -dump"; -f "/usr/local/bin/lynx" or $httpget="lynx -dump"; $opt_c="certs.cf"; # parse options @optdef=qw( d|certdir=s reloadcerts=s@ c|cf|config=s now help); $0 =~ s/.*\///; $Getopt::Long::ignorecase=0; &GetOptions(@optdef); $opt_help and do { print "Usage: $0 [--config ca_setup_file] [--reloadcerts=symname] \n"; print " [--now] [--certdir directory]\n"; print "\n"; print " Update the certificates directory for Globus, retrieving new\n"; print " CRLs from the poriginal repositories and verify the integrety\n"; print " of the CA certificates.\n"; print "\n"; print " --config specify the configuration file to use\n"; print " --certdir target directory that holds cacerts and policy\n"; print " --reloadcerts reload the CA certs from the location\n"; print " specified in the configuration file\n"; print " NOTE: verify manually, possible security hole!\n"; print " --now disable the (small) load-levelling wait\n"; print "\n"; exit(0); }; # # # The list of CA's, in perl syntax as a hash of hashes: # -f $opt_c or die "Cannot find configuration $opt_c\n"; $config=`cat $opt_c`; $rc=eval $config; ( "$@" eq "" ) or die "Invalid configuration file $opt_c: $@"; # check target directory # $opt_d and do { -d "$opt_d" or die "Target directory $opt_d does not exist\n"; chdir "$opt_d" or die "Cannot cd to $opt_d: $!\n"; }; # implement the random wait $opt_now or do { $sleeptime=60*rand(10); sleep($sleeptime); }; # immediate output from now onwards $|=1; # # optionally reload some certs first ( $#opt_reloadcerts >=0 ) and do { print ">>>> Reloading certs requested for @opt_reloadcerts\n"; foreach $id ( @opt_reloadcerts ) { $calist{$id} or die "Reload of $id failed: ID not defined\n"; $caref=$calist{$id}; &cacert_load($id,$$caref{cert},$$caref{cert_filter}); } }; while ( ($caname,$caref)=each %calist ) { $$caref{cert} or next; if (! &cacert_correct($caname,$$caref{cert},$$caref{cert_filter}) ) { $$caref{dis} = "cacert inconsistent with source URL"; } } while ( ($caname,$caref)=each %calist ) { $cacertname="cacert-$caname.pem"; $cacrlname="cacrl-$caname.pem"; $cahash=$$caref{hash}; $dn=`$openssl x509 -noout -subject -in $cacertname`; $dn=~s/^subject=\s*//; chop($dn); print ">>>> $dn\n"; grep { /Certificate has expired/ and do { $$caref{dis}="Cert has expired"; warn "!!!! CERT for $caname has expired\n" }; } `$openssl verify $cacertname`; if ( -f $cacrlname ) { $status=`$openssl crl -noout -CAfile $cacertname -in $cacrlname 2>&1`; chop($status); $status eq "verify OK" or do { warn "!!! CRL status: $status\n"; }; $$caref{crl} and &update_crl($caname,$$caref{crl},$$caref{crl_filter}); } else { warn "---- CRL not found for $caname\n"; $$caref{crl} and &update_crl($caname,$$caref{crl},$$caref{crl_filter}); } } # # # verify directory integrety and compute hashes while ( ($caname,$caref)=each %calist ) { $cacertname="cacert-$caname.pem"; $cacrlname="cacrl-$caname.pem"; -f $cacertname or do { warn "Certificate for $caname not found in $cacertname\n"; next; }; $cahash=`$openssl x509 -noout -hash -in $cacertname 2>/dev/null`; chop($cahash); length($cahash) == 8 or die "cacert for $cacertname seems invalid (no hash)\n"; $$caref{hash}=$cahash; print ">>>> CA $cahash - $caname\n"; ( -f "$cahash.0" and ! -s "$cahash.0" ) and die "Hash reference to CACERT $caname is not symlink\n"; ( -f "$cahash.r0" and ! -s "$cahash.r0" ) and die "Hash reference to CRL $caname is not symlink\n"; unlink "$cahash.0","$cahash.r0"; -f "$cacertname" and symlink "$cacertname","$cahash.0"; -f "$cacrlname" and symlink "$cacrlname","$cahash.r0"; } print "\n\n>>>> Writing ca-signing-policy ...\n"; open POL,">$polname.temp" or die "Cannot open $polname.temp: $!\n"; print POL "# ca-signing-policy.conf\n#\n"; print POL "# generated by get_certificates on ".`date`; print POL "#\n#\n"; while ( ($caname,$caref)=each %calist ) { $cacertname="cacert-$caname.pem"; $cacrlname="cacrl-$caname.pem"; $cahash=$$caref{hash}; $catauth=$$caref{auth}; @caauth=@$catauth; $$caref{dis} and do { warn "!!!! skiping $caname ($$caref{dis})\n"; next; }; print STDERR ">>>> $caname: @caauth\n"; $dn=`$openssl x509 -noout -subject -in $cacertname`; $dn=~s/^subject=\s*//; chop($dn); print POL "# EACL for $cahash (symname: $caname)\n"; print POL " access_id_CA X509 '$dn'\n"; print POL " pos_rights globus CA:sign\n"; print POL " cond_subjects globus '"; print POL "\"$dn\" "; foreach $domain ( @caauth ) { print POL "\"$domain\" "; } print POL "'\n\n"; } close POL; unlink "$polname.old"; rename "$polname","$polname.old"; rename "$polname.temp","$polname"; 0; sub update_crl { my ($caname,$crlurl,$crlfilter) = @_; my ($cacertname,$cacrlname); my ($tmp,@tmpcrl); $tmp=&tmpname("crl-$caname"); $cacertname="cacert-$caname.pem"; $cacrlname="cacrl-$caname.pem"; unlink $tmp; system("$httpget '$crlurl' $crlfilter > $tmp"); $status=`$openssl crl -noout -CAfile $cacertname -in $tmp 2>&1`; chop($status); $status eq "verify OK" or do { print STDERR "!!!! reloaded CRL for $caname failed verify.\n"; print STDERR "!!!! URL: $crlurl gave\n"; map { print STDERR "$_" } `cat $tmp`; unlink $tmp; return -1; }; unlink "$cacrlname.old"; rename "$cacrlname","$cacrlname.old"; open CRL,">$cacrlname" or die "Cannot re-open crl for $caname\n"; map { print CRL "$_" } `cat $tmp`; close CRL; unlink $tmp; $nextupd=`$openssl crl -noout -CAfile $cacertname -nextupdate -in $cacrlname 2>/dev/null`; chop($nextupd); print "**** Reload $nextupd\n"; } sub cacert_correct { my ($caname,$certurl,$certfilter) = @_; my ($cacertname,$cacrlname); my ($tmp,@tmpcert); $tmp=&tmpname("cert-check-$caname"); $cacertname="cacert-$caname.pem"; unlink $tmp; system("$httpget '$certurl' $certfilter > $tmp"); $newfp=`$openssl x509 -noout -fingerprint -in $tmp 2>&1`; $currentfp=`$openssl x509 -noout -fingerprint -in $cacertname 2>&1`; unlink $tmp; if ( "$newfp" eq "$currentfp" ) { return 1; } else { print STDERR "!!!! reloaded CA CERT for $caname has different fingerprint\n"; print STDERR "!!!! current: $currentfp"; print STDERR "!!!! new : $newfp"; print STDERR "!!!! CAs URL: $certurl\n"; return 0; } } sub cacert_load { my ($caname,$certurl,$certfilter) = @_; my ($cacertname,$cacrlname); my ($tmp,@tmpcert); $tmp=&tmpname("cert-load-$caname"); $cacertname="cacert-$caname.pem"; unlink $tmp; system("$httpget '$certurl' $certfilter > $tmp"); $newfp=`$openssl x509 -noout -fingerprint -in $tmp 2>&1`; $newhash=`$openssl x509 -noout -hash -in $tmp 2>/dev/null`; print ">>>> reloaded CA CERT for $caname:\n"; print ">>>> CAs URL : $certurl\n"; print ">>>> new fingerprint : $newfp"; print ">>>> new hash : $newhash"; unlink "$cacertname.old"; rename "$cacertname","$cacertname.old"; rename "$tmp","$cacertname"; return 0; } sub tmpname { my ($name)=@_; my ($rnd,$r); do { $rnd=sprintf "%d",rand 65536; if ( $name ) { $r="/tmp/cert-temp$$-$rnd-$name"; } else { $r="/tmp/cert-temp$$-$rnd"; } } while ( -f $r ); return $r; }