diff --git a/custom/custom-lib.pl b/custom/custom-lib.pl index 45e96a266..fbdbf13ee 100755 --- a/custom/custom-lib.pl +++ b/custom/custom-lib.pl @@ -647,5 +647,4 @@ else { } } - 1; diff --git a/forgot.cgi b/forgot.cgi index 98aefbacf..8726f5fd4 100755 --- a/forgot.cgi +++ b/forgot.cgi @@ -16,9 +16,9 @@ my $timeout = $gconfig{'passreset_timeout'} || 15; $remote_user && &error($text{'forgot_elogin'}); $ENV{'HTTPS'} eq 'ON' || $gconfig{'forgot_pass'} == 2 || &error($text{'forgot_essl'}); -$ENV{'SSL_HOST_CERT'} == 1 || +$ENV{'SSL_CN_CERT'} == 1 || &error(&text('forgot_esslhost', - &html_escape($ENV{'HTTP_HOST'} || $ENV{'SSL_HOST'}))) + &html_escape($ENV{'HTTP_HOST'} || $ENV{'SSL_CN'}))) if ($ENV{'HTTPS'} eq 'ON'); # Check that the random ID is valid diff --git a/forgot_form.cgi b/forgot_form.cgi index 12015ae3c..608cc7a76 100755 --- a/forgot_form.cgi +++ b/forgot_form.cgi @@ -15,9 +15,9 @@ $gconfig{'forgot_pass'} || &error($text{'forgot_ecannot'}); $remote_user && &error($text{'forgot_elogin'}); $ENV{'HTTPS'} eq 'ON' || $gconfig{'forgot_pass'} == 2 || &error($text{'forgot_essl'}); -$ENV{'SSL_HOST_CERT'} == 1 || +$ENV{'SSL_CN_CERT'} == 1 || &error(&text('forgot_esslhost', - &html_escape($ENV{'HTTP_HOST'} || $ENV{'SSL_HOST'}))) + &html_escape($ENV{'HTTP_HOST'} || $ENV{'SSL_CN'}))) if ($ENV{'HTTPS'} eq 'ON'); &ui_print_header(undef, $text{'forgot_title'}, "", undef, undef, 1, 1); diff --git a/forgot_send.cgi b/forgot_send.cgi index 3db41c484..a42d705c6 100755 --- a/forgot_send.cgi +++ b/forgot_send.cgi @@ -14,9 +14,9 @@ $gconfig{'forgot_pass'} || &error($text{'forgot_ecannot'}); $remote_user && &error($text{'forgot_elogin'}); $ENV{'HTTPS'} eq 'ON' || $gconfig{'forgot_pass'} == 2 || &error($text{'forgot_essl'}); -$ENV{'SSL_HOST_CERT'} == 1 || +$ENV{'SSL_CN_CERT'} == 1 || &error(&text('forgot_esslhost', - &html_escape($ENV{'HTTP_HOST'} || $ENV{'SSL_HOST'}))) + &html_escape($ENV{'HTTP_HOST'} || $ENV{'SSL_CN'}))) if ($ENV{'HTTPS'} eq 'ON'); # Lookup the Webmin user diff --git a/makedebian.pl b/makedebian.pl index 6f91c48ad..d67081b95 100755 --- a/makedebian.pl +++ b/makedebian.pl @@ -109,7 +109,7 @@ if ($product eq "webmin") { $size = int(`du -sk $tmp_dir`); @deps = ( "perl", "libnet-ssleay-perl", "openssl", "libauthen-pam-perl", "libpam-runtime", "libio-pty-perl", "unzip", "shared-mime-info", "tar", "libdigest-sha-perl", "libdigest-md5-perl", "gzip" ); $deps = join(", ", @deps); -@recommends = ( "libdatetime-perl", "libdatetime-timezone-perl", "libdatetime-locale-perl", "libtime-piece-perl", "libencode-detect-perl", "libtime-hires-perl", "libsocket6-perl", "html2text", "qrencode", "libdbi-perl", "libdbd-mysql-perl", "libjson-xs-perl" ); +@recommends = ( "libdatetime-perl", "libdatetime-timezone-perl", "libdatetime-locale-perl", "libtime-piece-perl", "libencode-detect-perl", "libtime-hires-perl", "libsocket6-perl", "html2text", "qrencode", "libdbi-perl", "libdbd-mysql-perl", "libjson-xs-perl", "libsys-syslog-perl" ); $recommends = join(", ", @recommends); open(CONTROL, ">$control_file"); print CONTROL <{'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 -{ -local ($file) = @_; -return $cert_file_info_cache{$file} if ($cert_file_info_cache{$file}); -return undef if (!-r $file); -my %rv; -my $cmd = "openssl x509 -in ".quotemeta($file)." -issuer -subject -enddate -startdate -text"; -open(OUT, $cmd." 2>/dev/null |"); -local $_; -while() { - 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; - } +=head2 cert_names($file) - 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 = ; - $alts =~ s/^\s+//; - foreach my $a (split(/[, ]+/, $alts)) { - if ($a =~ /^DNS:(\S+)/) { - push(@{$rv{'alt'}}, $1); - } - } - } - # Try to detect key algorithm - if (/Key\s+Algorithm:.*?(rsa|ec)[EP]/) { - $rv{'algo'} = $1; - } - if (/RSA\s+Public\s+Key:\s+\((\d+)\s*bit/) { - $rv{'size'} = $1; - } - 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"; +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 +{ +my ($file) = @_; +return $cert_names_cache{$file} if ($cert_names_cache{$file}); +return undef if (!$file || !-r $file); +my %rv; +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); + } + +# 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); } } -close(OUT); -foreach my $k (keys %rv) { - $rv{$k} =~ s/http:\|\|/http:\/\//g; + +# 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'; } -$rv{'self'} = $rv{'o'} eq $rv{'issuer_o'} ? 1 : 0; -$cert_file_info_cache{$file} = \%rv; + +# 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; + } + $rv{alt} = \@dns if @dns; + } + +Net::SSLeay::X509_free($cert); +$cert_names_cache{$file} = \%rv; return \%rv; } diff --git a/proc/linux-lib.pl b/proc/linux-lib.pl index 35e17a93b..c982455a2 100755 --- a/proc/linux-lib.pl +++ b/proc/linux-lib.pl @@ -700,14 +700,14 @@ if (!@fans && @cpu && @fans_all && } # Fall back logic for CPU temperature and fans spread over multiple -# devices like Raspberry Pi #2517 and #2539 +# devices like Raspberry Pi #2517 and #2539 #2545 if (@cpu || !@fans) { # - Look for least two ISA voltage rails anywhere # - See a CPU temp under cpu_thermal - # - Optionally grab a fan RPM under pwmfan-isa-* + # - Optionally grab a fan RPM under *fan-isa-* my $can_fallback = (!@cpu && (grep { /^\s*cpu_thermal/i } @sensors)) || - (@cpu && !@fans && (grep { /^\s*pwmfan-isa-/i } @sensors)); + (@cpu && !@fans && (grep { /fan-isa-\d+/i } @sensors)); return (\@cpu, \@fans) if (!$can_fallback); my ($chip, $bus); # isa|pci|platform|virtual my $isa_volt; @@ -735,7 +735,7 @@ if (@cpu || !@fans) { } # Fan RPM - if (defined $chip && $chip =~ /^pwmfan/i && + if (defined $chip && $chip =~ /fan$/i && /\b(?:cpu[_ ]?fan(?:\s*\d+)?|fan\d+)\s*:\s*(\d+)\s*rpm\b/i) { my $rpm = $1 + 0; $fan_rpm = $rpm if (!$fan_rpm || $rpm > $fan_rpm); diff --git a/setup.pl b/setup.pl index 6e649d888..9845d1d0a 100755 --- a/setup.pl +++ b/setup.pl @@ -971,7 +971,7 @@ if (!$ENV{'nostart'}) { print " - DateTime, DateTime::Locale, DateTime::TimeZone, Data::Dumper,\n"; print " - Digest::MD5, Digest::SHA, Encode::Detect, File::Basename,\n"; print " - File::Path, Net::SSLeay, Time::HiRes, Time::Local, Time::Piece,\n"; - print " - JSON::XS, lib, open\n"; + print " - Sys::Syslog, JSON::XS, lib, open\n"; print " Packages:\n"; print " - openssl - Cryptography library with TLS implementation\n"; print " - shared-mime-info - Shared MIME information database\n"; diff --git a/setup.sh b/setup.sh index 177f800ad..bc2d47e45 100755 --- a/setup.sh +++ b/setup.sh @@ -1063,7 +1063,7 @@ if [ "$nostart" = "" ]; then echo " - DateTime, DateTime::Locale, DateTime::TimeZone, Data::Dumper," echo " - Digest::MD5, Digest::SHA, Encode::Detect, File::Basename," echo " - File::Path, Net::SSLeay, Time::HiRes, Time::Local, Time::Piece," - echo " - JSON::XS, lib, open" + echo " - Sys::Syslog, JSON::XS, lib, open" echo " Packages:" echo " - openssl - Cryptography library with TLS implementation" echo " - shared-mime-info - Shared MIME information database" diff --git a/web-lib-funcs.pl b/web-lib-funcs.pl index bcc8853ca..8646d9420 100755 --- a/web-lib-funcs.pl +++ b/web-lib-funcs.pl @@ -7581,14 +7581,8 @@ if ($opened) { close $fh or return "Error: close($file): $!"; } -# Optionally print to UI (web scripts only) -if ($main::webmin_script_type eq 'web') { - # Print HTTP header once per process - our $var_dump__hdr_sent ||= 0; - if (!$var_dump__hdr_sent && !$main::done_webmin_header) { - print "Content-type: text/html; charset=UTF-8\n\n"; - $var_dump__hdr_sent = 1; - } +# Optionally print to UI (web scripts only) only if header already sent +if ($main::done_webmin_header && $main::webmin_script_type eq 'web') { # Limit long dumps by wrapping parameter chunks between accordion lines my $out = $dump_txt; # Add header @@ -13261,11 +13255,11 @@ if (!$def && $gconfig{'webmin_email_url'}) { # From a config option $url = $gconfig{'webmin_email_url'}; } -elsif ($ENV{'HTTP_HOST'} || $ENV{'SSL_HOST'}) { +elsif ($ENV{'HTTP_HOST'} || $ENV{'SSL_CN'}) { # From this HTTP request my $port = $ENV{'SERVER_PORT'} || 80; - my $host = $ENV{'SSL_HOST'} - ? "$ENV{'SSL_HOST'}:$port" + my $host = $ENV{'SSL_CN'} + ? "$ENV{'SSL_CN'}:$port" : $ENV{'HTTP_HOST'}; if ($host =~ s/:(\d+)$//) { $port = $1;