[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[ossec-cvs] ossec-hids: ossec-batch-manager.pl (NEW) [jeff]




Module name:	ossec-hids
Changes by:	jeff	07/08/07 21:03:19

Added files:
	ossec-batch-manager.pl

Log message:
Description:	ossec-batch-manager.pl started as a quick hack to script the
		functionality available in the manage_agents binary. After a
		bit of looking into it, it wasn't very hard to clone the
		functionality completely in a sane way using perl.
Features:
		- Add / Remove / List / Extract Key / Import agent
		- Makes it much easier to script ossec from the server and
		  agent side of things on a mass scale.
		- perl is sexy
Examples:
	./ossec-batch-manager.pl -l # List all available agents
	# Add a new agent. The key and id will autogenerate unless
	# they are specified with the --id and --key options
	./ossec-batch-manager.pl -a --name agent02 --ip 10.75.25.42
	./ossec-batch-manager.pl -a --name agent02 --ip 10.75.25.42 --key '5a832efb8f93660857ce2acf8eec66a19fd9d4fa58e3221bbd2927ca8a0b40c3' --id 001
	./ossec-batch-manager.pl -e 001 # Extract the key of 001
	# Import a key extracted from the server onto an agent
	./ossec-batch-manager.pl -m -n agent02 -p 10.75.25.42 -i 10.75.25.42 -k 'NWE4MzJlZmI4ZjkzNjYwODU3Y2UyYWNmOGVlYzY2YTE5ZmQ5ZDRmYTU4ZTMyMjFiYmQyOTI3Y2E4YTBiNDBjMw=='
	./ossec-batch-manager.pl -r 001 # Remove agent 001
Contact jeffschroeder@xxxxxxxxxxxx with any problems or feature requests.
-jeff schroeder (SEJeff on irc)
Reviewed by: Daniel Cid

--- NEW FILE: ossec-batch-manager.pl ---
#!/usr/bin/perl
# vim:shiftwidth=2:tabstop=2:expandtab:textwidth=80:softtabstop=2:ai:

   #########################################################
  # Written Aug 4, 2007 and released under the GNU/GPLv2  ##
 # by Jeff Schroeder (jeffschroeder@xxxxxxxxxxxx)        # #
#########################################################  #
#                                                       #  #
# ossec-batch-manager.pl - Add and extract agents from  #  #
# the ossec client.keys file non-interactively. This    #  #
# started as a hack to properly script manage_agents.   #  #
#                                                       # #
##########################################################

#$Id: ossec-batch-manager.pl,v 1.1 2007/08/08 00:03:18 jeff Exp $

use strict;
use warnings;
require 5.8.2; # Time::HiRes is standard from this version forth
#use diagnostics;
use MIME::Base64;
use Digest::MD5 qw(md5_hex);
use Getopt::Long;

use constant AUTH_KEY_FILE => "/var/ossec/etc/client.keys";

my ($key, $add, $remove, $extract, $import, $listagents);
my ($agentid, $agentname, $ipaddress);

GetOptions(
  'k|key=s'     => \$key,         # Unencoded ssh key
  'a|add'       => \$add,         # Add a new agent
  'r|remove=s'  => \$remove,      # Remove an agent
  'e|extract=s' => \$extract,     # Extract a key
  'm|import'    => \$import,        # Import a key
  'l|list'      => \$listagents,  # List all agents
  'i|id=s'      => \$agentid,     # Unique agent id
  'n|name=s'    => \$agentname,   # Agent name. 32 char max
  'p|ip=s'      => \$ipaddress    # IP Address in "dotted quad" notation
);

# Spit out a list of available agents, their names, and ip information
if ($listagents) {
  print "Available agents:\n";
  list_agents();
}
# Decode and extract the key for $agentid
elsif ($extract) {
  $agentid = $extract;
  if ($agentid) {
    extract_key($agentid);
  }
  else {
    usage();
  }
}
# Adding a new agent
elsif ($add) {
  if ($agentname && $ipaddress && $ipaddress =~
      m/(1?\d\d?|2[0-4]\d|25[0-5])(\.(1?\d\d?|2[0-4]\d|25[0-5])){3}/ &&
      # ossec doesn't like agent names > 32 characters.
      length($agentname) <= 32) {

      # Autogenerate an id incremented 1 from the last in a sorted list of
      # all current ones if it isn't specified from the command line.
      if (!$agentid) {
        my @used_agent_ids = ();

        # Make a list of all of the used agentids and then sort it.
        open (FH, "<", AUTH_KEY_FILE) or die "Error: $!\n";
        while (<FH>) {
            my ($id, $name, $ip, $key) = split;
            push(@used_agent_ids, $id);
        }
        close(FH);

      if (@used_agent_ids) {
        @used_agent_ids = sort(@used_agent_ids);
        $agentid = sprintf("%03d", $used_agent_ids[-1] + 1);
      }
      else {
        # If the client.keys is empty, create the first entry
        $agentid = sprintf("%03d", 001);
      }
    }

    # Autogenerate a key unless one was specified on the command line
    if (!$key) {
      use Time::HiRes; # Standard with perl >= 5.8.2

      my $rand_str1 = time() . $agentname . rand(10000);
      my $rand_str2 = Time::HiRes::time . $ipaddress . $agentid . rand(10000);
      $key = md5_hex($rand_str1) . md5_hex($rand_str2);
    }
      
    add_agent($agentid, $agentname, $ipaddress, $key);
  }
  else {
    warn "Error: adding agents requires: --name and --ip options.\n";
    usage();
  }
}
elsif ($remove) {
  if ($agentid) {
    remove_agent($agentid);
  }
  else {
    remove_agent($remove)
  }
}
elsif ($import) {
  # Every option needs to be specified and NOT autogenerated because what
  # is autogenerated on the server and the agent will likely be different
  if (!$agentid || !$agentname || !$ipaddress || !$key) {
    warn "Error: importing requires: --id, --name, --ip, and --key\n";
    usage();
  }
  else {
    # The key extracted from the server needs to be decoded before being put
    # into the client.keys 
    $key = MIME::Base64::decode($key);

    add_agent($agentid, $agentname, $ipaddress, $key);
  }
}
else {
  warn "Error: no options specified!\n";
  usage();
}

sub usage {
  warn "Usage: $0 [OPERATION] [OPTIONS]\n";
  warn "  [operations]\n";
  warn "    -a or --add               = Add a new agent\n";
  warn "    -r or --remove  [id]      = Remove agent\n";
  warn "    -e or --extract [id]      = Extract key\n";
  warn "    -m or --import  [keydata] = Import key\n";
  warn "    -l or --list              = List available agents\n";
  warn "  [options]\n";
  warn "    -k or --key     [keydata] = Key data\n";
  warn "    -n or --name    [name]    = Agent name (32 character max)\n";
  warn "    -i or --id      [id]      = Agent identification (integer)\n";
  warn "    -p or --ip      [ip]      = IP address\n\n";
  exit 1;
}

sub list_agents {
  if (-r AUTH_KEY_FILE) {
	  open (FH, "<", AUTH_KEY_FILE);
  }
  else {
    die "No ".AUTH_KEY_FILE."!\n";
  }
  print "ID",    " " x (25 - length('ID')),
        "NAME",  " " x (25 - length('NAME')),
        "IP",    " " x (25 - length('IP'));
  print "\n";
	while (<FH>) {
		chomp;
		my ($id, $name, $ip, $key) = split;
    print "$id",    " " x (25 - length($id)),
          "$name",  " " x (25 - length($name)),
          "$ip",    " " x (25 - length($ip)) . "\n";
	  }
  close(FH);
  exit 0;
}

sub extract_key {
	my $extractid = shift;
	my ($encoded, $decoded);

  if (-r AUTH_KEY_FILE) {
	  open (FH, "<", AUTH_KEY_FILE);
  }
  else {
    die "No ".AUTH_KEY_FILE."!\n";
  }
	while (<FH>) {
		chomp;
		my ($id, $name, $ip, $key) = split;
	  if ($id == $extractid) {
		  # Newlines are valid base64 characters so use '' instead for \n
		  $decoded = MIME::Base64::encode($key, '');
		  print "$decoded\n";
      exit 0;
	  }
  }
  warn "Error: Agent ID $extractid doesn't exist!\n";
}

sub add_agent {
  my $id = shift;
  my $name = shift;
  my $ip = shift;
  my $agentkey = shift;

  if ($name && $ip && $agentkey && -e AUTH_KEY_FILE) {
    # Valid example key:
    # 5a832efb8f93660857ce2acf8eec66a19fd9d4fa58e3221bbd2927ca8a0b40c3
    if ($agentkey !~ m/[a-z0-9]{64}/) { 
      warn "Error: invalid keydata! Let this script autogenerate it.\n";
      usage();
    }

    my @newagent = ($id, $name, $ip, $agentkey);
    my $exists = check_if_exists(\@newagent);

    if ($exists == 0) {
      open (FH, ">>", AUTH_KEY_FILE) or die "Error: $!\n";
      print FH join(' ', @newagent), "\n";
      close(FH);
    }
    elsif ($exists == 1) {
      warn "ID: $id already in ".AUTH_KEY_FILE."!\n";
    }
    elsif ($exists == 2) {
      warn "Agent: $name already in ".AUTH_KEY_FILE."!\n";
    }
    elsif ($exists == 3) {
      warn "IP: $ip already in ".AUTH_KEY_FILE."!\n";
    }
  }
  else {
    warn "Missing options to add agent or problem with ".AUTH_KEY_FILE."!\n";
    usage();
  }
}

sub remove_agent {
  my $removeid = shift;
  my @agent_array;

	open (FH, "<", AUTH_KEY_FILE) if -e AUTH_KEY_FILE or die "No ".AUTH_KEY_FILE."!\n";
  while (<FH>) {
    push(@agent_array, $_);
  }
  close(FH);

	open (FHRW, ">", AUTH_KEY_FILE) if -e AUTH_KEY_FILE or die "No ".AUTH_KEY_FILE."!\n";
  foreach my $line (@agent_array) {
    if ($line !~ $removeid) {
      print FHRW "$line";
    }
  }
  close(FHRW);
  exit 0;
}

sub import_key {
  my $keydata = shift;
  warn "Importing keys not implemented yet!\n";
  exit 0;
}

sub check_if_exists {
	my $agentlist_ref = shift;
  my ($newid, $newname, $newip);
  my $rval = 0;

  $newid = $agentlist_ref->[0];
  $newname = $agentlist_ref->[1];
  $newip = $agentlist_ref->[2];

	open (FH, AUTH_KEY_FILE) if -e AUTH_KEY_FILE or die "No ".AUTH_KEY_FILE."!\n";
	while (<FH>) {
    chomp;
    my ($id, $name, $ip, $key) = split;
    $rval = 1 if ($id == $newid && $rval == 0);
    $rval = 2 if ($name eq $newname && $rval == 0); 
    $rval = 3 if ($ip eq $newip && $rval == 0);
  }
	close(FH);
  return $rval;
}


OSSEC home | Main Index | Thread Index


OSSEC project: www.ossec.net.
Mailling list information: http://www.ossec.net/en/mailing_lists.html.