changeset: 6447:babc30377614 user: Kevin McCarthy <[email protected]> date: Fri May 15 10:47:38 2015 -0700 link: http://dev.mutt.org/hg/mutt/rev/babc30377614
Start cleaning up and fixing smime_keys.pl (closes #3324) (see #2456) * Convert to using File::Temp (#3324). This was also suggested at https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=775199 * Use File::Temp for add_p12 temp file. (#2456) * Make the query_label() method a bit more robust with empty strings, ctrl-d, and leading spaces. * Clean up openssl_do_verify() logic. Mark cert as invalid rather that die'ing if an openssl verify command fails. * General cleanup: - Clearly separate op handler, certificate management, and helper functions by section and using prefixes. - Create openssl helper functions to reduce copy/paste invocations and make the code clearer. - Make indentation consistent at 2 spaces. - Change handle_add_pem() to re-use handle_add_chain() once the correct files are identified. - Change openssl_parse_pem() to return a single array of data structures representing the parsed certs/keys. diffs (truncated from 1545 to 950 lines): diff -r c46dfbdb5eff -r babc30377614 smime_keys.pl --- a/smime_keys.pl Sun May 03 16:25:45 2015 -0700 +++ b/smime_keys.pl Fri May 15 10:47:38 2015 -0700 @@ -8,12 +8,12 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. @@ -21,158 +21,129 @@ use strict; use File::Copy; use File::Glob ':glob'; +use File::Temp qw(tempfile tempdir); umask 077; use Time::Local; +# helper routines sub usage (); -sub newfile ($;$$); -sub mutt_Q ($ ); +sub mutt_Q ($); sub mycopy ($$); +sub query_label (); +sub mkdir_recursive ($); +sub verify_files_exist (@); +sub create_tempfile (;$); +sub new_cert_structure (); -# directory setup routines -sub mkdir_recursive ($ ); -sub init_paths (); +# openssl helpers +sub openssl_format ($); +sub openssl_hash ($); +sub openssl_fingerprint ($); +sub openssl_emails ($); +sub openssl_do_verify($$$); +sub openssl_parse_pem ($$); # key/certificate management methods -sub list_certs (); -sub query_label (); -sub add_entry ($$$$$ ); -sub add_certificate ($$$$;$ ); -sub add_key ($$$$); -sub add_root_cert ($ ); -sub parse_pem (@ ); -sub handle_pem (@ ); -sub modify_entry ($$$;$ ); -sub remove_pair ($ ); -sub change_label ($ ); -sub verify_cert($$); -sub do_verify($$$ ); - -# Get the directories mutt uses for certificate/key storage. +sub cm_list_certs (); +sub cm_add_entry ($$$$$); +sub cm_add_certificate ($$$$;$); +sub cm_add_key ($$$$); +sub cm_modify_entry ($$$;$); + +# op handlers +sub handle_init_paths (); +sub handle_change_label ($); +sub handle_add_cert ($); +sub handle_add_pem ($); +sub handle_add_p12 ($); +sub handle_add_chain ($$$); +sub handle_verify_cert($$); +sub handle_remove_pair ($); +sub handle_add_root_cert ($); + my $mutt = $ENV{MUTT_CMDLINE} || 'mutt'; my $opensslbin = "/usr/bin/openssl"; -my @tempfiles = (); -my @cert_tmp_file = (); +my $tmpdir; -my $tmpdir; +# Get the directories mutt uses for certificate/key storage. + my $private_keys_path = mutt_Q 'smime_keys'; die "smime_keys is not set in mutt's configuration file" - if length $private_keys_path == 0; + if length $private_keys_path == 0; my $certificates_path = mutt_Q 'smime_certificates'; die "smime_certificates is not set in mutt's configuration file" - if length $certificates_path == 0; + if length $certificates_path == 0; + my $root_certs_path = mutt_Q 'smime_ca_location'; die "smime_ca_location is not set in mutt's configuration file" - if length $root_certs_path == 0; + if length $root_certs_path == 0; my $root_certs_switch; if ( -d $root_certs_path) { - $root_certs_switch = -CApath; + $root_certs_switch = -CApath; } else { - $root_certs_switch = -CAfile; + $root_certs_switch = -CAfile; } -# +###### # OPS -# +###### if(@ARGV == 1 and $ARGV[0] eq "init") { - init_paths; + handle_init_paths(); } elsif(@ARGV == 1 and $ARGV[0] eq "list") { - list_certs; + cm_list_certs(); } elsif(@ARGV == 2 and $ARGV[0] eq "label") { - change_label($ARGV[1]); + handle_change_label($ARGV[1]); } elsif(@ARGV == 2 and $ARGV[0] eq "add_cert") { - my $format = -B $ARGV[1] ? 'DER' : 'PEM'; - my $cmd = "$opensslbin x509 -noout -hash -in $ARGV[1] -inform $format"; - my $cert_hash = `$cmd`; - $? and die "'$cmd' returned $?"; - chomp($cert_hash); - my $label = query_label; - &add_certificate($ARGV[1], \$cert_hash, 1, $label, '?'); + verify_files_exist($ARGV[1]); + handle_add_cert($ARGV[1]); } elsif(@ARGV == 2 and $ARGV[0] eq "add_pem") { - -e $ARGV[1] and -s $ARGV[1] or die("$ARGV[1] is nonexistent or empty."); - open(PEM_FILE, "<$ARGV[1]") or die("Can't open $ARGV[1]: $!"); - my @pem = <PEM_FILE>; - close(PEM_FILE); - handle_pem(@pem); + verify_files_exist($ARGV[1]); + handle_add_pem($ARGV[1]); } elsif( @ARGV == 2 and $ARGV[0] eq "add_p12") { - -e $ARGV[1] and -s $ARGV[1] or die("$ARGV[1] is nonexistent or empty."); - - print "\nNOTE: This will ask you for two passphrases:\n"; - print " 1. The passphrase you used for exporting\n"; - print " 2. The passphrase you wish to secure your private key with.\n\n"; - - my $pem_file = "$ARGV[1].pem"; - - my $cmd = "$opensslbin pkcs12 -in $ARGV[1] -out $pem_file"; - system $cmd and die "'$cmd' returned $?"; - - -e $pem_file and -s $pem_file or die("Conversion of $ARGV[1] failed."); - open(PEM_FILE, $pem_file) or die("Can't open $pem_file: $!"); - my @pem = <PEM_FILE>; - close(PEM_FILE); - unlink $pem_file; - handle_pem(@pem); + verify_files_exist($ARGV[1]); + handle_add_p12($ARGV[1]); } elsif(@ARGV == 4 and $ARGV[0] eq "add_chain") { - my $mailbox; - my $format = -B $ARGV[2] ? 'DER' : 'PEM'; - my $cmd = "$opensslbin x509 -noout -hash -in $ARGV[2] -inform $format"; - my $cert_hash = `$cmd`; - - $? and die "'$cmd' returned $?"; - - $format = -B $ARGV[3] ? 'DER' : 'PEM'; - - $cmd = "$opensslbin x509 -noout -hash -in $ARGV[3] -inform $format"; - my $issuer_hash = `$cmd`; - $? and die "'$cmd' returned $?"; - - chomp($cert_hash); - chomp($issuer_hash); - - my $label = query_label; - - add_certificate($ARGV[3], \$issuer_hash, 0, $label); - my @mailbox = &add_certificate($ARGV[2], \$cert_hash, 1, $label, $issuer_hash); - - foreach $mailbox (@mailbox) { - chomp($mailbox); - add_key($ARGV[1], $cert_hash, $mailbox, $label); - } + verify_files_exist($ARGV[1], $ARGV[2], $ARGV[3]); + handle_add_chain($ARGV[1], $ARGV[2], $ARGV[3]); } elsif((@ARGV == 2 or @ARGV == 3) and $ARGV[0] eq "verify") { - verify_cert($ARGV[1], $ARGV[2]); + verify_files_exist($ARGV[2]) if (@ARGV == 3); + handle_verify_cert($ARGV[1], $ARGV[2]); } elsif(@ARGV == 2 and $ARGV[0] eq "remove") { - remove_pair($ARGV[1]); + handle_remove_pair($ARGV[1]); } elsif(@ARGV == 2 and $ARGV[0] eq "add_root") { - add_root_cert($ARGV[1]); + verify_files_exist($ARGV[1]); + handle_add_root_cert($ARGV[1]); } -else { - usage; - exit(1); +else { + usage(); + exit(1); } exit(0); +############## sub-routines ######################## - -############## sub-routines ######################## +################### +# helper routines +################### sub usage () { print <<EOF; @@ -207,87 +178,336 @@ } sub mutt_Q ($) { - my $var = shift or die; + my ($var) = @_; - my $cmd = "$mutt -v >/dev/null 2>/dev/null"; - system ($cmd) == 0 - or die<<EOF; + my $cmd = "$mutt -v >/dev/null 2>/dev/null"; + system ($cmd) == 0 or die<<EOF; Couldn't launch mutt. I attempted to do so by running the command "$mutt". -If that's not the right command, you can override it by setting the +If that's not the right command, you can override it by setting the environment variable \$MUTT_CMDLINE EOF - $cmd = "$mutt -Q $var 2>/dev/null"; - my $answer = `$cmd`; + $cmd = "$mutt -Q $var 2>/dev/null"; + my $answer = `$cmd`; - $? and die<<EOF; -Couldn't look up the value of the mutt variable "$var". + $? and die<<EOF; +Couldn't look up the value of the mutt variable "$var". You must set this in your mutt config file. See contrib/smime.rc for an example. EOF -#' - $answer =~ /\"(.*?)\"/ and return bsd_glob($1, GLOB_TILDE | GLOB_NOCHECK); - - $answer =~ /^Mutt (.*?) / and die<<EOF; + $answer =~ /\"(.*?)\"/ and return bsd_glob($1, GLOB_TILDE | GLOB_NOCHECK); + + $answer =~ /^Mutt (.*?) / and die<<EOF; This script requires mutt 1.5.0 or later. You are using mutt $1. EOF - - die "Value of $var is weird\n"; + + die "Value of $var is weird\n"; } sub mycopy ($$) { - my $source = shift or die; - my $dest = shift or die; + my ($source, $dest) = @_; - copy $source, $dest or die "Problem copying $source to $dest: $!\n"; + copy $source, $dest or die "Problem copying $source to $dest: $!\n"; } -# -# directory setup routines -# +sub query_label () { + my $input; + my $label; + my $junk; + print "\nYou may assign a label to this key, so you don't have to remember\n"; + print "the key ID. This has to be _one_ word (no whitespaces).\n\n"; + + print "Enter label: "; + $input = <STDIN>; + + if (defined($input) && ($input !~ /^\s*$/)) { + chomp($input); + $input =~ s/^\s+//; + ($label, $junk) = split(/\s/, $input, 2); + + if (defined($junk)) { + print "\nUsing '$label' as label; ignoring '$junk'\n"; + } + } + + if ((! defined($label)) || ($label =~ /^\s*$/)) { + $label = "-"; + } + + return $label; +} sub mkdir_recursive ($) { - my $path = shift or die; - my $tmp_path; - - for my $dir (split /\//, $path) { - $tmp_path .= "$dir/"; + my ($path) = @_; + my $tmp_path; - -d $tmp_path - or mkdir $tmp_path, 0700 - or die "Can't mkdir $tmp_path: $!"; - } + for my $dir (split /\//, $path) { + $tmp_path .= "$dir/"; + + -d $tmp_path + or mkdir $tmp_path, 0700 + or die "Can't mkdir $tmp_path: $!"; + } } -sub init_paths () { - mkdir_recursive($certificates_path); - mkdir_recursive($private_keys_path); +sub verify_files_exist (@) { + my (@files) = @_; - my $file; - - $file = $certificates_path . "/.index"; - -f $file or open(TMP_FILE, ">$file") and close(TMP_FILE) - or die "Can't touch $file: $!"; - - $file = $private_keys_path . "/.index"; - -f $file or open(TMP_FILE, ">$file") and close(TMP_FILE) - or die "Can't touch $file: $!"; + foreach my $file (@files) { + if ((! -e $file) || (! -s $file)) { + die("$file is nonexistent or empty."); + } + } } +# Returns a list ($fh, $filename) +sub create_tempfile (;$) { + my ($directory) = @_; + if (! defined($directory)) { + if (! defined($tmpdir)) { + $tmpdir = tempdir(CLEANUP => 1); + } + $directory = $tmpdir; + } -# + return tempfile(DIR => $directory); +} + +# Creates a cert data structure used by openssl_parse_pem +sub new_cert_structure () { + my $cert_data = {}; + + $cert_data->{datafile} = ""; + $cert_data->{type} = ""; + $cert_data->{localKeyID} = ""; + $cert_data->{subject} = ""; + $cert_data->{issuer} = ""; + + return $cert_data; +} + + +################## +# openssl helpers +################## + +sub openssl_format ($) { + my ($filename) = @_; + + return -B $filename ? 'DER' : 'PEM'; +} + +sub openssl_hash ($) { + my ($filename) = @_; + + my $format = openssl_format($filename); + my $cmd = "$opensslbin x509 -noout -hash -in $filename -inform $format"; + my $cert_hash = `$cmd`; + $? and die "'$cmd' returned $?"; + + chomp($cert_hash); + return $cert_hash; +} + +sub openssl_fingerprint ($) { + my ($filename) = @_; + + my $format = openssl_format($filename); + my $cmd = "$opensslbin x509 -in $filename -inform $format -fingerprint -noout"; + my $fingerprint = `$cmd`; + $? and die "'$cmd' returned $?"; + + chomp($fingerprint); + return $fingerprint; +} + +sub openssl_emails ($) { + my ($filename) = @_; + + my $format = openssl_format($filename); + my $cmd = "$opensslbin x509 -in $filename -inform $format -email -noout"; + my @mailboxes = `$cmd`; + $? and die "'$cmd' returned $?"; + + chomp(@mailboxes); + return @mailboxes; +} + +sub openssl_do_verify ($$$) { + my ($cert, $issuerid, $crl) = @_; + + my $result = 't'; + my $issuer_path; + my $cert_path = "$certificates_path/$cert"; + + if($issuerid eq '?') { + $issuer_path = "$certificates_path/$cert"; + } else { + $issuer_path = "$certificates_path/$issuerid"; + } + + my $cmd = "$opensslbin verify $root_certs_switch $root_certs_path -purpose smimesign -purpose smimeencrypt -untrusted $issuer_path $cert_path"; + my $output = `$cmd`; + chomp $output; + if ($?) { + print "'$cmd' returned exit code " . ($? >> 8) . " with output:\n"; + print "$output\n\n"; + print "Marking as invalid\n"; + return 'i'; + } + print "\n$output\n"; + + if ($output !~ /OK/) { + return 'i'; + } + + my $format = openssl_format($cert_path); + $cmd = "$opensslbin x509 -dates -serial -noout -in $cert_path -inform $format"; + (my $not_before, my $not_after, my $serial_in) = `$cmd`; + $? and die "'$cmd' returned $?"; + + if ( defined $not_before and defined $not_after ) { + my %months = ('Jan', '00', 'Feb', '01', 'Mar', '02', 'Apr', '03', + 'May', '04', 'Jun', '05', 'Jul', '06', 'Aug', '07', + 'Sep', '08', 'Oct', '09', 'Nov', '10', 'Dec', '11'); + + my @tmp = split (/\=/, $not_before); + my $not_before_date = $tmp[1]; + my @fields = + $not_before_date =~ /(\w+)\s*(\d+)\s*(\d+):(\d+):(\d+)\s*(\d+)\s*GMT/; + if ($#fields == 5) { + if (timegm($fields[4], $fields[3], $fields[2], $fields[1], + $months{$fields[0]}, $fields[5]) > time) { + print "Certificate is not yet valid.\n"; + return 'e'; + } + } else { + print "Expiration Date: Parse Error : $not_before_date\n\n"; + } + + @tmp = split (/\=/, $not_after); + my $not_after_date = $tmp[1]; + @fields = + $not_after_date =~ /(\w+)\s*(\d+)\s*(\d+):(\d+):(\d+)\s*(\d+)\s*GMT/; + if ($#fields == 5) { + if (timegm($fields[4], $fields[3], $fields[2], $fields[1], + $months{$fields[0]}, $fields[5]) < time) { + print "Certificate has expired.\n"; + return 'e'; + } + } else { + print "Expiration Date: Parse Error : $not_after_date\n\n"; + } + } + + if ( defined $crl ) { + my @serial = split (/\=/, $serial_in); + my $cmd = "$opensslbin crl -text -noout -in $crl | grep -A1 $serial[1]"; + (my $l1, my $l2) = `$cmd`; + $? and die "'$cmd' returned $?"; + + if ( defined $l2 ) { + my @revoke_date = split (/:\s/, $l2); + print "FAILURE: Certificate $cert has been revoked on $revoke_date[1]\n"; + $result = 'r'; + } + } + print "\n"; + + return $result; +} + +sub openssl_parse_pem ($$) { + my ($filename, $attrs_required) = @_; + + my $state = 0; + my $cert_data; + my @certs; + my $cert_count = 0; + my $bag_count = 0; + my $cert_tmp_fh; + my $cert_tmp_filename; + + $cert_data = new_cert_structure(); + ($cert_tmp_fh, $cert_data->{datafile}) = create_tempfile(); + + open(PEM_FILE, "<$filename") or die("Can't open $filename: $!"); + while(<PEM_FILE>) { + if(/^Bag Attributes/) { + $bag_count++; + $state == 0 or die("PEM-parse error at: $."); + $state = 1; + } + + if ($state == 1) { + if (/localKeyID:\s*(.*)/) { + $cert_data->{localKeyID} = $1; + } + + if (/subject=\s*(.*)/) { + $cert_data->{subject} = $1; + } + + if (/issuer=\s*(.*)/) { + $cert_data->{issuer} = $1; + } + } + + + if(/^-----/) { + if(/BEGIN/) { + print $cert_tmp_fh $_; + $state = 2; + + if(/PRIVATE/) { + $cert_data->{type} = "K"; + next; + } + if(/CERTIFICATE/) { + $cert_data->{type} = "C"; + next; + } + die("What's this: $_"); + } + if(/END/) { + $state = 0; + print $cert_tmp_fh $_; + close($cert_tmp_fh); + + $cert_count++; + push (@certs, $cert_data); + + $cert_data = new_cert_structure(); + ($cert_tmp_fh, $cert_data->{datafile}) = create_tempfile(); + next; + } + } + print $cert_tmp_fh $_; + } + close($cert_tmp_fh); + close(PEM_FILE); + + if ($attrs_required && ($bag_count != $cert_count)) { + die("Not all contents were bagged. can't continue."); + } + + return @certs; +} + + +################################# # certificate management methods -# +################################# -sub list_certs () { +sub cm_list_certs () { my %keyflags = ( 'i', '(Invalid)', 'r', '(Revoked)', 'e', '(Expired)', - 'u', '(Unverified)', 'v', '(Valid)', 't', '(Trusted)'); + 'u', '(Unverified)', 'v', '(Valid)', 't', '(Trusted)'); - open(INDEX, "<$certificates_path/.index") or + open(INDEX, "<$certificates_path/.index") or die "Couldn't open $certificates_path/.index: $!"; - + print "\n"; while(<INDEX>) { my $tmp; @@ -308,7 +528,7 @@ die "Couldn't open $certfile: $!"; local $/; $cert = <F>; - close F; + close F; } my $subject_in; @@ -316,7 +536,7 @@ my $date1_in; my $date2_in; - my $format = -B $certfile ? 'DER' : 'PEM'; + my $format = openssl_format($certfile); my $cmd = "$opensslbin x509 -subject -issuer -dates -noout -in $certfile -inform $format"; ($subject_in, $issuer_in, $date1_in, $date2_in) = `$cmd`; $? and print "ERROR: '$cmd' returned $?\n\n" and next; @@ -345,13 +565,13 @@ $tmp = $tmp[1]; @tmp = split (/\=/, $date2_in); print $tab."Certificate is not valid before $tmp". - $tab." or after ".$tmp[1]; + $tab." or after ".$tmp[1]; } -e "$private_keys_path/$fields[1]" and print "$tab - Matching private key installed -\n"; - $format = -B "$certificates_path/$fields[1]" ? 'DER' : 'PEM'; + $format = openssl_format("$certificates_path/$fields[1]"); $cmd = "$opensslbin x509 -purpose -noout -in $certfile -inform $format"; my $purpose_in = `$cmd`; $? and die "'$cmd' returned $?"; @@ -367,406 +587,335 @@ print "\n"; } - + close(INDEX); } +sub cm_add_entry ($$$$$) { + my ($mailbox, $hashvalue, $use_cert, $label, $issuer_hash) = @_; + my @fields; -sub query_label () { - my @words; - my $input; + if ($use_cert) { + open(INDEX, "+<$certificates_path/.index") or + die "Couldn't open $certificates_path/.index: $!"; + } + else { + open(INDEX, "+<$private_keys_path/.index") or + die "Couldn't open $private_keys_path/.index: $!"; + } - print "\nYou may assign a label to this key, so you don't have to remember\n"; - print "the key ID. This has to be _one_ word (no whitespaces).\n\n"; + while(<INDEX>) { + @fields = split; + return if ($fields[0] eq $mailbox && $fields[1] eq $hashvalue); + } - print "Enter label: "; - chomp($input = <STDIN>); + if ($use_cert) { + print INDEX "$mailbox $hashvalue $label $issuer_hash u\n"; + } + else { + print INDEX "$mailbox $hashvalue $label \n"; + } - my ($label, $junk) = split(/\s/, $input, 2); - - defined $junk - and print "\nUsing '$label' as label; ignoring '$junk'\n"; - - defined $label || ($label = "-"); - - return $label; + close(INDEX); } +sub cm_add_certificate ($$$$;$) { + my ($filename, $hashvalue, $add_to_index, $label, $issuer_hash) = @_; + my $iter = 0; + my @mailboxes; -sub add_entry ($$$$$) { - my $mailbox = shift or die; - my $hashvalue = shift or die; - my $use_cert = shift; - my $label = shift or die; - my $issuer_hash = shift; + my $fp1 = openssl_fingerprint($filename); - my @fields; + while (-e "$certificates_path/$$hashvalue.$iter") { + my $fp2 = openssl_fingerprint("$certificates_path/$$hashvalue.$iter"); - if ($use_cert) { - open(INDEX, "+<$certificates_path/.index") or - die "Couldn't open $certificates_path/.index: $!"; + last if $fp1 eq $fp2; + $iter++; + } + $$hashvalue .= ".$iter"; + + if (-e "$certificates_path/$$hashvalue") { + print "\nCertificate: $certificates_path/$$hashvalue already installed.\n"; + } + else { + mycopy $filename, "$certificates_path/$$hashvalue"; + + if ($add_to_index) { + @mailboxes = openssl_emails($filename); + foreach my $mailbox (@mailboxes) { + cm_add_entry($mailbox, $$hashvalue, 1, $label, $issuer_hash); + print "\ncertificate $$hashvalue ($label) for $mailbox added.\n"; + } + cm_modify_entry('V', $$hashvalue, 1); } else { - open(INDEX, "+<$private_keys_path/.index") or - die "Couldn't open $private_keys_path/.index: $!"; + print "added certificate: $certificates_path/$$hashvalue.\n"; } + } - while(<INDEX>) { - @fields = split; - return if ($fields[0] eq $mailbox && $fields[1] eq $hashvalue); - } - - if ($use_cert) { - print INDEX "$mailbox $hashvalue $label $issuer_hash u\n"; - } - else { - print INDEX "$mailbox $hashvalue $label \n"; - } - - close(INDEX); + return @mailboxes; } - -sub add_certificate ($$$$;$) { - my $filename = shift or die; - my $hashvalue = shift or die; - my $add_to_index = shift; - my $label = shift or die; - my $issuer_hash = shift; - - my $iter = 0; - my @mailbox; - my $mailbox; - - while(-e "$certificates_path/$$hashvalue.$iter") { - my ($t1, $t2); - my $format = -B $filename ? 'DER' : 'PEM'; - my $cmd = "$opensslbin x509 -in $filename -inform $format -fingerprint -noout"; - $t1 = `$cmd`; - $? and die "'$cmd' returned $?"; - - $format = -B "$certificates_path/$$hashvalue.$iter" ? 'DER' : 'PEM'; - $cmd = "$opensslbin x509 -in $certificates_path/$$hashvalue.$iter -inform $format -fingerprint -noout"; - $t2 = `$cmd`; - $? and die "'$cmd' returned $?"; - - $t1 eq $t2 and last; - - $iter++; - } - $$hashvalue .= ".$iter"; - - if (-e "$certificates_path/$$hashvalue") { - print "\nCertificate: $certificates_path/$$hashvalue already installed.\n"; - } - else { - mycopy $filename, "$certificates_path/$$hashvalue"; - - if ($add_to_index) { - my $format = -B $filename ? 'DER' : 'PEM'; - my $cmd = "$opensslbin x509 -in $filename -inform $format -email -noout"; - @mailbox = `$cmd`; - $? and die "'$cmd' returned $?"; - - foreach $mailbox (@mailbox) { - chomp($mailbox); - add_entry($mailbox, $$hashvalue, 1, $label, $issuer_hash); - - print "\ncertificate $$hashvalue ($label) for $mailbox added.\n"; - } - verify_cert($$hashvalue, undef); - } - else { - print "added certificate: $certificates_path/$$hashvalue.\n"; - } - } - - return @mailbox; -} - - -sub add_key ($$$$) { - my $file = shift or die; - my $hashvalue = shift or die; - my $mailbox = shift or die; - my $label = shift or die; +sub cm_add_key ($$$$) { + my ($file, $hashvalue, $mailbox, $label) = @_; unless (-e "$private_keys_path/$hashvalue") { mycopy $file, "$private_keys_path/$hashvalue"; - } + } - add_entry($mailbox, $hashvalue, 0, $label, ""); + cm_add_entry($mailbox, $hashvalue, 0, $label, ""); print "added private key: " . "$private_keys_path/$hashvalue for $mailbox\n"; -} - - - - - - -sub parse_pem (@) { - my $state = 0; - my $cert_iter = 0; - my @bag_attribs; - my $numBags = 0; - - $cert_tmp_file[$cert_iter] = newfile("cert_tmp.$cert_iter","temp"); - my $cert_tmp_iter = $cert_tmp_file[$cert_iter]; - open(CERT_FILE, ">$cert_tmp_iter") - or die "Couldn't open $cert_tmp_iter: $!"; - - while($_ = shift(@_)) { - if(/^Bag Attributes/) { - $numBags++; - $state == 0 or die("PEM-parse error at: $."); - $state = 1; - $bag_attribs[$cert_iter*4+1] = ""; - $bag_attribs[$cert_iter*4+2] = ""; - $bag_attribs[$cert_iter*4+3] = ""; - } - - ($state == 1) and /localKeyID:\s*(.*)/ - and ($bag_attribs[$cert_iter*4+1] = $1); - - ($state == 1) and /subject=\s*(.*)/ - and ($bag_attribs[$cert_iter*4+2] = $1); - - ($state == 1) and /issuer=\s*(.*)/ - and ($bag_attribs[$cert_iter*4+3] = $1); - - if(/^-----/) { - if(/BEGIN/) { - print CERT_FILE; - $state = 2; - - if(/PRIVATE/) { - $bag_attribs[$cert_iter*4] = "K"; - next; - } - if(/CERTIFICATE/) { - $bag_attribs[$cert_iter*4] = "C"; - next; - } - die("What's this: $_"); - } - if(/END/) { - $state = 0; - print CERT_FILE; - close(CERT_FILE); - $cert_iter++; - $cert_tmp_file[$cert_iter] = newfile("cert_tmp.$cert_iter","temp"); - $cert_tmp_iter = $cert_tmp_file[$cert_iter]; - open(CERT_FILE, ">$cert_tmp_iter") - or die "Couldn't open $cert_tmp_iter: $!"; - next; - } - } - print CERT_FILE; - } - close(CERT_FILE); - - # I'll add support for unbagged cetificates, in case this is needed. - $numBags == $cert_iter or - die("Not all contents were bagged. can't continue."); - - return @bag_attribs; } +sub cm_modify_entry ($$$;$) { + my ($op, $hashvalue, $use_cert, $opt_param) = @_; -# This requires the Bag Attributes to be set -sub handle_pem (@) { + my $crl; + my $label; + my $path; + my @fields; - my @pem_contents; - my $iter=0; - my $root_cert; - my $key; - my $certificate; - my $intermediate; - my @mailbox; - my $mailbox; + $op eq 'L' and ($label = $opt_param); + $op eq 'V' and ($crl = $opt_param);
