Fix to remove significant bottleneck of shelling out

This commit is contained in:
Ilia Ross
2025-09-25 14:48:14 +03:00
parent f08ad4eb19
commit d99a24b045

View File

@@ -4784,7 +4784,7 @@ eval 'Net::SSLeay::CTX_set_options($ssl_ctx,
&Net::SSLeay::OP_NO_RENEGOTIATION)';
# Get the hostnames each cert is valid for
my $info = &cert_file_info($certfile);
my $info = &cert_names($certfile);
my @hosts;
push(@hosts, $info->{'cn'}) if ($info->{'cn'});
push(@hosts, @{$info->{'alt'}}) if ($info->{'alt'});
@@ -5524,7 +5524,7 @@ foreach my $pe (split(/\t+/, $config{'expires_paths'})) {
undef(%sudocache);
# Reset cache of cert files
undef(%cert_file_info_cache);
undef(%cert_names_cache);
}
# is_group_member(&uinfo, groupname)
@@ -7014,122 +7014,60 @@ if (!$sig) {
return $sig;
}
# cert_file_info(file)
# Returns a hash of details of a cert in some file
sub cert_file_info
=head2 cert_names($file)
Extract Common Name and Subject Alternative Names from an X.509 certificate
file. Supports both PEM and DER certificates. Returns undef if file cannot be
read or parsed. Cache results for speed.
=cut
sub cert_names
{
local ($file) = @_;
return $cert_file_info_cache{$file} if ($cert_file_info_cache{$file});
return undef if (!-r $file);
my ($file) = @_;
return $cert_names_cache{$file} if ($cert_names_cache{$file});
return undef if (!$file || !-r $file);
my %rv;
my $cmd = "openssl x509 -in ".quotemeta($file)." -issuer -subject -enddate -startdate -text";
open(OUT, $cmd." 2>/dev/null |");
local $_;
while(<OUT>) {
s/\r|\n//g;
s/http:\/\//http:\|\|/g; # So we can parse with regexp
if (/subject=.*C\s*=\s*([^\/,]+)/) {
$rv{'c'} = $1;
}
if (/subject=.*ST\s*=\s*([^\/,]+)/) {
$rv{'st'} = $1;
}
if (/subject=.*L\s*=\s*([^\/,]+)/) {
$rv{'l'} = $1;
}
if (/subject=.*O\s*=\s*"(.*?)"/ || /subject=.*O\s*=\s*([^\/,]+)/) {
$rv{'o'} = $1;
}
if (/subject=.*OU\s*=\s*([^\/,]+)/) {
$rv{'ou'} = $1;
}
if (/subject=.*CN\s*=\s*([^\/,]+)/) {
$rv{'cn'} = $1;
}
if (/subject=.*emailAddress\s*=\s*([^\/,]+)/) {
$rv{'email'} = $1;
my $cert;
# Try PEM first
my $bio = Net::SSLeay::BIO_new_file($file, 'r');
if ($bio) {
$cert = Net::SSLeay::PEM_read_bio_X509($bio);
Net::SSLeay::BIO_free($bio);
}
if (/issuer=.*C\s*=\s*([^\/,]+)/) {
$rv{'issuer_c'} = $1;
}
if (/issuer=.*ST\s*=\s*([^\/,]+)/) {
$rv{'issuer_st'} = $1;
}
if (/issuer=.*L\s*=\s*([^\/,]+)/) {
$rv{'issuer_l'} = $1;
}
if (/issuer=.*O\s*=\s*"(.*?)"/ || /issuer=.*O\s*=\s*([^\/,]+)/) {
$rv{'issuer_o'} = $1;
}
if (/issuer=.*OU\s*=\s*([^\/,]+)/) {
$rv{'issuer_ou'} = $1;
}
if (/issuer=.*CN\s*=\s*([^\/,]+)/) {
$rv{'issuer_cn'} = $1;
}
if (/issuer=.*emailAddress\s*=\s*([^\/,]+)/) {
$rv{'issuer_email'} = $1;
}
if (/notAfter\s*=\s*(.*)/) {
$rv{'notafter'} = $1;
}
if (/notBefore\s*=\s*(.*)/) {
$rv{'notbefore'} = $1;
}
if (/Subject\s+Alternative\s+Name/i) {
my $alts = <OUT>;
$alts =~ s/^\s+//;
foreach my $a (split(/[, ]+/, $alts)) {
if ($a =~ /^DNS:(\S+)/) {
push(@{$rv{'alt'}}, $1);
# Try DER if PEM failed
if (!$cert) {
my $bio = Net::SSLeay::BIO_new_file($file, 'rb');
if ($bio) {
$cert = Net::SSLeay::d2i_X509_bio($bio);
Net::SSLeay::BIO_free($bio);
}
}
# Certificate not found
return undef if !$cert;
# Subject
my $subject = Net::SSLeay::X509_get_subject_name($cert);
if ($subject) {
# commonName
my $cn = Net::SSLeay::X509_NAME_get_text_by_NID($subject, 13);
$rv{cn} = $cn if defined $cn && $cn ne '' && $cn ne '-1';
}
# Try to detect key algorithm
if (/Key\s+Algorithm:.*?(rsa|ec)[EP]/) {
$rv{'algo'} = $1;
# subjectAltName
my @alts = Net::SSLeay::X509_get_subjectAltNames($cert);
if (@alts) {
my @dns;
while (my ($type, $val) = splice(@alts, 0, 2)) {
push @dns, $val if $type == 2;
}
if (/RSA\s+Public\s+Key:\s+\((\d+)\s*bit/) {
$rv{'size'} = $1;
$rv{alt} = \@dns if @dns;
}
elsif (/EC\s+Public\s+Key:\s+\((\d+)\s*bit/) {
$rv{'size'} = $1;
}
elsif (/Public-Key:\s+\((\d+)\s*bit/) {
$rv{'size'} = $1;
}
if (/Modulus\s*\(.*\):/ || /Modulus:/) {
$inmodulus = 1;
# RSA algo
$rv{'algo'} = "rsa" if (!$rv{'algo'});
}
elsif (/pub:/) {
$inmodulus = 1;
# ECC algo
$rv{'algo'} = 'ec' if (!$rv{'algo'});
}
if (/^\s+([0-9a-f:]+)\s*$/ && $inmodulus) {
$rv{'modulus'} .= $1;
}
# RSA exponent
if (/Exponent:\s*(\d+)/) {
$rv{'exponent'} = $1;
$inmodulus = 0;
}
# ECC properties
elsif (/(ASN1\s+OID):\s*(\S+)/ || /(NIST\s+CURVE):\s*(\S+)/) {
$inmodulus = 0;
my $comma = $rv{'exponent'} ? ", " : "";
$rv{'exponent'} .= "$comma$1: $2";
}
}
close(OUT);
foreach my $k (keys %rv) {
$rv{$k} =~ s/http:\|\|/http:\/\//g;
}
$rv{'self'} = $rv{'o'} eq $rv{'issuer_o'} ? 1 : 0;
$cert_file_info_cache{$file} = \%rv;
Net::SSLeay::X509_free($cert);
$cert_names_cache{$file} = \%rv;
return \%rv;
}