mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 08:24:42 +01:00
2183 lines
59 KiB
Perl
Executable file
2183 lines
59 KiB
Perl
Executable file
# $Id$
|
|
# ------------------------------------------------------------------
|
|
#
|
|
# Copyright (C) 2005-2006 Novell/SUSE
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of version 2 of the GNU General Public
|
|
# License published by the Free Software Foundation.
|
|
#
|
|
# ------------------------------------------------------------------
|
|
|
|
package Immunix::Reports;
|
|
|
|
################################################################################
|
|
# /usr/lib/perl5/site_perl/Reports.pm
|
|
#
|
|
# - Parses /var/log/messages for SubDomain messages
|
|
# - Writes results to .html or comma-delimited (.csv) files (Optional)
|
|
#
|
|
# Requires:
|
|
# Immunix::Events;
|
|
# Time::Local (temporary)
|
|
#
|
|
# Input (Optional):
|
|
# -Start Date|End Date (Month, Day, Year, Time)
|
|
# -Program Name
|
|
# -Profile Name
|
|
# -PID
|
|
# -Denied Resources
|
|
#
|
|
################################################################################
|
|
|
|
use strict;
|
|
use Immunix::Ycp; # debug
|
|
use DBI;
|
|
use DBD::SQLite;
|
|
|
|
use POSIX;
|
|
use Locale::gettext;
|
|
|
|
setlocale(LC_MESSAGES, "");
|
|
textdomain("Reports");
|
|
|
|
my $eventDb = '/var/log/apparmor/events.db';
|
|
my $numEvents = 1000;
|
|
|
|
sub month2Num {
|
|
|
|
my $lexMon = shift;
|
|
my $months = { "Jan" =>'01', "Feb"=>'02', "Mar"=>'03', "Apr"=>'04', "May"=>'05', "Jun"=>'06',
|
|
"Jul"=>'07', "Aug"=>'08', "Sep"=>'09', "Oct"=>'10', "Nov"=>'11', "Dec"=>'12' };
|
|
|
|
my $numMonth = $months->{$lexMon};
|
|
|
|
return $numMonth;
|
|
}
|
|
|
|
sub num2Month {
|
|
|
|
my $monthNum = shift;
|
|
|
|
my @months = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
|
|
my $lexMonth = $months[($monthNum -1)];
|
|
|
|
return $lexMonth;
|
|
}
|
|
|
|
# Converts Epoch Time to Formatted Date String
|
|
sub getDate {
|
|
|
|
my $epTime = shift;
|
|
|
|
my $date = localtime($epTime);
|
|
|
|
my ($day, $mon, $mondate, $time, $year) = split(/\s+/, $date);
|
|
my ($hour,$min,$sec) = split(/:/, $time);
|
|
|
|
$mon = month2Num($mon);
|
|
|
|
# we want 2 digits for easier reading
|
|
$mon = sprintf("%02d", $mon);
|
|
$mondate = sprintf("%02d", $mondate);
|
|
|
|
my $newDate = "$year-$mon-$mondate $time";
|
|
return $newDate;
|
|
}
|
|
|
|
sub round {
|
|
my $num = shift;
|
|
$num = sprintf("%.2f", $num);
|
|
return ("$num");
|
|
}
|
|
|
|
|
|
# round up
|
|
sub pageRound {
|
|
|
|
my $num = shift;
|
|
my $pnum = int($num);
|
|
|
|
if ( $pnum < $num ) {
|
|
$pnum++;
|
|
}
|
|
|
|
return $pnum;
|
|
}
|
|
|
|
sub checkFileExists {
|
|
|
|
my $file = shift;
|
|
|
|
if ( $file && -e $file ) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
# Translate mode & sdmode for parsing
|
|
sub rewriteModes {
|
|
|
|
my $filts = shift;
|
|
|
|
# Mode wrangling - Rewrite for better matches
|
|
if ($filts->{'mode'} && $filts->{'mode'} ne "All") {
|
|
|
|
my @mode = ();
|
|
my $tmpMode = undef;
|
|
|
|
@mode = split (//, $filts->{'mode'});
|
|
|
|
if (@mode > 0) {
|
|
$tmpMode = join("|", @mode);
|
|
} else {
|
|
delete($filts->{'mode'});
|
|
}
|
|
|
|
if ($tmpMode) {
|
|
$filts->{'mode'} = $tmpMode;
|
|
}
|
|
|
|
}
|
|
|
|
# Rewrite sdmode for more flexible matches
|
|
if ($filts->{'sdmode'} && $filts->{'sdmode'} ne "All") {
|
|
my @tmpMode = ();
|
|
if ( $filts->{'sdmode'} =~ /[pP]/ ) { push(@tmpMode,'PERMIT'); }
|
|
if ( $filts->{'sdmode'} =~ /[rR]/ ) { push(@tmpMode,'REJECT'); }
|
|
if ( $filts->{'sdmode'} =~ /[aA]/ ) { push(@tmpMode,'AUDIT'); }
|
|
if (@tmpMode > 0) {
|
|
$filts->{'sdmode'} = join('|', @tmpMode);
|
|
} else {
|
|
delete($filts->{'sdmode'});
|
|
}
|
|
}
|
|
|
|
return $filts;
|
|
}
|
|
|
|
sub enableEventD {
|
|
# make sure the eventd is enabled before we do any reports
|
|
my $need_enable = 0;
|
|
if(open(SDCONF, "/etc/apparmor/subdomain.conf")) {
|
|
while(<SDCONF>) {
|
|
if(/^\s*APPARMOR_ENABLE_AAEVENTD\s*=\s*(\S+)\s*$/) {
|
|
my $flag = lc($1);
|
|
# strip quotes from the value if present
|
|
$flag = $1 if $flag =~ /^"(\S+)"$/;
|
|
$need_enable = 1 if $flag ne "yes";
|
|
}
|
|
}
|
|
close(SDCONF);
|
|
}
|
|
|
|
# if the eventd isn't enabled, we'll turn it on the first time they
|
|
# run a report and start it up - if something fails for some reason,
|
|
# we should just fall through and the db check should correctly tell
|
|
# the caller that the db isn't initialized correctly
|
|
if($need_enable) {
|
|
my $old = "/etc/apparmor/subdomain.conf";
|
|
my $new = "/etc/apparmor/subdomain.conf.$$";
|
|
if(open(SDCONF, $old)) {
|
|
if(open(SDCONFNEW, ">$new")) {
|
|
my $foundit = 0;
|
|
|
|
while(<SDCONF>) {
|
|
if(/^\s*APPARMOR_ENABLE_AAEVENTD\s*=/) {
|
|
print SDCONFNEW "APPARMOR_ENABLE_AAEVENTD=\"yes\"\n";
|
|
|
|
$foundit = 1;
|
|
} else {
|
|
print SDCONFNEW;
|
|
}
|
|
}
|
|
|
|
unless($foundit) {
|
|
print SDCONFNEW "APPARMOR_ENABLE_AAEVENTD=\"yes\"\n";
|
|
}
|
|
|
|
close(SDCONFNEW);
|
|
|
|
# if we were able to overwrite the old config
|
|
# config file with the new stuff, we'll kick
|
|
# the init script to start up aa-eventd
|
|
if(rename($new, $old)) {
|
|
if(-e "/sbin/rcaaeventd") {
|
|
system("/sbin/rcaaeventd restart >/dev/null 2>&1");
|
|
} else {
|
|
system("/sbin/rcapparmor restart >/dev/null 2>&1");
|
|
}
|
|
}
|
|
}
|
|
close(SDCONF);
|
|
}
|
|
|
|
}
|
|
|
|
return $need_enable;
|
|
}
|
|
|
|
# Check that events db exists and is populated
|
|
# - Returns 1 for good db, 0 for bad db
|
|
sub checkEventDb {
|
|
|
|
my $count = undef;
|
|
my $eventDb = '/var/log/apparmor/events.db';
|
|
|
|
# make sure the event daemon is enabled
|
|
if(enableEventD()) {
|
|
|
|
my $now = time;
|
|
|
|
# wait until the event db appears or we hit 1 min
|
|
while (! -e $eventDb) {
|
|
sleep 2;
|
|
return 0 if ((time - $now) >= 60);
|
|
}
|
|
|
|
# wait until it stops changing or we hit 1 min - the event
|
|
# daemon flushes events to the db every five seconds.
|
|
my $last_modified = 0;
|
|
my $modified = (stat($eventDb))[9];
|
|
while($last_modified != $modified) {
|
|
sleep 10;
|
|
last if ((time - $now) >= 60);
|
|
$last_modified = $modified;
|
|
$modified = (stat($eventDb))[9];
|
|
}
|
|
}
|
|
|
|
my $query = "SELECT count(*) FROM events ";
|
|
|
|
# Pull stuff from db
|
|
my $dbh = DBI->connect("dbi:SQLite:dbname=$eventDb", "", "", {RaiseError => 1, AutoCommit => 1});
|
|
|
|
eval {
|
|
my $sth = $dbh->prepare($query);
|
|
$sth->execute;
|
|
$count = $sth->fetchrow_array();
|
|
|
|
$sth->finish;
|
|
};
|
|
|
|
if ( $@ ) {
|
|
Immunix::Ycp::y2error(sprintf(gettext("DBI Execution failed: %s."), $DBI::errstr));
|
|
return;
|
|
}
|
|
|
|
$dbh->disconnect();
|
|
|
|
|
|
if ( $count && $count > 0 ) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
# Called from ag_reports_parse
|
|
sub getNumPages {
|
|
|
|
my $args = shift;
|
|
my $db = ();
|
|
my $numPages = 0;
|
|
my $count = 0;
|
|
my $type = undef;
|
|
my $eventRep = "/var/log/apparmor/reports/events.rpt";
|
|
|
|
# Figure out whether we want db count or file parse
|
|
if ( $args->{'type'} ) {
|
|
if ( $args->{'type'} eq 'sir' || $args->{'type'} eq 'ess-multi' ) {
|
|
$type = 'db';
|
|
} elsif ( $args->{'type'} eq 'ess') {
|
|
return 1; # ess reports have one page by definition
|
|
} else {
|
|
$type = 'arch'; # archived or file
|
|
}
|
|
}
|
|
|
|
# Parse sdmode & mode labels
|
|
if ( $args->{'sdmode'} ) {
|
|
$args->{'sdmode'} =~ s/\&//g;
|
|
$args->{'sdmode'} =~ s/\://g;
|
|
$args->{'sdmode'} =~ s/\s//g;
|
|
$args->{'sdmode'} =~ s/AccessType//g;
|
|
|
|
if ($args->{'sdmode'} eq "All") {
|
|
delete($args->{'sdmode'});
|
|
}
|
|
}
|
|
|
|
if ( $args->{'mode'} ) {
|
|
$args->{'mode'} =~ s/\&//g;
|
|
$args->{'mode'} =~ s/Mode\://g;
|
|
$args->{'mode'} =~ s/\s//g;
|
|
|
|
if ($args->{'mode'} eq "All") {
|
|
delete($args->{'mode'});
|
|
}
|
|
}
|
|
########################################
|
|
|
|
$args = rewriteModes($args);
|
|
|
|
if ( $type && $type eq 'db' ) {
|
|
|
|
my $start = undef; my $end = undef;
|
|
|
|
if ( $args->{'startTime'} && $args->{'startTime'} > 0 ) {
|
|
$start = $args->{'startTime'};
|
|
}
|
|
|
|
if ( $args->{'endTime'} && $args->{'endTime'} > 0 ) {
|
|
$end = $args->{'endTime'};
|
|
}
|
|
|
|
my $query = "SELECT count(*) FROM events ";
|
|
|
|
# We need filter information for getting a correct count
|
|
#my $filts = getSirFilters($args); # these should be sent from YaST
|
|
my $filts = undef;
|
|
|
|
if ( $args->{'prog'} ) { $filts->{'prog'} = $args->{'prog'}; }
|
|
if ( $args->{'profile'} ) { $filts->{'profile'} = $args->{'profile'}; }
|
|
if ( $args->{'pid'} ) { $filts->{'pid'} = $args->{'pid'}; }
|
|
if ( $args->{'resource'} ) { $filts->{'resource'} = $args->{'resource'}; }
|
|
if ( $args->{'severity'} ) { $filts->{'severity'} = $args->{'severity'}; }
|
|
if ( $args->{'sdmode'} ) { $filts->{'sdmode'} = $args->{'sdmode'}; }
|
|
if ( $args->{'mode'} ) { $filts->{'mode'} = $args->{'mode'}; }
|
|
|
|
for(sort(keys(%$filts))) {
|
|
if ( $filts->{$_} eq '-' || $filts->{$_} eq 'All' ) {
|
|
delete( $filts->{$_});
|
|
}
|
|
}
|
|
|
|
my $midQuery = getQueryFilters($filts,$start,$end);
|
|
|
|
$query .= "$midQuery";
|
|
|
|
# Pull stuff from db
|
|
my $dbh = DBI->connect("dbi:SQLite:dbname=$eventDb", "", "", {RaiseError => 1, AutoCommit => 1});
|
|
|
|
eval {
|
|
my $sth = $dbh->prepare($query);
|
|
$sth->execute;
|
|
$count = $sth->fetchrow_array();
|
|
|
|
$sth->finish;
|
|
};
|
|
|
|
if ( $@ ) {
|
|
Immunix::Ycp::y2error(sprintf(gettext("DBI Execution failed: %s."), $DBI::errstr));
|
|
return;
|
|
}
|
|
|
|
$dbh->disconnect();
|
|
|
|
#Immunix::Ycp::y2milestone("Numpages Query: $query"); # debug
|
|
|
|
$numPages = pageRound($count/$numEvents);
|
|
if ( $numPages < 1 ) { $numPages = 1; }
|
|
|
|
} elsif ( $type && $type eq 'arch' ) {
|
|
|
|
if ( open(REP, "<$eventRep") ) {
|
|
|
|
while(<REP>) {
|
|
if (/^Page/) {
|
|
$numPages++;
|
|
} else {
|
|
$count++;
|
|
}
|
|
}
|
|
|
|
close REP;
|
|
|
|
} else {
|
|
Immunix::Ycp::y2error(sprintf(gettext("Couldn't open file: %s."), $eventRep));
|
|
}
|
|
|
|
} else {
|
|
Immunix::Ycp::y2error(gettext("No type value passed. Unable to determine page count."));
|
|
return("1");
|
|
}
|
|
|
|
if ( $numPages < 1 ) { $numPages = 1; }
|
|
|
|
my $numCheck = int($count/$numEvents);
|
|
|
|
if ($numPages < $numCheck) {
|
|
$numPages = $numCheck;
|
|
}
|
|
|
|
return($numPages);
|
|
}
|
|
|
|
sub getEpochFromNum {
|
|
|
|
my $date = shift;
|
|
my $place = shift || undef; # Used to set default $sec if undef
|
|
|
|
my ($numMonth,$numDay,$time,$year) = split(/\s+/, $date);
|
|
my ($hour,$min,$sec) = '0';
|
|
my $junk = undef;
|
|
|
|
if ($time =~ /:/) {
|
|
($hour,$min,$sec,$junk) = split(/\:/, $time);
|
|
if (! $hour || $hour eq "" ) { $hour = '0'; }
|
|
if (! $min || $min eq "" ) { $min = '0'; }
|
|
if (! $sec || $sec eq "" ) {
|
|
if ($place eq 'end') {
|
|
$sec = '59';
|
|
} else {
|
|
$sec = '0';
|
|
}
|
|
}
|
|
}
|
|
|
|
$numMonth--; # Months start from 0 for epoch translation
|
|
|
|
if (! $year) { $year = (split(/\s+/, localtime))[4]; }
|
|
my $epochDate = timelocal($sec,$min,$hour,$numDay,$numMonth,$year);
|
|
|
|
return $epochDate;
|
|
}
|
|
|
|
sub getEpochFromStr {
|
|
|
|
my $lexDate = shift;
|
|
|
|
my ($lexMonth, $dateDay, $fullTime, $year) = split(/\s+/, $lexDate);
|
|
#my ($lexDay, $lexMonth, $dateDay, $fullTime, $year) = split(/\s+/, $lexDate);
|
|
my ($hour,$min,$sec) = split(/\:/, $fullTime);
|
|
|
|
if (! $year) { $year = (split(/\s+/, localtime))[4]; }
|
|
|
|
my $numMonth = month2Num($lexMonth);
|
|
|
|
my $epochDate = timelocal($sec,$min,$hour,$dateDay,$numMonth,$year);
|
|
|
|
return $epochDate;
|
|
}
|
|
|
|
# Replaces old files with new files
|
|
sub updateFiles {
|
|
|
|
my ( $oldFile, $newFile ) = @_;
|
|
|
|
if ( unlink("$oldFile") ) {
|
|
if ( ! rename ("$newFile", "$oldFile") ) {
|
|
if ( ! system('/bin/mv', "$newFile","$oldFile") ) {
|
|
Immunix::Ycp::y2error(sprintf(gettext("Failed copying %s."), $oldFile));
|
|
return 1;
|
|
}
|
|
}
|
|
} else {
|
|
system('/bin/rm', "$oldFile");
|
|
system('/bin/mv', "$newFile", "$oldFile");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
# This is a holder, that was originally part of exportLog()
|
|
# Used by /usr/bin/reportgen.pl
|
|
sub exportFormattedText {
|
|
|
|
my ($repName,$logFile,$db) = @_;
|
|
|
|
my $date = localtime;
|
|
open (LOG, ">$logFile") || die "Couldn't open $logFile";
|
|
|
|
# Date Profile PID Mesg
|
|
print LOG "$repName: Log generated by Novell AppArmor, $date\n\n";
|
|
printf LOG "%-21s%-32s%-8s%-51s", "Host","Date","Program","Profile","PID","Severity","Mode","Detail","Access Type";
|
|
print LOG "\n";
|
|
|
|
for (sort (@$db) ) {
|
|
print LOG "$_->{'host'},$_->{'time'},$_->{'prog'},$_->{'profile'},";
|
|
print LOG "$_->{'pid'},$_->{'severity'},$->{'mode'},$_->{'resource'},$_->{'sdmode'}\n";
|
|
}
|
|
|
|
|
|
close LOG;
|
|
}
|
|
|
|
sub exportLog {
|
|
|
|
my ($exportLog,$db,$header) = @_;
|
|
|
|
if ( open (LOG, ">$exportLog") ) {
|
|
|
|
my $date = localtime();
|
|
|
|
if ($exportLog =~ /csv/ ) {
|
|
|
|
# $header comes from reportgen.pl (scheduled reports)
|
|
if ($header) { print LOG "$header\n\n"; }
|
|
|
|
for (@$db) {
|
|
no strict;
|
|
|
|
# host time prog profile pid severity resource sdmode mode
|
|
#print LOG "$_->{'host'},$_->{'time'},$_->{'prog'},$_->{'profile'},$_->{'pid'},";
|
|
print LOG "$_->{'host'},$_->{'date'},$_->{'prog'},$_->{'profile'},$_->{'pid'},";
|
|
print LOG "$_->{'severity'},$_->{'mode'},$_->{'resource'},$_->{'sdmode'}\n";
|
|
|
|
}
|
|
|
|
} elsif ( $exportLog =~ /html/ ) {
|
|
|
|
print LOG "<html><body bgcolor='fffeec'>\n\n";
|
|
print LOG "<font face='Helvetica,Arial,Sans-Serif'>\n";
|
|
|
|
# $header comes from reportgen.pl (scheduled reports)
|
|
if ($header) {
|
|
print LOG "$header\n\n";
|
|
} else {
|
|
print LOG "<br><h3>$exportLog</h3><br>\n<h4>Log generated by Novell AppArmor, $date</h4>\n\n";
|
|
}
|
|
|
|
print LOG "<hr><br><table border='1' cellpadding='2'>\n";
|
|
#print LOG "<tr bgcolor='edefff'><th>Date</th><th>Profile</th><th>PID</th><th>Message</th></tr>\n";
|
|
print LOG "<tr bgcolor='edefff'><th>Host</th><th>Date</th><th>Program</th><th>Profile</th><th>PID</th>" .
|
|
"<th>Severity</th><th>Mode</th><th>Detail</th><th>Access Type</th></tr>\n";
|
|
|
|
my $idx = 1;
|
|
|
|
for (@$db) {
|
|
no strict;
|
|
$idx++;
|
|
if ( $idx%2 == 0 ) {
|
|
|
|
#"<td> $_->{'time'} </td>" .
|
|
print LOG "<tr><td> $_->{'host'} </td>" .
|
|
"<td> $_->{'date'} </td>" .
|
|
"<td> $_->{'prog'} </td>" .
|
|
"<td> $_->{'profile'} </td>" .
|
|
"<td> $_->{'pid'} </td>" .
|
|
"<td> $_->{'severity'} </td>" .
|
|
"<td> $_->{'mode'} </td>" .
|
|
"<td> $_->{'resource'} </td>" .
|
|
"<td> $_->{'sdmode'} </td></tr>\n";
|
|
|
|
} else {
|
|
# Shade every other row
|
|
print LOG "<tr='edefef'><td> $_->{'host'} </td>" .
|
|
"<td> $_->{'date'} </td>" .
|
|
"<td> $_->{'prog'} </td>" .
|
|
"<td> $_->{'profile'} </td>" .
|
|
"<td> $_->{'pid'} </td>" .
|
|
"<td> $_->{'severity'} </td>" .
|
|
"<td> $_->{'mode'} </td>" .
|
|
"<td> $_->{'resource'} </td>" .
|
|
"<td> $_->{'sdmode'} </td></tr>\n";
|
|
|
|
}
|
|
}
|
|
|
|
print LOG "<br></table></font></body></html>\n\n";
|
|
}
|
|
|
|
close LOG;
|
|
} else {
|
|
Immunix::Ycp::y2error(sprintf(gettext("Export Log Error: Couldn't open %s"), $exportLog));
|
|
}
|
|
# return($error);
|
|
}
|
|
|
|
# Pulls info on single report from apparmor xml file
|
|
sub getXmlReport {
|
|
|
|
my ($repName,$repConf) = @_;
|
|
my $repFlag = 0;
|
|
|
|
my %rep = ();
|
|
|
|
if ( defined($repName) && ref($repName) ) {
|
|
|
|
if ( $repName->{'base'} ) {
|
|
$repName = $repName->{'base'};
|
|
} elsif ( $repName->{'name'} ) {
|
|
$repName = $repName->{'name'};
|
|
}
|
|
|
|
}
|
|
|
|
if ( ! $repName ) {
|
|
Immunix::Ycp::y2error(gettext("Fatal error. No report name given. Exiting."));
|
|
}
|
|
|
|
if ( ! $repConf || ! -e $repConf ) {
|
|
$repConf = '/etc/apparmor/reports.conf';
|
|
if ( ! -e $repConf ) {
|
|
Immunix::Ycp::y2error(sprintf(gettext("Unable to get configuration info for %s.
|
|
Unable to find %s."), $repName, $repConf));
|
|
exit 1;
|
|
}
|
|
}
|
|
|
|
if ( open(XML, "<$repConf") ) {
|
|
|
|
while(<XML>) {
|
|
|
|
chomp;
|
|
|
|
if ( /\<name\>/ ) {
|
|
#my $name = (split(/\"/, $_))[1];
|
|
/\<name\>(.+)\<\/name\>/;
|
|
my $name = $1;
|
|
if ( $name eq $repName ) {
|
|
$rep{'name'} = $name;
|
|
$repFlag = 1;
|
|
}
|
|
|
|
} elsif ( /\<\/report\>/ ) {
|
|
|
|
$repFlag = 0;
|
|
|
|
} elsif ( $repFlag == 1 ) {
|
|
if ( /\s*\<\w+\s+(.*)\/\>.*$/ ) {
|
|
my $attrs = $1;
|
|
chomp($attrs);
|
|
my @attrlist = split(/\s+/, $attrs);
|
|
for ( @attrlist ) {
|
|
#Match attributes
|
|
if ( /\s*(\S+)=\"(\S+)\"/ ) {
|
|
$rep{$1} = $2 unless $2 eq '-';
|
|
}
|
|
}
|
|
} elsif ( /\<(\w+)\>([\w+|\/].*)\<\// ) {
|
|
|
|
if ($1) {
|
|
$rep{"$1"}= $2 unless $2 eq '-';
|
|
} else {
|
|
Immunix::Ycp::y2error(sprintf(gettext("Failed to parse: %s."), $_));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
close XML;
|
|
|
|
} else {
|
|
Immunix::Ycp::y2error(sprintf(gettext("Fatal Error. Couldn't open %s."), $repConf));
|
|
exit 1;
|
|
}
|
|
|
|
return \%rep;
|
|
}
|
|
|
|
# Returns info on currently confined processes
|
|
sub getCfInfo {
|
|
|
|
my $ref = ();
|
|
my @cfDb = ();
|
|
|
|
my $cfApp = '/usr/sbin/unconfined';
|
|
|
|
if ( open (CF, "$cfApp |") ) {
|
|
|
|
my $host = `hostname`;
|
|
chomp($host);
|
|
|
|
my $date = localtime;
|
|
|
|
while(<CF>) {
|
|
|
|
my $ref = ();
|
|
my $all = undef;
|
|
$ref->{'host'} = $host;
|
|
$ref->{'date'} = $date;
|
|
chomp;
|
|
|
|
($ref->{'pid'}, $ref->{'prog'}, $all) = split(/\s+/, $_, 3);
|
|
$all = /\s*((not)*\s*confined\s*(by)*)/;
|
|
$ref->{'state'} = $1;
|
|
$ref->{'state'} =~ s/\s*by//g;
|
|
$ref->{'state'} =~ s/not\s+/not-/g;
|
|
($ref->{'prof'}, $ref->{'type'}) = split(/\s+/, $_);
|
|
|
|
if ( $ref->{'prog'} eq "") { $ref->{'prog'} = "-"; }
|
|
if ( $ref->{'prof'} eq "") { $ref->{'prof'} = "-"; }
|
|
if ( $ref->{'pid'} eq "") { $ref->{'pid'} = "-"; }
|
|
if ( $ref->{'state'} eq "") { $ref->{'state'} = "-"; }
|
|
if ( $ref->{'type'} eq "") { $ref->{'type'} = "-"; }
|
|
|
|
push (@cfDb, $ref);
|
|
}
|
|
close CF;
|
|
|
|
} else {
|
|
my $error = sprintf(gettext("Fatal Error. Can't run %s. Exiting."), $cfApp);
|
|
Immunix::Ycp::y2error($error);
|
|
return $error;
|
|
}
|
|
|
|
return (\@cfDb);
|
|
}
|
|
|
|
# generate stats for ESS reports
|
|
sub getEssStats {
|
|
|
|
my $args = shift;
|
|
|
|
#my ($host, $targetDir, $startdate, $enddate) = @_;
|
|
|
|
my @hostDb = ();
|
|
my @hostList = ();
|
|
my $targetDir = undef;
|
|
my $host = undef;
|
|
my $startdate = undef;
|
|
my $enddate = undef;
|
|
|
|
if ( ! $args->{'targetDir'} ) {
|
|
$targetDir = '/var/log/apparmor/';
|
|
}
|
|
|
|
if ( $args->{'host'} ) { $host = $args->{'host'}; }
|
|
|
|
if ( $args->{'startdate'} ) {
|
|
$startdate = $args->{'startdate'};
|
|
} else {
|
|
$startdate = '1104566401'; # Jan 1, 2005
|
|
}
|
|
|
|
if ( $args->{'enddate'} ) {
|
|
$enddate = $args->{'enddate'};
|
|
} else {
|
|
$enddate = time;
|
|
}
|
|
|
|
if ( ! -e $targetDir ) {
|
|
Immunix::Ycp::y2error(sprintf(gettext("Fatal Error. No directory, %s, found. Exiting."), $targetDir));
|
|
return;
|
|
}
|
|
|
|
# Max Sev, Ave. Sev, Num. Rejects, Start Time, End Time
|
|
my $ctQuery = "SELECT count(*) FROM events WHERE time >= $startdate AND time <= $enddate";
|
|
|
|
my $query = "SELECT MAX(severity), AVG(severity), COUNT(id), MIN(time), " .
|
|
"MAX(time) FROM events WHERE sdmode='REJECTING' AND " .
|
|
"time >= $startdate AND time <= $enddate";
|
|
# "MAX(time) FROM events join info WHERE sdmode='REJECTING' AND " .
|
|
|
|
# Get list of hosts to scan
|
|
if ( opendir (TDIR, $targetDir) ) {
|
|
|
|
@hostList = grep(/\.db/, readdir(TDIR));
|
|
close TDIR;
|
|
|
|
} else {
|
|
Immunix::Ycp::y2error(sprintf(gettext("Fatal Error. Couldn't open %s. Exiting"), $targetDir));
|
|
return;
|
|
}
|
|
|
|
# Cycle through for each host
|
|
for my $eventDb (@hostList) {
|
|
|
|
$eventDb = "$targetDir/$eventDb";
|
|
|
|
my $ess = undef;
|
|
my $ret = undef;
|
|
my $count = undef;
|
|
#my $eventDb = '/var/log/apparmor/events.db';
|
|
|
|
my $dbh = DBI->connect("dbi:SQLite:dbname=$eventDb", "", "", {RaiseError => 1, AutoCommit => 1});
|
|
|
|
# get hostname
|
|
my $host = undef;
|
|
my $hostQuery = "SELECT * FROM info WHERE name='host'";
|
|
|
|
eval {
|
|
my $sth = $dbh->prepare($hostQuery);
|
|
$sth->execute;
|
|
$host = $sth->fetchrow_array();
|
|
$sth->finish;
|
|
};
|
|
|
|
if ( $@ ) {
|
|
Immunix::Ycp::y2error(sprintf(gettext("DBI Execution failed: %s"), $DBI::errstr));
|
|
return;
|
|
}
|
|
|
|
# Get number of events
|
|
eval {
|
|
my $sth = $dbh->prepare($ctQuery);
|
|
$sth->execute;
|
|
$count = $sth->fetchrow_array();
|
|
$sth->finish;
|
|
};
|
|
|
|
if ( $@ ) {
|
|
Immunix::Ycp::y2error(sprintf(gettext("DBI Execution failed: %s"), $DBI::errstr));
|
|
return;
|
|
}
|
|
|
|
# Get rest of stats
|
|
eval {
|
|
$ret = $dbh->selectall_arrayref("$query");
|
|
};
|
|
|
|
if ( $@ ) {
|
|
Immunix::Ycp::y2error(sprintf(gettext("DBI Execution failed: %s"), $DBI::errstr));
|
|
return;
|
|
}
|
|
|
|
$dbh->disconnect();
|
|
|
|
# hostIp, startDate, endDate, sevHi, sevMean, numRejects
|
|
if ( $host ) {
|
|
$ess->{'host'} = $host;
|
|
} else {
|
|
$ess->{'host'} = '';
|
|
}
|
|
|
|
$ess->{'sevHi'} = $$ret[0]->[0];
|
|
|
|
if ( ! $ess->{'sevHi'} ) {
|
|
$ess->{'sevHi'} = 0;
|
|
}
|
|
|
|
$ess->{'sevMean'} = $$ret[0]->[1];
|
|
|
|
if ( ! $ess->{'sevMean'} || $ess->{'sevHi'} == 0) {
|
|
$ess->{'sevMean'} = 0;
|
|
} else {
|
|
$ess->{'sevMean'} = round("$ess->{'sevMean'}");
|
|
}
|
|
|
|
$ess->{'numRejects'} = $$ret[0]->[2];
|
|
$ess->{'startdate'} = $$ret[0]->[3];
|
|
$ess->{'enddate'} = $$ret[0]->[4];
|
|
$ess->{'numEvents'} = $count;
|
|
|
|
# Convert dates
|
|
if ( $ess->{'startdate'} && $ess->{'startdate'} !~ /:/) {
|
|
$ess->{'startdate'} = Immunix::Reports::getDate($ess->{'startdate'});
|
|
}
|
|
if ( $ess->{'enddate'} && $ess->{'enddate'} !~ /:/) {
|
|
$ess->{'enddate'} = Immunix::Reports::getDate($ess->{'enddate'});
|
|
}
|
|
|
|
push (@hostDb, $ess);
|
|
}
|
|
|
|
return \@hostDb;
|
|
}
|
|
|
|
|
|
# get ESS stats for archived reports (warning -- this can be slow for large files
|
|
# debug -- not fully functional yet
|
|
sub getArchEssStats {
|
|
|
|
my $args = shift;
|
|
|
|
my $prevTime = '0';
|
|
my $prevDate = '0';
|
|
my $startDate = '1104566401'; # Jan 1, 2005
|
|
my $endDate = time;
|
|
|
|
if ($args->{'startdate'} ) { $startDate = $args->{'startdate'}; }
|
|
if ($args->{'enddate'} ) { $endDate = $args->{'enddate'}; }
|
|
|
|
# hostIp, startDate, endDate, sevHi, sevMean, numRejects
|
|
my @eventDb = getEvents("$startDate","$endDate");
|
|
|
|
my @hostIdx = (); # Simple index to all hosts for quick host matching
|
|
my @hostDb = (); # Host-keyed Data for doing REJECT stats
|
|
|
|
# Outer Loop for Raw Event db
|
|
for (@eventDb) {
|
|
|
|
if ( $_->{'host'} ) {
|
|
|
|
my $ev = $_; # current event record
|
|
|
|
# Create new host entry, or add to existing
|
|
if ( grep(/$ev->{'host'}/, @hostIdx) == 1 ) {
|
|
|
|
# Inner loop, but the number of hosts should be small
|
|
for (@hostDb) {
|
|
|
|
if ($_->{'host'} eq $ev->{'host'}) {
|
|
|
|
if ( $_->{'startdate'} > $ev->{'date'} ) {
|
|
$_->{'startdate'} = $ev->{'date'}; # Find earliest start date
|
|
}
|
|
|
|
$_->{'numEvents'}++; # tally all events reported for host
|
|
|
|
if ( $ev->{'sdmode'} ) {
|
|
if ( $ev->{'sdmode'} =~ /PERMIT/ ) { $_->{'numPermits'}++; }
|
|
if ( $ev->{'sdmode'} =~ /REJECT/ ) { $_->{'numRejects'}++; }
|
|
if ( $ev->{'sdmode'} =~ /AUDIT/ ) { $_->{'numAudits'}++; }
|
|
}
|
|
|
|
# Add stats to host entry
|
|
#if ( $ev->{'severity'} && $ev->{'severity'} =~ /\b\d+\b/ ) {}
|
|
if ( $ev->{'severity'} && $ev->{'severity'} != -1 ) {
|
|
|
|
$_->{'sevNum'}++;
|
|
$_->{'sevTotal'} = $_->{'sevTotal'} + $ev->{'severity'};
|
|
|
|
if ($ev->{'severity'} > $_->{'sevHi'} ) {
|
|
$_->{'sevHi'} = $ev->{'severity'};
|
|
}
|
|
} else {
|
|
$_->{'unknown'}++;
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
# New host
|
|
my $rec = undef;
|
|
push(@hostIdx,$ev->{'host'}); # Add host entry to index
|
|
|
|
$rec->{'host'} = $ev->{'host'};
|
|
$rec->{'startdate'} = $startDate;
|
|
#$rec->{'startdate'} = $ev->{'date'};
|
|
|
|
if ( $endDate ) {
|
|
$rec->{'enddate'} = $endDate;
|
|
} else {
|
|
$rec->{'enddate'} = time;
|
|
}
|
|
|
|
# Add stats to host entry
|
|
if ( $ev->{'sev'} && $ev->{'sev'} ne "U" ) {
|
|
|
|
$rec->{'sevHi'} = $ev->{'sev'};
|
|
$rec->{'sevTotal'} = $ev->{'sev'};
|
|
$rec->{'sevNum'} = 1;
|
|
$rec->{'unknown'} = 0;
|
|
|
|
} else {
|
|
$rec->{'sevHi'} = 0;
|
|
$rec->{'sevTotal'} = 0;
|
|
$rec->{'sevNum'} = 0;
|
|
$rec->{'unknown'} = 1;
|
|
}
|
|
|
|
# Start sdmode stats
|
|
$rec->{'numPermits'} = 0;
|
|
$rec->{'numRejects'} = 0;
|
|
$rec->{'numAudits'} = 0;
|
|
$rec->{'numEvents'} = 1; # tally all events reported for host
|
|
|
|
if ( $ev->{'sdmode'} ) {
|
|
if ( $ev->{'sdmode'} =~ /PERMIT/ ) { $rec->{'numPermits'}++; }
|
|
if ( $ev->{'sdmode'} =~ /REJECT/ ) { $rec->{'numRejects'}++; }
|
|
if ( $ev->{'sdmode'} =~ /AUDIT/ ) { $rec->{'numAudits'}++; }
|
|
}
|
|
|
|
push (@hostDb,$rec); # Add new records to host data list
|
|
}
|
|
|
|
} else {
|
|
next; # Missing host info -- big problem
|
|
}
|
|
} # END @eventDb loop
|
|
|
|
# Process simple REJECT-related stats (for Executive Security Summaries)
|
|
for ( @hostDb ) {
|
|
|
|
# In the end, we want this info:
|
|
# - Hostname, Startdate, Enddate, # Events, # Rejects, Ave. Severity, High Severity
|
|
|
|
if ( $_->{'sevTotal'} > 0 && $_->{'sevNum'} > 0 ) {
|
|
$_->{'sevMean'} = round($_->{'sevTotal'}/$_->{'sevNum'});
|
|
} else {
|
|
$_->{'sevMean'} = 0;
|
|
}
|
|
|
|
# Convert dates
|
|
if ($_->{'startdate'} !~ /:/) {$_->{'startdate'} = getDate($startDate); }
|
|
if ($_->{'enddate'} !~ /:/) { $_->{'enddate'} = getDate($_->{'enddate'}); }
|
|
|
|
# Delete stuff that we may use in later versions (YaST is a silly, silly data handler)
|
|
delete($_->{'sevTotal'});
|
|
delete($_->{'sevNum'});
|
|
delete($_->{'numPermits'});
|
|
delete($_->{'numAudits'});
|
|
delete($_->{'unknown'});
|
|
|
|
}
|
|
|
|
return(\@hostDb);
|
|
}
|
|
|
|
# special version of getEvents() for /usr/bin/reportgen.pl
|
|
sub grabEvents {
|
|
|
|
my ($rep,$start,$end) = @_;
|
|
my $db = undef;
|
|
my $prevDate = "0";
|
|
my $prevTime = "0";
|
|
|
|
my $query = "SELECT * FROM events ";
|
|
|
|
# Clear unnecessary filters
|
|
if ( $rep->{'prog'} ) { $rep->{'prog'} =~ s/\s+//g; }
|
|
if ( $rep->{'prof'} ) { $rep->{'prof'} =~ s/\s+//g; }
|
|
if ( $rep->{'mode'} ) { $rep->{'mode'} =~ s/\s+//g; }
|
|
if ( $rep->{'sdmode'} ) { $rep->{'sdmode'} =~ s/\s+//g; }
|
|
if ( $rep->{'sev'} ) { $rep->{'sev'} =~ s/\s+//g; }
|
|
if ( $rep->{'res'} ) { $rep->{'res'} =~ s/\s+//g; }
|
|
|
|
if ($rep->{'prog'} && ($rep->{'prog'} eq "-" || $rep->{'prog'} eq "All") ) {
|
|
delete($rep->{'prog'});
|
|
}
|
|
if ($rep->{'prof'} && $rep->{'prof'} eq "-") { delete($rep->{'prof'}); }
|
|
if ($rep->{'pid'} && $rep->{'pid'} eq "-") { delete($rep->{'pid'}); }
|
|
if ( $rep->{'sev'} && ( $rep->{'sev'} eq "-" || $rep->{'sev'} eq "All" ) ) {
|
|
delete($rep->{'sev'});
|
|
}
|
|
if ($rep->{'resource'} && $rep->{'resource'} eq "-") { delete($rep->{'resource'}); }
|
|
|
|
if ($rep->{'mode'} && ( $rep->{'mode'} eq "-" || $rep->{'mode'} eq "All" ) ) {
|
|
delete($rep->{'mode'});
|
|
}
|
|
|
|
if ($rep->{'sdmode'} && ($rep->{'sdmode'} eq "-" || $rep->{'sdmode'} eq "All") ) {
|
|
delete($rep->{'sdmode'});
|
|
}
|
|
|
|
$rep = rewriteModes($rep);
|
|
|
|
# Set Dates far enough apart to get all entries (ie. no date filter)
|
|
my $startDate = '1104566401'; # Jan 1, 2005
|
|
my $endDate = time;
|
|
|
|
if ( $start && $start > 0 ) { $startDate = $start; }
|
|
|
|
if ( ref($rep) ) {
|
|
my $midQuery = getQueryFilters($rep,$startDate,$endDate);
|
|
$query .= "$midQuery";
|
|
}
|
|
|
|
$db = getEvents($query,"$startDate","$endDate");
|
|
|
|
return($db);
|
|
}
|
|
|
|
sub getQueryFilters {
|
|
|
|
my ($filts,$start,$end) = @_;
|
|
|
|
my $query = undef;
|
|
my $wFlag = 0;
|
|
|
|
if ( $filts ) {
|
|
|
|
# Match any requested filters or drop record
|
|
############################################################
|
|
if ( $filts->{'prog'} ) {
|
|
$query .= "WHERE events.prog = \'$filts->{'prog'}\' ";
|
|
$wFlag = 1;
|
|
}
|
|
|
|
if ( $filts->{'profile'} && $_->{'profile'} ) {
|
|
if ( $wFlag == 1 ) {
|
|
$query .= "AND events.profile = \'$filts->{'profile'}\' ";
|
|
} else {
|
|
$query .= "WHERE events.profile = \'$filts->{'profile'}\' ";
|
|
}
|
|
$wFlag = 1;
|
|
}
|
|
|
|
if ( $filts->{'pid'} ) {
|
|
if ( $wFlag == 1 ) {
|
|
$query .= "AND events.pid = \'$filts->{'pid'}\' ";
|
|
} else {
|
|
$query .= "WHERE events.pid = \'$filts->{'pid'}\' ";
|
|
}
|
|
$wFlag = 1;
|
|
}
|
|
|
|
if ($filts->{'severity'}) {
|
|
if ($filts->{'severity'} eq "-" || $filts->{'severity'} eq "All" ) {
|
|
delete($filts->{'severity'});
|
|
} elsif ( $filts->{'severity'} eq "-1" || $filts->{'severity'} eq "U" ) {
|
|
if ( $wFlag == 1 ) {
|
|
$query .= "AND events.severity = '-1' ";
|
|
} else {
|
|
$query .= "WHERE events.severity = '-1' ";
|
|
}
|
|
$wFlag = 1;
|
|
} else {
|
|
if ( $wFlag == 1 ) {
|
|
$query .= "AND events.severity >= \'$filts->{'severity'}\' ";
|
|
} else {
|
|
$query .= "WHERE events.severity >= \'$filts->{'severity'}\' ";
|
|
}
|
|
$wFlag = 1;
|
|
}
|
|
}
|
|
|
|
if ( $filts->{'resource'} ) {
|
|
if ( $wFlag == 1 ) {
|
|
$query .= "AND events.resource LIKE '%$filts->{'resource'}%' ";
|
|
} else {
|
|
$query .= "WHERE events.resource LIKE '%$filts->{'resource'}%' ";
|
|
}
|
|
$wFlag = 1;
|
|
}
|
|
|
|
if ( $filts->{'mode'} ) {
|
|
if ( $wFlag == 1 ) {
|
|
$query .= "AND events.mode LIKE '%$filts->{'mode'}%' ";
|
|
} else {
|
|
$query .= "WHERE events.mode LIKE '%$filts->{'mode'}%' ";
|
|
}
|
|
$wFlag = 1;
|
|
}
|
|
|
|
if ( $filts->{'sdmode'} ) {
|
|
|
|
if ( $filts->{'sdmode'} =~ /\|/ ) {
|
|
|
|
my @sdmunge = split(/\|/, $filts->{'sdmode'});
|
|
for (@sdmunge) { $_ = "\'\%" . "$_" . "\%\'"; }
|
|
|
|
$filts->{'sdmode'} = join( " OR events.sdmode LIKE ", @sdmunge);
|
|
|
|
} else {
|
|
$filts->{'sdmode'} = "\'\%" . "$filts->{'sdmode'}" . "\%\'";
|
|
}
|
|
|
|
if ( $wFlag == 1 ) {
|
|
$query .= "AND events.sdmode LIKE $filts->{'sdmode'} ";
|
|
} else {
|
|
$query .= "WHERE events.sdmode LIKE $filts->{'sdmode'} ";
|
|
}
|
|
$wFlag = 1;
|
|
|
|
}
|
|
}
|
|
|
|
if ( $start && $start =~ /\d+/ && $start > 0 ) {
|
|
if ( $wFlag == 1 ) {
|
|
$query .= "AND events.time >= $start ";
|
|
} else {
|
|
$query .= "WHERE events.time >= $start ";
|
|
}
|
|
$wFlag = 1;
|
|
}
|
|
|
|
if ( $end && $end =~ /\d+/ && $end > $start ) {
|
|
if ( $wFlag == 1 ) {
|
|
$query .= "AND events.time <= $end ";
|
|
} else {
|
|
$query .= "WHERE events.time <= $end ";
|
|
}
|
|
}
|
|
|
|
return $query;
|
|
}
|
|
|
|
|
|
sub getQuery {
|
|
|
|
my ($filts,$page,$sortKey,$numEvents) = @_;
|
|
|
|
if ( ! $page || $page < 1 || $page !~ /\d+/ ) { $page = 1; }
|
|
if ( ! $sortKey ) { $sortKey = 'time'; }
|
|
if ( ! $numEvents ) { $numEvents = '1000'; }
|
|
|
|
my $limit = ( ($page * $numEvents) - $numEvents );
|
|
|
|
my $query = "SELECT * FROM events ";
|
|
|
|
if ( $filts ) {
|
|
my $midQuery = getQueryFilters($filts);
|
|
$query .= "$midQuery";
|
|
}
|
|
|
|
# Finish query
|
|
$query .= "Order by $sortKey LIMIT $limit,$numEvents";
|
|
|
|
return $query;
|
|
}
|
|
|
|
# - This should exec AFTER the initial select (should limit the number of records
|
|
# that we'll be mangling
|
|
# - There may be a way to do this with a creative query statement generator
|
|
|
|
sub queryPostProcess {
|
|
|
|
my $db = shift;
|
|
my @newDb = ();
|
|
my $prevTime = 0;
|
|
my $prevDate = 0;
|
|
|
|
for (@$db) {
|
|
|
|
# Shuffle special events into appropriate column variables
|
|
############################################################
|
|
if ( $_->{'attrch'} ) { $_->{'sdmode'} .= " $_->{'attrch'}"; }
|
|
|
|
if ( $_->{'type'} ) {
|
|
|
|
if ( $_->{'type'} eq 'control_variable' ) {
|
|
# OWLSM gets special treatment
|
|
if ( $_->{'variable'} eq 'owlsm' ) {
|
|
#if ( $_->{'value'} ) {}
|
|
if ( $_->{'value'} == '0' ) {
|
|
$_->{'resource'} = "GLOBAL MODULE CHANGE: OWLSM DISABLED";
|
|
} elsif ( $_->{'value'} == '1' ) {
|
|
$_->{'resource'} = "GLOBAL MODULE CHANGE: OWLSM ENABLED";
|
|
} else {
|
|
$_->{'resource'} = "Unrecognized OWLSM activity.";
|
|
}
|
|
} else {
|
|
$_->{'resource'} = "$_->{'variable'}";
|
|
}
|
|
} elsif ( $_->{'type'} eq 'capability' ) {
|
|
$_->{'resource'} .= " $_->{'capability'}";
|
|
|
|
} elsif ( $_->{'type'} eq 'attribute_change' ) {
|
|
$_->{'sdmode'} .= " $_->{'attribute'} change";
|
|
} elsif ( $_->{'type'} eq 'subdomain_insmod' ) {
|
|
$_->{'resource'} = "AppArmor Started";
|
|
} elsif ( $_->{'type'} eq 'subdomain_rmmod' ) {
|
|
$_->{'resource'} = "AppArmor Stopped";
|
|
# DROP logprof-hints
|
|
} elsif ( $_->{'type'} eq 'unknown_hat' ) {
|
|
next;
|
|
# DROP logprof-hints
|
|
} elsif ( $_->{'type'} eq 'changing_profile' ) {
|
|
next;
|
|
# DROP logprof-hints
|
|
} elsif ( $_->{'type'} eq 'fork' ) {
|
|
next;
|
|
} elsif ( $_->{'type'} ne 'path' ) {
|
|
$_->{'resource'} .= " $_->{'type'}";
|
|
}
|
|
}
|
|
|
|
# Convert Epoch Time to Date
|
|
if ( $_->{'time'} && $_->{'time'} == $prevTime ) {
|
|
$_->{'date'} = $prevDate;
|
|
} elsif ($_->{'time'}) {
|
|
my $newDate = getDate("$_->{'time'}");
|
|
$_->{'date'} = $newDate;
|
|
$prevDate = $newDate;
|
|
$prevTime = $_->{'time'};
|
|
} else {
|
|
$_->{'date'} = "0000-00-00 00:00:00";
|
|
}
|
|
# $_->{'time'} = undef; # Don't need 'time', only 'date'
|
|
if (! $_->{'host'}) { $_->{'host'} = "-"; }
|
|
if (! $_->{'date'}) { $_->{'date'} = "-"; }
|
|
if (! $_->{'prog'}) { $_->{'prog'} = "-"; }
|
|
if (! $_->{'profile'}) { $_->{'profile'} = "-"; }
|
|
if (! $_->{'pid'}) { $_->{'pid'} = "-"; }
|
|
if (! $_->{'mode'}) { $_->{'mode'} = "-"; }
|
|
if (! $_->{'resource'}) { $_->{'resource'} = "-"; }
|
|
if (! $_->{'sdmode'}) { $_->{'sdmode'} = "-"; }
|
|
|
|
if (! $_->{'severity'}) {
|
|
$_->{'severity'} = "-";
|
|
} elsif ($_->{'severity'} eq "-1") {
|
|
$_->{'severity'} = "U";
|
|
}# else {
|
|
# $_->{'severity'} = sprintf("%02d", $_->{'severity'});
|
|
#}
|
|
|
|
push(@newDb, $_); # Don't quote the $_ (breaks hash)
|
|
|
|
}
|
|
|
|
return \@newDb;
|
|
|
|
}
|
|
|
|
# Creates single hashref for the various filters
|
|
sub setFormFilters {
|
|
|
|
my $args = shift;
|
|
my $filts = undef;
|
|
|
|
if ( $args ) {
|
|
|
|
if ( $args->{'prog'} ) { $filts->{'prog'} = $args->{'prog'}; }
|
|
if ( $args->{'profile'} ) { $filts->{'profile'} = $args->{'profile'}; }
|
|
if ( $args->{'pid'} ) { $filts->{'pid'} = $args->{'pid'}; }
|
|
if ( $args->{'resource'} ) { $filts->{'resource'} = $args->{'resource'}; }
|
|
if ( $args->{'severity'} ) { $filts->{'severity'} = $args->{'severity'}; }
|
|
if ( $args->{'sdmode'} ) { $filts->{'sdmode'} = $args->{'sdmode'}; }
|
|
if ( $args->{'mode'} ) { $filts->{'mode'} = $args->{'mode'}; }
|
|
|
|
}
|
|
|
|
return $filts;
|
|
}
|
|
|
|
# helper for getSirFilters()
|
|
# Makes gui-centric filters querying-friendly
|
|
sub rewriteFilters {
|
|
|
|
my $filts = shift;
|
|
|
|
# Clear unnecessary filters
|
|
for (keys(%$filts)) { if ( $filts->{$_} eq "All" ) { delete($filts->{$_}); } }
|
|
|
|
if ($filts->{'prog'} && ($filts->{'prog'} eq "-" || $filts->{'prog'} eq "All") ) {
|
|
delete($filts->{'prog'});
|
|
}
|
|
if ($filts->{'profile'} && ($filts->{'profile'} eq "-") ) { delete($filts->{'profile'}); }
|
|
if ($filts->{'pid'} && ($filts->{'pid'} eq "-") ) { delete($filts->{'pid'}); }
|
|
if ($filts->{'severity'} && ($filts->{'severity'} eq "-") ) { delete($filts->{'severity'}); }
|
|
if ($filts->{'resource'} && ($filts->{'resource'} eq "-") ) { delete($filts->{'resource'}); }
|
|
|
|
if ($filts->{'mode'} && ($filts->{'mode'} eq "-" || $filts->{'mode'} eq "All") ) {
|
|
delete($filts->{'mode'});
|
|
}
|
|
|
|
if ($filts->{'sdmode'} && ($filts->{'sdmode'} eq "-" || $filts->{'sdmode'} eq "All") ) {
|
|
delete($filts->{'sdmode'});
|
|
}
|
|
############################################################
|
|
|
|
$filts = rewriteModes($filts);
|
|
|
|
return $filts;
|
|
}
|
|
|
|
# returns ref to active filters for the specific SIR report
|
|
sub getSirFilters {
|
|
|
|
my $args = shift;
|
|
my $repName = undef;
|
|
|
|
if ( $args && $args->{'name'} ) {
|
|
$repName = $args->{'name'};
|
|
} else {
|
|
$repName = "Security.Incident.Report";
|
|
}
|
|
|
|
my $repConf = '/etc/apparmor/reports.conf';
|
|
my $rec = undef;
|
|
|
|
my $filts = getXmlReport($repName);
|
|
|
|
# Clean hash of useless refs
|
|
for (sort keys(%$filts) ) {
|
|
if ($filts->{$_} eq "-") {
|
|
delete($filts->{$_});
|
|
}
|
|
}
|
|
|
|
# remove non-filter info
|
|
if ( $filts->{'name'} ) { delete( $filts->{'name'}); }
|
|
if ( $filts->{'exportpath'} ) { delete( $filts->{'exportpath'}); }
|
|
if ( $filts->{'exporttype'} ) { delete( $filts->{'exporttype'}); }
|
|
if ( $filts->{'addr1'} ) { delete( $filts->{'addr1'}); }
|
|
if ( $filts->{'addr2'} ) { delete( $filts->{'addr2'}); }
|
|
if ( $filts->{'addr3'} ) { delete( $filts->{'addr3'}); }
|
|
if ( $filts->{'time'} ) { delete( $filts->{'time'}); }
|
|
|
|
if ( ! $args->{'gui'} || $args->{'gui'} ne "1" ) {
|
|
$filts = rewriteModes($filts);
|
|
$filts = rewriteFilters($filts);
|
|
}
|
|
|
|
return $filts;
|
|
}
|
|
|
|
# deprecated (pre-xml)
|
|
sub OldgetSirFilters {
|
|
|
|
my $args = shift;
|
|
my $repName = undef;
|
|
|
|
if ( $args && $args->{'name'} ) {
|
|
$repName = $args->{'name'};
|
|
}
|
|
|
|
my $repConf = '/etc/apparmor/reports.conf';
|
|
my $rec = undef;
|
|
|
|
if (! $repName) {
|
|
$repName = "\"Security.Incident.Report\"";
|
|
} else {
|
|
$repName = "\"$repName\"";
|
|
}
|
|
|
|
if ( open(CF, "<$repConf") ) {
|
|
|
|
while (<CF>) {
|
|
next if /^#/;
|
|
chomp;
|
|
my ($cfRptName) = (split(/:/, $_))[0];
|
|
$cfRptName =~ s/\s+$//; # remove trailing spaces
|
|
|
|
next unless ($cfRptName eq "$repName");
|
|
|
|
# Name : csv.html : prog, prof, pid, res, sev, sdmode, mode : (up to 3) email addresses : last run time
|
|
my ($name,$info) = split(/:/, $_, 2);
|
|
$info =~ s/\s+//g;
|
|
$name =~ s/^\s+//;
|
|
$name =~ s/\s+$//;
|
|
my ($mailtype, $filters, $email, $lastRun) = split(/\s*:\s*/, $info, 4);
|
|
|
|
$rec->{'name'} = $name;
|
|
$rec->{'name'} =~ s/\"//g;
|
|
($rec->{'prog'}, $rec->{'profile'}, $rec->{'pid'}, $rec->{'resource'},
|
|
$rec->{'severity'}, $rec->{'sdmode'}, $rec->{'mode'}) = split(/\,/, $filters, 7);
|
|
|
|
}
|
|
|
|
close CF;
|
|
} else {
|
|
logError("Couldn't open $repConf. No filters will be used in report generation.");
|
|
return;
|
|
}
|
|
|
|
# Clean hash of useless refs
|
|
for (sort keys(%$rec) ) {
|
|
if ($rec->{$_} eq "-") {
|
|
delete($rec->{$_});
|
|
}
|
|
}
|
|
|
|
$rec = rewriteModes($rec);
|
|
|
|
if ( ! $args->{'gui'} || $args->{'gui'} ne "1" ) {
|
|
$rec = rewriteFilters($rec);
|
|
}
|
|
|
|
return $rec;
|
|
}
|
|
|
|
# Main SIR report generator
|
|
sub getEvents {
|
|
|
|
my ($query, $start, $end, $dbFile) = @_;
|
|
my @events = ();
|
|
my $prevTime = 0;
|
|
my $prevDate = '0';
|
|
|
|
if ( ! $query || $query !~ /^SELECT/ ) { $query = "SELECT * FROM events"; }
|
|
if ( $dbFile && -f $dbFile ) { $eventDb = $dbFile; }
|
|
|
|
my $hostName = `/bin/hostname` || 'unknown';
|
|
chomp $hostName unless $hostName eq 'unknown';
|
|
|
|
if ( ! $start) { $start = '1104566401'; } # Give default start of 1/1/2005
|
|
if ( ! $end) { $end = time; }
|
|
|
|
# make sure they don't give us a bad range
|
|
($start, $end) = ($end, $start) if $start > $end;
|
|
|
|
# Events Schema
|
|
# - (id,time,counter,pid,sdmode,type,mode,resource,target,profile,prog,severity);
|
|
|
|
# Pull stuff from db
|
|
my $dbh = DBI->connect("dbi:SQLite:dbname=$eventDb", "", "", {RaiseError => 1, AutoCommit => 1});
|
|
my $all = undef;
|
|
eval {
|
|
$all = $dbh->selectall_arrayref("$query");
|
|
};
|
|
|
|
if ( $@ ) {
|
|
Immunix::Ycp::y2error(sprintf(gettext("DBI Execution failed: %s."), $DBI::errstr));
|
|
return;
|
|
}
|
|
|
|
$dbh->disconnect();
|
|
|
|
for my $row (@$all) {
|
|
my $rec = undef;
|
|
($rec->{'id'}, $rec->{'time'}, $rec->{'counter'}, $rec->{'pid'}, $rec->{'sdmode'}, $rec->{'type'},
|
|
$rec->{'mode'}, $rec->{'resource'}, $rec->{'target'}, $rec->{'profile'}, $rec->{'prog'}, $rec->{'severity'} ) = @$row;
|
|
|
|
# Give empty record values a default value
|
|
if ( ! $rec->{'host'} ) { $rec->{'host'} = $hostName; }
|
|
for (keys(%$rec)) { if ( !$rec->{$_} ) { $rec->{$_} = '-'; } }
|
|
|
|
# Change 'time' to date
|
|
if ( $rec->{'time'} && $rec->{'time'} == $prevTime ) {
|
|
$rec->{'date'} = $prevDate;
|
|
} elsif ( $rec->{'time'} ) {
|
|
my $newDate = getDate("$rec->{'time'}");
|
|
$rec->{'date'} = $newDate;
|
|
$prevDate = $newDate;
|
|
$prevTime = $rec->{'time'};
|
|
} else {
|
|
$rec->{'date'} = "0000-00-00-00:00:00";
|
|
}
|
|
|
|
if ( $rec->{'severity'} && $rec->{'severity'} eq '-1' ) {
|
|
$rec->{'severity'} = 'U';
|
|
}
|
|
|
|
delete($rec->{'time'});
|
|
delete($rec->{'counter'});
|
|
|
|
push(@events, $rec);
|
|
}
|
|
|
|
return \@events;
|
|
}
|
|
|
|
# Archived Reports Stuff -- Some of this would go away in an ideal world
|
|
################################################################################
|
|
sub getArchReport {
|
|
|
|
my $args = shift;
|
|
my @rec = ();
|
|
my $eventRep = "/var/log/apparmor/reports/events.rpt";
|
|
|
|
#if ( $args->{'type'} && $args->{'type'} eq 'archRep' ) {}
|
|
if ( $args->{'logFile'} ) {
|
|
$eventRep = $args->{'logFile'};
|
|
}
|
|
|
|
if ( open(REP, "<$eventRep") ) {
|
|
|
|
my $page = 1;
|
|
|
|
if ( $args->{'page'} ) { $page = $args->{'page'}; }
|
|
|
|
my $id = 1;
|
|
my $slurp = 0;
|
|
#my $numPages = 0;
|
|
|
|
my $prevTime = undef;
|
|
my $prevDate = undef;
|
|
|
|
while (<REP>) {
|
|
|
|
my $db = ();
|
|
|
|
# Why not get rid of page and just do divide by $i later?
|
|
if (/Page/) {
|
|
# $numPages++;
|
|
chomp;
|
|
if ($_ eq "Page $page") {
|
|
$slurp = 1;
|
|
} else {
|
|
$slurp = 0;
|
|
}
|
|
} elsif ( $slurp == 1 ) {
|
|
|
|
chomp;
|
|
|
|
($db->{'host'}, $db->{'time'}, $db->{'prog'}, $db->{'profile'}, $db->{'pid'}, $db->{'severity'},
|
|
$db->{'mode'}, $db->{'denyRes'}, $db->{'sdmode'} ) = split(/\,/, $_);
|
|
|
|
# Convert epoch time to date
|
|
if ($db->{'time'} == $prevTime) {
|
|
$db->{'date'} = $prevDate;
|
|
} else {
|
|
$prevTime = $db->{'time'};
|
|
$prevDate = getDate("$db->{'time'}");
|
|
$db->{'date'} = $prevDate;
|
|
}
|
|
|
|
$id++;
|
|
$db->{'date'} = $db->{'time'};
|
|
delete $db->{'time'};
|
|
push(@rec, $db);
|
|
}
|
|
}
|
|
|
|
|
|
close REP;
|
|
|
|
} else {
|
|
Immunix::Ycp::y2error(sprintf(gettext("Fatal Error. getArchReport() couldn't open %s"), $eventRep));
|
|
return("Couldn't open $eventRep");
|
|
}
|
|
|
|
return(\@rec);
|
|
}
|
|
|
|
sub writeEventReport {
|
|
|
|
my ( $db, $args) = @_; # Filters for date, && regexp
|
|
#my $type = shift || undef;
|
|
my $eventRep = "/var/log/apparmor/reports/events.rpt";
|
|
|
|
# Not sure if this is needed anymore, but it messes up archived SIR reports
|
|
# if ( $args->{'logFile'} ) { $eventRep = $args->{'logFile'}; }
|
|
|
|
if ( open(REP, ">$eventRep") ) {
|
|
|
|
my $i = 1;
|
|
my $page = 1;
|
|
my $skip = 0;
|
|
|
|
# Title for scheduled reports
|
|
if ( $args->{'title'} ) { print REP "$args->{'title'}"; }
|
|
|
|
print REP "Page $page\n";
|
|
$page++;
|
|
|
|
for (@$db) {
|
|
|
|
print REP "$_->{'host'},$_->{'date'},$_->{'prog'},$_->{'profile'},$_->{'pid'},$_->{'severity'},$_->{'mode'},$_->{'resource'},$_->{'sdmode'}\n";
|
|
|
|
if ( ($i % $numEvents) == 0 && $skip == 0) {
|
|
print REP "Page $page\n";
|
|
$page++;
|
|
$skip = 1;
|
|
} else {
|
|
$i++;
|
|
$skip = 0;
|
|
}
|
|
|
|
}
|
|
|
|
close REP;
|
|
|
|
} else {
|
|
return("Couldn't open $eventRep");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
sub prepSingleLog {
|
|
|
|
my $args = shift;
|
|
|
|
my $dir = '/var/log/apparmor/reports-archived';
|
|
my $error = "0";
|
|
my @errors = (); # For non-fatal errors
|
|
my @repList = ();
|
|
my $readFile = "";
|
|
my $eventRep = "/var/log/apparmor/reports/all-reports.rpt"; # write summary to this file - changed 04-14-2005
|
|
#my $eventRep = "/tmp/events.rpt"; # write summary to this file
|
|
|
|
if ( $args->{'logFile'} ) { $readFile = $args->{'logFile'}; }
|
|
if ( $args->{'repPath'} ) { $dir = $args->{'repPath'}; }
|
|
|
|
my @rawDb = ();
|
|
my $numPages = 1;
|
|
my $numRecords = 1;
|
|
my $skip = 0;
|
|
|
|
# Open record compilation file
|
|
if ( open(RREP, "<$dir/$readFile") ) {
|
|
|
|
if ( open(WREP, ">$eventRep") ) {
|
|
# print WREP "Page $numPages\n";
|
|
$numPages++;
|
|
|
|
while(<RREP>) {
|
|
|
|
next if (/Page/);
|
|
next if /^#/;
|
|
|
|
print WREP "$_";
|
|
|
|
if ( ($numRecords % $numEvents) == 0 && $skip == 0) {
|
|
print WREP "Page $numPages\n";
|
|
$numPages++;
|
|
$skip = 1;
|
|
} else {
|
|
$numRecords++;
|
|
$skip = 0;
|
|
}
|
|
|
|
}
|
|
close WREP;
|
|
} else {
|
|
$error = "Problem in prepSingleLog() - couldn't open $eventRep.";
|
|
return $error;
|
|
}
|
|
|
|
close RREP;
|
|
|
|
} else {
|
|
$error = "Problem in prepSingleLog() - couldn't open -$dir/$readFile-.";
|
|
return $error;
|
|
}
|
|
|
|
return $error;
|
|
}
|
|
|
|
# Cats files in specified directory for easier parsing
|
|
sub prepArchivedLogs {
|
|
|
|
my $args = shift;
|
|
|
|
my $dir = '/var/log/apparmor/reports-archived';
|
|
my $error = "0";
|
|
my @errors = (); # For non-fatal errors
|
|
my @repList = ();
|
|
my @db = ();
|
|
my $eventRep = "/var/log/apparmor/reports/all-reports.rpt";
|
|
|
|
my $useFilters = 0;
|
|
|
|
if ( $args->{'logFile'} ) {
|
|
$eventRep = $args->{'logFile'};
|
|
}
|
|
|
|
if ( $args->{'repPath'} ) {
|
|
$dir = $args->{'repPath'};
|
|
}
|
|
|
|
# Check to see if we need to use filters
|
|
if ( $args->{'mode'} && ( $args->{'mode'} =~ /All/ || $args->{'mode'} =~ /^\s*-\s*$/) ) {
|
|
delete($args->{'mode'});
|
|
}
|
|
if ( $args->{'sdmode'} && ( $args->{'sdmode'} =~ /All/ || $args->{'sdmode'} =~ /^\s*-\s*$/) ) {
|
|
delete($args->{'sdmode'});
|
|
}
|
|
if ( $args->{'resource'} && ( $args->{'resource'} =~ /All/ || $args->{'resource'} =~ /^\s*-\s*$/) ) {
|
|
delete($args->{'resource'});
|
|
}
|
|
if ( $args->{'sevLevel'} && ( $args->{'sevLevel'} =~ /All/ || $args->{'sevLevel'} =~ /^\s*-\s*$/) ) {
|
|
delete($args->{'sevLevel'});
|
|
}
|
|
|
|
if ( $args->{'prog'} || $args->{'profile'} || $args->{'pid'} || $args->{'denyRes'} ||
|
|
$args->{'mode'} || $args->{'sdmode'} || ($args->{'startdate'} && $args->{'enddate'} ) ) {
|
|
|
|
$useFilters = 1;
|
|
}
|
|
############################################################
|
|
|
|
|
|
# Get list of files in archived report directory
|
|
if ( opendir (RDIR, $dir) ) {
|
|
|
|
my @firstPass = grep(/csv/, readdir(RDIR));
|
|
@repList = grep(!/Applications.Audit|Executive.Security.Summary/, @firstPass);
|
|
close RDIR;
|
|
|
|
} else {
|
|
$error = "Failure in prepArchivedLogs() - couldn't open $dir.";
|
|
return($error); # debug - exit instead?
|
|
}
|
|
|
|
my @rawDb = ();
|
|
my $numPages = 1;
|
|
my $numRecords = 1;
|
|
|
|
# Open record compilation file
|
|
if ( open(AREP, ">$eventRep") ) {
|
|
|
|
for (@repList) {
|
|
|
|
my $file = $_;
|
|
|
|
# Cycle through each $file in $dir
|
|
if (open (RPT, "<$dir/$file") ) {
|
|
push(@rawDb, <RPT>);
|
|
close RPT;
|
|
} else {
|
|
$error = "Problem in prepArchivedLogs() - couldn't open $dir/$file.";
|
|
push(@errors, $error);
|
|
}
|
|
}
|
|
|
|
# sort & store cat'd files
|
|
if (@rawDb > 0 ) {
|
|
|
|
# Run Filters
|
|
if ( $useFilters == 1 ) {
|
|
|
|
my @tmpDb = parseMultiDb($args,@rawDb);
|
|
@db = sort(@tmpDb);
|
|
|
|
} else {
|
|
@db = sort(@rawDb);
|
|
}
|
|
|
|
my $skip = 0;
|
|
print AREP "Page $numPages\n";
|
|
$numPages++;
|
|
|
|
for (@db) {
|
|
|
|
next if /^Page/;
|
|
next if /^#/;
|
|
|
|
print AREP "$_";
|
|
|
|
if ( ($numRecords % $numEvents) == 0 && $skip == 0) {
|
|
print AREP "Page $numPages\n";
|
|
$numPages++;
|
|
$skip = 1;
|
|
} else {
|
|
$numRecords++;
|
|
$skip = 0;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
$error = "DB created from $dir is empty.";
|
|
}
|
|
|
|
close AREP;
|
|
|
|
} else {
|
|
$error = "Problem in prepArchivedLogs() - couldn't open $eventRep.";
|
|
push(@errors, $error);
|
|
}
|
|
|
|
return $error;
|
|
}
|
|
|
|
# Similar to parseLog(), but expects @db to be passed
|
|
sub parseMultiDb {
|
|
|
|
my ($args,@db) = @_;
|
|
my @newDb = ();
|
|
|
|
my $error = undef;
|
|
my $startDate = undef;
|
|
my $endDate = undef;
|
|
|
|
# deref dates for speed
|
|
if ($args->{'startdate'} && $args->{'enddate'} ) {
|
|
$startDate = getEpochFromNum("$args->{'startdate'}",'start');
|
|
$endDate = getEpochFromNum("$args->{'enddate'}",'end');
|
|
}
|
|
|
|
$args = rewriteModes($args);
|
|
|
|
for (@db) {
|
|
|
|
my $rec = undef;
|
|
my $line = $_;
|
|
|
|
next if /true|false/; # avoid horrible yast bug
|
|
next if /^Page/;
|
|
next if /^#/;
|
|
chomp;
|
|
next if (! $_ || $_ eq "");
|
|
|
|
# Lazy filters -- maybe these should be with the rest below
|
|
if ( $args->{'prog'} ) { next unless /$args->{'prog'}/; }
|
|
if ( $args->{'profile'} ) { next unless /$args->{'profile'}/; }
|
|
|
|
# Need (epoch) 'time' element here, do we want to store 'date' instead?
|
|
($rec->{'host'},$rec->{'time'},$rec->{'prog'},$rec->{'profile'},
|
|
$rec->{'pid'},$rec->{'sevLevel'},$rec->{'mode'}, $rec->{'resource'}, $rec->{'sdmode'})
|
|
= split(/\,/, $_);
|
|
|
|
# Make sure we get the time/date ref. name right. If it's $args->"time",
|
|
# the arg will be converted to a human-friendly "date" ref in writeEventReport().
|
|
if ($rec->{'time'} =~ /\:|\-/ ) {
|
|
$rec->{'date'} = $rec->{'time'};
|
|
delete $rec->{'time'};
|
|
}
|
|
|
|
# Check filters
|
|
if ( $args->{'pid'} && $args->{'pid'} ne '-' ) {
|
|
next unless ($args->{'pid'} eq $rec->{'pid'});
|
|
}
|
|
if ( $args->{'sevLevel'} && $args->{'sevLevel'} ne "00" && $args->{'sevLevel'} ne '-' ) {
|
|
if ( $args->{'sevLevel'} eq "U" ) { $args->{'sevLevel'} = '-1'; }
|
|
next unless ($args->{'sevLevel'} eq $rec->{'sevLevel'});
|
|
}
|
|
if ( $args->{'mode'} && $args->{'mode'} ne '-' ) {
|
|
next unless ($args->{'mode'} eq $rec->{'mode'});
|
|
}
|
|
|
|
if ( $args->{'denyRes'} && $args->{'denyRes'} ne '-' ) {
|
|
next unless ($args->{'denyRes'} eq $rec->{'denyRes'});
|
|
}
|
|
if ( $args->{'sdmode'} && $args->{'sdmode'} ne '-' ) {
|
|
# Needs reversal of comparison for sdmode
|
|
next unless ( $rec->{'sdmode'} =~ /$args->{'sdmode'}/ );
|
|
}
|
|
|
|
push(@newDb, $line);
|
|
|
|
}
|
|
|
|
return @newDb;
|
|
}
|
|
|
|
# Grab & filter events from archived reports (.csv files)
|
|
sub parseLog {
|
|
|
|
my $args = shift;
|
|
|
|
my @db = ();
|
|
my $eventRep = "/var/log/apparmor/reports/events.rpt";
|
|
|
|
if ( $args->{'logFile'} ) {
|
|
$eventRep = $args->{'logFile'};
|
|
}
|
|
|
|
#my $id = keys(%$db);
|
|
#my $rec = undef;
|
|
my $error = undef;
|
|
my $startDate = undef;
|
|
my $endDate = undef;
|
|
|
|
# deref dates for speed
|
|
if ($args->{'startdate'} && $args->{'enddate'} ) {
|
|
$startDate = getEpochFromNum("$args->{'startdate'}",'start');
|
|
$endDate = getEpochFromNum("$args->{'enddate'}",'end');
|
|
}
|
|
|
|
#if ( $args->{'mode'} && ( $args->{'mode'} =~ /All/ || $args->{'mode'} =~ /\s*\-\s*/) ) {}
|
|
if ( $args->{'mode'} && ( $args->{'mode'} =~ /All/ || $args->{'mode'} =~ /^\s*-\s*$/) ) {
|
|
delete($args->{'mode'});
|
|
}
|
|
if ( $args->{'sdmode'} && ( $args->{'sdmode'} =~ /All/ || $args->{'sdmode'} =~ /^\s*-\s*$/) ) {
|
|
delete($args->{'sdmode'});
|
|
}
|
|
if ( $args->{'resource'} && ( $args->{'resource'} =~ /All/ || $args->{'resource'} =~ /^\s*-\s*$/) ) {
|
|
delete($args->{'resource'});
|
|
}
|
|
if ( $args->{'sevLevel'} && ( $args->{'sevLevel'} =~ /All/ || $args->{'sevLevel'} =~ /^\s*-\s*$/) ) {
|
|
delete($args->{'sevLevel'});
|
|
}
|
|
|
|
$args = rewriteModes($args);
|
|
|
|
if ( open (LOG, "<$eventRep") ) {
|
|
|
|
# Log Parsing
|
|
while (<LOG>) {
|
|
|
|
my $rec = undef;
|
|
|
|
next if /true|false/; # avoid horrible yast bug
|
|
next if /Page/;
|
|
next if /^#/;
|
|
chomp;
|
|
next if (! $_ || $_ eq "");
|
|
|
|
# Lazy filters -- maybe these should be with the rest below
|
|
if ( $args->{'prog'} ) { next unless /$args->{'prog'}/; }
|
|
if ( $args->{'profile'} ) { next unless /$args->{'profile'}/; }
|
|
|
|
# Need (epoch) 'time' element here, do we want to store 'date' instead?
|
|
($rec->{'host'},$rec->{'time'},$rec->{'prog'},$rec->{'profile'},
|
|
$rec->{'pid'},$rec->{'sevLevel'},$rec->{'mode'}, $rec->{'resource'}, $rec->{'sdmode'})
|
|
= split(/\,/, $_);
|
|
|
|
# Make sure we get the time/date ref. name right. If it's $args->"time",
|
|
# the arg will be converted to a human-friendly "date" ref in writeEventReport().
|
|
if ($rec->{'time'} =~ /\:|\-/ ) {
|
|
$rec->{'date'} = $rec->{'time'};
|
|
delete $rec->{'time'};
|
|
}
|
|
|
|
# Check filters
|
|
if ( $args->{'pid'} && $args->{'pid'} ne '-' ) {
|
|
next unless ($args->{'pid'} eq $rec->{'pid'});
|
|
}
|
|
if ( $args->{'sevLevel'} && $args->{'sevLevel'} ne "00" && $args->{'sevLevel'} ne '-' ) {
|
|
next unless ($args->{'sevLevel'} eq $rec->{'sevLevel'});
|
|
}
|
|
if ( $args->{'mode'} && $args->{'mode'} ne '-' ) {
|
|
next unless ($args->{'mode'} eq $rec->{'mode'});
|
|
}
|
|
if ( $args->{'denyRes'} && $args->{'denyRes'} ne '-' ) {
|
|
next unless ($args->{'denyRes'} eq $rec->{'denyRes'});
|
|
}
|
|
if ( $args->{'sdmode'} && $args->{'sdmode'} ne '-' ) {
|
|
# Needs reversal of comparison for sdmode
|
|
next unless ( $rec->{'sdmode'} =~ /$args->{'sdmode'}/ );
|
|
}
|
|
|
|
push(@db, $rec);
|
|
|
|
}
|
|
|
|
close LOG;
|
|
|
|
# Export results to file if requested
|
|
if ( $args->{'exporttext'} || $args->{'exporthtml'} ) {
|
|
|
|
my $rawLog = undef;
|
|
my $expLog = undef;
|
|
|
|
if ( $args->{'exportPath'} ) {
|
|
$rawLog = $args->{'exportPath'} . '/export-log';
|
|
} else {
|
|
$rawLog = '/var/log/apparmor/reports-exported/export-log';
|
|
}
|
|
|
|
if ( $args->{'exporttext'} && $args->{'exporttext'} eq 'true') {
|
|
$expLog = "$rawLog.csv";
|
|
exportLog($expLog,\@db); # redo w/ @$db instead of %db?
|
|
}
|
|
|
|
if ( $args->{'exporthtml'} && $args->{'exporthtml'} eq 'true') {
|
|
$expLog = "$rawLog.html";
|
|
exportLog($expLog,\@db); # redo w/ @$db instead of %db?
|
|
}
|
|
}
|
|
|
|
# write out files to single sorted file (for state, and to speed up yast)
|
|
#if (! $args->{'single'} ) {
|
|
# $error = writeEventReport(\@db, $args);
|
|
#}
|
|
|
|
# changed 04-13-05 - should probably do this, regardless
|
|
$error = writeEventReport(\@db, $args);
|
|
|
|
} else {
|
|
$error = "Couldn't open $eventRep.";
|
|
}
|
|
|
|
return $error;
|
|
}
|
|
|
|
# OLD STUFF -- delete
|
|
|
|
# deprecated -- replaced by better SQL queries
|
|
sub OLDgetEssStats {
|
|
|
|
my $args = shift;
|
|
|
|
my $prevTime = '0';
|
|
my $prevDate = '0';
|
|
my $startDate = '1104566401'; # Jan 1, 2005
|
|
my $endDate = time;
|
|
|
|
if ($args->{'startdate'} ) { $startDate = $args->{'startdate'}; }
|
|
if ($args->{'enddate'} ) { $endDate = $args->{'enddate'}; }
|
|
|
|
my $query = "SELECT * FROM events";
|
|
|
|
# hostIp, startDate, endDate, sevHi, sevMean, numRejects
|
|
my $eventDb = getEvents($query,"","$startDate","$endDate");
|
|
|
|
my @hostIdx = (); # Simple index to all hosts for quick host matching
|
|
my @hostDb = (); # Host-keyed data for doing REJECT stats
|
|
|
|
# Outer Loop for Raw Event db
|
|
for (@$eventDb) {
|
|
|
|
my $ev = $_; # current event record
|
|
|
|
if ( $ev->{'host'} ) {
|
|
|
|
# Create new host entry, or add to existing
|
|
if ( grep(/$ev->{'host'}/, @hostIdx) == 1 ) {
|
|
|
|
# Inner loop, but the number of hosts should be small
|
|
for my $hdb (@hostDb) {
|
|
|
|
if ($hdb->{'host'} eq $ev->{'host'}) {
|
|
|
|
if ( $hdb->{'startdate'} gt $ev->{'date'} ) {
|
|
$hdb->{'startdate'} = $ev->{'date'}; # Find earliest start date
|
|
}
|
|
|
|
$hdb->{'numEvents'}++; # tally all events reported for host
|
|
|
|
if ( $ev->{'sdmode'} ) {
|
|
if ( $ev->{'sdmode'} =~ /PERMIT/ ) { $hdb->{'numPermits'}++; }
|
|
if ( $ev->{'sdmode'} =~ /REJECT/ ) { $hdb->{'numRejects'}++; }
|
|
if ( $ev->{'sdmode'} =~ /AUDIT/ ) { $hdb->{'numAudits'}++; }
|
|
}
|
|
|
|
# Add stats to host entry
|
|
#if ( $ev->{'severity'} && $ev->{'severity'} =~ /\b\d+\b/ ) {}
|
|
if ( $ev->{'severity'} && $ev->{'severity'} != -1 ) {
|
|
|
|
$hdb->{'sevNum'}++;
|
|
$hdb->{'sevTotal'} = $hdb->{'sevTotal'} + $ev->{'severity'};
|
|
|
|
if ($ev->{'severity'} > $hdb->{'sevHi'} ) {
|
|
$hdb->{'sevHi'} = $ev->{'severity'};
|
|
}
|
|
} else {
|
|
$hdb->{'unknown'}++;
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
# New host
|
|
my $rec = undef;
|
|
push(@hostIdx,$ev->{'host'}); # Add host entry to index
|
|
|
|
$rec->{'host'} = $ev->{'host'};
|
|
$rec->{'startdate'} = $startDate;
|
|
#$rec->{'startdate'} = $ev->{'date'};
|
|
|
|
if ( $endDate ) {
|
|
$rec->{'enddate'} = $endDate;
|
|
} else {
|
|
$rec->{'enddate'} = time;
|
|
}
|
|
|
|
# Add stats to host entry
|
|
if ( $ev->{'sev'} && $ev->{'sev'} ne "U" ) {
|
|
|
|
$rec->{'sevHi'} = $ev->{'sev'};
|
|
$rec->{'sevTotal'} = $ev->{'sev'};
|
|
$rec->{'sevNum'} = 1;
|
|
$rec->{'unknown'} = 0;
|
|
|
|
} else {
|
|
$rec->{'sevHi'} = 0;
|
|
$rec->{'sevTotal'} = 0;
|
|
$rec->{'sevNum'} = 0;
|
|
$rec->{'unknown'} = 1;
|
|
}
|
|
|
|
# Start sdmode stats
|
|
$rec->{'numPermits'} = 0;
|
|
$rec->{'numRejects'} = 0;
|
|
$rec->{'numAudits'} = 0;
|
|
$rec->{'numEvents'} = 1; # tally all events reported for host
|
|
|
|
if ( $ev->{'sdmode'} ) {
|
|
if ( $ev->{'sdmode'} =~ /PERMIT/ ) { $rec->{'numPermits'}++; }
|
|
if ( $ev->{'sdmode'} =~ /REJECT/ ) { $rec->{'numRejects'}++; }
|
|
if ( $ev->{'sdmode'} =~ /AUDIT/ ) { $rec->{'numAudits'}++; }
|
|
}
|
|
|
|
push (@hostDb,$rec); # Add new records to host data list
|
|
}
|
|
|
|
} else {
|
|
next; # Missing host info -- big problem
|
|
}
|
|
} # END @eventDb loop
|
|
|
|
# Process simple REJECT-related stats (for Executive Security Summaries)
|
|
for ( @hostDb ) {
|
|
|
|
# In the end, we want this info:
|
|
# - Hostname, Startdate, Enddate, # Events, # Rejects, Ave. Severity, High Severity
|
|
|
|
if ( $_->{'sevTotal'} > 0 && $_->{'sevNum'} > 0 ) {
|
|
$_->{'sevMean'} = Immunix::Reports::round($_->{'sevTotal'}/$_->{'sevNum'});
|
|
} else {
|
|
$_->{'sevMean'} = 0;
|
|
}
|
|
|
|
# Convert dates
|
|
if ($_->{'startdate'} !~ /:/) {$_->{'startdate'} = Immunix::Reports::getDate($startDate); }
|
|
if ($_->{'enddate'} !~ /:/) { $_->{'enddate'} = Immunix::Reports::getDate($_->{'enddate'}); }
|
|
|
|
# Delete stuff that we may use in later versions (YaST is a silly, silly data handler)
|
|
delete($_->{'sevTotal'});
|
|
delete($_->{'sevNum'});
|
|
delete($_->{'numPermits'});
|
|
delete($_->{'numAudits'});
|
|
delete($_->{'unknown'});
|
|
|
|
}
|
|
|
|
return(\@hostDb);
|
|
}
|
|
|
|
|
|
1;
|
|
|