home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / bin / smime_keys < prev    next >
Encoding:
Text File  |  2011-12-19  |  26.6 KB  |  1,014 lines

  1. #! /usr/bin/perl -w
  2.  
  3. # Copyright (C) 2001,2002 Oliver Ehli <elmy@acm.org>
  4. # Copyright (C) 2001 Mike Schiraldi <raldi@research.netsol.com>
  5. # Copyright (C) 2003 Bjoern Jacke <bjoern@j3e.de>
  6. #
  7. #     This program is free software; you can redistribute it and/or modify
  8. #     it under the terms of the GNU General Public License as published by
  9. #     the Free Software Foundation; either version 2 of the License, or
  10. #     (at your option) any later version.
  11. #     This program is distributed in the hope that it will be useful,
  12. #     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. #     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. #     GNU General Public License for more details.
  15. #     You should have received a copy of the GNU General Public License
  16. #     along with this program; if not, write to the Free Software
  17. #     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  18.  
  19. use strict;
  20. use File::Copy;
  21. use File::Glob ':glob';
  22.  
  23. umask 077;
  24.  
  25. require "timelocal.pl";
  26.  
  27. sub usage ();
  28. sub newfile ($;$$);
  29. sub mutt_Q ($ );
  30. sub mycopy ($$);
  31.  
  32. #  directory setup routines
  33. sub mkdir_recursive ($ );
  34. sub init_paths ();
  35.  
  36. # key/certificate management methods
  37. sub list_certs ();
  38. sub query_label ();
  39. sub add_entry ($$$$$ );
  40. sub add_certificate ($$$$;$ );
  41. sub add_key ($$$$);
  42. sub add_root_cert ($ );
  43. sub parse_pem (@ );
  44. sub handle_pem (@ );
  45. sub modify_entry ($$$;$ );
  46. sub remove_pair ($ );
  47. sub change_label ($ );
  48. sub verify_cert($$);
  49. sub do_verify($$$ );
  50.               
  51. # Get the directories mutt uses for certificate/key storage.
  52.  
  53. my $mutt = $ENV{MUTT_CMDLINE} || 'mutt';
  54. my $opensslbin = "/usr/bin/openssl";
  55. my @tempfiles = ();
  56. my @cert_tmp_file = ();
  57.  
  58. my $tmpdir;
  59. my $private_keys_path = mutt_Q 'smime_keys';
  60. die "smime_keys is not set in mutt's configuration file"
  61.     if length $private_keys_path == 0;
  62.  
  63. my $certificates_path = mutt_Q 'smime_certificates';
  64. die "smime_certificates is not set in mutt's configuration file"
  65.     if length $certificates_path == 0;
  66. my $root_certs_path   = mutt_Q 'smime_ca_location';
  67. die "smime_ca_location is not set in mutt's configuration file"
  68.     if length $root_certs_path == 0;
  69.  
  70. my $root_certs_switch;
  71. if ( -d $root_certs_path) {
  72.     $root_certs_switch = -CApath;
  73. } else {
  74.     $root_certs_switch = -CAfile;
  75. }
  76.  
  77.  
  78. #
  79. # OPS
  80. #
  81.  
  82.  
  83. sub get_certs {
  84.     my $file = shift;
  85.     return undef unless (defined($file) && -e $file);
  86.  
  87.     open IN, "<$file";
  88.  
  89.     my @certs = ();
  90.     my $in_cert = 0;
  91.     my $cert = q{};
  92.     while ( <IN> ) {
  93.         $in_cert = 1 if ( /^-----BEGIN CERTIFICATE-----$/ );
  94.         $cert .= $_;
  95.  
  96.         if ( /^-----END CERTIFICATE-----$/ )  {
  97.             push @certs, $cert;
  98.             $cert = q{};
  99.             $in_cert = 0;
  100.         }
  101.     }
  102.  
  103.     return @certs;
  104. }
  105.  
  106. if(@ARGV == 1 and $ARGV[0] eq "init") {
  107.     init_paths;
  108. }
  109. elsif(@ARGV == 1 and $ARGV[0] eq "list") {
  110.     list_certs;
  111. }
  112. elsif(@ARGV == 2 and $ARGV[0] eq "label") {
  113.     change_label($ARGV[1]);
  114. }
  115. elsif(@ARGV == 2 and $ARGV[0] eq "add_cert") {
  116.     foreach my $cert ( get_certs( $ARGV[1] ) ) {
  117.  
  118.         my $file = sprintf( '/tmp/smime-%d.%d', $$, int(rand( 999999 ) ) );
  119.         print STDERR "TMPFILE: $file\n";
  120.         if ( -e $file ) {
  121.             die( "ERROR: TMPFILE $file existss?!?!" );
  122.         }
  123.         open OUT, ">$file";
  124.         print OUT $cert;
  125.         close OUT;
  126.  
  127.         my $format = -B $file ? 'DER' : 'PEM'; 
  128.         my $cmd = "$opensslbin x509 -noout -hash -in $file -inform $format";
  129.  
  130.         my $cert_hash = `$cmd`;
  131.         $? and die "'$cmd' returned $?";
  132.         chomp($cert_hash); 
  133.         my $label = query_label;
  134.         &add_certificate($ARGV[1], \$cert_hash, 1, $label, '?');
  135.         unlink $file;
  136.     }
  137. }
  138. elsif(@ARGV == 2 and $ARGV[0] eq "add_pem") {
  139.     -e $ARGV[1] and -s $ARGV[1] or die("$ARGV[1] is nonexistent or empty.");
  140.     open(PEM_FILE, "<$ARGV[1]") or die("Can't open $ARGV[1]: $!");
  141.     my @pem = <PEM_FILE>;
  142.     close(PEM_FILE);
  143.     handle_pem(@pem);
  144. }
  145. elsif( @ARGV == 2 and $ARGV[0] eq "add_p12") {
  146.     -e $ARGV[1] and -s $ARGV[1] or die("$ARGV[1] is nonexistent or empty.");
  147.  
  148.     print "\nNOTE: This will ask you for two passphrases:\n";
  149.     print "       1. The passphrase you used for exporting\n";
  150.     print "       2. The passphrase you wish to secure your private key with.\n\n";
  151.  
  152.     my $pem_file = "$ARGV[1].pem";
  153.     
  154.     my $cmd = "$opensslbin pkcs12 -in $ARGV[1] -out $pem_file";
  155.     system $cmd and die "'$cmd' returned $?";
  156.     
  157.     -e $pem_file and -s $pem_file or die("Conversion of $ARGV[1] failed.");
  158.     open(PEM_FILE, $pem_file) or die("Can't open $pem_file: $!");
  159.     my @pem = <PEM_FILE>;
  160.     close(PEM_FILE);
  161.     unlink $pem_file;
  162.     handle_pem(@pem);
  163. }
  164. elsif(@ARGV == 4 and $ARGV[0] eq "add_chain") {
  165.     my $mailbox;
  166.     my $format = -B $ARGV[2] ? 'DER' : 'PEM'; 
  167.     my $cmd = "$opensslbin x509 -noout -hash -in $ARGV[2] -inform $format";
  168.     my $cert_hash = `$cmd`;
  169.  
  170.     $? and die "'$cmd' returned $?";
  171.  
  172.     $format = -B $ARGV[3] ? 'DER' : 'PEM'; 
  173.  
  174.     $cmd = "$opensslbin x509 -noout -hash -in $ARGV[3] -inform $format";
  175.     my $issuer_hash = `$cmd`;
  176.     $? and die "'$cmd' returned $?";
  177.     
  178.     chomp($cert_hash); 
  179.     chomp($issuer_hash);
  180.  
  181.     my $label = query_label;
  182.     
  183.     add_certificate($ARGV[3], \$issuer_hash, 0, $label); 
  184.     my @mailbox = &add_certificate($ARGV[2], \$cert_hash, 1, $label, $issuer_hash);
  185.     
  186.     foreach $mailbox (@mailbox) {
  187.       chomp($mailbox);
  188.       add_key($ARGV[1], $cert_hash, $mailbox, $label);
  189.     }
  190. }
  191. elsif((@ARGV == 2 or @ARGV == 3) and $ARGV[0] eq "verify") {
  192.     verify_cert($ARGV[1], $ARGV[2]);
  193. }
  194. elsif(@ARGV == 2 and $ARGV[0] eq "remove") {
  195.     remove_pair($ARGV[1]);
  196. }
  197. elsif(@ARGV == 2 and $ARGV[0] eq "add_root") {
  198.     add_root_cert($ARGV[1]);
  199. }
  200. else {    
  201.     usage;
  202.     exit(1);
  203. }
  204.  
  205. exit(0);
  206.  
  207.  
  208.  
  209.  
  210.  
  211. ##############  sub-routines  ########################
  212.  
  213. sub usage () {
  214.     print <<EOF;
  215.  
  216. Usage: smime_keys <operation>  [file(s) | keyID [file(s)]]
  217.  
  218.         with operation being one of:
  219.  
  220.         init      : no files needed, inits directory structure.
  221.  
  222.         list      : lists the certificates stored in database.
  223.         label     : keyID required. changes/removes/adds label.
  224.         remove    : keyID required.
  225.         verify    : 1=keyID and optionally 2=CRL
  226.                     Verifies the certificate chain, and optionally wether
  227.                     this certificate is included in supplied CRL (PEM format).
  228.                     Note: to verify all certificates at the same time,
  229.                     replace keyID with "all"
  230.  
  231.         add_cert  : certificate required.
  232.         add_chain : three files reqd: 1=Key, 2=certificate
  233.                     plus 3=intermediate certificate(s).
  234.         add_p12   : one file reqd. Adds keypair to database.
  235.                     file is PKCS12 (e.g. export from netscape).
  236.         add_pem   : one file reqd. Adds keypair to database.
  237.                     (file was converted from e.g. PKCS12).
  238.  
  239.         add_root  : one file reqd. Adds PEM root certificate to the location
  240.                     specified within muttrc (smime_verify_* command)
  241.  
  242. EOF
  243. }
  244.  
  245. sub mutt_Q ($) {
  246.     my $var = shift or die;
  247.  
  248.     my $cmd = "$mutt -v >/dev/null 2>/dev/null";
  249.     system ($cmd) == 0 
  250.         or die<<EOF;
  251. Couldn't launch mutt. I attempted to do so by running the command "$mutt".
  252. If that's not the right command, you can override it by setting the 
  253. environment variable \$MUTT_CMDLINE
  254. EOF
  255.  
  256.     $cmd = "$mutt -Q $var 2>/dev/null";
  257.     my $answer = `$cmd`;
  258.  
  259.     $? and die<<EOF;
  260. Couldn't look up the value of the mutt variable "$var". 
  261. You must set this in your mutt config file. See contrib/smime.rc for an example.
  262. EOF
  263. #'
  264.  
  265.     $answer =~ /\"(.*?)\"/ and return bsd_glob($1, GLOB_TILDE | GLOB_NOCHECK);
  266.     
  267.     $answer =~ /^Mutt (.*?) / and die<<EOF;
  268. This script requires mutt 1.5.0 or later. You are using mutt $1.
  269. EOF
  270.     
  271.     die "Value of $var is weird\n";
  272. }
  273.  
  274. sub mycopy ($$) {
  275.     my $source = shift or die;
  276.     my $dest = shift or die;
  277.  
  278.     copy $source, $dest or die "Problem copying $source to $dest: $!\n";
  279. }
  280.  
  281. #
  282. #  directory setup routines
  283. #
  284.  
  285.  
  286. sub mkdir_recursive ($) {
  287.     my $path = shift or die;
  288.     my $tmp_path;
  289.     
  290.     for my $dir (split /\//, $path) {
  291.         $tmp_path .= "$dir/";
  292.  
  293.         -d $tmp_path 
  294.             or mkdir $tmp_path, 0700
  295.                 or die "Can't mkdir $tmp_path: $!";
  296.     }
  297. }
  298.  
  299. sub init_paths () {
  300.     mkdir_recursive($certificates_path);
  301.     mkdir_recursive($private_keys_path);
  302.  
  303.     my $file;
  304.  
  305.     $file = $certificates_path . "/.index";
  306.     -f $file or open(TMP_FILE, ">$file") and close(TMP_FILE)
  307.         or die "Can't touch $file: $!";
  308.  
  309.     $file = $private_keys_path . "/.index";
  310.     -f $file or open(TMP_FILE, ">$file") and close(TMP_FILE)
  311.         or die "Can't touch $file: $!";
  312. }
  313.  
  314.  
  315.  
  316. #
  317. # certificate management methods
  318. #
  319.  
  320. sub list_certs () {
  321.   my %keyflags = ( 'i', '(Invalid)',  'r', '(Revoked)', 'e', '(Expired)',
  322.            'u', '(Unverified)', 'v', '(Valid)', 't', '(Trusted)');
  323.  
  324.   open(INDEX, "<$certificates_path/.index") or 
  325.     die "Couldn't open $certificates_path/.index: $!";
  326.   
  327.   print "\n";
  328.   while(<INDEX>) {
  329.     my $tmp;
  330.     my @tmp;
  331.     my $tab = "            ";
  332.     my @fields = split;
  333.  
  334.     if($fields[2] eq '-') {
  335.       print "$fields[1]: Issued for: $fields[0] $keyflags{$fields[4]}\n";
  336.     } else {
  337.       print "$fields[1]: Issued for: $fields[0] \"$fields[2]\" $keyflags{$fields[4]}\n";
  338.     }
  339.  
  340.     my $certfile = "$certificates_path/$fields[1]";
  341.     my $cert;
  342.     {
  343.         open F, $certfile or
  344.             die "Couldn't open $certfile: $!";
  345.         local $/;
  346.         $cert = <F>;
  347.     close F;
  348.     }
  349.  
  350.     my $subject_in;
  351.     my $issuer_in;
  352.     my $date1_in;
  353.     my $date2_in;
  354.  
  355.     my $format = -B $certfile ? 'DER' : 'PEM'; 
  356.     my $cmd = "$opensslbin x509 -subject -issuer -dates -noout -in $certfile -inform $format";
  357.     ($subject_in, $issuer_in, $date1_in, $date2_in) = `$cmd`;
  358.     $? and print "ERROR: '$cmd' returned $?\n\n" and next;
  359.  
  360.  
  361.     my @subject = split(/\//, $subject_in);
  362.     while(@subject) {
  363.       $tmp = shift @subject;
  364.       ($tmp =~ /^CN\=/) and last;
  365.       undef $tmp;
  366.     }
  367.     defined $tmp and @tmp = split (/\=/, $tmp) and
  368.       print $tab."Subject: $tmp[1]\n";
  369.  
  370.     my @issuer = split(/\//, $issuer_in);
  371.     while(@issuer) {
  372.       $tmp = shift @issuer;
  373.       ($tmp =~ /^CN\=/) and last;
  374.       undef $tmp;
  375.     }
  376.     defined $tmp and @tmp = split (/\=/, $tmp) and
  377.       print $tab."Issued by: $tmp[1]";
  378.  
  379.     if ( defined $date1_in and defined $date2_in ) {
  380.       @tmp = split (/\=/, $date1_in);
  381.       $tmp = $tmp[1];
  382.       @tmp = split (/\=/, $date2_in);
  383.       print $tab."Certificate is not valid before $tmp".
  384.     $tab."                      or after  ".$tmp[1];
  385.     }
  386.  
  387.     -e "$private_keys_path/$fields[1]" and
  388.       print "$tab - Matching private key installed -\n";
  389.  
  390.     $format = -B "$certificates_path/$fields[1]" ? 'DER' : 'PEM'; 
  391.     $cmd = "$opensslbin x509 -purpose -noout -in $certfile -inform $format";
  392.     my $purpose_in = `$cmd`;
  393.     $? and die "'$cmd' returned $?";
  394.  
  395.     my @purpose = split (/\n/, $purpose_in);
  396.     print "$tab$purpose[0] (displays S/MIME options only)\n";
  397.     while(@purpose) {
  398.       $tmp = shift @purpose;
  399.       ($tmp =~ /^S\/MIME/ and $tmp =~ /Yes/) or next;
  400.       my @tmptmp = split (/:/, $tmp);
  401.       print "$tab  $tmptmp[0]\n";
  402.     }
  403.  
  404.     print "\n";
  405.   }
  406.   
  407.   close(INDEX);
  408. }
  409.  
  410.  
  411.  
  412. sub query_label () {
  413.     my @words;
  414.     my $input;
  415.  
  416.     print "\nYou may assign a label to this key, so you don't have to remember\n";
  417.     print "the key ID. This has to be _one_ word (no whitespaces).\n\n";
  418.  
  419.     print "Enter label: ";
  420.     $input = <STDIN>;
  421.     chomp($input) if ( defined($input) );
  422.  
  423.     my ($label, $junk) = split(/\s/, $input, 2) if ( defined($input) );
  424.     
  425.     defined $junk 
  426.         and print "\nUsing '$label' as label; ignoring '$junk'\n";
  427.  
  428.     defined $label || ($label =  "-");
  429.  
  430.     return $label;
  431. }
  432.  
  433.  
  434.  
  435. sub add_entry ($$$$$) {
  436.     my $mailbox = shift or die;
  437.     my $hashvalue = shift or die;
  438.     my $use_cert = shift;
  439.     my $label = shift or die;
  440.     my $issuer_hash = shift;
  441.  
  442.     my @fields;
  443.  
  444.     if ($use_cert) {
  445.         open(INDEX, "+<$certificates_path/.index") or 
  446.             die "Couldn't open $certificates_path/.index: $!";
  447.     }
  448.     else {
  449.         open(INDEX, "+<$private_keys_path/.index") or 
  450.             die "Couldn't open $private_keys_path/.index: $!";
  451.     }
  452.  
  453.     while(<INDEX>) {
  454.         @fields = split;
  455.         return if ($fields[0] eq $mailbox && $fields[1] eq $hashvalue);
  456.     }
  457.  
  458.     if ($use_cert) {
  459.         print INDEX "$mailbox $hashvalue $label $issuer_hash u\n";
  460.     }
  461.     else {
  462.         print INDEX "$mailbox $hashvalue $label \n";
  463.     }
  464.  
  465.     close(INDEX);
  466. }
  467.  
  468.  
  469. sub add_certificate ($$$$;$) {
  470.     my $filename = shift or die;
  471.     my $hashvalue = shift or die;
  472.     my $add_to_index = shift;
  473.     my $label = shift or die;
  474.     my $issuer_hash = shift;
  475.  
  476.     my $iter = 0;
  477.     my @mailbox;
  478.     my $mailbox;
  479.  
  480.     while(-e "$certificates_path/$$hashvalue.$iter") {
  481.         my ($t1, $t2);
  482.         my $format = -B $filename ? 'DER' : 'PEM'; 
  483.         my $cmd = "$opensslbin x509 -in $filename -inform $format -fingerprint -noout";
  484.         $t1 = `$cmd`;
  485.         $? and die "'$cmd' returned $?";
  486.  
  487.         $format = -B "$certificates_path/$$hashvalue.$iter" ? 'DER' : 'PEM'; 
  488.         $cmd = "$opensslbin x509 -in $certificates_path/$$hashvalue.$iter -inform $format -fingerprint -noout";
  489.         $t2 = `$cmd`;
  490.         $? and die "'$cmd' returned $?";
  491.         
  492.         $t1 eq $t2 and last;
  493.  
  494.         $iter++;
  495.     }
  496.     $$hashvalue .= ".$iter";
  497.     
  498.     if (-e "$certificates_path/$$hashvalue") {
  499.             print "\nCertificate: $certificates_path/$$hashvalue already installed.\n";
  500.     }
  501.     else {
  502.         mycopy $filename, "$certificates_path/$$hashvalue";
  503.  
  504.         if ($add_to_index) {
  505.             my $format = -B $filename ? 'DER' : 'PEM'; 
  506.         my $cmd = "$opensslbin x509 -in $filename -inform $format -email -noout";
  507.         @mailbox = `$cmd`;
  508.         $? and die "'$cmd' returned $?";
  509.  
  510.         foreach $mailbox (@mailbox) {
  511.           chomp($mailbox);
  512.           add_entry($mailbox, $$hashvalue, 1, $label, $issuer_hash);
  513.  
  514.           print "\ncertificate $$hashvalue ($label) for $mailbox added.\n";
  515.         }
  516.         verify_cert($$hashvalue, undef);
  517.         }
  518.         else {
  519.             print "added certificate: $certificates_path/$$hashvalue.\n";
  520.         }
  521.     }
  522.  
  523.     return @mailbox;
  524. }
  525.  
  526.  
  527. sub add_key ($$$$) {
  528.     my $file = shift or die;
  529.     my $hashvalue = shift or die;
  530.     my $mailbox = shift or die;
  531.     my $label = shift or die;
  532.  
  533.     unless (-e "$private_keys_path/$hashvalue") {
  534.         mycopy $file, "$private_keys_path/$hashvalue";
  535.     }    
  536.  
  537.     add_entry($mailbox, $hashvalue, 0, $label, "");
  538.     print "added private key: " .
  539.       "$private_keys_path/$hashvalue for $mailbox\n";
  540.  
  541.  
  542.  
  543.  
  544.  
  545.  
  546. sub parse_pem (@) {
  547.     my $state = 0;
  548.     my $cert_iter = 0;
  549.     my @bag_attribs;
  550.     my $numBags = 0;
  551.  
  552.     $cert_tmp_file[$cert_iter] = newfile("cert_tmp.$cert_iter","temp");
  553.     my $cert_tmp_iter = $cert_tmp_file[$cert_iter];
  554.     open(CERT_FILE, ">$cert_tmp_iter") 
  555.         or die "Couldn't open $cert_tmp_iter: $!";
  556.  
  557.     while($_ = shift(@_)) {
  558.         if(/^Bag Attributes/) {
  559.             $numBags++;
  560.             $state == 0 or  die("PEM-parse error at: $.");
  561.         $state = 1;
  562.             $bag_attribs[$cert_iter*4+1] = "";
  563.             $bag_attribs[$cert_iter*4+2] = "";
  564.             $bag_attribs[$cert_iter*4+3] = "";
  565.         }
  566.  
  567.         ($state == 1) and /localKeyID:\s*(.*)/ 
  568.             and ($bag_attribs[$cert_iter*4+1] = $1);
  569.  
  570.         ($state == 1) and /subject=\s*(.*)/    
  571.             and ($bag_attribs[$cert_iter*4+2] = $1);
  572.  
  573.         ($state == 1) and /issuer=\s*(.*)/     
  574.             and ($bag_attribs[$cert_iter*4+3] = $1);
  575.         
  576.         if(/^-----/) {
  577.             if(/BEGIN/) {
  578.                 print CERT_FILE;
  579.                 $state = 2;
  580.  
  581.                 if(/PRIVATE/) {
  582.                     $bag_attribs[$cert_iter*4] = "K";
  583.                     next;
  584.                 }
  585.                 if(/CERTIFICATE/) {
  586.                     $bag_attribs[$cert_iter*4] = "C";
  587.                     next;
  588.                 }
  589.                 die("What's this: $_");
  590.             }
  591.             if(/END/) {
  592.                 $state = 0;
  593.                 print CERT_FILE;
  594.                 close(CERT_FILE);
  595.                 $cert_iter++;
  596.             $cert_tmp_file[$cert_iter] = newfile("cert_tmp.$cert_iter","temp");
  597.             $cert_tmp_iter = $cert_tmp_file[$cert_iter];
  598.                 open(CERT_FILE, ">$cert_tmp_iter")
  599.                     or die "Couldn't open $cert_tmp_iter: $!";
  600.                 next;
  601.             }
  602.         }
  603.         print CERT_FILE;
  604.     }
  605.     close(CERT_FILE);
  606.  
  607.     # I'll add support for unbagged cetificates, in case this is needed.
  608.     $numBags == $cert_iter or 
  609.         die("Not all contents were bagged. can't continue.");
  610.  
  611.     return @bag_attribs;
  612. }
  613.  
  614.  
  615. # This requires the Bag Attributes to be set
  616. sub handle_pem (@) {
  617.  
  618.     my @pem_contents;
  619.     my $iter=0;
  620.     my $root_cert;
  621.     my $key;
  622.     my $certificate;
  623.     my $intermediate;
  624.     my @mailbox;
  625.     my $mailbox;
  626.  
  627.     @pem_contents = &parse_pem(@_);
  628.  
  629.     # private key and certificate use the same 'localKeyID'
  630.     while($iter <= $#pem_contents / 4) {
  631.         if($pem_contents[$iter * 4] eq "K") {
  632.             $key = $iter;
  633.             last;
  634.         }
  635.         $iter++;
  636.     }
  637.     ($iter > $#pem_contents / 2) and die("Couldn't find private key!");
  638.  
  639.     $pem_contents[($key * 4)+1] or die("Attribute 'localKeyID' wasn't set.");
  640.  
  641.     $iter = 0;
  642.     while($iter <= $#pem_contents / 4) {
  643.         $iter == $key and ($iter++) and next;
  644.         if($pem_contents[($iter * 4)+1] eq $pem_contents[($key * 4)+1]) {
  645.             $certificate = $iter;
  646.             last;
  647.         }
  648.         $iter++;
  649.     }
  650.     ($iter > $#pem_contents / 4) and die("Couldn't find matching certificate!");
  651.  
  652.     my $tmp_key = newfile("tmp_key","temp");
  653.     mycopy $cert_tmp_file[$key], $tmp_key;
  654.     my $tmp_certificate = newfile("tmp_certificate","temp");
  655.     mycopy $cert_tmp_file[$certificate], $tmp_certificate;
  656.  
  657.     # root certificate is self signed
  658.     $iter = 0;
  659.  
  660.     while($iter <= $#pem_contents / 4) {
  661.         if ($iter == $key or $iter == $certificate) {
  662.             $iter++; 
  663.             next;
  664.         }
  665.  
  666.         if($pem_contents[($iter * 4)+2] eq $pem_contents[($iter * 4)+3]) {
  667.             $root_cert = $iter;
  668.             last;
  669.         }
  670.         $iter++;
  671.     }
  672.     if ($iter > $#pem_contents / 4) {
  673.       print "Couldn't identify root certificate!\n";
  674.       $root_cert = -1;      
  675.     }
  676.  
  677.     # what's left are intermediate certificates.
  678.     $iter = 0;
  679.  
  680.     # needs to be set, so we can check it later
  681.     $intermediate = $root_cert;
  682.     my $tmp_issuer_cert = newfile("tmp_issuer_cert","temp");
  683.     while($iter <= $#pem_contents / 4) {
  684.         if ($iter == $key or $iter == $certificate or $iter == $root_cert) {
  685.             $iter++; 
  686.             next;
  687.         }
  688.  
  689.     open (IC, ">> $tmp_issuer_cert") or die "can't open $tmp_issuer_cert: $?";
  690.     my $cert_tmp_iter = $cert_tmp_file[$iter];
  691.     open (CERT, "< $cert_tmp_iter") or die "can't open $cert_tmp_iter: $?";
  692.     print IC while (<CERT>);
  693.     close IC;
  694.     close CERT;
  695.  
  696.     # although there may be many, just need to know if there was any
  697.     $intermediate = $iter;
  698.  
  699.         $iter++;
  700.     }
  701.  
  702.     # no intermediate certificates ? use root-cert instead (if that was found...)
  703.     if($intermediate == $root_cert) {
  704.         if ($root_cert == -1) {
  705.       die("No root and no intermediate certificates. Can't continue.");
  706.     }
  707.         mycopy $cert_tmp_file[$root_cert], $tmp_issuer_cert;
  708.     }
  709.  
  710.     my $label = query_label;
  711.  
  712.     my $format = -B $tmp_certificate ? 'DER' : 'PEM'; 
  713.     my $cmd = "$opensslbin x509 -noout -hash -in $tmp_certificate -inform $format";
  714.     my $cert_hash = `$cmd`;
  715.     $? and die "'$cmd' returned $?";
  716.  
  717.     $format = -B $tmp_issuer_cert ? 'DER' : 'PEM'; 
  718.     $cmd = "$opensslbin x509 -noout -hash -in $tmp_issuer_cert -inform $format";
  719.     my $issuer_hash = `$cmd`;
  720.     $? and die "'$cmd' returned $?";
  721.  
  722.     chomp($cert_hash); chomp($issuer_hash);
  723.  
  724.     # Note: $cert_hash will be changed to reflect the correct filename
  725.     #       within add_cert() ONLY, so these _have_ to get called first..
  726.     add_certificate($tmp_issuer_cert, \$issuer_hash, 0, $label);
  727.     @mailbox = &add_certificate("$tmp_certificate", \$cert_hash, 1, $label, $issuer_hash); 
  728.     foreach $mailbox (@mailbox) {
  729.       chomp($mailbox);
  730.       add_key($tmp_key, $cert_hash, $mailbox, $label);
  731.     }
  732. }
  733.  
  734.  
  735.  
  736.  
  737.  
  738.  
  739. sub modify_entry ($$$;$ ) {
  740.     my $op = shift or die;
  741.     my $hashvalue = shift or die;
  742.     my $use_cert = shift;
  743.     my $crl;
  744.     my $label;
  745.     my $path;
  746.     my @fields;
  747.  
  748.     $op eq 'L' and ($label = shift or die);
  749.     $op eq 'V' and ($crl = shift);
  750.  
  751.  
  752.     if ($use_cert) {
  753.         $path = $certificates_path;
  754.     }
  755.     else {
  756.         $path = $private_keys_path;
  757.     }
  758.  
  759.     open(INDEX, "<$path/.index") or  
  760.       die "Couldn't open $path/.index: $!";
  761.     my $newindex = newfile("$path/.index.tmp");
  762.     open(NEW_INDEX, ">$newindex") or 
  763.       die "Couldn't create $newindex: $!";
  764.  
  765.     while(<INDEX>) {
  766.         @fields = split;
  767.         if($fields[1] eq $hashvalue or $hashvalue eq 'all') {
  768.       $op eq 'R' and next;
  769.       print NEW_INDEX "$fields[0] $fields[1]";
  770.       if($op eq 'L') {
  771.         if($use_cert) {
  772.           print NEW_INDEX " $label $fields[3] $fields[4]";
  773.         }
  774.         else {
  775.           print NEW_INDEX " $label";
  776.         }
  777.       }
  778.       if ($op eq 'V') {
  779.         print "\n==> about to verify certificate of $fields[0]\n";
  780.         my $flag = &do_verify($fields[1], $fields[3], $crl);
  781.         print NEW_INDEX " $fields[2] $fields[3] $flag";
  782.       }
  783.       print NEW_INDEX "\n";
  784.       next;
  785.     }
  786.     print NEW_INDEX;
  787.     }
  788.     close(INDEX);
  789.     close(NEW_INDEX);
  790.  
  791.     rename $newindex, "$path/.index" 
  792.         or die "Couldn't rename $newindex to $path/.index: $!\n";
  793.  
  794.     print "\n";
  795. }
  796.  
  797.  
  798.  
  799.  
  800. sub remove_pair ($ ) {
  801.   my $keyid = shift or die;
  802.  
  803.   if (-e "$certificates_path/$keyid") {
  804.     unlink "$certificates_path/$keyid";
  805.     modify_entry('R', $keyid, 1);
  806.     print "Removed certificate $keyid.\n";
  807.   }
  808.   else {
  809.     die "No such certificate: $keyid";
  810.   }
  811.  
  812.   if (-e "$private_keys_path/$keyid") {
  813.     unlink "$private_keys_path/$keyid";
  814.     modify_entry('R', $keyid, 0);
  815.     print "Removed private key $keyid.\n";
  816.   }
  817. }
  818.  
  819.  
  820.  
  821. sub change_label ($ ) {
  822.   my $keyid = shift or die;
  823.   
  824.   my $label = query_label;
  825.  
  826.   if (-e "$certificates_path/$keyid") {
  827.     modify_entry('L', $keyid, 1, $label);
  828.     print "Changed label for certificate $keyid.\n";
  829.   }
  830.   else {
  831.     die "No such certificate: $keyid";
  832.   }
  833.  
  834.   if (-e "$private_keys_path/$keyid") {
  835.     modify_entry('L', $keyid, 0, $label);
  836.     print "Changed label for private key $keyid.\n";
  837.   }
  838.  
  839. }
  840.  
  841.  
  842.  
  843.  
  844. sub verify_cert ($$) {
  845.   my $keyid = shift or die;
  846.   my $crl = shift;
  847.  
  848.   -e "$certificates_path/$keyid" or $keyid eq 'all'
  849.     or die "No such certificate: $keyid";
  850.   modify_entry('V', $keyid, 1, $crl);
  851. }
  852.  
  853.  
  854.  
  855.  
  856. sub do_verify($$$) {
  857.  
  858.   my $cert = shift or die;
  859.   my $issuerid = shift or die;
  860.   my $crl = shift;
  861.  
  862.   my $result = 'i';
  863.   my $trust_q;
  864.   my $issuer_path;
  865.   my $cert_path = "$certificates_path/$cert";
  866.  
  867.   if($issuerid eq '?') {
  868.     $issuer_path = "$certificates_path/$cert";
  869.   } else {
  870.     $issuer_path = "$certificates_path/$issuerid";
  871.   }
  872.  
  873.   my $cmd = "$opensslbin verify $root_certs_switch $root_certs_path -purpose smimesign -purpose smimeencrypt -untrusted $issuer_path $cert_path";
  874.   my $output = `$cmd`;
  875.   $? and die "'$cmd' returned $?";
  876.   chop $output;
  877.   print "\n$output\n";
  878.  
  879.   ($output =~ /OK/) and ($result = 'v');
  880.  
  881.   $result eq 'i' and return $result;
  882.  
  883.   my $format = -B $cert_path ? 'DER' : 'PEM'; 
  884.   $cmd = "$opensslbin x509 -dates -serial -noout -in $cert_path -inform $format";
  885.   (my $date1_in, my $date2_in, my $serial_in) = `$cmd`;
  886.   $? and die "'$cmd' returned $?";
  887.  
  888.   if ( defined $date1_in and defined $date2_in ) {
  889.     my @tmp = split (/\=/, $date1_in);
  890.     my $tmp = $tmp[1];
  891.     @tmp = split (/\=/, $date2_in);
  892.     my %months = ('Jan', '00', 'Feb', '01', 'Mar', '02', 'Apr', '03',
  893.           'May', '04', 'Jun', '05', 'Jul', '06', 'Aug', '07',
  894.           'Sep', '08', 'Oct', '09', 'Nov', '10', 'Dec', '11');
  895.  
  896.     my @fields =
  897.       $tmp =~ /(\w+)\s*(\d+)\s*(\d+):(\d+):(\d+)\s*(\d+)\s*GMT/;
  898.  
  899.     $#fields != 5 and print "Expiration Date: Parse Error :  $tmp\n\n" or
  900.       timegm($fields[4], $fields[3], $fields[2], $fields[1],
  901.          $months{$fields[0]}, $fields[5]) > time and $result = 'e';
  902.     $result eq 'e' and print "Certificate is not yet valid.\n" and return $result;
  903.  
  904.     @fields =
  905.       $tmp[1] =~ /(\w+)\s*(\d+)\s*(\d+):(\d+):(\d+)\s*(\d+)\s*GMT/;
  906.  
  907.     $#fields != 5 and print "Expiration Date: Parse Error :  $tmp[1]\n\n" or
  908.       timegm($fields[4], $fields[3], $fields[2], $fields[1],
  909.          $months{$fields[0]}, $fields[5]) < time and $result = 'e';
  910.     $result eq 'e' and print "Certificate has expired.\n" and return $result;
  911.  
  912.   }
  913.     
  914.   if ( defined $crl ) {
  915.     my @serial = split (/\=/, $serial_in);
  916.     my $cmd = "$opensslbin crl -text -noout -in $crl | grep -A1 $serial[1]";
  917.     (my $l1, my $l2) = `$cmd`;
  918.     $? and die "'$cmd' returned $?";
  919.     
  920.     if ( defined $l2 ) {
  921.       my @revoke_date = split (/:\s/, $l2);
  922.       print "FAILURE: Certificate $cert has been revoked on $revoke_date[1]\n";
  923.       $result = 'r';
  924.     }
  925.   }    
  926.   print "\n";
  927.  
  928.   if ($result eq 'v') {
  929.     return 't';
  930.   }
  931.  
  932.   return $result;
  933. }
  934.  
  935.  
  936.  
  937. sub add_root_cert ($) {
  938.   my $root_cert = shift or die;
  939.  
  940.   my $format = -B $root_cert ? 'DER' : 'PEM'; 
  941.  
  942.   my $cmd = "$opensslbin x509 -noout -hash -in $root_cert -inform $format";
  943.   my $root_hash = `$cmd`;
  944.   $? and die "'$cmd' returned $?";
  945.  
  946.   if (-d $root_certs_path) {
  947.     -e "$root_certs_path/$root_hash" or
  948.         mycopy $root_cert, "$root_certs_path/$root_hash";
  949.   }
  950.   else {
  951.     open(ROOT_CERTS, ">>$root_certs_path") or 
  952.       die ("Couldn't open $root_certs_path for writing");
  953.  
  954.     $cmd = "$opensslbin x509 -in $root_cert -inform $format -fingerprint -noout";
  955.     $? and die "'$cmd' returned $?";
  956.     chomp(my $md5fp = `$cmd`);
  957.  
  958.     $cmd = "$opensslbin x509 -in $root_cert -inform $format -text -noout";
  959.     $? and die "'$cmd' returned $?";
  960.     my @cert_text = `$cmd`;
  961.  
  962.     print "Enter a label, name or description for this certificate: ";
  963.     my $input = <STDIN>;
  964.  
  965.     my $line = "=======================================\n";
  966.     print ROOT_CERTS "\n$input$line$md5fp\nPEM-Data:\n";
  967.  
  968.     $cmd = "$opensslbin x509 -in $root_cert -inform $format";
  969.     my $cert = `$cmd`;
  970.     $? and die "'$cmd' returned $?";
  971.     print ROOT_CERTS $cert;
  972.     print ROOT_CERTS @cert_text;
  973.     close (ROOT_CERTS);
  974.   }
  975.   
  976. }
  977.  
  978. sub newfile ($;$$) {
  979.     # returns a file name which does not exist for tmp file creation
  980.     my $filename = shift;
  981.     my $option = shift;
  982.     $option = "notemp" if (not defined($option));
  983.     if (! $tmpdir and $option eq "temp") {
  984.         $tmpdir = mutt_Q 'tmpdir';
  985.                 $tmpdir = '/tmp' if ($tmpdir =~ m/=/); # if the tmpdir contains '=', use the default
  986.         $tmpdir = newfile("$tmpdir/smime");
  987.         mkdir $tmpdir, 0700 || die "Can't create $tmpdir: $!\n";
  988.     }
  989.     $filename = "$tmpdir/$filename" if ($option eq "temp");
  990.     my $newfilename = $filename;
  991.     my $count = 0;
  992.     while (-e $newfilename) {
  993.         $newfilename = "$filename.$count";
  994.         $count++;
  995.     }
  996.     unshift(@tempfiles,$newfilename);
  997.     return $newfilename;
  998. }
  999.  
  1000.  
  1001. END {
  1002.     # remove all our temporary files in the end:
  1003.     for (@tempfiles){
  1004.         if (-f) {
  1005.             unlink;
  1006.         } elsif (-d) { 
  1007.             rmdir;
  1008.         }
  1009.     }
  1010. }
  1011.