Skip to content

Commit

Permalink
smime_keys: Handle certificate chains in add_cert. (closes #3339) (cl…
Browse files Browse the repository at this point in the history
…oses #3559)

Find all chains in the certificate provided.  For each chain create a
separate leaf and intermediate certificate file.  Because Mutt controls
the label prompt, use a single label for all chains.

Also, loosen up cert file parsing to allow attributes even if they
aren't delimited by a "Bag Attributes" header.

Thanks to David J. Weller-Fahy for his testing and feedback!
  • Loading branch information
kevin8t8 authored and karelzak committed Jul 17, 2015
1 parent 1f38187 commit f4ef8ce
Showing 1 changed file with 76 additions and 17 deletions.
93 changes: 76 additions & 17 deletions smime_keys.pl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
sub verify_files_exist (@);
sub create_tempfile (;$);
sub new_cert_structure ();
sub create_cert_chains (@);

# openssl helpers
sub openssl_exec (@);
Expand Down Expand Up @@ -294,6 +295,46 @@ ()
return $cert_data;
}

sub create_cert_chains (@) {
my (@certs) = @_;

my (%subject_hash, @leaves, @chains);

foreach my $cert (@certs) {
$cert->{children} = 0;
if ($cert->{subject}) {
$subject_hash{$cert->{subject}} = $cert;
}
}

foreach my $cert (@certs) {
my $parent = $subject_hash{$cert->{issuer}};
if (defined($parent)) {
$parent->{children} += 1;
}
}

@leaves = grep { $_->{children} == 0 } @certs;
foreach my $leaf (@leaves) {
my $chain = [];
my $cert = $leaf;

while (defined($cert)) {
push @$chain, $cert;

$cert = $subject_hash{$cert->{issuer}};
if (defined($cert) &&
(scalar(grep {$_ == $cert} @$chain) != 0)) {
$cert = undef;
}
}

push @chains, $chain;
}

return @chains;
}


##################
# openssl helpers
Expand Down Expand Up @@ -499,7 +540,8 @@ ($$)
$state = 1;
}

if ($state == 1) {
# Allow attributes without the "Bag Attributes" header
if ($state != 2) {
if (/localKeyID:\s*(.*)/) {
$cert_data->{localKeyID} = $1;
}
Expand Down Expand Up @@ -833,23 +875,40 @@ ($)
my ($filename) = @_;

my $label = query_label();
my @cert_contents = openssl_parse_pem($filename, 0);
@cert_contents = grep { $_->{type} eq "C" } @cert_contents;

my @cert_chains = create_cert_chains(@cert_contents);
print "Found " . scalar(@cert_chains) . " certificate chains\n";

foreach my $chain (@cert_chains) {
my $leaf = shift(@$chain);
my $issuer_chain_hash = "?";

print "Processing chain:\n";
if ($leaf->{subject}) {
print "subject=" . $leaf->{subject} . "\n";
}

my $cert_hash = openssl_hash($filename);
cm_add_certificate($filename, \$cert_hash, 1, $label, '?');

# TODO:
# Below is the method from http://kb.wisc.edu/middleware/page.php?id=4091
# Investigate threading the chain and separating out issuer as an alternative.

# my @cert_contents = openssl_parse_pem($filename, 0);
# foreach my $cert (@cert_contents) {
# if ($cert->{type} eq "C") {
# my $cert_hash = openssl_hash($cert->{datafile});
# cm_add_certificate($cert->{datafile}, \$cert_hash, 1, $label, '?');
# } else {
# print "Ignoring private key\n";
# }
# }
if (scalar(@$chain) > 0) {
my ($issuer_chain_fh, $issuer_chain_file) = create_tempfile();

foreach my $issuer (@$chain) {
my $issuer_datafile = $issuer->{datafile};
open(my $issuer_fh, "< $issuer_datafile") or
die "can't open $issuer_datafile: $?";
print $issuer_chain_fh $_ while (<$issuer_fh>);
close($issuer_fh);
}

close($issuer_chain_fh);
$issuer_chain_hash = openssl_hash($issuer_chain_file);
cm_add_certificate($issuer_chain_file, \$issuer_chain_hash, 0, $label);
}

my $leaf_hash = openssl_hash($leaf->{datafile});
cm_add_certificate($leaf->{datafile}, \$leaf_hash, 1, $label, $issuer_chain_hash);
}
}

sub handle_add_pem ($) {
Expand Down

0 comments on commit f4ef8ce

Please sign in to comment.