diff --git a/src/contrib/warden-client-nosoap/lib/WardenClientCommon.pm b/src/contrib/warden-client-nosoap/lib/WardenClientCommon.pm index db5cd580d20ec84362f80c28e8d5219e26f90993..f9d4a0ec0b086ebe2476b187286f94cc15156f0c 100755 --- a/src/contrib/warden-client-nosoap/lib/WardenClientCommon.pm +++ b/src/contrib/warden-client-nosoap/lib/WardenClientCommon.pm @@ -2,7 +2,7 @@ # # WardenClientCommon.pm # -# Copyright (C) 2011-2013 Cesnet z.s.p.o +# Copyright (C) 2011-2012 Cesnet z.s.p.o # # Use of this source is governed by a BSD-style license, see LICENSE file. @@ -10,9 +10,14 @@ package WardenClientCommon; use strict; use Carp; -use SOAP::Lite; -use IO::Socket::SSL qw(debug1); -use SOAP::Transport::HTTP; + +use Net::SSLeay qw(post_https make_headers make_form + load_error_strings + set_cert_and_key set_fd + get_peer_certificate ssl_write_all + print_errs die_if_ssl_error die_now ssl_read_all); +use XML::Parser; +use Data::Dumper; our $VERSION = "2.2"; @@ -50,6 +55,164 @@ sub errMsg } # End of errMsg +#------------------------------------------------------------------------------- +# https_send_receive - send $out_message, return (data, http error, headers) +#------------------------------------------------------------------------------- + +sub https_send_receive +{ + my ($dest_serv, $port, $out_message, $crt_path, $key_path, $ca_path) = @_; + my ($ctx, $ssl, $res, $errs); + + ($res, $errs) = Net::SSLeay::open_tcp_connection($dest_serv, $port); + goto final unless $res; + + # SSLeay init + load_error_strings(); + Net::SSLeay::SSLeay_add_ssl_algorithms(); + Net::SSLeay::randomize(); + + # Setup and configure protocol context + $ctx = Net::SSLeay::new_x_ctx(); + goto cleanup2 if $errs = print_errs('CTX_new') or !$ctx; + + Net::SSLeay::CTX_set_options($ctx, &Net::SSLeay::OP_ALL); + goto cleanup2 if $errs = print_errs('CTX_set_options'); + + # Setup certificates + warn "Cert `$crt_path' given without key" if $crt_path && !$key_path; + set_cert_and_key($ctx, $crt_path, $key_path) if $crt_path; + + # Set allowed CAs and require Warden server peer verify + Net::SSLeay::CTX_load_verify_locations($ctx, $ca_path, ''); + goto cleanup2 if $errs = print_errs('CTX_load_verify_locations'); + Net::SSLeay::CTX_set_verify($ctx, &Net::SSLeay::VERIFY_PEER, 0); + goto cleanup2 if $errs = print_errs('CTX_set_verify'); + + # Create SSL session + $ssl = Net::SSLeay::new($ctx); + goto cleanup if $errs = print_errs('SSL_new') or !$ssl; + + # Attach filehandle and connect + set_fd($ssl, fileno(Net::SSLeay::SSLCAT_S)); + goto cleanup if $errs = print_errs('set_fd'); + + $res = Net::SSLeay::connect($ssl); + goto cleanup_shut if $errs = print_errs('SSL_connect'); + + # Ok, exchange data + ($res, $errs) = ssl_write_all($ssl, $out_message); + goto cleanup_shut unless $res; + + ($res, $errs) = ssl_read_all($ssl); + +cleanup_shut: + shutdown Net::SSLeay::SSLCAT_S, 2; + +cleanup: + Net::SSLeay::free ($ssl); + $errs .= print_errs('SSL_free'); + +cleanup2: + Net::SSLeay::CTX_free ($ctx); + $errs .= print_errs('CTX_free'); + close Net::SSLeay::SSLCAT_S; + +final: + $res = "HTTP/1.0 900 NET OR SSL ERROR\r\n\r\n$errs\r\n\r\n$res" if $errs; + + my ($prolog, $data) = split /\s?\n\s?\n/, $res, 2; + my ($ret, $headers) = split /\s?\n/, $prolog, 2; + + return wantarray ? ($data, $ret, $headers) : $data; +} + +#------------------------------------------------------------------------------- +# expat2hash - convert convoluted expat list of lists to simpler hash +#------------------------------------------------------------------------------- +sub expat2hash { + my $l = shift; + + # send XML attributes to the fiery pits of hell + if (ref($l->[0]) eq "HASH") { + shift(@{$l}); + } + + my $res = {}; + while (scalar(@{$l})!=0) { + my $key = shift(@{$l}); + my $value = shift(@{$l}); + if (ref($value) eq "ARRAY") { + # process sublists recursively + $value = expat2hash($value); + } + push(@{$res->{$key}}, $value); + }; + + # flatten list singles + # special case warden events to simplify access, we want + # them in list even if only single event arrives + for my $key (keys %{$res}) { + if (scalar(@{$res->{$key}})==1 and not ($key eq 'event')) { + $res->{$key} = $res->{$key}->[0]; + } + } + + # nil values yield empty hashes now + if (!%{$res}) { + undef $res; + } + # flatten XML text node singles + elsif (scalar(keys(%{$res}))==1 and exists $res->{'0'}) { + $res = $res->{'0'}; + }; + + return $res; +} + +#------------------------------------------------------------------------------- +# hash2soapquery - convert parameters from hash into SOAP query payload +#------------------------------------------------------------------------------- +sub hash2soapquery { + my ($server, $port, $service, $method, $data) = @_; + my @datalist; + + while (my ($key, $value) = each %{$data} ) + { + if (!defined $value) { + push @datalist, "<$key xsi:nil=\"true\" />" + } elsif ($value =~ /^[+-]?\d+\z/) { + push @datalist, "<$key xsi:type=\"xsd:int\">$value</$key>" + } else { + push @datalist, "<$key xsi:type=\"xsd:string\">$value</$key>" + } + } + + my $payload = + '<?xml version="1.0" encoding="UTF-8"?>' . + '<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">' . + '<soap:Body>' . + "<$method xmlns=\"Warden\">" . + '<request>' . + join("", @datalist) . + '</request>' . + "</$method>" . + '</soap:Body>' . + '</soap:Envelope>'; + + my $paylen = length($payload); + + my $header = + "POST /$service HTTP/1.1\r\n" . + "Host: $server:$port\r\n" . + "Content-Type: application/soap+xml; charset=utf-8\r\n" . + "Content-Length: $paylen\r\n" . + "SOAPAction: \"http://www.w3.org/2003/05/soap-envelope\"\r\n" . + "\r\n"; + + return $header . $payload; +} + #------------------------------------------------------------------------------- # c2s - connect to server, send request and receive response @@ -65,62 +228,40 @@ sub c2s my $client; my ($server, $port, $service) = $uri =~ /https:\/\/(.+)\:(\d+)\/(.+)/; - eval { - $client = SOAP::Transport::HTTP::Client->new(); - } or return errMsg('Unknown error in c2s() when creating socket, SOAP::Transport::HTTP::Client->new(), ' . $@); - - eval {$client->timeout($WardenClientConf::CONNECTION_TIMEOUT);} - or return errMsg('Unknown error in c2s() when setting socket timeout, ' . $@); - eval { - $client->ssl_opts(verify_hostname => 1, - SSL_use_cert => 1, - SSL_verify_mode => 0x02, - SSL_key_file => $ssl_key_file, - SSL_cert_file => $ssl_cert_file, - SSL_ca_file => $ssl_ca_file); - return 1; # fix of eval triggering 'or' statement - } or return errMsg('Unknown error in c2s() when setting socket SSL options, ' . $@); - - # setting of URI and serialize SOAP envelope and data object - - my $soap; - eval { - $soap = SOAP::Lite->uri($service)->proxy($uri); - } or return errMsg('Unknown error in c2s() when serializing SOAP object, ' . $@); - - my $envelope; - if (!defined $data) { - eval { - $envelope = $soap->serializer->envelope(method => $method); - } or return errMsg('Unknown error in c2s() when setting enevelope, ' . $@); - } else { - eval { - $envelope = $soap->serializer->envelope(method => $method, $data); - } or return errMsg('Unknown error in c2s() when setting envelope, ' . $@); - } + # format HTTP request + my $message = hash2soapquery($server, $port, $service, $method, $data); + + my ($res, $err); - # setting of TCP URI and send serialized SOAP envelope and data - my $server_uri = "https://$server:$port/$service"; - my $result; eval { - $result = $client->send_receive(envelope => $envelope, endpoint => $server_uri); + ($res, $err, undef) = https_send_receive($server, $port, $message, $ssl_cert_file, $ssl_key_file, $ssl_ca_file) } or return errMsg('Unknown error in c2s() sending SOAP data, ' . $@); + # check HTTP error + my ($code) = $err =~ /[^ ]* ([^ ]*) /; + if ($code != "200") { + return errMsg("Server returned HTTP error: $err."); + } + # check server response - if (!defined $result) { - errMsg("Server returned empty response. Problem with used SSL ceritificates or Warden server at $server:$port is down."); - } else { - # deserialized response from server -> create SOAP envelope and data object - - my $response; - - eval { - $response = $soap->deserializer->deserialize($result); - } or return errMsg('Unknown error in SOAP data deserialization. Received data: ' . $result . ', ' . $@); - # check SOAP fault status - $response->fault ? return errMsg("Server sent error message:: " . $response->faultstring) : return $response; + if (!defined $res) { + return errMsg("Server returned empty response. Problem with used SSL ceritificates or Warden server at $server:$port is down."); } + # deserialize response from server + my $result; + eval { + my $parser = XML::Parser->new(Style => "Tree", ErrorContext => 2); + $result = expat2hash($parser->parse($res)); + } or return errMsg('Unknown error in SOAP data deserialization. Received data: ' . $res . ', ' . $@); + + # Flatten unneeded branches if structure is sane + unless (exists($result->{'soap:Envelope'}) and exists($result->{'soap:Envelope'}->{'soap:Body'})) { + return errMsg('Server returned unexpected SOAP response. Received data: ' . $res); + } + $result = $result->{'soap:Envelope'}->{'soap:Body'}; + + return $result; } #------------------------------------------------------------------------------- diff --git a/src/contrib/warden-client-nosoap/lib/WardenClientReceive.pm b/src/contrib/warden-client-nosoap/lib/WardenClientReceive.pm index 14c012b55c877061e25c7398d389594944bb2cc5..d8a6b9cecbc898a87e5f326d9935902c2664152c 100755 --- a/src/contrib/warden-client-nosoap/lib/WardenClientReceive.pm +++ b/src/contrib/warden-client-nosoap/lib/WardenClientReceive.pm @@ -2,16 +2,13 @@ # # WardenClientReceive.pm # -# Copyright (C) 2011-2013 Cesnet z.s.p.o +# Copyright (C) 2011-2012 Cesnet z.s.p.o # # Use of this source is governed by a BSD-style license, see LICENSE file. package WardenClientReceive; use strict; -use SOAP::Lite; -use IO::Socket::SSL qw(debug1); -use SOAP::Transport::HTTP; use FindBin; use Sys::Syslog; @@ -57,7 +54,9 @@ sub getNewEvents my $response = WardenClientCommon::c2s($WardenClientConf::URI, $WardenClientConf::SSL_KEY_FILE, $WardenClientConf::SSL_CERT_FILE, $WardenClientConf::SSL_CA_FILE, "getLastId"); defined $response or return; # receive data or return undef - $last_id = $response->result; + # SOAP generates random names for unnamed return values, we sidestep through array + my @last_id = values(%{$response->{'getLastIdResponse'}}); + $last_id = $last_id[0]; open(ID, "> $id_file") or return WardenClientCommon::errMsg("Cannot open ID file $id_file: $!"); print ID $last_id; close ID; @@ -66,29 +65,23 @@ sub getNewEvents #----------------------------------------------------------------------------- # get new events from warden server DB based on gathered last ID - my $request_data; - eval { - # create SOAP data object - $request_data = SOAP::Data->name( - request => \SOAP::Data->value( - SOAP::Data->name(REQUESTED_TYPE => $requested_type), - SOAP::Data->name(LAST_ID => $last_id), - SOAP::Data->name(MAX_RCV_EVENTS_LIMIT => $WardenClientConf::MAX_RCV_EVENTS_LIMIT) - ) - ) - } or return errMsg('Unknown error when creating SOAP data object, ' . $@); + my %request_data = ( + "REQUESTED_TYPE" => $requested_type, + "LAST_ID" => $last_id, + "MAX_RCV_EVENTS_LIMIT" => $WardenClientConf::MAX_RCV_EVENTS_LIMIT + ); # call server method getNewEvents - my $response = WardenClientCommon::c2s($WardenClientConf::URI, $WardenClientConf::SSL_KEY_FILE, $WardenClientConf::SSL_CERT_FILE, $WardenClientConf::SSL_CA_FILE, "getNewEvents", $request_data); + my $response = WardenClientCommon::c2s($WardenClientConf::URI, $WardenClientConf::SSL_KEY_FILE, $WardenClientConf::SSL_CERT_FILE, $WardenClientConf::SSL_CA_FILE, "getNewEvents", \%request_data); defined $response or return; # connect to warden server or return undef # parse returned SOAP data object my ($id, $hostname, $service, $detected, $type, $source_type, $source, $target_proto, $target_port, $attack_scale, $note, $priority, $timeout); - my @response_list = $response->valueof('/Envelope/Body/getNewEventsResponse/event/'); + my $response_list = ($response->{'getNewEventsResponse'}->{'event'} or []); - while (scalar @response_list) { - my $response_data = shift(@response_list); + while (scalar @{$response_list}) { + my $response_data = shift(@{$response_list}); my @event; # parse items of one event diff --git a/src/contrib/warden-client-nosoap/lib/WardenClientSend.pm b/src/contrib/warden-client-nosoap/lib/WardenClientSend.pm index 793095ec39546bacfbdd2b7ccd7ba0e3c3792754..bbddea82b00acfd1bcffe317e65f725d41e0cd4a 100755 --- a/src/contrib/warden-client-nosoap/lib/WardenClientSend.pm +++ b/src/contrib/warden-client-nosoap/lib/WardenClientSend.pm @@ -2,16 +2,13 @@ # # WardenClientSend.pm # -# Copyright (C) 2011-2013 Cesnet z.s.p.o +# Copyright (C) 2011-2012 Cesnet z.s.p.o # # Use of this source is governed by a BSD-style license, see LICENSE file. package WardenClientSend; use strict; -use SOAP::Lite; -use IO::Socket::SSL qw(debug1); -use SOAP::Transport::HTTP; use Sys::Syslog; our $VERSION = "2.2"; @@ -23,71 +20,63 @@ sub saveNewEvent { my $result; - my $warden_path = shift; - my $event_ref = shift; - - my $etcdir = $warden_path . "/etc/"; - my $libdir = $warden_path . "/lib/"; - - require $libdir . "WardenClientConf.pm"; - require $libdir . "WardenClientCommon.pm"; - - # read the config file - my $conf_file = $etcdir . "warden-client.conf"; - WardenClientConf::loadConf($conf_file); - - # prepare variables of event - my @event = @{$event_ref}; - my $service = $event[0]; - my $detected = $event[1]; - my $type = $event[2]; - my $source_type = $event[3]; - my $source = $event[4]; - my $target_proto = $event[5]; - my $target_port = $event[6]; - my $attack_scale = $event[7]; - my $note = $event[8]; - my $priority = $event[9]; - my $timeout = $event[10]; - - # Issue #596 - Should be removed in Warden client 3.0. - # Checking for obsolete attributes priority or timeout. If not default or 'undef' values are found, print out warning. - - # check if obsolete event attribute Priority is used - if ((defined $priority) && ($priority >= 1)) { - # print warning - WardenClientCommon::errMsg('Event attribute "Priority" is now obsolete and will be removed in Warden client 3.0', 'warn'); - } - - # check if obsolete event attribute Timeout is used - if ((defined $timeout) && ($timeout >= 0)) { - # print warning - WardenClientCommon::errMsg('Event attribute "Timeout" is now obsolete and will be removed in Warden client 3.0', 'warn'); - } - - # end of Issue #596 + my $warden_path = shift; + my $event_ref = shift; + + my $etcdir = $warden_path . "/etc/"; + my $libdir = $warden_path . "/lib/"; + + require $libdir . "WardenClientConf.pm"; + require $libdir . "WardenClientCommon.pm"; + + # read the config file + my $conf_file = $etcdir . "warden-client.conf"; + WardenClientConf::loadConf($conf_file); + + # prepare variables of event + my @event = @{$event_ref}; + my $service = $event[0]; + my $detected = $event[1]; + my $type = $event[2]; + my $source_type = $event[3]; + my $source = $event[4]; + my $target_proto = $event[5]; + my $target_port = $event[6]; + my $attack_scale = $event[7]; + my $note = $event[8]; + my $priority = $event[9]; + my $timeout = $event[10]; + + # Issue #596 - Should be removed in Warden client 3.0. + # Checking for obsolete attributes priority or timeout. If not default or 'undef' values are found, print out warning. + + # check if obsolete event attribute Priority is used + if ((defined $priority) && ($priority >= 1)) { + # print warning + WardenClientCommon::errMsg('Event attribute "Priority" is now obsolete and will be removed in Warden client 3.0', 'warn'); + } + + # check if obsolete event attribute Timeout is used + if ((defined $timeout) && ($timeout >= 0)) { + # print warning + WardenClientCommon::errMsg('Event attribute "Timeout" is now obsolete and will be removed in Warden client 3.0', 'warn'); + } + + # end of Issue #596 - - my $event; - eval { - # create SOAP data object - $event = SOAP::Data->name( - event => \SOAP::Data->value( - SOAP::Data->name(SERVICE => $service), - SOAP::Data->name(DETECTED => $detected), - SOAP::Data->name(TYPE => $type), - SOAP::Data->name(SOURCE_TYPE => $source_type), - SOAP::Data->name(SOURCE => $source), - SOAP::Data->name(TARGET_PROTO => $target_proto), - SOAP::Data->name(TARGET_PORT => $target_port), - SOAP::Data->name(ATTACK_SCALE => $attack_scale), - SOAP::Data->name(NOTE => $note), - SOAP::Data->name(PRIORITY => $priority), - SOAP::Data->name(TIMEOUT => $timeout) - ) - ); - } # end of eval - or WardenClientCommon::errMsg('Unknown error when creating SOAP data object, ' . $@); + my $event = { + SERVICE => $service, + DETECTED => $detected, + TYPE => $type, + SOURCE_TYPE => $source_type, + SOURCE => $source, + TARGET_PROTO => $target_proto, + TARGET_PORT => $target_port, + ATTACK_SCALE => $attack_scale, + NOTE => $note, + PRIORITY => $priority, + TIMEOUT => $timeout + }; # c2s() returns undef on fail. $result = WardenClientCommon::c2s($WardenClientConf::URI, $WardenClientConf::SSL_KEY_FILE, $WardenClientConf::SSL_CERT_FILE, $WardenClientConf::SSL_CA_FILE, "saveNewEvent", $event);