Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • Pavel.Valach/warden
1 result
Select Git revision
Show changes
Showing
with 495 additions and 3121 deletions
Installation process
====================
Content
A. Overview
B. Pre-installation step
C. Installation step
D. Post-installation steps
--------------------------------------------------------------------------------
A. Overview
-----------
For installation of warden-server package on local machine use install.sh.
Default destination directory is /opt/warden-server/.
For more information about install.sh options run install.sh -h.
You must be root for running this script.
B. Pre-installation step
------------------------
1) Install necessary packages
# aptitude install apache2 mysql-server libapache2-mod-perl2 apache2-mpm-prefork
C. Installation step
--------------------
1) Install Warden server package
# ./warden-server-2.1/install.sh -d /opt -k /etc/ssl/private/server.key -c /etc/ssl/certs/server.pem -a /etc/ssl/certs/bundle.pem
D. Post-installation steps
--------------------------
1) Enable of mod_ssl module
# a2enmod ssl
2) Apache server configuration
a) VirtualHost section configuration
- include parameters from the Warden server configuration file (<warden-server_path>/etc/warden-apache.conf)
# vim /etc/apache2/sites-enables/default(-ssl)
<VirtualHost *:443>
...
Include /opt/warden-server/etc/warden-apache.conf
</VirtualHost>
b) Apache server performance configuration
# vim /etc/apache2/apache2.conf
- prefork module settings
<IfModule mpm_prefork_module>
StartServers 2
MinSpareServers 4
MaxSpareServers 8
ServerLimit 700
MaxClients 700
MaxRequestsPerChild 0
</IfModule>
- connection settings
Timeout 10
KeepAlive Off
3) MySQL database configuration
a) Create new user acount (optional)
b) Create new database structure
$ mysql -u <user> -h localhost -p <password> < {warden-server_path}/doc/warden.mysql
4) Warden server configuration
- configure warden-server.conf, warden-client.conf and warden-apache.conf placed in <warden-server_path>/etc directory
5) Restart of Apache server
# /etc/init.d/apache2 restart
+----------------------------+
| README - Warden Server 2.1 |
+----------------------------+
Content
A. Overall Information
B. Installation Dependencies
C. Installation
D. Update
E. Uninstallation
F. Miscellaneous
G. Registration of Clients
H. Status Info
--------------------------------------------------------------------------------
A. Overall Information
1. About Warden System
Warden is a client-server architecture service designed to share detected
security events (issues) among CSIRT and CERT teams in a simple and fast way.
This package contains the Warden server.
2. Version
2.1 (2012-11-16)
3. Package structure
warden-server/
bin/
getClients.pl
getStatus.pl
getWebStatus.sh
registerReceiver.pl
registerSender.pl
unregisterClients.pl
doc/
AUTHORS
CHANGELOG
INSTALL
LICENSE
README
warden.mysql
etc/
package_version
warden-apache.conf
warden-client.conf
warden-server.conf
lib/
WardenConf.pm
Warden.pm
WardenReg.pm
WardenStatus.pm
Warden/
ApacheDispatch.pm
uninstall.sh
--------------------------------------------------------------------------------
B. Installation Dependencies
1. Applications:
Perl >= 5.10.1
MySQL >= 5.1.63
Apache >= 2.2.14
2. Perl modules:
SOAP::Lite >= 0.712
SOAP::Transport::HTTP >= 0.712
DBI >= 1.612
DBD::mysql >= 4.016
Format::Human::Bytes >= 0.05
Sys::Syslog >= 0.27
File::Basename >= 2.77
Net::CIDR::Lite >= 0.21
DateTime >= 0.61
Getopt::Std >= 1.06
Switch >= 2.14
IO::Socket::SSL >= 1.66
MIME::Base64 >= 3.08
Crypt::X509 >= 0.40
Carp >= 1.11
--------------------------------------------------------------------------------
C. Installation
1. Check SHA1 checksum of the Warden server package archive.
$ sha1sum -c warden-server-2.1.tar.gz.sig
2. Untar it.
$ tar xzvf warden-server-2.1.tar.gz
3. Run install.sh.
Default installation directory is /opt/warden-server/
For more information about install.sh options run install.sh -h
Usage: $ ./install.sh [-d <directory>] [-k <ssl_key_file>]
[-c <ssl_cert_file>] [-a <ssl_ca_file>] [-hV]"
-d <directory> installation directory (default: /opt)
-k <ssl_key_file> SSL certificate key file path
-c <ssl_cert_file> SSL certificate file path
-a <ssl_ca_file> CA certificate file path
-h print this help
-V print script version number and exit
Example: # ./install.sh -d /opt -k /etc/ssl/private/server.key
-c /etc/ssl/certs/server.pem
-a /etc/ssl/certs/bundle.pem
You must be root for running this script.
4. Configuration files
You are advised to check configuration file warden-apache.conf,
warden-server.conf and warden-client.conf in warden-server/etc/
directory after installation. For more information about post-installation
steps see INSTALL file in 'doc' directory.
Although this is the Warden server package it also contains several
functions (for administration and maintenance) that are strictly
client-side in a way the Warden system handles functions. Therefore you have
to check both server and client config files to make sure your installation
of the Warden server was successful and complete.
SOAP protocol is used for handling communication between server and clients.
Therefore, correct URI of Warden server must be set.
Authentication of clients and server is performed using client and server
SSL certificates. Both clients and server must have valid certificate.
Configuration files contain following parameters:
a) warden-client.conf:
URI - URI Warden server
e.g. 'https://warden.server.com:443/Warden'
SSL_KEY_FILE - path to a host key file,
e.g. '/etc/ssl/private/warden.server.com.key'
SSL_CERT_FILE - path to a host certificate file,
e.g. '/etc/ssl/certs/warden.server.com.pem'
SSL_CA_FILE - path to a CA file
e.g. '/etc/ssl/certs/tcs-ca-bundle.pem'
b) warden-server.conf:
The Warden server configuration file contains:
BASEDIR - base directory of the Warden server
e.g. /opt/warden-server/
SYSLOG - enable/disable syslog logging
e.g. 1
SYSLOG_VERBOSE - enable/disable logging in verbose mode (stack info added)
e.g. 1
SYSLOG_FACILITY - syslog facility
e.g. local7
DB_NAME - MySQL database name of Warden server
e.g. warden
DB_USER - MySQL database user of Warden server
e.g. warden
DB_PASS - MySQL database password of Warden server
DB_HOST - MySQL database host
e.g. localhost
MAX_EVENTS_LIMIT - server limit of maximum number of events that can be
delivered to one client in one batch
e.g. 1000000
VALID_STRINGS - validation hash containing allowed event attributes
e.g.
%VALID_STRINGS = (
'type' => ['portscan', 'bruteforce', 'probe', 'spam', 'phishing', 'botnet_c_c', 'dos', 'malware', 'copyright', 'webattack', 'test', 'other'],
'source_type' => ['IP', 'URL', 'Reply-To:']
);
c) warden-apache.conf
The Apache2 configuration file for Warden server:
SSLEngine on
SSLVerifyDepth 3
SSLVerifyClient require
SSLOptions +StdEnvVars +ExportCertData
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
SSLCertificateFile <path_to_server_certificate>
SSLCertificateKeyFile <path_to_server_certificate_key>
SSLCACertificateFile <path_to_CA_certificate>
PerlOptions +Parent
PerlSwitches -I <path_to_warden_server_libs>
<Location /Warden>
SetHandler perl-script
PerlHandler Warden::ApacheDispatch
SSLOptions +StdEnvVars
</Location>
--------------------------------------------------------------------------------
D. Update
For update of the Warden server package from local machine use update.sh.
Default destination directory is /opt/warden-server/.
For more information about update.sh options run update.sh -h
Usage: $ ./update.sh [-d <directory>] [-hV]
-d <directory> destination directory (default: /opt)
-h print this help
-V print script version number and exit
Example: # ./update.sh -d /opt
You must be root for running this script.
For more information about post-update steps see UPDATE file in 'doc'
directory.
--------------------------------------------------------------------------------
E. Uninstallation
For uninstallation of the Warden server package from local machine use uninstall.sh.
Default uninstallation directory is /opt/warden-server/.
For more information about uninstall.sh options run uninstall.sh -h
Usage: $ ./uninstall.sh [-d <directory>] [-hV]
-d <directory> uninstallation directory (default: /opt)
-h print this help
-V print script version number and exit
Example: # ./uninstall.sh -d /opt
You must be root for running this script.
For more information about post-uninstallation steps see UNINSTALL file in 'doc'
directory.
--------------------------------------------------------------------------------
F. Miscellaneous
1. Error Messages
Error messages of the server functions are sent via Syslog.
Default is local7 facility.
2. Firewall Settings
Make sure that the TCP port listed in /etc/apache2/sites-enables/default(-ssl)
is allowed on your firewall.
3. Privileges
The Warden server runs only under root privileges.
4. Known Issues
No issues are known.
--------------------------------------------------------------------------------
G. Registration of Clients
The Warden server administrator is responsible for registering new clients or
removing those already registered. Both registration or unregistration scripts
are provided in the Warden server package. Those scripts should be run from
localhost (the same machine the Warden server is installed and running on).
Members of Warden community who would like to have their client registered must
contact the Warden server administrator with the requirement. This is usually
done via secured e-mail. Requestor should provide all important data to the
Warden server administrator so that the client can be successfully registered.
1. Register Sender
New sender clients are registered in Warden system via registerSender.pl.
Following attributes must be provided in order to register new client
successfully:
hostname - hostname of the client,
requestor - organization or authorized person who demands new
client registration,
service - name of the service of a new registered client,
description_tags - tags describing the nature of the service,
ip_net_client - CIDR the client is only allowed to communicate from.
One can run registerSender.pl with -h argument to see a help.
2. Register Receiver
New receiver clients are registered in Warden system via
registerReceiver.pl.
Following attributes must be provided in order to register new client
successfully:
hostname - hostname of the client,
requestor - organization or authorized person who demands new
client registration,
type - the type of events the client wish to receive or '_any_'
for receiving of all types of events,
receive_own_events - boolean value describing if events originating from
the same CIDR will be sent to the client,
ip_net_client - CIDR the client is only allowed to communicate from.
One can run registerReceiver.pl with -h argument to see a help.
3. Unregister Client
In the Warden system, already registered clients can be removed
(unregistered) via unregisterClient.pl.
Following attribute must be provided in order to unregister existing client
successfully:
client_id - ID of the client that should be removed (unregistered).
One can run unregisterClient.pl with -h argument to see a help.
The process of unregistration deletes this client from clients table in DB.
But all messages stored by this client (considering "sender" client) are not
deleted, they are merely set 'invalid' in DB table events.
--------------------------------------------------------------------------------
H. Status Info
Functions in this section show status of the Warden server and active
(registered) clients to the Warden system administrator.
Similarly to (un)registration, these functions should be run from
localhost (e. g. from the same machine the Warden server is installed and
running on).
1. Get Status
Function getStatus is accessible via getStatus.pl. Function has no input
parameters and returns info about the Warden server, its DB status and
event's statistics of active registered senders.
2. Get Clients
Function getClients is accessible via getClients.pl. Function has no input
parameters and returns detailed information about all registered clients.
--------------------------------------------------------------------------------
Copyright (C) 2011-2012 Cesnet z.s.p.o
Uninstallation process
======================
Content
A. Overview
B. Uninstallation step
C. Post-uninstallation steps
--------------------------------------------------------------------------------
A. Overview
-----------
For uninstallation of warden-server package from local machine use uninstall.sh.
Default uninstallation directory is /opt/warden-server/.
For more information about uninstall.sh options run uninstall.sh -h.
You must be root for running this script.
B. Uninstallation step
----------------------
1) Uninstall Warden server package (default installation path)
# /opt/warden-server/uninstall.sh -d /opt
C. Post-uninstallation steps
--------------------------
# a2dismod ssl
# aptitude remove apache2 mysql-server libapache2-mod-perl2 apache2-mpm-prefork
-- MySQL dump 10.11
--
-- Host: localhost Database: warden
-- ------------------------------------------------------
-- Server version 5.0.51a-24+lenny3
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Current Database: `warden`
--
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `warden` /*!40100 DEFAULT CHARACTER SET latin1 */;
USE `warden`;
--
-- Table structure for table `clients`
--
DROP TABLE IF EXISTS `clients`;
SET @saved_cs_client = @@character_set_client;
SET character_set_client = utf8;
CREATE TABLE `clients` (
`client_id` int(11) NOT NULL auto_increment,
`hostname` varchar(256) default NULL,
`registered` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
`requestor` varchar(256) default NULL,
`service` varchar(64) default NULL,
`client_type` varchar(1) default NULL,
`type` varchar(64) default NULL,
`receive_own_events` varchar(1) default NULL,
`description_tags` varchar(256) default NULL,
`ip_net_client` varchar(256) default NULL,
PRIMARY KEY (`client_id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
SET character_set_client = @saved_cs_client;
--
-- Table structure for table `events`
--
DROP TABLE IF EXISTS `events`;
SET @saved_cs_client = @@character_set_client;
SET character_set_client = utf8;
CREATE TABLE `events` (
`id` int(11) NOT NULL auto_increment,
`hostname` varchar(256) default NULL,
`service` varchar(64) default NULL,
`detected` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
`received` timestamp NOT NULL default '0000-00-00 00:00:00',
`type` varchar(64) default NULL,
`source_type` varchar(64) default NULL,
`source` varchar(256) default NULL,
`target_proto` varchar(16) default NULL,
`target_port` int(2) unsigned default NULL,
`attack_scale` int(4) unsigned default NULL,
`note` text,
`priority` int(1) unsigned default NULL,
`timeout` int(2) unsigned default NULL,
`valid` varchar(1) default NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
SET character_set_client = @saved_cs_client;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2012-03-22 15:29:35
ALTER TABLE `clients` CHANGE `registered` `registered` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00';
ALTER TABLE `events` CHANGE `detected` `detected` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00';
warden-server-2.1
#
# warden-apache.conf - configuration file for the Apache server
#
SSLEngine on
SSLVerifyDepth 3
SSLVerifyClient require
SSLOptions +StdEnvVars +ExportCertData
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
SSLCertificateFile /etc/ssl/certs/warden-dev.cesnet.cz.pem
SSLCertificateKeyFile /opt/warden-client/etc/warden-dev.cesnet.cz.key
SSLCACertificateFile /etc/ssl/certs/tcs-ca-bundle.pem
PerlOptions +Parent
PerlSwitches -I/opt/warden-server/lib
<Location /Warden>
SetHandler perl-script
PerlHandler Warden::ApacheDispatch
SSLOptions +StdEnvVars
</Location>
#
# warden-client.conf - configuration file for registration and status clients
#
#-------------------------------------------------------------------------------
# URI - URI address of Warden server
#-------------------------------------------------------------------------------
$URI = "https://warden-dev.cesnet.cz:443/Warden";
#-------------------------------------------------------------------------------
# SSL_KEY_FILE - path to server SSL certificate key file
#-------------------------------------------------------------------------------
$SSL_KEY_FILE = "/etc/ssl/private/warden-dev.cesnet.cz.key";
#-------------------------------------------------------------------------------
# SSL_CERT_FILE - path to server SSL certificate file
#-------------------------------------------------------------------------------
$SSL_CERT_FILE = "/etc/ssl/certs/warden-dev.cesnet.cz.pem";
#-------------------------------------------------------------------------------
# SSL_CA_FILE - path to CA certificate file
#-------------------------------------------------------------------------------
$SSL_CA_FILE = "/etc/ssl/certs/tcs-ca-bundle.pem";
#
# warden-server.conf - configuration file for Warden server
#
#-------------------------------------------------------------------------------
# BASEDIR - base directory of Warden server
#-------------------------------------------------------------------------------
$BASEDIR = '/opt/warden-server';
#-------------------------------------------------------------------------------
# SYSLOG - enable/disable syslog logging
#-------------------------------------------------------------------------------
$SYSLOG = 1;
#-------------------------------------------------------------------------------
# SYSLOG_VERBOSE - enable/disable logging in verbose mode (stack info added)
#-------------------------------------------------------------------------------
$SYSLOG_VERBOSE = 1;
#-------------------------------------------------------------------------------
# SYSLOG_FACILITY - syslog facility
#-------------------------------------------------------------------------------
$SYSLOG_FACILITY = 'local7';
#-------------------------------------------------------------------------------
# DB_NAME - MySQL database name of Warden server
#-------------------------------------------------------------------------------
$DB_NAME = 'warden';
#-------------------------------------------------------------------------------
# DB_USER - MySQL database user of Warden server
#-------------------------------------------------------------------------------
$DB_USER = 'root';
#-------------------------------------------------------------------------------
# DB_PASS - MySQL database password of Warden server
#-------------------------------------------------------------------------------
$DB_PASS = '';
#-------------------------------------------------------------------------------
# DB_HOST - MySQL database host
#-------------------------------------------------------------------------------
$DB_HOST = 'localhost';
#-------------------------------------------------------------------------------
# MAX_EVENTS_LIMIT - server limit of maximum number of events that can be
# delivered to one client in one batch
#-------------------------------------------------------------------------------
$MAX_EVENTS_LIMIT = '1000000';
#-------------------------------------------------------------------------------
# VALID_STRINGS - validation hash containing allowed event attributes
#-------------------------------------------------------------------------------
%VALID_STRINGS = (
'type' => ['portscan', 'bruteforce', 'probe', 'spam', 'phishing', 'botnet_c_c', 'dos', 'malware', 'copyright', 'webattack', 'test', 'other'],
'source_type' => ['IP', 'URL', 'Reply-To:']
);
#!/usr/bin/perl -w
#
# Warden.pm
#
# 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;
use SOAP::Lite;
use Carp;
our $VERSION = "2.1";
################################################################################
# READING OF CONFIGURATION VARIABLES
################################################################################
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
our %VALID_STRINGS = undef;
# load set variables by user
unless (do $conf_file) {
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)
#-------------------------------------------------------------------------------
sub sendMsg
{
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
#-------------------------------------------------------------------------------
sub getAltNames
{
my @an_array;
my $cn = $ENV{'SSL_CLIENT_S_DN_CN'};
push(@an_array, $DBH->quote($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, $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 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 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;");
}
if (!defined $sth) {
sendMsg("err",
"Cannot prepare authorization statement in $function_name: $DBH->errstr",
"Internal 'prepare' server error")
}
$sth->execute($service_type, $client_type);
my ($an, $cidr, $receive_own, $cidr_list);
my $correct_ip_source = 0;
my %ret;
while(($an, $cidr, $receive_own) = $sth->fetchrow()) {
my $cidr_list = Net::CIDR::Lite-> new -> add($cidr);
$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");
return undef;
}
return %ret;
} # END of authorizeClient
################################################################################
# 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)
# parse object (event) parameters
my $service = $data->{'SERVICE'};
my $detected = $data->{'DETECTED'};
my $type = $data->{'TYPE'};
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 $note = $data->{'NOTE'};
my $priority = $data->{'PRIORITY'};
my $timeout = $data->{'TIMEOUT'};
my %client = authorizeClient($alt_names, $ip, $service, $client_type, $function_name);
if(defined %client) {
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);
# client network information
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
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->execute($last_id, $used_limit);
} else {
$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 {
if ($requested_type eq '_any_') {
$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);
} else {
$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];
$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) {
if (scalar @ids == 1) {
sendMsg("info",
"Sent 1 event [#$ids[0]] to '$ip' [CN(AN): $alt_names], client_limit: '$max_rcv_events_limit', requested_type: '$requested_type'",
undef);
} else {
sendMsg("info",
"Sent " . scalar @ids . " events [#$ids[0] - #$ids[-1]] to '$ip' [CN(AN): $alt_names], client_limit: '$max_rcv_events_limit', requested_type: '$requested_type'",
undef);
}
}
return @events;
}
} # END of getNewEvents
#-----------------------------------------------------------------------------
# getLastId - get lastest saved event ID
#-----------------------------------------------------------------------------
sub getLastId
{
my ($class, $arg) = @_;
my $function_name = 'getLastId';
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'};
my $function_name = 'registerSender';
if ($local_ip ne $ip) {
sendMsg("err",
"Unauthorized access to function '$function_name' from: '$ip' ('$cn') - access allowed only from localhost",
"Access denied - access allowed only from localhost");
} else {
# defined variables by method
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) {
sendMsg("err",
"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);
sendMsg("info",
"New sender '$hostname' {service: '$service', cidr: '$ip_net_client'} was registered at $ENV{'SERVER_NAME'}",
undef);
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'};
my $function_name = 'registerReceiver';
if ($local_ip ne $ip) {
sendMsg("err",
"Unauthorized access to function '$function_name' from: '$ip' ('$cn') - access allowed only from localhost",
"Access denied - access allowed only from localhost");
} else {
# variables defined by method
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) {
sendMsg("err",
"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);
sendMsg("info",
"New receiver '$hostname' {type: '$type', cidr: '$ip_net_client'} was registered at $ENV{'SERVER_NAME'}",
undef);
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'};
my $function_name = 'unregisterClient';
if ($local_ip ne $ip) {
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");
}
$sth->execute($client_id);
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->execute($client_id);
$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");
}
$sth->execute($hostname, $service);
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");
}
$sth->execute($client_id);
sendMsg("info",
"Receiver '$hostname' (client_id: '$client_id') was deleted from $ENV{'SERVER_NAME'}",
undef);
return 1;
}
}
}
} # END of unregisterClient
#-----------------------------------------------------------------------------
# getClients - get list of clients which were registered at warden server
#-----------------------------------------------------------------------------
sub getClients
{
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'};
my $function_name = 'getClients';
if ($local_ip ne $ip) {
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 (@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 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 getClients
#-----------------------------------------------------------------------------
# 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'};
my $function_name = 'getStatus';
#-----------------------------------------------------------------------------
# Warden server stats
if ($local_ip ne $ip) {
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)"}),
));
push(@status, $client_status);
}
}
sendMsg("info",
"Sent warden server status info from $ENV{'SERVER_NAME'}",
undef);
return @status;
}
} # END of getStatus
1;
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
use DBD::mysql;
use Data::Dumper;
use Test::More tests => 18;
use Test::MockModule;
use Test::Exception;
use lib '..';
use Warden;
# Fake $ENV values
BEGIN {
$ENV{'SSL_CLIENT_S_DN_CN'} = 'warden-dev.cesnet.cz';
$ENV{'SSL_CLIENT_CERT'} = '-----BEGIN CERTIFICATE-----
MIIEZzCCA0+gAwIBAgIRAKV1flST9dLTKDnQZsgWFmQwDQYJKoZIhvcNAQEFBQAw
NjELMAkGA1UEBhMCTkwxDzANBgNVBAoTBlRFUkVOQTEWMBQGA1UEAxMNVEVSRU5B
IFNTTCBDQTAeFw0xMTA4MTgwMDAwMDBaFw0xMzA4MTcyMzU5NTlaMD0xCzAJBgNV
BAYTAkNaMQ8wDQYDVQQKEwZDRVNORVQxHTAbBgNVBAMTFHdhcmRlbi1kZXYuY2Vz
bmV0LmN6MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxIzyexCL/CB6
COj691JLEWYEVkBLhROqbENk0ka/LbTtS5oNS3WDJVOK7aoHO3yluAdg1VocfFeY
hTgZGAzC82IuNLc+873XTfO2bzotbJL31CBUwpz2QhEAjGgjdvSx++VZAlbDKIa7
RnRcp9AsxPgqlUokVZbmR55sRO7QPaFqBUO061rj56uKzZocXy9RB+vVBQFUR5CF
GKOJhsiRU3GtHpEshKpOGX6jiC5OkUkcVr61Fb4BgKPFFptiiuwTuHUM40PLAdC/
B2lWdt4qPZqeiDFOVAQJH2tpi0Bhn2dmS1ttU76qpfP4RCPXZFxdxqxWgMjGq7Fp
ON3G3ySb3QIDAQABo4IBZzCCAWMwHwYDVR0jBBgwFoAUDL2TaAzz3qujSWsrN1dH
6pDjue0wHQYDVR0OBBYEFMfGqZzdRFP42/ewN/5kPQoI83hxMA4GA1UdDwEB/wQE
AwIFoDAMBgNVHRMBAf8EAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
AjAYBgNVHSAEETAPMA0GCysGAQQBsjEBAgIdMDoGA1UdHwQzMDEwL6AtoCuGKWh0
dHA6Ly9jcmwudGNzLnRlcmVuYS5vcmcvVEVSRU5BU1NMQ0EuY3JsMG0GCCsGAQUF
BwEBBGEwXzA1BggrBgEFBQcwAoYpaHR0cDovL2NydC50Y3MudGVyZW5hLm9yZy9U
RVJFTkFTU0xDQS5jcnQwJgYIKwYBBQUHMAGGGmh0dHA6Ly9vY3NwLnRjcy50ZXJl
bmEub3JnMB8GA1UdEQQYMBaCFHdhcmRlbi1kZXYuY2VzbmV0LmN6MA0GCSqGSIb3
DQEBBQUAA4IBAQC/mZ2bKGj4ysHVB4q/skhYXMiTYBmVD7G7X434YIg70VDBM5IJ
efNNfx8HNdCprboX5PSPCpxl9n3TPARnEO2cm7XvVvt+wkdjNOys8z7a2DIPaeJ+
An3sIPzWUtm85UxujghIJ9nPh1ovZ75cQ2HF5C79qCyKzHtfP77Kq61Nm2Ay4aTq
gWyjFAPRjnB9fczhjdzntVRdjUFVq8z4ifq3Lv+hbN6aOjhfKRt8Ksr3IFlfKJdy
0sE0lEZdjG+O8rsuHCT/c+90IvGsG5JLT5SPJIxwQ1+fPJDfB37VWmUC9meSe7rt
iP0EQsnY1ytKuyUylJl0FiF/wG3rB8N7qlua
-----END CERTIFICATE-----';
$ENV{'REMOTE_ADDR'} = '195.113.161.39';
$ENV{'SERVER_NAME'} = 'warden-dev.cesnet.cz';
$ENV{'SERVER_ADDR'} = '195.113.161.39';
$ENV{'SERVER_PORT'} = '443';
$Warden::MAX_EVENTS_LIMIT = "1000001";
%Warden::VALID_STRINGS = (
'type' => ['portscan', 'bruteforce', 'probe', 'spam', 'phishing', 'botnet_c_c', 'dos', 'malware', 'copyright', 'webattack', 'test', 'other'],
'source_type' => ['IP', 'URL', 'Reply-To:']);
my $correctDBH = DBI->connect("DBI:mysql:database=warden;host=localhost", "root", "w4rd3n&r00t", {RaiseError => 1, mysql_auto_reconnect => 1}) || die "Could not connect to database: $DBI::errstr";
my $failDBH;
$Warden::DBH = $correctDBH;
}
my $ret;
# Alternate names test
print "GetAltNames test\n";
# 1
#$alt_names, $ip, $service_type, $client_type, $function_name
lives_and( sub{ is Warden::getAltNames("warden-dev.cesnet.cz","195.113.161.39","honeyscan","s","someEvent"), "'warden-dev.cesnet.cz','warden-dev.cesnet.cz'"}, 'everything is OK.');
#print "$@\n";
# Client authorizaton test
print "AuthorizeClient tests\n";
# 2
dies_ok( sub{ Warden::authorizeClient("'warden-dev.cesnet.cz','warden-dev.cesnet.cz'","195.113.161.39","honeyscan","s","badAndUglyEvent") }, 'badAndUglyEvent: die (doesn\'t exist)' );
#print "$@\n";
# 3
dies_ok( sub{ Warden::authorizeClient("'warden-dev.cesnet.cz','warden-dev.cesnet.cz'","195.113.161.222","badType","s","saveNewEvent")}, 'saveNewEvent: client is not registered');
#print "$@\n";
# 4
dies_ok( sub{ Warden::authorizeClient("'warden-dev.cesnet.cz','warden-dev.cesnet.cz'","195.113.161.222","badType","s","getNewEvents")}, 'getNewEvents: client is not registered');
#print "$@\n";
# 5
lives_and( sub{ is Warden::authorizeClient("'warden-dev.cesnet.cz','warden-dev.cesnet.cz'","195.113.161.39","honeyscan","s","saveNewEvent"), '3/8'}, 'saveNewEvent: OK.');
#print "$@\n";
# 6
lives_and( sub{ is Warden::authorizeClient("'warden-dev.cesnet.cz','warden-dev.cesnet.cz'","195.113.161.39","any","r","getNewEvents"), '3/8'}, 'getNewEvents: OK.');
#print "$@\n";
# Save event test
print "SaveNewEvent tests\n";
# TODO: run database and SELECT error checks
my %event = ('TYPE' => 'badType', 'DETECTED' => '2012-09-18T06:06:06+01:00', 'SERVICE' => 'test', 'SOURCE_TYPE' => 'IP', 'SOURCE' => '123.123.123.123', 'TARGET_PROTO' => 'TCP', 'TARGET_PORT' => '22', 'ATTACK_SCALE' => '1234567890', 'NOTE' => 'Unit testing event', 'PRIORITY' => '1', 'TIMEOUT' => '20');
dies_ok( sub{ $ret = Warden::saveNewEvent("test", \%event)}, 'bad type');
%event = ('TYPE' => 'test', 'DETECTED' => '2012-09-18T06:06:06+01:00', 'SERVICE' => 'test', 'SOURCE_TYPE' => 'BADTYPE', 'SOURCE' => '123.123.123.123', 'TARGET_PROTO' => 'TCP', 'TARGET_PORT' => '22', 'ATTACK_SCALE' => '1234567890', 'NOTE' => 'Unit testing event', 'PRIORITY' => '1', 'TIMEOUT' => '20');
dies_ok( sub{ $ret = Warden::saveNewEvent("test", \%event)}, 'bad source type.');
%event = ('TYPE' => 'test', 'DETECTED' => 'XXXX-09-18T06:06:06+01:00', 'SERVICE' => 'test', 'SOURCE_TYPE' => 'IP', 'SOURCE' => '123.123.123.123', 'TARGET_PROTO' => 'TCP', 'TARGET_PORT' => '22', 'ATTACK_SCALE' => '1234567890', 'NOTE' => 'Unit testing event', 'PRIORITY' => '1', 'TIMEOUT' => '20');
dies_ok( sub{ $ret = Warden::saveNewEvent("test", \%event)}, 'bad date format.');
%event = ('TYPE' => 'test', 'DETECTED' => '2012-09-18T06:06:06+01:00', 'SERVICE' => 'test', 'SOURCE_TYPE' => 'IP', 'SOURCE' => '123.123.123.123', 'TARGET_PROTO' => 'TCP', 'TARGET_PORT' => 'XXX', 'ATTACK_SCALE' => '1234567890', 'NOTE' => 'Unit testing event', 'PRIORITY' => '1', 'TIMEOUT' => '20');
dies_ok( sub{ $ret = Warden::saveNewEvent("test", \%event)}, 'port is not a number.');
%event = ('TYPE' => 'test', 'DETECTED' => '2012-09-18T06:06:06+01:00', 'SERVICE' => 'test', 'SOURCE_TYPE' => 'IP', 'SOURCE' => '123.123.123.123', 'TARGET_PROTO' => 'TCP', 'TARGET_PORT' => '22', 'ATTACK_SCALE' => 'XXX', 'NOTE' => 'Unit testing event', 'PRIORITY' => '1', 'TIMEOUT' => '20');
dies_ok( sub{ $ret = Warden::saveNewEvent("test", \%event)}, 'attack scale is not a number.');
%event = ('TYPE' => 'test', 'DETECTED' => '2012-09-18T06:06:06+01:00', 'SERVICE' => 'test', 'SOURCE_TYPE' => 'IP', 'SOURCE' => '123.123.123.123', 'TARGET_PROTO' => 'TCP', 'TARGET_PORT' => '22', 'ATTACK_SCALE' => '1234567890', 'NOTE' => 'Unit testing event', 'PRIORITY' => 'XXX', 'TIMEOUT' => '20');
dies_ok( sub{ $ret = Warden::saveNewEvent("test", \%event)}, 'priority is not a number.');
%event = ('TYPE' => 'test', 'DETECTED' => '2012-09-18T06:06:06+01:00', 'SERVICE' => 'test', 'SOURCE_TYPE' => 'IP', 'SOURCE' => '123.123.123.123', 'TARGET_PROTO' => 'TCP', 'TARGET_PORT' => '22', 'ATTACK_SCALE' => '1234567890', 'NOTE' => 'Unit testing event', 'PRIORITY' => '1', 'TIMEOUT' => 'XXX');
dies_ok( sub{ $ret = Warden::saveNewEvent("test", \%event)}, 'timeout is not a number.');
%event = ('TYPE' => 'test', 'DETECTED' => '2012-09-18T06:06:06+01:00', 'SERVICE' => 'test', 'SOURCE_TYPE' => 'IP', 'SOURCE' => '123.123.123.123', 'TARGET_PROTO' => 'TCP', 'TARGET_PORT' => '22', 'ATTACK_SCALE' => '1234567890', 'NOTE' => 'Unit testing event', 'PRIORITY' => '1', 'TIMEOUT' => '20');
lives_ok( sub{ $ret = Warden::saveNewEvent("test", \%event)}, 'everything is fine.');
# Get new event
print "GetNewEvents tests\n";
# TODO: run database and SELECT error checks
# dies_ok {Warden::getNewEvents()} 'Cannot work with the database.';
my %getEventHash = ('REQUESTED_TYPE' => 'any', 'LAST_ID' => '1', 'MAX_RCV_EVENTS_LIMIT' => '10');
lives_ok( sub{ Warden::getNewEvents("test",\%getEventHash)}, 'everything is fine.');
# Get last ID test
print "GetLastId test\n";
# TODO: run database and SELECT error checks
lives_ok( sub{Warden::getLastId() =~ /^\d+$/}, 'getLastID is OK.');
# Get clients test
print "GetClients\n";
# TODO: run database and SELECT error checks
lives_ok( sub{ Warden::getClients()}, 'everything is fine.');
print "GetStatus\n";
# run database and SELECT error checks
lives_ok( sub{ Warden::getStatus()}, 'everything is fine.');
#!/usr/bin/perl -w
#
# ApacheDispatch.pm
#
# 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::ApacheDispatch;
use strict;
use SOAP::Transport::HTTP;
our $VERSION = "2.0";
# set server dispatch_to
my $server = SOAP::Transport::HTTP::Apache->dispatch_to('.','Warden');
#-------------------------------------------------------------------------------
# handler - call handler for Warden server
#-------------------------------------------------------------------------------
sub handler {
$server->handler(@_)
}
1;
#!/usr/bin/perl -w
#
# WardenConf.pm
#
# Copyright (C) 2011-2012 Cesnet z.s.p.o
#
# Use of this source is governed by a BSD-style license, see LICENSE file.
package WardenConf;
use strict;
our $VERSION = "2.0";
#-------------------------------------------------------------------------------
# loadConf - load variables from configuration file
#-------------------------------------------------------------------------------
sub loadConf
{
my $conf_file = shift;
# preset of default variables
our $URI = undef;
our $SSL_KEY_FILE = undef;
our $SSL_CERT_FILE = undef;
our $SSL_CA_FILE = undef;
# load set variables by user
unless (do $conf_file) {
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
}
return ($URI, $SSL_KEY_FILE, $SSL_CERT_FILE, $SSL_CA_FILE);
} # End of loadConf
1;
#!/usr/bin/perl -w
#
# WardenReg.pm
#
# Copyright (C) 2011-2012 Cesnet z.s.p.o
#
# Use of this source is governed by a BSD-style license, see LICENSE file.
package WardenReg;
use strict;
use SOAP::Lite;
use IO::Socket::SSL qw(debug1);
use SOAP::Transport::HTTP;
our $VERSION = "2.0";
#-------------------------------------------------------------------------------
# errMsg - print error message and die
#-------------------------------------------------------------------------------
sub errMsg
{
my $msg = shift;
die($msg . "\n");
} # End of errMsg
#-------------------------------------------------------------------------------
# c2s - connect to server, send request and receive response
#-------------------------------------------------------------------------------
sub c2s
{
my $uri = shift;
my $ssl_key_file = shift;
my $ssl_cert_file = shift;
my $ssl_ca_file = shift;
my $method = shift;
my $data = shift;
my $client;
my ($server, $port, $service) = $uri =~ /https:\/\/(.+)\:(\d+)\/(.+)/;
if (!($client = SOAP::Transport::HTTP::Client->new())) {
errMsg("Sorry, unable to create socket: " . &SOAP::Transport::HTTP::Client::errstr)
}
$client->timeout(10);
$client->ssl_opts( verify_hostname => 1,
SSL_use_cert => 1,
SSL_verify_mode => 0x02,
SSL_key_file => $ssl_key_file,
SSL_cert_file => $ssl_cert_file,
SSL_ca_file => $ssl_ca_file);
# set URI and serialize SOAP envelope and data object
my $soap = SOAP::Lite->uri($service)->proxy($uri);
my $envelope = $soap->serializer->envelope(method => $method, $data);
# set URI and send serialized SOAP envelope and data
my $server_uri = "https://$server:$port/$service";
my $result = $client->send_receive(envelope => $envelope, endpoint => $server_uri);
# check server response
if (!defined $result) {
errMsg("Error: server returned empty response." . "\n" . "Problem with used SSL ceritificates or Warden server at $server:$port is down.");
} else {
# deserialized response from server -> create SOAP envelope and data object
my $response = $soap->deserializer->deserialize($result);
# check SOAP fault status
$response->fault ? errMsg("Server sent error message:: " . $response->faultstring) : return 1;
}
}
#-------------------------------------------------------------------------------
# registerSender - register new warden sender
#-------------------------------------------------------------------------------
sub registerSender
{
my $warden_path = shift;
my $hostname = shift;
my $requestor = shift;
my $service = shift;
my $description_tags = shift;
my $ip_net_client = shift;
my $etcdir = $warden_path . "/etc/";
my $libdir = $warden_path . "/lib/";
# read the config file
require $libdir . "WardenConf.pm";
my $conf_file = $etcdir . "warden-client.conf";
my ($uri, $ssl_key_file, $ssl_cert_file, $ssl_ca_file) = WardenConf::loadConf($conf_file);
# create SOAP data obejct
my $request_data = SOAP::Data->name(client => \SOAP::Data->value(
SOAP::Data->name(HOSTNAME => $hostname),
SOAP::Data->name(REQUESTOR => $requestor),
SOAP::Data->name(SERVICE => $service),
SOAP::Data->name(DESCRIPTION_TAGS => $description_tags),
SOAP::Data->name(IP_NET_CLIENT => $ip_net_client)
));
my $result = c2s($uri, $ssl_key_file, $ssl_cert_file, $ssl_ca_file, "registerSender", $request_data);
$result ? return 1 : return 0;
} # End of registerSender
#-------------------------------------------------------------------------------
# registerReceiver - register new warden receiver
#-------------------------------------------------------------------------------
sub registerReceiver
{
my $warden_path = shift;
my $hostname = shift;
my $requestor = shift;
my $type = shift;
my $receive_own_events = shift;
my $ip_net_client = shift;
my $etcdir = $warden_path . "/etc/";
my $libdir = $warden_path . "/lib/";
# read the config file
require $libdir . "WardenConf.pm";
my $conf_file = $etcdir . "warden-client.conf";
my ($uri, $ssl_key_file, $ssl_cert_file, $ssl_ca_file) = WardenConf::loadConf($conf_file);
# create SOAP data obejct
my $request_data = SOAP::Data->name(client => \SOAP::Data->value(
SOAP::Data->name(HOSTNAME => $hostname),
SOAP::Data->name(REQUESTOR => $requestor),
SOAP::Data->name(TYPE => $type),
SOAP::Data->name(RECEIVE_OWN_EVENTS => $receive_own_events),
SOAP::Data->name(IP_NET_CLIENT => $ip_net_client)
));
my $result = c2s($uri, $ssl_key_file, $ssl_cert_file, $ssl_ca_file, "registerReceiver", $request_data);
$result ? return 1 : return 0;
} # End of registerReceiver
#-------------------------------------------------------------------------------
# unregisterClient - unregister client from warden server
#-------------------------------------------------------------------------------
sub unregisterClient
{
my $warden_path = shift;
my $client_id = shift;
my $etcdir = $warden_path . "/etc/";
my $libdir = $warden_path . "/lib/";
# read the config file
require $libdir . "WardenConf.pm";
my $conf_file = $etcdir . "warden-client.conf";
my ($uri, $ssl_key_file, $ssl_cert_file, $ssl_ca_file) = WardenConf::loadConf($conf_file);
# create SOAP data obejct
my $request_data = SOAP::Data->name(client => \SOAP::Data->value(
SOAP::Data->name(CLIENT_ID => $client_id)
));
my $result = c2s($uri, $ssl_key_file, $ssl_cert_file, $ssl_ca_file, "unregisterClient", $request_data);
$result ? return 1 : return 0;
} # End of unregisterClient
1;
#!/usr/bin/perl -w
#
# WardenStatus.pm
#
# Copyright (C) 2011-2012 Cesnet z.s.p.o
#
# Use of this source is governed by a BSD-style license, see LICENSE file.
package WardenStatus;
use strict;
use SOAP::Lite;
use IO::Socket::SSL qw(debug1);
use SOAP::Transport::HTTP;
our $VERSION = "2.0";
#-------------------------------------------------------------------------------
# errMsg - print error message and die
#-------------------------------------------------------------------------------
sub errMsg
{
my $msg = shift;
die($msg . "\n");
} # End of errMsg
#-------------------------------------------------------------------------------
# c2s - connect to server, send request and receive response
#-------------------------------------------------------------------------------
sub c2s
{
my $uri = shift;
my $ssl_key_file = shift;
my $ssl_cert_file = shift;
my $ssl_ca_file = shift;
my $method = shift;
my $data = shift;
my $client;
my ($server, $port, $service) = $uri =~ /https:\/\/(.+)\:(\d+)\/(.+)/;
if (!($client = SOAP::Transport::HTTP::Client->new())) {
errMsg("Sorry, unable to create socket: " . &SOAP::Transport::HTTP::Client::errstr)
}
$client->timeout(10);
$client->ssl_opts(verify_hostname => 1,
SSL_use_cert => 1,
SSL_verify_mode => 0x02,
SSL_key_file => $ssl_key_file,
SSL_cert_file => $ssl_cert_file,
SSL_ca_file => $ssl_ca_file);
# set URI and serialize SOAP envelope and data object
my $soap = SOAP::Lite->uri($service)->proxy($uri);
my $envelope = $soap->serializer->envelope(method => $method);
# setting of TCP URI and send serialized SOAP envelope and data
my $server_uri = "https://$server:$port/$service";
my $result = $client->send_receive(envelope => $envelope, endpoint => $server_uri);
# check server response
if (!defined $result) {
errMsg("Error: server returned empty response." . "\n" . "Problem with used SSL ceritificates or Warden server at $server:$port is down.");
} else {
# deserialized response from server -> create SOAP envelope and data object
my $response = $soap->deserializer->deserialize($result);
# check SOAP fault status
$response->fault ? errMsg("Server sent error message:: " . $response->faultstring) : return $response;
}
}
#-------------------------------------------------------------------------------
# getClients - get list of registered clients
#-------------------------------------------------------------------------------
sub getClients
{
my $warden_path = shift;
my $etcdir = $warden_path . "/etc/";
my $libdir = $warden_path . "/lib/";
# read the config file
require $libdir . "WardenConf.pm";
my $conf_file = $etcdir . "warden-client.conf";
my ($uri, $ssl_key_file, $ssl_cert_file, $ssl_ca_file) = WardenConf::loadConf($conf_file);
# call method getClients on the Warden server
my $response = c2s($uri, $ssl_key_file, $ssl_cert_file, $ssl_ca_file, "getClients");
# parse returned SOAP data object with clients
my @clients;
my ($client_id, $hostname, $registered, $requestor, $service, $client_type, $type, $receive_own_events, $description_tags, $ip_net_client);
my @response_list = $response->valueof('/Envelope/Body/getClientsResponse/client/');
while (scalar @response_list) {
my $response_data = shift(@response_list);
my @client;
$client_id = $response_data->{'CLIENT_ID'} ;
$hostname = $response_data->{'HOSTNAME'};
$registered = $response_data->{'REGISTERED'};
$requestor = $response_data->{'REQUESTOR'};
$service = defined $response_data->{'SERVICE'} ? $response_data->{'SERVICE'} : "-";
$client_type = $response_data->{'CLIENT_TYPE'};
$type = defined $response_data->{'TYPE'} ? $response_data->{'TYPE'} : "-";
$receive_own_events = defined $response_data->{'RECEIVE_OWN_EVENTS'} ? $response_data->{'RECEIVE_OWN_EVENTS'} : "-";
$description_tags = defined $response_data->{'DESCRIPTION_TAGS'} ? $response_data->{'DESCRIPTION_TAGS'} : "-";
$ip_net_client = $response_data->{'IP_NET_CLIENT'};
# push received clients from warden server into @clients which is returned
@client = ($client_id, $hostname, $registered, $requestor, $service, $client_type, $type, $receive_own_events, $description_tags, $ip_net_client);
push (@clients,\@client);
}
return @clients;
} # End of getClients
#-------------------------------------------------------------------------------
# getStatus - get warden server status
#-------------------------------------------------------------------------------
sub getStatus
{
my $warden_path = shift;
my $etcdir = $warden_path . "/etc/";
my $libdir = $warden_path . "/lib/";
# read the config file
require $libdir . "WardenConf.pm";
my $conf_file = $etcdir . "warden-client.conf";
my ($uri, $ssl_key_file, $ssl_cert_file, $ssl_ca_file) = WardenConf::loadConf($conf_file);
# call method getStatus on Warden server
my $response = c2s($uri, $ssl_key_file, $ssl_cert_file, $ssl_ca_file, "getStatus");
#-----------------------------------------------------------------------------
# parse returned SOAP object with server status
my @response_list = $response->valueof('/Envelope/Body/getStatusResponse/server_status/');
my $response_data = shift(@response_list);
my $version = $response_data->{'VERSION'};
my $server_hostname = $response_data->{'HOSTNAME'};
my $ip_address = $response_data->{'IP_ADDRESS'};
my $port = $response_data->{'PORT'};
my $db_name = $response_data->{'DB_NAME'};
my $db_user = $response_data->{'DB_USER'};
my $db_host = $response_data->{'DB_HOST'};
my $syslog = $response_data->{'SYSLOG'};
my $syslog_verbose = $response_data->{'SYSLOG_VERBOSE'};
my $syslog_facility = $response_data->{'SYSLOG_FACILITY'};
my $db_size = $response_data->{'DB_SIZE'};
my $events_sum = $response_data->{'EVENTS_SUM'};
my $events_last_id = $response_data->{'EVENTS_LAST_ID'};
my $events_first_timestamp = $response_data->{'EVENTS_FIRST_TIMESTAMP'};
my $events_last_timestamp = $response_data->{'EVENTS_LAST_TIMESTAMP'};
my $clients_sum = $response_data->{'CLIENTS_SUM'};
my @server_status = ($version, $server_hostname, $ip_address, $port, $db_name, $db_user, $db_host, $syslog, $syslog_verbose, $syslog_facility, $db_size, $events_sum, $events_last_id, $events_first_timestamp, $events_last_timestamp, $clients_sum);
my @status;
push(@status, \@server_status);
#-----------------------------------------------------------------------------
# parse returned SOAP object with client status
@response_list = $response->valueof('/Envelope/Body/getStatusResponse/client_status/');
my ($client_id, $hostname, $service, $count, $timestamp);
while (scalar @response_list) {
my $response_data = shift(@response_list);
my @client_status;
my $client_id = $response_data->{'CLIENT_ID'};
my $hostname = $response_data->{'HOSTNAME'};
my $service = $response_data->{'SERVICE'};
my $count = $response_data->{'COUNT'};
my $timestamp = $response_data->{'TIMESTAMP'};
@client_status = ($client_id, $hostname, $service, $count, $timestamp);
push(@status, \@client_status);
}
return @status;
} # End of getStatus
1;
#!/bin/bash
#
# install.sh
#
# Copyright (C) 2011-2012 Cesnet z.s.p.o
#
# Use of this source is governed by a BSD-style license, see LICENSE file.
VERSION="2.1"
#-------------------------------------------------------------------------------
# FUNCTIONS
#-------------------------------------------------------------------------------
usage()
{
echo "Usage: `basename $0` [-d <directory>] [-k <ssl_key_file>] [-c <ssl_cert_file>] [-a <ssl_ca_file>] [-hV]"
echo "-d <directory> installation directory (default: /opt)"
echo "-k <ssl_key_file> path to SSL certificate key file"
echo "-c <ssl_cert_file> path to SSL certificate file"
echo "-a <ssl_ca_file> path to CA certificate file"
echo "-h print this help"
echo "-V print script version number and exit"
echo
echo "Example: # ./`basename $0` -d /opt -k /etc/ssl/private/server.key -c /etc/ssl/certs/server.pem -a /etc/ssl/certs/tcs-ca-bundle.pem"
echo
echo "Note: You must be root for running this script."
echo " For more information about installation process, see README file (section Installation)."
echo
exit 0
}
version()
{
echo "`basename ${0}` - current version is $VERSION"
exit 0
}
err()
{
echo "FAILED!"
cat $err
rm -rf $err
echo
echo "Installation of $package_version package FAILED!!!"
exit 1
}
err_clean()
{
echo "FAILED!"
echo " -> Uninstalling server package ... OK"
rm -rf $server_path > /dev/null 2>&1
cat $err
rm -rf $err
echo
echo "Installation of $package_version package FAILED!!!"
exit 1
}
root_chck()
{
if [ $UID -ne 0 ]; then
echo "You must be root for running this script!"
exit 1
fi
}
params_chck()
{
if [ -z $prefix ]; then
prefix=/opt
echo "Warning: parameter -d <directory> is not set - default installation directory is ${prefix}!"
fi
if [ -z $key ]; then
echo "Parameter -k <ssl_key_file> is not set!"
exit 1
fi
if [ -z $cert ]; then
echo "Parameter -c <ssl_cert_file> is not set!"
exit 1
fi
if [ -z $ca_file ]; then
echo "Parameter -a <ssl_ca_file> is not set!"
exit 1
fi
}
old_package_chck()
{
old_package_version_file="${etc}/package_version"
if [ -f $old_package_version_file ]; then
old_package_version=`cat $old_package_version_file`
echo "Sorry, but $old_package_version package is installed!"
echo "For update of warden server package please use update.sh"
exit 1
fi
}
perl_chck()
{
echo -n "Checking Perl interpreter ... "
if which perl 1> /dev/null; then
echo "OK"
else
echo "FAILED!"
echo "Error: Perl interpreter is not installed!"
exit 1
fi
}
modules_chck()
{
for module in ${modules[@]};
do
echo -n "Checking $module module ... "
if perl -e "use $module" 2> $err; then
echo "OK"
else
err
fi
done
}
make_warden_dir()
{
echo -n "Creating Warden server directory ... "
test -d $prefix || mkdir -p $prefix
if cp -R ${dirname}/warden-server $prefix 2> $err; then
echo "OK"
else
err_clean
fi
files=(AUTHORS CHANGELOG INSTALL UNINSTALL LICENSE README)
for file in ${files[@]};
do
cp ${dirname}/$file ${server_path}/doc
done
cp ${dirname}/uninstall.sh $server_path
}
make_client_conf()
{
echo -n "Creating client configuration file ... "
echo "#
# warden-client.conf - configuration file for the warden sender/receiver client
#
#-------------------------------------------------------------------------------
# URI - URI address of Warden server
#-------------------------------------------------------------------------------
\$URI = \"https://${hostname}:443/Warden\";
#-------------------------------------------------------------------------------
# SSL_KEY_FILE - path to client SSL certificate key file
#-------------------------------------------------------------------------------
\$SSL_KEY_FILE = \"${key}\";
#-------------------------------------------------------------------------------
# SSL_CERT_FILE - path to client SSL certificate file
#-------------------------------------------------------------------------------
\$SSL_CERT_FILE = \"${cert}\";
#-------------------------------------------------------------------------------
# SSL_CA_FILE - path to CA certificate file
#-------------------------------------------------------------------------------
\$SSL_CA_FILE = \"${ca_file}\";
" > $client_conf 2> $err; ret_val=`echo $?`
if [ $ret_val -eq 0 ]; then
echo "OK"
else
err_clean
fi
}
make_server_conf()
{
echo -n "Creating server configuration file ... "
echo "#
# warden-server.conf - configuration file for Warden server
#
#-------------------------------------------------------------------------------
# BASEDIR - base directory of Warden server
#-------------------------------------------------------------------------------
\$BASEDIR = \'${server_path}\';
#-------------------------------------------------------------------------------
# SYSLOG - enable/disable syslog logging
#-------------------------------------------------------------------------------
\$SYSLOG = 1;
#-------------------------------------------------------------------------------
# SYSLOG_VERBOSE - enable/disable logging in verbose mode (stack info added)
#-------------------------------------------------------------------------------
\$SYSLOG_VERBOSE = 1;
#-------------------------------------------------------------------------------
# SYSLOG_FACILITY - syslog facility
#-------------------------------------------------------------------------------
\$YSLOG_FACILITY = \'local7\';
#-------------------------------------------------------------------------------
# DB_NAME - MySQL database name of Warden server
#-------------------------------------------------------------------------------
\$DB_NAME = \'warden\';
#-------------------------------------------------------------------------------
# DB_USER - MySQL database user of Warden server
#-------------------------------------------------------------------------------
\$DB_USER = \'username\';
#-------------------------------------------------------------------------------
# DB_PASS - MySQL database password of Warden server
#-------------------------------------------------------------------------------
\$DB_PASS = \'\';
#-------------------------------------------------------------------------------
# DB_HOST - MySQL database host
#-------------------------------------------------------------------------------
\$DB_HOST = \'localhost\';
#-------------------------------------------------------------------------------
# MAX_EVENTS_LIMIT - server limit of maximum number of events that can be
# delivered to one client in one batch
#-------------------------------------------------------------------------------
\$MAX_EVENTS_LIMIT = \'1000000\';
#-------------------------------------------------------------------------------
# VALID_STRINGS - validation hash containing allowed event attributes
#-------------------------------------------------------------------------------
\%VALID_STRINGS = (
\'type\' => [\'portscan\', \'bruteforce\', \'probe\', \'spam\', \'phishing\', \'botnet_c_c\', \'dos\', \'malware\', \'copyright\', \'webattack\', \'test\', \'other\', \'_any_\'],
\'source_type' => ['IP', 'URL', 'Reply-To:']
);
" > $server_conf 2> $err; ret_val=`echo $?`
if [ $ret_val -eq 0 ]; then
echo "OK"
else
err_clean
fi
}
changeServerPath()
{
echo "Update server path ...";
for file in `ls -1 $bin`
do
echo "- update server path: ${bin}/$file"
perl -pi -e "s#/opt#${prefix}#" ${bin}/$file
done
echo "- update server path: $apache_conf"
perl -pi -e "s#/opt#${prefix}#" $apache_conf
echo "- update server path: ${lib}/Warden.pm"
perl -pi -e "s#/opt#${prefix}#" ${lib}/Warden.pm
}
updateCertsPath()
{
echo "- update certs path: $apache_conf"
perl -pi -e "s#server-cert.pem#${cert}#" $apache_conf
perl -pi -e "s#server-key.pem#${key}#" $apache_conf
perl -pi -e "s#ca-cert.pem#${ca_file}#" $apache_conf
}
create_symlinks()
{
echo "Creating symbolic links ..."
for file in `ls -1 $bin`
do
echo "- making symlink: ${local_bin}/$file -> ${bin}/$file"
ln -s ${bin}/$file ${local_bin}/$file
done
}
#-------------------------------------------------------------------------------
# MAIN
#-------------------------------------------------------------------------------
# list of used Perl modules
modules=(SOAP::Lite SOAP::Transport::HTTP DBI DBD::mysql Format::Human::Bytes Sys::Syslog File::Basename Net::CIDR::Lite DateTime Getopt::Std Switch IO::Socket::SSL MIME::Base64 Crypt::X509 Carp)
# read input
while getopts "d:k:c:a:Vh" options; do
case $options in
d ) prefix=$OPTARG;;
k ) key=$OPTARG;;
c ) cert=$OPTARG;;
a ) ca_file=$OPTARG;;
h ) usage;;
V ) version;;
* ) usage;;
esac
done
# root test
root_chck
# params test
params_chck
# create variables
dirname=`dirname $0`
hostname=`hostname`
key_file=`basename $key`
cert_file=`basename $cert`
package_version=`cat ${dirname}/warden-server/etc/package_version`
[[ $prefix == */ ]] && prefix="${prefix%?}" # remove last char (slash) from prefix
server_path="${prefix}/warden-server"
bin="${server_path}/bin"
local_bin="/usr/local/bin"
etc="${server_path}/etc"
client_conf="${etc}/warden-client.conf"
server_conf="${etc}/warden-server.conf"
apache_conf="${etc}/warden-apache.conf"
var="${server_path}/var"
lib="${server_path}/lib"
err="/tmp/warden-err"
# check if warden-server is installed
old_package_chck
echo
echo "------------------------- Dependencies check-in -------------------------"
# Perl interpreter test
perl_chck
# Perl modules test
modules_chck
echo
echo "------------------------- Installation process --------------------------"
# make warden client directory
make_warden_dir
# create client configuration file
make_client_conf
# create server configuration file
make_server_conf
#update paths in utilities
changeServerPath
#update paths in apachefile
updateCertsPath
# crate symlinks from warden server bin directory to /usr/local/bin
create_symlinks
echo
echo "Please check client configuration file in ${client_conf}!"
echo "Please check server configuration file in ${server_conf}!"
echo "Please check Apache configuration file in ${apache_conf}!"
echo
echo "Warden server directory: $server_path"
echo
echo "Installation of $package_version package was SUCCESSFUL!!!"
echo
echo "Please follow post-installation steps in ${dirname}/doc/INSTALL!"
# cleanup section
rm -rf $err
exit 0
#!/bin/bash
#
# uninstall.sh
#
# Copyright (C) 2011-2012 Cesnet z.s.p.o
#
VERSION="2.1"
#-------------------------------------------------------------------------------
# FUNCTIONS
#-------------------------------------------------------------------------------
usage()
{
echo "Usage: `basename $0` [-d <directory>] [-hV]"
echo "-d <directory> uninstallation directory (default: /opt)"
echo "-h print this help"
echo "-V print script version number and exit"
echo
echo "Example: # ./`basename $0` -d /opt"
echo
echo "Note: You must be root for running this script."
echo " For more information about uninstallation process, see README file (section Uninstallation)."
echo
exit 0
}
version()
{
echo "`basename ${0}` - current version is $VERSION"
exit 0
}
err()
{
echo "FAILED!"
cat $err
rm -rf $err $backup_dir
echo
echo "Uninstallation of $package_version package FAILED!!!"
exit 1
}
err_clean()
{
echo "FAILED!"
echo " -> Reverting changes of warden server package ... OK"
rm -rf ${server_path}/* > /dev/null 2>&1 # delete new version
cp -R ${backup_dir}/* $server_path # copy old backuped server
for file in `ls -1 $bin`
do
ln -s ${bin}/$file ${local_bin}/$file # create symlinks to /usr/local/bin
done
$init start # start server
cat $err
rm -rf $err $backup_dir
echo
echo "Uninstallation of $package_version package FAILED!!!"
exit 1
}
root_chck()
{
if [ $UID -ne 0 ]; then
echo "You must be root for running this script!"
exit 1
fi
}
params_chck()
{
if [ -z $prefix ]; then
prefix=/opt
echo "Warning: parameter -d <directory> is not set - default uninstallation directory is ${prefix}!"
fi
}
obtain_package_version()
{
if [ -f $old_package_version_file ]; then
package_version=`cat $old_package_version_file`
else
package_version="unknown"
fi
}
warden_dir_chck()
{
echo -n "Checking Warden server directory ... "
if [ ! -d $server_path ]; then
echo "FAILED!"
ls $server_path
exit 1
else
echo "OK"
fi
}
stop_warden_server()
{
echo "Stopping Warden server ... "
${init} stop 1>/dev/null 2>&1
}
backup()
{
echo -n "Backing-up Warden server directory ... "
mkdir $backup_dir
if cp -R ${server_path}/* $backup_dir 2> $err; then
echo "OK"
else
err
fi
}
delete_symlinks()
{
echo -n "Deleting symlinks from /usr/local/bin ..."
for file in `ls -1 $bin`
do
rm -rf ${local_bin}/$file 2> /dev/null
done
echo "OK"
}
uninstall_warden_server()
{
echo -n "Uninstalling $package_version package ... "
cp ${doc}/UNINSTALL $uninstall_file
if rm -rf $server_path 2> $err; then
echo "OK"
else
err_clean
fi
}
#-------------------------------------------------------------------------------
# MAIN
#-------------------------------------------------------------------------------
# read input
while getopts "d:Vh" options; do
case $options in
d ) prefix=$OPTARG;;
h ) usage;;
V ) version;;
* ) usage;;
esac
done
# root test
root_chck
# params test
params_chck
# create variables
[[ $prefix == */ ]] && prefix="${prefix%?}" # remove last char (slash) from prefix
dirname=`dirname $0`
server_path="${prefix}/warden-server"
bin="${server_path}/bin"
local_bin="/usr/local/bin"
etc="${server_path}/etc"
doc="${server_path}/doc"
uninstall_file="/tmp/UNINSTALL"
old_package_version_file="${etc}/package_version"
err="/tmp/warden-err"
backup_dir="/tmp/warden-backup"
init="/etc/init.d/apache2"
# obtain version of installed warden-server package
obtain_package_version
echo
echo "------------------------- Uninstallation process --------------------------------"
# check if $prefix/warden-server directory exist
warden_dir_chck
# stop running warden server
stop_warden_server
# make backup of currently installed warden-server package
backup
# delete symbolic links
delete_symlinks
# do uninstallation
uninstall_warden_server
echo
echo "Uninstallation of $package_version package was SUCCESSFUL!"
echo
echo "Please follow post-uninstallation steps in $uninstall_file!"
# cleanup section
rm -rf $err $backup_dir
exit 0
#!/bin/bash
#
# update.sh
#
# Copyright (C) 2011-2012 Cesnet z.s.p.o
#
# Use of this source is governed by a BSD-style license, see LICENSE file.
VERSION="2.1"
#-------------------------------------------------------------------------------
# FUNCTIONS
#-------------------------------------------------------------------------------
usage()
{
echo "Usage: `basename $0` [-d <directory>] [-hV]"
echo "-d <directory> destination directory (default: /opt)"
echo "-h print this help"
echo "-V print script version number and exit"
echo
echo "Example: # ./`basename $0` -d /opt"
echo
echo "Note: You must be root for running this script."
echo " For more information about update process, see README file (section Update)."
echo
exit 0
}
version()
{
echo "`basename ${0}` - current version is $VERSION"
exit 0
}
err()
{
echo "FAILED!"
cat $err
rm -rf $err
rm -rf $backup_dir
echo
echo "Update from $old_package_version to $package_version package FAILED!!!"
exit 1
}
err_clean()
{
echo "FAILED!"
echo " -> Reverting changes of Warden server package ... OK"
rm -rf ${server_path}/* > /dev/null 2>&1
cp -R ${backup_dir}/* $server_path
cat $err
rm -rf $err $backup_dir
echo
echo "Update from $old_package_version to $package_version package FAILED!!!"
exit 1
}
root_chck()
{
if [ $UID -ne 0 ]; then
echo "You must be root for running this script!"
exit 1
fi
}
params_chck()
{
if [ -z $prefix ]; then
prefix=/opt
echo "Warning: parameter -d <directory> is not set - default update directory is ${prefix}!"
fi
}
obtain_package_version()
{
if [ -f $old_package_version_file ]; then
old_package_version=`cat $old_package_version_file`
if [ "$old_package_version" == "$package_version" ]; then
echo "Sorry, but $package_version package is already installed!"
exit 1
fi
else
echo "Sorry, but Warden server package is not installed!"
echo "For installation of Warden server package please use install.sh script."
exit 1
fi
}
perl_chck()
{
echo -n "Checking Perl interpreter ... "
if which perl 1> /dev/null; then
echo "OK"
else
echo "FAILED!"
echo "Error: Perl interpreter is not installed!"
exit 1
fi
}
modules_chck()
{
for module in ${modules[@]};
do
echo -n "Checking $module module ... "
if perl -e "use $module" 2> $err; then
echo "OK"
else
err
fi
done
}
warden_dir_chck()
{
echo -n "Checking Warden server directory ... "
if [ ! -d $server_path ]; then
echo "FAILED!"
ls $server_path
exit 1
else
echo "OK"
fi
}
backup()
{
echo -n "Backing-up Warden server directory ... "
mkdir $backup_dir
if cp -R ${server_path}/* $backup_dir 2> $err; then
echo "OK"
else
err
fi
}
obtain_warden_user()
{
echo -n "Obtaining Warden server directory owner ... "
if user=`stat -c %U $server_conf_file` 2> $err; then
echo "OK"
else
err
fi
}
update_warden_dir()
{
echo -n "Updating Warden server directory ... "
if rsync -q --recursive --archive --delete --exclude='etc' --exclude='sh' ${dirname}/warden-server $prefix 2> $err; then
echo "OK"
else
err_clean
fi
cp ${dirname}/warden-server/etc/package_version $etc
cp ${dirname}/uninstall.sh $server_path
}
update_conf_files()
{
echo "Updating $apache_conf_file ... "
ssl_certificate_file=`cat $apache_conf_file | grep 'SSLCertificateFile'`
ssl_certificate_key_file=`cat $apache_conf_file | grep 'SSLCertificateKeyFile'`
ssl_ca_certificate_file=`cat $apache_conf_file | grep 'SSLCACertificateFile'`
perl_switches=`cat $apache_conf_file | grep 'PerlSwitches'`
echo "#
# warden-apache.conf - configuration file for the Apache server
#
SSLEngine on
SSLVerifyDepth 3
SSLVerifyClient require
SSLOptions +StdEnvVars +ExportCertData
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
$ssl_certificate_file
$ssl_certificate_key_file
$ssl_ca_certificate_file
PerlOptions +Parent
$perl_switches
<Location /Warden>
SetHandler perl-script
PerlHandler Warden::ApacheDispatch
SSLOptions +StdEnvVars
</Location>
" > $apache_conf_file 2> $err; ret_val=`echo $?`
if [ $ret_val -eq 0 ]; then
echo "OK"
else
err_clean
fi
#-------------------------------------------------------------------------------
echo "Updating $client_conf_file ... "
uri=`cat $client_conf_file | grep '$URI'`
ssl_key_file=`cat $client_conf_file | grep '$SSL_KEY_FILE'`
ssl_cert_file=`cat $client_conf_file | grep '$SSL_CERT_FILE'`
ssl_ca_file=`cat $client_conf_file | grep '$SSL_CA_FILE'`
echo "#
# warden-client.conf - configuration file for registration and status clients
#
#-------------------------------------------------------------------------------
# URI - URI address of Warden server
#-------------------------------------------------------------------------------
$uri
#-------------------------------------------------------------------------------
# SSL_KEY_FILE - path to server SSL certificate key file
#-------------------------------------------------------------------------------
$ssl_key_file
#-------------------------------------------------------------------------------
# SSL_CERT_FILE - path to server SSL certificate file
#-------------------------------------------------------------------------------
$ssl_cert_file
#-------------------------------------------------------------------------------
# SSL_CA_FILE - path to CA certificate file
#-------------------------------------------------------------------------------
$ssl_ca_file
" > $client_conf_file 2> $err; ret_val=`echo $?`
if [ $ret_val -eq 0 ]; then
echo "OK"
else
err_clean
fi
#-------------------------------------------------------------------------------
echo "Updating $server_conf_file ... "
basedir=`cat $server_conf_file | grep '$BASEDIR'`
syslog=`cat $server_conf_file | grep '$SYSLOG' | head -n1`
if [ -z "$syslog" ]; then
syslog='$SYSLOG = 1;'
fi
syslog_verbose=`cat $server_conf_file | grep '$SYSLOG_VERBOSE'`
if [ -z "$syslog_verbose" ]; then
syslog_verbose='$SYSLOG_VERBOSE = 1;'
fi
syslog_facility=`cat $server_conf_file | grep '$SYSLOG_FACILITY'`
if [ -z "$syslog_facility" ]; then
syslog_facility=`cat $server_conf_file | grep '$FACILITY'`
else
syslog_facility='$SYSLOG_FACILITY = local7;'
fi
db_name=`cat $server_conf_file | grep '$DB_NAME'`
db_user=`cat $server_conf_file | grep '$DB_USER'`
db_pass=`cat $server_conf_file | grep '$DB_PASS'`
db_host=`cat $server_conf_file | grep '$DB_HOST'`
max_events_limit=`cat $server_conf_file | grep '$MAX_EVENTS_LIMIT'`
if [ -z "$max_events_limit" ]; then
max_events_limit='$MAX_EVENTS_LIMIT = 1000000;'
fi
valid_strings=`cat $server_conf_file | grep -A 3 '%VALID_STRINGS'`
if [ -z "$valid_strings" ]; then
valid_strings="\%VALID_STRINGS = (
\'type\' => [\'portscan\', \'bruteforce\', \'probe\', \'spam\', \'phishing\', \'botnet_c_c\', \'dos\', \'malware\', \'copyright\', \'webattack\', \'test\', \'other\'],
\'source_type\' => [\'IP\', \'URL\', \'Reply-To:\']
);"
fi
echo "#
# warden-server.conf - configuration file for Warden server
#
#-------------------------------------------------------------------------------
# BASEDIR - base directory of Warden server
#-------------------------------------------------------------------------------
$basedir
#-------------------------------------------------------------------------------
# SYSLOG - enable/disable syslog logging
#-------------------------------------------------------------------------------
$syslog
#-------------------------------------------------------------------------------
# SYSLOG_VERBOSE - enable/disable logging in verbose mode (stack info added)
#-------------------------------------------------------------------------------
$syslog_verbose
#-------------------------------------------------------------------------------
# SYSLOG_FACILITY - syslog facility
#-------------------------------------------------------------------------------
$syslog_facility
#-------------------------------------------------------------------------------
# DB_NAME - MySQL database name of Warden server
#-------------------------------------------------------------------------------
$db_name
#-------------------------------------------------------------------------------
# DB_USER - MySQL database user of Warden server
#-------------------------------------------------------------------------------
$db_user
#-------------------------------------------------------------------------------
# DB_PASS - MySQL database password of Warden server
#-------------------------------------------------------------------------------
$db_pass
#-------------------------------------------------------------------------------
# DB_HOST - MySQL database host
#-------------------------------------------------------------------------------
$db_host
#-------------------------------------------------------------------------------
# MAX_EVENTS_LIMIT - server limit of maximum number of events that can be
# delivered to one client in one batch
#-------------------------------------------------------------------------------
$max_events_limit
#-------------------------------------------------------------------------------
# VALID_STRINGS - validation hash containing allowed event attributes
#-------------------------------------------------------------------------------
$valid_strings
" > $server_conf_file 2> $err; ret_val=`echo $?`
if [ $ret_val -eq 0 ]; then
echo "OK"
else
err_clean
fi
}
#-------------------------------------------------------------------------------
# MAIN
#-------------------------------------------------------------------------------
# list of used Perl modules
modules=(SOAP::Lite SOAP::Transport::HTTP DBI DBD::mysql Format::Human::Bytes Sys::Syslog File::Basename Net::CIDR::Lite DateTime Getopt::Std Switch IO::Socket::SSL MIME::Base64 Crypt::X509 Carp)
# read input
while getopts "d:Vh" options; do
case $options in
d ) prefix=$OPTARG;;
h ) usage;;
V ) version;;
* ) usage;;
esac
done
# root test
root_chck
# params test
params_chck
# create variables
dirname=`dirname $0`
package_version=`cat ${dirname}/warden-server/etc/package_version`
[[ $prefix == */ ]] && prefix="${prefix%?}" # remove last char (slash) from prefix
server_path="${prefix}/warden-server"
etc="${server_path}/etc"
old_package_version_file="${etc}/package_version"
apache_conf_file="${etc}/warden-apache.conf"
client_conf_file="${etc}/warden-client.conf"
server_conf_file="${etc}/warden-server.conf"
err="/tmp/warden-err"
backup_dir="/tmp/warden-server-backup"
# obtain version of old warden server
obtain_package_version
echo
echo "------------------------- Dependencies check-in -------------------------"
# Perl interpreter test
perl_chck
# Perl modules test
modules_chck
echo
echo "------------------------- Update process --------------------------------"
# check warden server directory
warden_dir_chck
# backup old warden server installation
backup
# obtain current warden server user
obtain_warden_user
# make warden server directory
update_warden_dir
# create conf files
update_conf_files
echo
echo "Please check configuration file in ${conf_file}!"
echo
echo "Warden server directory: $server_path"
echo
echo "Update from $old_package_version to $package_version package was SUCCESSFUL!!!"
echo
echo "Please follow post-update steps in ${dirname}/doc/UPDATE!"
echo
# cleanup section
rm -rf $err $backup_dir
exit 0
BSD License
Copyright © 2011-2015 Cesnet z.s.p.o
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* 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.
* 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 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.
+---------------------------------+
| Warden Client Library 3.0-beta3 |
+---------------------------------+
Content
A. Introduction
B. Quick start (TL;DR)
C. Concepts
D. HTTP/JSON API
E. Python API
------------------------------------------------------------------------------
A. Introduction
The main goal of Warden 3 is to address the shortcomings, which emerged
during several years of Warden 2.X operation. Warden 3 uses flexible and
descriptive event format, based on JSON. Warden 3 protocol is based on plain
HTTPS queries with help of JSON (Warden 2 SOAP is heavyweight, outdated and
draws in many dependencies). Clients can be multilanguage, unlike SOAP/HTTPS,
plain HTTPS and JSON is mature in many mainstream programming languages.
Server is written in Python - mature language with consistent and coherent
libraries and many skilled developers.
------------------------------------------------------------------------------
B. Quick start (TL;DR)
* Obtain X509 key/cert, corresponding with DNS name of you machine.
* Obtain X509 CA chain for server validation.
* Choose client name ("reverse DNS", like org.example.warden.client, but
it does not necessary need to correspond to your machine's DNS name).
* Ask Warden server admins for registration. They will want to know at least
client name and dns name, and short description of the (planned) client
and its purpose. Work with them. They may request some changes or
clarifications, offer you useful guidelines, provide you with alternative
sandbox URL, etc.
If succesful, you will receive authentication secret.
* Use warden_curl_test.sh to check you are able to talk to server.
* See warden_client_examples.py on how to integrate sending/receiving
into your Python application.
* Alternatively, check 'contrib' directory in Warden GIT for various
ready to use tools or recipes. You may find senders for various
honeypots, or warden_filer may get handy if you do not want to delve
into Python at all.
* Welcome! Thanks for participating in in the data exchange to improve
network security awareness.
------------------------------------------------------------------------------
C. Concepts
C.1. Event description format
IDEA - Intrusion Detection Extensible Alert, flexible extensible format
for security events, see:
https://idea.cesnet.cz/
C.2. Event serial ID
Each received event gets assigned integer serial number. These numbers are
sequential, so each recipient can keep track of the last event "id" it
received and next time ask only for following events.
C.3. Authentication
In Warden 2, clients get authenticated by server certificate, however
server certificate is usually same for the whole machine, so individual
clients are differentiated only by telling their own name. However, client name
is widely known, so this allows for client impersonation within one machine.
Warden 3 slightly improves this schema by replacing client name in
authentication phase by "secret", random string, shared among particular
client and main server, which makes it harder to forge client identity (be it
by mistake or intentional).
However, best solution for these cases is of course specific certificate
for each particular client (which is also fully supported).
Client also has to have server CA certificate (or chain) at its disposal
to be able to verify server authenticity.
C.4. Client name
Unlike Warden 2, client names in Warden 3 have hierarchy. Modelled after
Java class names, client name is dot separated list of labels, with
significance from left to right – leftmost denoting largest containing realm,
rightmost denoting single entity.
Country.organisation.suborganizations.machine.local realm scheme akin to
"org.example.csirt.northwest.honeypot.jabberwock" is strongly recommended.
Label case is significant, label can contain only letters, numbers or
underscore and must not start with number.
The reason is the possibility to filter incoming events based not only on
particular client, or (for some recipients flawed) notion of "own" messages,
but based on wider units.
------------------------------------------------------------------------------
D. HTTP/JSON API
Client must know the base URL of the Warden server. Warden 3 accepts
queries on paths under base URL (which correspond to called method), with
usual query string variable=data pairs separated by ampersand as arguments.
Multivalues are specified by repeating same variable with each value several
times.
https://warden.example.org/warden3/getEvents?secret=PwD&cat=Abusive.Spam&cat=Fraud.Phishing
\________________ _______________/ \___ ___/ \____ ___/ \______ _______/ \________ _______/
V V V V V
Base URL --' | | | |
Called method ------------------------' | | |
Key/value pair -----------------------------------' | |
Multivalue ------------------------------------------------'------------------'
Method may expect bulk data (events to save, for example) - query then
must be POST, with POST JSON data, formed appropriately as documented in
particular method.
If HTTPS call succeeds (200 OK), method returns JSON object containing
requested data.
D.1. Error handling
Should the call fail, server returns HTTP status code, together with JSON
object, describing the errors (there may be multiple ones, especially when
sending events). The keys of the object, which may be available, are:
* method - name of the method called
* req_id - unique identifier or the request (for troubleshooting, Warden
administrator can uniquely identify related log lines)
* errors - always present list of JSON objects, which contain:
* error - HTTP status code
* message - human readable error description
* Other context dependent fields may appear, see particular method
description.
Client errors (4xx) are considered permanent - client must not try to send
same event again as it will always get rejected - client administrator
will need to inspect logs and rectify the cause.
Server errors (5xx) may be considered by client as temporary and client is
advised to try again after reasonable recess.
D.2. Common arguments
* secret - shared secret, assigned to client during registration
* client - client name, optional, can be used to mimic Warden 2
authentication behavior if explicitly allowed for this client by server
administrator
= getEvents =
Fetches events from server.
Arguments:
* count - number of requested events
* id - starting serial number requested, id of all received events will
be greater
* cat, nocat - selects only events with categories, which are/are not
present in the event Category field (mutually exclusive)
* group, nogroup - selects only events originated/not originated from
this realms and/or client names, as denoted in the event Node.Name field
(mutually exclusive)
* tag, notag - selects only events with/without this client description
tags, as denoted in the event Node.Type field (mutually exclusive)
Returns:
* lastid - serial number of the last received event
* events - array of Idea events
Example:
$ curl \
--key key.pem \
--cert cert.pem \
--cacert ca.pem \
--connect-timeout 3 \
--request POST \
\
"https://warden.example.org/getEvents?\
secret=SeCrEt\
&count=1\
&nogroup=org.example\
&cat=Abusive.Spam\
&cat=Fraud.Phishing"
{"lastid": 581,
"events": [{
"Format": "IDEA0",
"DetectTime": "2015-02-03T09:55:21.563638Z",
"Target": [{"URL": ["http://example.com/kocHq"]}],
"Category": ["Fraud.Phishing"],
"Note": "Example event"}]}
= sendEvents =
Uploads events to server.
Arguments:
* POST data - JSON array of Idea events
Returns:
Returns object with number of saved messages in "saved" attribute.
In case of error, multiple errors may be returned in "errors" list (see
[[Warden3#Error-handling|Error handling]] section). Each of the error objects
may contain "events" key with list of indexes of events affected by this
particular error. If there is error object without "events" key, caller
must consider all events affected.
Should the call fail because of errors in just couple of events, error
message will contain JSON object in "detail.errors" section. The keys of the
object are indexes into POST data array, values are error messages for each
particular failed Idea event.
Example:
$ eventid=$RANDOM$RANDOM$RANDOM$RANDOM$RANDOM
$ detecttime=$(date --rfc-3339=seconds|tr " " "T")
$ client="cz.example.warden.test"
$ printf '
[
{
"Format": "IDEA0",
"ID": "%s",
"DetectTime": "%s",
"Category": ["Test"],
"Node": [{"Name": "%s"}]
}
]' $eventid $detecttime $client |\
curl \
--key $keyfile \
--cert $certfile \
--cacert $cafile \
--connect-timeout 3 \
--request POST \
--data-binary "@-" \
"https://warden.example.org/sendEvents?client=$client&secret=SeCrEt"
{"saved":1}
(However note that this is not the best way to generate Idea messages. :) )
Example with error:
$ curl \
--key $keyfile \
--cert $certfile \
--cacert $cafile \
--connect-timeout 3 \
--request POST \
--data-binary '[{"Format": "IDEA0","ID":"ASDF","Category":[],"DetectTime":"asdf"}]' \
"https://warden.example.org/sendEvents?client=cz.example.warden.test&secret=SeCrEt"
{"errors":
[
{"message": "Validation error: key \"DetectTime\", value \"asdf\", expected - RFC3339 timestamp.",
"events": [0],
"error": 460
}
],
"method": "sendEvents",
"req_id": 3726454025
}
= getInfo =
Returns basic server information.
Returns:
* version - Warden server version string
* description - server greeting
* send_events_limit - sendEvents will be rejected if client sends more
events in one call
* get_events_limit - getEvents will return at most that much events
Example:
$ curl \
--key key.pem \
--cert cert.pem \
--cacert ca.pem \
--connect-timeout 3 \
--request POST \
"https://warden.example.org/getInfo?secret=SeCrEt"
{"version": "3.0",
"send_events_limit": 500,
"get_events_limit": 1000,
"description": "Warden 3 server"}
E. Python API
Python API tries to abstract from raw HTTPS/URL/JSON details. User
instantiates Client class with necessary settings (certificates, secret,
client name, logging, limits, ...) and then uses its method to access server.
= Client constructor =
wclient = warden.Client(
url,
certfile=None,
keyfile=None,
cafile=None,
timeout=60,
retry=3,
pause=5,
get_events_limit=6000,
send_events_limit=500,
errlog={},
syslog=None,
filelog=None,
idstore=None,
name="org.example.warden_client",
secret=None)
* url - Warden server base URL
* certfile, keyfile, cafile - paths to X509 material
* timeout - network timeout value in seconds
* retry - number retries on transitional errors during sending events
* pause - wait time in seconds between transitional error retries
* get_events_limit - maximum number of events to receive (note that server
may have its own notion)
* send_events_limit - when sending, event lists will be split and sent by
chunks of at most this size (note that used value will get adjusted according
to the limit reported by server)
* errlog - stderr logging configuration dict
* level - most verbose loglevel to log
* syslog - syslog logging configuration dict
* level - most verbose loglevel to log
* socket - syslog socket path (defaults to "/dev/log")
* facility - syslog facility (defaults to "local7")
* filelog - file logging configuration dict
* level - most verbose loglevel to log
* file - path to log file
* idstore - path to simple text file, in which last received event ID gets
stored. If None, server notion is used
* name - client name
* secret - authentication secret
= Configuration file helper =
warden.read_cfg(cfgfile)
Warden object can get initialized from JSON like configuration file. It's
essentially JSON, but full line comments, starting with "#" or "//", are
allowed. read_cfg reads the configuration file and returns dict suitable
for passing as Client constructor arguments.
Usage example:
wclient = warden.Client(**warden.read_cfg("warden_client.cfg"))
= warden.Client.getEvents =
wclient.getEvents(
id=None,
idstore=None,
count=1,
cat=None, nocat=None,
tag=None, notag=None,
group=None, nogroup=None)
* id - can be used to explicitly override value from idstore file
* idstore - can be used to explicitly override idstore for this request
* count - number of requested events
* cat, nocat - selects only events with categories, which are/are not
present in the event Category field (mutually exclusive)
* group, nogroup - selects only events originated/not originated from
this realms and/or client names, as denoted in the event Node.Name field
(mutually exclusive)
* tag, notag - selects only events with/without this client description
tags, as denoted in the event Node.Type field (mutually exclusive)
Returns:
* list of Idea events
= warden.Client.sendEvents =
wclient.sendEvents(self, events=[], retry=None, pause=None):
* events - list of Idea events to be sent to server
* retry - use this retry value just for this call instead from value from
constructor
* pause - use this pause value just for this call instead from value from
constructor
Returns:
* dict with number of sent events under "saved" key
Note:
events list length is limited only by available resources, sendEvents
will split it and send separately in at most send_events_limit long chunks
(however note that sendEvents will also need additional memory for its
internal data structures).
Server errors (5xx) are considered transitional and sendEvents will do
retry number of attempts to deliver corresponding events, delayed by
pause seconds.
Should the call fail because of errors, particular errors may contain
"events" list. Values of the list are then indexes into POST data array. If
no "events" list is present, all events attempted to send must be
considered as failed (with this particular error). See also
[[Warden3#Error-handling|Error handling]] section.
Errors may also contain event IDs from Idea messages in "events_id" list.
This is primarily for logging - client administrator may identify offending
messages by stable identifiers.
= warden.Client.getInfo =
wclient.getInfo()
Returns dictionary of information from getInfo Warden call.
= Error class =
Error(
message,
logger=None,
error=None,
prio="error",
method=None,
req_id=None,
detail=None,
exc=None)
Class, which gets returned in case of client or server error. Caller can
test whether it received data or error by checking:
isinstance(res, Error).
However if he does not want to deal with errors altogether, this error
object also returns False value if used in Bool context and acts as an
empty iterator - in following examples do_stuff() is not evaluated:
if res:
do_stuff(res)
for e in res:
do_stuff(e)
str(Error_Instance) outputs formatted error, info_str() and
debug_str() output increasingly more detailed info.
------------------------------------------------------------------------------
Copyright (C) 2011-2022 Cesnet z.s.p.o