#!/usr/bin/perl -w # # warden-server.pl # # Copyright (C) 2011-2012 Cesnet z.s.p.o # Author(s): Tomas PLESNIK <plesnik@ics.muni.cz> # Jan SOUKAL <soukal@ics.muni.cz> # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # 3. Neither the name of the Cesnet z.s.p.o nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # This software is provided ``as is'', and any express or implied # warranties, including, but not limited to, the implied warranties of # merchantability and fitness for a particular purpose are disclaimed. # In no event shall the Cesnet z.s.p.o or contributors be liable for # any direct, indirect, incidental, special, exemplary, or consequential # damages (including, but not limited to, procurement of substitute # goods or services; loss of use, data, or profits; or business # interruption) however caused and on any theory of liability, whether # in contract, strict liability, or tort (including negligence or # otherwise) arising in any way out of the use of this software, even # if advised of the possibility of such damage. package Warden; use strict; use SOAP::Lite; use SOAP::Transport::TCP; use File::Pid; use POSIX; use DBI; use Format::Human::Bytes; use Sys::Syslog qw(:DEFAULT setlogsock); Sys::Syslog::setlogsock('unix'); use File::Basename; use FindBin; use Data::Dumper; use Net::CIDR::Lite; use DateTime; our $VERSION = "0.1"; ################################################################################ # CONFIG FILE VARIABLES ################################################################################ my $script_name = $FindBin::Script; my $conf_file = "/opt/warden-server/etc/warden-server.conf"; # first declaration of globa variables from config file our $ADDRESS = undef; our $PORT = undef; our $LOGDIR = undef; our $PIDDIR = undef; our $VARDIR = undef; our $SSL_KEY_FILE = undef; our $SSL_CERT_FILE = undef; our $SSL_CA_FILE = undef; our $FACILITY = undef; # read config file if ( ! open( TMP, $conf_file) ) { die errMsg("Can't read config file '$conf_file': $!\n"); } close TMP; # load set variables by user if ( !do $conf_file ) { die errMsg("Errors in config file '$conf_file': $@"); } ################################################################################ # VARIABLES ################################################################################ my $die_now = 0; # PID path my $pid_file = $PIDDIR . $script_name . ".pid"; # DB file my $db_file = "warden.db"; my $db = $VARDIR . $db_file; # connect to DB - DBH is GLOBAL variable my $dbargs = {AutoCommit => 0, PrintError => 1}; our $DBH = DBI->connect("dbi:SQLite:dbname=$db","","",$dbargs) or die errMsg("Can't connect to DB: $!"); #our $DBH = DBI->connect("DBI:mysql:database=warden;host=localhost", "root", "", {RaiseError => 1, mysql_auto_reconnect => 1}) || die "Could not connect to database: $DBI::errstr"; ################################################################################ # LOCAL FUNCTIONS ################################################################################ #------------------------------------------------------------------------------- # errMsg - print error message and die #------------------------------------------------------------------------------- sub errMsg { my $msg = shift; $msg = trim($msg); print $msg . "\n"; exit 1; } # End of errMsg #------------------------------------------------------------------------------- # trim - remove whitespace from the start and end of the string #------------------------------------------------------------------------------- sub trim { my $string = shift; $string =~ s/^\s+//; $string =~ s/\s+$//; return $string; } # End of trim #------------------------------------------------------------------------------- # write2log - writing message to syslog #------------------------------------------------------------------------------- sub write2log { my $priority = shift; my $msg = shift; my $filename = File::Basename::basename($0); Sys::Syslog::openlog($filename, "cons,pid", $FACILITY); Sys::Syslog::syslog("$priority", "$msg"); Sys::Syslog::closelog(); } # End of write2log #------------------------------------------------------------------------------- # signalHandler - catch signals and end the program if one is caught. #------------------------------------------------------------------------------- sub signalHandler { $die_now = 1; # this will cause the "infinite loop" to exit } # End of signalHandler #------------------------------------------------------------------------------- # sslErrorHandler - handle errors in SSL negitiation #------------------------------------------------------------------------------- sub sslErrorHandler { my $socket = shift; my $msg = shift; my $ip = $socket->peerhost; print $socket $msg; $socket->close; write2log ("err", "Caught SSL handshake error from $ip: $msg"); return 1; } # End of sslErrorHandler #------------------------------------------------------------------------------- # altNamesFilter - parse hostnames from subjectAltNames array for SQL # IN operator in database query #------------------------------------------------------------------------------- sub altNamesFilter { my $alt_names_array_ref = shift; my @alt_names_array = @$alt_names_array_ref; our $CN; my @an_array; push @an_array, $DBH->quote($CN); my $i = 1; while ($i <= scalar @alt_names_array) { push @an_array, $DBH->quote($alt_names_array[$i]); $i+=2; } my $an_filter = join(',', @an_array); return $an_filter; } ################################################################################ # SOAP Functions ################################################################################ #----------------------------------------------------------------------------- # saveNewEvent - save new received event into database #----------------------------------------------------------------------------- sub saveNewEvent { my ($class, $data) = @_; my ($sth, $cidr_list); # variables defined by server our $IP; # IP address of sender our $CN; # common name of sender our $AN_FILTER; # alternate names of sender my $cn_db = $DBH->quote($CN); # variables defined by server my $client_type = "s"; # incoming client MUST be sender my $client_type_db = $DBH->quote($client_type); my $valid = "t"; # registered sender has valid events my $valid_db = $DBH->quote($valid); my $received = DateTime->now; # time of event delivery (UTC) my $received_db = $DBH->quote($received); # parse object (event) parameters my $service = $data->{'SERVICE'}; my $service_db = $DBH->quote($service); my $detected = $data->{'DETECTED'}; my $detected_db = $DBH->quote($detected); my $type = $data->{'TYPE'}; my $type_db = $DBH->quote($type); my $source_type = $data->{'SOURCE_TYPE'}; my $source_type_db = $DBH->quote($source_type); my $source = $data->{'SOURCE'}; my $source_db = $DBH->quote($source); my $target_proto = $data->{'TARGET_PROTO'}; my $target_proto_db = $DBH->quote($target_proto); my $target_port = $data->{'TARGET_PORT'}; my $target_port_db = $DBH->quote($target_port); my $attack_scale = $data->{'ATTACK_SCALE'}; my $attack_scale_db = $DBH->quote($attack_scale); my $note = $data->{'NOTE'}; my $note_db = $DBH->quote($note); my $priority = $data->{'PRIORITY'}; my $priority_db = $DBH->quote($priority); my $timeout = $data->{'TIMEOUT'}; my $timeout_db = $DBH->quote($timeout); # Authorization of incomming client #----------------------------------------------------------------------------- # obtain cidr based on rigth common name and alternate names, service and client_type # $sth = $DBH->prepare("SELECT hostname, ip_net_client FROM clients WHERE hostname IN ($AN_FILTER) AND service = $service_db AND client_type = $client_type_db limit 1;"); # if ( !defined $sth ) {die("Cannot prepare authorization statement in saveNewEvent: $DBI::errstr\n")} # $sth->execute; # my ($an, $cidr) = $sth->fetchrow(); # # check if client is registered # if (!defined $cidr) { # write2log ("err", "Unauthorized access to saveNewEvent from: $IP (CN: $CN; AN: $an) - client is not registered"); # die("Access denied - client is not registered at warden server!"); # } else { # $cidr_list = Net::CIDR::Lite # -> new # -> add($cidr); # } # check if client has IP from registered CIDR # if (!$cidr_list->bin_find($IP)) { # write2log ("err", "Unauthorized access to saveNewEvent from: $IP (CN: $CN; AN: $an) - access from bad subnet: $cidr"); # die("Access denied - access from bad subnet!"); # } else { { # insert new event $DBH->do("INSERT INTO events VALUES (null,$cn_db,$service_db,$detected_db,$received_db,$type_db,$source_type_db,$source_db,$target_proto_db,$target_port_db,$attack_scale_db,$note_db,$priority_db,$timeout_db,$valid_db);"); if ($DBH->err()) {die("Cannot do insert statement in saveNewEvent: $DBI::errstr\n")} #$DBH->commit(); # log last inserted ID $sth = $DBH->prepare("SELECT last_insert_rowid()"); if ( !defined $sth ) {die("Cannot prepare last ID statement in saveNewEvent: $DBI::errstr\n")} $sth->execute; my $id= $sth->fetchrow(); write2log ("info", "Stored new event (#$id) from $IP (CN: $CN; AN: )"); if (! defined $id) { write2log ("err", "Event from $IP ($CN) was not save: INSERT INTO events VALUES (null,$cn_db,$service_db,$detected_db,$received_db,$type_db,$source_type_db,$source_db,$target_proto_db,$target_port_db,$attack_scale_db,$note_db,$priority_db,$timeout_db,$valid_db);"); die("Event was not save at warden server - database return empty ID!"); } else { return 1; } } } # END of saveNewEvent #----------------------------------------------------------------------------- # getNewEvents - get new events from the DB greater than received ID #----------------------------------------------------------------------------- sub getNewEvents { my ($class, $data) = @_; my ($sth, @events, $event, @ids); my ($id, $hostname, $service, $detected, $type, $source_type, $source, $target_proto, $target_port, $attack_scale, $note, $priority, $timeout); # variables defined by server our $IP; # IP address of receiver our $CN; # common name of receiver our $AN_FILTER; # alternate name of receiver my $cn_db = $DBH->quote($CN); my $client_type = "r"; # incoming client MUST be sender my $client_type_db = $DBH->quote($client_type); my $cidr_list; # parse SOAP data object my $requested_type = $data->{'REQUESTED_TYPE'}; my $requested_type_db = $DBH->quote($requested_type); my $last_id = $data->{'LAST_ID'}; my $last_id_db = $DBH->quote($last_id); # Authorization of incomming client #----------------------------------------------------------------------------- # obtain cidr based on rigth common name, service and client_type $sth = $DBH->prepare("SELECT hostname, receive_own_events, ip_net_client FROM clients WHERE hostname IN ($AN_FILTER) AND type = $requested_type_db AND client_type = $client_type_db limit 1;"); if ( !defined $sth ) {die("Cannot prepare authorization statement in getNewEvents: $DBI::errstr\n")} $sth->execute; my ($an, $receive_own_events, $cidr) = $sth->fetchrow(); # check if client is registered if (!defined $cidr) { write2log ("err", "Unauthorized access to getNewEvents from: $IP (CN: $CN; AN: $an) - client is not registered"); die("Access denied - client is not registered at warden server!"); } else { $cidr_list = Net::CIDR::Lite -> new -> add($cidr); } # check if client has IP from registered CIDR if (!$cidr_list->bin_find($IP)) { write2log ("err", "Unauthorized access to getNewEvents from: $IP (CN: $CN; AN: $an) - access from bad subnet: $cidr"); die("Access denied - access from bad subnet!"); } else { # check if client want your own events or not if ($receive_own_events eq 't') { $sth = $DBH->prepare("SELECT * FROM events WHERE type != 'test' AND id > $last_id_db AND type = $requested_type_db AND valid = 't' ORDER BY id ASC;"); } else { my ($domain) = $CN =~ /([^\.]+\.[^\.]+)$/; my $domain_db = $DBH->quote("%$domain"); $sth = $DBH->prepare("SELECT * FROM events WHERE type != 'test' AND id > $last_id_db AND type = $requested_type_db AND valid = 't' AND hostname NOT LIKE $domain_db ORDER BY id ASC;"); } if ( !defined $sth ) { die("Cannot prepare statement in getNewEvents: $DBI::errstr\n") } $sth->execute; # parse items of events stored in DB while (my @result = $sth->fetchrow()) { $id = $result[0]; $hostname = $result[1]; $service = $result[2]; $detected = $result[3]; $type = $result[5]; $source_type = $result[6]; $source = $result[7]; $target_proto = $result[8]; $target_port = $result[9]; $attack_scale = $result[10]; $note = $result[11]; $priority = $result[12]; $timeout = $result[13]; # create SOAP data object $event = SOAP::Data->name(event => \SOAP::Data->value( SOAP::Data->name(ID => $id), SOAP::Data->name(HOSTNAME => $hostname), 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) )); push(@events, $event); push(@ids, $id); } # log sent ID of events if (scalar(@events)!=0) { write2log("info", "Sent events with ID: [@ids] to $IP (CN: $CN; AN: $an)"); } return @events; } } # END of getNewEvents #----------------------------------------------------------------------------- # getLastId - get lastest saved event ID #----------------------------------------------------------------------------- sub getLastId { my ($class, $arg) = @_; my $sth = $DBH->prepare("SELECT max(id) FROM events;"); if ( !defined $sth ) { die("Cannot prepare statement in getLastId: $DBI::errstr\n") } $sth->execute; my $result = $sth->fetchrow(); return $result; } # END of getLastID #----------------------------------------------------------------------------- # registerSender - register new sender #----------------------------------------------------------------------------- sub registerSender { my ($class, $data) = @_; my $sth; our $IP; our $LOCAL_IP; our $CN; if ($LOCAL_IP ne $IP) { write2log ("err", "Unauthorized access to registerSender from: $IP ($CN) - access allowed only from localhost"); die("Access denied - access allowed only from localhost!"); } else { # defined variables by server my $client_type = "s"; my $client_type_db = $DBH->quote($client_type); my $registered = DateTime->now; my $registered_db = $DBH->quote($registered); my $type = "null"; my $type_db = $DBH->quote($type); my $receive_own_events = "null"; my $receive_own_events_db = $DBH->quote($receive_own_events); # parse SOAP data oject my $hostname = $data->{'HOSTNAME'}; my $hostname_db = $DBH->quote($hostname); my $requestor = $data->{'REQUESTOR'}; my $requestor_db = $DBH->quote($requestor); my $service = $data->{'SERVICE'}; my $service_db = $DBH->quote($service); my $description_tags = $data->{'DESCRIPTION_TAGS'}; my $description_tags_db = $DBH->quote($description_tags); my $ip_net_client = $data->{'IP_NET_CLIENT'}; my $ip_net_client_db = $DBH->quote($ip_net_client); # check if sender has been already registered $sth = $DBH->prepare("SELECT registered FROM clients WHERE hostname = $hostname_db AND requestor = $requestor_db AND service = $service_db AND client_type = $client_type_db AND type = $type_db AND receive_own_events = $receive_own_events_db AND description_tags = $description_tags_db AND ip_net_client = $ip_net_client_db;"); if ( !defined $sth ) {die("Cannot prepare check statement in registerSender: $DBI::errstr\n")} $sth->execute; my $result = $sth->fetchrow(); # register new sender if (defined $result) { write2log ("err", "Attempt to re-register the sender"); die("Error - sender has already been registered at $result"); } else { $DBH->do("INSERT INTO clients VALUES (null,$hostname_db,$registered_db,$requestor_db,$service_db,$client_type_db,$type_db,$receive_own_events_db,$description_tags_db,$ip_net_client_db);"); if ($DBH->err()) {die("Cannot do statement in registerSender: $DBI::errstr\n")} $DBH->commit(); write2log("info", "New sender $hostname (service: $service, cidr: $ip_net_client) was registered"); return 1; } } } # END of registerSender #----------------------------------------------------------------------------- # registerReceiver - register new receiver #----------------------------------------------------------------------------- sub registerReceiver { my ($class, $data) = @_; my $sth; our $IP; our $LOCAL_IP; our $CN; if ($LOCAL_IP ne $IP) { write2log ("err", "Unauthorized access to registerReceiver from: $IP ($CN) - access allowed only from localhost"); die("Access denied - access allowed only from localhost!"); } else { # variables defined by server my $client_type = "r"; my $client_type_db = $DBH->quote($client_type); my $registered = DateTime->now; my $registered_db = $DBH->quote($registered); my $service = "null"; my $service_db = $DBH->quote($service); my $description_tags = "null"; my $description_tags_db = $DBH->quote($description_tags); # parse SOAP data oject my $hostname = $data->{'HOSTNAME'}; my $hostname_db = $DBH->quote($hostname); my $requestor = $data->{'REQUESTOR'}; my $requestor_db = $DBH->quote($requestor); my $type = $data->{'TYPE'}; my $type_db = $DBH->quote($type); my $receive_own_events = $data->{'RECEIVE_OWN_EVENTS'}; my $receive_own_events_db = $DBH->quote($receive_own_events); my $ip_net_client = $data->{'IP_NET_CLIENT'}; my $ip_net_client_db = $DBH->quote($ip_net_client); # check if receiver has been already registered $sth = $DBH->prepare("SELECT registered FROM clients WHERE hostname = $hostname_db AND requestor = $requestor_db AND service = $service_db AND client_type = $client_type_db AND type = $type_db AND receive_own_events = $receive_own_events_db AND description_tags = $description_tags_db AND ip_net_client = $ip_net_client_db;"); if ( !defined $sth ) {die("Cannot prepare check statement in registerReceiver: $DBI::errstr\n")} $sth->execute; my $result = $sth->fetchrow(); # register new receiver if (defined $result) { write2log ("err", "Attempt to re-register the receiver"); die("Error - receiver has already been registered at $result"); } else { $DBH->do("INSERT INTO clients VALUES (null,$hostname_db,$registered_db,$requestor_db,$service_db,$client_type_db,$type_db,$receive_own_events_db,$description_tags_db,$ip_net_client_db);"); if ($DBH->err()) {die("Cannot do statement in registerReceiver: $DBI::errstr\n")} $DBH->commit(); write2log("info", "New receiver $hostname (type: $type, cidr: $ip_net_client: receive_own_events: $receive_own_events) was registered"); return 1; } } } # END of registerReceiver #----------------------------------------------------------------------------- # unregisterClient - unregister client #----------------------------------------------------------------------------- sub unregisterClient { my ($class, $data) = @_; my $sth; our $IP; our $LOCAL_IP; our $CN; if ($LOCAL_IP ne $IP) { write2log ("err", "Unauthorized access to unregisterClients from: $IP ($CN) - access allowed only from localhost"); die("Access denied - access allowed only from localhost!"); } else { # parse SOAP data oject my $client_id = $data->{'CLIENT_ID'}; my $client_id_db = $DBH->quote($client_id); # check if receiver has been already registered $sth = $DBH->prepare("SELECT client_id, hostname, service, client_type FROM clients WHERE client_id = $client_id_db;"); if ( !defined $sth ) {die("Cannot prepare check statement in unregisterClient: $DBI::errstr\n")} $sth->execute; my ($id, $hostname, $service, $client_type) = $sth->fetchrow(); my $hostname_db = $DBH->quote($hostname); my $service_db = $DBH->quote($service); # delete registered client if (!defined $id) { write2log ("err", "Attempt to delete unregister client"); die("Error - client (#$client_id) is not registered"); } else { if ($client_type eq 's') { $DBH->do("DELETE FROM clients WHERE client_id = $client_id_db;"); if ($DBH->err()) {die("Cannot do delete statement of sender in unregisterClient: $DBI::errstr\n")} $DBH->commit(); $DBH->do("UPDATE events SET valid = 'f' where hostname = $hostname_db AND service = $service_db;"); if ($DBH->err()) {die("Cannot do unvalidation statement in unregisterClient: $DBI::errstr\n")} $DBH->commit(); write2log("info", "Sender $hostname (client_id: $client_id, service: $service) was deleted and its data were invalidated" ); return 1; } else { $DBH->do("DELETE FROM clients WHERE client_id = $client_id_db;"); if ($DBH->err()) {die("Cannot do delete statement of receiver in unregisterClient: $DBI::errstr\n")} $DBH->commit(); write2log("info", "Receiver $hostname (client_id: $client_id) was deleted" ); return 1; } } } } # END of unregisterClient #----------------------------------------------------------------------------- # getClients - get list of clients which were registered at warden server #----------------------------------------------------------------------------- sub getClients { my ($class, $arg) = @_; our $IP; our $LOCAL_IP; our $CN; if ($LOCAL_IP ne $IP) { write2log ("err", "Unauthorized access to getClients from: $IP ($CN) - access allowed only from localhost"); die("Access denied - access allowed only from localhost!"); } else { my (@clients, $client); my ($client_id, $hostname, $registered, $requestor, $service, $client_type, $type, $receive_own_events, $description_tags, $ip_net_client); my $sth = $DBH->prepare("SELECT * FROM clients;"); if (!defined $sth) { die("Cannot prepare statement in getClients: $DBI::errstr\n") } $sth->execute; while ( my @result = $sth->fetchrow() ) { $client_id = $result[0]; $hostname = $result[1]; $registered = $result[2]; $requestor = $result[3]; $service = $result[4]; $client_type = $result[5]; $type = $result[6]; $receive_own_events = $result[7]; $description_tags = $result[8]; $ip_net_client = $result[9]; $client = SOAP::Data->name(client => \SOAP::Data->value( SOAP::Data->name(CLIENT_ID => $client_id), SOAP::Data->name(HOSTNAME => $hostname), SOAP::Data->name(REGISTERED => $registered), SOAP::Data->name(REQUESTOR => $requestor), SOAP::Data->name(SERVICE => $service), SOAP::Data->name(CLIENT_TYPE => $client_type), SOAP::Data->name(TYPE => $type), SOAP::Data->name(RECEIVE_OWN_EVENTS => $receive_own_events), SOAP::Data->name(DESCRIPTION_TAGS => $description_tags), SOAP::Data->name(IP_NET_CLIENT => $ip_net_client), )); push(@clients, $client); } my $sum = scalar @clients; write2log("info", "Sending information about $sum registered clients"); return @clients; } } # END of getClients #----------------------------------------------------------------------------- # getStatus - get list of status items of warden server #----------------------------------------------------------------------------- sub getStatus { my ($class, $arg) = @_; our $IP; our $LOCAL_IP; our $CN; if ($LOCAL_IP ne $IP) { write2log ("err", "Unauthorized access to getStatus from: $IP ($CN) - access allowed only from localhost"); die("Access denied - access allowed only from localhost!"); } else { my ($sth, @status); # size of database events my $db_size = Format::Human::Bytes::base10(-s $db); # sum of records in table events $sth = $DBH->prepare("SELECT count(*) FROM events WHERE valid = 't';"); if (!defined $sth) { die("Cannot prepare statement in getStatus: $DBI::errstr\n") } $sth->execute; my $events_sum = $sth->fetchrow(); if (!defined $events_sum) { $events_sum = "none" } # id of last record in table events $sth = $DBH->prepare("SELECT max(id) FROM events;"); if (!defined $sth) { die("Cannot prepare statement in getStatus: $DBI::errstr\n") } $sth->execute; my $events_last_id = $sth->fetchrow(); if (!defined $events_last_id) { $events_last_id = "none" } # timestamp of first record in table events $sth = $DBH->prepare("SELECT received FROM events WHERE id = (SELECT min(id) FROM events);"); if (!defined $sth) { die("Cannot prepare statement in getStatus: $DBI::errstr\n") } $sth->execute; my $events_first_timestamp = $sth->fetchrow(); if (!defined $events_first_timestamp) { $events_first_timestamp = "none" } # timestamp of last record in table events $sth = $DBH->prepare("SELECT received FROM events WHERE id = (SELECT max(id) FROM events);"); if (!defined $sth) { die("Cannot prepare statement in getStatus: $DBI::errstr\n") } $sth->execute; my $events_last_timestamp = $sth->fetchrow(); if (!defined $events_last_timestamp) { $events_last_timestamp = "none" } # sum of records in table clients $sth = $DBH->prepare("SELECT count(*) FROM clients;"); if (!defined $sth) { die("Cannot prepare statement in getStatus: $DBI::errstr\n") } $sth->execute; my $clients_sum = $sth->fetchrow(); if (!defined $clients_sum) { $clients_sum = "none" } my $server_status = SOAP::Data->name(server_status => \SOAP::Data->value( SOAP::Data->name(VERSION => $VERSION), SOAP::Data->name(ADDRESS => $ADDRESS), SOAP::Data->name(PORT => $PORT), SOAP::Data->name(LOGDIR => $LOGDIR), SOAP::Data->name(PIDDIR => $PIDDIR), SOAP::Data->name(VARDIR => $VARDIR), SOAP::Data->name(SSL_KEY_FILE => $SSL_KEY_FILE), SOAP::Data->name(SSL_CERT_FILE => $SSL_CERT_FILE), SOAP::Data->name(SSL_CA_FILE => $SSL_CA_FILE), SOAP::Data->name(FACILITY => $FACILITY), SOAP::Data->name(DB_SIZE => $db_size), SOAP::Data->name(EVENTS_SUM => $events_sum), SOAP::Data->name(EVENTS_LAST_ID => $events_last_id), SOAP::Data->name(EVENTS_FIRST_TIMESTAMP => $events_first_timestamp), SOAP::Data->name(EVENTS_LAST_TIMESTAMP => $events_last_timestamp), SOAP::Data->name(CLIENTS_SUM => $clients_sum) )); push(@status, $server_status); # statistics of senders if ($clients_sum != 0) { $sth = $DBH->prepare("SELECT client_id, hostname, service FROM clients WHERE client_type = 's';"); if (!defined $sth) { die("Cannot prepare statement in getStatus: $DBI::errstr\n") } $sth->execute; my ($client_id, $hostname, $service); my $client_status; while(($client_id, $hostname, $service) = $sth->fetchrow()) { my $hostname_db = $DBH->quote($hostname); my $service_db = $DBH->quote($service); my $sth2; # sum of stored events $sth2 = $DBH->prepare("SELECT count(*) FROM events WHERE hostname = $hostname_db AND service = $service_db;"); if ( !defined $sth2 ) { die("Cannot prepare statement in getStatus: $DBI::errstr\n") } $sth2->execute; my $count = $sth2->fetchrow(); if (!defined $count) {$count = "none"} # timestamp of last stored event $sth2 = $DBH->prepare("SELECT max(received) FROM events WHERE hostname = $hostname_db AND service = $service_db;"); if ( !defined $sth2 ) { die("Cannot prepare statement in getStatus: $DBI::errstr\n") } $sth2->execute; my $timestamp = $sth2->fetchrow(); if (!defined $timestamp) { $timestamp = "none" } # create SOAP data object $client_status = SOAP::Data->name(client_status => \SOAP::Data->value( SOAP::Data->name(CLIENT_ID => $client_id), SOAP::Data->name(HOSTNAME => $hostname), SOAP::Data->name(SERVICE => $service), SOAP::Data->name(COUNT => $count), SOAP::Data->name(TIMESTAMP => $timestamp), )); push(@status, $client_status); } } write2log("info", "Sent of warden server status info"); return @status; } } # END of getStatus ################################################################################ # MAIN warden-server ################################################################################ #------------------------------------------------------------------------------- # Superuser controle #------------------------------------------------------------------------------- my $UID = $<; if ($UID != 0) { die errMsg("You must be root for running this script!") } #------------------------------------------------------------------------------- # Daemonize section #------------------------------------------------------------------------------- use POSIX qw(setsid); chdir '/'; umask 0; # all STDERR messages are printed on terminal open STDIN, '/dev/null' or die errMsg("Can't read /dev/null: $!"); open STDOUT, '/dev/null' or die errMsg("Can't write to /dev/null: $!"); defined( my $pid = fork ) or die errMsg("Can't fork: $!"); exit if $pid; #------------------------------------------------------------------------------- # Dissociate this process from the controlling terminal # that started it and stop being part of whatever # process group this process was a part of. #------------------------------------------------------------------------------ POSIX::setsid() or die errMsg("Can't start a new session."); #------------------------------------------------------------------------------- # Callback signal handler for signals. #------------------------------------------------------------------------------- $SIG{INT} = $SIG{TERM} = $SIG{HUP} = \&signalHandler; $SIG{PIPE} = 'ignore'; #------------------------------------------------------------------------------- # Create pid file in /var/run/ #------------------------------------------------------------------------------- my $pfh = File::Pid->new( { file => $pid_file, } ); $pfh->write or die errMsg("Can't write PID file $pid_file: $!"); my $pid_number = $pfh->pid; #------------------------------------------------------------------------------- # Starting of Warden server #------------------------------------------------------------------------------- write2log("info", "Starting WARDEN server daemon with pid $pid_number"); # log of warden database size my $db_size_human = Format::Human::Bytes::base10(-s $db); write2log("info", "Size of DB file ($db_file) is: $db_size_human"); # start TCP server my $server = SOAP::Transport::TCP::Server ->new( Listen => 20, LocalAddr => $ADDRESS, LocalPort => $PORT, Proto => "tcp", ReuseAddr => 1, SSL_verify_mode => 0x02, SSL_use_cert => 1, SSL_server => 1, SSL_key_file => $SSL_KEY_FILE, SSL_cert_file => $SSL_CERT_FILE, SSL_ca_file => $SSL_CA_FILE, SSL_error_trap =>\&sslErrorHandler, ); # check if socket exist $server or die errMsg("Socket error: $!"); # start SOAP server my $soap = SOAP::Server ->new() ->dispatch_to('Warden'); #------------------------------------------------------------------------------- # Process of incoming client's requests and send response #------------------------------------------------------------------------------- write2log("info", "Starting TCP and SOAP server at $ADDRESS:$PORT"); while ($die_now != 1) { my $socket = $server->accept(); next if (!$socket); our $CN = $socket->peer_certificate("cn"); my @alt_names_array = $socket->peer_certificate("subjectAltNames"); our $AN_FILTER = altNamesFilter(\@alt_names_array); our $IP = $socket->peerhost; our $LOCAL_IP = $socket->sockhost; # read input serialized SOAP envelope and data my ($request, $buf); while (defined($buf = <$socket>)) { $request .= $buf; } # handle of called server function from client and send response to client my $response = $soap->handle($request); print $socket $response; $socket->close; undef($socket); undef($CN); undef($AN_FILTER); undef($IP); undef($LOCAL_IP); } ################################################################################ # Cleanup section ################################################################################ END { if ($die_now == 1) { my $pid = trim(`cat $pid_file`); write2log("info", "Stopping WARDEN server daemon with pid $pid"); # close connection to DB $DBH->disconnect(); # remove pid file $pfh->remove if defined $pfh; } }