From 34fe20e3e7a4d6eac1cf55979e148753500c5246 Mon Sep 17 00:00:00 2001 From: Irina Peshinskaya Date: Mon, 7 Nov 2016 08:31:35 +0200 Subject: [PATCH] TT#4902 Check API CA cert Change-Id: Icc7e9c20a041852b470a0dc6353fc3081e49de63 --- lib/NGCP/Panel/Controller/Administrator.pm | 16 ++++++++ lib/NGCP/Panel/Model/CA.pm | 30 +++++++++++--- lib/NGCP/Panel/Utils/DateTime.pm | 38 +++++++---------- t/api-rest/api-cert-auth.t | 48 +++++++++++++++++----- 4 files changed, 93 insertions(+), 39 deletions(-) diff --git a/lib/NGCP/Panel/Controller/Administrator.pm b/lib/NGCP/Panel/Controller/Administrator.pm index 5d070161bb..262b4abab1 100644 --- a/lib/NGCP/Panel/Controller/Administrator.pm +++ b/lib/NGCP/Panel/Controller/Administrator.pm @@ -279,6 +279,22 @@ sub api_key :Chained('base') :PathPart('api_key') :Args(0) { $serial++; }; } + } elsif ($c->req->body_parameters->{'ca.verify'} || $c->req->parameters->{'ca.verify'}) { + my $result = $c->model('CA')->check_ca_errors($c); + if($result){ + NGCP::Panel::Utils::Message::error( + c => $c, + error => $result, + data => { $c->stash->{administrator}->get_inflated_columns }, + desc => $c->loc('CA certificate verification failed: '.$result), + ); + }else{ + NGCP::Panel::Utils::Message::info( + c => $c, + desc => $c->loc('CA certificate is OK'), + ); + } + NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for('/administrator')); } elsif ($c->req->body_parameters->{'del.delete'}) { undef $serial; undef $cert; diff --git a/lib/NGCP/Panel/Model/CA.pm b/lib/NGCP/Panel/Model/CA.pm index 6f82d99578..509c1e34c9 100644 --- a/lib/NGCP/Panel/Model/CA.pm +++ b/lib/NGCP/Panel/Model/CA.pm @@ -29,11 +29,8 @@ sub COMPONENT { sub make_client { my ($self, $c, $serial) = @_; my $client_key = Path::Tiny->tempfile; - my $command = 'openssl x509 -noout -purpose -in ' . $c->config->{ssl}->{rest_api_certfile}; - $c->log->debug($command); - my $stdout = `$command 2>&1` // ""; - unless ($stdout =~ m/SSL (client|server) CA : Yes/) { - $c->log->error("Failed to check CA certificate: $stdout"); + my($command,$stdout); + if($self->check_ca_errors($c)){ die [$c->loc('Cannot use the configured certificate for signing client certificates'), "showdetails"]; } $command = sprintf 'certtool -p --bits 3248 --outfile %s 1>&- 2>&-', $client_key->stringify; @@ -60,6 +57,29 @@ sub make_client { return $cert; } +sub check_ca_errors { + my ($self, $c) = @_; + my($command,$stdout,$error); + $command = 'openssl x509 -noout -purpose -in ' . $c->config->{ssl}->{rest_api_certfile}; + $c->log->debug($command); + $stdout = `$command 2>&1` // ""; + unless ($stdout =~ m/SSL (client|server) CA : Yes/) { + $error = "Failed to check CA certificate: $stdout"; + } + $command = 'openssl verify ' . $c->config->{ssl}->{rest_api_certfile}; + #$command = 'openssl verify /etc/ngcp-config/ssl/client-auth-ca.crt'; + $c->log->debug($command); + $stdout = `$command 2>&1` // ""; + if ($stdout =~ m/certificate has expired/) { + $error = "Failed to check CA certificate: expired: $stdout"; + } + if($error){ + $c->log->error($error); + return $error; + } + return 0; +} + sub make_pkcs12 { my ($self, $c, $serial, $cert, $pass) = @_; diff --git a/lib/NGCP/Panel/Utils/DateTime.pm b/lib/NGCP/Panel/Utils/DateTime.pm index 9efa5ec30a..d882cdd38f 100644 --- a/lib/NGCP/Panel/Utils/DateTime.pm +++ b/lib/NGCP/Panel/Utils/DateTime.pm @@ -114,6 +114,7 @@ sub epoch_local { epoch => $epoch, ); } + sub from_string { my $s = shift; @@ -132,13 +133,10 @@ sub from_string { sub from_rfc1123_string { my $s = shift; - my $strp = DateTime::Format::Strptime->new(pattern => RFC_1123_FORMAT_PATTERN, - locale => 'en_US', - on_error => 'undef'); - + locale => 'en_US', + on_error => 'undef'); return $strp->parse_datetime($s); - } sub new_local { @@ -154,8 +152,7 @@ sub new_local { } # convert seconds to 'HH:MM:SS' format -sub sec_to_hms -{ +sub sec_to_hms { use integer; local $_ = shift; my ($h, $m, $s); @@ -165,8 +162,7 @@ sub sec_to_hms return "$h:$m:$s"; } -sub to_string -{ +sub to_string { my ($dt) = @_; return unless defined ($dt); my $s = $dt->ymd('-') . ' ' . $dt->hms(':'); @@ -175,28 +171,24 @@ sub to_string } sub to_rfc1123_string { - my $dt = shift; - my $strp = DateTime::Format::Strptime->new(pattern => RFC_1123_FORMAT_PATTERN, - locale => 'en_US', - on_error => 'undef'); - + locale => 'en_US', + on_error => 'undef'); return $strp->format_datetime($dt); - } sub get_weekday_names { my $c = shift; return [ - $c->loc('Monday'), - $c->loc('Tuesday'), - $c->loc('Wednesday'), - $c->loc('Thursday'), - $c->loc('Friday'), - $c->loc('Saturday'), - $c->loc('Sunday') - ]; + $c->loc('Monday'), + $c->loc('Tuesday'), + $c->loc('Wednesday'), + $c->loc('Thursday'), + $c->loc('Friday'), + $c->loc('Saturday'), + $c->loc('Sunday') + ]; } 1; diff --git a/t/api-rest/api-cert-auth.t b/t/api-rest/api-cert-auth.t index a29cca9242..10659ded6a 100644 --- a/t/api-rest/api-cert-auth.t +++ b/t/api-rest/api-cert-auth.t @@ -31,6 +31,7 @@ unless ($valid_ssl_client_cert && $ssl_ca_cert) { ($valid_ssl_client_cert, $ssl_ca_cert) = _download_certs($uri); $valid_ssl_client_key = $valid_ssl_client_cert; } +my $ca_verify_error = _verify_ca($uri); my ($ua, $res); $ua = LWP::UserAgent->new; @@ -49,7 +50,6 @@ SKIP: { is($res->code, 400, "check invalid client certificate") || note ($res->message); } - SKIP: { unless ( $unauth_ssl_client_cert && (-e $unauth_ssl_client_cert) ) { skip ("Skip unauthorized client certificate, we have none", 1); @@ -66,15 +66,21 @@ SKIP: { } # successful auth -$ua->ssl_opts( - SSL_cert_file => $valid_ssl_client_cert, - SSL_key_file => $valid_ssl_client_key, - SSL_verify_mode => 0, - verify_hostname => 0, -); -$res = $ua->get($uri.'/api/'); -is($res->code, 200, "check valid client certificate") - || note ($res->message); +SKIP: { + if($ca_verify_error){ + skip ("Skip valid certificate test: CA has errors: $ca_verify_error", 1); + }else{ + $ua->ssl_opts( + SSL_cert_file => $valid_ssl_client_cert, + SSL_key_file => $valid_ssl_client_key, + SSL_verify_mode => 0, + verify_hostname => 0, + ); + $res = $ua->get($uri.'/api/'); + is($res->code, 200, "check valid client certificate") + || note ($res->message); + } +} #my @links = $res->header('Link'); #ok(grep /^<\/api\/contacts\/>; rel="collection /, @links); @@ -82,12 +88,19 @@ is($res->code, 200, "check valid client certificate") done_testing; -sub _download_certs { +sub _prepare_ua { my ($uri) = @_; my ($ua, $req, $res); $ua = LWP::UserAgent->new(cookie_jar => {}, ssl_opts => {verify_hostname => 0, SSL_verify_mode => 0}); $res = $ua->post($uri.'/login/admin', {username => 'administrator', password => 'administrator'}, 'Referer' => $uri.'/login/admin'); $res = $ua->get($uri.'/dashboard/'); + return $ua; +} + +sub _download_certs { + my ($uri) = @_; + my ($ua, $req, $res); + $ua = _prepare_ua($uri); $res = $ua->get($uri.'/administrator/1/api_key'); if ($res->decoded_content =~ m/gen\.generate/) { # key need to be generated first $res = $ua->post($uri.'/administrator/1/api_key', {'gen.generate' => 'foo'}, 'Referer' => $uri.'/dashboard'); @@ -100,4 +113,17 @@ sub _download_certs { return ($tmp_apiclient_filename, $tmp_apica_filename); } +sub _verify_ca { + my ($uri) = @_; + my ($ua, $req, $res); + $ua = _prepare_ua($uri); + $res = $ua->get($uri.'/administrator/1/api_key?ca.verify=1', 'Referer' => $uri.'/dashboard'); + my $content = $res->decoded_content; + if($content !~ /CA certificate is OK/i){ + (my ($error)) = $res->decoded_content =~/
(.*?)<\/div>/ism; + return $error; + } + return; +} + # vim: set tabstop=4 expandtab: