From ee4532f1f2ffeb45008df01a3aab80f494396ca9 Mon Sep 17 00:00:00 2001 From: Jamie Cameron Date: Sun, 22 Jun 2008 23:10:18 +0000 Subject: [PATCH] Completed SSL monitor --- status/CHANGELOG | 1 + status/lang/en | 11 +++- status/sslcert-monitor.pl | 120 ++++++++++++++++++++++++++++---------- 3 files changed, 100 insertions(+), 32 deletions(-) diff --git a/status/CHANGELOG b/status/CHANGELOG index 2c0f858d3..4c9f90f89 100644 --- a/status/CHANGELOG +++ b/status/CHANGELOG @@ -55,3 +55,4 @@ Email, SMS and SNMP messages sent by Webmin can be customized, with multiple mes The process check monitor can now be limited to a particular user. ---- Changes since 1.420 ---- Allow saving of remote Webmin monitors when the remote host is down. +Added a new monitor type for checking the expiry and validity of SSL certificates in a local file or on any SSL website. diff --git a/status/lang/en b/status/lang/en index 790f009e8..6733fab30 100644 --- a/status/lang/en +++ b/status/lang/en @@ -460,10 +460,19 @@ dtmpls_err=Failed to delete templates dtmpls_enone=None selected dtmpls_eusers=$1 is in use by the following monitors : $2 -sslcert_url=SSL URL to check certificate for +sslcert_src=Certificate location +sslcert_url=From HTTPS URL +sslcert_file=From file on server sslcert_eurl=Missing, invalid or non-SSL URL +sslcert_efile=Missing or invalid certificate file sslcert_ecert=Could not get certificate sslcert_days=Days before expiry to fail sslcert_when=When expired sslcert_mismatch=Also detect hostname mismatch? sslcert_edays=Missing or invalid number of days +sslcert_eopenssl=The openssl command is needed to check certificates +sslcert_estart=Not valid until $1 +sslcert_eend=Not valid after $1 +sslcert_left=Up - $1 days until expiry +sslcert_ematch=Hostname $1 does not match certificate $2 +sslcert_emismatch=Certificate hostname checking can only be used when checking a URL diff --git a/status/sslcert-monitor.pl b/status/sslcert-monitor.pl index 2f30ba07c..6002ee4cc 100644 --- a/status/sslcert-monitor.pl +++ b/status/sslcert-monitor.pl @@ -4,54 +4,97 @@ sub get_sslcert_status { local $up = 0; local $desc; -eval "use Net::SSLeay"; +local $certfile; +local ($host, $port, $page, $ssl); -# Parse the URL and connect -local ($host, $port, $page, $ssl) = &parse_http_url($_[0]->{'url'}); +if ($_[0]->{'url'}) { + # Parse the URL and connect + ($host, $port, $page, $ssl) = &parse_http_url($_[0]->{'url'}); -# Run the openssl command to connect -local $cmd = "openssl s_client -host ".quotemeta($host). - " -port ".quotemeta($port)." &1"; -local $out = &backquote_with_timeout($cmd, 10); -if ($?) { - # Connection failed - return { 'up' => -1 }; - } + # Run the openssl command to connect + local $cmd = "openssl s_client -host ".quotemeta($host). + " -port ".quotemeta($port)." &1"; + local $out = &backquote_with_timeout($cmd, 10); + if ($?) { + # Connection failed + return { 'up' => -1 }; + } -# Extract the cert part and save -local $temp = &transname(); -if ($out =~ /(-----BEGIN CERTIFICATE-----\n(.*\n)+-----END CERTIFICATE-----\n)/) { - local $cert = $1; - print STDERR "cert=$cert\n"; - &open_tempfile(CERT, ">$temp", 0, 1); - &print_tempfile(CERT, $cert); - &close_tempfile(CERT); + # Extract the cert part and save + $certfile = &transname(); + if ($out =~ /(-----BEGIN CERTIFICATE-----\n(.*\n)+-----END CERTIFICATE-----\n)/) { + local $cert = $1; + &open_tempfile(CERT, ">$certfile", 0, 1); + &print_tempfile(CERT, $cert); + &close_tempfile(CERT); + } + else { + # No cert? + return { 'up' => 0, + 'desc' => $text{'sslcert_ecert'} }; + } } else { - # No cert? - return { 'up' => 0, - 'desc' => $text{'sslcert_ecert'} }; + # Cert is already in a file + $certfile = $_[0]->{'file'}; } # Get end date with openssl x509 -in cert.pem -inform PEM -text -noout -enddate -local $info = &backquote_command("openssl x509 -in ".quotemeta($temp). +local $info = &backquote_command("openssl x509 -in ".quotemeta($certfile). " -inform PEM -text -noout -enddate ". " &1"); print STDERR "info=$info\n"; # Check dates -# XXX (before and after) +&foreign_require("mailboxes", "mailboxes-lib.pl"); +local ($start, $end); +if ($info =~ /Not\s*Before\s*:\s*(.*)/i) { + $start = &mailboxes::parse_mail_date("$1"); + } +if ($info =~ /Not\s+After\s*:\s*(.*)/i) { + $end = &mailboxes::parse_mail_date("$1"); + } +local $now = time(); +print STDERR "start=$start end=$end now=$now\n"; +if ($start && $now < $start) { + # Too new?! + $desc = &text('sslcert_estart', &make_date($start)); + } +elsif ($end && $now > $end-$_[0]->{'days'}*24*60*60) { + # Too old + $desc = &text('sslcert_eend', &make_date($end)); + } +elsif ($_[0]->{'mismatch'} && $_[0]->{'url'} && + $info =~ /Subject:.*CN=([a-z0-9\.\-\_\*]+)/i) { + # Check hostname + local $cn = $1; + local $match = $1; + $match =~ s/\*/\.\*/g; # Make perl RE + if ($host !~ /^$match$/i) { + $desc = &text('sslcert_ematch', "$host", + "$cn"); + } + } -$up = 1; +if (!$desc) { + # All OK! + $desc = &text('sslcert_left', int(($end-$now)/(24*60*60))); + $up = 1; + } return { 'up' => $up, 'desc' => $desc }; } sub show_sslcert_dialog { -# URK to check -print &ui_table_row($text{'sslcert_url'}, - &ui_textbox("url", $_[0]->{'url'}, 50), 3); +# URL or file to check +print &ui_table_row($text{'sslcert_src'}, + &ui_radio_table("src", $_[0]->{'file'} ? 1 : 0, + [ [ 0, $text{'sslcert_url'}, + &ui_textbox("url", $_[0]->{'url'}, 50) ], + [ 1, $text{'sslcert_file'}, + &ui_textbox("file", $_[0]->{'file'}, 50)." ". + &file_chooser_button("file") ] ]), 3); # Days before expiry to warn print &ui_table_row($text{'sslcert_days'}, @@ -64,9 +107,21 @@ print &ui_table_row($text{'sslcert_mismatch'}, sub parse_sslcert_dialog { -# Parse URL -$in{'url'} =~ /^https:\/\/(\S+)$/ || &error($text{'sslcert_eurl'}); -$_[0]->{'url'} = $in{'url'}; +&has_command("openssl") || &error($text{'sslcert_eopenssl'}); + +if ($in{'src'} == 0) { + # Parse URL + $in{'url'} =~ /^https:\/\/(\S+)$/ || &error($text{'sslcert_eurl'}); + $_[0]->{'url'} = $in{'url'}; + delete($_[0]->{'file'}); + } +else { + # Parse file + $in{'file'} =~ /^\// && -r $in{'file'} || + &error($text{'sslcert_efile'}); + $_[0]->{'file'} = $in{'file'}; + delete($_[0]->{'url'}); + } # Parse number of days if ($in{'days_def'}) { @@ -79,5 +134,8 @@ else { # Check hostname $_[0]->{'mismatch'} = $in{'mismatch'}; +if ($in{'mismatch'} && $in{'src'}) { + &error($text{'sslcert_emismatch'}); + } }