#!/usr/perl5/bin/perl

# The Incredable Human Friendly BSM Audit Dumper
#  benr@cuddletech.com 	         Oct 15th, 2009
#
#  Ben Rockwood (c) 2009
#  License: CDDL (http://opensource.org/licenses/cddl1.php)





use warnings;
use XML::Simple;
use Data::Dumper;
use Getopt::Std;

################## OPTIONS HANDLING ####################################
getopts('dc:a:x:', \%options);


my $AUDIT_XML="";

## -d: Debug Dump
## -c: Class flags....
## -a: Raw Audit File (terminated) Conversion needed.
## -x: XML Audit File (terminated) Already converted.
if ( $options{c} && $options{a} ) { 
	print("Reducing $options{a} for class $options{c} ....\n");

	$ret = system("/usr/sbin/auditreduce -c $options{c}  $options{a} | /usr/sbin/praudit -x  > /tmp/.audit-tmp.xml"); # || die("ERROR: Problem reducing $options{a} audit trail.\n");
	if ($ret == 0) {
		$AUDIT_XML = "/tmp/.audit-tmp.xml";
	} else {
		exit(9);
	}


} elsif ( $options{a} ) { 
	print("Reducing $options{a} ....\n");

	$ret = system("/usr/sbin/praudit -x $options{a} > /tmp/.audit-tmp.xml"); # || die("ERROR: Problem reducing $options{a} audit trail.\n");
	if ($ret == 0) {
		$AUDIT_XML = "/tmp/.audit-tmp.xml";
	} else {
		exit(9);
	}
	

} elsif ( $options{x} ) {
	$AUDIT_XML = $options{x};
} else {
	print(" The Incredable Human Friendly BSM Audit Dumper                        benr\@cuddletech.com\n");
	print("USAGE: $0 [-d] ( [-c <audit_class>] -a <file> ) | (-x /path/to/reduced.xml) \n");
	exit(1);
}



################## DATA LOAD      ####################################

# create object
$xml = new XML::Simple;

print("Processing $AUDIT_XML ....\n");

# read XML file
$data = $xml->XMLin($AUDIT_XML, suppressempty => '');



if ( $options{d} ) {
	printf("DEBUG DUMP PROCEEDING....\n");
	print Dumper($data);
	exit(0);
}

################## THE MAIN EVENT    ################################

###	RECORD REFERENCE:
#                      {
#                        'zone' => {
#                                  'name' => 'global'
#                                },
#                        'subject' => {
#                                     'sid' => '2035764268',
#                                     'uid' => 'root',
#                                     'audit-uid' => 'benr',
#                                     'tid' => '0 0 quadra',
#                                     'ruid' => 'benr',
#                                     'rgid' => 'staff',
#                                     'pid' => '6325',
#                                     'gid' => 'staff'
#                                   },
#                        'version' => '2',
#                        'iso8601' => '2009-10-15 03:03:51.213 -07:00',
#                        'event' => 'screenlock - lock',
#                        'return' => {
#                                    'retval' => '0',
#                                    'errval' => 'success'
#                                  },
#                        'host' => 'quadra'
#                      }


print("\n\t\tC U D D L E T E C H   A U D I T    D U M P E R\n\n");

# access XML data
print("Audit Begins: $data->{file}->[0]->{iso8601} \n");
if ( $data->{file}->[1]->{iso8601} ) 
{ 
	print("Audit Ends:   $data->{file}->[1]->{iso8601} \n\n\n");
} else {
	print("WARNING: Audit Trail is NOT Terminated!\n");
}


my $counter = 0;

foreach $record (@{$data->{record}})
{
	$counter++;

	#0 0 quadra
	($tid1,$tid2,$tid3) = split('\s+', $record->{subject}->{tid});
	#print("DEBUG: $tid1 -  $tid2 -  $tid3 \n");

	if ( $tid1 == 0 && $tid2 == 0 ) 
	{
		$clean_tid = "from CONSOLE of ${tid3}";	
	} else { 
		$clean_tid = "REMOTELY from ${tid3}";	
	}

	## This bug took a while to figure out, must put elements with a hyphon in quotes: 'audit-uid'
	$user =	"$record->{subject}->{'audit-uid'} as $record->{subject}->{uid}";

	my $zone = "";
	if ( $record->{zone}->{name} ) { 
		$zone = "in zone $record->{zone}->{name} ";
	}

	#        1    2    3     4    5   6    7     
	printf("$record->{event} ($record->{return}->{errval}) by $user $clean_tid $zone ($record->{subject}->{sid}) ");



	#### Now we output the exec arguments or path:


	if ( $record->{event} =~ m/execve/  && $record->{return}->{errval} eq "success") {
		print(": ");

		### If Multiple Arguments, access as array:
		my $i = 0;
		foreach my $exec ( @{$record->{exec_args}->{arg}} )
		{
			$i++;
			chomp($exec);
			print("$exec ");
		}

		### If Single Argument, access directly:
		if ($i == 0 ){ 
			my $exec = $record->{exec_args}->{arg};
			chomp($exec);
			print("$exec ");
		}

		print(" \n");

	} elsif ($record->{event} =~ m/execve/  && $record->{return}->{errval} =~ m/fail.*/) {
		## If exec fails, no args are presented, just the  execve path:
		print(": ");
		my $path = $record->{path};
		chomp($path);
		print("$path \n");

	} else {
			print("\n");
		
	}


}
