Support This Project

This is an example script to show how to use the Perl bindings for Prelude. In this example, we extract informations (Hardware list) from the system using http://www.ocsinventory-ng.com, and send them as prelude alerts.

Versions used:

  • OCSInventory-client 1.01
  • libprelude-perl 0.9.13.2

Note: This code could be simplified using the Prelude Easy bindings

Get hardware information

Use OCSInventory to generate a XML file containing informations for your system. Since we are not interested in software list, and since this list is quite huge, we tell the client to suppress it:

sekspyz:~# ocsinventory-client.pl -xml -nosoft
        Choose your target directory ? :  /tmp
Tue May  8 21:32:47 2007 => Inventory generated...
Tue May  8 21:32:47 2007 => Terminated... :-)
Tue May  8 21:32:47 2007 => Execution time : 5 secs

We now have a XML file /tmp/sekspyz-2007-05-08-15-07-00.ocs containing the list. Here is an extract of the file:

<REQUEST>
  <QUERY>INVENTORY</QUERY>
  <DEVICEID>sekspyz-2007-05-08-15-07-00</DEVICEID>
  <CONTENT>
...
    <HARDWARE>
      <SWAP>956</SWAP>
      <PROCESSORS>600.000</PROCESSORS>
      <NAME>sekspyz</NAME>
      <ETIME>5</ETIME>
      <OSVERSION>2.6.18.3</OSVERSION>
      <PROCESSORN>1</PROCESSORN>
      <TYPE>8</TYPE>
      <OSNAME>Linux</OSNAME>
      <OSCOMMENTS>Debian / 4.0 / #2 PREEMPT Wed Dec 6 18:31:27 CET 2006</OSCOMMENTS>

The first part of the perl script will use XML::Simple to open the XML file. After that, we just have to get interesting values and format them.

Extract values from XML structure

Using Perl magic, we can access the XML data directly, just as if the XML structure is a hash table. For example, the element:

<REQUEST>
  <QUERY>INVENTORY</QUERY>

can be accessed using:

$xmlfile->{QUERY}

The first-level container does not need to be specified.

my $source_ip = $xmlfile->{CONTENT}->{HARDWARE}->{IPADDR};
my $data = "Hostname: " . $xmlfile->{CONTENT}->{HARDWARE}->{NAME} . "\n" .
	"OS: " . $xmlfile->{CONTENT}->{HARDWARE}->{OSNAME} . "\n" .
	"Version: " . $xmlfile->{CONTENT}->{HARDWARE}->{OSVERSION} . "\n" .
	"Comments: " . $xmlfile->{CONTENT}->{HARDWARE}->{OSCOMMENTS} . "\n";

Prelude / Perl

Since the script will manipulate text, Perl is a good choice. Install the perl bindings (libprelude-perl package on debian). Before using Perl, we have to register the sensor to the manager as usual:

# prelude-adduser register MySensor "idmef:w" www.wallinfire.net --uid 1020 --gid 1020

See https://trac.prelude-ids.org/wiki/RegisteringASensor for details.

Connecting to the Manager

The first step is to connect to the manager. You have to configure the manager address in the configuration file (generally, /etc/prelude/default/client.conf).

The Perl part is quite easy:

my $client;

sub init_prelude
{
	my $ret;

	$ret = Prelude::prelude_init(1, ["PolluxTest"]);
	if ( $ret != 0 ) {
		print "error initializing library: " . Prelude::prelude_strerror($ret) . "\n";
		exit();
	}

	$ret = Prelude::prelude_client_new(\$client, "PolluxTest");
	if ( $ret != 0 ) {
		print "error creating client: " . Prelude::prelude_strerror($ret) . "\n";
		exit();
	}

	$ret = Prelude::prelude_client_start($client);
	if ( $ret != 0 ) {
		print "error starting client: " . Prelude::prelude_strerror($ret) . "\n";

		if ( Prelude::prelude_client_is_setup_needed($ret) ) {
			Prelude::prelude_client_print_setup_error($client);
		}
		exit();
	}

}

Basically, we just start a new connection to the manager. The Prelude API will take care of the authentication etc.

Creating an IDMEF path

Since we are trying to use high-level functions, we will use the IDMEF path API. We create a wrapper function to simplify object creation:

sub add_idmef_object($$$)
{
	my $ret;
	my $path;
	my $val;
        my $message = shift @_;
        my $object = shift @_;
        my $value  = shift @_;

        $ret = Prelude::idmef_path_new(\$path, $object);
        if ( $ret != 0 ) {
                print "$object: " . Prelude::prelude_strerror($ret) . "\n";
                exit();
        }

        $ret = Prelude::idmef_value_new_from_path(\$val, $path, $value);
        if ( $ret != 0 ) {
                print "could not create value for object $object: " . Prelude::prelude_strerror($ret) . "\n";
                exit();
        }

        $ret = Prelude::idmef_path_set($path, $message, $val);
        if ( $ret != 0 ) {
                print "error setting path $object: " . Prelude::prelude_strerror($ret) . "\n";
                exit();
        }

        Prelude::idmef_path_destroy($path);
}

The alert creation is not difficult, we just fill values that we know, and then send the alert. The first parameter of the function is a text value containing the data.

sub send_alert($$)
{
	my $ret;
	my $message;
	my $time;
	my ($source_ip,$data) = @_;

	$ret = Prelude::idmef_message_new(\$message);
	if ( $ret != 0 ) {
		exit();
	}

	# Set create time
	my $alert;
	Prelude::idmef_time_new_from_gettimeofday(\$time);
	Prelude::idmef_message_new_alert($message, \$alert);
	Prelude::idmef_alert_set_create_time($alert, $time);

	# Classification
	add_idmef_object($message, "alert.classification.text", "OCSInventory configuration");

	# Analyzer
	add_idmef_object($message, "alert.analyzer(0).name", "ocs2prelude");
	add_idmef_object($message, "alert.analyzer(0).manufacturer", "Pierre Chifflier");
	add_idmef_object($message, "alert.analyzer(0).class", "Inventory");

	# Source
	add_idmef_object($message, "alert.source(0).node.address(0).address", "$source_ip");

	# Assessment
	add_idmef_object($message, "alert.assessment.impact.severity", "low");
	add_idmef_object($message, "alert.assessment.impact.completion", "succeeded");
	add_idmef_object($message, "alert.assessment.impact.type", "recon");

	# Additional Data
	add_idmef_object($message, "alert.additional_data(0).data", "$data");

	$ret = Prelude::prelude_client_send_idmef($client, $message);
	Prelude::idmef_message_destroy($message);
}

Attachments