Skip to content
Snippets Groups Projects
Warden.pm 31.7 KiB
Newer Older
#!/usr/bin/perl -w
#
#
# Copyright (C) 2011-2012 Cesnet z.s.p.o
#
# Use of this source is governed by a BSD-style license, see LICENSE file.

package Warden;

use strict;
use DBI;
use DBD::mysql;
use Format::Human::Bytes;
use Sys::Syslog qw(:DEFAULT setlogsock);
Sys::Syslog::setlogsock('unix');
use File::Basename;
use Net::CIDR::Lite;
use DateTime;
use MIME::Base64;
use Crypt::X509;
our $VERSION = "2.2-devel";

################################################################################
################################################################################

my $conf_file = "/opt/warden-server/etc/warden-server.conf"; # path is updated by install.sh
our $SYSLOG		= undef;
our $SYSLOG_VERBOSE	= undef;
our $SYSLOG_FACILITY	= undef;
our $DB_NAME		= undef;
our $DB_USER		= undef;
our $DB_PASS		= undef;
our $DB_HOST		= undef;
our $MAX_EVENTS_LIMIT	= 1000000; # default value
# load set variables by user
  die("Errors in config file '$conf_file': $@") if $@;
  die("Can't read config file '$conf_file': $!") unless defined $_;
  # if $_ defined, it's retvalue of last statement of conf, for which we don't care
################################################################################
#				VARIABLES
################################################################################

our $DBH = DBI->connect("DBI:mysql:database=$DB_NAME;host=$DB_HOST", $DB_USER, $DB_PASS, {RaiseError => 1, mysql_auto_reconnect => 1}) || die "Could not connect to database: $DBH->errstr";

################################################################################
#				LOCAL FUNCTIONS
################################################################################

#-------------------------------------------------------------------------------
# sendMsg - sent message to syslog (SYS::Syslog) and to client (SOAP::Fault)
#
# Args:	(SYSLOG severity, SYSLOG msg, SOAP msg)
#-------------------------------------------------------------------------------
  my $severity		= shift;
  my $syslog_msg 	= shift;
  my $soap_msg		= shift;
  my $filename		= File::Basename::basename($0);

  if ($SYSLOG_VERBOSE == 1 && ($severity eq "err" || $severity eq "debug")) {
    $syslog_msg .= "\nStack info: " . Carp::longmess();
  }

  if ($SYSLOG == 1 && defined $severity && defined $syslog_msg) {
    Sys::Syslog::openlog($filename, "cons,pid", $SYSLOG_FACILITY);
    Sys::Syslog::syslog("$severity", "$syslog_msg");
    Sys::Syslog::closelog();
  }
  if (defined $soap_msg) {
    die SOAP::Fault->faultstring($soap_msg);
  }
} # End of sendMsg


#-------------------------------------------------------------------------------
# getAltNames - parse Alternate names from SSL certifiate
#-------------------------------------------------------------------------------
{
  my @an_array;
  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, $DBH->quote($tmp));
    }
  }
  my $alt_names = join(',', @an_array);
  return $alt_names;
#-------------------------------------------------------------------------------
# authorizeClient - authorize client by CN,AN and source IP range
#-------------------------------------------------------------------------------

sub authorizeClient
{
  my ($alt_names, $ip, $service_type, $client_type, $function_name) = @_;
  my $sth;

  # obtain cidr based on rigth common name and alternate names, service and client_type
  if($function_name eq 'saveNewEvent') {
    $sth = $DBH->prepare("SELECT client_id, hostname, ip_net_client, receive_own_events FROM clients WHERE hostname IN ($alt_names) AND service = ? AND client_type = ? ORDER BY SUBSTRING_INDEX(ip_net_client,'/', -1) DESC;");
  } elsif($function_name eq 'getNewEvents') {
    $sth = $DBH->prepare("SELECT client_id, hostname, ip_net_client, receive_own_events FROM clients WHERE hostname IN ($alt_names) AND (type = ? OR type = '_any_') AND client_type = ? ORDER BY SUBSTRING_INDEX(ip_net_client,'/', -1) DESC;");
  } elsif($function_name eq 'getClientInfo') {
    $sth = $DBH->prepare("SELECT client_id, hostname, ip_net_client, receive_own_events FROM clients WHERE hostname IN ($alt_names) ORDER BY SUBSTRING_INDEX(ip_net_client,'/', -1) DESC;");
  } elsif($function_name eq 'getLastId') {
    $sth = $DBH->prepare("SELECT client_id, hostname, ip_net_client, receive_own_events FROM clients WHERE hostname IN ($alt_names) AND client_type = 'r' ORDER BY SUBSTRING_INDEX(ip_net_client,'/', -1) DESC;");
 }

  # check db handler
  if (!defined $sth) {
    sendMsg("err",
           "Cannot prepare authorization statement in $function_name: $DBH->errstr",
	   "Internal 'prepare' server error")
  }
  # execute query for two or none params functions
  if ($function_name eq 'saveNewEvent' || $function_name eq 'getNewEvents') {
    $sth->execute($service_type, $client_type);
  } else {
    $sth->execute;
  }

  # obtain registration info about clients
  my ($client_id, $an, $cidr, $receive_own, $cidr_list);
  while(($client_id, $an, $cidr, $receive_own)  = $sth->fetchrow()) {
    my $cidr_list = Net::CIDR::Lite-> new -> add($cidr);
    $ret{'client_id'}	= $client_id;
    $ret{'dns'}		= $an;
    $ret{'cidr'}	= $cidr;
    $ret{'receive_own'} = $receive_own;

    if ($cidr_list->bin_find($ip)) {
      $correct_ip_source = 1;
      last;
  # check if client is registered
  if ($sth->rows == 0) {
    sendMsg("err",
           "Unauthorized access to function '$function_name' from: '$ip'; CN(AN): $alt_names; used service: '$service_type' - client is not registered",
           "Access denied - client is not registered at warden server $ENV{'SERVER_NAME'}");
    return undef;
  }

  # check if client has IP from registered CIDR
  if (!$correct_ip_source) {
    sendMsg ("err",
            "Unauthorized access to function '$function_name' from: '$ip'; CN(AN): $alt_names; used service: '$service_type' - access from bad subnet: Registered subnet '$ret{'cidr'}'",
            "Access denied - access to $ENV{'SERVER_NAME'} from unauthorized subnet");

################################################################################
# 				SOAP Functions
################################################################################

#-----------------------------------------------------------------------------
# saveNewEvent - save new received event into database
#-----------------------------------------------------------------------------
sub saveNewEvent
{
  my ($class, $data) = @_;
  my ($sth, $cidr_list);

  # client network information
  my $cn	= $ENV{'SSL_CLIENT_S_DN_CN'};
  my $alt_names = getAltNames(undef);
  my $ip	= $ENV{'REMOTE_ADDR'};

  # variables defined by server
  my $function_name	= 'saveNewEvent';
  my $client_type	= 's';			# incoming client MUST be sender
  my $valid		= 't';			# registered sender has valid events
  my $received		= DateTime->now;	# time of event delivery (UTC)

  my $service		= $data->{'SERVICE'};
  my $detected		= $data->{'DETECTED'};
  my $source_type	= $data->{'SOURCE_TYPE'};
  my $source		= $data->{'SOURCE'};
  my $target_proto	= $data->{'TARGET_PROTO'};
  my $target_port	= $data->{'TARGET_PORT'};
  my $attack_scale 	= $data->{'ATTACK_SCALE'};
  my $priority		= $data->{'PRIORITY'};
  my $timeout		= $data->{'TIMEOUT'};

  my %client = authorizeClient($alt_names, $ip, $service, $client_type, $function_name);
    if (!(exists $VALID_STRINGS{'type'} && grep $type eq $_, @{$VALID_STRINGS{'type'}})) {
      sendMsg("err",
             "Unknown event type - client from: '$ip'; CN(AN): $alt_names; used type: '$type'",
             "Unknown event type '$type'");
    } elsif (!(exists $VALID_STRINGS{'source_type'} && grep $source_type eq $_, @{$VALID_STRINGS{'source_type'}})) {
      sendMsg("err",
             "Unknown source type - client from: '$ip'; CN(AN): $alt_names; used source_type: '$source_type'",
             "Unknown source type '$source_type'");
    # http://my.safaribooksonline.com/book/programming/regular-expressions/9780596802837/4dot-validation-and-formatting/id2983571
    } elsif ($detected !~ /^((?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[0-1]|0[1-9]|[1-2][0-9])T(2[0-3]|[0-1][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z|[+-](?:2[0-3]|[0-1][0-9]):[0-5][0-9])?/) {
      sendMsg("err",
             "Unknown detected time format - client from: '$ip'; CN(AN): $alt_names; used detected: '$detected'",
             "Unknown detected time format '$detected'");
    } elsif ($target_port !~ /^\d+\z/ && defined $target_port) {
      sendMsg("err",
             "Unknown target port - client from: '$ip'; CN(AN): $alt_names; used target_port: '$target_port'",
             "Unknown target port '$target_port'");
    } elsif ($attack_scale !~ /^\d+\z/ && defined $attack_scale) {
      sendMsg("err",
             "Unknown attack scale - client from: '$ip'; CN(AN): $alt_names; used attack_scale: '$attack_scale'",
             "Unknown attack scale '$attack_scale'");
    } elsif ($priority !~ /^\d+\z/ && defined $priority) {
      sendMsg("err",
             "Unknown priority - client from: '$ip'; CN(AN): $alt_names; used priority: '$priority'",
             "Unknown priority '$priority'");
    } elsif ($timeout !~ /^\d+\z/ && defined $timeout) {
      sendMsg("err",
             "Unknown timeout - client from: '$ip'; CN(AN): $alt_names; used timeout: '$timeout'",
             "Unknown timeout '$timeout'");
    } else {
      $sth=$DBH->prepare("INSERT INTO events VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);");
      if (!defined $sth) {
        sendMsg("err",
	       "Cannot prepare statement in function '$function_name': $DBH->errstr",
	       "Internal 'prepare' server error");
      }
      $sth->execute(undef, $client{'dns'}, $service, $detected, $received, $type, $source_type, $source, $target_proto, $target_port, $attack_scale, $note, $priority, $timeout, $valid);
      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, $cidr_list);
  my ($id, $hostname, $service, $detected, $type, $source_type, $source, $target_proto, $target_port, $attack_scale, $note, $priority, $timeout);

  my $cn	= $ENV{'SSL_CLIENT_S_DN_CN'};
  my $alt_names	= getAltNames(undef);
  my $ip	= $ENV{'REMOTE_ADDR'};

  my $client_type	= 'r';	# incoming client MUST be sender
  my $function_name	= 'getNewEvents';

  # parse SOAP data object
Tomáš Plesník's avatar
Tomáš Plesník committed
  my $requested_type		= $data->{'REQUESTED_TYPE'} || '_any_';
  my $last_id			= $data->{'LAST_ID'};
  my $max_rcv_events_limit 	= $data->{'MAX_RCV_EVENTS_LIMIT'}; # client events limit

  # comparison of client and server limit - which can be used
  my $used_limit;
  if (defined $max_rcv_events_limit && $max_rcv_events_limit < $MAX_EVENTS_LIMIT) {
    $used_limit = $max_rcv_events_limit;
  } else {
    $used_limit = $MAX_EVENTS_LIMIT;
  }
  my %client = authorizeClient($alt_names, $ip, $requested_type, $client_type, $function_name);
  if(defined %client) {
    if ($client{'receive_own'} eq 't') {
      if ($requested_type eq '_any_') {
        $sth = $DBH->prepare("SELECT * FROM events WHERE type != 'test' AND id > ? AND valid = 't' ORDER BY id ASC LIMIT ?;");
        if (!defined $sth) {
	  sendMsg("err",
	         "Cannot prepare ROE-ANY statement in function '$function_name': $DBH->errstr",
		 "Internal 'prepare' server error");
        }
        $sth = $DBH->prepare("SELECT * FROM events WHERE type != 'test' AND id > ? AND type = ? AND valid = 't' ORDER BY id ASC LIMIT ?;");
        if (!defined $sth) {
	  sendMsg("err",
	         "Cannot prepare ROE statement in function '$function_name': $DBH->errstr",
		 "Internal 'prepare' server error");
	}
	$sth->execute($last_id, $requested_type, $used_limit);
    } else {
        $sth = $DBH->prepare("SELECT * FROM events WHERE type != 'test' AND id > ? AND valid = 't' AND hostname NOT LIKE ? ORDER BY id ASC LIMIT ?;");
        if (!defined $sth) {
	  sendMsg("err",
	         "Cannot prepare ANY statement in function '$function_name': $DBH->errstr",
		 "Internal 'prepare' server error");
	}
        my ($domain) = $cn =~ /([^\.]+\.[^\.]+)$/;
        $domain = '\%' . $domain;
        $sth->execute($last_id, $domain, $used_limit);
        $sth = $DBH->prepare("SELECT * FROM events WHERE type != 'test' AND id > ? AND type = ? AND valid = 't' AND hostname NOT LIKE ? ORDER BY id ASC LIMIT ?;");
        if (!defined $sth) {
	  sendMsg("err",
	         "Cannot prepare statement in function '$function_name': $DBH->errstr\n",
		 "Internal 'prepare' server error");
	}
        my ($domain) = $cn =~ /([^\.]+\.[^\.]+)$/;
        $domain = '\%' . $domain;
        $sth->execute($last_id, $requested_type, $domain, $used_limit);
    }

    # 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];
      $note 		= $result[11];
      $priority 	= $result[12];
      $timeout	 	= $result[13];

      # create SOAP data object
      $event = SOAP::Data->name(event => \SOAP::Data->value(
        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) {
      if (scalar @ids == 1) {
	       "Sent 1 event [#$ids[0]] to '$ip' [CN(AN): $alt_names], client_limit: '$max_rcv_events_limit', requested_type: '$requested_type'",
	       "Sent " . scalar @ids . " events [#$ids[0] - #$ids[-1]] to '$ip' [CN(AN): $alt_names], client_limit: '$max_rcv_events_limit', requested_type: '$requested_type'",
    }
    return @events;
  }
} # END of getNewEvents


#-----------------------------------------------------------------------------
# getLastId - get lastest saved event ID
#-----------------------------------------------------------------------------
sub getLastId
{
  my ($class, $arg) = @_;

  # client network information
  my $cn	= $ENV{'SSL_CLIENT_S_DN_CN'};
  my $alt_names	= getAltNames(undef);
  my $ip	= $ENV{'REMOTE_ADDR'};

  my $service		= undef;
  my $client_type	= undef;

  my %client = authorizeClient($alt_names, $ip, $service, $client_type, $function_name);
  if (defined %client) {
    my $sth = $DBH->prepare("SELECT max(id) FROM events;");
    if (!defined $sth) {
      sendMsg("err",
              "Cannot prepare statement in function '$function_name': $DBH->errstr",
              "Internal 'prepare' server error");
    }
    $sth->execute;
    my $result = $sth->fetchrow();
    return $result;
} # END of getLastID


#-----------------------------------------------------------------------------
# registerSender - register new sender
#-----------------------------------------------------------------------------
sub registerSender
{
  my ($class, $data) = @_;
  my $sth;

  # client network information
  my $cn = $ENV{'SSL_CLIENT_S_DN_CN'};
  my $ip = $ENV{'REMOTE_ADDR'};
  my $local_ip = $ENV{'SERVER_ADDR'};
    sendMsg("err",
           "Unauthorized access to function '$function_name' from: '$ip' ('$cn') - access allowed only from localhost",
           "Access denied - access allowed only from localhost");
  } else {
    my $client_type 		= "s";
    my $registered 		= DateTime->now;
    my $type			= undef;
    my $receive_own_events	= undef;

    # parse SOAP data oject
    my $hostname		= $data->{'HOSTNAME'};
    my $requestor		= $data->{'REQUESTOR'};
    my $service			= $data->{'SERVICE'};
    my $description_tags	= $data->{'DESCRIPTION_TAGS'};
    my $ip_net_client		= $data->{'IP_NET_CLIENT'};

    # check if sender has been already registered
    $sth = $DBH->prepare("SELECT registered FROM clients WHERE hostname = ? AND service = ? AND client_type = ? AND ip_net_client = ? LIMIT 1;");
    if (!defined $sth) {
      sendMsg("err",
             "Cannot prepare statement in function '$function_name': $DBH->errstr",
             "Internal 'prepare' server error");
    }
    $sth->execute($hostname, $service, $client_type, $ip_net_client);
    my $result = $sth->fetchrow();

    # register new sender
    if (defined $result) {
             "Attempt to re-register the sender: {hostname: '$hostname', service: '$service', cidr: '$ip_net_client'}",
             "Sender has been already registered at $ENV{'SERVER_NAME'} in '$result'");
    } else {
      $sth = $DBH->prepare("INSERT INTO clients VALUES (?,?,?,?,?,?,?,?,?,?);");
      if (!defined $sth) {
        sendMsg("err",
	       "Cannot prepare statement in function '$function_name': $DBH->errstr",
	       "Internal 'prepare' server error");
      }
      $sth->execute(undef, $hostname, $registered, $requestor, $service, $client_type, $type, $receive_own_events, $description_tags, $ip_net_client);
	       "New sender '$hostname' {service: '$service', cidr: '$ip_net_client'} was registered at $ENV{'SERVER_NAME'}",
      return 1;
    }
  }
} # END of registerSender


#-----------------------------------------------------------------------------
# registerReceiver - register new receiver
#-----------------------------------------------------------------------------
sub registerReceiver
{
  my ($class, $data) = @_;
  my $sth;

  # client network information 
  my $cn = $ENV{'SSL_CLIENT_S_DN_CN'};
  my $ip = $ENV{'REMOTE_ADDR'};
  my $local_ip = $ENV{'SERVER_ADDR'};
    sendMsg("err",
           "Unauthorized access to function '$function_name' from: '$ip' ('$cn') - access allowed only from localhost",
           "Access denied - access allowed only from localhost");
  } else {
    my $client_type		= "r";
    my $registered 		= DateTime->now;
    my $service			= undef;
    my $description_tags	= undef;

    # parse SOAP data oject
    my $hostname		= $data->{'HOSTNAME'};
    my $requestor		= $data->{'REQUESTOR'};
    my $type			= $data->{'TYPE'};
    my $receive_own_events	= $data->{'RECEIVE_OWN_EVENTS'};
    my $ip_net_client		= $data->{'IP_NET_CLIENT'};

    # check if receiver has been already registered
    $sth = $DBH->prepare("SELECT registered FROM clients WHERE hostname = ? AND client_type = ? AND type = ? AND ip_net_client = ? LIMIT 1;");
    if (!defined $sth) {
      sendMsg("err",
             "Cannot prepare statement in function '$function_name': $DBH->errstr",
	     "Internal 'prepare' server error");
    }
    $sth->execute($hostname, $client_type, $type, $ip_net_client);
    my $result = $sth->fetchrow();

    # register new receiver
    if (defined $result) {
             "Attempt to re-register the receiver: {hostname: '$hostname', type: '$type', cidr: '$ip_net_client'}",
             "Receiver has already been registered at $ENV{'SERVER_NAME'} in '$result'");
    } else {
      $sth = $DBH->prepare("INSERT INTO clients VALUES (?,?,?,?,?,?,?,?,?,?);");
      if (!defined($sth)) {
        sendMsg("err",
	       "Cannot prepare statement in function '$function_name': $DBH->errstr",
	       "Internal 'prepare' server error");
      }
      $sth->execute(undef, $hostname, $registered, $requestor, $service, $client_type, $type, $receive_own_events, $description_tags, $ip_net_client);
             "New receiver '$hostname' {type: '$type', cidr: '$ip_net_client'} was registered at $ENV{'SERVER_NAME'}",
      return 1;
    }
  }
} # END of registerReceiver


#-----------------------------------------------------------------------------
# unregisterClient - unregister client
#-----------------------------------------------------------------------------
sub unregisterClient
{
  my ($class, $data) = @_;
  my $sth;

  # client network information
  my $cn = $ENV{'SSL_CLIENT_S_DN_CN'};
  my $ip = $ENV{'REMOTE_ADDR'};
  my $local_ip = $ENV{'SERVER_ADDR'};
    sendMsg("err",
           "Unauthorized access to function '$function_name' from: '$ip' ('$cn') - access allowed only from localhost",
           "Access denied - access allowed only from localhost");
  } else {
    # parse SOAP data oject
    my $client_id	= $data->{'CLIENT_ID'};

    # check if receiver has been already registered
    $sth = $DBH->prepare("SELECT client_id, hostname, service, client_type FROM clients WHERE client_id = ? LIMIT 1;");
    if (!defined $sth) {
      sendMsg("err",
             "Cannot prepare statement in function '$function_name': $DBH->errstr",
	     "Internal 'prepare' server error");
    }
    my ($id, $hostname, $service, $client_type) = $sth->fetchrow();

    # delete registered client
    if (!defined $id) {
      sendMsg("err",
             "Attempt to delete unregister client '$id', '$hostname', '$service', '$client_type'",
             "Client (#$client_id) is not registered at $ENV{'SERVER_NAME'}");
    } else {
      if ($client_type eq 's') {
        $sth = $DBH->prepare("DELETE FROM clients WHERE client_id = ?;");
        if (!defined $sth) {
          sendMsg("err",
	         "Cannot prepare statement in function '$function_name': $DBH->errstr",
		 "Internal 'prepare' server error");
	}
        $sth = $DBH->prepare("UPDATE events SET valid = 'f' where hostname = ? AND service = ?;");
        if (!defined $sth) {
          sendMsg("err",
	         "Cannot prepare statement in function '$function_name': $DBH->errstr",
		 "Internal 'prepare' server error");
	}
        sendMsg("info",
	       "Sender '$hostname' (client_id: '$client_id', service: '$service') was deleted and its data were invalidated",
	       undef);
        return 1;
      } else {
        $sth = $DBH->prepare("DELETE FROM clients WHERE client_id = ?;");
        if (!defined $sth) {
	  sendMsg("err",
	         "Cannot prepare statement in function '$function_name': $DBH->errstr",
		 "Internal 'prepare' server error");
	}
	       "Receiver '$hostname' (client_id: '$client_id') was deleted from $ENV{'SERVER_NAME'}",
        return 1;
      }
    }
  }
} # END of unregisterClient


#-----------------------------------------------------------------------------
# getStatus - get list of status items of warden server
#-----------------------------------------------------------------------------
sub getStatus
{
  my ($class, $arg) = @_;

  # client network information
  my $cn = $ENV{'SSL_CLIENT_S_DN_CN'};
  my $ip = $ENV{'REMOTE_ADDR'};
  my $local_ip = $ENV{'SERVER_ADDR'};
  #-----------------------------------------------------------------------------
  # Warden server stats

    sendMsg("err",
           "Unauthorized access to function '$function_name' from: '$ip' ('$cn') - access allowed only from localhost",
           "Access denied - access allowed only from localhost");
  } else {
    my ($sth, @status);

    # Warden server hostname
    my $hostname = $ENV{'SERVER_NAME'};

    # IP address of Warden server
    my $ip_address = $ENV{'REMOTE_ADDR'};

    # used port
    my $port = $ENV{'SERVER_PORT'};

    # size of database events
    $sth = $DBH->prepare("SELECT data_length + index_length FROM information_schema.TABLES WHERE table_schema = ? AND TABLE_NAME = ?");
    $sth->execute('warden', 'events');
    my $size = $sth->fetchrow();
    my $db_size = (defined $size ? Format::Human::Bytes::base10($size) : "none");

    # sum of records in table events
    $sth = $DBH->prepare("SELECT count(*) FROM events WHERE valid = 't';");
    if (!defined $sth) {
      sendMsg("err",
             "Cannot prepare statement in function '$function_name': $DBH->errstr",
	     "Internal 'prepare' server error");
    }
    $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) {
      sendMsg("err",
             "Cannot prepare statement in function '$function_name': $DBH->errstr",
	     "Internal 'prepare' server error");
    }
    $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) {
      sendMsg("err",
             "Cannot prepare statement in function '$function_name': $DBH->errstr",
	     "Internal 'prepare' server error");
    }
    $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) {
      sendMsg("err",
             "Cannot prepare statement in function '$function_name': $DBH->errstr",
	     "Internal 'prepare' server error");
    }
    $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) {
      sendMsg("err",
             "Cannot prepare statement in function '$function_name': $DBH->errstr",
	     "Internal 'prepare' server error");
    }
    $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(HOSTNAME			=> $hostname),
      SOAP::Data->name(IP_ADDRESS		=> $ip_address),
      SOAP::Data->name(PORT			=> $port),
      SOAP::Data->name(SYSLOG			=> $SYSLOG),
      SOAP::Data->name(SYSLOG_VERBOSE		=> $SYSLOG_VERBOSE),
      SOAP::Data->name(SYSLOG_FACILITY		=> $SYSLOG_FACILITY),
      SOAP::Data->name(DB_NAME			=> $DB_NAME),
      SOAP::Data->name(DB_USER			=> $DB_USER),
      SOAP::Data->name(DB_HOST			=> $DB_HOST),
      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 table of senders

    if ($clients_sum != 0) {
      $sth = $DBH->prepare("SELECT clients.client_id, clients.hostname, clients.service, count(*), max(received) FROM events LEFT JOIN clients ON (events.hostname=clients.hostname AND events.service=clients.service) GROUP BY hostname, service;");
      if (!defined $sth) {
        sendMsg("err",
	       "Cannot prepare statement in function '$function_name': $DBH->errstr",
	       "Internal 'prepare' server error");
      }
      $sth->execute;
      my ($hash_ref, $client_id, $hostname, $service, $count, $timestamp, $client_status);
      $hash_ref = $sth->fetchall_hashref("client_id");
      foreach my $key (sort {$a<=>$b} keys %$hash_ref) {
        $client_status = SOAP::Data->name(client_status => \SOAP::Data->value(
          SOAP::Data->name(CLIENT_ID	=> $hash_ref->{$key}->{client_id}),
          SOAP::Data->name(HOSTNAME	=> $hash_ref->{$key}->{hostname}),
          SOAP::Data->name(SERVICE	=> $hash_ref->{$key}->{service}),
          SOAP::Data->name(COUNT	=> $hash_ref->{$key}->{"count(*)"}),
          SOAP::Data->name(TIMESTAMP	=> $hash_ref->{$key}->{"max(received)"}),
           "Sent warden server status info from $ENV{'SERVER_NAME'}",
    return @status;
  }
} # END of getStatus


#-------------------------------------------------------------------------------
# getClientInfo - get list of registered clients on Warden server
#		  by Warden client
#-------------------------------------------------------------------------------
sub getClientInfo
{
  my ($class, $data) = @_;
  my (@clients, $client);
  my ($client_id, $hostname, $registered, $requestor, $service, $client_type, $type, $receive_own_events, $description_tags, $ip_net_client);

  # client network information
  my $cn	= $ENV{'SSL_CLIENT_S_DN_CN'};
  my $alt_names	= getAltNames(undef);
  my $ip	= $ENV{'REMOTE_ADDR'};

  my $service		= undef;
  my $client_type	= undef;

  my $function_name = 'getClientInfo';

  my %client = authorizeClient($alt_names, $ip, $service, $client_type, $function_name);
  if (defined %client) {
    my $sth = $DBH->prepare("SELECT * FROM clients ORDER BY client_id ASC;");
    if (!defined $sth) {
      sendMsg("err",
            "Cannot prepare statement in function '$function_name': $DBH->errstr",
            "Internal 'prepare' server error");
    }
    $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;
    sendMsg("info",
            "Sending information about '$sum' registered clients from $ENV{'SERVER_NAME'}",
            undef);
    return @clients;
  }
} # END of getClientInfo