#!/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 SOAP::Transport::HTTP; #use File::Pid; #use POSIX; use DBI; use DBD::mysql; 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; #use Crypt::OpenSSL::X509; use MIME::Base64; use Crypt::X509; 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; #DB options our $DB_NAME = undef; our $DB_USER = undef; our $DB_PASS = undef; our $DB_HOST = undef; # #$ADDRESS = "147.228.52.72"; ## ###------------------------------------------------------------------------------- ### PORT - used TCP port for Warden server ###------------------------------------------------------------------------------- #$PORT = "8889"; ## ###------------------------------------------------------------------------------- ### BASEDIR - base directory of Warden server ###------------------------------------------------------------------------------- #our $BASEDIR = "/opt/warden-server/"; ## ###------------------------------------------------------------------------------- ### VARDIR - var directory ###------------------------------------------------------------------------------- #$VARDIR = "$BASEDIR/var/"; ## ###------------------------------------------------------------------------------- ### LOGDIR - logging directory ###------------------------------------------------------------------------------- #$LOGDIR = "/var/log/"; ## ###------------------------------------------------------------------------------- ### PIDDIR - process ID directory ###------------------------------------------------------------------------------- #$PIDDIR = "/var/run/"; ## ## ## ## 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=$DB_NAME;host=$DB_HOST", $DB_USER, $DB_PASS, {RaiseError => 1, mysql_auto_reconnect => 0}) || die "Could not connect to database: $DBI::errstr"; #our $DBH; ################################################################################ # 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 $CN = $ENV{'SSL_CLIENT_S_DN_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; push @an_array, "'$CN'"; my @a = split("\n", $ENV{'SSL_CLIENT_CERT'}); pop @a; shift @a; my $der = decode_base64(join("", @a)); my $decoded= Crypt::X509->new(cert => $der); foreach my $tmp (@{$decoded->SubjectAltName}){ if($tmp =~ s/dNSName=//){ push @an_array, "'$tmp'"; } } 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 @alt_names_array = $socket->peer_certificate("subjectAltNames"); my $CN = $ENV{'SSL_CLIENT_S_DN_CN'}; my $AN_FILTER = altNamesFilter(undef); my $IP = $ENV{'REMOTE_ADDR'}; my $LOCAL_IP = $ENV{'SERVER_ADDR'}; 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_id()"); 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!"); return 0; } else { return 1; } } #$DBH->disconnect(); } # 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 = $ENV{'SSL_CLIENT_S_DN_CN'}; my $AN_FILTER = altNamesFilter(undef); my $IP = $ENV{'REMOTE_ADDR'}; 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') { write2log("info", "SELECT * FROM events WHERE type != 'test' AND id > $last_id_db AND type = $requested_type_db AND valid = 't' ORDER BY id ASC;"); $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"); write2log("info", "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;"); $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; my $CN = $ENV{'SSL_CLIENT_S_DN_CN'}; my $IP = $ENV{'REMOTE_ADDR'}; my $LOCAL_IP = $ENV{'SERVER_ADDR'}; 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; my $CN = $ENV{'SSL_CLIENT_S_DN_CN'}; my $IP = $ENV{'REMOTE_ADDR'}; my $LOCAL_IP = $ENV{'SERVER_ADDR'}; 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; my $CN = $ENV{'SSL_CLIENT_S_DN_CN'}; my $IP = $ENV{'REMOTE_ADDR'}; my $LOCAL_IP = $ENV{'SERVER_ADDR'}; 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; my $CN = $ENV{'SSL_CLIENT_S_DN_CN'}; my $IP = $ENV{'REMOTE_ADDR'}; my $LOCAL_IP = $ENV{'SERVER_ADDR'}; 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; my $CN = $ENV{'SSL_CLIENT_S_DN_CN'}; my $IP = $ENV{'REMOTE_ADDR'}; my $LOCAL_IP = $ENV{'SERVER_ADDR'}; 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); $sth = $DBH->prepare("SELECT data_length + index_length FROM information_schema.TABLES WHERE table_schema = ? AND TABLE_NAME = ?"); $sth->execute("warden", "events"); my $db_size_db = $sth->fetchrow(); my $db_size = (defined $db_size_db ? Format::Human::Bytes::base10($db_size_db) : "none"); # 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 => 5, ## 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 = $ENV{'SSL_CLIENT_S_DN_CN'}; # my @alt_names_array = $socket->peer_certificate("subjectAltNames"); # our $AN_FILTER = altNamesFilter(undef); # our $IP = $ENV{'REMOTE_ADDR'}; # our $LOCAL_IP = $ENV{'SERVER_ADDR'}; ## ## # 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; ## } ##} # 1;